Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/bedtools-immutable-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ jobs:
PILOT_RUNNER_ARCH: ${{ runner.arch }}
run: python3 -m scripts.pilot_runner --output canary-output --runner-only

- name: Checkout pinned tool runtime
uses: actions/checkout@v4
with:
repository: OmniBioAI/omnibioai-tool-runtime
ref: a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5
path: omnibioai-tool-runtime
- name: Resolve pinned tool runtime into build context
env:
TOOL_RUNTIME_COMMIT: 34a80219335f5dbeedeb7f0e6a4439913d12e25f
run: python3 scripts/runtime_resolver.py --stage omnibioai-tool-runtime

- uses: docker/setup-buildx-action@v3

Expand Down
10 changes: 4 additions & 6 deletions .github/workflows/pilot-immutable-multiarch-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,10 @@ jobs:
PILOT_RUNNER_ARCH: ${{ runner.arch }}
run: python3 -m scripts.pilot_runner --output release-output --runner-only

- name: Checkout pinned tool runtime
uses: actions/checkout@v4
with:
repository: OmniBioAI/omnibioai-tool-runtime
ref: a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5
path: omnibioai-tool-runtime
- name: Resolve pinned tool runtime into build context
env:
TOOL_RUNTIME_COMMIT: 34a80219335f5dbeedeb7f0e6a4439913d12e25f
run: python3 scripts/runtime_resolver.py --stage omnibioai-tool-runtime

- uses: docker/setup-buildx-action@v3

Expand Down
10 changes: 4 additions & 6 deletions .github/workflows/pilot-multiarch-sifs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,10 @@ jobs:
PILOT_RUNNER_OS: ${{ runner.os }}
PILOT_RUNNER_ARCH: ${{ runner.arch }}
run: python3 -m scripts.pilot_runner --output pilot-output --runner-only
- name: Checkout pinned tool runtime
uses: actions/checkout@v4
with:
repository: OmniBioAI/omnibioai-tool-runtime
ref: a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5
path: omnibioai-tool-runtime
- name: Resolve pinned tool runtime into build context
env:
TOOL_RUNTIME_COMMIT: 34a80219335f5dbeedeb7f0e6a4439913d12e25f
run: python3 scripts/runtime_resolver.py --stage omnibioai-tool-runtime
- uses: docker/setup-buildx-action@v3
- name: Install native Apptainer
run: |
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ pytest tests/ -v -k "not test_tool_runs_in_sif"

---

## Tool Runtime dependency

Tool image builds use the canonical `omnibioai-tool-runtime` repository as an external, commit-pinned build input. The resolver in `scripts/runtime_resolver.py` accepts `TOOL_RUNTIME_DIR` for an existing clean checkout, uses the sibling ecosystem checkout when available, and otherwise stages a temporary checkout at the approved `TOOL_RUNTIME_COMMIT`. It validates the exact commit and fails closed for missing, dirty, or mismatched inputs. Dockerfiles retain their existing `omnibioai-tool-runtime/` copy paths.

The historical nested runtime Gitlink was removed from active metadata. Historical references to its former SHA remain in audit records. The current approved runtime SHA is `34a80219335f5dbeedeb7f0e6a4439913d12e25f`.

## ORAS dependency

Historical vendored ORAS 1.1.0 artifacts were removed because active CI and
release code do not consume them. Manual release operations require a verified
`oras` executable available through `PATH`. CI installs ORAS 1.3.0 from the
upstream release archive, verifies the architecture-specific SHA-256 checksum,
and then installs it for the job. Generated SIF images and build logs remain
ignored and external to Git.

## Distribution

Once SIFs are built, four scripts push them off this machine to a remote
Expand All @@ -73,7 +88,7 @@ store. None of these are wired into the API — they're run manually, host-side:
| Script | Target | Requires |
|---|---|---|
| `scripts/push_to_hf.sh` | Hugging Face dataset repo `omnibioai/omnibioai-sif-images` | `huggingface-cli`, logged in |
| `scripts/push_to_ghcr.sh` | `ghcr.io/omnibioai/omnibioai-sif/<tool>:arm64` via `oras` (vendored in `scripts/`, auto-installed if missing) | `GH_USER` and `GH_TOKEN` env vars |
| `scripts/push_to_ghcr.sh` | `ghcr.io/omnibioai/omnibioai-sif/<tool>:arm64` via an `oras` executable on `PATH` | `GH_USER` and `GH_TOKEN` env vars |
| `scripts/upload_sifs_s3.sh` | `s3://<S3_SIF_BUCKET>-<AWS account ID>/` (bucket auto-created) | AWS CLI credentials |
| `scripts/upload_sifs_azure.sh` | Azure Blob container `${AZURE_SIF_CONTAINER:-omnibioai-sif}` (auto-created) | `AZURE_STORAGE_CONNECTION_STRING` |

