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
96 changes: 96 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# CI workflow for Flynn
#
# Builds all components and runs unit tests on every push and pull request
# to the tuf-rebuild branch. Uses a Debian container with Go 1.13.15 and
# all required system dependencies.
#
# The build uses script/bootstrap-build which compiles all 34 Flynn binaries
# from source without requiring a running Flynn cluster.
#
# Note: Debian Buster (10) is EOL and its repos are offline. We use
# Bookworm (12) for system packages; Go 1.13 is installed separately.

name: CI

on:
push:
branches: [tuf-rebuild, master]
pull_request:
branches: [tuf-rebuild, master]

jobs:
build:
name: Build all components
runs-on: ubuntu-latest
container:
image: debian:bookworm-slim

steps:
- name: Install system dependencies
run: |
apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gcc \
libseccomp-dev \
git \
python3 \
curl \
ca-certificates

- name: Install Go 1.13.15
run: |
curl -fsSL "https://go.dev/dl/go1.13.15.linux-amd64.tar.gz" \
| tar -C /usr/local -xz
echo "/usr/local/go/bin" >> $GITHUB_PATH
echo "GOROOT=/usr/local/go" >> $GITHUB_ENV
echo "GOPATH=/go" >> $GITHUB_ENV
echo "/go/bin" >> $GITHUB_PATH

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

- name: Build all components
run: script/bootstrap-build --version "ci-$(echo "$GITHUB_SHA" | cut -c1-8)"

- name: Verify binaries
run: |
echo "Built binaries:"
ls -la build/bin/
echo ""
echo "Binary count: $(ls -1 build/bin/ | grep -v '.gz$' | grep -v '^flynn$' | wc -l)"
echo ""
echo "flynn-host version:"
build/bin/flynn-host version || true

test:
name: Unit tests
runs-on: ubuntu-latest
container:
image: debian:bookworm-slim

steps:
- name: Install system dependencies
run: |
apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gcc \
libseccomp-dev \
git \
python3 \
curl \
ca-certificates

- name: Install Go 1.13.15
run: |
curl -fsSL "https://go.dev/dl/go1.13.15.linux-amd64.tar.gz" \
| tar -C /usr/local -xz
echo "/usr/local/go/bin" >> $GITHUB_PATH
echo "GOROOT=/usr/local/go" >> $GITHUB_ENV
echo "GOPATH=/go" >> $GITHUB_ENV
echo "/go/bin" >> $GITHUB_PATH

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

