diff --git a/.github/actions/build-docker/action.yml b/.github/actions/build-docker/action.yml new file mode 100644 index 00000000..be06d733 --- /dev/null +++ b/.github/actions/build-docker/action.yml @@ -0,0 +1,118 @@ +name: 'Build and Push Docker Images' +description: 'Builds multi-arch Docker images with caching and SemVer tagging using official Docker actions' + +inputs: + dockerfile: + description: 'Path to Dockerfile (relative to repository root)' + required: true + image-name: + description: 'Docker image name (without registry prefix)' + required: true + registry: + description: 'Docker registry (docker.io or ghcr.io)' + required: false + default: 'docker.io' + username: + description: 'Docker registry username' + required: true + password: + description: 'Docker registry password/token' + required: true + platforms: + description: 'Target platforms for multi-arch build' + required: false + default: 'linux/amd64,linux/arm64' + push: + description: 'Whether to push the image (true/false)' + required: false + default: 'false' + build-args: + description: 'Build arguments as KEY=VALUE pairs (one per line)' + required: false + default: '' + + cache-scope: + description: 'Cache scope for build cache' + required: false + default: 'default' + +outputs: + image-id: + description: 'Image ID of the built image' + value: ${{ steps.build.outputs.imageid }} + digest: + description: 'Image digest of the built image' + value: ${{ steps.build.outputs.digest }} + metadata: + description: 'Build result metadata' + value: ${{ steps.build.outputs.metadata }} + +runs: + using: 'composite' + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + network=host + + - name: Log in to Docker Registry + if: inputs.push == 'true' + uses: docker/login-action@v3 + with: + registry: ${{ inputs.registry == 'docker.io' && '' || inputs.registry }} + username: ${{ inputs.username }} + password: ${{ inputs.password }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ inputs.image-name }} + tags: | + # For releases: apply SemVer tags and latest (excludes pre-releases automatically) + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=semver,pattern=latest + # For branches: branch name as tag + type=ref,event=branch + # For PRs: pr- + type=ref,event=pr + # SHA for unique identification + type=sha,prefix=sha-,format=short + labels: | + org.opencontainers.image.title=${{ inputs.image-name }} + org.opencontainers.image.description=PgWatch - PostgreSQL monitoring solution + org.opencontainers.image.vendor=Cybertec PostgreSQL International GmbH + org.opencontainers.image.licenses=BSD-3-Clause + + - name: Build and push Docker image + id: build + uses: docker/build-push-action@v5 + with: + context: . + file: ${{ inputs.dockerfile }} + platforms: ${{ inputs.platforms }} + push: ${{ inputs.push == 'true' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: ${{ inputs.build-args }} + cache-from: type=gha,scope=${{ inputs.cache-scope }} + cache-to: type=gha,mode=max,scope=${{ inputs.cache-scope }} + provenance: false + sbom: false + + - name: Output image details + shell: bash + run: | + echo "🐳 Built image: ${{ inputs.image-name }}" + echo "📋 Tags:" + echo "${{ steps.meta.outputs.tags }}" | sed 's/^/ - /' + echo "🔍 Digest: ${{ steps.build.outputs.digest }}" + echo "🆔 Image ID: ${{ steps.build.outputs.imageid }}" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1f84446..cbb27523 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -197,4 +197,34 @@ jobs: echo "push_opt=--push" >> $GITHUB_ENV - name: Build mkdocs - run: mike deploy ${{ env.push_opt }} devel \ No newline at end of file + run: mike deploy ${{ env.push_opt }} devel + + test-docker-images: + if: true # false to skip job during debug + needs: [test-postgresql-ubuntu, test-postgresql-windows, test-postgresql-macos] + name: Test Docker Image Build + runs-on: ubuntu-latest + steps: + + - name: Check out code + uses: actions/checkout@v5 + + - name: Prepare build metadata + id: meta + run: | + echo "GIT_HASH=${{ github.sha }}" >> $GITHUB_OUTPUT + echo "GIT_TIME=$(git show -s --format=%cI HEAD)" >> $GITHUB_OUTPUT + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_OUTPUT + + - name: Test build Docker image (fast, amd64 only) + uses: ./.github/actions/build-docker + with: + dockerfile: Dockerfile + image-name: cybertecpostgresql/pg_timetable + platforms: linux/amd64 + push: 'false' + cache-scope: test-build + build-args: | + COMMIT=${{ steps.meta.outputs.GIT_HASH }} + DATE=${{ steps.meta.outputs.GIT_TIME }} + VERSION=${{ steps.meta.outputs.VERSION }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index b810307e..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Update Docker -on: - push: - paths: - - '**.go' - - '**.yaml' - - '**.yml' - - 'go.mod' - - 'go.sum' - - 'Dockerfile' - branches: - - 'master' - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - docker: - if: true # false to skip job during debug - runs-on: ubuntu-latest - steps: - - - name: Check out code - uses: actions/checkout@v5 - - - name: Set up Golang - uses: actions/setup-go@v6 - with: - go-version: '1.25' - - # despite the fact docker will build binary internally - # we want to stop workflow in case of any error before pushing to registry - - name: Get dependencies and Build - run: | - go version - go mod download - go build - - - name: Version strings - id: version - run: | - echo "RELEASE_TIME=$(git show -s --format=%cI HEAD)" >> $GITHUB_OUTPUT - - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - env: - VERSION: ${{ github.ref_name }} - COMMIT: ${{ github.sha }} - DATE: ${{ steps.version.outputs.RELEASE_TIME }} - with: - name: cybertecpostgresql/pg_timetable - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - buildargs: VERSION,COMMIT,DATE diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3682272..5030b36e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,13 @@ name: Release + +permissions: + contents: write + packages: write + pages: write + deployments: write + pull-requests: write + issues: write + on: release: types: [created] @@ -38,20 +47,25 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v5 - - name: Version strings - id: version + - name: Prepare build metadata + id: meta run: | - echo "RELEASE_TIME=$(git show -s --format=%cI HEAD)" >> $GITHUB_OUTPUT + echo "GIT_HASH=${{ github.sha }}" >> $GITHUB_OUTPUT + echo "GIT_TIME=$(git show -s --format=%cI HEAD)" >> $GITHUB_OUTPUT + echo "VERSION=${{ github.ref_name }}" >> $GITHUB_OUTPUT - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - env: - VERSION: ${{ github.ref_name }} - COMMIT: ${{ github.sha }} - DATE: ${{ steps.version.outputs.RELEASE_TIME }} + - name: Build and push Docker image + uses: ./.github/actions/build-docker with: - name: cybertecpostgresql/pg_timetable + dockerfile: Dockerfile + image-name: cybertecpostgresql/pg_timetable + registry: docker.io username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - buildargs: VERSION,COMMIT,DATE - tag_semver: true \ No newline at end of file + platforms: linux/amd64,linux/arm64 + push: 'true' + cache-scope: shared-release + build-args: | + COMMIT=${{ steps.meta.outputs.GIT_HASH }} + DATE=${{ steps.meta.outputs.GIT_TIME }} + VERSION=${{ steps.meta.outputs.VERSION }}