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
109 changes: 109 additions & 0 deletions .github/workflows/tag-external-contributions.yml
Original file line number Diff line number Diff line change
@@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scope permissions tp more granular steps?

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