diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 19e5077..dc39435 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -22,13 +22,30 @@ # ☐ Also push 'latest' tag # # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Usage Examples: +# Automatic Triggers: +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# +# 1. Push to master (regular commit): +# → Auto-build + push +# → Tags: commit_sha only (e.g., qdrvm/qlean-mini:59b2c37) +# → Latest: NOT pushed +# +# 2. Git tag push (release, e.g., v1.0.0): +# → Auto-build + push +# → Tags: commit_sha + tag_name + latest +# → Example: 59b2c37, v1.0.0, latest +# +# 3. Pull request: +# → Build only, no push +# +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Manual Trigger Examples (workflow_dispatch): # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # # Scenario 1: Daily development (test build only) # ☑ amd64 ☑ arm64 ☐ push ☐ build_deps → Uses deps:latest from registry # -# Scenario 2: Production release (with new deps) +# Scenario 2: Production release (manual) # ☑ build_deps deps_tag: v2 ☑ push custom_tag: v1.0.0 ☑ push_latest # # Scenario 3: Hotfix (ARM64 only, no deps rebuild) @@ -45,12 +62,12 @@ name: "Docker Build" on: push: branches: - - ci/docker # TODO: Add master after testing + - master # Auto-build on master push tags: - - '*' + - '*' # Auto-build on any git tag (release) pull_request: branches: - - ci/docker # TODO: Add master after testing + - master workflow_dispatch: inputs: @@ -105,13 +122,37 @@ env: DOCKER_REGISTRY: qdrvm DOCKER_IMAGE_NAME: qlean-mini DOCKER_DEPS_TAG: ${{ inputs.deps_tag || 'latest' }} - DOCKER_PUSH_TAG: ${{ inputs.custom_tag != '' && 'true' || 'false' }} - DOCKER_IMAGE_TAG: ${{ inputs.custom_tag || 'localBuild' }} - DOCKER_PUSH_LATEST: ${{ inputs.push_to_registry && inputs.push_latest && 'true' || 'false' }} GIT_COMMIT: ${{ github.sha }} - # Auto-push configuration based on event type - AUTO_PUSH: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/ci/docker' || startsWith(github.ref, 'refs/tags/')) }} + # Tagging logic: + # - Manual trigger: use inputs.custom_tag + # - Git tag (release): use tag name (e.g., v1.0.0) + # - Master/other: no custom tag + DOCKER_PUSH_TAG: ${{ + github.event_name == 'workflow_dispatch' && inputs.custom_tag != '' && 'true' || + startsWith(github.ref, 'refs/tags/') && 'true' || + 'false' }} + DOCKER_IMAGE_TAG: ${{ + github.event_name == 'workflow_dispatch' && inputs.custom_tag || + startsWith(github.ref, 'refs/tags/') && github.ref_name || + 'localBuild' }} + + # Latest tag: only for git releases OR manual with push_latest enabled + DOCKER_PUSH_LATEST: ${{ + startsWith(github.ref, 'refs/tags/') && 'true' || + github.event_name == 'workflow_dispatch' && inputs.push_to_registry && inputs.push_latest && 'true' || + 'false' }} + + # Auto-push configuration: + # - master branch: push (no latest) + # - git tags: push (with latest) + # - ci/docker: push (testing) + # - pull requests: no push + AUTO_PUSH: ${{ + github.event_name == 'push' && + (github.ref == 'refs/heads/master' || + github.ref == 'refs/heads/ci/docker' || + startsWith(github.ref, 'refs/tags/')) }} IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }} jobs: @@ -174,7 +215,25 @@ jobs: - name: "Debug outputs" run: | - echo "=== Setup Matrix Outputs ===" + echo "=== Build Configuration ===" + echo "Event: ${{ github.event_name }}" + echo "Ref: ${{ github.ref }}" + echo "Ref name: ${{ github.ref_name }}" + echo "" + echo "=== Docker Tags ===" + echo "Commit tag: ${GIT_COMMIT:0:7} (always pushed)" + if [[ "${DOCKER_PUSH_TAG}" == "true" ]]; then + echo "Custom tag: ${DOCKER_IMAGE_TAG} ✅" + else + echo "Custom tag: (none)" + fi + if [[ "${DOCKER_PUSH_LATEST}" == "true" ]]; then + echo "Latest tag: ✅ (will be pushed)" + else + echo "Latest tag: ❌ (not pushed)" + fi + echo "" + echo "=== Matrix Outputs ===" echo "is_multi_arch: ${{ steps.matrix.outputs.is_multi_arch }}" echo "should_push: ${{ steps.config.outputs.should_push }}" echo "should_build_deps: ${{ steps.config.outputs.should_build_deps }}" @@ -353,16 +412,28 @@ jobs: SHORT_COMMIT=$(git rev-parse --short HEAD) echo "" - echo "Runtime manifest (${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT}):" - - # Get manifest and extract platforms + echo "[1/N] Commit tag manifest (${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT}):" MANIFEST=$(docker manifest inspect ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT}) echo "$MANIFEST" | jq -r '.manifests[] | " • \(.platform.os)/\(.platform.architecture)"' || echo "$MANIFEST" + if [[ "${DOCKER_PUSH_TAG}" == "true" ]]; then + echo "" + echo "[2/N] Custom tag manifest (${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}):" + MANIFEST=$(docker manifest inspect ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}) + echo "$MANIFEST" | jq -r '.manifests[] | " • \(.platform.os)/\(.platform.architecture)"' || echo "$MANIFEST" + fi + + if [[ "${DOCKER_PUSH_LATEST}" == "true" ]]; then + echo "" + echo "[3/N] Latest tag manifest (${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:latest):" + MANIFEST=$(docker manifest inspect ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:latest) + echo "$MANIFEST" | jq -r '.manifests[] | " • \(.platform.os)/\(.platform.architecture)"' || echo "$MANIFEST" + fi + echo "" echo "NOTE: Builder image is NOT pushed to registry (intermediate build stage only)" echo "" - echo "✅ Runtime multi-arch manifest created successfully!" + echo "✅ All multi-arch manifests created successfully!" - name: "Display final image tags" run: | @@ -370,26 +441,28 @@ jobs: echo "=== Successfully pushed Docker images ===" echo "" - echo "🐳 Runtime image:" - echo " ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT}" + echo "🐳 Runtime multi-arch images:" + echo " • ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT} (commit)" if [[ "${DOCKER_PUSH_TAG}" == "true" ]]; then - echo " ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG}" + echo " • ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG} (custom tag)" fi if [[ "${DOCKER_PUSH_LATEST}" == "true" ]]; then - echo " ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:latest" + echo " • ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:latest" fi if [[ "${{ needs.setup_matrix.outputs.should_build_deps }}" == "true" ]]; then echo "" echo "📦 Dependencies images (platform-specific):" - echo " ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}-dependencies:${DOCKER_DEPS_TAG}-arm64" - echo " ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}-dependencies:${DOCKER_DEPS_TAG}-amd64" + echo " • ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}-dependencies:${DOCKER_DEPS_TAG}-arm64" + echo " • ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}-dependencies:${DOCKER_DEPS_TAG}-amd64" echo " (Note: dependencies are NOT multi-arch, each platform uses its own)" fi echo "" - echo "Pull with: docker pull ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT}" - echo "Run with: docker run --rm ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT} --help" + echo "📋 Quick commands:" + echo " Pull: docker pull ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT}" + echo " Run: docker run --rm ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${SHORT_COMMIT} --help" + diff --git a/Makefile b/Makefile index c530372..f94835c 100644 --- a/Makefile +++ b/Makefile @@ -433,11 +433,25 @@ docker_push_platform: echo "ERROR: Unknown platform $(DOCKER_PLATFORM)"; \ exit 1; \ fi; \ - echo "Pushing runtime..."; \ + echo "[1/N] Pushing commit tag ($(GIT_COMMIT)$$ARCH_SUFFIX)..."; \ docker tag $(DOCKER_IMAGE_RUNTIME) $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)$$ARCH_SUFFIX; \ docker push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)$$ARCH_SUFFIX; \ echo "✓ Pushed: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)$$ARCH_SUFFIX"; \ echo ""; \ + if [ "$(DOCKER_PUSH_TAG)" = "true" ]; then \ + echo "[2/N] Pushing custom tag ($(DOCKER_IMAGE_TAG)$$ARCH_SUFFIX)..."; \ + docker tag $(DOCKER_IMAGE_RUNTIME) $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)$$ARCH_SUFFIX; \ + docker push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)$$ARCH_SUFFIX; \ + echo "✓ Pushed: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)$$ARCH_SUFFIX"; \ + echo ""; \ + fi; \ + if [ "$(DOCKER_PUSH_LATEST)" = "true" ]; then \ + echo "[3/N] Pushing latest tag (latest$$ARCH_SUFFIX)..."; \ + docker tag $(DOCKER_IMAGE_RUNTIME) $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest$$ARCH_SUFFIX; \ + docker push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest$$ARCH_SUFFIX; \ + echo "✓ Pushed: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest$$ARCH_SUFFIX"; \ + echo ""; \ + fi; \ echo "✓ Platform images pushed to $(DOCKER_REGISTRY)!" # NOTE: Dependencies are platform-specific and do NOT have a multi-arch manifest. @@ -445,24 +459,51 @@ docker_push_platform: # This is intentional to simplify CI/CD and avoid unnecessary manifest overhead. docker_manifest_create: - @echo "=== Creating multi-arch manifest for runtime image ===" + @echo "=== Creating multi-arch manifests for runtime image ===" @echo "Registry: $(DOCKER_REGISTRY)" @echo "" @echo "NOTE: Builder image is NOT pushed to registry (intermediate build stage only)" @echo "NOTE: Dependencies are platform-specific (deps:latest-arm64, deps:latest-amd64)" @echo "" - @echo "[1/2] Creating runtime manifest..." + @echo "[1/N] Creating commit tag manifest ($(GIT_COMMIT))..." @docker manifest rm $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME) 2>/dev/null || true @docker manifest create $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME) \ --amend $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)-arm64 \ --amend $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)-amd64 - @echo "" - @echo "[2/2] Pushing runtime manifest..." @docker manifest push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME) + @echo "✓ Pushed manifest: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)" + @echo "" + @if [ "$(DOCKER_PUSH_TAG)" = "true" ]; then \ + echo "[2/N] Creating custom tag manifest ($(DOCKER_IMAGE_TAG))..."; \ + docker manifest rm $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 2>/dev/null || true; \ + docker manifest create $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) \ + --amend $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)-arm64 \ + --amend $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)-amd64; \ + docker manifest push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG); \ + echo "✓ Pushed manifest: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"; \ + echo ""; \ + fi + @if [ "$(DOCKER_PUSH_LATEST)" = "true" ]; then \ + echo "[3/N] Creating latest tag manifest..."; \ + docker manifest rm $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest 2>/dev/null || true; \ + docker manifest create $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest \ + --amend $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest-arm64 \ + --amend $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest-amd64; \ + docker manifest push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest; \ + echo "✓ Pushed manifest: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest"; \ + echo ""; \ + fi + @echo "✓ Multi-arch runtime manifests created successfully!" @echo "" - @echo "✓ Multi-arch runtime manifest created successfully!" + @echo "Images pushed:" + @echo " • $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)" + @if [ "$(DOCKER_PUSH_TAG)" = "true" ]; then \ + echo " • $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"; \ + fi + @if [ "$(DOCKER_PUSH_LATEST)" = "true" ]; then \ + echo " • $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_NAME):latest"; \ + fi @echo "" - @echo "Image: $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_RUNTIME)" @echo "Platforms: linux/amd64, linux/arm64" @echo "" @echo "Verify with:"