Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .github/workflows/tag-external-contributions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Automatically tag issues and pull requests as "external"
# when created by users who are not members of the langchain-ai
# GitHub organization.
#
# Setup Requirements:
# 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
#
# 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 no secrets are 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

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 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;

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}`);