Expand Down
Binary file removed scripts/oras
Binary file not shown.
Binary file removed scripts/oras_1.1.0_linux_arm64.tar.gz
Binary file not shown.
73 changes: 73 additions & 0 deletions scripts/runtime_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Resolve and stage the canonical OmniBioAI tool runtime for builds."""
from __future__ import annotations
import argparse, os, shutil, subprocess, tempfile
from pathlib import Path

DEFAULT_COMMIT = "34a80219335f5dbeedeb7f0e6a4439913d12e25f"
DEFAULT_REPOSITORY = "https://github.com/OmniBioAI/omnibioai-tool-runtime.git"

class RuntimeResolutionError(RuntimeError):
pass

def run(*args: str, cwd: Path | None = None) -> str:
p = subprocess.run(args, cwd=cwd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if p.returncode:
raise RuntimeResolutionError(f"command failed: {' '.join(args)}: {p.stderr.strip()}")
return p.stdout.strip()

def sha(path: Path) -> str:
try:
return run("git", "-C", str(path), "rev-parse", "HEAD")
except RuntimeResolutionError as exc:
raise RuntimeResolutionError(f"runtime checkout is not a Git repository: {path}") from exc

def validate(path: Path, expected: str) -> None:
if not path.is_dir():
raise RuntimeResolutionError(f"runtime checkout is missing: {path}")
if run("git", "-C", str(path), "status", "--porcelain"):
raise RuntimeResolutionError(f"runtime checkout is dirty: {path}")
actual = sha(path)
if actual != expected:
raise RuntimeResolutionError(f"runtime SHA mismatch: expected {expected}, got {actual}")

def resolve(repo: Path, expected: str) -> tuple[Path, tempfile.TemporaryDirectory[str] | None]:
override = os.environ.get("TOOL_RUNTIME_DIR")
candidates = [Path(override).expanduser()] if override else [repo.parent / "omnibioai-tool-runtime"]
for candidate in candidates:
if candidate.is_dir():
validate(candidate, expected)
return candidate, None
td = tempfile.TemporaryDirectory(prefix="omnibioai-tool-runtime-")
target = Path(td.name) / "checkout"
commit = os.environ.get("TOOL_RUNTIME_COMMIT", expected)
run("git", "clone", "--no-checkout", DEFAULT_REPOSITORY, str(target))
run("git", "-C", str(target), "checkout", "--detach", commit)
validate(target, expected)
return target, td

def stage(repo: Path, destination: Path, expected: str) -> Path:
runtime, temporary = resolve(repo, expected)
try:
if destination.exists():
raise RuntimeResolutionError(f"staging destination already exists: {destination}")
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copytree(runtime, destination, ignore=shutil.ignore_patterns(".git"))
validate(destination, expected) if (destination / ".git").exists() else None
return destination
finally:
if temporary is not None:
temporary.cleanup()

def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--repo", type=Path, default=Path(__file__).resolve().parents[1])
ap.add_argument("--commit", default=os.environ.get("TOOL_RUNTIME_COMMIT", DEFAULT_COMMIT))
ap.add_argument("--stage", type=Path, required=True)
args = ap.parse_args()
stage(args.repo.resolve(), args.stage.resolve(), args.commit)
print(args.commit)
return 0

if __name__ == "__main__":
raise SystemExit(main())
2 changes: 1 addition & 1 deletion scripts/sif_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

ALLOWED_ARCHITECTURES = ("amd64", "arm64")
ARTIFACT_TYPE = "application/vnd.omnibioai.sif.v1"
RUNTIME_COMMIT = "a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5"
RUNTIME_COMMIT = "34a80219335f5dbeedeb7f0e6a4439913d12e25f"
SBOM_STATUSES = {"SBOM_OPTIONAL_NOT_GENERATED", "SBOM_PASS"}
CANDIDATE_RECORD_SCHEMA_VERSION = "1"
RELEASE_PLAN_SCHEMA_VERSION = "1"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_bedtools_canary_oras_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _candidate_dict(**changes):
"provenance_status": "PASS",
"build_inputs": {
"package_revision": "2.30.0+dfsg-3",
"runtime_commit": "a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5",
"runtime_commit": "34a80219335f5dbeedeb7f0e6a4439913d12e25f",
"source_checksum": "4" * 64,
},
"operational_provenance": {
Expand Down
2 changes: 1 addition & 1 deletion tests/test_bedtools_canary_staging_containment.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def candidate(**changes):
"provenance_status": "PASS",
"build_inputs": {
"package_revision": "2.30.0+dfsg-3",
"runtime_commit": "a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5",
"runtime_commit": "34a80219335f5dbeedeb7f0e6a4439913d12e25f",
"source_checksum": "4" * 64,
},
"operational_provenance": {
Expand Down
10 changes: 4 additions & 6 deletions tests/test_contract_native_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,9 +541,7 @@ def test_reconciliation_failure_summary_is_durable(plan, tmp_path):
assert result["release_complete"] is False and result["publication_authorized"] is False


def test_checkout_gitlink_has_registered_repository():
configuration = configparser.ConfigParser()
configuration.read(REPO / ".gitmodules")
runtime = configuration['submodule "omnibioai-tool-runtime"']
assert runtime["path"] == "omnibioai-tool-runtime"
assert runtime["url"] == "https://github.com/OmniBioAI/omnibioai-tool-runtime.git"
def test_runtime_is_resolved_externally_at_canonical_sha():
from scripts.runtime_resolver import DEFAULT_COMMIT
assert not (REPO / ".gitmodules").exists()
assert DEFAULT_COMMIT == "34a80219335f5dbeedeb7f0e6a4439913d12e25f"
37 changes: 37 additions & 0 deletions tests/test_runtime_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from pathlib import Path

import pytest

from scripts import runtime_resolver as resolver

EXPECTED = "34a80219335f5dbeedeb7f0e6a4439913d12e25f"
RUNTIME = Path(__file__).resolve().parents[2] / "omnibioai-tool-runtime"


def test_sibling_runtime_resolves_and_validates(monkeypatch):
monkeypatch.delenv("TOOL_RUNTIME_DIR", raising=False)
resolved, temporary = resolver.resolve(Path(__file__).resolve().parents[1], EXPECTED)
assert resolved == RUNTIME
assert temporary is None


def test_wrong_sha_fails_closed():
with pytest.raises(resolver.RuntimeResolutionError, match="SHA mismatch"):
resolver.validate(RUNTIME, "0" * 40)


def test_missing_runtime_fails_closed_without_network(monkeypatch, tmp_path):
monkeypatch.setenv("TOOL_RUNTIME_DIR", str(tmp_path / "missing"))
def fail_clone(*args, **kwargs):
raise resolver.RuntimeResolutionError("network disabled in test")
monkeypatch.setattr(resolver, "run", fail_clone)
with pytest.raises(resolver.RuntimeResolutionError):
resolver.resolve(Path(__file__).resolve().parents[1], EXPECTED)


def test_staging_excludes_git_metadata(monkeypatch, tmp_path):
monkeypatch.setenv("TOOL_RUNTIME_DIR", str(RUNTIME))
destination = tmp_path / "omnibioai-tool-runtime"
resolver.stage(Path(__file__).resolve().parents[1], destination, EXPECTED)
assert (destination / "pyproject.toml").is_file()
assert not (destination / ".git").exists()
2 changes: 1 addition & 1 deletion tests/test_sif_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def candidate(**changes):
"provenance_status": "PASS",
"build_inputs": {
"package_revision": "2.30.0+dfsg-3",
"runtime_commit": "a4f3da56c2e6e9fd85e54ba009f6f3456de3b5b5",
"runtime_commit": "34a80219335f5dbeedeb7f0e6a4439913d12e25f",
"source_checksum": "4" * 64,
},
"operational_provenance": {
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sif_release_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ def test_bedtools_reference_build_identity_is_unchanged_by_canonicalization():
value = candidate(tool="bedtools", version="2.30.0")
assert release.canonicalize_version_evidence(value["version_evidence"]) == "bedtools v2.30.0"
assert identity.build_identity_sha256(value) == (
"5bfd3c83d10a2e68224f24ec2747545299b6c3b45bdac9b892ab21df201342fc"
"a8884fd73110f18fe4fa80f1f7a2df346d459937d631f1488c55aced7c6951c0"
)


Expand Down
Loading