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
137 changes: 137 additions & 0 deletions .github/actions/pr-check/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: PR check
description: >-
Runs one dev/check-<name>.mjs on the lines a pull request adds and reports
its findings on the PR: a summary comment plus a review comment on each
flagged line (dev/check-report.sh). The job must check out the repository
first, with fetch-depth 0; the default ref, the merge commit, is right.

inputs:
script:
description: The check script, e.g. dev/check-links.mjs
required: true
name:
description: >-
Marks the comments the check posts, e.g. check-links; see
dev/check-report.sh
required: true
arguments:
description: Flags for every run of the script
default: ''
head-only-arguments:
description: >-
Flags for the run on the pull request only, not for recording the
baseline on the merge base
default: ''
diff-paths:
description: Paths the diff of the pull request is limited to; all by default
default: ''
baseline:
description: >-
Record the script's findings on the merge base too (--format json
--root) and pass them as --baseline, so only the findings this PR
introduces are reported
default: 'false'
dependency:
description: >-
An npm package the script imports, installed at the version in
package.json without the rest of the site's dependencies
default: ''
fail-on-findings:
description: Fail the job when the script reports findings; advisory otherwise
default: 'false'

outputs:
result:
description: clean, found, or crashed
value: ${{ steps.check.outputs.result }}

runs:
using: composite
steps:
# The workflow and this action are read from the merge commit, so run
# the scripts from there too: a branch that predates a check has no
# script for it, and the merge commit also has this PR's changes to
# them. Everything else is checked as the PR branch has it.
- name: Check out the pull request head, with the check scripts from the merge commit
shell: bash
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
git checkout --quiet "$HEAD_SHA"
git checkout --quiet "$GITHUB_SHA" -- dev

- name: Install ${{ inputs.dependency }}
if: inputs.dependency != ''
shell: bash
env:
DEPENDENCY: ${{ inputs.dependency }}
# Into a scratch prefix, not the repo: `npm install <pkg>` next to
# package.json would install every dependency of the site
run: |
npm install --prefix "$RUNNER_TEMP/deps" --no-package-lock --no-audit --no-fund \
"$DEPENDENCY@$(node -p "require('./package.json').dependencies['$DEPENDENCY']")"
ln -s "$RUNNER_TEMP/deps/node_modules" node_modules

