Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2d63e7c
feat: chiselled rocks build for bread flavours
lczyk Jun 1, 2026
a1a5319
fix: retry chisel-releases archive flakes when cutting rock slices
lczyk Jun 1, 2026
25d9eab
fix: use iproute2_bins slice (ip-bin is 26.04-only)
lczyk Jun 1, 2026
31cbacd
fix: nest contract task under run/ so spread discovers it
lczyk Jun 1, 2026
9334757
fix: grep-free contract task + bounded allocate with diagnostics
lczyk Jun 1, 2026
8d1125f
fix: robust banner check + TCP-22 allocate probe with diagnostics
lczyk Jun 1, 2026
5661ebc
fix: add sshd-session + sshd-auth to openssh slice on 25.10/26.04
lczyk Jun 2, 2026
d8abfc0
fix: clear BREAD_BANNER_SHOWN in banner contract check
lczyk Jun 2, 2026
a81e6ee
refactor: dedupe rock hack/patches into shared dir + ruby sync
lczyk Jun 2, 2026
6d1bb02
ci: trial node24 rockcraft-pack from craft-actions#59
lczyk Jun 2, 2026
5001423
refactor: source rock chisel patches from repo root, drop rocks/patches
lczyk Jun 2, 2026
71991bc
refactor: single rocks/makefile with build-% pattern, drop per-dir ma…
lczyk Jun 2, 2026
3b4844a
refactor: couple rock spread suite into top-level tests/
lczyk Jun 2, 2026
0970978
refactor: wire rock backend + suite into tests/spread.yaml
lczyk Jun 2, 2026
662e4d9
refactor: source shared rock hack scripts from repo root
lczyk Jun 2, 2026
53e767e
refactor: point sync_shared.rb at repo-root hack for shared scripts
lczyk Jun 2, 2026
e81b3ae
chore: empty commit
lczyk Jun 2, 2026
f0218b2
ci: revert rockcraft-pack to canonical@main
lczyk Jun 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ name: CI
run-name: CI for ${{ github.ref }}

# Build + test on PR and on push to main. No publishing.
# Rock-only changes are handled by rocks-ci.yaml -- ignore them here.

on:
pull_request:
paths-ignore:
- 'rocks/**'
push:
branches:
- main
paths-ignore:
- 'rocks/**'
workflow_dispatch:

# Cancel in-flight runs on the same ref when a newer one starts.
Expand Down
112 changes: 112 additions & 0 deletions .github/workflows/rocks-ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: rocks CI
run-name: rocks CI for ${{ github.ref }}

# Build + test the chiselled rocks on PR + push to main. No publishing.
# Build packs each rock per-arch (native runners); test loads that arch's rocks
# into the local daemon and runs the single top-level spread suite over the
# native-arch systems.
on:
pull_request:
paths:
- 'rocks/**'
- '.github/workflows/rocks-ci.yaml'
push:
branches:
- main
paths:
- 'rocks/**'
- '.github/workflows/rocks-ci.yaml'
workflow_dispatch:

concurrency:
group: rocks-ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
build:
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
dir:
- bread-24.04
- bread-25.10
- bread-26.04
- bread-chisel-releases-24.04
- bread-chisel-releases-25.10
- bread-chisel-releases-26.04
arch:
- amd64
- arm64
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Sync shared hack/ + patches/ into the rock dir
run: ruby rocks/sync_shared.rb ${{ matrix.dir }}

- name: Pack rock
uses: canonical/craft-actions/rockcraft-pack@main
with:
path: rocks/${{ matrix.dir }}
verbosity: brief

