Skip to content

Commit 4583edc

Browse files
committed
feat: Add CI/CD workflow for building and publishing registry
- Automated validation and testing on every push and PR - Daily scheduled builds to catch any updates - Creates dated GitHub releases (v2025.01.08 format) - Provides multiple download options: - Direct release assets with checksums - Stable URL for latest release - Versioned URLs for specific releases - PR preview builds with statistics - Automatic release notes with changes - Updates existing daily releases if re-run ToolHive can now import from: https://github.com/stacklok/toolhive-registry/releases/latest/download/registry.json
1 parent f12518d commit 4583edc

File tree

1 file changed

+293
-0
lines changed

1 file changed

+293
-0
lines changed
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
name: Build and Publish Registry
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
schedule:
11+
# Run daily at 00:00 UTC to catch any updates
12+
- cron: '0 0 * * *'
13+
workflow_dispatch:
14+
15+
permissions:
16+
contents: write
17+
pull-requests: read
18+
19+
jobs:
20+
validate-and-test:
21+
name: Validate and Test
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
27+
- name: Set up Go
28+
uses: actions/setup-go@v5
29+
with:
30+
go-version-file: 'go.mod'
31+
cache: true
32+
33+
- name: Install dependencies
34+
run: go mod download
35+
36+
- name: Run tests
37+
run: go test -v -race -coverprofile=coverage.out ./...
38+
39+
- name: Upload coverage reports
40+
uses: codecov/codecov-action@v4
41+
with:
42+
file: ./coverage.out
43+
flags: unittests
44+
name: codecov-umbrella
45+
continue-on-error: true
46+
47+
- name: Build tools
48+
run: |
49+
go build -o registry-builder ./cmd/registry-builder
50+
go build -o import-from-toolhive ./cmd/import-from-toolhive
51+
52+
- name: Validate registry entries
53+
run: ./registry-builder validate -v
54+
55+
build-and-release:
56+
name: Build and Release Registry
57+
runs-on: ubuntu-latest
58+
needs: validate-and-test
59+
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
60+
steps:
61+
- name: Checkout code
62+
uses: actions/checkout@v4
63+
with:
64+
fetch-depth: 0
65+
66+
- name: Set up Go
67+
uses: actions/setup-go@v5
68+
with:
69+
go-version-file: 'go.mod'
70+
cache: true
71+
72+
- name: Build registry-builder
73+
run: go build -o registry-builder ./cmd/registry-builder
74+
75+
- name: Build registry.json
76+
run: |
77+
mkdir -p dist
78+
./registry-builder build -v
79+
cp build/registry.json dist/registry.json
80+
echo "Registry built successfully with $(jq '.servers | length' dist/registry.json) entries"
81+
82+
- name: Validate JSON
83+
run: |
84+
# Validate JSON structure
85+
jq empty dist/registry.json
86+
echo "✅ Valid JSON"
87+
88+
# Check required fields
89+
jq -e '.last_updated' dist/registry.json > /dev/null
90+
echo "✅ Has last_updated field"
91+
92+
jq -e '.servers' dist/registry.json > /dev/null
93+
echo "✅ Has servers field"
94+
95+
jq -e '."$schema"' dist/registry.json > /dev/null
96+
echo "✅ Has schema field"
97+
98+
- name: Generate metadata
99+
id: metadata
100+
run: |
101+
# Generate version based on date and time
102+
VERSION=$(date +'%Y.%m.%d')
103+
TIMESTAMP=$(date +'%Y%m%d-%H%M%S')
104+
echo "version=$VERSION" >> $GITHUB_OUTPUT
105+
echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT
106+
107+
# Count servers by status and tier
108+
TOTAL=$(jq '.servers | length' dist/registry.json)
109+
ACTIVE=$(jq '[.servers[] | select(.status == "Active")] | length' dist/registry.json)
110+
BETA=$(jq '[.servers[] | select(.status == "Beta")] | length' dist/registry.json)
111+
DEPRECATED=$(jq '[.servers[] | select(.status == "Deprecated")] | length' dist/registry.json)
112+
OFFICIAL=$(jq '[.servers[] | select(.tier == "Official")] | length' dist/registry.json)
113+
PARTNER=$(jq '[.servers[] | select(.tier == "Partner")] | length' dist/registry.json)
114+
COMMUNITY=$(jq '[.servers[] | select(.tier == "Community")] | length' dist/registry.json)
115+
116+
echo "total=$TOTAL" >> $GITHUB_OUTPUT
117+
echo "active=$ACTIVE" >> $GITHUB_OUTPUT
118+
echo "beta=$BETA" >> $GITHUB_OUTPUT
119+
echo "deprecated=$DEPRECATED" >> $GITHUB_OUTPUT
120+
echo "official=$OFFICIAL" >> $GITHUB_OUTPUT
121+
echo "partner=$PARTNER" >> $GITHUB_OUTPUT
122+
echo "community=$COMMUNITY" >> $GITHUB_OUTPUT
123+
124+
- name: Create checksums
125+
run: |
126+
cd dist
127+
sha256sum registry.json > registry.json.sha256
128+
md5sum registry.json > registry.json.md5
129+
130+
- name: Create tarball
131+
run: |
132+
cd dist
133+
tar -czf registry-${{ steps.metadata.outputs.version }}.tar.gz registry.json registry.json.sha256 registry.json.md5
134+
tar -tzf registry-${{ steps.metadata.outputs.version }}.tar.gz
135+
136+
- name: Check if release exists
137+
id: check_release
138+
run: |
139+
if gh release view "v${{ steps.metadata.outputs.version }}" >/dev/null 2>&1; then
140+
echo "exists=true" >> $GITHUB_OUTPUT
141+
echo "Release v${{ steps.metadata.outputs.version }} already exists"
142+
else
143+
echo "exists=false" >> $GITHUB_OUTPUT
144+
echo "Release v${{ steps.metadata.outputs.version }} does not exist"
145+
fi
146+
env:
147+
GH_TOKEN: ${{ github.token }}
148+
149+
- name: Get changes since last release
150+
id: changes
151+
if: steps.check_release.outputs.exists == 'false'
152+
run: |
153+
# Get the last release tag
154+
LAST_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName' || echo "")
155+
156+
if [ -z "$LAST_TAG" ]; then
157+
echo "No previous release found"
158+
CHANGES="Initial release"
159+
else
160+
echo "Last release: $LAST_TAG"
161+
# Get commit messages since last release
162+
CHANGES=$(git log --pretty=format:"- %s" $LAST_TAG..HEAD --grep="^feat\|^fix\|^docs\|^chore" | head -20)
163+
164+
if [ -z "$CHANGES" ]; then
165+
CHANGES="- Minor updates and maintenance"
166+
fi
167+
fi
168+
169+
# Write to file to preserve formatting
170+
echo "$CHANGES" > changes.txt
171+
env:
172+
GH_TOKEN: ${{ github.token }}
173+
174+
- name: Create Release
175+
if: steps.check_release.outputs.exists == 'false'
176+
uses: ncipollo/release-action@v1
177+
with:
178+
tag: v${{ steps.metadata.outputs.version }}
179+
name: Registry v${{ steps.metadata.outputs.version }}
180+
body: |
181+
## 📦 ToolHive Registry Snapshot
182+
183+
**Date**: ${{ steps.metadata.outputs.version }}
184+
**Build**: ${{ steps.metadata.outputs.timestamp }}
185+
186+
### 📊 Statistics
187+
188+
| Category | Count |
189+
|----------|-------|
190+
| **Total Servers** | ${{ steps.metadata.outputs.total }} |
191+
| **Active** | ${{ steps.metadata.outputs.active }} |
192+
| **Beta** | ${{ steps.metadata.outputs.beta }} |
193+
| **Deprecated** | ${{ steps.metadata.outputs.deprecated }} |
194+
195+
| Tier | Count |
196+
|------|-------|
197+
| **Official** | ${{ steps.metadata.outputs.official }} |
198+
| **Partner** | ${{ steps.metadata.outputs.partner }} |
199+
| **Community** | ${{ steps.metadata.outputs.community }} |
200+
201+
### 📥 Download Options
202+
203+
- **registry.json** - The complete registry file
204+
- **registry-${{ steps.metadata.outputs.version }}.tar.gz** - Archive with checksums
205+
206+
### 🔗 Direct URLs
207+
208+
Latest registry is always available at:
209+
- `https://github.com/stacklok/toolhive-registry/releases/latest/download/registry.json`
210+
211+
This specific version:
212+
- `https://github.com/stacklok/toolhive-registry/releases/download/v${{ steps.metadata.outputs.version }}/registry.json`
213+
214+
### 📝 Recent Changes
215+
216+
${{ steps.changes.outputs.changes }}
217+
218+
---
219+
*This is an automated release generated from the main branch.*
220+
artifacts: |
221+
dist/registry.json
222+
dist/registry.json.sha256
223+
dist/registry.json.md5
224+
dist/registry-${{ steps.metadata.outputs.version }}.tar.gz
225+
makeLatest: true
226+
artifactErrorsFailBuild: true
227+
228+
- name: Update existing release
229+
if: steps.check_release.outputs.exists == 'true'
230+
run: |
231+
echo "Updating existing release v${{ steps.metadata.outputs.version }}"
232+
233+
# Delete old assets
234+
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json --yes || true
235+
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json.sha256 --yes || true
236+
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json.md5 --yes || true
237+
gh release delete-asset "v${{ steps.metadata.outputs.version }}" "registry-${{ steps.metadata.outputs.version }}.tar.gz" --yes || true
238+
239+
# Upload new assets
240+
gh release upload "v${{ steps.metadata.outputs.version }}" \
241+
dist/registry.json \
242+
dist/registry.json.sha256 \
243+
dist/registry.json.md5 \
244+
"dist/registry-${{ steps.metadata.outputs.version }}.tar.gz" \
245+
--clobber
246+
247+
echo "✅ Release updated successfully"
248+
env:
249+
GH_TOKEN: ${{ github.token }}
250+
251+
build-pr:
252+
name: Build PR Preview
253+
runs-on: ubuntu-latest
254+
if: github.event_name == 'pull_request'
255+
steps:
256+
- name: Checkout code
257+
uses: actions/checkout@v4
258+
259+
- name: Set up Go
260+
uses: actions/setup-go@v5
261+
with:
262+
go-version-file: 'go.mod'
263+
cache: true
264+
265+
- name: Build registry-builder
266+
run: go build -o registry-builder ./cmd/registry-builder
267+
268+
- name: Build registry.json
269+
run: |
270+
mkdir -p build
271+
./registry-builder build -v
272+
273+
- name: Generate PR comment
274+
run: |
275+
TOTAL=$(jq '.servers | length' build/registry.json)
276+
SIZE=$(du -h build/registry.json | cut -f1)
277+
278+
echo "## 📦 Registry Build Preview" > pr-comment.md
279+
echo "" >> pr-comment.md
280+
echo "✅ Registry built successfully!" >> pr-comment.md
281+
echo "" >> pr-comment.md
282+
echo "- **Total Servers**: $TOTAL" >> pr-comment.md
283+
echo "- **File Size**: $SIZE" >> pr-comment.md
284+
echo "- **Last Updated**: $(jq -r '.last_updated' build/registry.json)" >> pr-comment.md
285+
echo "" >> pr-comment.md
286+
echo "The registry.json will be published when this PR is merged." >> pr-comment.md
287+
288+
- name: Upload PR artifact
289+
uses: actions/upload-artifact@v4
290+
with:
291+
name: pr-registry-json
292+
path: build/registry.json
293+
retention-days: 7

0 commit comments

Comments
 (0)