- name: Run standalone unit tests
run: make test-unit-standalone
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ demo/*.log
*.test
/tmp
/build
._*
*.backup
53 changes: 53 additions & 0 deletions Dockerfile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Dockerfile.ci — Reproducible build environment for Flynn CI
#
# This image provides Go 1.13.15 with CGO support and all system dependencies
# needed to build all Flynn components and run unit tests.
#
# Usage:
# docker build -f Dockerfile.ci -t flynn-ci .
# docker run --rm -v $(pwd):/go/src/github.com/flynn/flynn -w /go/src/github.com/flynn/flynn flynn-ci script/bootstrap-build --version dev
#
# For unit tests:
# docker run --rm -v $(pwd):/go/src/github.com/flynn/flynn -w /go/src/github.com/flynn/flynn flynn-ci make test-unit-standalone

FROM debian:buster-slim

# Avoid interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# Install system dependencies:
# - build-essential, gcc: CGO compilation (required for flynn-host / libcontainer)
# - libseccomp-dev: seccomp support for container runtime
# - git: version detection, go module operations
# - python3: TUF root key extraction from builder/manifest.json
# - squashfs-tools: mksquashfs for building image layers (export-tuf)
# - debootstrap: building Ubuntu base layers (export-tuf)
# - busybox-static: building busybox base layer (export-tuf)
# - curl, ca-certificates: downloading Go toolchain
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gcc \
libseccomp-dev \
git \
python3 \
squashfs-tools \
debootstrap \
busybox-static \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# Install Go 1.13.15 (the required version for this project)
ENV GO_VERSION=1.13.15
RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
| tar -C /usr/local -xz

ENV GOROOT=/usr/local/go
ENV GOPATH=/go
ENV PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"

# Set the working directory to the Flynn source tree
WORKDIR /go/src/github.com/flynn/flynn

# Default: show Go version to verify setup
CMD ["go", "version"]
45 changes: 43 additions & 2 deletions Makefile
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
GO_ENV=GOROOT=`readlink -f build/_go`
# Portable readlink -f (works on macOS and GNU/Linux)
_readlink_f = $(shell cd $(1) && pwd -P 2>/dev/null)
GO_ENV = GOROOT=$(call _readlink_f,build/_go)

build:
script/build-flynn

bootstrap-build:
script/bootstrap-build

release:
script/build-flynn --git-version

Expand All @@ -11,13 +16,49 @@ clean:

test: test-unit test-integration

# test-unit requires a prior 'make build' (uses GOROOT from build output)
test-unit: build
env $(GO_ENV) PATH=${PWD}/build/bin:${PATH} go test -race -cover ./...

test-unit-root: test-unit
sudo -E env $(GO_ENV) PATH=${PWD}/build/bin:${PATH} go test -race -cover ./host/volume/...

# test-unit-standalone runs pure Go tests without requiring 'make build'.
# Uses the system Go toolchain directly. Suitable for CI where Go is installed
# via the Dockerfile rather than extracted from a build image.
#
# These packages have no external service dependencies (no PostgreSQL, discoverd,
# Redis, ZFS, etc.) and can run in any Linux environment with Go installed.
TEST_PACKAGES_STANDALONE = \
./pkg/attempt/... \
./pkg/cors/... \
./pkg/ipallocator/... \
./pkg/lru/... \
./pkg/mauth/compare/... \
./pkg/mux/... \
./pkg/pinned/... \
./pkg/rpcplus/... \
./pkg/signal/... \
./pkg/stream/... \
./pkg/syslog/rfc5424/... \
./pkg/sirenia/state/... \
./flannel/pkg/ip/... \
./flannel/subnet/... \
./host/resource/... \
./controller/scheduler/... \
./discoverd/client/... \
./discoverd/health/... \
./logaggregator/buffer/... \
./logaggregator/snapshot/... \
./router/proxyproto/...
# Excluded from standalone tests:
# ./pkg/lockedfile/... — imports internal/testenv (Go stdlib internal, not allowed)
# ./pkg/term/... — requires /dev/tty (not available in CI containers)

test-unit-standalone:
GO111MODULE=on go test -mod=vendor -race -cover $(TEST_PACKAGES_STANDALONE)

test-integration: build
script/run-integration-tests

.PHONY: build release clean test test-unit test-unit-root test-integration
.PHONY: build bootstrap-build release clean test test-unit test-unit-root test-unit-standalone test-integration
5 changes: 3 additions & 2 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ func newTUFClient(config *TUFConfig, dbPath string) (*tuf.Client, error) {
return client, nil
}
if err == tuf.ErrNoRootKeys {
if err := client.Init(config.RootKeys, len(config.RootKeys)); err != nil {
if err := client.Init(config.RootKeys, 1); err != nil {
return nil, err
}
_, err = client.Update()
Expand Down Expand Up @@ -1052,7 +1052,8 @@ type fileInput struct {
// inputs and computing the SHA512/256 sum of the resulting bytes.
//
// TODO: consider storing a map of filenames to hashes and cache based
// on the last modified time to avoid unnecessary work.
//
// on the last modified time to avoid unnecessary work.
func (b *Builder) generateLayerID(name string, run []string, env map[string]string, artifact *ct.Artifact, inputs ...string) (id string, err error) {
start := time.Now()
defer func() {
Expand Down
34 changes: 22 additions & 12 deletions builder/img/busybox.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
#!/bin/bash
#
# Build a minimal busybox rootfs as a squashfs layer.
#
# Requires busybox-static to be installed on the host system:
# apt-get install -y busybox-static
#

TMP="$(mktemp --directory)"
set -e

URL="http://archive.ubuntu.com/ubuntu/pool/main/b/busybox/busybox-static_1.22.0-19ubuntu2_amd64.deb"
SHA="f5796bf9d10d60850ab379e3d1cfee138ae8d636691dfe62f86854980baa408b"
curl -fSLo "${TMP}/busybox.deb" "${URL}"
echo "${SHA} ${TMP}/busybox.deb" | shasum -a "256" -c -
TMP="$(mktemp --directory)"
trap "rm -rf '${TMP}'" EXIT

dpkg -i "${TMP}/busybox.deb"
# Use the system-installed busybox-static binary
BUSYBOX="$(which busybox)"
if [[ ! -x "${BUSYBOX}" ]]; then
echo "ERROR: busybox-static not found. Install with: apt-get install -y busybox-static" >&2
exit 1
fi

mkdir "${TMP}/root"
cd "${TMP}/root"
mkdir bin etc dev dev/pts lib proc sys tmp
touch etc/resolv.conf
cp /etc/nsswitch.conf etc/nsswitch.conf
echo root:x:0:0:root:/:/bin/sh > etc/passwd
echo root:x:0: > etc/group
echo root:x:0:0:root:/:/bin/sh >etc/passwd
echo root:x:0: >etc/group
ln -s lib lib64
ln -s bin sbin
cp /bin/busybox bin
cp "${BUSYBOX}" bin/busybox
for name in $(busybox --list); do
ln -s busybox "bin/${name}"
[[ "${name}" = "busybox" ]] && continue
ln -s busybox "bin/${name}"
done
cp /lib/x86_64-linux-gnu/lib{c,dl,nsl,nss_*,pthread,resolv}.so.* lib
cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 lib
cp /lib/x86_64-linux-gnu/lib{c,dl,nsl,nss_*,pthread,resolv}.so.* lib 2>/dev/null || true
cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 lib 2>/dev/null || true

mksquashfs "${TMP}/root" "/mnt/out/layer.squashfs" -noappend
54 changes: 44 additions & 10 deletions builder/img/ubuntu-bionic.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,21 +1,55 @@
#!/bin/bash
#
# Build an Ubuntu 18.04 (Bionic) rootfs as a squashfs layer.
#
# Requires debootstrap on the host:
# apt-get install -y debootstrap
#

set -e

TMP="$(mktemp --directory)"

URL="https://partner-images.canonical.com/core/bionic/20190621/ubuntu-bionic-core-cloudimg-amd64-root.tar.gz"
SHA="ed1753585d70724010e9ca26cf47337201ecc5c65c7251ca7a97b5d1c0ed6365"
curl -fSLo "${TMP}/ubuntu.tar.gz" "${URL}"
echo "${SHA} ${TMP}/ubuntu.tar.gz" | sha256sum -c -
cleanup() {
# Unmount bind mounts
umount "${TMP}/root/dev/pts" 2>/dev/null || true
umount "${TMP}/root/dev" 2>/dev/null || true
umount "${TMP}/root/proc" 2>/dev/null || true
umount "${TMP}/root/sys" 2>/dev/null || true
# Clear resolv.conf
>"${TMP}/root/etc/resolv.conf" 2>/dev/null || true
rm -rf "${TMP}"
}
trap cleanup EXIT

mkdir -p "${TMP}/root"
tar xf "${TMP}/ubuntu.tar.gz" -C "${TMP}/root"

# Use debootstrap to create a minimal Bionic rootfs
if command -v debootstrap >/dev/null 2>&1; then
echo "Building Ubuntu Bionic rootfs via debootstrap..."
debootstrap --variant=minbase --arch=amd64 bionic "${TMP}/root" http://archive.ubuntu.com/ubuntu
else
# Fallback: download the minimal cloud image root tarball
echo "Building Ubuntu Bionic rootfs via cloud image download..."
URL="https://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64-root.tar.xz"
curl -fSLo "${TMP}/ubuntu.tar.xz" "${URL}"
tar xf "${TMP}/ubuntu.tar.xz" -C "${TMP}/root"
fi

# Set up bind mounts for chroot
mount --bind /dev "${TMP}/root/dev"
mount --bind /dev/pts "${TMP}/root/dev/pts"
mount -t proc proc "${TMP}/root/proc"
mount -t sysfs sysfs "${TMP}/root/sys"

cp "/etc/resolv.conf" "${TMP}/root/etc/resolv.conf"
cleanup() {
>"${TMP}/root/etc/resolv.conf"
}
trap cleanup EXIT

chroot "${TMP}/root" bash -e < "builder/ubuntu-setup.sh"
chroot "${TMP}/root" bash -e <"builder/ubuntu-setup.sh"

# Unmount before creating squashfs
umount "${TMP}/root/sys" 2>/dev/null || true
umount "${TMP}/root/proc" 2>/dev/null || true
umount "${TMP}/root/dev/pts" 2>/dev/null || true
umount "${TMP}/root/dev" 2>/dev/null || true

mksquashfs "${TMP}/root" "/mnt/out/layer.squashfs" -noappend
Loading
Loading