-
Notifications
You must be signed in to change notification settings - Fork 137
198 lines (175 loc) · 7.8 KB
/
release.yml
File metadata and controls
198 lines (175 loc) · 7.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
name: Release
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
# `contents: write` is required for `gh release create` to publish
# the GitHub Release at the end of the workflow. `id-token: write`
# stays for npm provenance.
contents: write
id-token: write
steps:
- name: Checkout source code
uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22.x
cache: "npm"
registry-url: "https://registry.npmjs.org"
- name: Install dependencies
run: npm install --legacy-peer-deps
- name: Build library
run: npm run dist
- name: Build MCP server
run: npm run build:mcp
- name: Run tests with coverage
run: npx vitest run --coverage
- name: Run type check
run: npm run typescript
- name: Run test type check
run: npm run typescript:tests
- name: Check chart spec registry round-trip
run: npm run check:chart-specs
- name: Check capability matrix freshness
run: npm run check:capabilities
- name: Check blog metadata registry freshness
run: npm run check:blog-entries
- name: Verify TypeScript declarations
run: |
for f in dist/semiotic.d.ts dist/semiotic-xy.d.ts dist/semiotic-ordinal.d.ts dist/semiotic-network.d.ts dist/semiotic-geo.d.ts dist/semiotic-realtime.d.ts dist/semiotic-ai.d.ts dist/semiotic-data.d.ts dist/semiotic-server.d.ts dist/semiotic-themes.d.ts; do
if [ ! -f "$f" ]; then
echo "MISSING: $f — aborting release"
exit 1
fi
done
echo "All declaration files present"
- name: Determine npm dist-tag
id: dist-tag
run: |
VERSION=$(node -p "require('./package.json').version")
if echo "$VERSION" | grep -qE '[-](alpha|beta|rc)'; then
echo "tag=beta" >> "$GITHUB_OUTPUT"
else
echo "tag=latest" >> "$GITHUB_OUTPUT"
fi
# Pack-and-import smoke test runs BEFORE publish (not inside
# prepublishOnly). Running `npm pack` inside the prepublishOnly
# hook of `npm publish` can hit a re-entrancy edge case on some
# npm versions where the inner pack returns 0 without producing
# a tarball, breaking the smoke check (seen on Node 22 / npm 10
# in CI). Splitting it out keeps the validation while sidestepping
# the lifecycle interaction.
- name: Pack-and-import smoke test
run: npm run check:pack
- name: Dry-run publish (validate package)
run: npm publish --dry-run --tag ${{ steps.dist-tag.outputs.tag }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish to npm
run: npm publish --provenance --access public --tag ${{ steps.dist-tag.outputs.tag }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Post-publish smoke test
run: |
VERSION=$(node -p "require('./package.json').version")
TAG=${{ steps.dist-tag.outputs.tag }}
echo "Waiting for npm to propagate semiotic@${VERSION}..."
for i in 1 2 3 4 5; do
PUBLISHED=$(npm view semiotic@${VERSION} version 2>/dev/null || echo "")
if [ "$PUBLISHED" = "$VERSION" ]; then
echo "Package available on npm"
break
fi
echo "Attempt $i: not yet available, waiting 15s..."
sleep 15
done
if [ "$PUBLISHED" != "$VERSION" ]; then
echo "WARNING: Package not yet visible on npm registry after 75s"
echo "This is likely a propagation delay — check manually"
exit 0
fi
# Install in a temp project and verify imports
TMPDIR=$(mktemp -d)
cd "$TMPDIR"
npm init -y > /dev/null 2>&1
npm install semiotic@${VERSION} react react-dom > /dev/null 2>&1
node -e "
const assert = require('assert');
// Main entry
const s = require('semiotic');
assert(s.BarChart, 'BarChart not exported from semiotic');
assert(s.LineChart, 'LineChart not exported from semiotic');
assert(s.ForceDirectedGraph, 'ForceDirectedGraph not exported from semiotic');
// Sub-path entries
const xy = require('semiotic/xy');
assert(xy.LineChart, 'LineChart not exported from semiotic/xy');
const ord = require('semiotic/ordinal');
assert(ord.BarChart, 'BarChart not exported from semiotic/ordinal');
const net = require('semiotic/network');
assert(net.SankeyDiagram, 'SankeyDiagram not exported from semiotic/network');
const rt = require('semiotic/realtime');
assert(rt.RealtimeLineChart, 'RealtimeLineChart not exported from semiotic/realtime');
const srv = require('semiotic/server');
assert(srv.renderChart, 'renderChart not exported from semiotic/server');
const themes = require('semiotic/themes');
assert(themes.resolveThemePreset, 'resolveThemePreset not exported from semiotic/themes');
const utils = require('semiotic/utils');
assert(utils.validateProps, 'validateProps not exported from semiotic/utils');
console.log('All smoke tests passed — 8 entry points verified');
"
rm -rf "$TMPDIR"
- name: Create or update GitHub Release
# Pulls the matching CHANGELOG section so the GH release page
# mirrors the version's entry. Falls back to `--generate-notes`
# (auto-built from PR titles) if the section is missing or empty
# so a release page is still created either way. Without this
# step a successful npm publish leaves the GitHub Releases page
# stuck on the previous version (the v3.2.3 → v3.4.2 backfill
# in 2026-04-28 fixed exactly that gap).
#
# Idempotent on workflow re-runs: if the release for this tag
# already exists, `gh release edit` updates the title/notes in
# place rather than failing on the conflict. Without that, a
# re-run for any reason (transient infra failure, manually
# re-triggered after a fix) would error out at this step.
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${GITHUB_REF_NAME#v}"
NOTES_FILE="$(mktemp)"
awk -v ver="$VERSION" '
BEGIN { flag = 0 }
$0 ~ "^## \\[" ver "\\]" { flag = 1; next }
flag && /^## \[/ { exit }
flag { print }
' CHANGELOG.md > "$NOTES_FILE"
if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
echo "Release $GITHUB_REF_NAME already exists; updating in place"
if [ -s "$NOTES_FILE" ]; then
gh release edit "$GITHUB_REF_NAME" \
--title "Semiotic $GITHUB_REF_NAME" \
--notes-file "$NOTES_FILE"
else
echo "No CHANGELOG section for $VERSION; leaving existing notes unchanged"
gh release edit "$GITHUB_REF_NAME" \
--title "Semiotic $GITHUB_REF_NAME"
fi
else
if [ -s "$NOTES_FILE" ]; then
gh release create "$GITHUB_REF_NAME" \
--title "Semiotic $GITHUB_REF_NAME" \
--notes-file "$NOTES_FILE" \
--verify-tag
else
echo "No CHANGELOG section for $VERSION; falling back to --generate-notes"
gh release create "$GITHUB_REF_NAME" \
--title "Semiotic $GITHUB_REF_NAME" \
--generate-notes \
--verify-tag
fi
fi
rm -f "$NOTES_FILE"