fix(ci): use COVERAGE_FILE env var for reliable artifact paths; add --reuse-published to infra build
tracker-policy-pr / check-pr (pull_request) Successful in 7s
test / integration-docker (pull_request) Successful in 15s
lint / lint (push) Successful in 2m35s
test / unit (pull_request) Successful in 31s
test / integration-firecracker (pull_request) Successful in 3m11s
test / coverage (pull_request) Failing after 1m43s
test / publish-infra (pull_request) Has been skipped
tracker-policy-pr / check-pr (pull_request) Successful in 7s
test / integration-docker (pull_request) Successful in 15s
lint / lint (push) Successful in 2m35s
test / unit (pull_request) Successful in 31s
test / integration-firecracker (pull_request) Successful in 3m11s
test / coverage (pull_request) Failing after 1m43s
test / publish-infra (pull_request) Has been skipped
coverage run's --data-file flag can be overridden or ignored in some
runner environments (Nix Python, older act-based runners). Switching to
the COVERAGE_FILE env var with an absolute ${{ github.workspace }} path
ensures coverage.py writes to a known location in every runner context,
so upload-artifact can find the file.
Also adds --reuse-published to the infra build step: if the artifact for
this content hash already exists in the registry, download it instead of
running the full docker build → mke2fs → gzip pipeline.
This commit is contained in:
@@ -57,10 +57,14 @@ jobs:
|
||||
run: python3 -m pip install --break-system-packages -r requirements-dev.txt
|
||||
|
||||
- name: Run unit tests with coverage
|
||||
run: python3 -m coverage run --data-file=.coverage.unit -m unittest discover -t . -s tests/unit -v
|
||||
env:
|
||||
COVERAGE_FILE: ${{ github.workspace }}/.coverage.unit
|
||||
run: python3 -m coverage run -m unittest discover -t . -s tests/unit -v
|
||||
|
||||
- name: Report unit coverage
|
||||
run: python3 -m coverage report --data-file=.coverage.unit -m
|
||||
env:
|
||||
COVERAGE_FILE: ${{ github.workspace }}/.coverage.unit
|
||||
run: python3 -m coverage report -m
|
||||
|
||||
- name: Upload unit coverage artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -91,7 +95,8 @@ jobs:
|
||||
- name: Run integration tests (docker) with coverage
|
||||
env:
|
||||
BOT_BOTTLE_BACKEND: docker
|
||||
run: python3 -m coverage run --data-file=.coverage.docker -m unittest discover -t . -s tests/integration -v
|
||||
COVERAGE_FILE: ${{ github.workspace }}/.coverage.docker
|
||||
run: python3 -m coverage run -m unittest discover -t . -s tests/integration -v
|
||||
|
||||
- name: Upload docker coverage artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -139,7 +144,7 @@ jobs:
|
||||
- name: Build infra candidate from this checkout
|
||||
env:
|
||||
BOT_BOTTLE_FC_DROPBEAR: /var/cache/bot-bottle-fc/dropbear
|
||||
run: python3 -m bot_bottle.backend.firecracker.publish_infra --output infra-candidate
|
||||
run: python3 -m bot_bottle.backend.firecracker.publish_infra --output infra-candidate --reuse-published
|
||||
|
||||
- name: Replace the persistent infra VM with the candidate
|
||||
run: python3 -c 'from bot_bottle.backend.firecracker import infra_vm; infra_vm.stop()'
|
||||
@@ -151,7 +156,8 @@ jobs:
|
||||
env:
|
||||
BOT_BOTTLE_BACKEND: firecracker
|
||||
BOT_BOTTLE_INFRA_ARTIFACT_DIR: ${{ github.workspace }}/infra-candidate
|
||||
run: python3 -m coverage run --data-file=.coverage.firecracker -m unittest discover -t . -s tests/integration -v
|
||||
COVERAGE_FILE: ${{ github.workspace }}/.coverage.firecracker
|
||||
run: python3 -m coverage run -m unittest discover -t . -s tests/integration -v
|
||||
|
||||
- name: Upload firecracker coverage artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
@@ -181,6 +187,8 @@ jobs:
|
||||
#
|
||||
# Runs on ubuntu-latest — no KVM needed, no test reruns. Coverage files use
|
||||
# relative_files = True (.coveragerc) so they combine cleanly across runners.
|
||||
# Each test job sets COVERAGE_FILE to an absolute path so coverage.py writes
|
||||
# to a known location that upload-artifact can find regardless of runner env.
|
||||
#
|
||||
# Restricted to the same events as integration-firecracker: it depends on
|
||||
# that job's coverage artifact and skips for fork PRs alongside it.
|
||||
|
||||
@@ -131,6 +131,29 @@ def build_artifact(out_dir: Path) -> tuple[str, Path, Path]:
|
||||
return version, gz, sha
|
||||
|
||||
|
||||
def _try_download_published(out_dir: Path) -> tuple[str, Path, Path] | None:
|
||||
"""If this version's artifact is already in the registry, download the gz
|
||||
and sha to out_dir and return (version, gz_path, sha_path). Returns None
|
||||
when not yet published."""
|
||||
version = infra_artifact.infra_artifact_version(infra_vm._infra_init())
|
||||
sha_url = infra_artifact.artifact_url(version, "rootfs.ext4.gz.sha256")
|
||||
try:
|
||||
with urllib.request.urlopen(infra_artifact._open(sha_url)):
|
||||
pass
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
return None
|
||||
raise SystemExit(f"registry check failed (HTTP {e.code}): {sha_url}")
|
||||
except urllib.error.URLError as e:
|
||||
raise SystemExit(f"registry unreachable: {sha_url} ({e.reason})")
|
||||
print(f"infra rootfs {version} already published — downloading instead of building")
|
||||
gz = out_dir / "rootfs.ext4.gz"
|
||||
sha = out_dir / "rootfs.ext4.gz.sha256"
|
||||
infra_artifact._download(infra_artifact.artifact_url(version, "rootfs.ext4.gz"), gz)
|
||||
infra_artifact._download(infra_artifact.artifact_url(version, "rootfs.ext4.gz.sha256"), sha)
|
||||
return version, gz, sha
|
||||
|
||||
|
||||
def _publish_bundle(root: Path, token: str) -> str:
|
||||
version_file = root / "version.txt"
|
||||
# Guard the read so a missing version.txt is a clean error, not a raw
|
||||
@@ -187,6 +210,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||
help="build a candidate bundle in DIR without publishing")
|
||||
mode.add_argument("--publish-dir", type=Path,
|
||||
help="publish an already-built and tested candidate bundle")
|
||||
parser.add_argument("--reuse-published", action="store_true",
|
||||
help="with --output: download from registry if already published instead of building")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
_, _, token = infra_artifact._config()
|
||||
@@ -197,6 +222,14 @@ def main(argv: list[str] | None = None) -> int:
|
||||
|
||||
if args.output is not None:
|
||||
args.output.mkdir(parents=True, exist_ok=True)
|
||||
reused = None
|
||||
if args.reuse_published:
|
||||
reused = _try_download_published(args.output)
|
||||
if reused is not None:
|
||||
version, _, _ = reused
|
||||
(args.output / "version.txt").write_text(version + "\n", encoding="utf-8")
|
||||
print(f"reused published infra rootfs candidate {version}")
|
||||
return 0
|
||||
version, _gz, _sha = build_artifact(args.output)
|
||||
(args.output / "version.txt").write_text(version + "\n", encoding="utf-8")
|
||||
print(f"built infra rootfs candidate {version}")
|
||||
|
||||
Reference in New Issue
Block a user