diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..0c3462b --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,122 @@ +# domogeek image without Docker nor privileges (restricted job pod): +# Go cross-compilation (CGO off, netgo, vendored) for amd64/arm64 in the job, then +# `crane append` of the binary onto the pinned distroless/static base per arch, entrypoint and +# user set with `crane mutate`, `crane index append`, CycloneDX SBOM -> DependencyTrack. +# Tag convention unchanged (vX.Y.Z): image git.cyrilix.bzh/cyrilix/domogeek: (+ -amd64/-arm64). +name: build +on: + push: + tags: ["v[0-9]*"] + workflow_dispatch: # publishes test- + +permissions: + contents: read + +env: + REGISTRY: git.cyrilix.bzh + IMAGE: git.cyrilix.bzh/cyrilix/domogeek + # Same base as the Dockerfile (distroless/static), pinned multi-arch index. + BASE: gcr.io/distroless/static-debian12@sha256:d75cdd72874d4790092fcb1b058493ecf6bb5bf2b2b897045b00ff01d91843f2 + # Toolchain of the Dockerfile builder (golang:1.27); go.mod only requires go 1.25. + GO_VERSION: "1.27" + CRANE_VERSION: v0.22.1 + CRANE_SHA256: 0ab7a1d6932a213aed964ce97666c3077fe691c8606413674a8b3e0b9ec4cda0 + SYFT_VERSION: 1.54.1 + SYFT_SHA256: c069905b391cc4c20a5ba65ad5c10be2a7ba074f8ea6ad203e24d14e303dad47 + DT_BASE_URL: http://dependency-track-dt-api-server.dependency-track.svc.cluster.local:8080 + +jobs: + build: + runs-on: banquise-amd64 + # 512Mi job pods: bound the Go runtime (compiler, crane, syft). + env: + GOMEMLIMIT: 350MiB + GOGC: "25" + steps: + - uses: builtin:checkout + + - name: tag + id: meta + run: | + set -eu + case "$GITHUB_REF" in + refs/tags/*) tag="${GITHUB_REF#refs/tags/}" ;; + *) tag="test-$(echo "$GITHUB_SHA" | cut -c1-8)" ;; + esac + echo "tag=$tag" | tee -a "$GITHUB_OUTPUT" + + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: ${{ env.GO_VERSION }} + check-latest: true + cache: false # vendor/ is committed + + - name: build binaries (cross-compiled, no qemu) + run: | + set -eu + for arch in amd64 arm64; do + mkdir -p "layer-$arch/go/bin" + CGO_ENABLED=0 GOOS=linux GOARCH=$arch go build -p 2 -mod=vendor -tags netgo -trimpath -ldflags="-s -w" \ + -o "layer-$arch/go/bin/domogeek" ./cmd/domogeek + done + go version -m layer-amd64/go/bin/domogeek | head -2 + + - name: install crane and syft (pinned, checksum-verified) + run: | + set -eu + mkdir -p "$HOME/bin" && cd "$(mktemp -d)" + curl -fsSLo crane.tgz "https://github.com/google/go-containerregistry/releases/download/${CRANE_VERSION}/go-containerregistry_Linux_x86_64.tar.gz" + echo "${CRANE_SHA256} crane.tgz" | sha256sum -c - + tar -xzf crane.tgz crane && mv crane "$HOME/bin/" + curl -fsSLo syft.tgz "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" + echo "${SYFT_SHA256} syft.tgz" | sha256sum -c - + tar -xzf syft.tgz syft && mv syft "$HOME/bin/" + echo "$HOME/bin" >> "$GITHUB_PATH" + + # Gitea 28.1 rejects the job token on the container registry (empty access scope for + # Actions task tokens) -> dedicated PAT of the package owner, scope write:package only. + - name: registry login + run: echo "${{ secrets.REGISTRY_TOKEN }}" | crane auth login "$REGISTRY" -u cyrilix --password-stdin + + - name: image per arch, then the multi-arch index + run: | + set -eu + tag="${{ steps.meta.outputs.tag }}" + epoch=$(git log -1 --format=%ct) + for arch in amd64 arm64; do + tar --sort=name --owner=0 --group=0 --numeric-owner --mtime="@$epoch" -C "layer-$arch" -cf "app-$arch.tar" . + crane append --platform "linux/$arch" -b "$BASE" -f "app-$arch.tar" -t "$IMAGE:$tag-$arch" + crane mutate "$IMAGE:$tag-$arch" --entrypoint /go/bin/domogeek --user 1234 --exposed-ports 8080/tcp \ + --label org.opencontainers.image.source=https://git.cyrilix.bzh/cyrilix/domogeek \ + --label org.opencontainers.image.version="$tag" + done + crane index append -t "$IMAGE:$tag" -m "$IMAGE:$tag-amd64" -m "$IMAGE:$tag-arm64" + echo "index digest: $(crane digest "$IMAGE:$tag")" + + - name: SBOM (syft, CycloneDX) + env: + SYFT_CHECK_FOR_APP_UPDATE: "false" + run: | + set -eu + syft "registry:$IMAGE:${{ steps.meta.outputs.tag }}" --platform linux/amd64 -o cyclonedx-json=sbom.cdx.json + echo "components: $(grep -o '"bom-ref"' sbom.cdx.json | wc -l)" + + - name: upload SBOM to DependencyTrack + env: + DT_API_KEY: ${{ secrets.DT_API_KEY }} + run: | + set -eu + name="${IMAGE#*/}" + version="${{ steps.meta.outputs.tag }}" + code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 30 -X PUT \ + -H "X-Api-Key: $DT_API_KEY" -H 'Content-Type: application/json' \ + -d '{"name":"oci","active":true,"collectionLogic":"AGGREGATE_DIRECT_CHILDREN"}' \ + "$DT_BASE_URL/api/v1/project") + case "$code" in 201|409) ;; *) echo "parent project 'oci': HTTP $code"; exit 1 ;; esac + { printf '{"parentName":"oci","projectName":"%s","projectVersion":"%s","autoCreate":true,"bom":"' "$name" "$version" + base64 -w0 sbom.cdx.json; printf '"}'; } > dt-payload.json + code=$(curl -s -o dt-response.json -w '%{http_code}' --max-time 120 -X PUT \ + -H "X-Api-Key: $DT_API_KEY" -H 'Content-Type: application/json' \ + -d @dt-payload.json "$DT_BASE_URL/api/v1/bom") + [ "$code" = 200 ] || { echo "BOM upload: HTTP $code"; cat dt-response.json; exit 1; } + echo "DependencyTrack: $name $version accepted" diff --git a/CLAUDE.md b/CLAUDE.md index 66b57bd..67462c6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,29 +45,28 @@ docker build -t domogeek . # or: buildah bud -t domogeek . ## Continuous builds -Pushing a Git tag matching `v[0-9]*` (existing convention: `vX.Y.Z`, e.g. `v0.4.0`) to -the Gitea remote triggers the homelab Tekton pipeline `gitea-docker-build-multiarch`, -which builds and pushes a multi-arch (amd64 + arm64) image plus a signed CycloneDX SBOM: +Pushing a Git tag matching `v[0-9]*` (convention: `vX.Y.Z`, e.g. `v0.4.0`) to the Gitea remote +triggers the **Gitea Actions** workflow `.gitea/workflows/build.yaml` (homelab runner, no Docker, +no privileges): the binary is cross-compiled for amd64/arm64 (CGO off, `netgo`, vendored, Go 1.27 = +`GO_VERSION` in the workflow), appended onto the pinned `gcr.io/distroless/static-debian12` base +with `crane` (entrypoint `/go/bin/domogeek`, user 1234, port 8080), and published as a multi-arch +image plus a CycloneDX SBOM uploaded to DependencyTrack (no cosign signature anymore): ``` git.cyrilix.bzh/cyrilix/domogeek: ``` -The pipeline uses its defaults — `Dockerfile` at the repo root, build context `.` — so -the Dockerfile must stay at the root. It builds each target platform natively (no -`GOOS`/`GOARCH` overrides in the `go build` step), so the Dockerfile must not hardcode a -single-arch `GOOS`/`GOARCH` — that was a real bug fixed alongside this: the builder image -is now pinned to `golang:1.27-alpine` (matching the `go 1.25` module requirement) and the -`go build` step targets the whole package (`./cmd/domogeek`), not a single file. - -There is no Tekton YAML in this repo: the pipeline and its Gitea webhook trigger are -defined cluster-side. Watch a run: +A manual `workflow_dispatch` run publishes `test-`. The `Dockerfile` remains for local builds +(`docker build -t domogeek .`) but is not used by CI — keep its base/user/entrypoint in sync with the +workflow. Migrated from the Tekton pipeline (and its Gitea webhook) on 2026-10-13. ```sh -git tag v0.4.0 +git tag v0.4.1 git push --tags ``` +Watch the run: Gitea → `cyrilix/domogeek` → Actions. + Note: this replaces the previous `docker.io/cyrilix/domogeek:` releases built locally via the now-removed `build-docker.sh`/`buildah` script. diff --git a/README.md b/README.md index 7e9faf1..7e67a92 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,17 @@ and date/time information as MCP tools (`get_current_datetime`, `get_calendar_to ## Continuous builds -Pushing a Git tag matching `v[0-9]*` (e.g. `v0.4.0`) to the Gitea remote triggers the -homelab Tekton pipeline `gitea-docker-build-multiarch`, which builds and pushes a -multi-arch (amd64 + arm64) image plus a signed SBOM: +Pushing a Git tag matching `v[0-9]*` (e.g. `v0.4.1`) to the Gitea remote triggers the Gitea Actions +workflow `.gitea/workflows/build.yaml` (no Docker, no privileges), which builds and pushes a +multi-arch (amd64 + arm64) image plus a CycloneDX SBOM to DependencyTrack: ``` git.cyrilix.bzh/cyrilix/domogeek: ``` ```sh -git tag v0.4.0 +git tag v0.4.1 git push --tags ``` -Watch the run: - +Watch the run: Gitea → `cyrilix/domogeek` → Actions.