# The script exits 1 both with findings and when it crashes. It writes
# its output in one go at the end, so a crash leaves it empty. A crash
# does not fail the check, since it says nothing about the PR; the
# report step says so instead of posting the empty report.
- name: Run ${{ inputs.script }} on the lines this PR adds
id: check
shell: bash
env:
SCRIPT: ${{ inputs.script }}
ARGUMENTS: ${{ inputs.arguments }}
HEAD_ONLY_ARGUMENTS: ${{ inputs.head-only-arguments }}
DIFF_PATHS: ${{ inputs.diff-paths }}
BASELINE: ${{ inputs.baseline }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
# File links in the report open the file on the PR branch
LINK_BASE: ${{ github.event.pull_request.head.repo.html_url }}/blob/${{ github.event.pull_request.head.ref }}
run: |
# The arguments are lists, so they are left unquoted on purpose
# shellcheck disable=SC2086
merge_base=$(git merge-base "$BASE_SHA" HEAD)
git diff -U0 "$merge_base" HEAD -- $DIFF_PATHS > "$RUNNER_TEMP/changes.diff"

baseline_flag=
if [ "$BASELINE" = true ]; then
git worktree add --quiet "$RUNNER_TEMP/base" "$merge_base"
node "$SCRIPT" $ARGUMENTS --format json --root "$RUNNER_TEMP/base" > "$RUNNER_TEMP/baseline.json" || true
if ! [ -s "$RUNNER_TEMP/baseline.json" ]; then
echo "::warning::$SCRIPT crashed on the base branch, so this PR was not checked"
echo "result=crashed" >> "$GITHUB_OUTPUT"
exit 0
fi
baseline_flag="--baseline $RUNNER_TEMP/baseline.json"
fi

if node "$SCRIPT" $ARGUMENTS $HEAD_ONLY_ARGUMENTS --format markdown $baseline_flag \
--diff "$RUNNER_TEMP/changes.diff" \
--review "$RUNNER_TEMP/review.json" \
--link-base "$LINK_BASE" > "$RUNNER_TEMP/report.md"; then
echo "result=clean" >> "$GITHUB_OUTPUT"
elif [ -s "$RUNNER_TEMP/report.md" ]; then
echo "result=found" >> "$GITHUB_OUTPUT"
else
echo "::warning::$SCRIPT crashed, so this PR was not checked"
echo "result=crashed" >> "$GITHUB_OUTPUT"
fi
cat "$RUNNER_TEMP/report.md"

- name: Report on the pull request
# Fork PRs get a read-only token; the report is still in the job log
if: github.event.pull_request.head.repo.full_name == github.repository
shell: bash
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
NAME: ${{ inputs.name }}
RESULT: ${{ steps.check.outputs.result }}
run: dev/check-report.sh "$NAME" "$RESULT" "$RUNNER_TEMP/report.md" "$RUNNER_TEMP/review.json"

- name: Fail when this PR introduces findings
if: inputs.fail-on-findings == 'true' && steps.check.outputs.result == 'found'
shell: bash
run: exit 1
98 changes: 10 additions & 88 deletions .github/workflows/check-duplicates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ on:
paths:
- 'docs/**'
- 'dev/check-duplicates.mjs'
- 'dev/sync-review-comments.sh'
- 'dev/check-shared.mjs'
- 'dev/check-report.sh'
- '.github/actions/pr-check/action.yml'
- '.github/workflows/check-duplicates.yml'

# A new push supersedes the run for the previous one
Expand All @@ -27,93 +29,13 @@ jobs:
name: Duplicated content introduced by this PR (advisory)
runs-on: ubuntu-latest
steps:
- name: Check out pull request head
uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Check out merge base
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
merge_base=$(git merge-base "$BASE_SHA" HEAD)
git worktree add "$RUNNER_TEMP/base" "$merge_base"
git diff -U0 "$merge_base" HEAD -- docs > "$RUNNER_TEMP/changes.diff"

# The script exits 1 both with findings and when it crashes. It writes
# its output in one go at the end, so a crash leaves it empty. A crash
# does not fail the check, since it says nothing about the PR; the
# comment step reports it instead of posting the empty report.
- name: Record duplicated passages already present on the base branch
run: |
node dev/check-duplicates.mjs --format json \
--root "$RUNNER_TEMP/base" > "$RUNNER_TEMP/base-duplicates.json" || true

- name: Find duplicated passages introduced by this PR
id: check
env:
# File links in the report open the file on the PR branch
LINK_BASE: ${{ github.event.pull_request.head.repo.html_url }}/blob/${{ github.event.pull_request.head.ref }}
run: |
if ! [ -s "$RUNNER_TEMP/base-duplicates.json" ]; then
echo "::warning::check-duplicates crashed on the base branch, so this PR was not checked"
echo "result=crashed" >> "$GITHUB_OUTPUT"
exit 0
fi
if node dev/check-duplicates.mjs --format markdown \
--baseline "$RUNNER_TEMP/base-duplicates.json" \
--diff "$RUNNER_TEMP/changes.diff" \
--review "$RUNNER_TEMP/review.json" \
--link-base "$LINK_BASE" > "$RUNNER_TEMP/report.md"; then
echo "result=clean" >> "$GITHUB_OUTPUT"
elif [ -s "$RUNNER_TEMP/report.md" ]; then
echo "result=found" >> "$GITHUB_OUTPUT"
else
echo "::warning::check-duplicates crashed, so this PR was not checked"
echo "result=crashed" >> "$GITHUB_OUTPUT"
fi
cat "$RUNNER_TEMP/report.md"

- name: Comment on the pull request
# Fork PRs get a read-only token; the report is still in the job log
if: github.event.pull_request.head.repo.full_name == github.repository
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
RESULT: ${{ steps.check.outputs.result }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
marker='<!-- check-duplicates-report -->'
existing_comment=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \
--paginate --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n 1)

# Comment when there is something to report, or an earlier report to resolve
case "$RESULT" in
found)
{ echo "$marker"; cat "$RUNNER_TEMP/report.md"; } > "$RUNNER_TEMP/comment.md" ;;
crashed)
printf '%s\n### ⚠️ The duplicated content check could not run on this revision\n\nThis is a problem with the check, not with this PR; see the [job log](%s).\n' \
"$marker" "$RUN_URL" > "$RUNNER_TEMP/comment.md" ;;
*)
[ -n "$existing_comment" ] || exit 0
{ echo "$marker"; cat "$RUNNER_TEMP/report.md"; } > "$RUNNER_TEMP/comment.md" ;;
esac

if [ -n "$existing_comment" ]; then
gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing_comment" \
--field body=@"$RUNNER_TEMP/comment.md"
else
gh pr comment "$PR_NUMBER" --body-file "$RUNNER_TEMP/comment.md"
fi

