fix(ci): use COVERAGE_FILE env var for reliable artifact paths; add --reuse-published to infra build
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
|
run: python3 -m pip install --break-system-packages -r requirements-dev.txt
|
||||||
|
|
||||||
- name: Run unit tests with coverage
|
- 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
|
- 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
|
- name: Upload unit coverage artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@@ -91,7 +95,8 @@ jobs:
|
|||||||
- name: Run integration tests (docker) with coverage
|
- name: Run integration tests (docker) with coverage
|
||||||
env:
|
env:
|
||||||
BOT_BOTTLE_BACKEND: docker
|
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
|
- name: Upload docker coverage artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@@ -139,7 +144,7 @@ jobs:
|
|||||||
- name: Build infra candidate from this checkout
|
- name: Build infra candidate from this checkout
|
||||||
env:
|
env:
|
||||||
BOT_BOTTLE_FC_DROPBEAR: /var/cache/bot-bottle-fc/dropbear
|
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
|
- name: Replace the persistent infra VM with the candidate
|
||||||
run: python3 -c 'from bot_bottle.backend.firecracker import infra_vm; infra_vm.stop()'
|
run: python3 -c 'from bot_bottle.backend.firecracker import infra_vm; infra_vm.stop()'
|
||||||
@@ -151,7 +156,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
BOT_BOTTLE_BACKEND: firecracker
|
BOT_BOTTLE_BACKEND: firecracker
|
||||||
BOT_BOTTLE_INFRA_ARTIFACT_DIR: ${{ github.workspace }}/infra-candidate
|
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
|
- name: Upload firecracker coverage artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@@ -181,6 +187,8 @@ jobs:
|
|||||||
#
|
#
|
||||||
# Runs on ubuntu-latest — no KVM needed, no test reruns. Coverage files use
|
# Runs on ubuntu-latest — no KVM needed, no test reruns. Coverage files use
|
||||||
# relative_files = True (.coveragerc) so they combine cleanly across runners.
|
# 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
|
# Restricted to the same events as integration-firecracker: it depends on
|
||||||
# that job's coverage artifact and skips for fork PRs alongside it.
|
# 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
|
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:
|
def _publish_bundle(root: Path, token: str) -> str:
|
||||||
version_file = root / "version.txt"
|
version_file = root / "version.txt"
|
||||||
# Guard the read so a missing version.txt is a clean error, not a raw
|
# 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")
|
help="build a candidate bundle in DIR without publishing")
|
||||||
mode.add_argument("--publish-dir", type=Path,
|
mode.add_argument("--publish-dir", type=Path,
|
||||||
help="publish an already-built and tested candidate bundle")
|
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)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
_, _, token = infra_artifact._config()
|
_, _, token = infra_artifact._config()
|
||||||
@@ -197,6 +222,14 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
|
|
||||||
if args.output is not None:
|
if args.output is not None:
|
||||||
args.output.mkdir(parents=True, exist_ok=True)
|
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)
|
version, _gz, _sha = build_artifact(args.output)
|
||||||
(args.output / "version.txt").write_text(version + "\n", encoding="utf-8")
|
(args.output / "version.txt").write_text(version + "\n", encoding="utf-8")
|
||||||
print(f"built infra rootfs candidate {version}")
|
print(f"built infra rootfs candidate {version}")
|
||||||
|
|||||||
Reference in New Issue
Block a user