Skip to content

Commit a73bfc9

Browse files
authored
Simplify release pipeline and post build preview comments on PRs (#770)
* Post a comment with the registry build preview Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com> * Ensure released and packagted registries are the same Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com> --------- Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
1 parent decd65c commit a73bfc9

File tree

2 files changed

+131
-292
lines changed

2 files changed

+131
-292
lines changed

.github/workflows/build-and-publish.yml

Lines changed: 41 additions & 292 deletions
Original file line numberDiff line numberDiff line change
@@ -69,285 +69,11 @@ jobs:
6969
- name: Validate catalog entries
7070
run: task catalog:validate
7171

72-
build-and-release:
73-
name: Build and Release Registry
72+
update-catalog-data:
73+
name: Update Catalog Data Files
7474
runs-on: ubuntu-latest
7575
needs: [lint, validate-and-test]
7676
if: github.ref == 'refs/heads/main' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
77-
steps:
78-
- name: Checkout code
79-
uses: actions/checkout@v6
80-
with:
81-
fetch-depth: 0
82-
83-
- name: Set up Go
84-
uses: actions/setup-go@v6
85-
with:
86-
go-version-file: 'go.mod'
87-
cache: true
88-
89-
- name: Install Task
90-
uses: arduino/setup-task@v2
91-
92-
- name: Build registry files
93-
run: |
94-
mkdir -p dist
95-
task catalog:build
96-
# catalog outputs to build/<registry-name>/
97-
for registry_dir in build/*/; do
98-
registry_name=$(basename "$registry_dir")
99-
[ -f "$registry_dir/registry.json" ] || continue
100-
cp "$registry_dir/registry.json" "dist/registry.json"
101-
cp "$registry_dir/official-registry.json" "dist/official-registry.json"
102-
done
103-
CONTAINER_COUNT=$(jq '.servers | length' dist/registry.json)
104-
REMOTE_COUNT=$(jq '.remote_servers | length // 0' dist/registry.json)
105-
TOTAL_COUNT=$((CONTAINER_COUNT + REMOTE_COUNT))
106-
echo "Registry built successfully with $TOTAL_COUNT entries ($CONTAINER_COUNT container-based, $REMOTE_COUNT remote)"
107-
108-
- name: Validate JSON files
109-
run: |
110-
echo "Validating ToolHive format..."
111-
jq empty dist/registry.json
112-
echo "Valid JSON"
113-
114-
jq -e '.last_updated' dist/registry.json > /dev/null
115-
echo "Has last_updated field"
116-
117-
jq -e '.servers' dist/registry.json > /dev/null
118-
echo "Has servers field"
119-
120-
jq -e 'has("remote_servers")' dist/registry.json > /dev/null && echo "Has remote_servers field" || echo "No remote_servers field"
121-
122-
jq -e '."$schema"' dist/registry.json > /dev/null
123-
echo "Has schema field"
124-
125-
echo ""
126-
echo "Validating Official MCP format..."
127-
jq empty dist/official-registry.json
128-
echo "Valid JSON"
129-
130-
jq -e '.version' dist/official-registry.json > /dev/null
131-
echo "Has version field"
132-
133-
jq -e '.meta.last_updated' dist/official-registry.json > /dev/null
134-
echo "Has meta.last_updated field"
135-
136-
jq -e '.data.servers' dist/official-registry.json > /dev/null
137-
echo "Has data.servers field"
138-
139-
SERVER_COUNT=$(jq '.data.servers | length' dist/official-registry.json)
140-
if [ "$SERVER_COUNT" -gt 0 ]; then
141-
jq -e '.data.servers[0].name' dist/official-registry.json > /dev/null
142-
echo "Servers have name field"
143-
144-
jq -e '.data.servers[0]._meta."io.modelcontextprotocol.registry/publisher-provided"' dist/official-registry.json > /dev/null
145-
echo "Servers have _meta with publisher-provided extensions"
146-
fi
147-
148-
- name: Generate metadata
149-
id: metadata
150-
run: |
151-
VERSION=$(date +'%Y.%m.%d')
152-
TIMESTAMP=$(date +'%Y%m%d-%H%M%S')
153-
echo "version=$VERSION" >> $GITHUB_OUTPUT
154-
echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT
155-
156-
CONTAINER_COUNT=$(jq '.servers | length' dist/registry.json)
157-
REMOTE_COUNT=$(jq '.remote_servers | length // 0' dist/registry.json)
158-
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
159-
160-
ACTIVE_CONTAINER=$(jq '[.servers[] | select(.status == "Active")] | length' dist/registry.json)
161-
ACTIVE_REMOTE=$(jq '[(.remote_servers // {})[] | select(.status == "Active")] | length' dist/registry.json)
162-
ACTIVE=$((ACTIVE_CONTAINER + ACTIVE_REMOTE))
163-
164-
BETA_CONTAINER=$(jq '[.servers[] | select(.status == "Beta")] | length' dist/registry.json)
165-
BETA_REMOTE=$(jq '[(.remote_servers // {})[] | select(.status == "Beta")] | length' dist/registry.json)
166-
BETA=$((BETA_CONTAINER + BETA_REMOTE))
167-
168-
DEPRECATED_CONTAINER=$(jq '[.servers[] | select(.status == "Deprecated")] | length' dist/registry.json)
169-
DEPRECATED_REMOTE=$(jq '[(.remote_servers // {})[] | select(.status == "Deprecated")] | length' dist/registry.json)
170-
DEPRECATED=$((DEPRECATED_CONTAINER + DEPRECATED_REMOTE))
171-
172-
OFFICIAL_CONTAINER=$(jq '[.servers[] | select(.tier == "Official")] | length' dist/registry.json)
173-
OFFICIAL_REMOTE=$(jq '[(.remote_servers // {})[] | select(.tier == "Official")] | length' dist/registry.json)
174-
OFFICIAL=$((OFFICIAL_CONTAINER + OFFICIAL_REMOTE))
175-
176-
PARTNER_CONTAINER=$(jq '[.servers[] | select(.tier == "Partner")] | length' dist/registry.json)
177-
PARTNER_REMOTE=$(jq '[(.remote_servers // {})[] | select(.tier == "Partner")] | length' dist/registry.json)
178-
PARTNER=$((PARTNER_CONTAINER + PARTNER_REMOTE))
179-
180-
COMMUNITY_CONTAINER=$(jq '[.servers[] | select(.tier == "Community")] | length' dist/registry.json)
181-
COMMUNITY_REMOTE=$(jq '[(.remote_servers // {})[] | select(.tier == "Community")] | length' dist/registry.json)
182-
COMMUNITY=$((COMMUNITY_CONTAINER + COMMUNITY_REMOTE))
183-
184-
echo "total=$TOTAL" >> $GITHUB_OUTPUT
185-
echo "container_count=$CONTAINER_COUNT" >> $GITHUB_OUTPUT
186-
echo "remote_count=$REMOTE_COUNT" >> $GITHUB_OUTPUT
187-
echo "active=$ACTIVE" >> $GITHUB_OUTPUT
188-
echo "beta=$BETA" >> $GITHUB_OUTPUT
189-
echo "deprecated=$DEPRECATED" >> $GITHUB_OUTPUT
190-
echo "official=$OFFICIAL" >> $GITHUB_OUTPUT
191-
echo "partner=$PARTNER" >> $GITHUB_OUTPUT
192-
echo "community=$COMMUNITY" >> $GITHUB_OUTPUT
193-
194-
- name: Create checksums
195-
run: |
196-
cd dist
197-
sha256sum registry.json > registry.json.sha256
198-
md5sum registry.json > registry.json.md5
199-
sha256sum official-registry.json > official-registry.json.sha256
200-
md5sum official-registry.json > official-registry.json.md5
201-
202-
- name: Create tarball
203-
run: |
204-
cd dist
205-
tar -czf registry-${{ steps.metadata.outputs.version }}.tar.gz \
206-
registry.json registry.json.sha256 registry.json.md5 \
207-
official-registry.json official-registry.json.sha256 official-registry.json.md5
208-
tar -tzf registry-${{ steps.metadata.outputs.version }}.tar.gz
209-
210-
- name: Check if release exists
211-
id: check_release
212-
run: |
213-
if gh release view "v${{ steps.metadata.outputs.version }}" >/dev/null 2>&1; then
214-
echo "exists=true" >> $GITHUB_OUTPUT
215-
echo "Release v${{ steps.metadata.outputs.version }} already exists"
216-
else
217-
echo "exists=false" >> $GITHUB_OUTPUT
218-
echo "Release v${{ steps.metadata.outputs.version }} does not exist"
219-
fi
220-
env:
221-
GH_TOKEN: ${{ github.token }}
222-
223-
- name: Get changes since last release
224-
id: changes
225-
if: steps.check_release.outputs.exists == 'false'
226-
run: |
227-
LAST_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName' || echo "")
228-
229-
if [ -z "$LAST_TAG" ]; then
230-
echo "No previous release found"
231-
CHANGES="Initial release"
232-
else
233-
echo "Last release: $LAST_TAG"
234-
CHANGES=$(git log --pretty=format:"- %s" $LAST_TAG..HEAD --grep="^feat\|^fix\|^docs\|^chore" | head -20)
235-
236-
if [ -z "$CHANGES" ]; then
237-
CHANGES="- Minor updates and maintenance"
238-
fi
239-
fi
240-
241-
{
242-
echo "changes<<EOF"
243-
echo "$CHANGES"
244-
echo "EOF"
245-
} >> $GITHUB_OUTPUT
246-
env:
247-
GH_TOKEN: ${{ github.token }}
248-
249-
- name: Create Release
250-
if: steps.check_release.outputs.exists == 'false'
251-
uses: ncipollo/release-action@v1
252-
with:
253-
tag: v${{ steps.metadata.outputs.version }}
254-
name: Registry v${{ steps.metadata.outputs.version }}
255-
body: |
256-
## ToolHive Registry Snapshot
257-
258-
**Date**: ${{ steps.metadata.outputs.version }}
259-
**Build**: ${{ steps.metadata.outputs.timestamp }}
260-
261-
### Statistics
262-
263-
| Category | Count |
264-
|----------|-------|
265-
| **Total Servers** | ${{ steps.metadata.outputs.total }} |
266-
| **Container-based** | ${{ steps.metadata.outputs.container_count }} |
267-
| **Remote** | ${{ steps.metadata.outputs.remote_count }} |
268-
| **Active** | ${{ steps.metadata.outputs.active }} |
269-
| **Beta** | ${{ steps.metadata.outputs.beta }} |
270-
| **Deprecated** | ${{ steps.metadata.outputs.deprecated }} |
271-
272-
| Tier | Count |
273-
|------|-------|
274-
| **Official** | ${{ steps.metadata.outputs.official }} |
275-
| **Partner** | ${{ steps.metadata.outputs.partner }} |
276-
| **Community** | ${{ steps.metadata.outputs.community }} |
277-
278-
### Download Options
279-
280-
**Individual Files:**
281-
- **registry.json** - ToolHive format registry file
282-
- **official-registry.json** - Official MCP format registry file
283-
284-
**Archives:**
285-
- **registry-${{ steps.metadata.outputs.version }}.tar.gz** - Complete archive with both formats and checksums
286-
287-
### Direct URLs
288-
289-
**ToolHive Format:**
290-
- Latest: `https://github.com/stacklok/toolhive-registry/releases/latest/download/registry.json`
291-
- This version: `https://github.com/stacklok/toolhive-registry/releases/download/v${{ steps.metadata.outputs.version }}/registry.json`
292-
293-
**Official MCP Format:**
294-
- Latest: `https://github.com/stacklok/toolhive-registry/releases/latest/download/official-registry.json`
295-
- This version: `https://github.com/stacklok/toolhive-registry/releases/download/v${{ steps.metadata.outputs.version }}/official-registry.json`
296-
297-
### Recent Changes
298-
299-
${{ steps.changes.outputs.changes }}
300-
301-
---
302-
*This is an automated release generated from the main branch.*
303-
artifacts: |
304-
dist/registry.json
305-
dist/registry.json.sha256
306-
dist/registry.json.md5
307-
dist/official-registry.json
308-
dist/official-registry.json.sha256
309-
dist/official-registry.json.md5
310-
dist/registry-${{ steps.metadata.outputs.version }}.tar.gz
311-
makeLatest: true
312-
artifactErrorsFailBuild: true
313-
314-
- name: Update existing release
315-
if: steps.check_release.outputs.exists == 'true'
316-
run: |
317-
echo "Updating existing release v${{ steps.metadata.outputs.version }}"
318-
319-
# Clean up legacy catalog-* prefixed assets from previous dual-pipeline builds
320-
for asset in $(gh release view "v${{ steps.metadata.outputs.version }}" --json assets -q '.assets[].name | select(startswith("catalog-"))'); do
321-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" "$asset" --yes || true
322-
done
323-
324-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json --yes || true
325-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json.sha256 --yes || true
326-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json.md5 --yes || true
327-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" official-registry.json --yes || true
328-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" official-registry.json.sha256 --yes || true
329-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" official-registry.json.md5 --yes || true
330-
gh release delete-asset "v${{ steps.metadata.outputs.version }}" "registry-${{ steps.metadata.outputs.version }}.tar.gz" --yes || true
331-
332-
gh release upload "v${{ steps.metadata.outputs.version }}" \
333-
dist/registry.json \
334-
dist/registry.json.sha256 \
335-
dist/registry.json.md5 \
336-
dist/official-registry.json \
337-
dist/official-registry.json.sha256 \
338-
dist/official-registry.json.md5 \
339-
"dist/registry-${{ steps.metadata.outputs.version }}.tar.gz" \
340-
--clobber
341-
342-
echo "Release updated successfully"
343-
env:
344-
GH_TOKEN: ${{ github.token }}
345-
346-
open-dist-pr:
347-
name: Open PR with Built Registry Files
348-
runs-on: ubuntu-latest
349-
needs: [build-and-release]
350-
if: github.ref == 'refs/heads/main' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
35177
steps:
35278
- name: Checkout code
35379
uses: actions/checkout@v6
@@ -376,20 +102,23 @@ jobs:
376102
id: metadata
377103
run: echo "version=$(date +'%Y.%m.%d')" >> $GITHUB_OUTPUT
378104

379-
- name: Open PR with built registry files
105+
- name: Open or update PR with catalog data files
380106
uses: peter-evans/create-pull-request@v8
381107
with:
382108
token: ${{ secrets.GITHUB_TOKEN }}
383-
commit-message: 'chore: update built registry files for v${{ steps.metadata.outputs.version }}'
384-
title: 'chore: update built registry files for v${{ steps.metadata.outputs.version }}'
109+
commit-message: 'chore: update catalog data files for v${{ steps.metadata.outputs.version }}'
110+
title: 'chore: update catalog data files for v${{ steps.metadata.outputs.version }}'
385111
body: |
386-
Automated PR to commit the built registry files for release `v${{ steps.metadata.outputs.version }}`.
112+
Automated PR to update the embedded catalog data files for `v${{ steps.metadata.outputs.version }}`.
387113
388114
Files updated:
389115
- `pkg/catalog/toolhive/data/registry.json` — ToolHive legacy format
390116
- `pkg/catalog/toolhive/data/official-registry.json` — Upstream MCP format
391-
branch: update-dist-${{ steps.metadata.outputs.version }}
117+
118+
When merged, a release will be created automatically.
119+
branch: update-catalog-data-${{ steps.metadata.outputs.version }}
392120
delete-branch: true
121+
auto-merge: true
393122
add-paths: |
394123
pkg/catalog/*/data/registry.json
395124
pkg/catalog/*/data/official-registry.json
@@ -415,23 +144,43 @@ jobs:
415144
run: task catalog:build
416145

417146
- name: Generate PR comment
147+
id: comment
418148
run: |
419149
CONTAINER_COUNT=$(jq '.servers | length' build/toolhive/registry.json)
420150
REMOTE_COUNT=$(jq '.remote_servers | length // 0' build/toolhive/registry.json)
421151
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
422152
SIZE=$(du -h build/toolhive/registry.json | cut -f1)
153+
LAST_UPDATED=$(jq -r '.last_updated' build/toolhive/registry.json)
423154
424-
echo "## Registry Build Preview" > pr-comment.md
425-
echo "" >> pr-comment.md
426-
echo "Registry built successfully!" >> pr-comment.md
427-
echo "" >> pr-comment.md
428-
echo "- **Total Servers**: $TOTAL" >> pr-comment.md
429-
echo " - Container-based: $CONTAINER_COUNT" >> pr-comment.md
430-
echo " - Remote: $REMOTE_COUNT" >> pr-comment.md
431-
echo "- **File Size**: $SIZE" >> pr-comment.md
432-
echo "- **Last Updated**: $(jq -r '.last_updated' build/toolhive/registry.json)" >> pr-comment.md
433-
echo "" >> pr-comment.md
434-
echo "The registry files will be published when this PR is merged." >> pr-comment.md
155+
{
156+
echo "body<<EOF"
157+
echo "## Registry Build Preview"
158+
echo ""
159+
echo "Registry built successfully!"
160+
echo ""
161+
echo "- **Total Servers**: $TOTAL"
162+
echo " - Container-based: $CONTAINER_COUNT"
163+
echo " - Remote: $REMOTE_COUNT"
164+
echo "- **File Size**: $SIZE"
165+
echo "- **Last Updated**: $LAST_UPDATED"
166+
echo "EOF"
167+
} >> $GITHUB_OUTPUT
168+
169+
- name: Find existing comment
170+
id: find-comment
171+
uses: peter-evans/find-comment@v3
172+
with:
173+
issue-number: ${{ github.event.pull_request.number }}
174+
comment-author: github-actions[bot]
175+
body-includes: Registry Build Preview
176+
177+
- name: Post or update PR comment
178+
uses: peter-evans/create-or-update-comment@v4
179+
with:
180+
comment-id: ${{ steps.find-comment.outputs.comment-id }}
181+
issue-number: ${{ github.event.pull_request.number }}
182+
body: ${{ steps.comment.outputs.body }}
183+
edit-mode: replace
435184

436185
- name: Upload PR artifact
437186
uses: actions/upload-artifact@v6

0 commit comments

Comments
 (0)