name: Build and Push on: push: branches: [main] # Don't rebuild on doc-only or CI-config-only changes paths-ignore: - 'README.md' - '.gitea/**' - 'deploy/**' workflow_dispatch: env: REGISTRY: registry.c5ai.ch IMAGE: pieced/pieced-portal jobs: build: # 'self-hosted' matches the label our act_runner registers with. # 'ubuntu-latest' would work too because we configure both labels in the # runner config, but self-hosted makes intent explicit. runs-on: ubuntu-latest env: DOCKER_HOST: tcp://172.17.0.1:2375 outputs: version: ${{ steps.version.outputs.version }} steps: - name: Checkout uses: actions/checkout@v4 - name: Determine next patch version id: version # Reads tags from the registry's OCI Distribution v2 API, filters to # strict semver (skips 'latest', 'dev', '-dirty', etc.), picks the # highest with version-sort, and bumps the patch component. If nothing # numeric exists yet (fresh registry), starts at 0.1.0. env: REG_USER: ${{ secrets.REGISTRY_USERNAME }} REG_PASS: ${{ secrets.REGISTRY_PASSWORD }} run: | set -euo pipefail tags_json=$(curl -sf -u "$REG_USER:$REG_PASS" \ "https://${REGISTRY}/v2/${IMAGE}/tags/list") highest=$(echo "$tags_json" \ | jq -r '.tags // [] | .[]' \ | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \ | sort -V \ | tail -n1 || true) if [ -z "$highest" ]; then next="0.1.0" echo "No semver tags found — starting at $next" else major=$(echo "$highest" | cut -d. -f1) minor=$(echo "$highest" | cut -d. -f2) patch=$(echo "$highest" | cut -d. -f3) next="${major}.${minor}.$((patch + 1))" echo "Highest existing: $highest → next: $next" fi echo "version=${next}" >> "$GITHUB_OUTPUT" - name: Install skopeo run: | apt-get update -qq && apt-get install -y -qq skopeo - name: Push with skopeo debug env: REG_USER: ${{ secrets.REGISTRY_USERNAME }} REG_PASS: ${{ secrets.REGISTRY_PASSWORD }} VERSION: ${{ steps.version.outputs.version }} run: | set -euo pipefail docker build --pull -t "${REGISTRY}/${IMAGE}:${VERSION}" . docker save "${REGISTRY}/${IMAGE}:${VERSION}" -o /tmp/image.tar AUTH=$(printf '%s:%s' "$REG_USER" "$REG_PASS" | base64 -w0) mkdir -p /tmp/auth printf '{"auths":{"%s":{"auth":"%s"}}}\n' "$REGISTRY" "$AUTH" > /tmp/auth/auth.json # --debug prints HTTP request/response details skopeo --debug copy --authfile /tmp/auth/auth.json \ "docker-archive:/tmp/image.tar" \ "docker://${REGISTRY}/${IMAGE}:${VERSION}" 2>&1 | tail -100 - name: Tag git commit with version env: VERSION: ${{ steps.version.outputs.version }} run: | set -euo pipefail git config user.name "pieced-ci" git config user.email "ci@pieced.ch" git tag -a "v${VERSION}" -m "Release ${VERSION}" # Use CI_TOKEN explicitly so we can push a tag (the workflow's # default token may or may not have push scope depending on Gitea # actions config — explicit token avoids ambiguity). git push \ "https://oauth2:${{ secrets.CI_TOKEN }}@git.c5ai.ch/pieced/pieced-portal.git" \ "v${VERSION}" - name: Summary env: VERSION: ${{ steps.version.outputs.version }} run: | { echo "## Build complete: ${VERSION}" echo echo "**Image:** \`${REGISTRY}/${IMAGE}:${VERSION}\`" echo echo "Run the **Deploy to GitOps** workflow to roll this version out." } >> "$GITHUB_STEP_SUMMARY"