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