Skip to content

Commit 0fe11c4

Browse files
ci: add GitHub workflow for publishing dev releases (#9524)
1 parent cc022b0 commit 0fe11c4

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

.github/workflows/dev-release.yml

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
name: Dev Release
2+
on:
3+
workflow_dispatch:
4+
inputs:
5+
npm_tag:
6+
description: "NPM tag for the release"
7+
required: false
8+
default: "dev"
9+
type: string
10+
11+
env:
12+
CI: true
13+
14+
concurrency:
15+
group: ${{ github.workflow }}-${{ github.ref }}
16+
cancel-in-progress: true
17+
18+
jobs:
19+
release:
20+
name: Dev Release
21+
if: github.repository == 'langchain-ai/langchainjs'
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: read
25+
id-token: write
26+
pull-requests: write
27+
steps:
28+
- name: ⬇️ Checkout repo
29+
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
30+
with:
31+
fetch-depth: 0
32+
33+
- name: 📦 Setup pnpm
34+
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
35+
36+
- name: ⎔ Setup node
37+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
38+
with:
39+
node-version-file: ".nvmrc"
40+
cache: "pnpm"
41+
42+
- name: 📥 Install deps
43+
run: pnpm install --frozen-lockfile
44+
45+
- name: 🔍 Check for changesets
46+
id: changesets
47+
run: |
48+
# Check if there are any changeset files (excluding README.md)
49+
CHANGESET_COUNT=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ')
50+
echo "count=$CHANGESET_COUNT" >> $GITHUB_OUTPUT
51+
if [ "$CHANGESET_COUNT" -eq 0 ]; then
52+
echo "::error::No changesets found. Please add a changeset before running a dev release."
53+
echo "::error::Run 'pnpm changeset' to create a changeset for your changes."
54+
exit 1
55+
fi
56+
echo "Found $CHANGESET_COUNT changeset(s)"
57+
58+
- name: 🧹 Prepare for snapshot release
59+
run: |
60+
# Disable changelog generation for snapshot releases
61+
pnpm dlx json -I -f .changeset/config.json -e "this.changelog = false"
62+
63+
- name: 📦 Version packages (snapshot)
64+
id: version
65+
run: |
66+
# Version all packages with changesets using snapshot mode
67+
# This will also update internal dependencies automatically
68+
OUTPUT=$(pnpm changeset version --snapshot ${{ inputs.npm_tag }} 2>&1) || true
69+
echo "$OUTPUT"
70+
if echo "$OUTPUT" | grep -q "No unreleased changesets found"; then
71+
echo "success=0" >> $GITHUB_OUTPUT
72+
else
73+
echo "success=1" >> $GITHUB_OUTPUT
74+
fi
75+
76+
- name: 🔨 Build packages
77+
if: steps.version.outputs.success == '1'
78+
run: |
79+
# Build all packages (turbo will handle dependency order)
80+
pnpm run build
81+
82+
- name: 🔐 Setup npm auth
83+
if: steps.version.outputs.success == '1'
84+
run: |
85+
echo "registry=https://registry.npmjs.org" >> ~/.npmrc
86+
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc
87+
88+
- name: 🚀 Publish to npm
89+
if: steps.version.outputs.success == '1'
90+
run: pnpm changeset publish --tag ${{ inputs.npm_tag }} --no-git-tag
91+
env:
92+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
93+
94+
- name: 📋 Get published packages
95+
if: steps.version.outputs.success == '1'
96+
id: published
97+
uses: actions/github-script@v7
98+
with:
99+
script: |
100+
const fs = require('fs');
101+
const path = require('path');
102+
const glob = require('@actions/glob');
103+
104+
// Find all package.json files in libs directory
105+
const globber = await glob.create('libs/**/package.json', {
106+
followSymbolicLinks: false
107+
});
108+
const files = await globber.glob();
109+
110+
const tag = '${{ inputs.npm_tag }}';
111+
const packages = [];
112+
113+
for (const file of files) {
114+
// Skip node_modules
115+
if (file.includes('node_modules')) continue;
116+
117+
try {
118+
const content = fs.readFileSync(file, 'utf8');
119+
const pkg = JSON.parse(content);
120+
121+
// Check if this package has a snapshot version
122+
if (pkg.version && pkg.version.includes(`-${tag}`)) {
123+
packages.push({
124+
name: pkg.name,
125+
version: pkg.version
126+
});
127+
}
128+
} catch (e) {
129+
// Skip files that can't be parsed
130+
}
131+
}
132+
133+
if (packages.length === 0) {
134+
console.log('No packages with snapshot versions found');
135+
core.setOutput('packages', '[]');
136+
core.setOutput('count', '0');
137+
return;
138+
}
139+
140+
// Sort packages alphabetically
141+
packages.sort((a, b) => a.name.localeCompare(b.name));
142+
143+
console.log(`Found ${packages.length} published package(s):`);
144+
packages.forEach(p => console.log(` - ${p.name}@${p.version}`));
145+
146+
core.setOutput('packages', JSON.stringify(packages));
147+
core.setOutput('count', packages.length.toString());
148+
149+
// Write to step summary
150+
const summary = core.summary
151+
.addHeading('📦 Published Packages', 3)
152+
.addTable([
153+
[{data: 'Package', header: true}, {data: 'Version', header: true}],
154+
...packages.map(p => [p.name, `\`${p.version}\``])
155+
])
156+
.addHeading('Install', 4)
157+
.addCodeBlock(
158+
packages.map(p => `npm i ${p.name}@${p.version} --save-exact`).join('\n'),
159+
'sh'
160+
);
161+
162+
await summary.write();
163+
164+
- name: 💬 Comment on PR
165+
if: steps.version.outputs.success == '1' && steps.published.outputs.count != '0'
166+
uses: actions/github-script@v7
167+
with:
168+
script: |
169+
// Get the current branch name
170+
const branch = context.ref.replace('refs/heads/', '');
171+
172+
// Find open PRs where this branch is the head
173+
const { data: prs } = await github.rest.pulls.list({
174+
owner: context.repo.owner,
175+
repo: context.repo.repo,
176+
state: 'open',
177+
head: `${context.repo.owner}:${branch}`
178+
});
179+
180+
if (prs.length === 0) {
181+
console.log(`No open PRs found for branch: ${branch}`);
182+
return;
183+
}
184+
185+
const packages = JSON.parse('${{ steps.published.outputs.packages }}');
186+
const run_url = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
187+
188+
// Build the package table
189+
const tableRows = packages.map(p => `| ${p.name} | \`${p.version}\` |`).join('\n');
190+
191+
// Build install commands
192+
const packageVersions = packages.map(p => `${p.name}@${p.version}`).join(' ');
193+
194+
const body = `## 📦 Dev Release Published
195+
196+
A dev release has been published for this PR!
197+
198+
| Package | Version |
199+
| --- | --- |
200+
${tableRows}
201+
202+
**Install:**
203+
\`\`\`sh
204+
npm i ${packageVersions} --save-exact
205+
\`\`\`
206+
207+
[View workflow run](${run_url})`;
208+
209+
// Comment on each PR
210+
for (const pr of prs) {
211+
console.log(`Commenting on PR #${pr.number}`);
212+
await github.rest.issues.createComment({
213+
owner: context.repo.owner,
214+
repo: context.repo.repo,
215+
issue_number: pr.number,
216+
body: body.split('\n').map(line => line.trim()).join('\n')
217+
});
218+
}

CONTRIBUTING.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,36 @@ a developer and published to [npm](https://www.npmjs.com/package/langchain).
7676
If your contribution has made its way into a release, we will want to give you credit on Twitter (only if you want though)!
7777
If you have a Twitter account you would like us to mention, please let us know in the PR or in another manner.
7878

79+
#### 🧪 Dev Releases
80+
81+
For testing unreleased changes, maintainers can publish dev releases of individual packages. Dev releases are published to npm with a special tag and version format that includes the git commit SHA.
82+
83+
**To create a dev release:**
84+
85+
1. Go to [Actions → Dev Release](https://github.com/langchain-ai/langchainjs/actions/workflows/dev-release.yml)
86+
2. Click "Run workflow"
87+
3. Select the branch you want to release from (defaults to `main`, but you can choose your feature branch)
88+
5. Optionally change the npm tag (defaults to `dev`)
89+
6. Click "Run workflow"
90+
91+
**Version format:** `x.y.z-<tag>.<short-sha>` (e.g., `1.1.0-dev.abc1234`)
92+
93+
**To install a dev release:**
94+
95+
```bash
96+
# Install the latest dev release
97+
npm install @langchain/core@dev
98+
99+
# Install a specific dev version
100+
npm install @langchain/core@1.1.0-dev.abc1234
101+
```
102+
103+
Dev releases are useful for:
104+
105+
- Testing bug fixes before an official release
106+
- Validating new features in downstream projects
107+
- CI/CD pipelines that need to test against the latest changes
108+
79109
### 🛠️ Tooling
80110

81111
This project uses the following tools, which are worth getting familiar

0 commit comments

Comments
 (0)