-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdevcontainer-cache-experiments.yml
More file actions
313 lines (285 loc) · 12.3 KB
/
devcontainer-cache-experiments.yml
File metadata and controls
313 lines (285 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
name: Devcontainer Cache Experiments
on:
push:
branches:
- "sam/cloudflare-devcontainer-cache-experiments-*"
paths:
- ".github/workflows/devcontainer-cache-experiments.yml"
- "scripts/experiments/**"
workflow_dispatch:
inputs:
run_cloudflare_registry:
description: "Run Cloudflare managed registry push/pull experiment"
required: true
default: "true"
type: choice
options: ["true", "false"]
run_r2:
description: "Run R2 tarball and BuildKit S3 cache experiments"
required: true
default: "true"
type: choice
options: ["true", "false"]
run_sam_devcontainer_stress:
description: "Build and push the real SAM devcontainer to Cloudflare registry"
required: true
default: "false"
type: choice
options: ["true", "false"]
permissions:
contents: read
jobs:
cloudflare-registry:
if: ${{ github.event_name == 'push' || inputs.run_cloudflare_registry == 'true' }}
runs-on: ubuntu-latest
environment: staging
timeout-minutes: 25
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
IMAGE_NAME: sam-devcontainer-cache-exp
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093
with:
version: 9.15.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Show Wrangler container command help
run: |
pnpm --filter @simple-agent-manager/api exec wrangler containers registries credentials --help
- name: Build local test image
run: |
cat > Dockerfile.cache-exp <<'DOCKERFILE'
FROM alpine:3.20
RUN dd if=/dev/zero of=/cache-test.bin bs=1M count=64
CMD ["sh", "-c", "test -f /cache-test.bin && echo ok"]
DOCKERFILE
docker build -t "$IMAGE_NAME:${GITHUB_RUN_ID}" -f Dockerfile.cache-exp .
- name: Push through wrangler containers push
id: wrangler_push
continue-on-error: true
run: |
set -o pipefail
pnpm --filter @simple-agent-manager/api exec wrangler containers push "$IMAGE_NAME:${GITHUB_RUN_ID}" 2>&1 | tee wrangler-push.log
{
echo "### Wrangler containers push"
echo
echo '```text'
sed -E 's/[A-Za-z0-9_-]{24,}/***/g' wrangler-push.log
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Plain Docker push/pull against registry.cloudflare.com
run: |
set -euxo pipefail
REF="registry.cloudflare.com/${CLOUDFLARE_ACCOUNT_ID}/${IMAGE_NAME}:docker-${GITHUB_RUN_ID}"
docker tag "$IMAGE_NAME:${GITHUB_RUN_ID}" "$REF"
docker push "$REF"
docker rmi "$REF"
docker pull "$REF"
echo "### Docker registry.cloudflare.com push/pull" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- Ref: \`$REF\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Result: push and pull succeeded" >> "$GITHUB_STEP_SUMMARY"
r2-cache:
if: ${{ github.event_name == 'push' || inputs.run_r2 == 'true' }}
runs-on: ubuntu-latest
environment: staging
timeout-minutes: 35
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093
with:
version: 9.15.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Prepare experiment context
run: |
mkdir -p /tmp/sam-cache-exp
cat > /tmp/sam-cache-exp/Dockerfile <<'DOCKERFILE'
FROM alpine:3.20
RUN dd if=/dev/zero of=/r2-cache-test.bin bs=1M count=64
RUN sha256sum /r2-cache-test.bin > /r2-cache-test.sha256
CMD ["cat", "/r2-cache-test.sha256"]
DOCKERFILE
docker build -t sam-r2-cache-exp:${GITHUB_RUN_ID} /tmp/sam-cache-exp
docker save sam-r2-cache-exp:${GITHUB_RUN_ID} -o /tmp/sam-cache-exp-image.tar
- name: Create temporary R2 bucket
run: |
BUCKET="sam-devcontainer-cache-exp-${GITHUB_RUN_ID}"
echo "BUCKET=$BUCKET" >> "$GITHUB_ENV"
pnpm --filter @simple-agent-manager/api exec wrangler r2 bucket create "$BUCKET"
- name: Test R2 tarball upload/download
run: |
set -euxo pipefail
KEY="docker-save/sam-r2-cache-exp-${GITHUB_RUN_ID}.tar"
pnpm --filter @simple-agent-manager/api exec wrangler r2 object put "$BUCKET/$KEY" --file /tmp/sam-cache-exp-image.tar
pnpm --filter @simple-agent-manager/api exec wrangler r2 object get "$BUCKET/$KEY" --file /tmp/sam-cache-exp-image-downloaded.tar
docker rmi sam-r2-cache-exp:${GITHUB_RUN_ID}
docker load -i /tmp/sam-cache-exp-image-downloaded.tar
docker run --rm sam-r2-cache-exp:${GITHUB_RUN_ID}
echo "### R2 docker save/load tarball" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "- Bucket: \`$BUCKET\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Key: \`$KEY\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Result: upload, download, load, run succeeded" >> "$GITHUB_STEP_SUMMARY"
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd
with:
driver: docker-container
buildkitd-flags: --debug
- name: Test BuildKit S3 cache against R2
id: buildkit_s3
continue-on-error: true
run: |
set -o pipefail
CACHE_ARGS="type=s3,region=auto,bucket=${BUCKET},name=sam-buildkit-cache-${GITHUB_RUN_ID},endpoint_url=https://${CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com,use_path_style=true,access_key_id=${AWS_ACCESS_KEY_ID},secret_access_key=${AWS_SECRET_ACCESS_KEY},mode=max"
docker buildx build \
--progress=plain \
--cache-to "$CACHE_ARGS" \
--cache-from "$CACHE_ARGS" \
--load \
-t "sam-r2-buildkit-cache-exp:${GITHUB_RUN_ID}" \
/tmp/sam-cache-exp 2>&1 | tee buildkit-s3.log
{
echo "### BuildKit S3 cache to R2"
echo
echo "Exit status: \`${PIPESTATUS[0]}\`"
echo
echo '```text'
tail -120 buildkit-s3.log | sed -E 's/(access_key_id=)[^,]+/\1***/g; s/(secret_access_key=)[^,]+/\1***/g'
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Cleanup temporary R2 bucket
if: always()
continue-on-error: true
run: |
if [ -n "${BUCKET:-}" ]; then
aws --endpoint-url "https://${CLOUDFLARE_ACCOUNT_ID}.r2.cloudflarestorage.com" s3 rm "s3://${BUCKET}" --recursive || true
pnpm --filter @simple-agent-manager/api exec wrangler r2 bucket delete "$BUCKET" --yes || true
fi
sam-devcontainer-registry-stress:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_sam_devcontainer_stress == 'true' }}
runs-on: ubuntu-latest
environment: staging
timeout-minutes: 75
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
IMAGE_NAME: sam-devcontainer-cache-stress
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093
with:
version: 9.15.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install devcontainer CLI
run: npm install -g @devcontainers/cli
- name: Build SAM devcontainer image
run: |
set -euxo pipefail
LOCAL_REF="${IMAGE_NAME}:local-${GITHUB_RUN_ID}"
devcontainer build --workspace-folder . --image-name "$LOCAL_REF"
IMAGE_ID="$(docker image inspect "$LOCAL_REF" --format '{{.Id}}')"
SIZE_BYTES="$(docker image inspect "$IMAGE_ID" --format '{{.Size}}')"
SIZE_MIB="$(awk "BEGIN { printf \"%.1f\", ${SIZE_BYTES} / 1024 / 1024 }")"
echo "LOCAL_REF=$LOCAL_REF" >> "$GITHUB_ENV"
echo "IMAGE_ID=$IMAGE_ID" >> "$GITHUB_ENV"
echo "SIZE_BYTES=$SIZE_BYTES" >> "$GITHUB_ENV"
echo "SIZE_MIB=$SIZE_MIB" >> "$GITHUB_ENV"
{
echo "### SAM devcontainer image"
echo
echo "- Local ref: \`$LOCAL_REF\`"
echo "- Image ID: \`$IMAGE_ID\`"
echo "- Local image size: \`${SIZE_BYTES}\` bytes (${SIZE_MIB} MiB)"
echo
echo "#### Image inspect"
echo
echo '```json'
docker image inspect "$IMAGE_ID" | jq '.[0] | {Id, RepoTags, RepoDigests, Size, VirtualSize, Architecture, Os, RootFS, Config: {Image: .Config.Image, Labels: .Config.Labels}}'
echo '```'
echo
echo "#### Docker history"
echo
echo '```text'
docker history --no-trunc "$IMAGE_ID"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
- name: Mint Cloudflare registry credentials
run: |
set -euo pipefail
pnpm --filter @simple-agent-manager/api exec wrangler containers registries credentials registry.cloudflare.com \
--push \
--pull \
--expiration-minutes 120 \
--json > registry-credentials.raw
node -e "
const fs = require('fs');
const raw = fs.readFileSync('registry-credentials.raw', 'utf8');
const start = raw.lastIndexOf('{');
if (start < 0) {
console.error(raw);
throw new Error('No JSON object found in Wrangler credentials output');
}
fs.writeFileSync('registry-credentials.json', raw.slice(start));
"
REGISTRY_HOST="$(jq -r '.registry_host' registry-credentials.json)"
REGISTRY_USERNAME="$(jq -r '.username' registry-credentials.json)"
REGISTRY_PASSWORD="$(jq -r '.password' registry-credentials.json)"
if [ -z "$REGISTRY_PASSWORD" ] || [ "$REGISTRY_PASSWORD" = "null" ]; then
echo "Cloudflare registry credentials did not include a password"
exit 1
fi
echo "::add-mask::$REGISTRY_PASSWORD"
{
echo "REGISTRY_HOST=$REGISTRY_HOST"
echo "REGISTRY_USERNAME=$REGISTRY_USERNAME"
echo "REGISTRY_PASSWORD=$REGISTRY_PASSWORD"
} >> "$GITHUB_ENV"
- name: Push and pull SAM devcontainer with plain Docker
run: |
set -euo pipefail
REF="${REGISTRY_HOST}/${CLOUDFLARE_ACCOUNT_ID}/${IMAGE_NAME}:sam-real-${GITHUB_RUN_ID}"
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USERNAME" --password-stdin
docker tag "$IMAGE_ID" "$REF"
docker push "$REF" 2>&1 | tee docker-push.log
docker rmi "$REF" "$LOCAL_REF" "$IMAGE_ID" || true
docker pull "$REF" 2>&1 | tee docker-pull.log
{
echo "### SAM devcontainer Cloudflare registry stress result"
echo
echo "- Ref: \`$REF\`"
echo "- Local image size before push: \`${SIZE_BYTES}\` bytes (${SIZE_MIB} MiB)"
echo "- Result: plain Docker push and pull succeeded"
echo
echo "#### Push tail"
echo
echo '```text'
tail -80 docker-push.log
echo '```'
echo
echo "#### Pull tail"
echo
echo '```text'
tail -80 docker-pull.log
echo '```'
} >> "$GITHUB_STEP_SUMMARY"