Skip to content

Commit de92515

Browse files
authored
Merge pull request #583 from Dstack-TEE/build/consolidate-builder-scripts
build: consolidate reproducible builder scripts
2 parents e4c0ed2 + 0886f98 commit de92515

28 files changed

Lines changed: 1797 additions & 1649 deletions

.github/workflows/docker-build-check.yml

Lines changed: 109 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,51 +11,99 @@ on:
1111
branches: [ master, next, dev-* ]
1212

1313
jobs:
14-
kms:
14+
gateway:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- uses: actions/checkout@v4
1818

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

22-
- name: Write GIT_REV
23-
run: git rev-parse HEAD > kms/dstack-app/builder/.GIT_REV
24-
25-
- name: Build KMS Docker image
22+
- name: Build Gateway Docker image
2623
uses: docker/build-push-action@v5
2724
with:
28-
context: kms/dstack-app/builder
25+
context: gateway/dstack-app/builder
2926
push: false
30-
platforms: linux/amd64
27+
load: true
28+
tags: dstack-gateway-check:latest
3129
provenance: false
30+
build-contexts: |
31+
build-shared=build/shared
3232
build-args: |
3333
DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}
3434
DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}
3535
36-
- name: Build KMS contracts
36+
- name: Verify pinned packages
3737
run: |
38-
cd kms/auth-eth
39-
npm ci
40-
npx hardhat compile
38+
build/shared/verify-pinned-packages.sh dstack-gateway-check:latest \
39+
gateway/dstack-app/builder/shared/pinned-packages.txt
4140
42-
gateway:
41+
- name: Build gateway-builder target
42+
run: |
43+
docker buildx build \
44+
--load \
45+
--target gateway-builder \
46+
--tag gateway-builder-check:latest \
47+
--provenance=false \
48+
--build-context build-shared=build/shared \
49+
--build-arg "DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}" \
50+
--build-arg "DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}" \
51+
gateway/dstack-app/builder
52+
53+
- name: Verify builder pinned packages
54+
run: |
55+
build/shared/verify-pinned-packages.sh gateway-builder-check:latest \
56+
gateway/dstack-app/builder/shared/builder-pinned-packages.txt
57+
58+
kms:
4359
runs-on: ubuntu-latest
4460
steps:
4561
- uses: actions/checkout@v4
4662

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

50-
- name: Build Gateway Docker image
66+
- name: Build KMS Docker image
5167
uses: docker/build-push-action@v5
5268
with:
53-
context: gateway/dstack-app/builder
69+
context: kms/dstack-app/builder
5470
push: false
55-
platforms: linux/amd64
71+
load: true
72+
tags: dstack-kms-check:latest
5673
provenance: false
74+
build-contexts: |
75+
build-shared=build/shared
5776
build-args: |
5877
DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}
78+
DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}
79+
80+
- name: Verify pinned packages (qemu stage)
81+
run: |
82+
build/shared/verify-pinned-packages.sh dstack-kms-check:latest \
83+
kms/dstack-app/builder/shared/qemu-pinned-packages.txt
84+
85+
- name: Build kms-builder target
86+
run: |
87+
docker buildx build \
88+
--load \
89+
--target kms-builder \
90+
--tag kms-builder-check:latest \
91+
--provenance=false \
92+
--build-context build-shared=build/shared \
93+
--build-arg "DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}" \
94+
--build-arg "DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}" \
95+
kms/dstack-app/builder
96+
97+
- name: Verify builder pinned packages
98+
run: |
99+
build/shared/verify-pinned-packages.sh kms-builder-check:latest \
100+
kms/dstack-app/builder/shared/builder-pinned-packages.txt
101+
102+
- name: Build KMS contracts
103+
run: |
104+
cd kms/auth-eth
105+
npm ci
106+
npx hardhat compile
59107
60108
verifier:
61109
runs-on: ubuntu-latest
@@ -71,7 +119,52 @@ jobs:
71119
context: verifier
72120
file: verifier/builder/Dockerfile
73121
push: false
74-
platforms: linux/amd64
122+
load: true
123+
tags: dstack-verifier-check:latest
75124
provenance: false
125+
build-contexts: |
126+
build-shared=build/shared
76127
build-args: |
77128
DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}
129+
DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}
130+
131+
- name: Verify pinned packages (runtime)
132+
run: |
133+
build/shared/verify-pinned-packages.sh dstack-verifier-check:latest \
134+
verifier/builder/shared/pinned-packages.txt
135+
136+
- name: Build verifier-builder target
137+
run: |
138+
docker buildx build \
139+
--load \
140+
--target verifier-builder \
141+
--tag verifier-builder-check:latest \
142+
--provenance=false \
143+
--file verifier/builder/Dockerfile \
144+
--build-context build-shared=build/shared \
145+
--build-arg "DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}" \
146+
--build-arg "DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}" \
147+
verifier
148+
149+
- name: Verify builder pinned packages
150+
run: |
151+
build/shared/verify-pinned-packages.sh verifier-builder-check:latest \
152+
verifier/builder/shared/builder-pinned-packages.txt
153+
154+
- name: Build acpi-builder target
155+
run: |
156+
docker buildx build \
157+
--load \
158+
--target acpi-builder \
159+
--tag verifier-acpi-check:latest \
160+
--provenance=false \
161+
--file verifier/builder/Dockerfile \
162+
--build-context build-shared=build/shared \
163+
--build-arg "DSTACK_REV=${{ github.event.pull_request.head.sha || github.sha }}" \
164+
--build-arg "DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}" \
165+
verifier
166+
167+
- name: Verify qemu pinned packages
168+
run: |
169+
build/shared/verify-pinned-packages.sh verifier-acpi-check:latest \
170+
verifier/builder/shared/qemu-pinned-packages.txt

