Skip to content

Build DistinctionOS #520

Build DistinctionOS

Build DistinctionOS #520

Workflow file for this run

---
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@da05d552573ad5aba039eaac05058a918a7bf631 # v2
with:
name: ${{ steps.changelog.outputs.title }}
tag_name: ${{ steps.changelog.outputs.tag }}
body_path: ./changelog.md
make_latest: true