Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .devcontainer
131 changes: 131 additions & 0 deletions .github/workflows/build-devcontainer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Build DevContainer

on:
push:
branches: [main]
tags: ['v*']
pull_request:
paths:
- 'devenv/**'
- '.github/workflows/build-devcontainer.yml'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/devenv-debian

jobs:
validate-devcontainer:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install just
uses: extractions/setup-just@v2

- name: Validate devcontainer.json syntax
run: just devcontainer-validate

build:
needs: validate-devcontainer
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
platform: linux/amd64
arch: amd64
- runner: ubuntu-24.04-arm
platform: linux/arm64
arch: arm64

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: devenv
file: devenv/Containerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ github.event_name != 'pull_request' }}

- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"

- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.arch }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

merge:
runs-on: ubuntu-latest
needs: build
if: github.event_name != 'pull_request'
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,prefix={{branch}}-,format=short
type=sha,prefix={{branch}}-,format=long
type=ref,event=pr
type=ref,event=tag

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)

- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}

35 changes: 35 additions & 0 deletions .github/workflows/container-gc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Container Image Garbage Collection

on:
workflow_dispatch:
inputs:
retention-days:
description: "Delete container images older than this many days"
required: false
default: "14"
type: string
dry-run:
description: "Dry run mode - don't actually delete anything"
required: false
default: false
type: boolean
schedule:
# Run weekly on Sundays at 2 AM UTC
- cron: '0 2 * * 0'

jobs:
cleanup:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Delete old container images
uses: snok/container-retention-policy@v3.0.0
with:
account: ${{ github.repository_owner }}
token: ${{ secrets.GITHUB_TOKEN }}
cut-off: ${{ github.event.inputs.retention-days || '14' }}d
dry-run: ${{ github.event.inputs.dry-run || false }}

7 changes: 7 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Validate devcontainer.json syntax
devcontainer-validate:
npx --yes @devcontainers/cli read-configuration --workspace-folder .

