Skip to content

Commit 1da7c47

Browse files
committed
feat: replace legacy rechunker with rpm-ostree compose build-chunked-oci
1 parent 1bda1a7 commit 1da7c47

6 files changed

Lines changed: 156 additions & 162 deletions

File tree

.github/workflows/reusable-build.yml

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -163,25 +163,19 @@ jobs:
163163
echo "SBOM=${SBOM}" >> $GITHUB_OUTPUT
164164
sudo rm -rf ${OCI_DIR}
165165
166-
- name: Rechunk Image
167-
id: rechunk-image
168-
shell: bash
166+
- name: Rechunk Image with rpm-ostree
167+
id: rechunker
169168
env:
170-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
169+
MATRIX_BASE_NAME: ${{ matrix.base_name }}
170+
MATRIX_STREAM_NAME: ${{ matrix.stream_name }}
171+
MATRIX_IMAGE_FLAVOR: ${{ matrix.image_flavor }}
172+
DEFAULT_TAG: ${{ env.DEFAULT_TAG }}
171173
run: |
172-
sudo -E $(command -v just) rechunk "${{ matrix.base_name }}" \
173-
"${{ matrix.stream_name }}" \
174-
"${{ matrix.image_flavor }}" \
174+
sudo -E $(command -v just) rechunk "${MATRIX_BASE_NAME}" \
175+
"${MATRIX_STREAM_NAME}" \
176+
"${MATRIX_IMAGE_FLAVOR}" \
175177
"1"
176178
177-
- name: Load Image into Podman
178-
id: load-rechunk
179-
shell: bash
180-
run: |
181-
sudo -E $(command -v just) load-rechunk "${{ matrix.base_name }}" \
182-
"${{ env.DEFAULT_TAG }}" \
183-
"${{ matrix.image_flavor }}"
184-
185179
- name: Secureboot Check
186180
id: secureboot
187181
shell: bash
@@ -190,6 +184,38 @@ jobs:
190184
"${{ env.DEFAULT_TAG }}" \
191185
"${{ matrix.image_flavor }}"
192186
187+
- name: Export to OCI Archive
188+
if: github.event_name == 'pull_request'
189+
id: oci-archive
190+
env:
191+
IMAGE_NAME: ${{ env.IMAGE_NAME }}
192+
DEFAULT_TAG: ${{ env.DEFAULT_TAG }}
193+
run: |
194+
sudo -E $(command -v just) export-oci "${{ matrix.base_name }}" \
195+
"${{ env.DEFAULT_TAG }}" \
196+
"${{ matrix.image_flavor }}"
197+
198+
- name: Upload OCI dir as Artifact
199+
if: github.event_name == 'pull_request'
200+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
201+
with:
202+
name: ${{ env.IMAGE_NAME }}.oci
203+
path: ${{ env.IMAGE_NAME }}.oci
204+
archive: false
205+
if-no-files-found: error
206+
retention-days: 1
207+
208+
- name: PR Testing Instructions
209+
if: github.event_name == 'pull_request'
210+
id: pr-summary
211+
env:
212+
IMAGE_NAME: ${{ env.IMAGE_NAME }}
213+
MATRIX_STREAM_NAME: "${{ matrix.stream_name }}"
214+
run: |
215+
echo "Download the .oci file" >> $GITHUB_STEP_SUMMARY
216+
echo "Rebase: sudo bootc switch --transport oci-archive /path/to/${IMAGE_NAME}.oci" >> $GITHUB_STEP_SUMMARY
217+
echo "Go back to the production image e.g.: sudo bootc switch --enforce-container-sigpolicy ghcr.io/ublue-os/${IMAGE_NAME}:${MATRIX_STREAM_NAME}" >> $GITHUB_STEP_SUMMARY
218+
193219
- name: Generate tags
194220
id: generate-tags
195221
shell: bash
@@ -225,10 +251,18 @@ jobs:
225251
with:
226252
string: ${{ env.IMAGE_REGISTRY }}
227253