.github/workflows/gateway-release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
tags: ${{ vars.DOCKERHUB_ORG }}/dstack-gateway:${{ env.VERSION }}
5555
platforms: linux/amd64
5656
provenance: false
57+
build-contexts: |
58+
build-shared=build/shared
5759
build-args: |
5860
DSTACK_REV=${{ env.GIT_REV }}
5961
SOURCE_DATE_EPOCH=${{ env.TIMESTAMP }}

.github/workflows/kms-release.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ jobs:
3838
- name: Set up Docker Buildx
3939
uses: docker/setup-buildx-action@v3
4040

41-
- name: Write GIT_REV
42-
run: git rev-parse HEAD > kms/dstack-app/builder/.GIT_REV
43-
4441
- name: Get Git commit timestamps
4542
run: |
4643
echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
@@ -57,6 +54,8 @@ jobs:
5754
tags: ${{ vars.DOCKERHUB_ORG }}/dstack-kms:${{ env.VERSION }}
5855
platforms: linux/amd64
5956
provenance: false
57+
build-contexts: |
58+
build-shared=build/shared
6059
build-args: |
6160
DSTACK_REV=${{ env.GIT_REV }}
6261
DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}

.github/workflows/verifier-release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
tags: ${{ vars.DOCKERHUB_ORG }}/dstack-verifier:${{ env.VERSION }}
5555
platforms: linux/amd64
5656
provenance: false
57+
build-contexts: |
58+
build-shared=build/shared
5759
build-args: |
5860
DSTACK_REV=${{ env.GIT_REV }}
5961
DSTACK_SRC_URL=${{ github.server_url }}/${{ github.repository }}.git

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/target
22
/certs
33
/build-config.sh
4-
/build
4+
/build/*
5+
!/build/shared/
56
generated/
67
node_modules/
78
/.cargo

REUSE.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ path = [
3232
"sdk/simulator/*.json",
3333
"sdk/go/go.sum",
3434
"sdk/go/ratls/go.sum",
35-
"kms/dstack-app/builder/shared/kms-pinned-packages.txt",
35+
"kms/dstack-app/builder/shared/builder-pinned-packages.txt",
3636
"kms/dstack-app/builder/shared/qemu-pinned-packages.txt",
3737
"gateway/dstack-app/builder/shared/builder-pinned-packages.txt",
3838
"gateway/dstack-app/builder/shared/pinned-packages.txt",

build/shared/build-lib.sh

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/bin/bash
2+
3+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
# Shared build library for reproducible Docker image builds.
8+
#
9+
# Expected variables (set by the sourcing script):
10+
# REPO_ROOT - absolute path to the git repo root
11+
# CONTEXT_DIR - Docker build context directory
12+
# DOCKERFILE - path to the Dockerfile
13+
# GIT_REV - git revision to build
14+
# DSTACK_SRC_URL - git URL for dstack source
15+
16+
set -euo pipefail
17+
18+
BUILDKIT_VERSION="v0.20.2"
19+
BUILDKIT_BUILDER="buildkit_20"
20+
BUILD_SHARED_DIR="$REPO_ROOT/build/shared"
21+
22+
ensure_buildkit() {
23+
if ! docker buildx inspect "$BUILDKIT_BUILDER" &>/dev/null; then
24+
docker buildx create --use --driver-opt "image=moby/buildkit:$BUILDKIT_VERSION" --name "$BUILDKIT_BUILDER"
25+
fi
26+
}
27+
28+
extract_packages() {
29+
local image_name=$1
30+
local pkg_list_file=${2:-}
31+
if [ -z "$pkg_list_file" ]; then
32+
return
33+
fi
34+
docker run --rm --entrypoint bash "$image_name" \
35+
-c "dpkg -l | grep '^ii' | awk '{print \$2\"=\"\$3}' | sort" \
36+
>"$pkg_list_file"
37+
}
38+
39+
docker_build() {
40+
local image_name=$1
41+
local target=${2:-}
42+
local pkg_list_file=${3:-}
43+
44+
local commit_timestamp
45+
commit_timestamp=$(git -C "$REPO_ROOT" show -s --format=%ct "$GIT_REV")
46+
47+
local args=(
48+
--builder "$BUILDKIT_BUILDER"
49+
--progress=plain
50+
--output "type=docker,name=$image_name,rewrite-timestamp=true"
51+
--build-context "build-shared=$BUILD_SHARED_DIR"
52+
--build-arg "SOURCE_DATE_EPOCH=$commit_timestamp"
53+
--build-arg "DSTACK_REV=$GIT_REV"
54+
--build-arg "DSTACK_SRC_URL=$DSTACK_SRC_URL"
55+
)
56+
57+
if [ -n "${NO_CACHE:-}" ]; then
58+
args+=(--no-cache)
59+
fi
60+
61+
if [ -n "$target" ]; then
62+
args+=(--target "$target")
63+
fi
64+
65+
docker buildx build "${args[@]}" \
66+
--file "$DOCKERFILE" \
67+
"$CONTEXT_DIR"
68+
69+
extract_packages "$image_name" "$pkg_list_file"
70+
}
71+
72+
# Verify that pinned-packages files haven't changed (idempotency check).
73+
check_clean_tree() {
74+
local check_path=$1
75+
local rel_path
76+
rel_path=$(realpath --relative-to="$REPO_ROOT" "$check_path")
77+
local git_status
78+
git_status=$(git -C "$REPO_ROOT" status --porcelain -- "$rel_path")
79+
if [ -n "$git_status" ]; then
80+
echo "The working tree has updates in $rel_path. Commit or stash before re-running." >&2
81+
exit 1
82+
fi
83+
}
File renamed without changes.

build/shared/pin-packages.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
3+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
# Pin APT packages to exact versions from a frozen Debian snapshot.
8+
# Usage: pin-packages.sh <pkg-list-file>
9+
#
10+
# This script:
11+
# 1. Points APT at a frozen snapshot.debian.org mirror (reproducible package sources)
12+
# 2. Reads package=version pairs from the given file and creates APT pin preferences
13+
# with priority 1001 to force exact versions
14+
15+
set -e
16+
17+
PKG_LIST=$1
18+
SNAPSHOT_DATE=${SNAPSHOT_DATE:-20260317T000000Z}
19+
20+
if [ -z "$PKG_LIST" ]; then
21+
echo "Usage: $0 <pkg-list-file>" >&2
22+
exit 1
23+
fi
24+
25+
echo "deb [check-valid-until=no] http://snapshot.debian.org/archive/debian/${SNAPSHOT_DATE} bookworm main" > /etc/apt/sources.list
26+
echo "deb [check-valid-until=no] http://snapshot.debian.org/archive/debian-security/${SNAPSHOT_DATE} bookworm-security main" >> /etc/apt/sources.list
27+
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/10no-check-valid-until
28+
29+
mkdir -p /etc/apt/preferences.d
30+
while IFS= read -r line; do
31+
pkg=$(echo "$line" | cut -d= -f1)
32+
ver=$(echo "$line" | cut -d= -f2)
33+
if [ -n "$pkg" ] && [ -n "$ver" ]; then
34+
printf 'Package: %s\nPin: version %s\nPin-Priority: 1001\n\n' "$pkg" "$ver" >> /etc/apt/preferences.d/pinned-packages
35+
fi
36+
done < "$PKG_LIST"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
# SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
# Verify that installed packages in a Docker image match the committed
8+
# pinned-packages file. Detects when Dockerfile changes cause package
9+
# drift without regenerating the pinned-packages list.
10+
#
11+
# Usage: verify-pinned-packages.sh <image> <pinned-packages-file>
12+
13+
set -euo pipefail
14+
15+
IMAGE=$1
16+
PKG_FILE=$2
17+
18+
if [ -z "$IMAGE" ] || [ -z "$PKG_FILE" ]; then
19+
echo "Usage: $0 <image> <pinned-packages-file>" >&2
20+
exit 1
21+
fi
22+
23+
ACTUAL=$(docker run --rm --entrypoint bash "$IMAGE" \
24+
-c "dpkg -l | grep '^ii' | awk '{print \$2\"=\"\$3}' | sort")
25+
26+
EXPECTED=$(sort "$PKG_FILE")
27+
28+
if [ "$ACTUAL" = "$EXPECTED" ]; then
29+
echo "OK: packages in $IMAGE match $PKG_FILE"
30+
exit 0
31+
fi
32+
33+
echo "ERROR: packages in $IMAGE differ from $PKG_FILE" >&2
34+
echo "" >&2
35+
diff --unified <(echo "$EXPECTED") <(echo "$ACTUAL") >&2 || true
36+
echo "" >&2
37+
echo "Regenerate pinned packages by running the service's build-image.sh" >&2
38+
exit 1

0 commit comments

Comments
 (0)