-
Notifications
You must be signed in to change notification settings - Fork 115
feature: docker build and release workflow #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
eb32ef9
48807b9
7d9ce0c
f831595
40b81fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| name: Docker Build & Push | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| variant: | ||
| description: 'Build variant' | ||
| required: true | ||
| type: choice | ||
| options: | ||
| - primary | ||
| - cu129-arm64 | ||
| - cu13-arm64 | ||
| - debug | ||
| dockerfile: | ||
| description: 'Path to dockerfile (e.g. docker/Dockerfile.dev)' | ||
| required: false | ||
| default: 'docker/Dockerfile' | ||
| type: string | ||
|
|
||
| jobs: | ||
| build-and-push: | ||
| runs-on: self-hosted | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
| with: | ||
| driver-opts: | | ||
| image=moby/buildkit:latest | ||
| network=host | ||
|
|
||
| - name: Login to Docker Hub | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
|
|
||
| - name: Build and push | ||
| run: python docker/build.py --variant ${{ inputs.variant }} --dockerfile ${{ inputs.dockerfile }} |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,133 @@ | ||||||||||||||||||||||||
| #!/usr/bin/env python3 | ||||||||||||||||||||||||
| """Build and push Miles Docker images. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Replaces the justfile with a single script that handles all build variants. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Usage: | ||||||||||||||||||||||||
| python docker/build.py --variant primary | ||||||||||||||||||||||||
| python docker/build.py --variant cu129-arm64 | ||||||||||||||||||||||||
| python docker/build.py --variant cu13-arm64 | ||||||||||||||||||||||||
| python docker/build.py --variant debug | ||||||||||||||||||||||||
| python docker/build.py --variant primary --dry-run | ||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||
| import subprocess | ||||||||||||||||||||||||
| from enum import Enum | ||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import typer | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| CACHE_DIR = "/tmp/miles-docker-cache" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| VARIANTS = { | ||||||||||||||||||||||||
| "primary": { | ||||||||||||||||||||||||
| "image": "radixark/miles", | ||||||||||||||||||||||||
| "tag_postfix": "", | ||||||||||||||||||||||||
| "build_args": {}, | ||||||||||||||||||||||||
| "tag_latest": True, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "cu129-arm64": { | ||||||||||||||||||||||||
| "image": "radixark/miles", | ||||||||||||||||||||||||
| "tag_postfix": "-cu129-arm64", | ||||||||||||||||||||||||
| "build_args": { | ||||||||||||||||||||||||
| "SGLANG_IMAGE_TAG": "v0.5.5.post3-cu129-arm64", | ||||||||||||||||||||||||
| "ENABLE_SGLANG_PATCH": "0", | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "tag_latest": False, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "cu13-arm64": { | ||||||||||||||||||||||||
| "image": "radixark/miles", | ||||||||||||||||||||||||
| "tag_postfix": "-cu13-arm64", | ||||||||||||||||||||||||
| "build_args": { | ||||||||||||||||||||||||
| "SGLANG_IMAGE_TAG": "dev-arm64-cu13-20251122", | ||||||||||||||||||||||||
| "ENABLE_CUDA_13": "1", | ||||||||||||||||||||||||
| "ENABLE_SGLANG_PATCH": "0", | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "tag_latest": False, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "debug": { | ||||||||||||||||||||||||
| "image": "radixark/miles-test", | ||||||||||||||||||||||||
| "tag_postfix": "", | ||||||||||||||||||||||||
| "build_args": {}, | ||||||||||||||||||||||||
| "tag_latest": True, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def get_version(repo_root: Path) -> str: | ||||||||||||||||||||||||
| version_file = repo_root / "docker" / "version.txt" | ||||||||||||||||||||||||
| return version_file.read_text().strip() | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def run(cmd: list[str], dry_run: bool) -> None: | ||||||||||||||||||||||||
| print(f"+ {' '.join(cmd)}", flush=True) | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The script prints the full Docker command, including sensitive proxy credentials (
Suggested change
|
||||||||||||||||||||||||
| if dry_run: | ||||||||||||||||||||||||
| return | ||||||||||||||||||||||||
| subprocess.run(cmd, check=True) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def build_and_push(variant: str, dry_run: bool, dockerfile: str) -> None: | ||||||||||||||||||||||||
| config = VARIANTS[variant] | ||||||||||||||||||||||||
| repo_root = Path(__file__).resolve().parent.parent | ||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For better code structure and to make functions more self-contained, consider defining
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| version = get_version(repo_root) | ||||||||||||||||||||||||
| image = config["image"] | ||||||||||||||||||||||||
| image_tag = f"{version}{config['tag_postfix']}" | ||||||||||||||||||||||||
| full_tag = f"{image}:{image_tag}" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cache_key = f"{CACHE_DIR}/{variant}" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Build command using buildx with cache and --push | ||||||||||||||||||||||||
| cmd = [ | ||||||||||||||||||||||||
| "docker", "buildx", "build", | ||||||||||||||||||||||||
| "-f", dockerfile, | ||||||||||||||||||||||||
| # "--cache-from", f"type=local,src={cache_key}", | ||||||||||||||||||||||||
| # "--cache-to", f"type=local,dest={cache_key},mode=max", | ||||||||||||||||||||||||
| # "--push", | ||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Proxy args (pass through if set in environment) | ||||||||||||||||||||||||
| for env_var, arg_name in [ | ||||||||||||||||||||||||
| ("http_proxy", "HTTP_PROXY"), | ||||||||||||||||||||||||
| ("https_proxy", "HTTPS_PROXY"), | ||||||||||||||||||||||||
| ]: | ||||||||||||||||||||||||
| value = os.environ.get(env_var, "") | ||||||||||||||||||||||||
| if value: | ||||||||||||||||||||||||
| cmd += ["--build-arg", f"{arg_name}={value}"] | ||||||||||||||||||||||||
|
Comment on lines
+91
to
+97
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current proxy handling only checks for lowercase environment variables (
Suggested change
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cmd += ["--build-arg", "NO_PROXY=localhost,127.0.0.1"] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Variant-specific build args | ||||||||||||||||||||||||
| for key, value in config["build_args"].items(): | ||||||||||||||||||||||||
| cmd += ["--build-arg", f"{key}={value}"] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Tags | ||||||||||||||||||||||||
| cmd += ["-t", full_tag] | ||||||||||||||||||||||||
| if config["tag_latest"]: | ||||||||||||||||||||||||
| cmd += ["-t", f"{image}:latest"] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Context is repo root | ||||||||||||||||||||||||
| cmd += ["."] | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| print(f"\n=== Building and pushing {full_tag} ===", flush=True) | ||||||||||||||||||||||||
| run(cmd, dry_run) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class Variant(str, Enum): | ||||||||||||||||||||||||
| primary = "primary" | ||||||||||||||||||||||||
| cu129_arm64 = "cu129-arm64" | ||||||||||||||||||||||||
| cu13_arm64 = "cu13-arm64" | ||||||||||||||||||||||||
| debug = "debug" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def main( | ||||||||||||||||||||||||
| variant: Variant = typer.Option(..., help="Build variant to use."), | ||||||||||||||||||||||||
| dockerfile: str = typer.Option("docker/Dockerfile", help="Path to the Dockerfile."), | ||||||||||||||||||||||||
| dry_run: bool = typer.Option(False, help="Print commands without executing them."), | ||||||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||||||
| build_and_push(variant.value, dry_run, dockerfile) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||||||||
| typer.run(main) | ||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of a hardcoded, predictable directory in
/tmpfor the Docker build cache is insecure. Since/tmpis world-writable, a local attacker could pre-create this directory or use symlinks to trick the script (especially if run with elevated privileges, which is common for Docker builds) into writing data to arbitrary locations on the filesystem. Consider using a user-specific cache directory.