- name: Point out the duplicates as review comments
# One comment per added passage, kept in sync with the findings;
# see dev/sync-review-comments.sh
if: >-
github.event.pull_request.head.repo.full_name == github.repository
&& contains(fromJSON('["clean", "found"]'), steps.check.outputs.result)
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: dev/sync-review-comments.sh '<!-- check-duplicates-finding:' "$RUNNER_TEMP/review.json"
- uses: ./.github/actions/pr-check
with:
script: dev/check-duplicates.mjs
name: check-duplicates
diff-paths: docs
baseline: true
81 changes: 10 additions & 71 deletions .github/workflows/check-hostnames.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ on:
paths:
- 'docs/**'
- 'dev/check-hostnames.mjs'
- 'dev/check-shared.mjs'
- 'dev/example-hostnames.json'
- 'dev/sync-review-comments.sh'
- 'dev/check-report.sh'
- '.github/actions/pr-check/action.yml'
- '.github/workflows/check-hostnames.yml'

# A new push supersedes the run for the previous one
Expand All @@ -24,78 +26,15 @@ permissions:

jobs:
check-hostnames:
name: Check example hostnames
name: Placeholder hostnames introduced by this PR (advisory)
runs-on: ubuntu-latest
steps:
- name: Check out pull request head
uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

- name: Find placeholder hostnames added by this PR
id: check
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
# File links in the report open the file on the PR branch
LINK_BASE: ${{ github.event.pull_request.head.repo.html_url }}/blob/${{ github.event.pull_request.head.ref }}
# The script exits 1 both with findings and when it crashes. It writes
# its output in one go at the end, so a crash leaves it empty. The
# comment step reports a crash instead of posting the empty report.
run: |
git diff -U0 "$(git merge-base "$BASE_SHA" HEAD)" HEAD -- docs > "$RUNNER_TEMP/changes.diff"
if node dev/check-hostnames.mjs --format markdown \
--diff "$RUNNER_TEMP/changes.diff" \
--review "$RUNNER_TEMP/review.json" \
--link-base "$LINK_BASE" > "$RUNNER_TEMP/report.md"; then
echo "result=clean" >> "$GITHUB_OUTPUT"
elif [ -s "$RUNNER_TEMP/report.md" ]; then
echo "result=found" >> "$GITHUB_OUTPUT"
else
echo "::warning::check-hostnames crashed, so this PR was not checked"
echo "result=crashed" >> "$GITHUB_OUTPUT"
fi
cat "$RUNNER_TEMP/report.md"

- name: Comment on the pull request
# Fork PRs get a read-only token; the report is still in the job log
if: github.event.pull_request.head.repo.full_name == github.repository
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
RESULT: ${{ steps.check.outputs.result }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
marker='<!-- check-hostnames-report -->'
existing_comment=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments" \
--paginate --jq ".[] | select(.body | startswith(\"$marker\")) | .id" | head -n 1)

# Comment when there is something to report, or an earlier report to resolve
case "$RESULT" in
found)
{ echo "$marker"; cat "$RUNNER_TEMP/report.md"; } > "$RUNNER_TEMP/comment.md" ;;
crashed)
printf '%s\n### ⚠️ The example hostnames check could not run on this revision\n\nThis is a problem with the check, not with this PR; see the [job log](%s).\n' \
"$marker" "$RUN_URL" > "$RUNNER_TEMP/comment.md" ;;
*)
[ -n "$existing_comment" ] || exit 0
{ echo "$marker"; cat "$RUNNER_TEMP/report.md"; } > "$RUNNER_TEMP/comment.md" ;;
esac

if [ -n "$existing_comment" ]; then
gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$existing_comment" \
--field body=@"$RUNNER_TEMP/comment.md"
else
gh pr comment "$PR_NUMBER" --body-file "$RUNNER_TEMP/comment.md"
fi

- name: Suggest fixes as review comments
# One suggested change per flagged line, kept in sync with the
# findings; see dev/sync-review-comments.sh
if: >-
github.event.pull_request.head.repo.full_name == github.repository
&& contains(fromJSON('["clean", "found"]'), steps.check.outputs.result)
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: dev/sync-review-comments.sh '<!-- check-hostnames-finding:' "$RUNNER_TEMP/review.json"
- uses: ./.github/actions/pr-check
with:
script: dev/check-hostnames.mjs
name: check-hostnames
diff-paths: docs
Loading
Loading