fix(cli): resolve conflicting langsmith env var precedence #1989
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Unified PR labeler — applies size, file-based, title-based, and | |
| # contributor classification labels in a single sequential workflow. | |
| # | |
| # Consolidates pr_size_labeler.yml, pr_labeler_file.yml, | |
| # pr_labeler_title.yml, and PR-handling from tag-external-issues.yml | |
| # into one workflow to eliminate race conditions from concurrent label | |
| # mutations. tag-external-issues.yml remains active for issue-only | |
| # labeling. Backfill lives in pr_labeler_backfill.yml. | |
| # | |
| # Config and shared logic live in .github/scripts/pr-labeler-config.json | |
| # and .github/scripts/pr-labeler.js — update those when adding partners. | |
| # | |
| # Setup Requirements: | |
| # 1. Create a GitHub App with permissions: | |
| # - Repository: Pull requests (write) | |
| # - Repository: Issues (write) | |
| # - Organization: Members (read) | |
| # 2. Install the app on your organization and this repository | |
| # 3. Add these repository secrets: | |
| # - ORG_MEMBERSHIP_APP_ID: Your app's ID | |
| # - ORG_MEMBERSHIP_APP_PRIVATE_KEY: Your app's private key | |
| # | |
| # The GitHub App token is required to check private organization membership | |
| # and to propagate label events to downstream workflows. | |
| name: "🏷️ PR Labeler" | |
| on: | |
| # Safe since we only check out the base branch, not the PR's code. | |
| # NEVER CHECK OUT UNTRUSTED CODE FROM A PR's HEAD IN A pull_request_target JOB. | |
| # Doing so would allow attackers to execute arbitrary code in the context of your repository. | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, edited] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| # Separate opened events so external/tier labels are never lost to cancellation | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}-${{ github.event.action == 'opened' && 'opened' || 'update' }} | |
| cancel-in-progress: ${{ github.event.action != 'opened' }} | |
| jobs: | |
| label: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| # Checks out the base branch (NOT the PR head) so that | |
| # require('./.github/scripts/pr-labeler.js') resolves. | |
| - uses: actions/checkout@v6 | |
| - name: Generate GitHub App token | |
| if: github.event.action == 'opened' | |
| id: app-token | |
| uses: actions/create-github-app-token@v3 | |
| with: | |
| app-id: ${{ secrets.ORG_MEMBERSHIP_APP_ID }} | |
| private-key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }} | |
| - name: Verify App token | |
| if: github.event.action == 'opened' | |
| run: | | |
| if [ -z "${{ steps.app-token.outputs.token }}" ]; then | |
| echo "::error::GitHub App token generation failed — cannot classify contributor" | |
| exit 1 | |
| fi | |
| - name: Check org membership | |
| if: github.event.action == 'opened' | |
| id: check-membership | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); | |
| const author = context.payload.sender.login; | |
| const { isExternal } = await h.checkMembership( | |
| author, context.payload.sender.type, | |
| ); | |
| core.setOutput('is-external', isExternal ? 'true' : 'false'); | |
| # Rename `deepagents` scope → `sdk` for non-release PRs. | |
| # Release PRs (e.g. `release(deepagents): 1.2.0`) are left | |
| # untouched since their titles are canonical version records. | |
| # Runs before labeling so title-based labels read the | |
| # corrected title. | |
| - name: Rename deepagents scope to sdk | |
| id: rename-scope | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const pr = context.payload.pull_request; | |
| if (!pr) { | |
| console.log('No pull_request in payload; skipping scope rename'); | |
| return; | |
| } | |
| const title = pr.title ?? ''; | |
| const match = title.match(/^(\w+!?)\(([^)]+)\)(!?:\s*.*)$/); | |
| if (!match) { | |
| console.log(`Title has no scoped format; skipping rename: "${title}"`); | |
| return; | |
| } | |
| const type = match[1].replace('!', '').toLowerCase(); | |
| if (type === 'release') { | |
| console.log(`Skipping release PR: ${title}`); | |
| return; | |
| } | |
| const scopeStr = match[2]; | |
| const newScope = scopeStr | |
| .split(',') | |
| .map(s => s.trim() === 'deepagents' ? 'sdk' : s.trim()) | |
| .join(','); | |
| if (newScope === scopeStr) return; | |
| const newTitle = `${match[1]}(${newScope})${match[3]}`; | |
| console.log(`Renaming: "${title}" → "${newTitle}"`); | |
| try { | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: pr.number, | |
| title: newTitle, | |
| }); | |
| } catch (error) { | |
| core.warning( | |
| `Failed to rename PR #${pr.number} title ` + | |
| `(${error.status ?? 'unknown'}): ${error.message}. ` + | |
| `Labeling will continue with the original title.` | |
| ); | |
| return; | |
| } | |
| // Pass corrected title to the labeling step via output; | |
| // context.payload.pull_request.title is frozen from the | |
| // webhook event and won't reflect the API update. | |
| core.setOutput('title', newTitle); | |
| - name: Apply PR labels | |
| uses: actions/github-script@v8 | |
| env: | |
| IS_EXTERNAL: ${{ steps.check-membership.outputs.is-external }} | |
| RENAMED_TITLE: ${{ steps.rename-scope.outputs.title }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); | |
| const pr = context.payload.pull_request; | |
| if (!pr) return; | |
| const prNumber = pr.number; | |
| const action = context.payload.action; | |
| const toAdd = new Set(); | |
| const toRemove = new Set(); | |
| const currentLabels = (await github.paginate( | |
| github.rest.issues.listLabelsOnIssue, | |
| { owner, repo, issue_number: prNumber, per_page: 100 }, | |
| )).map(l => l.name ?? ''); | |
| // ── Size + file labels (skip on 'edited' — files unchanged) ── | |
| if (action !== 'edited') { | |
| for (const sl of h.sizeLabels) await h.ensureLabel(sl); | |
| const files = await github.paginate(github.rest.pulls.listFiles, { | |
| owner, repo, pull_number: prNumber, per_page: 100, | |
| }); | |
| const { totalChanged, sizeLabel } = h.computeSize(files); | |
| toAdd.add(sizeLabel); | |
| for (const sl of h.sizeLabels) { | |
| if (currentLabels.includes(sl) && sl !== sizeLabel) toRemove.add(sl); | |
| } | |
| console.log(`Size: ${totalChanged} changed lines → ${sizeLabel}`); | |
| for (const label of h.matchFileLabels(files)) { | |
| toAdd.add(label); | |
| } | |
| } | |
| // ── Title-based labels ── | |
| // Use renamed title if the scope-rename step rewrote it, | |
| // since pr.title still reflects the pre-update value. | |
| const title = process.env.RENAMED_TITLE || pr.title || ''; | |
| const { labels: titleLabels, typeLabel } = h.matchTitleLabels(title); | |
| for (const label of titleLabels) { | |
| toAdd.add(label); | |
| } | |
| // Remove stale type labels only when a type was detected | |
| if (typeLabel) { | |
| for (const tl of h.allTypeLabels) { | |
| if (currentLabels.includes(tl) && !titleLabels.has(tl)) toRemove.add(tl); | |
| } | |
| } | |
| // ── Internal label (only on open, non-external contributors) ── | |
| // IS_EXTERNAL is empty string on non-opened events (step didn't | |
| // run), so this guard is only true for opened + internal. | |
| if (action === 'opened' && process.env.IS_EXTERNAL === 'false') { | |
| toAdd.add('internal'); | |
| } | |
| // ── Apply changes ── | |
| // Ensure all labels we're about to add exist (addLabels returns | |
| // 422 if any label in the batch is missing, which would prevent | |
| // ALL labels from being applied). | |
| for (const name of toAdd) { | |
| await h.ensureLabel(name); | |
| } | |
| for (const name of toRemove) { | |
| if (toAdd.has(name)) continue; | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner, repo, issue_number: prNumber, name, | |
| }); | |
| } catch (e) { | |
| if (e.status !== 404) throw e; | |
| } | |
| } | |
| const addList = [...toAdd]; | |
| if (addList.length > 0) { | |
| await github.rest.issues.addLabels({ | |
| owner, repo, issue_number: prNumber, labels: addList, | |
| }); | |
| } | |
| const removed = [...toRemove].filter(r => !toAdd.has(r)); | |
| console.log(`PR #${prNumber}: +[${addList.join(', ')}] -[${removed.join(', ')}]`); | |
| - name: Apply contributor tier label | |
| if: github.event.action == 'opened' && steps.check-membership.outputs.is-external == 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); | |
| const pr = context.payload.pull_request; | |
| await h.applyTierLabel(pr.number, pr.user.login); | |
| - name: Add external label | |
| if: github.event.action == 'opened' && steps.check-membership.outputs.is-external == 'true' | |
| uses: actions/github-script@v8 | |
| with: | |
| # Use App token so the "labeled" event propagates to downstream | |
| # workflows (e.g. require_issue_link.yml). Events created by the | |
| # default GITHUB_TOKEN do not trigger additional workflow runs. | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const { h } = require('./.github/scripts/pr-labeler.js').loadAndInit(github, owner, repo, core); | |
| const prNumber = context.payload.pull_request.number; | |
| await h.ensureLabel('external'); | |
| await github.rest.issues.addLabels({ | |
| owner, repo, | |
| issue_number: prNumber, | |
| labels: ['external'], | |
| }); | |
| console.log(`Added 'external' label to PR #${prNumber}`); |