diff --git a/.github/actions/publish-image/action.yml b/.github/actions/publish-image/action.yml new file mode 100644 index 00000000..70d1374b --- /dev/null +++ b/.github/actions/publish-image/action.yml @@ -0,0 +1,90 @@ +name: "publish-image" +description: "Composite action to publish trestle-bot images." + +inputs: + image: + required: true + description: "The image repository location in the format of registry/name/app" + release_version: + required: true + description: "The version to build type semver tags from" + no_cache: + description: "Skip using cache when building the image." + required: false + default: "false" +outputs: + image_sha: + value: ${{ inputs.image}}@${{ steps.build-and-push.outputs.digest }} + description: The publish image with digest + +runs: + using: "composite" + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up cosign + uses: sigstore/cosign-installer@v3.4.0 + + # Tags are defined here based on workflow triggers + - name: Define metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ inputs.image }} + tags: | + type=semver,pattern=v{{major}},enable=${{ !startsWith(inputs.release_version, 'v0.') }},value=${{ inputs.release_version }} + type=semver,pattern=v{{major}}.{{minor}},value=${{ inputs.release_version }} + type=semver,pattern=v{{version}},value=${{ inputs.release_version }} + type=schedule,pattern={{date 'YYYYMMDD'}},prefix=${{ inputs.release_version }}. + flavor: | + latest=false + + - name: Build and export to Docker + uses: docker/build-push-action@v5 + id: build-and-export + with: + load: true + no-cache: ${{ inputs.no-cache == 'true' }} + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Pre-push Image Scan + uses: aquasecurity/trivy-action@0.19.0 + with: + image-ref: ${{ inputs.image }}@${{ steps.build-and-export.outputs.digest }} + exit-code: 1 + skip-files: "**/.venv/lib/**/METADATA" + scanners: secret + severity: HIGH,CRITICAL,MEDIUM + + # Does not rebuild. Uses internal cache from previous step. + - name: Build and Push + uses: docker/build-push-action@v5 + id: build-and-push + with: + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Sign the image with GitHub OIDC Token + run: cosign sign --yes "$IMAGE@$DIGEST" + env: + DIGEST: ${{ steps.build-and-push.outputs.digest }} + IMAGE: ${{ inputs.image }} + shell: bash + + - name: Verify image + run: | + cosign verify "$IMAGE@$DIGEST" --certificate-identity-regexp="$SUBJECT" \ + --certificate-oidc-issuer=https://token.actions.githubusercontent.com + env: + SUBJECT: https://github\.com/${{ github.repository_owner }}/trestle-bot/\.github/.+ + IMAGE: ${{ inputs.image }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + shell: bash diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 16e8601f..8953d1a1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,6 +1,8 @@ name: Publish Image to Quay on: + schedule: + - 0 0 */30 * * release: types: [published] workflow_dispatch: @@ -24,7 +26,6 @@ env: IMAGE_REGISTRY: quay.io jobs: - publish-image: runs-on: 'ubuntu-latest' permissions: @@ -35,35 +36,21 @@ jobs: skip_tests: ${{ steps.check_event.outputs.event_type == 'release' || (steps.check_event.outputs.event_type == 'workflow_dispatch' && github.event.inputs.skip_tests == 'true') }} - image: ${{ steps.set_image_repo.outputs.image_repo }}@${{ steps.build-and-push.outputs.digest }} + image: ${{ steps.build_publis_image.outputs.image_sha }} steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Set up cosign - uses: sigstore/cosign-installer@v3.4.0 - - name: Login to Quay uses: docker/login-action@v3 with: username: ${{ secrets.QUAY_USER }} password: ${{ secrets.QUAY_TOKEN }} registry: ${{ env.IMAGE_REGISTRY }} - - - name: Set image repository - id: set_image_repo - run: | - echo "image_repo=${{ env.IMAGE_REGISTRY }}/${{ vars.QUAY_ORG }}/${{ env.IMAGE_NAME }}" >> "$GITHUB_OUTPUT" - name: Check if triggered by release or workflow dispatch id: check_event run: echo "event_type=${{ toJson(github.event_name) }}" >> "$GITHUB_OUTPUT" # Using intermediary variable to process event based input - - name: Set TAG environment variable for Release + - name: Set environment information for release if: ${{ steps.check_event.outputs.event_type == 'release' }} run: | echo "TAG=$RELEASE_VERSION" >> "$GITHUB_ENV" @@ -71,56 +58,29 @@ jobs: env: RELEASE_VERSION: ${{ github.event.release.tag_name }} - - name: Set TAG environment variable for Workflow Dispatch + - name: Set environment information for workflow dispatch if: ${{ steps.check_event.outputs.event_type == 'workflow_dispatch' }} run: | echo "TAG=$INPUT_VERSION" >> "$GITHUB_ENV" - echo "NO_CACHE=$INPUT_NO_CACHE" >> "$GITHUB_ENV" + echo "NO_CACHE=true" >> "$GITHUB_ENV" env: INPUT_VERSION: ${{ github.event.inputs.tag }} INPUT_NO_CACHE: ${{ github.event.inputs.no_cache }} - - name: Build and export to Docker - uses: docker/build-push-action@v5 - id: build-and-export - with: - load: true - no-cache: ${{ env.NO_CACHE == 'true' }} - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ steps.set_image_repo.outputs.image_repo }}:${{ env.TAG }} - - - name: Pre-push Image Scan - uses: aquasecurity/trivy-action@0.19.0 - with: - image-ref: ${{ steps.set_image_repo.outputs.image_repo }}:${{ env.TAG }} - exit-code: 1 - skip-files: "**/.venv/lib/**/METADATA" - scanners: secret - severity: HIGH,CRITICAL,MEDIUM + - name: Set environment information for schedule + if: ${{ steps.check_event.outputs.event_type == 'workflow_dispatch' }} + run: | + LATEST_VERSION=$( gh release view --json tagName --jq '.["tagName"]' ) + echo "TAG=$LATEST_VERSION" >> "$GITHUB_ENV" + echo "NO_CACHE=$INPUT_NO_CACHE" >> "$GITHUB_ENV" - # Does not rebuild. Uses internal cache from previous step. - - name: Build and Push - uses: docker/build-push-action@v5 - id: build-and-push + - name: Build and Publish the image + uses: ./.github/actions/publish-image + id: build_publish_image with: - push: true - tags: ${{ steps.set_image_repo.outputs.image_repo }}:${{ env.TAG }} - - - name: Sign the image with GitHub OIDC Token - run: cosign sign --yes "$IMAGE@$DIGEST" - env: - DIGEST: ${{ steps.build-and-push.outputs.digest }} - IMAGE: ${{ steps.set_image_repo.outputs.image_repo }} - - - name: Verify image - run: | - cosign verify "$IMAGE@$DIGEST" --certificate-identity-regexp="$SUBJECT" \ - --certificate-oidc-issuer=https://token.actions.githubusercontent.com - env: - SUBJECT: https://github\.com/${{ github.repository_owner }}/trestle-bot/\.github/.+ - IMAGE: ${{ steps.set_image_repo.outputs.image_repo }} - DIGEST: ${{ steps.build-and-push.outputs.digest }} + image: ${{ env.IMAGE_REGISTRY }}/${{ vars.QUAY_ORG }}/${{ env.IMAGE_NAME }} + release_version: ${{ env.TAG }} + no-cache: ${{ env.NO_CACHE }} test: permissions: