Adopt commit-based container tags (#232)
## Summary - Replace git-tag-triggered container builds with path-based triggers on main and workflow_dispatch - Image tags now encode upstream app version + commit SHA (`vX.Y.Z-<sha>`) for full traceability - Replace `container-tag-and-release` task with `container-build-and-release` (dispatches workflows via Forgejo API) - Update dagger `publish()` to accept `commit_sha` parameter - Update all docs and references to the new workflow ## Deployment and Testing - [ ] Merge to main - [ ] `mise run container-build-and-release <name>` for each container to populate new-format tags - [ ] Verify tags in registry via `mise run container-list` - [ ] Existing images untouched — old tags remain available Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/232
This commit is contained in:
parent
0e2c10176d
commit
ffa8727660
13 changed files with 363 additions and 258 deletions
142
mise-tasks/container-build-and-release
Executable file
142
mise-tasks/container-build-and-release
Executable file
|
|
@ -0,0 +1,142 @@
|
|||
#!/usr/bin/env -S uv run --script
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = ["typer>=0.15.0", "httpx>=0.28.0"]
|
||||
# ///
|
||||
#MISE description="Trigger container build workflows via Forgejo API"
|
||||
#USAGE arg "<container>" help="Container name (directory under containers/)"
|
||||
#USAGE flag "--ref <ref>" help="Commit SHA to build (defaults to current HEAD)"
|
||||
#USAGE flag "--dry-run" help="Show what would be done without triggering"
|
||||
"""Trigger container build workflows via Forgejo API dispatch.
|
||||
|
||||
Dispatches both Build Container and Build Container (Nix) workflows.
|
||||
Each workflow checks for its build file and skips if not present.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
import typer
|
||||
|
||||
REGISTRY = "registry.ops.eblu.me"
|
||||
FORGE_URL = "https://forge.ops.eblu.me"
|
||||
FORGE_API = f"{FORGE_URL}/api/v1"
|
||||
REPO = "eblume/blumeops"
|
||||
FORGE_ACTIONS = f"{FORGE_URL}/{REPO}/actions"
|
||||
|
||||
WORKFLOWS = [
|
||||
"build-container.yaml",
|
||||
"build-container-nix.yaml",
|
||||
]
|
||||
|
||||
app = typer.Typer(add_completion=False)
|
||||
|
||||
|
||||
def git(*args: str) -> str:
|
||||
result = subprocess.run(
|
||||
["git", *args], capture_output=True, text=True, check=True
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def get_forge_token() -> str:
|
||||
result = subprocess.run(
|
||||
["op", "read", "op://blumeops/w3663ffnvkewbftncqxtcpeavy/api-token"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def list_containers() -> None:
|
||||
typer.echo("Available containers:")
|
||||
for d in sorted(Path("containers").iterdir()):
|
||||
if not d.is_dir():
|
||||
continue
|
||||
types = []
|
||||
if (d / "Dockerfile").exists():
|
||||
types.append("dockerfile")
|
||||
if (d / "default.nix").exists():
|
||||
types.append("nix")
|
||||
if types:
|
||||
typer.echo(f" - {d.name} ({', '.join(types)})")
|
||||
|
||||
|
||||
@app.command()
|
||||
def main(
|
||||
container: str = typer.Argument(help="Container name (directory under containers/)"),
|
||||
ref: str = typer.Option("", "--ref", help="Commit SHA to build (defaults to current HEAD)"),
|
||||
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be done without triggering"),
|
||||
) -> None:
|
||||
"""Trigger container build workflows via Forgejo API dispatch."""
|
||||
container_dir = Path("containers") / container
|
||||
has_dockerfile = (container_dir / "Dockerfile").exists()
|
||||
has_nix = (container_dir / "default.nix").exists()
|
||||
|
||||
if not has_dockerfile and not has_nix:
|
||||
typer.echo(f"Error: No Dockerfile or default.nix found in '{container_dir}'")
|
||||
typer.echo()
|
||||
list_containers()
|
||||
raise typer.Exit(1)
|
||||
|
||||
if not ref:
|
||||
ref = git("rev-parse", "HEAD")
|
||||
|
||||
short_sha = ref[:7]
|
||||
image = f"blumeops/{container}"
|
||||
|
||||
# Show expected builds
|
||||
builds = []
|
||||
if has_dockerfile:
|
||||
builds.append(f" dockerfile -> {REGISTRY}/{image}:v<version>-{short_sha}")
|
||||
if has_nix:
|
||||
builds.append(f" nix -> {REGISTRY}/{image}:v<version>-{short_sha}-nix")
|
||||
|
||||
if dry_run:
|
||||
typer.echo("[dry-run mode]")
|
||||
typer.echo(f"Container: {container}")
|
||||
typer.echo(f"Commit: {ref} ({short_sha})")
|
||||
typer.echo(f"Expected builds:")
|
||||
for b in builds:
|
||||
typer.echo(b)
|
||||
typer.echo()
|
||||
|
||||
if dry_run:
|
||||
typer.echo("[dry-run] Would dispatch workflows:")
|
||||
for wf in WORKFLOWS:
|
||||
typer.echo(f" - {wf}")
|
||||
typer.echo()
|
||||
typer.echo(f"Monitor builds at: {FORGE_ACTIONS}")
|
||||
return
|
||||
|
||||
token = get_forge_token()
|
||||
headers = {
|
||||
"Authorization": f"token {token}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
for wf in WORKFLOWS:
|
||||
url = f"{FORGE_API}/repos/{REPO}/actions/workflows/{wf}/dispatches"
|
||||
payload = {
|
||||
"ref": "main",
|
||||
"inputs": {
|
||||
"container": container,
|
||||
"ref": ref,
|
||||
},
|
||||
}
|
||||
resp = httpx.post(url, json=payload, headers=headers, timeout=30)
|
||||
if resp.status_code == 204:
|
||||
typer.echo(f"Dispatched {wf}")
|
||||
else:
|
||||
typer.echo(f"Error dispatching {wf}: {resp.status_code} {resp.text}")
|
||||
raise typer.Exit(1)
|
||||
|
||||
typer.echo()
|
||||
typer.echo(f"Monitor builds at: {FORGE_ACTIONS}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
Loading…
Add table
Add a link
Reference in a new issue