Build DistinctionOS #527
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 DistinctionOS | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| schedule: | |
| - cron: '05 6 */5 * *' # 12:05am Mountain time every 5 days Ref: 'minute hour day(s) Month day-of-the-week' | |
| push: | |
| branches: | |
| - main | |
| paths-ignore: | |
| - '.github/**' | |
| - '**/README.md' | |
| - '**/claude.md' | |
| - 'repo_files/**' | |
| - 'system_files/**' # modifcations to system files are usually small and don't warrant a full rebuild | |
| workflow_dispatch: | |
| inputs: | |
| fresh-rechunk: | |
| description: 'Clear rechunk history (forces full redownload for users)' | |
| type: boolean | |
| default: false | |
| handwritten: | |
| description: 'Optional handwritten changelog message for this release' | |
| type: string | |
| default: '' | |
| env: | |
| IMAGE_DESC: "My Customized Bazzite Image" | |
| IMAGE_KEYWORDS: "bootc,ublue,universal-blue" | |
| IMAGE_LOGO_URL: "https://avatars.githubusercontent.com/u/120078124?s=200&v=4" | |
| IMAGE_NAME: "${{ github.event.repository.name }}" # output image name, usually same as repo name | |
| IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" # do not edit | |
| DEFAULT_TAG: "latest" | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| build_push: | |
| name: Build and push image | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| outputs: | |
| digest: ${{ steps.push.outputs.digest }} | |
| steps: | |
| # These stage versions are pinned by https://github.com/renovatebot/renovate | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 | |
| with: | |
| fetch-depth: 0 # Full history for changelog commit range | |
| - name: Prepare environment | |
| run: | | |
| # Lowercase the image uri | |
| echo "IMAGE_REGISTRY=${IMAGE_REGISTRY,,}" >> ${GITHUB_ENV} | |
| echo "IMAGE_NAME=${IMAGE_NAME,,}" >> ${GITHUB_ENV} | |
| - name: Get current date | |
| id: date | |
| run: | | |
| echo "date=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)" >> $GITHUB_OUTPUT | |
| - name: Image Metadata | |
| uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 | |
| id: metadata | |
| with: | |
| # This generates all the tags for your image, you can add custom tags here too! | |
| # Default tags are "$DEFAULT_TAG" and "$DEFAULT_TAG.$date". | |
| tags: | | |
| type=raw,value=${{ env.DEFAULT_TAG }} | |
| type=raw,value=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| type=raw,value={{date 'YYYYMMDD'}} | |
| type=sha,enable=${{ github.event_name == 'pull_request' }} | |
| type=ref,event=pr | |
| labels: | | |
| io.artifacthub.package.readme-url=https://raw.githubusercontent.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/refs/heads/main/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 }}/refs/heads/main/README.md | |
| org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}/blob/main/Containerfile | |
| org.opencontainers.image.title=${{ env.IMAGE_NAME }} | |
| org.opencontainers.image.url=https://github.com/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} | |
| org.opencontainers.image.vendor=${{ github.repository_owner }} | |
| org.opencontainers.image.version=${{ env.DEFAULT_TAG }}.{{date 'YYYYMMDD'}} | |
| org.opencontainers.image.revision=${{ github.sha }} | |
| 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 | |
| containers.bootc=1 | |
| sep-tags: " " | |
| sep-annotations: " " | |
| - name: Maximize build space | |
| uses: jlumbroso/free-disk-space@v1.3.1 | |
| with: | |
| android: true | |
| dotnet: true | |
| haskell: true | |
| large-packages: true | |
| docker-images: false | |
| swap-storage: true | |
| - name: Build image (rootful) | |
| id: build_image | |
| run: | | |
| # Builds image in root store as root, to be picked up by Rechunker | |
| sudo buildah bud \ | |
| --format docker \ | |
| --tag "localhost/${IMAGE_NAME}:${{ env.DEFAULT_TAG }}" \ | |
| --file Containerfile \ | |
| . | |
| - name: Remove source images | |
| run: | | |
| images=$(sudo podman images -n --sort repository --format '{{.ID}} {{.Repository}}' | grep -v localhost | awk '{print $1}') | |
| if [ -n "${images}" ]; then | |
| for image in ${images}; do | |
| echo "Removing image: $image" | |
| sudo podman rmi --force "$image" | |
| done | |
| else | |
| echo "No images to remove." | |
| fi | |
| - name: Run Rechunker | |
| id: rechunk | |
| uses: hhd-dev/rechunk@v1.2.4 | |
| if: github.event_name != 'pull_request' | |
| with: | |
| rechunk: "ghcr.io/hhd-dev/rechunk:v1.2.3" | |
| ref: "localhost/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}" | |
| prev-ref: "${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }}" | |
| skip_compression: false | |
| max-layers: 90 | |
| labels: ${{ steps.metadata.outputs.labels }} # Rechunk strips out all the labels during build, this needs to be reapplied here with newline separator | |
| - name: Remove Rechunker image | |
| run: | | |
| image=$(sudo podman images -n --sort repository --format '{{.ID}} {{.Repository}}' | grep rechunk | awk '{print $1}') | |
| if [ -n "${image}" ]; then | |
| sudo podman rmi --force "$image" | |
| else | |
| echo "No image to remove" | |
| fi | |
| - name: Rechunk output | |
| continue-on-error: true | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| if [[ "${STEPS_RECHUNK_CONCLUSION}" == "success" ]]; then | |
| echo "=== Rechunk Changelog ===" | |
| if [ -f "${STEPS_RECHUNK_OUTPUTS_CHANGELOG}" ]; then | |
| cat "${STEPS_RECHUNK_OUTPUTS_CHANGELOG}" | |
| fi | |
| echo "" | |
| echo "=== Rechunk Manifest ===" | |
| if [ -f "${STEPS_RECHUNK_OUTPUTS_MANIFEST}" ]; then | |
| cat "${STEPS_RECHUNK_OUTPUTS_MANIFEST}" | |
| fi | |
| else | |
| echo "Rechunk conclusion: ${STEPS_RECHUNK_CONCLUSION}" | |
| fi | |
| env: | |
| STEPS_RECHUNK_CONCLUSION: ${{ steps.rechunk.conclusion }} | |
| STEPS_RECHUNK_OUTPUTS_CHANGELOG: ${{ steps.rechunk.outputs.changelog }} | |
| STEPS_RECHUNK_OUTPUTS_MANIFEST: ${{ steps.rechunk.outputs.manifest }} | |
| - name: Load in podman and tag | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| IMAGE=$(podman pull ${STEPS_RECHUNK_OUTPUTS_REF}) | |
| sudo rm -rf ${STEPS_RECHUNK_OUTPUTS_LOCATION} | |
| for tag in ${STEPS_METADATA_OUTPUTS_TAGS}; do | |
| podman tag $IMAGE ${IMAGE_NAME}:$tag | |
| done | |
| env: | |
| STEPS_RECHUNK_OUTPUTS_REF: ${{ steps.rechunk.outputs.ref }} | |
| STEPS_RECHUNK_OUTPUTS_LOCATION: ${{ steps.rechunk.outputs.location }} | |
| STEPS_METADATA_OUTPUTS_TAGS: ${{ steps.metadata.outputs.tags }} | |
| IMAGE_NAME: ${{ env.IMAGE_NAME }} | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 | |
| if: github.event_name != 'pull_request' | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Lowercase Registry | |
| id: registry_case | |
| uses: ASzc/change-string-case-action@v6 | |
| with: | |
| string: ${{ env.IMAGE_REGISTRY }} | |
| - name: Inspect layer sizes | |
| run: | | |
| echo "=== Layer size analysis ===" | |
| podman inspect localhost/${IMAGE_NAME}:${DEFAULT_TAG} | jq '.[0].RootFS.Layers[] | length' | \ | |
| awk '{sum+=$1; print "Layer size: " $1/1024/1024 " MB"} END {print "Total: " sum/1024/1024 " MB"}' | |
| - name: Push To GHCR | |
| uses: redhat-actions/push-to-registry@5ed88d269cf581ea9ef6dd6806d01562096bee9c # v2 | |
| if: github.event_name != 'pull_request' | |
| id: push | |
| env: | |
| REGISTRY_USER: ${{ github.actor }} | |
| REGISTRY_PASSWORD: ${{ github.token }} | |
| with: | |
| registry: ${{ env.IMAGE_REGISTRY }} | |
| image: ${{ env.IMAGE_NAME }} | |
| tags: ${{ steps.metadata.outputs.tags }} | |
| username: ${{ env.REGISTRY_USER }} | |
| password: ${{ env.REGISTRY_PASSWORD }} | |
| extra-args: | | |
| --compression-format=gzip | |
| --compression-level=6 | |
| # This section is optional and only needs to be enabled if you plan on distributing | |
| # your project for others to consume. You will need to create a public and private key | |
| # using Cosign and save the private key as a repository secret in Github for this workflow | |
| # to consume. For more details, review the image signing section of the README. | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 | |
| if: github.event_name != 'pull_request' | |
| - name: Sign container image | |
| id: sign_container_image | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| echo "${STEPS_PUSH_OUTPUTS_REGISTRY_PATHS}" | |
| IMAGE_FULL="${STEPS_REGISTRY_CASE_OUTPUTS_LOWERCASE}/${IMAGE_NAME}" | |
| cosign sign -y --key env://COSIGN_PRIVATE_KEY $IMAGE_FULL@"${STEPS_PUSH_OUTPUTS_DIGEST}" | |
| env: | |
| COSIGN_EXPERIMENTAL: false | |
| COSIGN_PRIVATE_KEY: ${{ secrets.SIGNING_SECRET }} | |
| STEPS_PUSH_OUTPUTS_REGISTRY_PATHS: ${{ steps.push.outputs.registry-paths }} | |
| STEPS_REGISTRY_CASE_OUTPUTS_LOWERCASE: ${{ steps.registry_case.outputs.lowercase }} | |
| STEPS_PUSH_OUTPUTS_DIGEST: ${{ steps.push.outputs.digest }} | |
| generate_release: | |
| name: Generate Release | |
| needs: build_push | |
| if: github.event_name != 'pull_request' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4 | |
| with: | |
| fetch-depth: 500 # Deep history for commit range in changelog | |
| - name: Install skopeo | |
| run: | | |
| sudo apt-get update -q | |
| sudo apt-get install -y -q skopeo | |
| - name: Generate changelog | |
| id: changelog | |
| env: | |
| IMAGE_REGISTRY: "ghcr.io/${{ github.repository_owner }}" | |
| IMAGE_NAME: ${{ github.event.repository.name }} | |
| HANDWRITTEN: ${{ github.event.inputs.handwritten || '' }} | |
| run: | | |
| REGISTRY="${IMAGE_REGISTRY,,}" | |
| IMAGE="${IMAGE_NAME,,}" | |
| python3 .github/workflows/changelog.py \ | |
| "${IMAGE}" \ | |
| ./output.env \ | |
| ./changelog.md \ | |
| --registry "docker://${REGISTRY}/" \ | |
| --workdir . \ | |
| --handwritten "${HANDWRITTEN}" | |
| # Source the env file for tag and title | |
| source ./output.env | |
| echo "tag=${TAG}" >> $GITHUB_OUTPUT | |
| echo "title=${TITLE}" >> $GITHUB_OUTPUT | |
| # Show in job summary | |
| echo "## Release: ${TITLE}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| cat ./changelog.md >> $GITHUB_STEP_SUMMARY | |
| - name: Create Release | |
| uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2 | |
| with: | |
| name: ${{ steps.changelog.outputs.title }} | |
| tag_name: ${{ steps.changelog.outputs.tag }} | |
| body_path: ./changelog.md | |
| make_latest: true |