1+ name : Publish @solana/web3.js (v3.x)
2+
3+ on :
4+ workflow_dispatch :
5+ inputs :
6+ dry-run :
7+ description : ' Dry run (unchecked will publish to npm, push git tag, and create GitHub release)'
8+ required : true
9+ default : true
10+ type : boolean
11+
12+ permissions :
13+ contents : write
14+ id-token : write
15+
16+ # Prevent concurrent publishes
17+ concurrency :
18+ group : web3js-v3-publish
19+ cancel-in-progress : false
20+
21+ jobs :
22+ guard-branch :
23+ name : Guard allowed source branch for publish
24+ runs-on : ubuntu-latest
25+ steps :
26+ - name : Fail if workflow_dispatch is not on v3.x
27+ run : |
28+ if [ "${{ github.ref }}" != "refs/heads/v3.x" ]; then
29+ echo "::error::Publish must be run from v3.x. Current ref: ${{ github.ref }}"
30+ exit 1
31+ fi
32+
33+ publish :
34+ name : Publish @solana/web3.js
35+ runs-on : ubuntu-latest
36+ needs : guard-branch
37+ env :
38+ NPM_CONFIG_PROVENANCE : true
39+
40+ steps :
41+ - name : Checkout repository
42+ uses : actions/checkout@v4
43+ with :
44+ fetch-depth : 0
45+ token : ${{ secrets.GITHUB_TOKEN }}
46+
47+ - name : Setup Git user
48+ run : |
49+ git config user.name "github-actions[bot]"
50+ git config user.email "github-actions[bot]@users.noreply.github.com"
51+
52+ - name : Setup pnpm
53+ uses : pnpm/action-setup@v4
54+ with :
55+ version : 9.1.0
56+ run_install : false
57+
58+ - name : Setup Node.js
59+ uses : actions/setup-node@v4
60+ with :
61+ node-version : ' lts/*'
62+ registry-url : ' https://registry.npmjs.org'
63+ cache : ' pnpm'
64+
65+ - name : Install dependencies
66+ run : pnpm install --frozen-lockfile
67+
68+ - name : Get version
69+ id : version
70+ run : |
71+ VERSION=$(node -p "require('./package.json').version")
72+ echo "version=$VERSION" >> $GITHUB_OUTPUT
73+ echo "Publishing version: $VERSION"
74+
75+ - name : Resolve npm dist-tag from version
76+ id : prerelease
77+ env :
78+ VERSION : ${{ steps.version.outputs.version }}
79+ run : |
80+ # SemVer: MAJOR.MINOR.PATCH[-IDENTIFIER[.N]]. Use the prerelease identifier
81+ # as the npm dist-tag (e.g. 3.0.0-rc.1 -> rc, 1.0.0-maintenance -> maintenance).
82+ # Stable versions publish under 'latest'.
83+ if [[ "$VERSION" == *"-"* ]]; then
84+ PRERELEASE="${VERSION#*-}"
85+ NPM_TAG="${PRERELEASE%%.*}"
86+ IS_PRERELEASE=true
87+ else
88+ NPM_TAG=latest
89+ IS_PRERELEASE=false
90+ fi
91+
92+ if [ -z "$NPM_TAG" ]; then
93+ echo "::error::Could not derive npm dist-tag from version '$VERSION'"
94+ exit 1
95+ fi
96+
97+ echo "npm_tag=$NPM_TAG" >> $GITHUB_OUTPUT
98+ echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
99+ echo "Version: $VERSION | npm tag: $NPM_TAG | prerelease: $IS_PRERELEASE"
100+
101+ - name : Enforce code formatting
102+ run : pnpm test:prettier
103+
104+ - name : Compile types
105+ run : pnpm compile:typedefs
106+
107+ - name : Lint
108+ run : pnpm test:lint
109+
110+ - name : Build
111+ run : pnpm compile:js
112+
113+ - name : Run unit tests
114+ run : pnpm test:unit
115+
116+ - name : Pack tarball
117+ run : |
118+ mkdir -p .npm-pack
119+ # Mirror prepublishOnly: drop devDependencies before pack so tarball is clean.
120+ # pnpm pack does not run prepublishOnly, and we publish from the tarball below.
121+ pnpm pkg delete devDependencies
122+ pnpm pack --pack-destination .npm-pack
123+ echo ""
124+ echo "Packed tarball:"
125+ ls -la .npm-pack/
126+ # Restore package.json so later steps and the working tree are unaffected.
127+ git checkout -- package.json
128+
129+ - name : Guard - tarball contains required build artifacts
130+ run : |
131+ echo "Checking tarball for lib/ artifacts..."
132+ FAILED=0
133+ for tarball in .npm-pack/*.tgz; do
134+ CONTENTS=$(tar -tzf "$tarball")
135+ for f in \
136+ package/lib/index.cjs.js \
137+ package/lib/index.esm.js \
138+ package/lib/index.browser.cjs.js \
139+ package/lib/index.browser.esm.js \
140+ package/lib/index.native.js \
141+ package/lib/index.d.ts; do
142+ if ! echo "$CONTENTS" | grep -q "^${f}$"; then
143+ echo " ✗ ${tarball} missing ${f}"
144+ FAILED=1
145+ fi
146+ done
147+ done
148+ if [ "$FAILED" -eq 1 ]; then
149+ echo ""
150+ echo "Make sure 'pnpm compile:js' and 'pnpm compile:typedefs' ran successfully."
151+ exit 1
152+ fi
153+ echo " ✓ tarball contains all required lib/ artifacts"
154+
155+ - name : Guard - tarball does not include devDependencies
156+ run : |
157+ for tarball in .npm-pack/*.tgz; do
158+ tar -xzf "$tarball" package/package.json -O > /tmp/pkg.json
159+ if node -e "const p=require('/tmp/pkg.json'); process.exit(p.devDependencies && Object.keys(p.devDependencies).length ? 1 : 0)"; then
160+ echo " ✓ ${tarball} has no devDependencies"
161+ else
162+ echo " ✗ ${tarball} contains devDependencies"
163+ exit 1
164+ fi
165+ done
166+
167+ - name : Pre-flight - npm publish dry-run
168+ run : |
169+ VERSION="${{ steps.version.outputs.version }}"
170+ NPM_TAG="${{ steps.prerelease.outputs.npm_tag }}"
171+ TARBALL=".npm-pack/solana-web3.js-${VERSION}.tgz"
172+
173+ if [ ! -f "$TARBALL" ]; then
174+ echo "::error::Tarball not found: ${TARBALL}"
175+ ls -la .npm-pack/ || true
176+ exit 1
177+ fi
178+
179+ # Refuse to overwrite a published version.
180+ NPM_RC=0
181+ NPM_OUT=$(npm view "@solana/web3.js@${VERSION}" version 2>&1) || NPM_RC=$?
182+ if [ "$NPM_RC" -eq 0 ] && [ -n "$NPM_OUT" ]; then
183+ echo "::error::@solana/web3.js@${VERSION} is already published. Bump the version in package.json."
184+ exit 1
185+ fi
186+ if [ "$NPM_RC" -ne 0 ] && ! echo "$NPM_OUT" | grep -q "E404"; then
187+ echo "::error::npm registry error (not 404):"
188+ echo "$NPM_OUT"
189+ exit 1
190+ fi
191+
192+ echo "Running publish dry-run for ${TARBALL} (tag '${NPM_TAG}')..."
193+ npm publish "$TARBALL" --access public --tag "$NPM_TAG" --dry-run
194+ echo "✓ pre-flight passed"
195+
196+ - name : Publish to npm
197+ if : ${{ github.event.inputs.dry-run != 'true' }}
198+ run : |
199+ VERSION="${{ steps.version.outputs.version }}"
200+ NPM_TAG="${{ steps.prerelease.outputs.npm_tag }}"
201+ TARBALL=".npm-pack/solana-web3.js-${VERSION}.tgz"
202+ echo "Publishing ${TARBALL} with tag '${NPM_TAG}'..."
203+ npm publish "$TARBALL" --access public --tag "$NPM_TAG"
204+ echo "✓ published @solana/web3.js@${VERSION}"
205+
206+ - name : Create and push tag
207+ if : ${{ github.event.inputs.dry-run != 'true' }}
208+ run : |
209+ TAG="v${{ steps.version.outputs.version }}"
210+ if git rev-parse "$TAG" >/dev/null 2>&1; then
211+ echo "Tag $TAG already exists, skipping"
212+ else
213+ git tag "$TAG"
214+ git push origin "$TAG"
215+ echo "Created tag $TAG"
216+ fi
217+
218+ - name : Create GitHub Release
219+ if : ${{ github.event.inputs.dry-run != 'true' }}
220+ uses : actions/github-script@v7
221+ with :
222+ github-token : ${{ secrets.GITHUB_TOKEN }}
223+ script : |
224+ const version = `${{ steps.version.outputs.version }}`;
225+ const tagName = `v${version}`;
226+ const releaseName = `@solana/web3.js v${version}`;
227+ const isPrerelease = '${{ steps.prerelease.outputs.is_prerelease }}' === 'true';
228+
229+ const body = [
230+ `## @solana/web3.js v${version}`,
231+ '',
232+ '### Installation',
233+ '',
234+ '```bash',
235+ `pnpm add @solana/web3.js@${version}`,
236+ '```',
237+ '',
238+ `https://www.npmjs.com/package/@solana/web3.js/v/${version}`,
239+ ].join('\n');
240+
241+ try {
242+ await github.rest.repos.getReleaseByTag({
243+ owner: context.repo.owner,
244+ repo: context.repo.repo,
245+ tag: tagName,
246+ });
247+ console.log(`Release ${tagName} already exists, skipping`);
248+ return;
249+ } catch (e) {
250+ if (e.status !== 404) throw e;
251+ }
252+
253+ await github.rest.repos.createRelease({
254+ owner: context.repo.owner,
255+ repo: context.repo.repo,
256+ tag_name: tagName,
257+ name: releaseName,
258+ body: body.trim(),
259+ draft: false,
260+ prerelease: isPrerelease,
261+ });
262+ console.log(`Created release: ${releaseName}`);
263+
264+ - name : Publish summary
265+ run : |
266+ VERSION="${{ steps.version.outputs.version }}"
267+ NPM_TAG="${{ steps.prerelease.outputs.npm_tag }}"
268+ DRY_RUN="${{ github.event.inputs.dry-run }}"
269+
270+ {
271+ echo "## @solana/web3.js v${VERSION}"
272+ echo ""
273+ echo "**Package**: \`@solana/web3.js\`"
274+ echo "**Version**: \`${VERSION}\`"
275+ echo "**npm tag**: \`${NPM_TAG}\`"
276+ echo ""
277+ } >> $GITHUB_STEP_SUMMARY
278+
279+ if [ "$DRY_RUN" == "true" ]; then
280+ echo "Mode: **dry run** (OIDC + tarball validated, nothing published)" >> $GITHUB_STEP_SUMMARY
281+ else
282+ {
283+ echo "Published to npm: https://www.npmjs.com/package/@solana/web3.js/v/${VERSION}"
284+ echo ""
285+ echo "GitHub release: https://github.com/${{ github.repository }}/releases/tag/v${VERSION}"
286+ } >> $GITHUB_STEP_SUMMARY
287+ fi
0 commit comments