Skip to content

feat: switch base image to GitHub runner and add DevOps tools#1

Open
miragecentury wants to merge 6 commits intomainfrom
claude/update-base-image-tools-xNZ9h
Open

feat: switch base image to GitHub runner and add DevOps tools#1
miragecentury wants to merge 6 commits intomainfrom
claude/update-base-image-tools-xNZ9h

Conversation

@miragecentury
Copy link
Contributor

Replace ubuntu:24.04 base with ghcr.io/actions/runner for native
GitHub Actions runner support. Add skopeo, Argo Workflows CLI,
HashiCorp Packer, and Cloud Native Buildpacks (pack) CLI as
configurable build args.

https://claude.ai/code/session_01RofXXAMZxK4irobNYjYn3W

Replace ubuntu:24.04 base with ghcr.io/actions/runner for native
GitHub Actions runner support. Add skopeo, Argo Workflows CLI,
HashiCorp Packer, and Cloud Native Buildpacks (pack) CLI as
configurable build args.

https://claude.ai/code/session_01RofXXAMZxK4irobNYjYn3W
Swap out HashiCorp Packer for Kargo CLI (v1.9.2) for application
lifecycle orchestration support.

https://claude.ai/code/session_01RofXXAMZxK4irobNYjYn3W
Runs weekly (Monday 08:00 UTC) and on manual dispatch. Checks
latest releases for Argo, Kargo, and pack CLIs and opens a PR
when updates are available.

https://claude.ai/code/session_01RofXXAMZxK4irobNYjYn3W
- Add build tools to image (dive, trivy, buildah, yq, hadolint) so
  the image can build itself as a self-hosted runner
- Configure buildah vfs storage driver for container/rootless usage
- Create semantic-release config for automated versioning from
  conventional commits with changelog generation
- Add release workflow: semantic-release -> buildah build -> dive
  filesystem scan -> trivy vulnerability scan -> skopeo push with
  semver tags (major, major.minor, full, latest)
- Add CI workflow: commitlint, hadolint lint, and build test on PRs
- Update scheduled update-tools workflow with new tools (dive,
  hadolint, yq)
- Add best practice configs: .hadolint.yaml (trusted registries),
  .commitlintrc.yaml (conventional commits), .containerignore
  (minimal build context)

https://claude.ai/code/session_01RofXXAMZxK4irobNYjYn3W
Install pre-commit in the container image and add
.pre-commit-config.yaml with hooks for trailing whitespace,
YAML validation, hadolint, shellcheck, and commitlint.

https://claude.ai/code/session_01RofXXAMZxK4irobNYjYn3W
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request transitions the container image from a basic Ubuntu base to the official GitHub Actions runner image, adding comprehensive DevOps tooling to create a self-hosted runner capable of building itself. The changes establish a complete CI/CD pipeline with automated version management, security scanning, and tool updates.

Changes:

  • Replaced ubuntu:24.04 base image with ghcr.io/actions/runner for native GitHub Actions support
  • Added DevOps CLI tools (Argo Workflows, Kargo, pack, skopeo) and build pipeline tools (buildah, dive, trivy, hadolint, yq, pre-commit)
  • Implemented automated release workflow with semantic versioning, vulnerability scanning, and multi-tag publishing to GHCR

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
Containerfile Switched to GitHub runner base image; added skopeo, buildah, trivy, dive, hadolint, yq, Argo CLI, Kargo CLI, pack CLI, and pre-commit; added Python 3.12/3.13/3.14 with Poetry and UV
manifest.yaml Updated build args from Ubuntu-specific to tool version args (RUNNER_VERSION, ARGO_VERSION, KARGO_VERSION, PACK_VERSION, DIVE_VERSION, HADOLINT_VERSION, YQ_VERSION)
README.md Complete documentation rewrite covering included tools, CI/CD workflows, release process, local development setup, and project structure
.github/workflows/release.yaml New semantic release workflow with buildah build, hadolint/dive/trivy scanning, and multi-tag push to GHCR
.github/workflows/ci.yaml New CI workflow with commitlint, hadolint linting, and test build validation
.github/workflows/update-tools.yaml New automated tool version checker that creates PRs when updates are available
.releaserc.yaml Semantic release configuration for automated versioning and changelog generation
.pre-commit-config.yaml Pre-commit hooks for code quality, linting, and commit message validation
.hadolint.yaml Hadolint configuration defining trusted container registries
.commitlintrc.yaml Commitlint configuration extending conventional commit rules
.containerignore Build context exclusions for CI artifacts and configuration files
Comments suppressed due to low confidence (1)

