Webhook icon
Schedule icon
Request icon
WorkingDirectory icon
Commands icon
Script icon
If icon
Create icon
SlackIncomingWebhook icon
Log icon

Block unsigned contributors before their pull requests merge

Block pull requests from authors who have not signed the CLA. When a pull request opens, check each commit author, then file a signature issue and ping Slack.

Categories
Infrastructure

Compliance checks only help when they run before merge, and they never run when they depend on someone remembering to run them. This flow fires the moment a pull request opens, reads the commit list from the GitHub API, reduces it to distinct author identities and checks each one against a signed CLA roster plus DCO Signed-off-by trailers. Anyone who still owes a signature gets a GitHub issue that names them and a Slack ping to the maintainers, while a clean pull request passes with a verdict you can gate on.

How it works

  1. pull_request_opened (Webhook) receives the pull request opened event. nightly_resweep is a disabled Schedule kept as a fallback for pull requests that need a morning re-check.
  2. fetch_commits (io.kestra.plugin.core.http.Request) calls GET /repos/{repo}/pulls/{n}/commits with the GITHUB_TOKEN secret and retries on transient failures.
  3. verify_authors (io.kestra.plugin.core.flow.WorkingDirectory) gives the roster step and the check one shared directory. Inside it, write_cla_roster (io.kestra.plugin.scripts.shell.Commands) picks the roster by author type, human or bot, in one runnable step.
  4. The roster step writes cla-signers.txt with a heredoc, one login per line, and check_cla (io.kestra.plugin.scripts.python.Script) reads it next to the commit payload. A login on the roster or a matching DCO Signed-off-by trailer counts as signed, and the task prints one ::{"outputs": ...}:: line carrying verdict, unsigned and checked.
  5. cla_gate (io.kestra.plugin.core.flow.If) fires when unsigned is non-empty: file_signature_request (io.kestra.plugin.github.issues.Create) opens the signature request issue and ping_maintainers posts to Slack. When the list is empty, log_all_clear records the pass.

What you get

  • verdict (STRING): BLOCK when at least one author is missing, PASS when every checked author is on file or has signed off.
  • checked_authors (INT): distinct author logins or emails inspected on the pull request.
  • unsigned (ARRAY): the identities that still owe a signature, empty on a clean run.
  • pr_number (INT): the pull request the verdict belongs to, handy when several runs are in flight.

Who it's for

  • Maintainers of repositories that require a contributor license agreement or a DCO sign-off from every author.
  • Platform teams that want the compliance check on the opening event instead of a manual reviewer checklist.

Why orchestrate this with Kestra

  • The check is a chain of steps with a branch in the middle, and Kestra keeps the roster branch, the API retries and the escalation in one versioned file.
  • Failures escalate through the errors handler to Slack with the execution URL, so an expired token or an unreachable API is never silent.
  • Outputs are typed and machine readable, so a merge queue can gate on verdict instead of scraping logs.

Prerequisites

  • A Kestra instance with the GitHub and Slack plugins installed.
  • A pull request to inspect and a roster of signed logins to compare against.

Secrets

  • GITHUB_TOKEN: GitHub token that can read pull request commits and create issues.
  • SLACK_WEBHOOK_URL: Slack incoming webhook used for the signature request ping and for failure alerts.
  • CLA_WEBHOOK_KEY: key the webhook trigger expects from the relay that posts the event.

Quick start

  1. Add the three secrets and import the flow into your namespace.
  2. Point the pull request opened webhook at the trigger URL, or execute manually with repo, pr_number and author_login.
  3. Read verdict, checked_authors and unsigned in the outputs while the issue and Slack tasks do the chasing.

How to extend

  • Replace the heredoc roster with a roster fetched from your identity provider, or with a query task against your HR table.
  • Add another branch to the roster writer for service accounts and route it to its own waiver file.
  • Post the unsigned list to a different channel per repository by templating the Slack payload with inputs.repo.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.