This repository was archived by the owner on Feb 21, 2026. It is now read-only.
fix: generate tarball and expose inside of OCI image instead of gener… #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build container image | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| schedule: | |
| - cron: "0 1 * * SUN" | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - "**/README.md" | |
| workflow_dispatch: | |
| env: | |
| IMAGE_DESC: "OCI image for Homebrew" | |
| IMAGE_KEYWORDS: "homebrew" | |
| IMAGE_LOGO_URL: "https://avatars.githubusercontent.com/u/136393846?s=200&v=4" | |
| IMAGE_NAME: "${{ github.event.repository.name }}" | |
| IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" | |
| DEFAULT_TAG: "latest" | |
| jobs: | |
| build_push: | |
| name: Build and push image | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| platform: ["amd64", "arm64"] | |
| runs-on: ${{ matrix.platform == 'amd64' && 'ubuntu-24.04' || 'ubuntu-24.04-arm' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 | |
| - name: Build image | |
| run: podman build -t "${IMAGE_NAME}:${DEFAULT_TAG}" -f ./Containerfile . | |
| - name: Login to GitHub Container Registry | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LOGIN_USER: ${{ github.actor }} | |
| run: | | |
| echo "${GITHUB_TOKEN}" | podman login -u "${LOGIN_USER}" --password-stdin "ghcr.io" | |
| echo "${GITHUB_TOKEN}" | docker login -u "${LOGIN_USER}" --password-stdin "ghcr.io" | |
| - name: Push to GHCR | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| id: push | |
| env: | |
| IMAGE_REGISTRY: ${{ env.IMAGE_REGISTRY }} | |
| IMAGE_NAME: ${{ env.IMAGE_NAME }} | |
| IMAGE_DIGEST: ${{ steps.load.outputs.digest }} | |
| PLATFORM: ${{ matrix.platform }} | |
| MAX_RETRIES: 3 | |
| run: | | |
| set -x | |
| for i in $(seq "${MAX_RETRIES}"); do | |
| podman push --digestfile=/tmp/digestfile "localhost/${IMAGE_NAME}:${DEFAULT_TAG}" "${IMAGE_REGISTRY}/${IMAGE_NAME}:${DEFAULT_TAG}-${PLATFORM}" && break || sleep $((5 * i)); | |
| done | |
| echo "remote_image_digest=$(< /tmp/digestfile)" | tee "${GITHUB_OUTPUT}" | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| - name: Sign Image | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| env: | |
| REMOTE_IMAGE_DIGEST: ${{ steps.push.outputs.remote_image_digest }} | |
| COSIGN_EXPERIMENTAL: false | |
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | |
| run: cosign sign -y --key env://COSIGN_PRIVATE_KEY "${IMAGE_REGISTRY}/${IMAGE_NAME}@${REMOTE_IMAGE_DIGEST}" | |
| - name: Create Job Outputs | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| env: | |
| IMAGE_NAME: ${{ env.IMAGE_NAME }} | |
| PLATFORM: ${{ matrix.platform }} | |
| DIGEST: ${{ steps.push.outputs.remote_image_digest }} | |
| run: | | |
| set -x | |
| mkdir -p /tmp/outputs/digests | |
| echo "${DIGEST}" | tee "/tmp/outputs/digests/${IMAGE_NAME}-${PLATFORM}.txt" | |
| - name: Upload Output Artifacts | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 | |
| with: | |
| name: ${{ env.IMAGE_NAME }}-${{ matrix.platform }} | |
| retention-days: 1 | |
| if-no-files-found: error | |
| path: | | |
| /tmp/outputs/digests/*.txt | |
| manifest: | |
| name: Create Manifest | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: | |
| - build_push | |
| container: | |
| image: docker.io/library/alpine:latest | |
| options: --privileged --security-opt seccomp=unconfined | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Install dependencies | |
| run: apk add bash coreutils curl docker findutils fuse-overlayfs git grep jq podman sed | |
| # Necessary until bootc supports Cosign v3 | |
| - name: Install old cosign | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| # Thank you Bri! :3 @b- | |
| curl --retry 3 -H "Authorization: Bearer ${GITHUB_TOKEN}" -fsSLo "/usr/bin/cosign" "https://github.com/sigstore/cosign/releases/download/v2.6.1/cosign-linux-amd64" | |
| chmod +x /usr/bin/cosign | |
| - name: Exit on failure | |
| shell: bash | |
| env: | |
| JOBS: ${{ toJson(needs) }} | |
| run: | | |
| for i in $(jq -r 'to_entries[] | .value.result' <<< "${JOBS}"); do | |
| if [ "$i" != "success" ] && [ "$i" != "skipped" ]; then | |
| echo "Status check not okay!" | |
| exit 1 | |
| fi | |
| done | |
| # This generates a timestamp like what is defined on the ArtifactHub documentation | |
| # E.G: 2022-02-08T15:38:15Z' | |
| # https://artifacthub.io/docs/topics/repositories/container-images/ | |
| # https://linux.die.net/man/1/date | |
| - name: Get current date | |
| id: date | |
| run: echo "date=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)" | tee "${GITHUB_OUTPUT}" | |
| - name: Image Metadata | |
| uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5 | |
| id: metadata | |
| with: | |
| tags: | | |
| type=raw,value=${{ env.DEFAULT_TAG }} | |
| type=raw,value=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| type=raw,value={{date 'YYYYMMDD'}} | |
| type=ref,event=pr | |
| type=sha,enable=${{ github.event_name == 'pull_request' }} | |
| labels: | | |
| containers.bootc=1 | |
| io.artifacthub.package.deprecated=false | |
| io.artifacthub.package.keywords=${{ env.IMAGE_KEYWORDS }} | |
| io.artifacthub.package.license=Apache-2.0 | |
| io.artifacthub.package.logo-url=${{ env.IMAGE_LOGO_URL }} | |
| io.artifacthub.package.prerelease=false | |
| io.artifacthub.package.readme-url=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md | |
| org.opencontainers.image.created=${{ steps.date.outputs.date }} | |
| org.opencontainers.image.description=${{ env.IMAGE_DESC }} | |
| org.opencontainers.image.documentation=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/${{ github.sha }}/README.md | |
| org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/blob/${{ github.sha }}/Containerfile | |
| org.opencontainers.image.title=${{ env.IMAGE_NAME }} | |
| org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/tree/${{ github.sha }} | |
| org.opencontainers.image.vendor=${{ github.repository_owner }} | |
| org.opencontainers.image.version=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| - name: Fetch Build Outputs | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 | |
| with: | |
| pattern: ${{ env.IMAGE_NAME }}-* | |
| merge-multiple: true | |
| path: /tmp/artifacts | |
| - name: Load Outputs | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| shell: bash | |
| id: load-outputs | |
| run: | | |
| set -x | |
| OUTCSV="$(mktemp)" | |
| for digest_file in /tmp/artifacts/*.txt; do | |
| # Extract the platform from the file name | |
| PLATFORM="$(basename "${digest_file}" ".txt" | sed 's/.*-//g')" | |
| IMAGE_NAME="$(basename "${digest_file}" ".txt" | grep -Po ".*-" | sed 's/-$//')" | |
| DIGEST="$(< "${digest_file}")" | |
| echo "${PLATFORM},${IMAGE_NAME},${DIGEST}" | tee -a "${OUTCSV}" | |
| done | |
| echo "OUTPUT_CSV=$(base32 -w 0 < "${OUTCSV}")" | tee -a "${GITHUB_OUTPUT}" | |
| - name: Login to GitHub Container Registry | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| env: | |
| REGISTRY: ghcr.io | |
| run: | | |
| echo "${{ secrets.GITHUB_TOKEN }}" | podman login -u "${{ github.actor }}" --password-stdin "${REGISTRY}" | |
| echo "${{ secrets.GITHUB_TOKEN }}" | docker login -u "${{ github.actor }}" --password-stdin "${REGISTRY}" | |
| - name: Publish manifest with unified architecture and sign | |
| shell: bash | |
| if: github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) | |
| env: | |
| COSIGN_EXPERIMENTAL: false | |
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | |
| LABELS: ${{ steps.metadata.outputs.labels }} | |
| OUTPUT_CSV: ${{ steps.load-outputs.outputs.OUTPUT_CSV }} | |
| TAGS: ${{ steps.metadata.outputs.tags }} | |
| run: | | |
| set -x | |
| OUTPUT_CSV_FILE="$(mktemp)" | |
| echo "${OUTPUT_CSV}" | base32 -d | tee "${OUTPUT_CSV_FILE}" | |
| # This is insanely dumb, please PR if you have a better solution ~ @tulilirockz :3c | |
| IMAGES=$(cat "${OUTPUT_CSV_FILE}" | while IFS=$'\n' read -r ENTRY ; do echo "${ENTRY}" | cut -d, -f2 ; done | sort | uniq | tr "\n" " " | sed 's/.$//') | |
| for TARGET_IMAGE in ${IMAGES} ; do | |
| TARGET_MANIFEST="${IMAGE_REGISTRY}/${TARGET_IMAGE}" | |
| podman manifest create "${TARGET_MANIFEST}" | |
| PLATFORMS=$(grep -F -e "${IMAGE}" "${OUTPUT_CSV_FILE}" | while IFS=$'\n' read -r ENTRY ; do echo "${ENTRY}" | cut -d, -f1 ; done | sort | uniq | tr "\n" " " | sed 's/.$//') | |
| for TARGET_PLATFORM in ${PLATFORMS} ; do | |
| TARGET_DIGEST="$(grep -F -e ",${TARGET_IMAGE}," "${OUTPUT_CSV_FILE}" | grep -F -e "${TARGET_PLATFORM}" | cut -d, -f3 | tr -d "\n")" | |
| podman manifest add "${TARGET_MANIFEST}" "${TARGET_MANIFEST}@${TARGET_DIGEST}" --arch "${TARGET_PLATFORM}" | |
| done | |
| while IFS= read -r LABEL; do | |
| podman manifest annotate --index --annotation "${LABEL}" "${TARGET_MANIFEST}" | |
| done <<< "${LABELS}" | |
| while IFS= read -r TAG; do | |
| podman manifest push --all=false --digestfile=/tmp/digestfile "${TARGET_MANIFEST}" "${TARGET_MANIFEST}:${TAG}" | |
| done <<< "${TAGS}" | |
| cosign sign -y --key env://COSIGN_PRIVATE_KEY "${TARGET_MANIFEST}@$(< /tmp/digestfile)" | |
| done |