Skip to content
Merged
Show file tree
Hide file tree
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
111 changes: 111 additions & 0 deletions .github/workflows/demo-command.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Demo command

# Minimal example of a PR comment command built on comment-triggered-check.
# Copy this file and demo-suite.yaml to add a command to another repository.
#
# /demo-e2e ok the check passes
# /demo-e2e fail the check fails
# /demo-e2e nothing the check ends as "Nothing to run"
#
# issue_comment runs this file from the default branch, with secrets, for every
# comment. So this workflow never checks out PR code. It only parses the
# command, opens a check on the PR's head commit and starts demo-suite.yaml.

on:
issue_comment:
types: [created]

permissions: {}

jobs:
prepare:
name: Parse command
# Cheap pre-filter; the action does the real parsing.
if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/demo-e2e') }}
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
outputs:
should-run: ${{ steps.command.outputs.should-run }}
filter: ${{ steps.command.outputs.filter }}
is-fork: ${{ steps.command.outputs.is-fork }}
head-ref: ${{ steps.command.outputs.head-ref }}
base-ref: ${{ steps.command.outputs.base-ref }}
head-sha: ${{ steps.command.outputs.head-sha }}
key: ${{ steps.command.outputs.concurrency-key }}
check-run-id: ${{ steps.command.outputs.check-run-id }}
steps:
# Parses the comment, checks the author, finds the head commit and opens
# the check on it.
- name: Parse command and open check
id: command
uses: loft-sh/github-actions/.github/actions/comment-triggered-check@comment-triggered-check/v1
with:
mode: start
command: /demo-e2e
check-name-prefix: demo-e2e
finish-workflow: demo-suite.yaml
comment-body: ${{ github.event.comment.body }}
comment-author: ${{ github.event.comment.user.login }}
author-association: ${{ github.event.comment.author_association }}
pr-number: ${{ github.event.issue.number }}
run-id: ${{ github.run_id }}
server-url: ${{ github.server_url }}
# Fork PRs: safe only because demo-suite.yaml checks out head-sha
# and only reads the cache.
# allow-forks: true
# Give the check its own group on the PR (also pass to finish).
# app-client-id: ${{ vars.CI_APP_CLIENT_ID }}
# app-private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}

# A typo or a missing permission gets a reply instead of a run.
- name: Explain refusal
if: ${{ steps.command.outputs.matched == 'true' && steps.command.outputs.should-run != 'true' && steps.command.outputs.reason-title != '' }}
uses: loft-sh/github-actions/.github/actions/sticky-pr-comment@sticky-pr-comment/v1
with:
pr-number: ${{ github.event.issue.number }}
marker: '<!-- demo-e2e-refusal -->'
body: |
**${{ steps.command.outputs.reason-title }}**

${{ steps.command.outputs.reason-guidance }}
github-token: ${{ secrets.GITHUB_TOKEN }}

dispatch:
name: Start suite
needs: [prepare]
if: ${{ needs.prepare.outputs.should-run == 'true' }}
runs-on: ubuntu-latest
permissions:
actions: write
checks: write
steps:
# Same-repo PRs run their own branch's suite. Forks run the target
# branch's suite, because a fork's workflow files are not trusted.
- name: Dispatch demo-suite.yaml
id: dispatch
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
WORKFLOW_REF: ${{ needs.prepare.outputs.is-fork == 'true' && needs.prepare.outputs.base-ref || needs.prepare.outputs.head-ref }}
FILTER: ${{ needs.prepare.outputs.filter }}
HEAD_SHA: ${{ needs.prepare.outputs.head-sha }}
KEY: ${{ needs.prepare.outputs.key }}
CHECK_RUN_ID: ${{ needs.prepare.outputs.check-run-id }}
run: |
gh workflow run demo-suite.yaml --ref "$WORKFLOW_REF" \
-f "filter=$FILTER" -f "head-sha=$HEAD_SHA" \
-f "concurrency-key=$KEY" -f "check-run-id=$CHECK_RUN_ID"

# Never leave a check spinning: if the suite could not start, close it.
- name: Close the check if the suite did not start
if: ${{ steps.dispatch.outcome == 'failure' }}
uses: loft-sh/github-actions/.github/actions/comment-triggered-check@comment-triggered-check/v1
with:
mode: finish
check-run-id: ${{ needs.prepare.outputs.check-run-id }}
suite-result: failure
summary: Could not start demo-suite.yaml. Update the branch from its target and comment again.
details-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
94 changes: 94 additions & 0 deletions .github/workflows/demo-suite.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Demo suite

# The non-privileged half of demo-command.yaml. It runs the requested tests
# against the exact commit the check was opened on, then closes the check.
run-name: "demo-e2e: ${{ inputs.filter }}"

on:
workflow_dispatch:
inputs:
filter:
description: Filter from the comment.
required: true
type: string
head-sha:
description: Commit the check was opened on.
required: true
type: string
concurrency-key:
description: Request identity from the action.
required: true
type: string
check-run-id:
description: Check to close when the suite ends.
required: true
type: string

permissions: {}

# A fork run can restore caches but never save them.
cache-mode: read

jobs:
suite:
name: Run suite
runs-on: ubuntu-latest
permissions:
contents: read
# The same request again on the same PR replaces the older run.
concurrency:
group: demo-suite-${{ inputs.concurrency-key }}
cancel-in-progress: true
outputs:
check-conclusion: ${{ steps.result.outputs.check-conclusion }}
steps:
# The workflow comes from a trusted branch; the code comes from head-sha.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.head-sha }}
persist-credentials: false

- name: Verify the requested commit
env:
EXPECTED_HEAD_SHA: ${{ inputs.head-sha }}
run: |
ACTUAL_HEAD_SHA="$(git rev-parse HEAD)"
if [[ "$EXPECTED_HEAD_SHA" != "$ACTUAL_HEAD_SHA" ]]; then
echo "::error::expected ${EXPECTED_HEAD_SHA}, checked out ${ACTUAL_HEAD_SHA}. Comment again."
exit 1
fi
echo "testing ${ACTUAL_HEAD_SHA}"

# Stand-in for the real tests (Ginkgo, go test, ...). It must report one
# of success, failure, neutral or timed_out.
- name: Run tests
id: result
env:
FILTER: ${{ inputs.filter }}
run: |
echo "running tests matching: ${FILTER}"
sleep 20
case "$FILTER" in
*fail*) echo "check-conclusion=failure" >> "$GITHUB_OUTPUT" ;;
*nothing*) echo "check-conclusion=neutral" >> "$GITHUB_OUTPUT" ;;
*) echo "check-conclusion=success" >> "$GITHUB_OUTPUT" ;;
esac

# Runs even when the suite fails or is cancelled, so the check always closes.
finish:
name: Close check
needs: [suite]
if: ${{ always() && inputs.check-run-id != '' }}
runs-on: ubuntu-latest
permissions:
checks: write
steps:
- uses: loft-sh/github-actions/.github/actions/comment-triggered-check@comment-triggered-check/v1
with:
mode: finish
check-run-id: ${{ inputs.check-run-id }}
report-conclusion: ${{ needs.suite.result != 'cancelled' && needs.suite.outputs.check-conclusion || '' }}
suite-result: ${{ needs.suite.result }}
details-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
# app-client-id: ${{ vars.CI_APP_CLIENT_ID }}
# app-private-key: ${{ secrets.CI_APP_PRIVATE_KEY }}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ This repo contains caller workflows that exercise each reusable workflow from
| `caller-secret-broker-request.yaml` | creates and queues a short-lived broker request |
| `caller-secret-broker-response.yaml` | `secret-broker-request` action |

## Demo command

`demo-command.yaml` and `demo-suite.yaml` are a minimal, commented example of a
PR comment command built on `comment-triggered-check`. Copy both to add a
command to another repository. Comment on any PR here:

| Comment | Check result |
|---------|--------------|
| `/demo-e2e ok` | passes |
| `/demo-e2e fail` | fails |
| `/demo-e2e nothing` | "Nothing to run" |
| `/demo-e2e (ok` | no check; a reply explains the unbalanced parenthesis |

## Integration test orchestrator

`integration-tests.yaml` runs all E2E tests in parallel, creating real PRs,
Expand Down
Loading