254+
# TODO: remove me when we have a new podman in 26.04 runners
255+
# needed because old podman doesn't push layer annotations for
256+
# the rpm-ostree rechunker at all
257+
- name: install podman from brew
258+
if: github.event_name != 'pull_request'
259+
run: |
260+
/home/linuxbrew/.linuxbrew/bin/brew install podman
261+
228262
- name: Login to GitHub Container Registry
229263
if: github.event_name != 'pull_request'
230264
run: |
231-
echo ${{ secrets.GITHUB_TOKEN }} | podman login ghcr.io -u ${{ github.actor }} --password-stdin
265+
echo ${{ secrets.GITHUB_TOKEN }} | /home/linuxbrew/.linuxbrew/bin/podman login ghcr.io -u ${{ github.actor }} --password-stdin
232266
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
233267
234268
- name: Push to GHCR
@@ -241,9 +275,15 @@ jobs:
241275
timeout_minutes: 30
242276
command: |
243277
set -euox pipefail
278+
# HACK: push a second time so layer annotations are pushed
279+
# TODO: remove me when https://github.com/containers/podman/issues/27796 fixed
280+
281+
for tag in ${{ steps.generate-tags.outputs.alias_tags }}; do
282+
sudo -E /home/linuxbrew/.linuxbrew/bin/podman push ${{ env.IMAGE_NAME }}:${tag} ${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${tag}
283+
done
244284
245285
for tag in ${{ steps.generate-tags.outputs.alias_tags }}; do
246-
sudo -E podman push ${{ env.IMAGE_NAME }}:${tag} ${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${tag}
286+
sudo -E /home/linuxbrew/.linuxbrew/bin/podman push ${{ env.IMAGE_NAME }}:${tag} ${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${tag}
247287
done
248288
249289
digest=$(skopeo inspect docker://${{ steps.registry_case.outputs.lowercase }}/${{ env.IMAGE_NAME }}:${{ env.DEFAULT_TAG }} --format '{{.Digest}}')

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
flatpaks_with_deps
22
flatpak.*
33

4-
*_build
5-
*_build.*
4+
bluefin*.oci
65
previous.manifest.json
76
changelog.md
87
output.env

Justfile

Lines changed: 45 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
repo_organization := "ublue-os"
2-
rechunker_image := "ghcr.io/ublue-os/legacy-rechunk:v1.0.1-x86_64@sha256:2627cbf92ca60ab7372070dcf93b40f457926f301509ffba47a04d6a9e1ddaf7"
32
common_image := "ghcr.io/projectbluefin/common:latest"
43
brew_image := "ghcr.io/ublue-os/brew:latest"
54
images := '(
@@ -235,7 +234,7 @@ build $image="bluefin" $tag="latest" $flavor="main" rechunk="0" ghcr="0" pipelin
235234
elif [[ "{{ rechunk }}" == "1" && "{{ ghcr }}" == "1" ]]; then
236235
${SUDOIF} {{ just }} rechunk "${image}" "${tag}" "${flavor}" 1
237236
elif [[ "{{ rechunk }}" == "1" ]]; then
238-
${SUDOIF} {{ just }} rechunk "${image}" "${tag}" "${flavor}"
237+
{{ just }} rechunk "${image}" "${tag}" "${flavor}"
239238
fi
240239

241240
# Build Image and Rechunk
@@ -259,183 +258,86 @@ build-pipeline image="bluefin" tag="latest" flavor="main" kernel_pin="":
259258
#!/usr/bin/bash
260259
${SUDOIF} {{ just }} build {{ image }} {{ tag }} {{ flavor }} 1 1 1 {{ kernel_pin }}
261260

262-
# Rechunk Image
261+
# Rechunk Image with rpm-ostree
263262
[group('Image')]
264263
[private]
265264
rechunk $image="bluefin" $tag="latest" $flavor="main" ghcr="0" pipeline="0":
266265
#!/usr/bin/bash
267266

268-
echo "::group:: Rechunk Prep"
269267
set -eoux pipefail
270268

271269
# Validate
272270
{{ just }} validate "${image}" "${tag}" "${flavor}"
273271

274272
# Image Name
275273
image_name=$({{ just }} image_name {{ image }} {{ tag }} {{ flavor }})
274+
fedora_version=$({{ just }} fedora_version {{ image }} {{ tag }} {{ flavor }})
275+
DEFAULT_TAG=$({{ just }} generate-default-tag {{ tag }} {{ ghcr }})
276276

277-
# Check if image is already built
278-
ID=$(${PODMAN} images --filter reference=localhost/"${image_name}":"${tag}" --format "'{{ '{{.ID}}' }}'")
279-
if [[ -z "$ID" ]]; then
280-
{{ just }} build "${image}" "${tag}" "${flavor}"
281-
fi
282-
283-
# Load into Rootful Podman
284-
ID=$(${SUDOIF} ${PODMAN} images --filter reference=localhost/"${image_name}":"${tag}" --format "'{{ '{{.ID}}' }}'")
285-
if [[ -z "$ID" && ! ${PODMAN} =~ docker ]]; then
286-
COPYTMP=$(mktemp -p "${PWD}" -d -t podman_scp.XXXXXXXXXX)
287-
${SUDOIF} TMPDIR=${COPYTMP} ${PODMAN} image scp ${UID}@localhost::localhost/"${image_name}":"${tag}" root@localhost::localhost/"${image_name}":"${tag}"
288-
rm -rf "${COPYTMP}"
277+
if [[ "{{ ghcr }}" == "0" ]]; then
278+
{{ just }} load-rootful "${image}" "${tag}" "${flavor}"
289279
fi
290280

291-
# Prep Container
292-
CREF=$(${SUDOIF} ${PODMAN} create localhost/"${image_name}":"${tag}" bash)
293-
OLD_IMAGE=$(${SUDOIF} ${PODMAN} inspect $CREF | jq -r '.[].Image')
294-
OUT_NAME="${image_name}_build"
295-
MOUNT=$(${SUDOIF} ${PODMAN} mount "${CREF}")
296-
297-
# Fedora Version
298-
fedora_version=$(${SUDOIF} ${PODMAN} inspect $CREF | jq -r '.[].Config.Labels["ostree.linux"]' | grep -oP 'fc\K[0-9]+')
299-
300-
# Label Version
301-
VERSION=$(${SUDOIF} ${PODMAN} inspect $CREF | jq -r '.[].Config.Labels["org.opencontainers.image.version"]')
302-
303-
# Git SHA
304-
SHA="dedbeef"
305-
if [[ -z "$(git status -s)" ]]; then
306-
SHA=$(git rev-parse HEAD)
307-
fi
308-
309-
# Rest of Labels
310-
LABELS="
311-
io.artifacthub.package.deprecated=false
312-
io.artifacthub.package.keywords=bootc,fedora,bluefin,ublue,universal-blue
313-
io.artifacthub.package.logo-url=https://avatars.githubusercontent.com/u/120078124?s=200&v=4
314-
io.artifacthub.package.maintainers=[{\"name\": \"castrojo\", \"email\": \"jorge.castro@gmail.com\"}]
315-
io.artifacthub.package.readme-url=https://raw.githubusercontent.com/ublue-os/bluefin/refs/heads/main/README.md
316-
org.opencontainers.image.created=$(date -u +%Y\-%m\-%d\T%H\:%M\:%S\Z)
317-
org.opencontainers.image.license=Apache-2.0
318-
org.opencontainers.image.source=https://raw.githubusercontent.com/ublue-os/bluefin/refs/heads/main/Containerfile
319-
org.opencontainers.image.title=${image_name}
320-
org.opencontainers.image.url=https://projectbluefin.io
321-
org.opencontainers.image.vendor={{ repo_organization }}
322-
ostree.linux=$(${SUDOIF} ${PODMAN} inspect $CREF | jq -r '.[].Config.Labels["ostree.linux"]')
323-
containers.bootc=1
324-
"
325-
326-
# Cleanup Space during Github Action
281+
# In CI this will replace the unrechunked image
327282
if [[ "{{ ghcr }}" == "1" ]]; then
328-
base_image_name=silverblue-main
329-
if [[ "${tag}" =~ stable ]]; then
330-
tag="stable-daily"
331-
fi
332-
ID=$(${SUDOIF} ${PODMAN} images --filter reference=ghcr.io/{{ repo_organization }}/"${base_image_name}":${fedora_version} --format "{{ '{{.ID}}' }}")
333-
if [[ -n "$ID" ]]; then
334-
${PODMAN} rmi "$ID"
335-
fi
283+
CHUNKED_IMAGE="localhost/${image_name}:${DEFAULT_TAG}"
284+
else
285+
CHUNKED_IMAGE="localhost/${image_name}:${DEFAULT_TAG}-chunked"
336286
fi
337287

338-
# Rechunk Container
339-
rechunker="{{ rechunker_image }}"
340-
341-
echo "::endgroup::"
342-
echo "::group:: Prune"
343-
344-
# Run Rechunker's Prune
288+
# 128 layers, conservative default
289+
# In CI this renames stable to stable-daily
345290
${SUDOIF} ${PODMAN} run --rm \
346291
--pull=${PULL_POLICY} \
347-
--security-opt label=disable \
348-
--volume "$MOUNT":/var/tree \
349-
--env TREE=/var/tree \
350-
--user 0:0 \
351-
"${rechunker}" \
352-
/sources/rechunk/1_prune.sh
353-
354-
echo "::endgroup::"
355-
echo "::group:: Create ostree tree"
356-
357-
# Run Rechunker's Create
358-
${SUDOIF} ${PODMAN} run --rm \
359-
--security-opt label=disable \
360-
--volume "$MOUNT":/var/tree \
361-
--volume "cache_ostree:/var/ostree" \
362-
--env TREE=/var/tree \
363-
--env REPO=/var/ostree/repo \
364-
--env RESET_TIMESTAMP=1 \
365-
--user 0:0 \
366-
"${rechunker}" \
367-
/sources/rechunk/2_create.sh
368-
369-
# Cleanup Temp Container Reference
370-
${SUDOIF} ${PODMAN} unmount "$CREF"
371-
${SUDOIF} ${PODMAN} rm "$CREF"
372-
${SUDOIF} ${PODMAN} rmi "$OLD_IMAGE"
373-
374-
echo "::endgroup::"
375-
echo "::group:: Rechunker"
376-
377-
# Run Rechunker
378-
${SUDOIF} ${PODMAN} run --rm \
379-
--pull=${PULL_POLICY} \
380-
--security-opt label=disable \
381-
--volume "$PWD:/workspace" \
382-
--volume "$PWD:/var/git" \
383-
--volume cache_ostree:/var/ostree \
384-
--env REPO=/var/ostree/repo \
385-
--env PREV_REF=ghcr.io/ublue-os/"${image_name}":"${tag}" \
386-
--env OUT_NAME="$OUT_NAME" \
387-
--env LABELS="${LABELS}" \
388-
--env "DESCRIPTION='An interpretation of the Ubuntu spirit built on Fedora technology'" \
389-
--env "VERSION=${VERSION}" \
390-
--env VERSION_FN=/workspace/version.txt \
391-
--env OUT_REF="oci:$OUT_NAME" \
392-
--env GIT_DIR="/var/git" \
393-
--env REVISION="$SHA" \
394-
--user 0:0 \
395-
"${rechunker}" \
396-
/sources/rechunk/3_chunk.sh
397-
398-
# Fix Permissions of OCI
399-
${SUDOIF} find ${OUT_NAME} -type d -exec chmod 0755 {} \; || true
400-
${SUDOIF} find ${OUT_NAME}* -type f -exec chmod 0644 {} \; || true
401-
402-
if [[ "${UID}" -gt "0" ]]; then
403-
${SUDOIF} chown "${UID}:${GROUPS}" -R "${PWD}"
404-
elif [[ -n "${SUDO_UID:-}" ]]; then
405-
chown "${SUDO_UID}":"${SUDO_GID}" -R "${PWD}"
406-
fi
407-
408-
# Remove cache_ostree
409-
${SUDOIF} ${PODMAN} volume rm cache_ostree
410-
411-
echo "::endgroup::"
292+
--privileged \
293+
-v "/var/lib/containers:/var/lib/containers" \
294+
--entrypoint /usr/bin/rpm-ostree \
295+
"quay.io/fedora-ostree-desktops/silverblue:${fedora_version}" \
296+
compose build-chunked-oci \
297+
--max-layers 128 \
298+
--format-version=2 \
299+
--bootc \
300+
--from "localhost/${image_name}:${tag}" \
301+
--output containers-storage:${CHUNKED_IMAGE}
412302

413303
# Pipeline Checks
414304
if [[ {{ pipeline }} == "1" && -n "${SUDO_USER:-}" ]]; then
415-
sudo -u "${SUDO_USER}" {{ just }} load-rechunk "${image}" "${tag}" "${flavor}"
416305
sudo -u "${SUDO_USER}" {{ just }} secureboot "${image}" "${tag}" "${flavor}"
417306
fi
418307

419-
# Load OCI into Podman Store
308+
# For Rechunk
420309
[group('Image')]
421-
load-rechunk image="bluefin" tag="latest" flavor="main":
310+
load-rootful $image="bluefin" $tag="latest" $flavor="main":
422311
#!/usr/bin/bash
423-
set -eou pipefail
312+
set -eoux pipefail
424313

425314
# Validate
426315
{{ just }} validate {{ image }} {{ tag }} {{ flavor }}
427316

428317
# Image Name
429318
image_name=$({{ just }} image_name {{ image }} {{ tag }} {{ flavor }})
430319

431-
# Load Image
432-
OUT_NAME="${image_name}_build"
433-
IMAGE=$(${PODMAN} pull oci:"${PWD}"/"${OUT_NAME}")
434-
${PODMAN} tag ${IMAGE} localhost/"${image_name}":{{ tag }}
320+
if [[ ! "$(id -u)" == 0 && ! ${PODMAN} =~ docker ]]; then
321+
ID=$(${PODMAN} images --filter reference=localhost/"${image_name}":"${tag}" --format "'{{ '{{.ID}}' }}'")
322+
if [[ -z "$ID" ]]; then
323+
{{ just }} build "$image" "$tag" "$flavor"
324+
fi
325+
${PODMAN} image scp localhost/"${image_name}":"${tag}" root@localhost::
326+
fi
327+
328+
# Generate OCI Archive for PR Testing
329+
[group('Image')]
330+
export-oci $image="bluefin" $tag="latest" $flavor="main":
331+
#!/usr/bin/bash
332+
set -eoux pipefail
435333

436-
# Cleanup
437-
rm -rf "${OUT_NAME}*"
438-
rm -f previous.manifest.json
334+
# Validate
335+
{{ just }} validate {{ image }} {{ tag }} {{ flavor }}
336+
337+
# Image Name
338+
image_name=$({{ just }} image_name {{ image }} {{ tag }} {{ flavor }})
339+
340+
${PODMAN} push localhost/"${image_name}":"${tag}" oci-archive:"${image_name}".oci
439341

440342
# Run Container
441343
[group('Image')]

build_files/base/17-cleanup.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ systemctl enable rpm-ostree-countme.service
1919
systemctl enable tailscaled.service
2020
systemctl enable ublue-system-setup.service
2121

22+
# see /usr/bin/rechunker-group-fix
23+
# DO NOT REMOVE THIS
24+
systemctl enable rechunker-group-fix.service
25+
2226
# run flatpak preinstall once at startup
2327
if [[ "$(rpm -E %fedora)" -ge "42" ]]; then
2428
systemctl enable flatpak-preinstall.service
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
3+
# To use this script, you'll want to put this in your systemd service:
4+
# rm /etc/gshadow
5+
# systemd-sysusers
6+
# (run this script)
7+
# systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
8+
# This will populate /etc/group successfully, and then populate /etc/gshadow
9+
# with any missing groups that we nuked when we removed /etc/gshadow
10+
11+
GSHADOW_FILE="/etc/gshadow"
12+
GROUP_FILE="/etc/group"
13+
14+
for f in $(cat $GROUP_FILE); do
15+
cut -f1 -d':' <(echo $f) | xargs -I{} grep ^"{}:" $GSHADOW_FILE &>/dev/null || \
16+
echo $(cut -f1 -d':' <(echo $f)):'!*::' >> $GSHADOW_FILE
17+
done

0 commit comments

Comments
 (0)