Files
yetone 7bfe3137bd docker: the image has bash and busybox, so a terminal in the container opens (スライム on Discord: 这2天的docker版更新后无法进入terminal模式,导致配置不了devin … exec: "/bin/bash": stat /bin/bash: no such file or directory)
The image is distroless, which has no shell at all, so a NAS panel's
terminal (Synology, 1Panel, Portainer open /bin/bash or /bin/sh) and
`docker exec -it magpie bash` failed, and a sign-in that needs a terminal
(magpie accounts add devin) couldn't be made there. The image now copies
in Debian 12's bash with the libtinfo it links (distroless cc is Debian 12,
so its glibc runs it) and busybox's commands, and puts magpie on PATH
(/usr/local/bin/magpie). The image still runs as nonroot and starts
/magpie serve as before; it grows from about 37 MB of extras to 66.6 MB.

Built locally (docker 29.4, arm64): bash 5.2.15 and sh run as uid 65532,
`magpie version` and `magpie healthcheck` work from bash, the gateway
answers 200 on /v1/models, /config keeps its folders. The Docker workflow's
smoke test now runs bash and sh in the container. README says so.
2026-10-05 03:23:18 +08:00

155 lines
5.2 KiB
YAML

name: Docker
on:
push:
branches: [main]
tags: ['v*']
pull_request:
paths: [Dockerfile, .dockerignore, .github/workflows/docker.yml]
workflow_dispatch:
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/magpie
jobs:
build:
name: build (${{ matrix.platform }})
strategy:
fail-fast: false
matrix:
platform: [linux/amd64, linux/arm64]
# native arm runner instead of QEMU
runs-on: ${{ startsWith(matrix.platform, 'linux/arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v5
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Smoke test the image
run: |
docker build --load \
--cache-from type=gha,scope=${{ matrix.platform }} \
--cache-to type=gha,scope=${{ matrix.platform }},mode=max \
-t magpie-smoke .
docker run -d --name smoke -v smoke-ci:/config -p 13425:3425 magpie-smoke
READY=0
for _ in $(seq 1 30); do
if curl -s -o /dev/null http://127.0.0.1:13425/v1/models; then READY=1; break; fi
sleep 1
done
test "$READY" = 1
# a NAS panel's terminal opens bash or sh, with magpie on PATH
docker exec smoke /bin/bash -c 'magpie healthcheck && ls /config >/dev/null'
docker exec smoke /bin/sh -c 'echo sh works'
CODE=$(curl -s -o /tmp/body.json -w '%{http_code}' \
-H 'Authorization: Bearer magpie' -H 'Content-Type: application/json' \
-d '{"model":"ci-unknown-model","messages":[{"role":"user","content":"hi"}]}' \
http://127.0.0.1:13425/v1/chat/completions)
echo "chat/completions answered HTTP $CODE"
cat /tmp/body.json; echo
(( CODE >= 400 && CODE < 500 ))
jq -e '.error.type' /tmp/body.json > /dev/null
OUT=$(docker run --rm -v smoke-ci:/config magpie-smoke provider add ci-smoke key=sk-ci url=https://example.com)
echo "$OUT"
echo "$OUT" | grep -q 'added ci-smoke'
- name: Build and push by digest
id: build
uses: docker/build-push-action@v7
with:
context: .
platforms: ${{ matrix.platform }}
provenance: false
push: ${{ github.event_name != 'pull_request' }}
build-args: |
VERSION=${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || github.sha }}
outputs: |
type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true
cache-from: type=gha,scope=${{ matrix.platform }}
cache-to: type=gha,scope=${{ matrix.platform }},mode=max
- name: Export digest
if: github.event_name != 'pull_request'
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- uses: actions/upload-artifact@v6
if: github.event_name != 'pull_request'
with:
name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
if: github.event_name != 'pull_request'
needs: [build]
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
id-token: write
attestations: write
outputs:
digest: ${{ steps.manifest.outputs.digest }}
steps:
- uses: actions/download-artifact@v7
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Create manifest and tag
id: manifest
working-directory: ${{ runner.temp }}/digests
run: |
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
V="${GITHUB_REF_NAME#v}"
TAGS=(-t "$IMAGE:$V")
REF_TAG="$V"
if [[ "$V" != *-* ]]; then
TAGS+=(-t "$IMAGE:${V%.*}" -t "$IMAGE:${V%%.*}" -t "$IMAGE:latest")
fi
else
TAGS=(-t "$IMAGE:edge")
REF_TAG=edge
fi
DIGEST_ARGS=()
for d in *; do DIGEST_ARGS+=("$IMAGE@sha256:$d"); done
docker buildx imagetools create "${TAGS[@]}" "${DIGEST_ARGS[@]}"
docker buildx imagetools inspect "$IMAGE:$REF_TAG"
DIGEST=$(docker buildx imagetools inspect "$IMAGE:$REF_TAG" --format '{{json .}}' | jq -r '.manifest.digest')
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
- name: Attest build provenance
uses: actions/attest-build-provenance@v3
with:
push-to-registry: true
subject-name: ${{ env.IMAGE }}
subject-digest: ${{ steps.manifest.outputs.digest }}