Containerfile:114

  • Poetry and UV are installed twice - once in the base stage as root (lines 105-114) and again in the runtime stage as the runner user (lines 127-136). This duplication is wasteful and increases image size unnecessarily. The base stage installations as root won't be available to the runner user anyway. Consider removing lines 105-114 from the base stage since they are properly installed in the runtime stage where they will actually be used.
# Install Poetry latest version and add it to PATH
# hadolint ignore=DL4006
RUN curl -sSL https://install.python-poetry.org | python3 -

# Install UV
# hadolint ignore=DL4006
RUN curl -LsSf https://astral.sh/uv/install.sh | sh

# Add Poetry and UV to PATH
RUN echo "export PATH=\"${APP_HOME}/.local/bin:\$PATH\"" >> ~/.bashrc

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


| Tool | Version |
|------|---------|
| Python | 3.12, 3.13, 3.14 (via deadsnakes PPA) |
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states that Python 3.14 is installed, but Python 3.14 has not been released yet (as of February 2026, Python 3.14 is still in development). The deadsnakes PPA may not have a stable Python 3.14 release available, which could cause the build to fail. Verify that Python 3.14 is actually available from the deadsnakes PPA, or update the documentation and Containerfile to remove this version until it's officially released.

Suggested change
| Python | 3.12, 3.13, 3.14 (via deadsnakes PPA) |
| Python | 3.12, 3.13 (via deadsnakes PPA) |

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +99
docker load -i "build/${IMAGE_NAME}.tar" 2>/dev/null || true
docker tag "$(docker images -q | head -1)" "${IMAGE_NAME}:${IMAGE_VERSION}" 2>/dev/null || true
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dive scan expects the image to be loaded into the Docker daemon, but the load command on line 98 uses 2>/dev/null || true which silently ignores all errors. If the OCI archive is corrupted or incompatible, the load will fail silently, and the subsequent docker tag on line 99 will also fail silently. This means the dive scan on line 104 will fail with a confusing error about the image not being found. Remove the || true or add explicit error checking to ensure the image is successfully loaded before attempting to scan it.

Suggested change
docker load -i "build/${IMAGE_NAME}.tar" 2>/dev/null || true
docker tag "$(docker images -q | head -1)" "${IMAGE_NAME}:${IMAGE_VERSION}" 2>/dev/null || true
docker load -i "build/${IMAGE_NAME}.tar"
docker tag "$(docker images -q | head -1)" "${IMAGE_NAME}:${IMAGE_VERSION}"

Copilot uses AI. Check for mistakes.
- name: Validate Containerfile
run: |
docker pull -q ghcr.io/hadolint/hadolint:latest
docker run --rm -i hadolint/hadolint:latest < Containerfile
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hadolint validation step doesn't use the .hadolint.yaml configuration file that exists in the repository. The CI workflow (ci.yaml) uses hadolint/hadolint-action@v3.1.0 which automatically picks up the config, but this step uses a plain Docker run command without specifying the config file. Add --config .hadolint.yaml to the docker run command or mount the config file to ensure consistent linting behavior between CI and release workflows.

Suggested change
docker run --rm -i hadolint/hadolint:latest < Containerfile
docker run --rm -i -v "${PWD}/.hadolint.yaml:/.hadolint.yaml" hadolint/hadolint:latest --config /.hadolint.yaml < Containerfile

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +41
# Install skopeo
# hadolint ignore=DL3008
RUN apt-get update \
&& apt-get install --no-install-recommends -y skopeo \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# Install buildah
# hadolint ignore=DL3008
RUN apt-get update \
&& apt-get install --no-install-recommends -y buildah \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple consecutive RUN commands for skopeo (lines 28-33) and buildah (lines 35-40) each perform apt-get update separately. This creates unnecessary image layers and increases build time. Consider combining these apt-get installations into a single RUN command to reduce layers and improve build efficiency. This would also align with Docker best practices for minimizing layer count.

