diff --git a/.github/scripts/check-pr-permissions.js b/.github/scripts/check-pr-permissions.js deleted file mode 100644 index 24b6e9df37..0000000000 --- a/.github/scripts/check-pr-permissions.js +++ /dev/null @@ -1,206 +0,0 @@ -// Check PR author permissions for pull_request_target events. -// Used by the e2e workflow to gate CI runs on external PRs. -// -// Permission check order: -// 1. Trusted bots (dependabot, renovate) -// 2. Org/team membership -// 3. Repository collaborator (write/admin) -// 4. ok-to-test label (maintainer approval for external contributors) -// -// Security: on non-labeled events the ok-to-test label is removed -// to force re-approval after code changes. -module.exports = async ({ github, context, core }) => { - if (!context || !context.payload || !context.payload.pull_request) { - core.setFailed( - "Invalid GitHub context: missing required pull_request information", - ); - return; - } - - const actor = context.payload.pull_request.user.login; - const repoOwner = context.repo.owner; - const repoName = context.repo.repo; - const targetOrg = context.repo.owner; - - core.info(`šŸ” Starting permission check for user: @${actor}`); - core.info(`šŸ“‹ Repository: ${repoOwner}/${repoName}`); - core.info(`šŸ¢ Target organization: ${targetOrg}`); - - // Condition 1: Check if the user is a trusted bot. - const trustedBots = ["dependabot[bot]", "renovate[bot]"]; - core.info(`šŸ¤– Checking if @${actor} is a trusted bot...`); - core.info(` Trusted bots list: ${trustedBots.join(", ")}`); - - if (trustedBots.includes(actor)) { - core.info( - `āœ… Condition met: User @${actor} is a trusted bot. Proceeding.`, - ); - return; - } - core.info(` āŒ User @${actor} is not a trusted bot.`); - - // Condition 2: Check for public membership in the target organization. - core.info(`\nšŸ‘„ Condition 2: Checking organization and team membership...`); - core.info( - `User @${actor} is not a trusted bot. Checking for membership in '${targetOrg}'...`, - ); - try { - // Optional: check membership in one or more org teams (set TARGET_TEAM_SLUGS as comma-separated slugs in workflow env) - const teamSlugsEnv = process.env.TARGET_TEAM_SLUGS || ""; - const teamSlugs = teamSlugsEnv - .split(",") - .map((s) => s.trim()) - .filter(Boolean); - - core.info(`šŸ”§ TARGET_TEAM_SLUGS environment variable: "${teamSlugsEnv}"`); - core.info(`šŸ“ Parsed team slugs: [${teamSlugs.join(", ")}]`); - - if (teamSlugs.length > 0) { - core.info( - `šŸ” Checking team membership for ${teamSlugs.length} team(s)...`, - ); - for (const team_slug of teamSlugs) { - core.info(` Checking team: ${team_slug}...`); - try { - const membership = - await github.rest.teams.getMembershipForUserInOrg({ - org: targetOrg, - team_slug, - username: actor, - }); - core.info( - ` API response for team '${team_slug}': ${JSON.stringify(membership.data)}`, - ); - if ( - membership && - membership.data && - membership.data.state === "active" && - (membership.data.role === "maintainer" || membership.data.role === "member") - ) { - core.info( - `āœ… Condition met: User @${actor} is a member of team '${team_slug}' in '${targetOrg}'. Proceeding.`, - ); - return; - } else { - core.info( - ` āš ļø Team membership found but state is not 'active': ${membership.data.state}`, - ); - } - } catch (err) { - core.info( - ` āŒ User @${actor} is not a member of team '${team_slug}' (or team not found). Error: ${err.message}`, - ); - } - } - core.info( - `ā“˜ User @${actor} is not a member of any configured teams in '${targetOrg}'. Falling back to org membership checks.`, - ); - } else { - core.info( - `ā„¹ļø No teams configured in TARGET_TEAM_SLUGS. Skipping team membership checks.`, - ); - } - core.info( - `šŸ¢ Checking organization membership for @${actor} in '${targetOrg}'...`, - ); - try { - core.info(` Attempting checkMembershipForUser API call...`); - await github.rest.orgs.checkMembershipForUser({ - org: targetOrg, - username: actor, - }); - core.info( - `āœ… Condition met: User @${actor} is a member of '${targetOrg}'. Proceeding.`, - ); - return; - } catch (err) { - core.info(` āŒ Private membership check failed: ${err.message}`); - core.info(` Attempting checkPublicMembershipForUser API call...`); - try { - await github.rest.orgs.checkPublicMembershipForUser({ - org: targetOrg, - username: actor, - }); - core.info( - `āœ… Condition met: User @${actor} is a public member of '${targetOrg}'. Proceeding.`, - ); - return; - } catch (publicErr) { - core.info( - ` āŒ Public membership check failed: ${publicErr.message}`, - ); - throw publicErr; - } - } - } catch (error) { - core.info( - `ā“˜ User @${actor} is not a public member of '${targetOrg}'. Checking repository permissions as a fallback.`, - ); - } - - // Condition 3: Check for write/admin permission on the repository. - core.info( - `\nšŸ” Condition 3: Checking repository collaborator permissions...`, - ); - try { - core.info(` Attempting getCollaboratorPermissionLevel API call...`); - const response = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: repoOwner, - repo: repoName, - username: actor, - }); - - const permission = response.data.permission; - core.info( - ` User @${actor} has '${permission}' permission on ${repoOwner}/${repoName}`, - ); - - if (permission === "admin" || permission === "write") { - core.info( - `āœ… Condition met: User @${actor} has '${permission}' repository permission. Proceeding.`, - ); - return; - } else { - core.info( - ` āŒ Permission '${permission}' is insufficient (requires 'write' or 'admin')`, - ); - } - } catch (error) { - core.info(` āŒ Collaborator permission check failed: ${error.message}`); - } - - // Condition 4: Check for ok-to-test label (for external contributors approved by maintainers). - // Only users with repo write access can add labels, so this is inherently gated. - core.info(`\nšŸ·ļø Condition 4: Checking for ok-to-test label...`); - if ( - context.payload.action === "labeled" && - context.payload.label && - context.payload.label.name === "ok-to-test" - ) { - core.info( - `āœ… Condition met: ok-to-test label applied by @${context.actor}. Removing label and proceeding with tests.`, - ); - try { - await github.rest.issues.removeLabel({ - owner: repoOwner, - repo: repoName, - issue_number: context.payload.pull_request.number, - name: "ok-to-test", - }); - core.info(` ok-to-test label removed successfully.`); - } catch (err) { - // 404 is expected when multiple matrix jobs race to remove the same label - if (err.status !== 404) { - core.setFailed(` Failed to remove ok-to-test label: ${err.message}`); - return; - } - core.info(` Label already removed (likely by another matrix job).`); - } - return; - } - - core.info(` āŒ No ok-to-test label event detected.`); - core.setFailed( - `āŒ Permission check failed. User @${actor} did not meet any required conditions (trusted bot, org/team member, repo write access, or ok-to-test label).`, - ); -}; diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 0eff3df825..a2ba81a091 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -25,7 +25,6 @@ on: - ".github/workflows/**" - "test/testdata/**" - "vendor/**" - - ".github/scripts/**" jobs: e2e-tests: @@ -127,15 +126,11 @@ jobs: with: ref: ${{ inputs.target_ref || github.event.pull_request.head.sha || github.sha }} - # Step to check PR author's org membership and repo permissions. - # This step will fail the job if checks do not pass, skipping subsequent steps. - name: Check user permissions on PRs if: github.event_name == 'pull_request_target' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: pipelines-as-code/ok-to-test@v1 with: - script: | - const script = require('./.github/scripts/check-pr-permissions.js') - await script({github, context, core}) + team-slugs: ${{ env.TARGET_TEAM_SLUGS }} - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6 with: diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index 5d65a6bed9..085f170bc4 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -30,6 +30,7 @@ import ( "knative.dev/pkg/system" ) + const globalAdapterPort = "8082" // For incoming webhook requests and GitHub Apps with many installations the handler takes long