Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

docs: add recipes for spindle microvm engine

Signed-off-by: dawn <dawn@tangled.org>

author
dawn
committer
Tangled
date (Jun 22, 2026, 6:48 PM +0300) commit ff8c2343 parent 4f6e0dc8 change-id qxwytxzx
+225 -4
+225 -4
docs/DOCS.md
··· 1118 1118 #### Dependencies 1119 1119 1120 1120 On the microVM engine, `dependencies` is a flat list of 1121 - packages that get added to the guest's `PATH` (via 1122 - `environment.systemPackages`). This field only applies to 1123 - **NixOS images**, for other images you can use the package 1124 - manager included in a step. 1121 + packages that are made available to every step. This field 1122 + only applies to **NixOS images**; for other images you can 1123 + use the package manager included in a step. 1124 + 1125 + The guest builds a [`nix develop`](https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-develop)-style 1126 + devshell from your dependencies and uses it for each step, 1127 + so you can, for example, add `pkg-config` and `openssl` and 1128 + have the `openssl-sys` crate while compiling a Rust project 1129 + just work. 1125 1130 1126 1131 A bare name like `go` is looked up in nixpkgs. You can also 1127 1132 point at any flake with the `flakeref#attr` syntax, so ··· 1185 1190 1186 1191 virtualisation: 1187 1192 docker: true 1193 + ``` 1194 + 1195 + #### Recipes 1196 + 1197 + ##### Lint, test and build a Node project 1198 + 1199 + ```yaml 1200 + when: 1201 + - event: ["push", "pull_request"] 1202 + branch: ["main"] 1203 + 1204 + engine: microvm 1205 + image: nixos 1206 + 1207 + dependencies: 1208 + - pnpm 1209 + 1210 + steps: 1211 + - name: "Install dependencies" 1212 + command: pnpm install --frozen-lockfile 1213 + - name: "Lint and test" 1214 + command: | 1215 + pnpm run lint 1216 + pnpm test 1217 + - name: "Build" 1218 + command: pnpm run build 1219 + ``` 1220 + 1221 + ##### Build a Rust project that links OpenSSL 1222 + 1223 + ```yaml 1224 + when: 1225 + - event: ["push", "pull_request"] 1226 + branch: ["main"] 1227 + 1228 + engine: microvm 1229 + image: nixos 1230 + 1231 + dependencies: 1232 + - gcc 1233 + - cargo 1234 + - rustc 1235 + - clippy 1236 + - rustfmt 1237 + - pkg-config # exports PKG_CONFIG_PATH for the libraries below 1238 + - openssl # the C library + headers openssl-sys links against 1239 + 1240 + steps: 1241 + - name: "Check formatting" 1242 + command: cargo fmt --check 1243 + - name: "Clippy" 1244 + command: cargo clippy --all-targets -- -D warnings 1245 + - name: "Test" 1246 + command: cargo test --all 1247 + - name: "Release build" 1248 + command: cargo build --release 1249 + ``` 1250 + 1251 + ##### Run migrations and integration tests against PostgreSQL 1252 + 1253 + ```yaml 1254 + when: 1255 + - event: ["push", "pull_request"] 1256 + branch: ["main"] 1257 + 1258 + engine: microvm 1259 + image: nixos 1260 + 1261 + environment: 1262 + DATABASE_URL: "postgresql:///spindle-workflow?host=/run/postgresql" 1263 + 1264 + dependencies: 1265 + - gcc 1266 + - cargo 1267 + - rustc 1268 + - pkg-config 1269 + - openssl 1270 + - sqlx-cli 1271 + 1272 + services: 1273 + postgresql: 1274 + enable: true 1275 + # has to be same name as the user for peer auth to work automatically 1276 + ensureDatabases: ["spindle-workflow"] 1277 + ensureUsers: 1278 + - name: spindle-workflow 1279 + ensureDBOwnership: true 1280 + 1281 + steps: 1282 + - name: "Run migrations" 1283 + command: sqlx migrate run 1284 + - name: "Integration tests" 1285 + command: cargo test --all 1286 + ``` 1287 + 1288 + ##### Build and push a Docker image on tag 1289 + 1290 + ```yaml 1291 + when: 1292 + - event: ["push"] 1293 + tag: ["v*"] 1294 + 1295 + engine: microvm 1296 + image: nixos 1297 + 1298 + virtualisation: 1299 + docker: true 1300 + 1301 + steps: 1302 + - name: "Build and push to ghcr.io" 1303 + command: | 1304 + set -euo pipefail 1305 + 1306 + echo "$REGISTRY_TOKEN" | docker login ghcr.io -u "$REGISTRY_USER" --password-stdin 1307 + image="ghcr.io/$REGISTRY_USER/myapp:$TANGLED_REF_NAME" 1308 + 1309 + docker build -t "$image" -t "ghcr.io/$REGISTRY_USER/myapp:latest" . 1310 + docker push "$image" 1311 + docker push "ghcr.io/$REGISTRY_USER/myapp:latest" 1312 + ``` 1313 + 1314 + ##### Deploy to Cloudflare Workers on tag 1315 + 1316 + ```yaml 1317 + # .tangled/workflows/deploy.yml 1318 + when: 1319 + - event: ["push"] 1320 + tag: ["v*"] 1321 + 1322 + engine: microvm 1323 + image: nixos 1324 + 1325 + dependencies: 1326 + - pnpm 1327 + 1328 + steps: 1329 + - name: "Install dependencies" 1330 + command: pnpm install --frozen-lockfile 1331 + - name: "Deploy worker" 1332 + # `wrangler` picks up `CLOUDFLARE_API_TOKEN` from the env. 1333 + # set it under **Settings → Secrets**. 1334 + command: pnpm exec wrangler deploy 1335 + ``` 1336 + 1337 + ##### Publish a release artifact 1338 + 1339 + ```yaml 1340 + when: 1341 + - event: ["push"] 1342 + tag: ["v*"] # trigger on versions 1343 + 1344 + engine: microvm 1345 + image: nixos 1346 + 1347 + dependencies: 1348 + - go 1349 + 1350 + steps: 1351 + - name: "Build release binary" 1352 + command: | 1353 + mkdir -p dist 1354 + CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o dist/myapp ./cmd/myapp 1355 + 1356 + - name: "Publish artifact record" 1357 + command: | 1358 + set -euo pipefail 1359 + # change this if you're not on `tngl.sh` 1360 + PDS="https://tngl.sh" 1361 + # also update this to your handle or did 1362 + ATP_IDENTIFIER="user.tngl.sh" 1363 + ARTIFACT_PATH="dist/myapp" 1364 + ARTIFACT_NAME="myapp" 1365 + 1366 + # set `ATP_APP_PASSWORD` under **Settings → Secrets** 1367 + session=$(curl -fsS -X POST "$PDS/xrpc/com.atproto.server.createSession" \ 1368 + -H "Content-Type: application/json" \ 1369 + -d "{\"identifier\":\"$ATP_IDENTIFIER\",\"password\":\"$ATP_APP_PASSWORD\"}") 1370 + jwt=$(echo "$session" | jq -r .accessJwt) 1371 + did=$(echo "$session" | jq -r .did) 1372 + 1373 + # upload the binary as a blob 1374 + blob=$(curl -fsS -X POST "$PDS/xrpc/com.atproto.repo.uploadBlob" \ 1375 + -H "Authorization: Bearer $jwt" \ 1376 + -H "Content-Type: application/octet-stream" \ 1377 + --data-binary @"$ARTIFACT_PATH") 1378 + 1379 + # note that this requires an annotated tag (`git tag -a v1.0.0 -m ...`) 1380 + tag_hash=$(git rev-parse "$TANGLED_REF_NAME^{tag}") 1381 + tag_bytes=$(printf '%s' "$tag_hash" | xxd -r -p | base64 | tr -d '=') 1382 + 1383 + # the sh.tangled.repo.artifact record for your artifact 1384 + record=$(jq -n \ 1385 + --arg did "$did" \ 1386 + --arg tag "$tag_bytes" \ 1387 + --arg name "$ARTIFACT_NAME" \ 1388 + --arg repo "$TANGLED_REPO_URL" \ 1389 + --arg created "$(date -Iseconds)" \ 1390 + --argjson blob "$(echo "$blob" | jq .blob)" '{ 1391 + repo: $did, 1392 + collection: "sh.tangled.repo.artifact", 1393 + validate: false, 1394 + record: { 1395 + "$type": "sh.tangled.repo.artifact", 1396 + tag: {"$bytes": $tag}, 1397 + name: $name, 1398 + repo: $repo, 1399 + artifact: $blob, 1400 + createdAt: $created 1401 + } 1402 + }') 1403 + 1404 + # create the record on the PDS 1405 + curl -fsS -X POST "$PDS/xrpc/com.atproto.repo.createRecord" \ 1406 + -H "Authorization: Bearer $jwt" \ 1407 + -H "Content-Type: application/json" \ 1408 + -d "$record" 1188 1409 ``` 1189 1410 1190 1411 ## Self-hosting guide