Repository navigation
115 lines (108 loc) · 4.67 KB
/
Copy pathdocs-dispatch.yml
File metadata and controls
115 lines (108 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
name: Docs dispatch
# Fires on push to main when content that affects the published docs site
# changes (docs/, README, or CHANGELOG). Notifies makegov/docs via
# repository_dispatch so the docs site rebuilds without waiting for someone
# to push to the composer.
#
# tango-node is a `coloc-source` repo: its docs/ folder is the authoritative
# source for the Node SDK pages on docs.makegov.com (see makegov/docs#15).
#
# Required org settings for the makegov-dispatchbot App:
# DISPATCHBOT_CLIENT_ID — org variable: the App's Client ID, visible to all repos.
# DISPATCHBOT_PRIVATE_KEY — org secret: its private key. The secret has `visibility: selected`, so this repo has to be on its selected list or every dispatch fails auth.
#
# The App mints a short-lived installation token scoped to the one repo this dispatches to (makegov/docs unless DOCS_TARGET_REPO overrides it).
#
# Required variables (optional):
# DOCS_TARGET_REPO — override the dispatch target (default: makegov/docs).
on:
push:
branches:
- main
paths:
- "docs/**"
- "README.md"
- "CHANGELOG.md"
workflow_dispatch:
jobs:
dispatch:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
# Every `${{ }}` value reaches the scripts through `env:`, never inline, so a file name or event field cannot inject shell.
- name: Detect changed paths
id: changes
env:
EVENT_NAME: ${{ github.event_name }}
BEFORE: ${{ github.event.before }}
run: |
set -euo pipefail
# The empty-tree object: diffing against it lists every tracked file.
empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
# Without a usable `before` (a manual run, a new branch, or a force-push whose old tip is gone) the push range is unknown, so dispatch regardless and report the tip commit's paths.
force=false
if [ "$EVENT_NAME" = "push" ] && [ -n "$BEFORE" ] && ! echo "$BEFORE" | grep -qx '0*' && git cat-file -e "$BEFORE^{commit}" 2>/dev/null; then
base="$BEFORE"
else
force=true
if git rev-parse --verify -q HEAD~1 >/dev/null; then
base=HEAD~1
else
base="$empty_tree"
fi
fi
external=$(git diff --name-only "$base" HEAD -- 'docs' 'README.md' 'CHANGELOG.md' | paste -sd, -)
if [ -n "$external" ] || [ "$force" = true ]; then
has_external=true
else
has_external=false
fi
{
echo "external=$external"
echo "has_external=$has_external"
} >> "$GITHUB_OUTPUT"
- name: Resolve the docs composer target
if: steps.changes.outputs.has_external == 'true'
id: docs_target
env:
TARGET: ${{ vars.DOCS_TARGET_REPO || 'makegov/docs' }}
run: |
set -euo pipefail
# The token below is scoped to whatever repo the dispatch hits, so a `DOCS_TARGET_REPO` override still authenticates.
if ! [[ "$TARGET" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then
echo "::error::DOCS_TARGET_REPO must be owner/repo, got '$TARGET'"
exit 1
fi
{
echo "target=$TARGET"
echo "owner=${TARGET%%/*}"
echo "repo=${TARGET#*/}"
} >> "$GITHUB_OUTPUT"
- name: Mint an installation token for the docs composer
if: steps.changes.outputs.has_external == 'true'
id: docs_composer
uses: actions/create-github-app-token@v3
with:
client-id: ${{ vars.DISPATCHBOT_CLIENT_ID }}
private-key: ${{ secrets.DISPATCHBOT_PRIVATE_KEY }}
owner: ${{ steps.docs_target.outputs.owner }}
# Scoped to the single repo this step dispatches to, so a leaked token can't reach the rest of the org. A repository_dispatch needs Contents:write on the receiving repo.
repositories: ${{ steps.docs_target.outputs.repo }}
- name: Dispatch to docs composer (makegov/docs)
if: steps.changes.outputs.has_external == 'true'
env:
GH_TOKEN: ${{ steps.docs_composer.outputs.token }}
TARGET: ${{ steps.docs_target.outputs.target }}
SOURCE_REPO: ${{ github.repository }}
SOURCE_REF: ${{ github.sha }}
CHANGED_PATHS: ${{ steps.changes.outputs.external }}
run: |
gh api "repos/$TARGET/dispatches" \
-f event_type=external_updated \
-f "client_payload[source_repo]=$SOURCE_REPO" \
-f "client_payload[source_ref]=$SOURCE_REF" \
-f "client_payload[changed_paths]=$CHANGED_PATHS"