- name: Rename to predictable artefact name
run: mv rocks/${{ matrix.dir }}/*.rock rocks/${{ matrix.dir }}/${{ matrix.dir }}.rock

- name: Upload rock artefact
uses: actions/upload-artifact@v6
with:
name: rock-${{ matrix.dir }}-${{ matrix.arch }}
path: rocks/${{ matrix.dir }}/${{ matrix.dir }}.rock

test:
needs: build
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
- arch: arm64
runner: ubuntu-24.04-arm
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Download this arch's rocks
uses: actions/download-artifact@v6
with:
pattern: rock-*-${{ matrix.arch }}
path: dl

- name: Place rocks under their dirs
run: |
set -e
for d in dl/rock-*-${{ matrix.arch }}; do
base=$(basename "$d") # rock-<dir>-<arch>
dir=${base#rock-}
dir=${dir%-${{ matrix.arch }}}
cp "$d/$dir.rock" "rocks/$dir/$dir.rock"
done

- name: Install spread + skopeo
run: |
sudo apt-get update
sudo apt-get install -y skopeo
go install github.com/canonical/spread/cmd/spread@latest
echo "$HOME/go/bin" >> "$GITHUB_PATH"

- name: Run the top-level spread suite (${{ matrix.arch }})
run: make -C rocks spread-only
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,14 @@
.spread-reuse*.yaml
.stamp/
cache/

# rockcraft (rocks/) build artefacts
*.rock
.rock.stamp

# per-rock hack/ + patches/ are synced from rocks/hack + rocks/patches
# (see rocks/sync_shared.rb) -- only the shared source of truth is tracked.
rocks/bread-*/hack/
rocks/bread-*/patches/
__pycache__/
*.pyc
117 changes: 117 additions & 0 deletions rocks/bread-24.04/rockcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# spellchecker: ignore rockcraft chiselled coreutils passwd nologin sshd usrmerge pebble noninteractive

name: bread
version: "24.04"
summary: chiselled ubuntu 24.04 + sshd spread test-host rock
description: |
Throwaway spread test-host: a chiselled (base: bare) Ubuntu 24.04 rock with
just enough to act as a spread system -- bash, coreutils, openssh-server,
apt, and a pam_permit-only auth stack (accepts ANY password for root).

INSECURE BY DESIGN. Built for ephemeral spread test containers only; never
expose to a network or use in production.

base: bare
build-base: ubuntu@24.04

platforms:
amd64:
arm64:

# sshd runs as a pebble service (pebble is pid 1 in the rock). The
# openssh-server_services slice ships systemd units, not a pebble layer, so we
# drive sshd ourselves via hack/sshd-entry.sh (host-key gen + exec sshd -D).
services:
sshd:
override: replace
command: /usr/local/bin/sshd-entry.sh
startup: enabled

parts:
bread:
# We chisel-cut the slices ourselves rather than using stage-packages:
# rockcraft's stage-packages cut has no retry, and the chisel-releases
# archive intermittently flakes ("expected digest ..."). hack/chisel_cut.sh
# retries around that. (Pattern from not-quite-rust-rock.)
plugin: nil
build-snaps:
- chisel
build-packages:
- git
override-build: |
set -eu

releases=$(mktemp -d)
git clone --depth 1 --branch ubuntu-24.04 \
https://github.com/canonical/chisel-releases.git "$releases"

# tar + gzip: needed by spread to sync the project onto the system over
# ssh (not part of coreutils). dpkg_bins: apt_apt-get only pulls
# dpkg_maintscript-support, so apt-get install can't unpack without it.
# pam: only libpam-modules_libs (carries pam_permit.so) -- NOT the
# _config / _pam-config default stacks.
bash "$CRAFT_PROJECT_DIR/hack/chisel_cut.sh" "$releases" \
base-files_base base-files_etc base-files_var base-files_tmp \
base-files_lib base-files_home base-files_release-info base-files_chisel \
bash_bins coreutils_bins tar_bins gzip_bins \
base-passwd_data login_bins passwd_bins \
openssh-server_bins openssh-server_config ca-certificates_data \
libpam-modules_libs \
apt_apt-get apt_config apt_var apt_default-source-list dpkg_bins

cd "$CRAFT_PART_INSTALL"

# --- users: base-passwd_data gives us /etc/passwd + /etc/group (incl
# root), but not the sshd privsep user nor any /etc/shadow. add both.
grep -q '^sshd:' etc/passwd || \
echo 'sshd:x:101:65534::/run/sshd:/usr/sbin/nologin' >> etc/passwd
# root password "bread" (matches the docker-image flavour). pam_permit
# makes the value irrelevant for auth, but root must not be locked.
hash=$(openssl passwd -6 bread)
{
printf 'root:%s:20000:0:99999:7:::\n' "$hash"
printf 'sshd:!:20000:0:99999:7:::\n'
} > etc/shadow
chmod 0640 etc/shadow

