From a56b730683efbb5c38702ce6bebc33f776e65696 Mon Sep 17 00:00:00 2001 From: Kerolos Atallah Date: Wed, 12 Aug 2026 06:24:13 +0300 Subject: [PATCH] Publish multi-architecture container image --- .dockerignore | 6 +++ .github/workflows/ci.yml | 39 ++++++++++++++++++ .github/workflows/container.yml | 71 +++++++++++++++++++++++++++++++++ Dockerfile | 35 ++++++++++++++++ README.md | 8 ++++ 5 files changed, 159 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/container.yml create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..42c9af0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.github +assets +result +*.md +*.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..904eb9c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + with: + go-version-file: go.mod + cache: true + - name: Test + run: go test -race ./... + - name: Vet + run: go vet ./... + - name: Build + run: go build ./cmd/outagedeck + + container: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - name: Build image + run: docker build --build-arg VERSION=ci --tag outagedeck-cli:ci . + - name: Check image + run: | + test "$(docker run --rm --network=none --read-only --cap-drop=ALL outagedeck-cli:ci version)" = "ci" + docker run --rm --network=none --read-only --cap-drop=ALL outagedeck-cli:ci alerts github cloudflare | grep -F "stack=github%2Ccloudflare" + test "$(docker image inspect outagedeck-cli:ci --format '{{.Config.User}}')" = "65532:65532" diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml new file mode 100644 index 0000000..1d90e26 --- /dev/null +++ b/.github/workflows/container.yml @@ -0,0 +1,71 @@ +name: Publish container + +on: + push: + tags: + - "v[0-9]*" + workflow_dispatch: + inputs: + version: + description: Application version without the v prefix + required: true + default: "0.1.3" + +permissions: + contents: read + packages: write + +concurrency: + group: container-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Resolve version + id: version + env: + REQUESTED_VERSION: ${{ inputs.version }} + run: | + version="${REQUESTED_VERSION:-${GITHUB_REF_NAME#v}}" + if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$ ]]; then + echo "Invalid version: $version" >&2 + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + + - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Sign in to GHCR + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Container metadata + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 + with: + images: ghcr.io/outagedeck/cli + tags: | + type=semver,pattern={{version}},value=${{ steps.version.outputs.version }} + type=semver,pattern={{major}}.{{minor}},value=${{ steps.version.outputs.version }} + type=raw,value=latest + + - name: Build and publish + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: VERSION=${{ steps.version.outputs.version }} + provenance: mode=max + sbom: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..71b8327 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM golang:1.24-alpine3.22@sha256:3641e0d9b931dc4f2f185dcd669c4679670e9277c8166a838ddb98a2d4389cb5 AS build + +ARG TARGETOS +ARG TARGETARCH +ARG VERSION=dev + +WORKDIR /src + +RUN apk add --no-cache ca-certificates + +COPY go.mod ./ +RUN go mod download + +COPY cmd ./cmd + +RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \ + go build -trimpath \ + -ldflags "-s -w -X main.version=${VERSION}" \ + -o /out/outagedeck ./cmd/outagedeck + +FROM scratch + +LABEL org.opencontainers.image.title="OutageDeck CLI" \ + org.opencontainers.image.description="Check live cloud and SaaS provider status from a terminal or CI job" \ + org.opencontainers.image.source="https://github.com/outagedeck/cli" \ + org.opencontainers.image.licenses="MIT" + +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=build /out/outagedeck /usr/local/bin/outagedeck + +USER 65532:65532 +ENTRYPOINT ["/usr/local/bin/outagedeck"] +CMD ["--help"] diff --git a/README.md b/README.md index 5bf46e8..b72b4ae 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,14 @@ With Nix: nix run github:outagedeck/cli -- --version ``` +From the multi-architecture container image: + +```bash +docker run --rm ghcr.io/outagedeck/cli:0.1.3 status --fail-on=never github cloudflare +``` + +The image runs as a non-root user, has no shell or package manager, and is published for Linux AMD64 and ARM64 with provenance and an SBOM. + In a Dev Container or Codespace, use the published [Dev Containers Extra feature](https://github.com/devcontainers-extra/features/tree/main/src/outagedeck): ```json