Skip to content
Merged
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
56 changes: 31 additions & 25 deletions .github/workflows/cla-gate.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
name: CLA Gate

# This workflow makes CLA checks work with merge queue entries. Merge groups get
# a repository-owned `CLA Gate` commit status on the synthetic queue SHA, while
# pull request/status events use this Actions job result based on CLA Assistant's
# raw `license/cla` status.
#
# SECURITY: Pull request runs must still check out trusted base-branch code, not
# PR code, before running repository scripts.
# This workflow publishes a repository-owned commit status named `CLA Gate`.
# Make `CLA Gate` required instead of requiring CLA Assistant's raw `license/cla`
# status directly. That lets merge queue entries pass without waiting for CLA
# Assistant to report on the synthetic merge-group SHA, while pull requests still
# mirror the real CLA Assistant result.

on:
pull_request:
types: [opened, reopened]
status:
merge_group:

permissions:
contents: read
pull-requests: read
statuses: write

jobs:
Expand All @@ -42,11 +37,20 @@ jobs:
const { execFileSync } = require("child_process");

function claStatus(args) {
const output = execFileSync("cargo", ["ci", "cla-assistant", "status", ...args], {
const command = ["ci", "cla-assistant", "status", ...args];
core.info(`Running cargo ${command.join(" ")}`);
const output = execFileSync("cargo", command, {
encoding: "utf8",
env: process.env,
});
return JSON.parse(output);
core.info(`cargo ci output: ${output.trim()}`);
const status = JSON.parse(output);
core.info(
`Resolved license/cla for ${status.sha}: state=${status.state || "missing"} ` +
`description=${status.description || "<none>"} ` +
`target_url=${status.target_url || "<none>"}`
);
return status;
}

async function postStatus({ sha, state, description, targetUrl }) {
Expand All @@ -69,7 +73,12 @@ jobs:
let targetSha;
let claStatusArgs;

core.info(
`Handling ${context.eventName} event for workflow SHA ${process.env.GITHUB_SHA}`
);

if (context.eventName === "merge_group") {
core.info(`Merge group SHA is ${process.env.GITHUB_SHA}`);
await postStatus({
sha: process.env.GITHUB_SHA,
state: "success",
Expand All @@ -81,28 +90,25 @@ jobs:
if (context.eventName === "status") {
targetSha = context.payload.sha;
claStatusArgs = ["--sha", targetSha];
} else if (context.eventName === "pull_request") {
const pr = context.payload.pull_request;
targetSha = pr.head.sha;
claStatusArgs = ["--pr", String(pr.number)];
core.info(
`status event context=${context.payload.context} state=${context.payload.state} sha=${targetSha}`
);
} else {
core.setFailed(`Unsupported event type: ${context.eventName}`);
return;
}

core.info(`Checking license/cla with args: ${claStatusArgs.join(" ")}`);
const status = claStatus(claStatusArgs);

if (!status.state) {
core.info(`No CLA Gate status to publish for ${targetSha}`);
return;
}

if (status.state === "success") {
core.info(`license/cla is success for ${targetSha}`);
return;
}

const state = status.state || "missing";
const description = status.description || "license/cla status is missing";
const targetUrl = status.target_url ? ` (${status.target_url})` : "";
core.setFailed(`license/cla is ${state}: ${description}${targetUrl}`);
await postStatus({
sha: targetSha,
state: status.state,
description: status.description || undefined,
targetUrl: status.target_url || undefined,
});
Loading