Skip to content

cell claude and other cell launches drop from ~57s to ~10s on macOS… #180

cell claude and other cell launches drop from ~57s to ~10s on macOS…

cell claude and other cell launches drop from ~57s to ~10s on macOS… #180

Workflow file for this run

name: Dev Build
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
on:
push:
branches:
- main
- feature/wip
- feature/add-web
- feature/web
workflow_dispatch:
inputs:
skip_nix_cache:
description: 'Skip nix cache (genesis mode — full rebuild, no pre-seeding)'
type: boolean
default: false
permissions:
contents: write
packages: write
id-token: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
NIX_CACHE_IMAGE: ${{ inputs.skip_nix_cache == true && 'public.ecr.aws/docker/library/debian:trixie-slim' || 'ghcr.io/dimmkirr/devcell:v0.0.0-ultimate' }}
ECR_REGISTRY: public.ecr.aws/w1l3v2k8/devcell
jobs:
secrets:
name: Detect Secrets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker-build:
name: Docker Build (${{ matrix.arch }})
needs: secrets
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
arch: amd64
platform: linux/amd64
- runner: ubuntu-24.04-arm
arch: arm64
platform: linux/arm64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Set lowercase image name
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
sudo docker image prune -af
df -h
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-config-inline: |
[worker.oci]
max-parallelism = 4
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ECR_ROLE_ARN }}
aws-region: us-east-1
- name: Log in to ECR Public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Pre-pull nix-cache as OCI layout
if: inputs.skip_nix_cache != true
run: |
skopeo copy --src-tls-verify=true \
docker://${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-ultimate \
oci:/tmp/nix-cache-oci:latest
echo "NIX_CACHE_OCI=/tmp/nix-cache-oci" >> "$GITHUB_ENV"
- name: Build and push ci group (GHCR)
run: >-
docker buildx bake
--set '*.output=type=image,push=true,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true'
${{ env.NIX_CACHE_OCI && format('--set ultimate.contexts.nix-cache=oci-layout://{0}', env.NIX_CACHE_OCI) || '' }}
ci
env:
VERSION: v0.0.0-${{ matrix.arch }}
PLATFORMS: ${{ matrix.platform }}
REGISTRY: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}
CACHE_ARCH: -${{ matrix.arch }}
- name: Push to ECR Public
run: |
GHCR=${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}
ECR=${{ env.ECR_REGISTRY }}
V=v0.0.0-${{ matrix.arch }}
for suffix in "" "-core" "-ultimate"; do
SRC="${GHCR}:${V}${suffix}"
DST="${ECR}:${V}${suffix}"
docker buildx imagetools create -t "${DST}" "${SRC}"
done
docker-test:
name: Docker Test (${{ matrix.arch }}, ${{ matrix.variant }})
needs: docker-build
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
arch: amd64
variant: base
test_run: "TestBaseImage|TestDotenv"
timeout: 600s
- runner: ubuntu-24.04-arm
arch: arm64
variant: base
test_run: "TestBaseImage|TestDotenv"
timeout: 600s
- runner: ubuntu-latest
arch: amd64
variant: ultimate
test_run: ""
timeout: 1200s
- runner: ubuntu-24.04-arm
arch: arm64
variant: ultimate
test_run: ""
timeout: 1200s
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Set lowercase image name
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- name: Pull test images
run: |
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-${{ matrix.variant }}
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-core
- name: Run container tests
env:
DEVCELL_TEST_IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-${{ matrix.variant }}
DEVCELL_BASE_IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-core
DEVCELL_TEST_BASE_IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-core
DEVCELL_USER_IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-${{ matrix.variant }}
DEVCELL_TEST_PROJECT_DIR: ${{ runner.temp }}/devcell-test
MCP_SECRET_TEST_PASSWORD: fake-secret-ci-value
MCP_SECRET_GITHUB_TOKEN: fake-token-ci-value
run: |
if [ -n "${{ matrix.test_run }}" ]; then
go test -v -run "${{ matrix.test_run }}" -timeout ${{ matrix.timeout }} ./test/...
else
go test -v -timeout ${{ matrix.timeout }} ./test/...
fi
docker-manifest:
name: Docker Manifests
needs: [docker-build]
if: always() && needs.docker-build.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Set lowercase image name
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
env:
IMAGE_NAME: ${{ github.repository }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ECR_ROLE_ARN }}
aws-region: us-east-1
- name: Log in to ECR Public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Create GHCR manifests
run: |
R=${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}
for suffix in "" "-core" "-ultimate"; do
docker buildx imagetools create \
-t "${R}:dev${suffix}" \
-t "${R}:latest${suffix}" \
-t "${R}:v0.0.0${suffix}" \
"${R}:v0.0.0-amd64${suffix}" \
"${R}:v0.0.0-arm64${suffix}"
done
- name: Create ECR Public manifests
run: |
ECR=${{ env.ECR_REGISTRY }}
for suffix in "" "-core" "-ultimate"; do
docker buildx imagetools create \
-t "${ECR}:dev${suffix}" \
-t "${ECR}:latest${suffix}" \
-t "${ECR}:v0.0.0${suffix}" \
"${ECR}:v0.0.0-amd64${suffix}" \
"${ECR}:v0.0.0-arm64${suffix}"
done
pure-build:
name: Pure Image (${{ matrix.arch }}, ${{ matrix.stack }})
needs: secrets
strategy:
fail-fast: false
matrix:
include:
- { runner: ubuntu-latest, arch: amd64, stack: base }
- { runner: ubuntu-latest, arch: amd64, stack: ultimate }
- { runner: ubuntu-24.04-arm, arch: arm64, stack: base }
- { runner: ubuntu-24.04-arm, arch: arm64, stack: ultimate }
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- name: Set lowercase image name
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
sudo docker image prune -af
df -h
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v14
with:
extra-conf: |
experimental-features = nix-command flakes
sandbox = false
filter-syscalls = false
max-substitution-jobs = 128
http-connections = 128
# Caches /nix/store between runs. Cache hit (flake.lock + nixhome unchanged) → instant restore;
# partial restore on miss via restore-prefixes-first-match seeds substituted layers so we only
# re-download what actually changed. GC caps the on-disk store at 5 GB so the GHA cache stays small.
- name: Cache /nix/store
uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-${{ matrix.arch }}-${{ matrix.stack }}-${{ hashFiles('nixhome/flake.lock', 'nixhome/**/*.nix') }}
restore-prefixes-first-match: nix-${{ matrix.arch }}-${{ matrix.stack }}-
purge: true
purge-prefixes: nix-${{ matrix.arch }}-${{ matrix.stack }}-
purge-created: 604800 # 7 days
purge-primary-key: never
gc-max-store-size-linux: 5000000000 # 5 GB
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ECR_ROLE_ARN }}
aws-region: us-east-1
- name: Log in to ECR Public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Install go-task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build + push pure image to GHCR
run: task image:pure:push:${{ matrix.stack }}
env:
# Override Taskfile defaults so the task uses CI-installed nix directly,
# no sudo (single-user nix), and pushes to the per-arch tag.
ARCH: ${{ matrix.arch }}
STACK: ${{ matrix.stack }}
REGISTRY: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}
TAG: v0.0.0-${{ matrix.arch }}-${{ matrix.stack }}-pure
SUDO: ""
NIX: nix
DEVCELL_BUILD_DATE: ${{ github.event.head_commit.timestamp }}
DEVCELL_BUILD_REV: ${{ github.sha }}
- name: Mirror pure image to ECR Public
run: task image:mirror
env:
SRC: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-${{ matrix.stack }}-pure
DST: ${{ env.ECR_REGISTRY }}:v0.0.0-${{ matrix.arch }}-${{ matrix.stack }}-pure
pure-manifest:
name: Pure Image Manifests
needs: pure-build
if: always() && needs.pure-build.result == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set lowercase image name
run: echo "IMAGE_NAME_LC=${IMAGE_NAME,,}" >> $GITHUB_ENV
env:
IMAGE_NAME: ${{ github.repository }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ECR_ROLE_ARN }}
aws-region: us-east-1
- name: Log in to ECR Public
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public
- name: Install go-task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Stitch multi-arch GHCR manifests (pure)
run: |
for stack in base ultimate; do
task image:manifest \
REPO=${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }} \
VERSION=v0.0.0 \
SUFFIX=-${stack}-pure \
ARCHES=amd64,arm64
done
- name: Stitch multi-arch ECR Public manifests (pure)
run: |
for stack in base ultimate; do
task image:manifest \
REPO=${{ env.ECR_REGISTRY }} \
VERSION=v0.0.0 \
SUFFIX=-${stack}-pure \
ARCHES=amd64,arm64
done
cell-build:
name: Cell CLI Dev Build
needs: secrets
runs-on: ubuntu-latest
env:
RELEASE_VERSION: v0.0.0
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- name: Set SHORT_SHA
run: echo "SHORT_SHA=$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_ENV
- name: Delete existing tag
run: |
git config --global user.email "dmitry@atd.sh"
git config --global user.name "Dmitry Kireev"
git tag -d "${{ env.RELEASE_VERSION }}" || echo "No local tag to delete"
git push origin :refs/tags/${{ env.RELEASE_VERSION }} || echo "No remote tag to delete"
- name: Add tag
run: |
git tag -a ${{ env.RELEASE_VERSION }} -m "Development release"
git push origin ${{ github.ref_name }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: "~> v2"
args: release --clean -f .goreleaser.dev.yaml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
SHORT_SHA: ${{ env.SHORT_SHA }}
- name: Publish release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release edit "${{ env.RELEASE_VERSION }}" --draft=false --latest=false --prerelease
e2e-install:
name: E2E Install (${{ matrix.arch }})
needs: [cell-build, docker-manifest]
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
steps:
- name: Download cell binary
run: |
TARBALL="cell-linux-${{ matrix.arch }}.tar.gz"
URL="https://github.com/DimmKirr/devcell/releases/download/v0.0.0/${TARBALL}"
echo "Downloading: ${URL}"
curl -fsSL -o "${TARBALL}" "${URL}"
tar xzf "${TARBALL}"
chmod +x cell
sudo mv cell /usr/local/bin/cell
- name: Verify cell binary
run: |
cell --help
echo "--- cell binary OK ---"
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run cell claude --version (full pipeline)
env:
DEVCELL_NIXHOME_PATH: ${{ github.workspace }}/nixhome
run: |
# Simulate a new user in a fresh project dir
mkdir -p /tmp/e2e-project && cd /tmp/e2e-project
# --plain-text: disable spinners for CI
# Flow: scaffold devcell.toml → pull base image → build user image → start container → print claude version
OUTPUT=$(cell --plain-text claude --version 2>&1) || true
echo "$OUTPUT"
# Assert cell version string is present
if echo "$OUTPUT" | grep -qE "cell\s+v?[0-9]+\.[0-9]+\.[0-9]+"; then
echo "--- PASS: cell version found ---"
else
echo "--- FAIL: cell version string not found in output ---"
exit 1
fi
# Assert the image was built (user image should exist now)
if echo "$OUTPUT" | grep -qiE "(building|built|image)"; then
echo "--- PASS: image build occurred ---"
else
echo "--- WARN: no build output detected (image may have been cached) ---"
fi
brew-install:
name: Brew Install (${{ matrix.arch }})
needs: [cell-build]
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-latest
arch: amd64
- runner: ubuntu-24.04-arm
arch: arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
steps:
- name: Install Homebrew
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" < /dev/null
echo "/home/linuxbrew/.linuxbrew/bin" >> $GITHUB_PATH
- name: Brew install devcell-dev
run: |
brew tap DimmKirr/tap
brew install devcell-dev || true
# Verify binary was actually linked despite potential broken pipe
if ! command -v cell &>/dev/null; then
echo "Binary not found, retrying..."
brew install devcell-dev
fi
- name: Verify version
run: |
INSTALLED=$(cell --version)
echo "Installed: ${INSTALLED}"
if echo "${INSTALLED}" | grep -qE "^cell version [0-9]+\.[0-9]+\.[0-9]+"; then
echo "--- PASS: cell version OK ---"
else
echo "--- FAIL: unexpected version output ---"
exit 1
fi