@@ -2,48 +2,6 @@ name: Dev Release
22on :
33 workflow_dispatch :
44 inputs :
5- package :
6- description : " Package to release"
7- required : true
8- type : choice
9- options :
10- - " langchain"
11- - " @langchain/core"
12- - " @langchain/classic"
13- - " @langchain/community"
14- - " @langchain/textsplitters"
15- - " @langchain/mcp-adapters"
16- - " @langchain/anthropic"
17- - " @langchain/aws"
18- - " @langchain/azure-cosmosdb"
19- - " @langchain/azure-dynamic-sessions"
20- - " @langchain/baidu-qianfan"
21- - " @langchain/cerebras"
22- - " @langchain/cloudflare"
23- - " @langchain/cohere"
24- - " @langchain/deepseek"
25- - " @langchain/exa"
26- - " @langchain/google-cloud-sql-pg"
27- - " @langchain/google-common"
28- - " @langchain/google-gauth"
29- - " @langchain/google-genai"
30- - " @langchain/google-vertexai"
31- - " @langchain/google-vertexai-web"
32- - " @langchain/google-webauth"
33- - " @langchain/groq"
34- - " @langchain/mistralai"
35- - " @langchain/mixedbread-ai"
36- - " @langchain/mongodb"
37- - " @langchain/nomic"
38- - " @langchain/ollama"
39- - " @langchain/openai"
40- - " @langchain/pinecone"
41- - " @langchain/qdrant"
42- - " @langchain/redis"
43- - " @langchain/tavily"
44- - " @langchain/weaviate"
45- - " @langchain/xai"
46- - " @langchain/yandex"
475 npm_tag :
486 description : " NPM tag for the release"
497 required : false
5412 CI : true
5513
5614concurrency :
57- group : ${{ github.workflow }}-${{ github.ref }}-${{ inputs.package }}
15+ group : ${{ github.workflow }}-${{ github.ref }}
5816 cancel-in-progress : true
5917
6018jobs :
6119 release :
62- name : Dev Release ${{ inputs.package }}
20+ name : Dev Release
6321 if : github.repository == 'langchain-ai/langchainjs'
6422 runs-on : ubuntu-latest
6523 permissions :
6624 contents : read
6725 id-token : write
26+ pull-requests : write
6827 steps :
6928 - name : ⬇️ Checkout repo
7029 uses : actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
@@ -83,66 +42,177 @@ jobs:
8342 - name : 📥 Install deps
8443 run : pnpm install --frozen-lockfile
8544
86- - name : 🔍 Get package directory
87- id : package-dir
45+ - name : 🔍 Check for changesets
46+ id : changesets
8847 run : |
89- PACKAGE="${{ inputs.package }}"
90- # Map package name to directory
91- case "$PACKAGE" in
92- "@langchain/core")
93- DIR="libs/langchain-core"
94- ;;
95- "langchain")
96- DIR="libs/langchain"
97- ;;
98- "@langchain/community")
99- DIR="libs/langchain-community"
100- ;;
101- "@langchain/textsplitters")
102- DIR="libs/langchain-textsplitters"
103- ;;
104- "@langchain/mcp-adapters")
105- DIR="libs/langchain-mcp-adapters"
106- ;;
107- "@langchain/standard-tests")
108- DIR="libs/langchain-standard-tests"
109- ;;
110- "@langchain/classic")
111- DIR="libs/langchain-classic"
112- ;;
113- *)
114- # For provider packages, extract the package name without @langchain/
115- PKG_NAME="${PACKAGE#@langchain/}"
116- DIR="libs/providers/langchain-${PKG_NAME}"
117- ;;
118- esac
119- echo "dir=$DIR" >> $GITHUB_OUTPUT
120-
121- - name : 🏷️ Get short SHA
122- id : sha
123- run : echo "short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
124-
125- - name : 📝 Update package version
126- working-directory : ${{ steps.package-dir.outputs.dir }}
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
12765 run : |
128- # Get current version from package.json
129- CURRENT_VERSION=$(node -p "require('./package.json').version")
130- # Create dev version with tag and short SHA
131- DEV_VERSION="${CURRENT_VERSION}-${{ inputs.npm_tag }}.${{ steps.sha.outputs.short }}"
132- echo "Publishing ${{ inputs.package }}@${DEV_VERSION}"
133- # Update version in package.json
134- npm version "$DEV_VERSION" --no-git-tag-version
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
13575
136- - name : 🔨 Build package
137- run : pnpm run build --filter "${{ inputs.package }}"
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
13881
13982 - name : 🔐 Setup npm auth
83+ if : steps.version.outputs.success == '1'
14084 run : |
14185 echo "registry=https://registry.npmjs.org" >> ~/.npmrc
14286 echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc
14387
14488 - name : 🚀 Publish to npm
145- working-directory : ${{ steps.package-dir .outputs.dir }}
146- run : npm publish --tag ${{ inputs.npm_tag }} --access public
89+ if : steps.version .outputs.success == '1'
90+ run : pnpm changeset publish --tag ${{ inputs.npm_tag }} --no-git-tag
14791 env :
14892 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+ }
0 commit comments