name: CLA Assistant on: issue_comment: types: [created] pull_request_target: types: [opened, synchronize] permissions: actions: write contents: write pull-requests: write statuses: write jobs: cla: runs-on: ubuntu-latest steps: - name: Check stored CLA signatures id: cla_state if: github.event_name == 'pull_request_target' uses: actions/github-script@v7 with: script: | const signaturePath = "signatures/cla.json"; const signatureBranch = "dev"; const allowlist = [ "code-yeongyu", "bot*", "dependabot*", "github-actions*", "*[bot]", "sisyphus-dev-ai", "web-flow", ]; const matchesAllowlist = (login) => { const normalizedLogin = login.toLowerCase(); return allowlist.some((entry) => { const pattern = entry.toLowerCase(); if (pattern.startsWith("*")) { return normalizedLogin.endsWith(pattern.slice(1)); } if (pattern.endsWith("*")) { return normalizedLogin.startsWith(pattern.slice(0, -1)); } return normalizedLogin === pattern; }); }; const { owner, repo } = context.repo; const pullNumber = context.payload.pull_request.number; const [signatureFile, commits] = await Promise.all([ github.rest.repos.getContent({ owner, repo, path: signaturePath, ref: signatureBranch, }), github.paginate(github.rest.pulls.listCommits, { owner, repo, pull_number: pullNumber, per_page: 100, }), ]); if (Array.isArray(signatureFile.data) || signatureFile.data.type !== "file") { core.setFailed(`${signaturePath} is not a file on ${signatureBranch}`); return; } const signatureContent = Buffer.from( signatureFile.data.content, signatureFile.data.encoding, ).toString("utf8"); const signatures = JSON.parse(signatureContent); const signedContributors = Array.isArray(signatures.signedContributors) ? signatures.signedContributors : []; const signedIds = new Set( signedContributors .map((contributor) => Number(contributor.id)) .filter((id) => Number.isFinite(id)), ); const signedNames = new Set( signedContributors .map((contributor) => String(contributor.name || "").toLowerCase()) .filter(Boolean), ); const contributors = new Map(); for (const commit of commits) { if (commit.author?.login && commit.author?.id) { contributors.set(commit.author.login, { id: commit.author.id, login: commit.author.login, }); } } const unsigned = [...contributors.values()].filter((contributor) => { const login = contributor.login.toLowerCase(); return ( !matchesAllowlist(contributor.login) && !signedIds.has(contributor.id) && !signedNames.has(login) ); }); core.setOutput("needs_cla_action", unsigned.length > 0 ? "true" : "false"); if (unsigned.length > 0) { core.info( `CLA Assistant needed for unsigned contributors: ${unsigned .map((contributor) => contributor.login) .join(", ")}`, ); } else { core.info("All linked commit authors already signed the CLA or are allowlisted."); } - name: CLA Assistant if: >- (github.event_name == 'issue_comment' && (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA')) || steps.cla_state.outputs.needs_cla_action == 'true' uses: contributor-assistant/github-action@v2.6.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: path-to-signatures: 'signatures/cla.json' path-to-document: 'https://github.com/code-yeongyu/oh-my-openagent/blob/dev/CLA.md' branch: 'dev' allowlist: code-yeongyu,bot*,dependabot*,github-actions*,*[bot],sisyphus-dev-ai,web-flow custom-notsigned-prcomment: | Thank you for your contribution! Before we can merge this PR, we need you to sign our [Contributor License Agreement (CLA)](https://github.com/code-yeongyu/oh-my-opencode/blob/master/CLA.md). **To sign the CLA**, please comment on this PR with: ``` I have read the CLA Document and I hereby sign the CLA ``` This is a one-time requirement. Once signed, all your future contributions will be automatically accepted. custom-pr-sign-comment: 'I have read the CLA Document and I hereby sign the CLA' custom-allsigned-prcomment: | All contributors have signed the CLA. Thank you! ✅ lock-pullrequest-aftermerge: false