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
4 changes: 2 additions & 2 deletions .github/workflows/build_and_release_gar.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and Release Runner Image (EU dev GAR)
name: Dev Runner Image

# Triggered when a PR carries the `build-gar-images` label. Pushes test
# builds of all three variants to the EU dev GAR mirror only — Docker
Expand All @@ -22,7 +22,7 @@ permissions:

jobs:
build:
name: Build and Push to GAR (EU dev)
name: Build and Push
if: contains(github.event.pull_request.labels.*.name, 'build-gar-images')
runs-on: ubuntu-latest
steps:
Expand Down
124 changes: 78 additions & 46 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ jobs:
username: oauth2accesstoken
password: ${{ steps.gcp-auth.outputs.access_token }}

- name: Login to GAR (EU stage)
uses: docker/login-action@v3
with:
registry: ${{ vars.EU_STAGE_MIRROR_LOCATION }}-docker.pkg.dev
username: oauth2accesstoken
password: ${{ steps.gcp-auth.outputs.access_token }}

- name: Login to GAR (US prod)
uses: docker/login-action@v3
with:
Expand All @@ -55,17 +62,18 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Format Image Tag
- name: Format image tag
id: image_tag
run: |
echo "tag=${GITHUB_REF#refs/tags/}" | tee -a $GITHUB_OUTPUT

# Two regional GAR mirrors — EU dev and US production.
# Three GAR mirrors — EU dev, EU stage and US production.
- name: Compose GAR image paths
id: gar
run: |
echo "image_eu=${{ vars.EU_DEV_MIRROR_LOCATION }}-docker.pkg.dev/${{ vars.EU_DEV_GOOGLE_PROJECT }}/main/scalr/runner" | tee -a $GITHUB_OUTPUT
echo "image_us=${{ vars.US_PROD_MIRROR_LOCATION }}-docker.pkg.dev/${{ vars.US_PROD_GOOGLE_PROJECT }}/main/scalr/runner" | tee -a $GITHUB_OUTPUT
echo "image_eu_dev=${{ vars.EU_DEV_MIRROR_LOCATION }}-docker.pkg.dev/${{ vars.EU_DEV_GOOGLE_PROJECT }}/main/scalr/runner" | tee -a $GITHUB_OUTPUT
echo "image_eu_stage=${{ vars.EU_STAGE_MIRROR_LOCATION }}-docker.pkg.dev/${{ vars.EU_STAGE_GOOGLE_PROJECT }}/main/scalr/runner" | tee -a $GITHUB_OUTPUT
echo "image_us_prod=${{ vars.US_PROD_MIRROR_LOCATION }}-docker.pkg.dev/${{ vars.US_PROD_GOOGLE_PROJECT }}/main/scalr/runner" | tee -a $GITHUB_OUTPUT

- name: Build and push images
uses: docker/bake-action@v5
Expand All @@ -76,25 +84,31 @@ jobs:
docker-bake.hcl
versions.json
push: true
# Append both GAR region tags to each target so a single push writes
# to Docker Hub (declared in docker-bake.hcl) and both regional GAR
# mirrors. Both GAR mirrors additionally get a `v`-prefixed alias
# Append all GAR mirror tags to each target so a single push writes
# to Docker Hub (declared in docker-bake.hcl) and every GAR mirror.
# All GAR mirrors additionally get a `v`-prefixed alias
# (e.g. `v0.4.2`) so GAR cleanup policies can filter release tags
# by prefix. Cache uses GitHub Actions cache (per-repo, private) so
# nothing cache-related leaks into the public Docker Hub repo.
set: |
full.tags+=${{ steps.gar.outputs.image_eu }}:${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_eu }}:v${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_us }}:${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_us }}:v${{ steps.image_tag.outputs.tag }}
python39.tags+=${{ steps.gar.outputs.image_eu }}:${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_eu }}:v${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_us }}:${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_us }}:v${{ steps.image_tag.outputs.tag }}-python39
slim.tags+=${{ steps.gar.outputs.image_eu }}:${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_eu }}:v${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_us }}:${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_us }}:v${{ steps.image_tag.outputs.tag }}-slim
full.tags+=${{ steps.gar.outputs.image_eu_dev }}:${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_eu_dev }}:v${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_eu_stage }}:${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_eu_stage }}:v${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_us_prod }}:${{ steps.image_tag.outputs.tag }}
full.tags+=${{ steps.gar.outputs.image_us_prod }}:v${{ steps.image_tag.outputs.tag }}
python39.tags+=${{ steps.gar.outputs.image_eu_dev }}:${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_eu_dev }}:v${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_eu_stage }}:${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_eu_stage }}:v${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_us_prod }}:${{ steps.image_tag.outputs.tag }}-python39
python39.tags+=${{ steps.gar.outputs.image_us_prod }}:v${{ steps.image_tag.outputs.tag }}-python39
slim.tags+=${{ steps.gar.outputs.image_eu_dev }}:${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_eu_dev }}:v${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_eu_stage }}:${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_eu_stage }}:v${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_us_prod }}:${{ steps.image_tag.outputs.tag }}-slim
slim.tags+=${{ steps.gar.outputs.image_us_prod }}:v${{ steps.image_tag.outputs.tag }}-slim
full.cache-from=type=gha,scope=full
full.cache-to=type=gha,scope=full,mode=max
python39.cache-from=type=gha,scope=python39
Expand All @@ -105,8 +119,9 @@ jobs:
- name: Report published images
env:
TAG: ${{ steps.image_tag.outputs.tag }}
IMG_EU: ${{ steps.gar.outputs.image_eu }}
IMG_US: ${{ steps.gar.outputs.image_us }}
IMG_EU_DEV: ${{ steps.gar.outputs.image_eu_dev }}
IMG_EU_STAGE: ${{ steps.gar.outputs.image_eu_stage }}
IMG_US_PROD: ${{ steps.gar.outputs.image_us_prod }}
run: |
echo "Published runner images for release ${TAG}"
echo ""
Expand All @@ -116,20 +131,28 @@ jobs:
echo " scalr/runner:${TAG}-slim"
echo ""
echo "GAR — EU dev:"
echo " ${IMG_EU}:${TAG}"
echo " ${IMG_EU}:${TAG}-python39"
echo " ${IMG_EU}:${TAG}-slim"
echo " ${IMG_EU}:v${TAG}"
echo " ${IMG_EU}:v${TAG}-python39"
echo " ${IMG_EU}:v${TAG}-slim"
echo " ${IMG_EU_DEV}:${TAG}"
echo " ${IMG_EU_DEV}:${TAG}-python39"
echo " ${IMG_EU_DEV}:${TAG}-slim"
echo " ${IMG_EU_DEV}:v${TAG}"
echo " ${IMG_EU_DEV}:v${TAG}-python39"
echo " ${IMG_EU_DEV}:v${TAG}-slim"
echo ""
echo "GAR — EU stage:"
echo " ${IMG_EU_STAGE}:${TAG}"
echo " ${IMG_EU_STAGE}:${TAG}-python39"
echo " ${IMG_EU_STAGE}:${TAG}-slim"
echo " ${IMG_EU_STAGE}:v${TAG}"
echo " ${IMG_EU_STAGE}:v${TAG}-python39"
echo " ${IMG_EU_STAGE}:v${TAG}-slim"
echo ""
echo "GAR — US prod:"
echo " ${IMG_US}:${TAG}"
echo " ${IMG_US}:${TAG}-python39"
echo " ${IMG_US}:${TAG}-slim"
echo " ${IMG_US}:v${TAG}"
echo " ${IMG_US}:v${TAG}-python39"
echo " ${IMG_US}:v${TAG}-slim"
echo " ${IMG_US_PROD}:${TAG}"
echo " ${IMG_US_PROD}:${TAG}-python39"
echo " ${IMG_US_PROD}:${TAG}-slim"
echo " ${IMG_US_PROD}:v${TAG}"
echo " ${IMG_US_PROD}:v${TAG}-python39"
echo " ${IMG_US_PROD}:v${TAG}-slim"
{
echo "## Published runner images — \`${TAG}\`"
echo ""
Expand All @@ -141,21 +164,30 @@ jobs:
echo ""
echo "**GAR — EU dev**"
echo ""
echo "- \`${IMG_EU}:${TAG}\`"
echo "- \`${IMG_EU}:${TAG}-python39\`"
echo "- \`${IMG_EU}:${TAG}-slim\`"
echo "- \`${IMG_EU}:v${TAG}\`"
echo "- \`${IMG_EU}:v${TAG}-python39\`"
echo "- \`${IMG_EU}:v${TAG}-slim\`"
echo "- \`${IMG_EU_DEV}:${TAG}\`"
echo "- \`${IMG_EU_DEV}:${TAG}-python39\`"
echo "- \`${IMG_EU_DEV}:${TAG}-slim\`"
echo "- \`${IMG_EU_DEV}:v${TAG}\`"
echo "- \`${IMG_EU_DEV}:v${TAG}-python39\`"
echo "- \`${IMG_EU_DEV}:v${TAG}-slim\`"
echo ""
echo "**GAR — EU stage**"
echo ""
echo "- \`${IMG_EU_STAGE}:${TAG}\`"
echo "- \`${IMG_EU_STAGE}:${TAG}-python39\`"
echo "- \`${IMG_EU_STAGE}:${TAG}-slim\`"
echo "- \`${IMG_EU_STAGE}:v${TAG}\`"
echo "- \`${IMG_EU_STAGE}:v${TAG}-python39\`"
echo "- \`${IMG_EU_STAGE}:v${TAG}-slim\`"
echo ""
echo "**GAR — US prod**"
echo ""
echo "- \`${IMG_US}:${TAG}\`"
echo "- \`${IMG_US}:${TAG}-python39\`"
echo "- \`${IMG_US}:${TAG}-slim\`"
echo "- \`${IMG_US}:v${TAG}\`"
echo "- \`${IMG_US}:v${TAG}-python39\`"
echo "- \`${IMG_US}:v${TAG}-slim\`"
echo "- \`${IMG_US_PROD}:${TAG}\`"
echo "- \`${IMG_US_PROD}:${TAG}-python39\`"
echo "- \`${IMG_US_PROD}:${TAG}-slim\`"
echo "- \`${IMG_US_PROD}:v${TAG}\`"
echo "- \`${IMG_US_PROD}:v${TAG}-python39\`"
echo "- \`${IMG_US_PROD}:v${TAG}-slim\`"
} >> "$GITHUB_STEP_SUMMARY"

update_changelog:
Expand All @@ -172,7 +204,7 @@ jobs:
with:
ruby-version: "3.3"

- name: Install Changelog Generator
- name: Install changelog generator
run: gem install github_changelog_generator

- name: Update CHANGELOG.md
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## [0.6.0](https://github.com/Scalr/runner/tree/0.6.0) (Unreleased)

### Changed
- Updated Python: 3.14.6 → 3.14.8
- Reverted base image: `ubuntu:26.04` → `debian:trixie-slim` (snapshot `sha256:a29215f6a35e51e22adffa17f89e9d2ef06214e64a2bad10d765c46aea49f11f`).
- Updated CLI tools to latest versions:
- `kubectl`: v1.36.2 → v1.37.1
- `gcloud`: 575.0.0 → 588.0.0
- `aws`: 2.35.13 → 2.37.10
- `azure`: 2.87.0 → 2.91.0
- `session-manager-plugin`: 1.2.814.0 → 1.2.835.0
- `git-lfs`: v3.7.1 → v3.8.0 (patched `golang.org/x/crypto` v0.57.0, `golang.org/x/net` v0.59.0, `golang.org/x/sys` v0.48.0)

[Full Changelog](https://github.com/Scalr/runner/compare/0.5.0...0.6.0)

## [0.5.0](https://github.com/Scalr/runner/tree/0.5.0) (2026-07-01)

### Changed
Expand Down
24 changes: 9 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,35 @@
# - full: slim + Python + cloud CLIs + hardening (published as scalr/runner:<x.y.z>
# and scalr/runner:<x.y.z>-python39)
#
# UBUNTU_BASE_IMAGE, UBUNTU_BASE_DIGEST and other ARGs are supplied at build
# DEBIAN_BASE_IMAGE, DEBIAN_BASE_DIGEST and other ARGs are supplied at build
# time via bake (see docker-bake.hcl + versions.json). The skip directive
# above silences BuildKit's check for ARGs in FROM without a default.

ARG UBUNTU_BASE_IMAGE
ARG UBUNTU_BASE_DIGEST
ARG DEBIAN_BASE_IMAGE
ARG DEBIAN_BASE_DIGEST

# Custom git-lfs build, we rely on Wolfi's approach from
# https://github.com/wolfi-dev/os/blob/main/git-lfs.yaml
FROM golang:tip-bookworm AS git-lfs-build

SHELL ["/bin/bash", "-o", "pipefail", "-euxc"]

ARG GIT_LFS_VERSION=v3.7.1
ARG GIT_LFS_VERSION=v3.8.0

RUN <<EOT
git clone --depth 1 --branch "${GIT_LFS_VERSION}" https://github.com/git-lfs/git-lfs.git /git-lfs
cd /git-lfs
GOFLAGS=-mod=mod go get \
golang.org/x/crypto@v0.52.0 \
golang.org/x/net@v0.55.0 \
golang.org/x/sys@v0.45.0
golang.org/x/crypto@v0.57.0 \
golang.org/x/net@v0.59.0 \
golang.org/x/sys@v0.48.0
go mod tidy
CGO_ENABLED=0 go build -trimpath \
-ldflags "-s -w -X github.com/git-lfs/git-lfs/v3/config.GitCommit=$(git rev-parse --short HEAD)" \
-o /out/git-lfs .
EOT

FROM ${UBUNTU_BASE_IMAGE}@${UBUNTU_BASE_DIGEST} AS base-slim
FROM ${DEBIAN_BASE_IMAGE}@${DEBIAN_BASE_DIGEST} AS base-slim

ARG TARGETARCH

Expand Down Expand Up @@ -65,10 +65,7 @@ COPY --from=git-lfs-build /out/git-lfs /usr/bin/git-lfs

# Non-root user available in every variant (optional; used when the container
# runs with --user 1000). Created before hardening removes useradd.
RUN <<EOT
userdel -r ubuntu
useradd -u 1000 -m scalr
EOT
RUN useradd -u 1000 -m scalr

# Security hardening: strip privilege-escalation surface inherited from the base image.
# Must run last so it cannot be undone by a later layer.
Expand All @@ -91,9 +88,6 @@ RUN <<EOT
/bin/umount /usr/bin/umount
# Strip SUID/SGID bits from every remaining file (defense-in-depth).
find / -xdev \( -perm -4000 -o -perm -2000 \) -type f -exec chmod a-s {} + 2>/dev/null || true
# Remove unnecesary files for final build
rm -rf \
/usr/bin/pebble
EOT

FROM scratch AS slim
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ versions below are the current pins (kept in sync with `versions.json` by
`bump-versions.py`):

* **Programming language**
* Python ([v3.14.6](https://www.python.org/downloads/release/python-3146/)) — [standalone CPython build](https://github.com/astral-sh/python-build-standalone) from [astral.sh](https://astral.sh/)
* Python ([v3.14.8](https://www.python.org/downloads/release/python-3148/)) — [standalone CPython build](https://github.com/astral-sh/python-build-standalone) from [astral.sh](https://astral.sh/)
* **Cloud CLIs**
* AWS CLI ([2.35.13](https://github.com/aws/aws-cli/releases/tag/2.35.13)) — Amazon Web Services CLI
* AWS CLI ([2.37.10](https://github.com/aws/aws-cli/releases/tag/2.37.10)) — Amazon Web Services CLI
* AWS Session Manager Plugin — SSM session support for the AWS CLI
* Azure CLI ([2.87.0](https://github.com/Azure/azure-cli/releases/tag/azure-cli-2.87.0)) — Microsoft Azure CLI
* Google Cloud SDK ([575.0.0](https://cloud.google.com/sdk/docs/release-notes#57500)) — `gcloud` with `alpha`, `beta`, and `gke-gcloud-auth-plugin` components
* Kubectl ([0.36.2](https://github.com/kubernetes/kubectl/releases/tag/v0.36.2)) — Kubernetes CLI
* Azure CLI ([2.91.0](https://github.com/Azure/azure-cli/releases/tag/azure-cli-2.91.0)) — Microsoft Azure CLI
* Google Cloud SDK ([588.0.0](https://cloud.google.com/sdk/docs/release-notes#58800)) — `gcloud` with `alpha`, `beta`, and `gke-gcloud-auth-plugin` components
* Kubectl ([0.37.1](https://github.com/kubernetes/kubectl/releases/tag/v0.37.1)) — Kubernetes CLI
* Scalr CLI ([0.18.0](https://github.com/Scalr/scalr-cli/releases/tag/v0.18.0)) — command-line client for the Scalr API

### Added in the `-python39` image
Expand Down
20 changes: 10 additions & 10 deletions bump-versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ def _latest_python_version(series: str) -> tuple[str, str]:
return "", ""


def get_ubuntu_base_digest(image_ref: str) -> str:
"""Resolve a Docker Hub image reference (e.g. "ubuntu:26.04") to
def get_debian_base_digest(image_ref: str) -> str:
"""Resolve a Docker Hub image reference (e.g. "debian:trixie-slim") to
its current manifest digest. Uses Docker Hub's anonymous v2 registry API.
"""
name, _, tag = image_ref.partition(":")
Expand Down Expand Up @@ -385,18 +385,18 @@ def main() -> int:
base = read_versions(SECTION_BASE)
changes: list[tuple[str, str, str]] = []

# UBUNTU_BASE_IMAGE is read-only here — the base image (e.g. ubuntu:26.04)
# DEBIAN_BASE_IMAGE is read-only here — the base image (e.g. debian:trixie-slim)
# is a deliberate human choice and must not be auto-bumped. We only refresh the
# digest pinning for whatever image is currently configured.
ubuntu_image = base.get("UBUNTU_BASE_IMAGE", "")
if not ubuntu_image:
log.error(f"UBUNTU_BASE_IMAGE missing from {VERSIONS_FILE} ({SECTION_BASE})")
debian_image = base.get("DEBIAN_BASE_IMAGE", "")
if not debian_image:
log.error(f"DEBIAN_BASE_IMAGE missing from {VERSIONS_FILE} ({SECTION_BASE})")
return 1
bump(
"ubuntu_base_digest",
"UBUNTU_BASE_DIGEST",
get_ubuntu_base_digest(ubuntu_image),
base.get("UBUNTU_BASE_DIGEST", ""),
"debian_base_digest",
"DEBIAN_BASE_DIGEST",
get_debian_base_digest(debian_image),
base.get("DEBIAN_BASE_DIGEST", ""),
changes,
section=SECTION_BASE,
)
Expand Down
Loading
Loading