Stitch any CI into Tangled
1# tack - Connect Tangled to your CI
2
3Tack is a custom [Tangled](https://tangled.org) spindle that runs
4CI on alternate providers and reports their results back
5to Tangled using standard ATProto records so they show up natively
6in Tangled's UI.
7
8> [!WARNING]
9>
10> **Project status:** Tack is early and experimental software.
11> This repo itself uses Tack and I (mitchellh) use Tack for my projects,
12> but it is still new and relatively unproven.
13
14## Example
15
16A Tangled workflow that fires a Buildkite pipeline on every push to
17`main` and every pull request targeting `main`:
18
19```yaml
20# .tangled/workflows/ci.yml
21when:
22 - event: ["push"]
23 branch: ["main"]
24 - event: ["pull_request"]
25 branch: ["main"]
26
27# this does nothing for Tack but is required by Tangled
28engine: nixery
29
30tack:
31 buildkite:
32 pipeline: my-app-ci
33```
34
35The `when:` block is the standard Tangled trigger schema; the
36`tack:` block tells tack which Buildkite pipeline to fire and how.
37See [docs/buildkite.md](docs/buildkite.md) for the full set of
38options and end-to-end Buildkite setup. Tack can support multiple
39providers.
40
41## How it Works
42
43Tack is a drop-in alternative to the stock `spindle` runner. You run
44`tack` and [register it using the standard UI](https://tangled.org/settings/spindles).
45
46Instead of executing workflows in local containers, tack translates each
47Tangled pipeline trigger into a 3rd party CI build, and reports build state
48back to Tangled using the existing `sh.tangled.pipeline.status` wire format.
49
50This makes even 3rd party CIs integrate first class into Tangled so their
51status, counts, etc. can show up inline in things like pull requests.
52
53```
54 Jetstream Knot /events (WebSocket)
55 sh.tangled.repo sh.tangled.pipeline triggers
56 sh.tangled.spindle.member
57 sh.tangled.repo.collaborator
58 │ │
59 │ (discover knots │
60 │ + membership) │
61 ▼ ▼
62 ╭────────────────────────────────╮
63 │ tack │
64 │ ╭──────────────────────────╮ │
65 │ │ provider router │ │ selected per-workflow
66 │ │ (fake │ buildkite) │ │ via `tack: { … }` YAML
67 │ ╰──────────────────────────╯ │
68 ╰────────────────────────────────╯
69 │
70 │ Create Build (REST)
71 ▼
72 Buildkite
73 │
74 │ POST /webhooks/buildkite
75 ▼
76 tack ──── /events (WebSocket) ────▶ Tangled appview
77 sh.tangled.pipeline.status
78```
79
80## Configuration
81
82Core configuration controls how tack talks to Tangled. Provider-specific
83configuration (e.g. Buildkite) lives in its own section below.
84
85### Required
86
87| Env var | Description |
88| ---------------- | ----------------------------------------------------------- |
89| `TACK_HOSTNAME` | This spindle's hostname (matches `sh.tangled.repo.spindle`) |
90| `TACK_OWNER_DID` | DID of the spindle operator |
91
92### Optional
93
94| Env var | Description |
95| -------------------- | -------------------------------------------------------- |
96| `TACK_LISTEN_ADDR` | HTTP listen address (default `:8080`) |
97| `TACK_DB_PATH` | Local SQLite path (default `tack.db`) |
98| `TACK_JETSTREAM_URL` | Tangled Jetstream WebSocket URL |
99| `TACK_DEV` | Use `ws://` for knot event-streams (any non-empty value) |
100
101All configured providers are active simultaneously. Each workflow
102chooses its provider via the first key under its top-level `tack:`
103block. e.g. `tack: { buildkite: { ... } }` runs on Buildkite,
104`tack: { fake: {} }` runs on the in-process fake provider.
105
106## Providers
107
108Provider-specific setup (Buildkite-side configuration, the
109provider's tack env vars, and the workflow YAML schema) lives in
110its own doc per provider:
111
112* [Buildkite](docs/buildkite.md)
113* [Tekton](docs/tekton.md)