From 1fa27c0d3f24484a7a08b0efea6b1fca079a262b Mon Sep 17 00:00:00 2001 From: Mason Daugherty Date: Sat, 15 Nov 2025 16:46:12 -0500 Subject: [PATCH 1/2] feat(infra): add workflow to tag external issues and PRs --- .../workflows/tag-external-contributions.yml | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .github/workflows/tag-external-contributions.yml diff --git a/.github/workflows/tag-external-contributions.yml b/.github/workflows/tag-external-contributions.yml new file mode 100644 index 0000000000..be0401a8cc --- /dev/null +++ b/.github/workflows/tag-external-contributions.yml @@ -0,0 +1,109 @@ +# Automatically tag issues and pull requests as "external" +# when created by users who are not members of the langchain-ai +# GitHub organization. +# +# Setup Requirements: +# 1. Create a GitHub App with the following permissions: +# - Repository permissions: Issues (write), Pull requests (write) +# - Organization permissions: Members (read) +# 2. Install the GitHub App on the organization and repositories +# 3. Add the GitHub App's private key as ORG_MEMBERSHIP_TOKEN repository secret +# +# Alternatively, use a Personal Access Token (classic) with: +# - repo scope (for issues/PRs) +# - read:org scope (for organization membership) +# +# The workflow will fall back to GITHUB_TOKEN if ORG_MEMBERSHIP_TOKEN is not set, +# but this will only show public organization memberships. + +name: Tag External Contributions + +on: + issues: + types: [opened] + pull_request_target: + types: [opened] + +jobs: + tag-external: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: read + + steps: + - name: Check if contributor is external + id: check-membership + uses: actions/github-script@v7 + with: + # Use a GitHub App token or PAT with org:read permissions to check private membership + github-token: ${{ secrets.ORG_MEMBERSHIP_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { owner, repo } = context.repo; + const author = context.payload.sender.login; + + try { + // Check if the author is a member of the langchain-ai organization + // This requires org:read permissions to see private memberships + const membership = await github.rest.orgs.getMembershipForUser({ + org: 'langchain-ai', + username: author + }); + + // Check if membership is active (not just pending invitation) + if (membership.data.state === 'active') { + console.log(`User ${author} is an active member of langchain-ai organization`); + core.setOutput('is-external', 'false'); + } else { + console.log(`User ${author} has pending membership in langchain-ai organization`); + core.setOutput('is-external', 'true'); + } + } catch (error) { + if (error.status === 404) { + console.log(`User ${author} is not a member of langchain-ai organization`); + core.setOutput('is-external', 'true'); + } else { + console.error('Error checking membership:', error); + console.log('Status:', error.status); + console.log('Message:', error.message); + // If we can't determine membership due to API error, assume external for safety + core.setOutput('is-external', 'true'); + } + } + + - name: Add external label to issue + if: steps.check-membership.outputs.is-external == 'true' && github.event_name == 'issues' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { owner, repo } = context.repo; + const issue_number = context.payload.issue.number; + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number, + labels: ['external'] + }); + + console.log(`Added 'external' label to issue #${issue_number}`); + + - name: Add external label to pull request + if: steps.check-membership.outputs.is-external == 'true' && github.event_name == 'pull_request_target' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { owner, repo } = context.repo; + const pull_number = context.payload.pull_request.number; + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: pull_number, + labels: ['external'] + }); + + console.log(`Added 'external' label to pull request #${pull_number}`); From 2a4c5db3a46d0c97fe9f0178ddfa97c2473d5081 Mon Sep 17 00:00:00 2001 From: Mason Daugherty Date: Sat, 22 Nov 2025 16:40:07 -0500 Subject: [PATCH 2/2] cr --- .../workflows/tag-external-contributions.yml | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tag-external-contributions.yml b/.github/workflows/tag-external-contributions.yml index be0401a8cc..09d23c3f62 100644 --- a/.github/workflows/tag-external-contributions.yml +++ b/.github/workflows/tag-external-contributions.yml @@ -3,17 +3,22 @@ # GitHub organization. # # Setup Requirements: -# 1. Create a GitHub App with the following permissions: -# - Repository permissions: Issues (write), Pull requests (write) -# - Organization permissions: Members (read) -# 2. Install the GitHub App on the organization and repositories -# 3. Add the GitHub App's private key as ORG_MEMBERSHIP_TOKEN repository secret +# Option 1: GitHub App (Recommended - no user dependency) +# 1. Create a GitHub App with permissions: +# - Repository: Issues (write), Pull requests (write) +# - Organization: Members (read) +# 2. Install the app on your organization +# 3. Add these secrets: +# - ORG_MEMBERSHIP_APP_ID: Your app's ID +# - ORG_MEMBERSHIP_APP_PRIVATE_KEY: Your app's private key # -# Alternatively, use a Personal Access Token (classic) with: -# - repo scope (for issues/PRs) -# - read:org scope (for organization membership) +# Option 2: Personal Access Token (simpler but user-dependent) +# 1. Create a Personal Access Token (classic) with: +# - repo scope (for issues/PRs access) +# - read:org scope (for organization membership checks) +# 2. Add the PAT as ORG_MEMBERSHIP_TOKEN repository secret # -# The workflow will fall back to GITHUB_TOKEN if ORG_MEMBERSHIP_TOKEN is not set, +# The workflow will fall back to GITHUB_TOKEN if no secrets are set, # but this will only show public organization memberships. name: Tag External Contributions @@ -30,15 +35,22 @@ jobs: permissions: issues: write pull-requests: write - contents: read steps: + - name: Generate GitHub App token + if: ${{ secrets.ORG_MEMBERSHIP_APP_ID && secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }} + id: app-token + uses: tibdex/github-app-token@v1 + with: + app_id: ${{ secrets.ORG_MEMBERSHIP_APP_ID }} + private_key: ${{ secrets.ORG_MEMBERSHIP_APP_PRIVATE_KEY }} + - name: Check if contributor is external id: check-membership uses: actions/github-script@v7 with: - # Use a GitHub App token or PAT with org:read permissions to check private membership - github-token: ${{ secrets.ORG_MEMBERSHIP_TOKEN || secrets.GITHUB_TOKEN }} + # Use GitHub App token, PAT, or fallback to GITHUB_TOKEN + github-token: ${{ steps.app-token.outputs.token || secrets.ORG_MEMBERSHIP_TOKEN || secrets.GITHUB_TOKEN }} script: | const { owner, repo } = context.repo; const author = context.payload.sender.login;