Skip to content

Commit 93a64d5

Browse files
cr
1 parent 8801647 commit 93a64d5

File tree

1 file changed

+165
-95
lines changed

1 file changed

+165
-95
lines changed

.github/workflows/dev-release.yml

Lines changed: 165 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,6 @@ name: Dev Release
22
on:
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
@@ -54,17 +12,18 @@ env:
5412
CI: true
5513

5614
concurrency:
57-
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.package }}
15+
group: ${{ github.workflow }}-${{ github.ref }}
5816
cancel-in-progress: true
5917

6018
jobs:
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

Comments
 (0)