# --- sshd config. openssh-server_config ships sshd_config.d/ but not
# /etc/ssh/sshd_config itself, so write a minimal one.
mkdir -p etc/ssh
cat > etc/ssh/sshd_config <<'EOF'
PermitRootLogin yes
PasswordAuthentication yes
UsePAM yes
Subsystem sftp /usr/lib/openssh/sftp-server
PidFile /run/sshd.pid
EOF

# --- pam: permit-only stack (always-allow), verbatim shape from the
# docker-image flavour.
mkdir -p etc/pam.d
printf '%s\n' \
'auth required pam_permit.so' \
'account required pam_permit.so' \
'session required pam_permit.so' \
'password required pam_permit.so' \
> etc/pam.d/sshd

# --- /etc/profile: base-files ships profile.d/ but not /etc/profile, so
# login shells wouldn't source the bread-warning banner. add a minimal one.
cat > etc/profile <<'EOF'
if [ -d /etc/profile.d ]; then
for __pf in /etc/profile.d/*.sh; do
[ -r "$__pf" ] && . "$__pf"
done
unset __pf
fi
EOF

# --- bread bits: sshd entrypoint, lazy-apt shim, insecure-image banner.
install -D -m0755 "$CRAFT_PROJECT_DIR/hack/sshd-entry.sh" usr/local/bin/sshd-entry.sh
install -D -m0755 "$CRAFT_PROJECT_DIR/hack/lazy-apt.sh" usr/local/bin/apt-get
install -m0755 "$CRAFT_PROJECT_DIR/hack/lazy-apt.sh" usr/local/bin/apt
install -D -m0644 "$CRAFT_PROJECT_DIR/hack/banner.txt" etc/bread-banner.txt
install -D -m0644 "$CRAFT_PROJECT_DIR/hack/bread-warning.sh" etc/profile.d/00-bread-warning.sh
mkdir -p root
printf '\n# bread-warning\n[ -r /etc/profile.d/00-bread-warning.sh ] && . /etc/profile.d/00-bread-warning.sh\n' \
>> root/.bashrc
126 changes: 126 additions & 0 deletions rocks/bread-25.10/rockcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# spellchecker: ignore rockcraft chiselled coreutils passwd nologin sshd usrmerge pebble noninteractive

name: bread
version: "25.10"
summary: chiselled ubuntu 25.10 + sshd spread test-host rock
description: |
Throwaway spread test-host: a chiselled (base: bare) Ubuntu 25.10 rock with
just enough to act as a spread system -- bash, coreutils, openssh-server,
apt, and a pam_permit-only auth stack (accepts ANY password for root).

INSECURE BY DESIGN. Built for ephemeral spread test containers only; never
expose to a network or use in production.

base: bare
build-base: ubuntu@25.10

platforms:
amd64:
arm64:

# sshd runs as a pebble service (pebble is pid 1 in the rock). The
# openssh-server_services slice ships systemd units, not a pebble layer, so we
# drive sshd ourselves via hack/sshd-entry.sh (host-key gen + exec sshd -D).
services:
sshd:
override: replace
command: /usr/local/bin/sshd-entry.sh
startup: enabled

parts:
bread:
# We chisel-cut the slices ourselves rather than using stage-packages:
# rockcraft's stage-packages cut has no retry, and the chisel-releases
# archive intermittently flakes ("expected digest ..."). hack/chisel_cut.sh
# retries around that. (Pattern from not-quite-rust-rock.)
plugin: nil
build-snaps:
- chisel
build-packages:
- git
override-build: |
set -eu

releases=$(mktemp -d)
git clone --depth 1 --branch ubuntu-25.10 \
https://github.com/canonical/chisel-releases.git "$releases"

# OpenSSH >=9.8 (this release) re-execs sshd-session + sshd-auth per
# connection; the openssh-server slice ships only /usr/sbin/sshd, so sshd
# exits 255 at startup. Add them to the cloned slice before cutting.
# (24.04's OpenSSH 9.6 predates the split and has no such binaries.)
sed -i \
-e '\#^ /usr/sbin/sshd:#a\ /usr/lib/openssh/sshd-session:' \
-e '\#^ /usr/sbin/sshd:#a\ /usr/lib/openssh/sshd-auth:' \
"$releases/slices/openssh-server.yaml"

# tar + gzip: needed by spread to sync the project onto the system over
# ssh (not part of coreutils). dpkg_bins: apt_apt-get only pulls
# dpkg_maintscript-support, so apt-get install can't unpack without it.
# pam: only libpam-modules_libs (carries pam_permit.so) -- NOT the
# _config / _pam-config default stacks.
bash "$CRAFT_PROJECT_DIR/hack/chisel_cut.sh" "$releases" \
base-files_base base-files_etc base-files_var base-files_tmp \
base-files_lib base-files_home base-files_release-info base-files_chisel \
bash_bins coreutils_bins tar_bins gzip_bins \
base-passwd_data login_bins passwd_bins \
openssh-server_bins openssh-server_config ca-certificates_data \
libpam-modules_libs \
apt_apt-get apt_config apt_var apt_default-source-list dpkg_bins

cd "$CRAFT_PART_INSTALL"

# --- users: base-passwd_data gives us /etc/passwd + /etc/group (incl
# root), but not the sshd privsep user nor any /etc/shadow. add both.
grep -q '^sshd:' etc/passwd || \
echo 'sshd:x:101:65534::/run/sshd:/usr/sbin/nologin' >> etc/passwd
# root password "bread" (matches the docker-image flavour). pam_permit
# makes the value irrelevant for auth, but root must not be locked.
hash=$(openssl passwd -6 bread)
{
printf 'root:%s:20000:0:99999:7:::\n' "$hash"
printf 'sshd:!:20000:0:99999:7:::\n'
} > etc/shadow
chmod 0640 etc/shadow

# --- sshd config. openssh-server_config ships sshd_config.d/ but not
# /etc/ssh/sshd_config itself, so write a minimal one.
mkdir -p etc/ssh
cat > etc/ssh/sshd_config <<'EOF'
PermitRootLogin yes
PasswordAuthentication yes
UsePAM yes
Subsystem sftp /usr/lib/openssh/sftp-server
PidFile /run/sshd.pid
EOF

# --- pam: permit-only stack (always-allow), verbatim shape from the
# docker-image flavour.
mkdir -p etc/pam.d
printf '%s\n' \
'auth required pam_permit.so' \
'account required pam_permit.so' \
'session required pam_permit.so' \
'password required pam_permit.so' \
> etc/pam.d/sshd

# --- /etc/profile: base-files ships profile.d/ but not /etc/profile, so
# login shells wouldn't source the bread-warning banner. add a minimal one.
cat > etc/profile <<'EOF'
if [ -d /etc/profile.d ]; then
for __pf in /etc/profile.d/*.sh; do
[ -r "$__pf" ] && . "$__pf"
done
unset __pf
fi
EOF

# --- bread bits: sshd entrypoint, lazy-apt shim, insecure-image banner.
install -D -m0755 "$CRAFT_PROJECT_DIR/hack/sshd-entry.sh" usr/local/bin/sshd-entry.sh
install -D -m0755 "$CRAFT_PROJECT_DIR/hack/lazy-apt.sh" usr/local/bin/apt-get
install -m0755 "$CRAFT_PROJECT_DIR/hack/lazy-apt.sh" usr/local/bin/apt
install -D -m0644 "$CRAFT_PROJECT_DIR/hack/banner.txt" etc/bread-banner.txt
install -D -m0644 "$CRAFT_PROJECT_DIR/hack/bread-warning.sh" etc/profile.d/00-bread-warning.sh
mkdir -p root
printf '\n# bread-warning\n[ -r /etc/profile.d/00-bread-warning.sh ] && . /etc/profile.d/00-bread-warning.sh\n' \
>> root/.bashrc
Loading
Loading