Skip to content

Commit d35703b

Browse files
committed
feat: add README, GitHub Pages docs, and CI workflow
Add README with project overview and usage examples, a Python script that auto-generates an HTML package catalog from data.json files, and a GitHub Actions workflow to deploy it to GitHub Pages on push to main.
1 parent 5f6171f commit d35703b

4 files changed

Lines changed: 365 additions & 0 deletions

File tree

.github/workflows/docs.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Deploy docs to GitHub Pages
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: pages
15+
cancel-in-progress: true
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: actions/setup-python@v5
24+
with:
25+
python-version: "3.12"
26+
27+
- name: Generate documentation
28+
run: python docs/generate.py
29+
30+
- uses: actions/upload-pages-artifact@v3
31+
with:
32+
path: docs/_site
33+
34+
deploy:
35+
needs: build
36+
runs-on: ubuntu-latest
37+
environment:
38+
name: github-pages
39+
url: ${{ steps.deployment.outputs.page_url }}
40+
steps:
41+
- id: deployment
42+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ result
22
result-*
33
.direnv
44
.claude/
5+
docs/_site/

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Toolbox
2+
3+
A self-contained, data-driven package registry for [turnkey](https://github.com/firefly-engineering/turnkey). Version metadata lives in JSON data files — Nix code reads this data to build derivations automatically. Adding a new version means adding a JSON entry, not editing Nix code.
4+
5+
## Packages
6+
7+
Toolbox provides versioned, reproducible builds of curated development tools across Linux (x64/arm64) and macOS (x64/arm64), plus toolchain meta-packages that bundle related tools together.
8+
9+
Browse the full package catalog: **[firefly-engineering.github.io/toolbox](https://firefly-engineering.github.io/toolbox)**
10+
11+
## Usage
12+
13+
### Standalone
14+
15+
```bash
16+
# Build a specific version
17+
nix build .#go.1_26_0
18+
19+
# Build the default version
20+
nix build .#beads.default
21+
22+
# Build a toolchain meta-package
23+
nix build .#rust-toolchain.default
24+
25+
# Run a tool directly
26+
nix run .#jq.default -- --version
27+
```
28+
29+
### With Turnkey
30+
31+
```nix
32+
# In turnkey's flake.nix:
33+
inputs.toolbox.url = "github:firefly-engineering/toolbox";
34+
35+
# In registryExtensions:
36+
registryExtensions = {
37+
go = inputs.toolbox.registry.${system}.go;
38+
beads = inputs.toolbox.registry.${system}.beads;
39+
};
40+
```
41+
42+
## Platforms
43+
44+
All packages target:
45+
46+
- `x86_64-linux`
47+
- `aarch64-linux`
48+
- `x86_64-darwin`
49+
- `aarch64-darwin`
50+
51+
## Adding Packages
52+
53+
See [AGENTS.md](AGENTS.md) for the full guide on adding new packages and versions.
54+
55+
## License
56+
57+
[MIT](LICENSE)

docs/generate.py

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#!/usr/bin/env python3
2+
"""Generate GitHub Pages documentation from the toolbox package registry."""
3+
4+
import json
5+
import os
6+
import re
7+
from pathlib import Path
8+
9+
10+
def read_toolchain_components(nix_path: Path) -> list[str]:
11+
"""Parse a toolchain default.nix to extract component package names."""
12+
content = nix_path.read_text()
13+
return re.findall(r"toolbox\.(\w[\w-]*)\.versions", content)
14+
15+
16+
def main():
17+
repo_root = Path(__file__).resolve().parent.parent
18+
packages_dir = repo_root / "packages"
19+
out_dir = repo_root / "docs" / "_site"
20+
out_dir.mkdir(parents=True, exist_ok=True)
21+
22+
packages = []
23+
toolchains = []
24+
25+
for pkg_dir in sorted(packages_dir.iterdir()):
26+
if not pkg_dir.is_dir():
27+
continue
28+
29+
name = pkg_dir.name
30+
data_json = pkg_dir / "data.json"
31+
32+
if data_json.exists():
33+
data = json.loads(data_json.read_text())
34+
meta = data.get("_meta", {})
35+
default_version = meta.get("default", "")
36+
versions = sorted(
37+
[k for k in data if k != "_meta"],
38+
key=lambda v: v,
39+
reverse=True,
40+
)
41+
packages.append(
42+
{
43+
"name": name,
44+
"default": default_version,
45+
"versions": versions,
46+
}
47+
)
48+
else:
49+
# Toolchain meta-package
50+
nix_path = pkg_dir / "default.nix"
51+
components = (
52+
read_toolchain_components(nix_path) if nix_path.exists() else []
53+
)
54+
toolchains.append(
55+
{
56+
"name": name,
57+
"components": components,
58+
}
59+
)
60+
61+
html = render_html(packages, toolchains)
62+
(out_dir / "index.html").write_text(html)
63+
print(f"Generated docs with {len(packages)} packages and {len(toolchains)} toolchains")
64+
65+
66+
def render_html(packages: list[dict], toolchains: list[dict]) -> str:
67+
package_rows = ""
68+
for pkg in packages:
69+
versions_html = ", ".join(
70+
f'<span class="version{"" if v != pkg["default"] else " default"}">{v}</span>'
71+
for v in pkg["versions"]
72+
)
73+
package_rows += f""" <tr>
74+
<td class="pkg-name">{pkg["name"]}</td>
75+
<td><code>{pkg["default"]}</code></td>
76+
<td>{versions_html}</td>
77+
</tr>
78+
"""
79+
80+
toolchain_rows = ""
81+
for tc in toolchains:
82+
components_html = ", ".join(
83+
f"<code>{c}</code>" for c in tc["components"]
84+
)
85+
toolchain_rows += f""" <tr>
86+
<td class="pkg-name">{tc["name"]}</td>
87+
<td>{components_html}</td>
88+
</tr>
89+
"""
90+
91+
total_versions = sum(len(p["versions"]) for p in packages)
92+
93+
return f"""<!DOCTYPE html>
94+
<html lang="en">
95+
<head>
96+
<meta charset="utf-8">
97+
<meta name="viewport" content="width=device-width, initial-scale=1">
98+
<title>Toolbox Package Registry</title>
99+
<style>
100+
:root {{
101+
--bg: #0d1117;
102+
--surface: #161b22;
103+
--border: #30363d;
104+
--text: #e6edf3;
105+
--text-muted: #8b949e;
106+
--accent: #58a6ff;
107+
--green: #3fb950;
108+
}}
109+
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
110+
body {{
111+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
112+
background: var(--bg);
113+
color: var(--text);
114+
line-height: 1.6;
115+
padding: 2rem;
116+
max-width: 1100px;
117+
margin: 0 auto;
118+
}}
119+
h1 {{
120+
font-size: 2rem;
121+
margin-bottom: 0.25rem;
122+
}}
123+
.subtitle {{
124+
color: var(--text-muted);
125+
margin-bottom: 2rem;
126+
font-size: 1.1rem;
127+
}}
128+
.stats {{
129+
display: flex;
130+
gap: 2rem;
131+
margin-bottom: 2rem;
132+
}}
133+
.stat {{
134+
background: var(--surface);
135+
border: 1px solid var(--border);
136+
border-radius: 8px;
137+
padding: 1rem 1.5rem;
138+
}}
139+
.stat-value {{
140+
font-size: 1.5rem;
141+
font-weight: 600;
142+
color: var(--accent);
143+
}}
144+
.stat-label {{
145+
color: var(--text-muted);
146+
font-size: 0.85rem;
147+
}}
148+
h2 {{
149+
font-size: 1.4rem;
150+
margin: 2rem 0 1rem;
151+
padding-bottom: 0.5rem;
152+
border-bottom: 1px solid var(--border);
153+
}}
154+
table {{
155+
width: 100%;
156+
border-collapse: collapse;
157+
background: var(--surface);
158+
border: 1px solid var(--border);
159+
border-radius: 8px;
160+
overflow: hidden;
161+
}}
162+
th {{
163+
text-align: left;
164+
padding: 0.75rem 1rem;
165+
background: var(--bg);
166+
color: var(--text-muted);
167+
font-size: 0.85rem;
168+
font-weight: 600;
169+
text-transform: uppercase;
170+
letter-spacing: 0.05em;
171+
}}
172+
td {{
173+
padding: 0.6rem 1rem;
174+
border-top: 1px solid var(--border);
175+
}}
176+
.pkg-name {{
177+
font-weight: 600;
178+
color: var(--accent);
179+
}}
180+
code {{
181+
background: var(--bg);
182+
padding: 0.15em 0.4em;
183+
border-radius: 4px;
184+
font-size: 0.9em;
185+
}}
186+
.version {{
187+
display: inline-block;
188+
background: var(--bg);
189+
padding: 0.1em 0.5em;
190+
border-radius: 4px;
191+
font-size: 0.85em;
192+
margin: 0.1em;
193+
}}
194+
.version.default {{
195+
background: rgba(63, 185, 80, 0.15);
196+
color: var(--green);
197+
font-weight: 600;
198+
}}
199+
footer {{
200+
margin-top: 3rem;
201+
padding-top: 1rem;
202+
border-top: 1px solid var(--border);
203+
color: var(--text-muted);
204+
font-size: 0.85rem;
205+
}}
206+
footer a {{
207+
color: var(--accent);
208+
text-decoration: none;
209+
}}
210+
</style>
211+
</head>
212+
<body>
213+
<h1>Toolbox</h1>
214+
<p class="subtitle">Self-contained package registry for <a href="https://github.com/firefly-engineering/turnkey" style="color: var(--accent); text-decoration: none;">turnkey</a></p>
215+
216+
<div class="stats">
217+
<div class="stat">
218+
<div class="stat-value">{len(packages)}</div>
219+
<div class="stat-label">Packages</div>
220+
</div>
221+
<div class="stat">
222+
<div class="stat-value">{total_versions}</div>
223+
<div class="stat-label">Versions</div>
224+
</div>
225+
<div class="stat">
226+
<div class="stat-value">{len(toolchains)}</div>
227+
<div class="stat-label">Toolchains</div>
228+
</div>
229+
</div>
230+
231+
<h2>Packages</h2>
232+
<table>
233+
<thead>
234+
<tr>
235+
<th>Package</th>
236+
<th>Default</th>
237+
<th>Available Versions</th>
238+
</tr>
239+
</thead>
240+
<tbody>
241+
{package_rows} </tbody>
242+
</table>
243+
244+
<h2>Toolchain Meta-packages</h2>
245+
<table>
246+
<thead>
247+
<tr>
248+
<th>Toolchain</th>
249+
<th>Included Packages</th>
250+
</tr>
251+
</thead>
252+
<tbody>
253+
{toolchain_rows} </tbody>
254+
</table>
255+
256+
<footer>
257+
<p>Generated from <a href="https://github.com/firefly-engineering/toolbox">firefly-engineering/toolbox</a>. Default versions shown in <span style="color: var(--green);">green</span>.</p>
258+
</footer>
259+
</body>
260+
</html>
261+
"""
262+
263+
264+
if __name__ == "__main__":
265+
main()

0 commit comments

Comments
 (0)