Suggested change
# Install skopeo
# hadolint ignore=DL3008
RUN apt-get update \
&& apt-get install --no-install-recommends -y skopeo \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install buildah
# hadolint ignore=DL3008
RUN apt-get update \
&& apt-get install --no-install-recommends -y buildah \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install skopeo and buildah
# hadolint ignore=DL3008
RUN apt-get update \
&& apt-get install --no-install-recommends -y skopeo buildah \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Configure buildah storage for container/rootless usage
RUN mkdir -p /etc/containers \
&& printf '[storage]\ndriver = "vfs"\n' > /etc/containers/storage.conf
# Install trivy (vulnerability scanner)
# hadolint ignore=DL3008,DL4006

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +40
- name: Build image
run: docker build -f Containerfile -t test-build .
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI workflow's test build uses docker build which doesn't pass any of the build args defined in manifest.yaml (RUNNER_VERSION, ARGO_VERSION, etc.). This means the test build won't accurately reflect the actual build process used in the release workflow, potentially missing build failures related to these arguments. Consider using the same build approach as release.yaml or at least passing the build args from manifest.yaml.

Suggested change
- name: Build image
run: docker build -f Containerfile -t test-build .
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build image using manifest
run: docker buildx bake -f manifest.yaml --set *.tags=test-build

Copilot uses AI. Check for mistakes.
rev: v2.12.0
hooks:
- id: hadolint-docker
entry: hadolint/hadolint hadolint
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hadolint-docker hook uses a custom entry that runs the hadolint container, but this approach may not work correctly with pre-commit's hook execution model. The entry hadolint/hadolint hadolint appears to be malformed - it should either use the hadolint Docker image properly or use the hadolint binary directly. Consider using the standard hadolint hook configuration or verify this custom entry works as intended in local testing.

Suggested change
entry: hadolint/hadolint hadolint

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +128
MAJOR_MINOR="${IMAGE_VERSION%.*}"
MAJOR="${IMAGE_VERSION%%.*}"

Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version parsing logic assumes semantic versioning with at least two dots (MAJOR.MINOR.PATCH format). If the release version is "1.0" instead of "1.0.0", the MAJOR_MINOR variable on line 126 will be "1" (same as MAJOR), and MAJOR on line 127 will be empty, causing the subsequent skopeo copy commands to fail or push to incorrect tags. Add validation to ensure the version has the expected format, or make the parsing more robust to handle version strings with fewer components.

Suggested change
MAJOR_MINOR="${IMAGE_VERSION%.*}"
MAJOR="${IMAGE_VERSION%%.*}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$IMAGE_VERSION"
if [ -z "$MAJOR" ]; then
echo "ERROR: Failed to parse IMAGE_VERSION '$IMAGE_VERSION' into a major version component." >&2
exit 1
fi
if [ -n "$MINOR" ]; then
MAJOR_MINOR="${MAJOR}.${MINOR}"
else
MAJOR_MINOR="${MAJOR}"
fi

Copilot uses AI. Check for mistakes.
@@ -1,20 +1,10 @@
ARG UBUNTU_VERSION=24.04
ARG RUNNER_VERSION=latest
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using RUNNER_VERSION=latest as the default base image version introduces unpredictability and potential breaking changes. The GitHub Actions runner image could be updated at any time with breaking changes, causing builds to fail unexpectedly. Consider pinning to a specific version tag (e.g., a specific release version) to ensure reproducible builds, and update it through the automated tool version update workflow.

Suggested change
ARG RUNNER_VERSION=latest
ARG RUNNER_VERSION=2.319.1

Copilot uses AI. Check for mistakes.
args:
- APP_UID=1000
- UBUNTU_VERSION=24.04
- RUNNER_VERSION=latest
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manifest.yaml defines RUNNER_VERSION=latest but the update-tools.yaml workflow doesn't check for updates to the GitHub Actions runner version. Unlike the other tools (argo, kargo, pack, dive, hadolint, yq), there's no automated check or update mechanism for the runner base image version. Consider adding runner version checks to the update workflow to keep the base image up to date.

Suggested change
- RUNNER_VERSION=latest
- RUNNER_VERSION=2.321.0

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants