Init
All checks were successful
Checking yaml / Run yamllint (pull_request) Successful in 5s
Checking Renovate configuration / validate (pull_request) Successful in 1m54s
Build talosctl+talhelper+sops image (push on main only) / build (push) Successful in 6m0s

This commit is contained in:
Marc Plano-Lesay 2025-11-24 15:14:51 +11:00
parent 6d878b000a
commit d2a6f8d195
Signed by: kernald
GPG key ID: 66A41B08CC62A6CF
7 changed files with 403 additions and 2 deletions

View file

@ -0,0 +1,101 @@
---
# yamllint disable rule:line-length
name: Build talosctl+talhelper+sops image (push on main only)
on: # yamllint disable-line rule:truthy
push:
branches: ["**"]
workflow_dispatch: {}
env:
# Configure these in your repository Settings → Variables/Secrets
REGISTRY: ${{ vars.REGISTRY }}
# e.g. forgejo.example.com/owner/talos-tools
IMAGE_NAME: ${{ vars.IMAGE_NAME }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Extract component versions from Dockerfile
id: versions
run: |
set -euo pipefail
TALOSCTL_VERSION=$(grep -E "^ARG TALOSCTL_VERSION=" Dockerfile | head -n1 | cut -d'=' -f2)
TALHELPER_VERSION=$(grep -E "^ARG TALHELPER_VERSION=" Dockerfile | head -n1 | cut -d'=' -f2)
SOPS_VERSION=$(grep -E "^ARG SOPS_VERSION=" Dockerfile | head -n1 | cut -d'=' -f2)
echo "talosctl=${TALOSCTL_VERSION}" >> "$GITHUB_OUTPUT"
echo "talhelper=${TALHELPER_VERSION}" >> "$GITHUB_OUTPUT"
echo "sops=${SOPS_VERSION}" >> "$GITHUB_OUTPUT"
TAG="v${TALOSCTL_VERSION}-${TALHELPER_VERSION}-${SOPS_VERSION}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to registry (main only)
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push image (main only)
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
provenance: false
cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache
cache-to: type=registry,ref=${{ env.IMAGE_NAME }}:buildcache,mode=max
build-args: |
TALOSCTL_VERSION=${{ steps.versions.outputs.talosctl }}
TALHELPER_VERSION=${{ steps.versions.outputs.talhelper }}
SOPS_VERSION=${{ steps.versions.outputs.sops }}
tags: |
${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }}
${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
- name: Build without push (branches other than main)
if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master'
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
load: true
push: false
provenance: false
build-args: |
TALOSCTL_VERSION=${{ steps.versions.outputs.talosctl }}
TALHELPER_VERSION=${{ steps.versions.outputs.talhelper }}
SOPS_VERSION=${{ steps.versions.outputs.sops }}
tags: |
${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }}
labels: |
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
- name: "Smoke test (main: run pushed image)"
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
run: |
docker run --rm ${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }} sh -lc \
'talosctl version --client && talhelper --version && sops --version'
- name: "Smoke test (branch: run locally loaded image)"
if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master'
run: |
docker run --rm ${{ env.IMAGE_NAME }}:${{ steps.versions.outputs.tag }} sh -lc \
'talosctl version --client && talhelper --version && sops --version'
...

View file

@ -0,0 +1,24 @@
---
name: Checking Renovate configuration
on: # yamllint disable-line rule:truthy
pull_request:
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Cache npm (renovate)
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-renovate
restore-keys: |
${{ runner.os }}-npm-renovate-
- name: Validate Renovate configuration
uses: suzuki-shunsuke/github-action-renovate-config-validator@v1.1.1
env:
NPM_CONFIG_CACHE: ~/.npm
...

View file

@ -0,0 +1,18 @@
---
name: Checking yaml
on: # yamllint disable-line rule:truthy
pull_request:
jobs:
yamllint:
name: Run yamllint
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Run YAML linter
uses: bewuethr/yamllint-action@v1
with:
config-file: .yamllint.yaml
...

30
.yamllint.yaml Normal file
View file

@ -0,0 +1,30 @@
---
ignore: |
.platformio
secrets.yaml
rules:
braces: enable
brackets: enable
colons: enable
commas: enable
comments: enable
comments-indentation: enable
document-end: enable
document-start: enable
empty-lines:
max: 1
empty-values: disable
hyphens: enable
indentation: enable
key-duplicates: enable
key-ordering: disable
line-length:
max: 100
new-line-at-end-of-file: enable
new-lines: enable
octal-values: enable
quoted-strings: disable
trailing-spaces: enable
truthy: enable
...

128
Dockerfile Normal file
View file

@ -0,0 +1,128 @@
# syntax=docker/dockerfile:1.7
# Build an OCI image that provides:
# - talosctl
# - talhelper
# - sops
#
# Versions are controlled by build ARGs below. Renovate is configured to bump
# these ARGs automatically. The CI workflow builds the image and tags it with a
# composite version tag reflecting all three component versions.
# renovate: datasource=github-releases depName=siderolabs/talos versioning=semver
ARG TALOSCTL_VERSION=1.9.2
# renovate: datasource=github-releases depName=budimanjojo/talhelper versioning=semver
ARG TALHELPER_VERSION=3.0.39
# renovate: datasource=github-releases depName=getsops/sops versioning=semver
ARG SOPS_VERSION=3.11.0
# renovate: datasource=docker depName=cgr.dev/chainguard/wolfi-base
FROM cgr.dev/chainguard/wolfi-base:latest AS downloader
ARG TALOSCTL_VERSION
ARG TALHELPER_VERSION
ARG SOPS_VERSION
RUN set -eux; \
apk add --no-cache curl ca-certificates-bundle
# Map Docker TARGETARCH to upstream asset architecture naming where needed.
ARG TARGETARCH
RUN set -eux; \
case "${TARGETARCH}" in \
amd64) TALOS_ARCH=amd64; TALHELPER_ARCH=amd64; SOPS_ARCH=amd64 ;; \
arm64) TALOS_ARCH=arm64; TALHELPER_ARCH=arm64; SOPS_ARCH=arm64 ;; \
*) echo "Unsupported TARGETARCH=${TARGETARCH}"; exit 1 ;; \
esac; \
echo "TALOS_ARCH=${TALOS_ARCH}" > /tmp/arches.env; \
echo "TALHELPER_ARCH=${TALHELPER_ARCH}" >> /tmp/arches.env; \
echo "SOPS_ARCH=${SOPS_ARCH}" >> /tmp/arches.env
SHELL ["/bin/sh", "-c"]
# Download talosctl and verify checksum
RUN . /tmp/arches.env; \
set -eux; \
TALOS_URL="https://github.com/siderolabs/talos/releases/download/v${TALOSCTL_VERSION}/talosctl-linux-${TALOS_ARCH}"; \
curl -fsSL -o /tmp/talosctl "${TALOS_URL}"; \
chmod +x /tmp/talosctl; \
if curl -fsSL -o /tmp/talosctl.sha256 "${TALOS_URL}.sha256"; then \
TALOS_SHA=$(tr -d ' \n\r' < /tmp/talosctl.sha256); \
else \
curl -fsSL -o /tmp/talos_checksums.txt "https://github.com/siderolabs/talos/releases/download/v${TALOSCTL_VERSION}/sha256sum.txt"; \
TALOS_SHA=$(grep "$(basename ${TALOS_URL})" /tmp/talos_checksums.txt | awk '{print $1}' | tr -d ' \n\r'); \
fi; \
echo "${TALOS_SHA} /tmp/talosctl" | sha256sum -c -; \
echo "${TALOS_URL}" > /tmp/talosctl.src; \
echo "${TALOS_SHA}" > /tmp/talosctl.sha
# Download talhelper (tar.gz containing the binary) and verify checksum
RUN . /tmp/arches.env; \
set -eux; \
TALHELPER_TGZ_URL="https://github.com/budimanjojo/talhelper/releases/download/v${TALHELPER_VERSION}/talhelper_linux_${TALHELPER_ARCH}.tar.gz"; \
curl -fsSL -o /tmp/talhelper.tgz "${TALHELPER_TGZ_URL}"; \
if curl -fsSL -o /tmp/talhelper.tgz.sha256 "${TALHELPER_TGZ_URL}.sha256"; then \
TALHELPER_TGZ_SHA=$(tr -d ' \n\r' < /tmp/talhelper.tgz.sha256); \
else \
curl -fsSL -o /tmp/talhelper_checksums.txt "https://github.com/budimanjojo/talhelper/releases/download/v${TALHELPER_VERSION}/checksums.txt"; \
TALHELPER_TGZ_SHA=$(grep "$(basename ${TALHELPER_TGZ_URL})" /tmp/talhelper_checksums.txt | awk '{print $1}' | tr -d ' \n\r'); \
fi; \
echo "${TALHELPER_TGZ_SHA} /tmp/talhelper.tgz" | sha256sum -c -; \
mkdir -p /tmp/talhelper && tar -xzf /tmp/talhelper.tgz -C /tmp/talhelper; \
mv /tmp/talhelper/talhelper /tmp/talhelper.bin; \
chmod +x /tmp/talhelper.bin; \
echo "${TALHELPER_TGZ_URL}" > /tmp/talhelper.src; \
echo "${TALHELPER_TGZ_SHA}" > /tmp/talhelper.sha
# Download sops and verify checksum
RUN . /tmp/arches.env; \
set -eux; \
SOPS_URL="https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.${SOPS_ARCH}"; \
curl -fsSL -o /tmp/sops "${SOPS_URL}"; \
chmod +x /tmp/sops; \
if curl -fsSL -o /tmp/sops.sha256 "${SOPS_URL}.sha256"; then \
SOPS_SHA=$(tr -d ' \n\r' < /tmp/sops.sha256); \
else \
curl -fsSL -o /tmp/sops_checksums.txt "https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.checksums.txt"; \
SOPS_SHA=$(grep "$(basename ${SOPS_URL})" /tmp/sops_checksums.txt | awk '{print $1}' | tr -d ' \n\r'); \
fi; \
echo "${SOPS_SHA} /tmp/sops" | sha256sum -c -; \
echo "${SOPS_URL}" > /tmp/sops.src; \
echo "${SOPS_SHA}" > /tmp/sops.sha
# renovate: datasource=docker depName=cgr.dev/chainguard/wolfi-base
FROM cgr.dev/chainguard/wolfi-base:latest
ARG TALOSCTL_VERSION
ARG TALHELPER_VERSION
ARG SOPS_VERSION
LABEL org.opencontainers.image.title="talosctl + talhelper + sops"
LABEL org.opencontainers.image.description="Utility image containing talosctl, talhelper, and sops"
LABEL org.opencontainers.image.source="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.version.talosctl="${TALOSCTL_VERSION}"
LABEL org.opencontainers.image.version.talhelper="${TALHELPER_VERSION}"
LABEL org.opencontainers.image.version.sops="${SOPS_VERSION}"
LABEL org.opencontainers.image.url.talosctl="https://github.com/siderolabs/talos"
LABEL org.opencontainers.image.url.talhelper="https://github.com/budimanjojo/talhelper"
LABEL org.opencontainers.image.url.sops="https://github.com/getsops/sops"
RUN set -eux; \
apk add --no-cache ca-certificates-bundle bash git openssh-client; \
mkdir -p /usr/local/share/checksums
COPY --from=downloader /tmp/talosctl /usr/local/bin/talosctl
COPY --from=downloader /tmp/talhelper.bin /usr/local/bin/talhelper
COPY --from=downloader /tmp/sops /usr/local/bin/sops
COPY --from=downloader /tmp/*.sha /usr/local/share/checksums/
COPY --from=downloader /tmp/*.src /usr/local/share/checksums/
RUN set -eux; \
chmod +x /usr/local/bin/talosctl /usr/local/bin/talhelper /usr/local/bin/sops
ENV PAGER=cat
# Print versions by default so users can see what's inside quickly.
CMD talosctl version --client && talhelper --version && sops --version

View file

@ -1,3 +1,56 @@
# talos # talos tools image
talosctl + talhelper +sops image An OCI image (Wolfi-based) containing:
- talosctl
- talhelper
- sops
The image is built on Wolfi to keep size and surface area minimal, then built via Forgejo Actions. On pushes to `main` (or `master`), the image is pushed to your Forgejo container registry. On other branches, the workflow builds the image but does not push it (to validate PRs). The published tag encodes the versions of all three tools:
- Tag format: `v<TALOSCTL>-<TALHELPER>-<SOPS>` (for example: `v1.9.2-3.0.39-3.11.0`), plus `latest`.
Contents are defined in `Dockerfile`. Versions are pinned via build `ARG`s so they can be updated automatically by Renovate.
Additionally, the build verifies SHA256 checksums for all downloaded binaries and includes their source URLs and checksums inside the image at `/usr/local/share/checksums/`.
Setup
1) Configure Forgejo variables and secrets
- Repository Variables (Settings → Variables):
- `REGISTRY`: the registry hostname, e.g. `forgejo.example.com`
- `IMAGE_NAME`: full image name including registry and path, e.g. `forgejo.example.com/owner/talos-tools`
- Repository Secrets (Settings → Secrets):
- `REGISTRY_USERNAME`: username with push permission to the registry
- `REGISTRY_PASSWORD`: the password or token
2) Enable the workflow
The workflow file `.forgejo/workflows/build.yml` runs on any branch push and on manual dispatch.
- On `main`/`master`: it builds a multi-arch image (linux/amd64, linux/arm64) and pushes it, using a registry-backed Buildx cache (no extra infra needed).
- On other branches: it builds the image (no push) to ensure the change is buildable before merging.
3) Renovate configuration
`renovate.json` configures Renovate to track GitHub releases and update the version `ARG`s in the `Dockerfile` for:
- `siderolabs/talos` (talosctl)
- `budimanjojo/talhelper`
- `getsops/sops`
When Renovate opens a PR and it is merged, the workflow will build and push a new image. The tag is computed from the three versions in the `Dockerfile`.
Local build
To build locally (example versions):
```
docker build \
--build-arg TALOSCTL_VERSION=1.9.2 \
--build-arg TALHELPER_VERSION=3.0.39 \
--build-arg SOPS_VERSION=3.11.0 \
-t talos-tools:dev .
```
Image usage
```
docker run --rm -it your.registry/owner/talos-tools:v1.9.2-3.0.39-3.11.0 talosctl version --client
```
Notes
- The workflow uses Docker Buildx and QEMU to produce multi-arch images.
- The Dockerfile maps architectures to the upstream asset names as required (e.g., talhelper uses `amd64` and `arm64` asset names).
- Base image: `cgr.dev/chainguard/wolfi-base:latest` for a smaller footprint than Alpine. Renovate is enabled to track and pin the base image digest automatically.
- Runtime shell: `bash` is included because this image is intended to be used in Forgejo Actions jobs that require a shell.
- Integrity: SHA256 checksum verification is performed during the image build for `talosctl`, `talhelper` (tarball), and `sops`.

47
renovate.json Normal file
View file

@ -0,0 +1,47 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"assignees": ["kernald"],
"extends": [
"config:recommended"
],
"enabledManagers": ["custom.regex", "dockerfile"],
"pinDigests": true,
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^Dockerfile$"],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>[^\\s]+)\\s+depName=(?<depName>[^\\s]+)\\s+versioning=(?<versioning>[^\\s]+)\\nARG\\s+TALOSCTL_VERSION=(?<currentValue>[^\\n]+)"
],
"datasourceTemplate": "{{{datasource}}}",
"depNameTemplate": "{{{depName}}}",
"versioningTemplate": "{{{versioning}}}",
"extractVersionTemplate": "^v?(?<version>.*)$",
"autoReplaceStringTemplate": "ARG TALOSCTL_VERSION={{newValue}}"
},
{
"customType": "regex",
"fileMatch": ["^Dockerfile$"],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>[^\\s]+)\\s+depName=(?<depName>[^\\s]+)\\s+versioning=(?<versioning>[^\\s]+)\\nARG\\s+TALHELPER_VERSION=(?<currentValue>[^\\n]+)"
],
"datasourceTemplate": "{{{datasource}}}",
"depNameTemplate": "{{{depName}}}",
"versioningTemplate": "{{{versioning}}}",
"extractVersionTemplate": "^v?(?<version>.*)$",
"autoReplaceStringTemplate": "ARG TALHELPER_VERSION={{newValue}}"
},
{
"customType": "regex",
"fileMatch": ["^Dockerfile$"],
"matchStrings": [
"#\\s*renovate:\\s*datasource=(?<datasource>[^\\s]+)\\s+depName=(?<depName>[^\\s]+)\\s+versioning=(?<versioning>[^\\s]+)\\nARG\\s+SOPS_VERSION=(?<currentValue>[^\\n]+)"
],
"datasourceTemplate": "{{{datasource}}}",
"depNameTemplate": "{{{depName}}}",
"versioningTemplate": "{{{versioning}}}",
"extractVersionTemplate": "^v?(?<version>.*)$",
"autoReplaceStringTemplate": "ARG SOPS_VERSION={{newValue}}"
}
]
}