# Build devenv image with local tag
devenv-build:
cd devenv && podman build --jobs=4 -t localhost/bootc-devenv-debian .
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ This repository provides centralised configuration and automation for the [bootc
## Table of Contents

- [Purpose](#purpose)
- [Development Environment](#development-environment)
- [Container Image Management](#container-image-management)
- [Renovate](#renovate)
- [Getting Started](#getting-started)
- [Support & Contributions](#support--contributions)
Expand All @@ -24,6 +26,18 @@ The main goal of this repository is to:

---

## Development Environment

Containerized development environment with necessary tools and dependencies. For more,
see [devenv/README.md](devenv/README.md).

---

## Container Garbage Collection

Automated cleanup of old container images from GitHub Container Registry.

---

## Renovate

Expand Down
28 changes: 28 additions & 0 deletions common/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "bootc-devenv-debian",
// TODO override this back to prod image
"image": "ghcr.io/bootc-dev/devenv-debian",
"customizations": {
"vscode": {
// Abitrary, but most of our code is in one of these two
"extensions": [
"rust-lang.rust-analyzer",
"golang.Go"
]
}
},
"features": {},
"runArgs": [
// Because we want to be able to run podman and also use e.g. /dev/kvm
// among other things
"--privileged"
],
"postCreateCommand": {
// Our init script
"devenv-init": "sudo /usr/local/bin/devenv-init.sh"
},
"remoteEnv": {
"PATH": "${containerEnv:PATH}:/usr/local/cargo/bin"
}
}

8 changes: 8 additions & 0 deletions devenv/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Exclude everything by default, then include just what we need
# Especially note this means that .git is not included, and not tests/
# to avoid spurious rebuilds.
*
# And explicit includes
!packages.txt
!build-deps.txt
!devenv-init.sh
108 changes: 108 additions & 0 deletions devenv/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# These aren't packages, just low-dependency binaries dropped in /usr/local/bin
# so we can fetch them independently in a separate build.
ARG base=docker.io/library/debian:sid
FROM $base as base

Check warning on line 4 in devenv/Containerfile

View workflow job for this annotation

GitHub Actions / build (ubuntu-24.04-arm, linux/arm64, arm64)

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 4 in devenv/Containerfile

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, linux/amd64, amd64)

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
# Life is too short to care about dash
RUN ln -sfr /bin/bash /bin/sh
RUN <<EORUN
set -xeuo pipefail

# Initialize some basic packages
apt -y update && apt -y install curl time bzip2

# Enable deb-src repositories for build-dep
sed -i "s/^deb /deb [arch=$(dpkg --print-architecture)] /" /etc/apt/sources.list.d/debian.sources
sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/debian.sources

# Enable gh CLI repository
mkdir -p -m 755 /etc/apt/keyrings
curl -fLo /etc/apt/keyrings/githubcli-archive-keyring.gpg https://cli.github.com/packages/githubcli-archive-keyring.gpg
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
mkdir -p -m 755 /etc/apt/sources.list.d
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list

# And re-update after we've fetched repos
apt -y update
EORUN

FROM base as tools

Check warning on line 28 in devenv/Containerfile

View workflow job for this annotation

GitHub Actions / build (ubuntu-24.04-arm, linux/arm64, arm64)

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 28 in devenv/Containerfile

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, linux/amd64, amd64)

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
# renovate: datasource=github-releases depName=block/goose
ARG gooseversion=v1.11.1
# renovate: datasource=github-releases depName=bootc-dev/bcvk
ARG bcvkversion=v0.5.3
RUN <<EORUN
set -xeuo pipefail
arch=$(arch)

rm -vrf /usr/local/bin/*

# goose for local AI
target=goose-${arch}-unknown-linux-gnu.tar.bz2
/bin/time -f '%E %C' curl -fLO https://github.com/block/goose/releases/download/$gooseversion/$target
tar xvjf $target
mv goose /usr/local/bin/goose

# bcvk
if test "${arch}" = x86_64; then
td=$(mktemp -d)
cd $td
target=bcvk-${arch}-unknown-linux-gnu
/bin/time -f '%E %C' curl -fLO https://github.com/bootc-dev/bcvk/releases/download/$bcvkversion/${target}.tar.gz
tar xvzf $target.tar.gz
mv $target /usr/local/bin/bcvk
cd -
rm -rf $td
else
echo bcvk unavailable for $arch
fi
EORUN

FROM base as rust

Check warning on line 60 in devenv/Containerfile

View workflow job for this annotation

GitHub Actions / build (ubuntu-24.04-arm, linux/arm64, arm64)

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

Check warning on line 60 in devenv/Containerfile

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest, linux/amd64, amd64)

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
RUN <<EORUN
set -xeuo pipefail
# Setup rust; the idea here though is we install system-wide into /usr/local
# as if it was packaged.
export RUSTUP_HOME=/usr/local/rustup
export CARGO_HOME=/usr/local/cargo
# Install Rust system-wide
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile default
# Move binaries to /usr/local/bin (system-managed, root-owned)
mv /usr/local/cargo/bin/* /usr/local/bin/
# Nothing really left here
rm -vrf /usr/local/cargo/bin
EORUN

# This builds the image.
# Build this using `just devenv-build` from the root of the repository.
FROM base
COPY packages.txt build-deps.txt /run/src/
WORKDIR /run/src
RUN <<EORUN
set -xeuo pipefail
grep -vEe '^#' packages.txt | /bin/time -f '%E %C' xargs apt -y install
grep -vEe '^#' build-deps.txt | /bin/time -f '%E %C' xargs apt -y build-dep
apt clean && rm -rf /var/lib/apt/lists/*
EORUN

# Copy in the binaries from our tools container image
COPY --from=tools /usr/local/bin/* /usr/local/bin/
COPY --from=rust /usr/local/bin/* /usr/local/bin/
COPY --from=rust /usr/local/rustup /usr/local/rustup
# Point rustup at the system-wide installation, but let CARGO_HOME default to ~/.cargo
ENV RUSTUP_HOME=/usr/local/rustup
# Setup for codespaces
COPY devenv-init.sh /usr/local/bin/

WORKDIR /
# Create user before declaring volumes so home directory has correct ownership
RUN <<EORUN
set -xeuo pipefail
useradd -m devenv -s /bin/bash
# This needs to be precreated and owned by the devenv user
mkdir -p ~devenv/.local/share/containers
chown -R -h devenv: ~devenv/.local
echo 'devenv ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/devenv && chmod 0440 /etc/sudoers.d/devenv
EORUN
# To avoid overlay-on-overlay with nested containers
VOLUME [ "/var/lib/containers", "/home/devenv/.local/share/containers/" ]
USER devenv
21 changes: 21 additions & 0 deletions devenv/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# A devcontainer for work on bootc-org projects

This container image is suitable for use on
developing projects in the bootc-dev organization,
especially bootc.

It includes all tools used in the Justfile
for relevant projects.

## Base image

At the current time the default is using Debian sid, mainly because
other parts of the upstream use CentOS Stream as a *target system*
base, but this helps prove out the general case of "src != target"
that is a philosophy of bootc (and containers in general)
as well as just helping prepare/motivate for bootc-on-Debian.

## Building locally

See the `Justfile`, but it's just a thin wrapper around a default
of `podman build` of this directory.
1 change: 1 addition & 0 deletions devenv/build-deps.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ostree
14 changes: 14 additions & 0 deletions devenv/devenv-init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
set -euo pipefail
# Set things up so that podman can run nested inside the privileged
# docker container of a codespace.

# Fix the propagation
sudo mount -o remount --make-shared /

# This is actually safe to expose to all users really, like Fedora derivatives do
chmod a+rw /dev/kvm

# Handle nested cgroups
sed -i -e 's,^#cgroups =.*,cgroups = "no-conmon",' /usr/share/containers/containers.conf
sed -i -e 's,^#cgroup_manager =.*,cgroup_manager = "cgroupfs",' /usr/share/containers/containers.conf
Loading