-
Notifications
You must be signed in to change notification settings - Fork 843
143 lines (139 loc) · 7.51 KB
/
Copy pathnotify-tutorials-ims.yml
File metadata and controls
143 lines (139 loc) · 7.51 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Tutorial Repo Dispatch Workflow
#
# Copy this file to .github/workflows/notify-tutorials-ims.yml in each
# sap-tutorials tutorial *source* repo that should trigger a content rebuild.
#
# Auth (#1154): prefers a GitHub App installation token, falls back to a PAT.
# - App path (recommended): set repo variable USE_GITHUB_APP=true and add
# Actions secrets TUTORIALS_APP_ID + TUTORIALS_APP_PRIVATE_KEY. The App
# (sap-tutorials-builder) must be installed on this repo and hold
# Contents: write on sap-tutorials/tutorials-ims (repository_dispatch).
# See docs/developers/operations/github-app-setup.md.
# - PAT fallback (legacy): TUTORIALS_DISPATCH_TOKEN — GitHub PAT with `repo`
# scope on tutorials-ims. Used while USE_GITHUB_APP is unset/false.
#
# NOTE: the || fallback only engages if App-token GENERATION fails — NOT if
# the dispatch itself is unauthorized. Grant the App Contents:write on
# tutorials-ims BEFORE setting USE_GITHUB_APP=true here.
#
# Target env (#1154): this template sends client_payload.environment (default
# 'prod', override per-repo via the REBUILD_ENVIRONMENT repo variable).
# rebuild-content.yml honors that field (allowlist dev|qa|prod) as of PR #1273.
# BOTH must be merged before rolling this out to production source repos — on
# a rebuild-content.yml that predates #1273, repository_dispatch still routes
# to DEV regardless of payload, so every tutorial push would rebuild DEV.
name: Notify Tutorials Platform
on:
push:
# Source repos are a MIXTURE of default branches: most default to `main`,
# but some (e.g. sap-tutorials/Tutorials) still default to `master`. List
# BOTH so this template auto-publishes regardless of a repo's default —
# a `main`-only trigger silently never fires on a `master`-default repo
# (root cause of the cp-* PROD content-drift incidents). GitHub ignores a
# listed branch that doesn't exist, so [master, main] is safe everywhere.
branches: [master, main]
paths:
- '**.md'
- '**.png'
- '**.jpg'
- '**.gif'
jobs:
notify:
runs-on: ubuntu-latest
steps:
# Determine the single changed tutorial slug (if exactly one), so the
# rebuild can run in fast slug-targeted mode (~2 min) instead of a full
# rebuild (~10 min). Mirrors notify-qa.yml.template. Needs full history
# (fetch-depth: 0) for the before..after diff. Empty slug (0 or >1
# tutorials touched) → rebuild-content.yml falls back to a full rebuild.
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Determine changed slug
id: slug
run: |
BEFORE="${{ github.event.before }}"
SHA="${{ github.sha }}"
# Slugs whose tutorials/<slug>/ directory was ENTIRELY removed in this
# push (#2349). --diff-filter=D lists deleted paths; a slug counts as
# deleted only if NO file under tutorials/<slug>/ survives in the after
# tree (guards a partial delete, e.g. removing one screenshot). These
# route to the unpublish lane in rebuild-content.yml, NOT the fetch lane
# (fetch-tutorials hard-fails on a slug missing from upstream discovery).
deleted_raw=$(git diff --name-only --diff-filter=D "$BEFORE" "$SHA" \
| awk -F/ '/^tutorials\//{print $2}' | sort -u)
surviving=$(git ls-tree -r --name-only "$SHA" \
| awk -F/ '/^tutorials\//{print $2}' | sort -u)
deleted=""
for cand in $deleted_raw; do
# Keep only candidates with no surviving file under tutorials/<cand>/.
if ! echo "$surviving" | grep -qx "$cand"; then
case "$cand" in
*[!a-z0-9-]* | -* ) : ;; # non-slug charset → skip
*) deleted="${deleted:+$deleted,}$cand" ;;
esac
fi
done
echo "deleted_slugs=$deleted" >> "$GITHUB_OUTPUT"
# Changed (added/modified) tutorial slugs, EXCLUDING deletions — a lone
# deletion must not emit slug=<deleted> and trip the fetch hard-fail.
changed=$(git diff --name-only --diff-filter=d "$BEFORE" "$SHA" \
| awk -F/ '/^tutorials\//{print $2}' | sort -u)
count=$(echo "$changed" | grep -c . || true)
# Only emit a slug when EXACTLY one tutorial changed AND it matches the
# strict slug charset (whole-value POSIX case match). Anything else →
# empty slug → rebuild-content.yml runs a full rebuild. Validating here
# (sender) too — not just the receiver — keeps a malformed dir name out
# of the dispatch payload entirely.
if [ "$count" = "1" ] && [ -n "$changed" ]; then
case "$changed" in
*[!a-z0-9-]* | -* ) echo "slug=" >> "$GITHUB_OUTPUT" ;;
*) echo "slug=$changed" >> "$GITHUB_OUTPUT" ;;
esac
else
echo "slug=" >> "$GITHUB_OUTPUT"
fi
# GitHub App token (preferred). Activates when this repo has
# USE_GITHUB_APP=true and the TUTORIALS_APP_* secrets populated. Falls
# back to the classic TUTORIALS_DISPATCH_TOKEN PAT while unset.
- name: Generate GitHub App token
id: app-token
if: ${{ vars.USE_GITHUB_APP == 'true' }}
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.TUTORIALS_APP_ID }}
private-key: ${{ secrets.TUTORIALS_APP_PRIVATE_KEY }}
owner: sap-tutorials
repositories: tutorials-ims
- name: Trigger content rebuild
env:
DISPATCH_TOKEN: ${{ steps.app-token.outputs.token || secrets.TUTORIALS_DISPATCH_TOKEN }}
# rebuild-content.yml routes on client_payload.environment (allowlist
# dev|qa|prod, defaults dev when absent). This is the PRODUCTION
# source-repo template, so it targets prod; override per-repo with a
# REBUILD_ENVIRONMENT repo variable (e.g. a staging source repo → qa).
TARGET_ENV: ${{ vars.REBUILD_ENVIRONMENT || 'prod' }}
# Single changed slug (empty if 0 or >1 or non-slug-charset) →
# rebuild-content.yml uses it to run slug-targeted (fast) vs full.
SLUG: ${{ steps.slug.outputs.slug }}
# Tutorials whose dir was entirely deleted (#2349) → rebuild-content.yml
# routes these to the unpublish (soft-delete) lane, not the fetch lane.
DELETED_SLUGS: ${{ steps.slug.outputs.deleted_slugs }}
# Pin the github context values into env too, so the JSON body is
# assembled entirely from shell vars via jq (no template interpolation
# into the payload string).
REPO: ${{ github.repository }}
REF: ${{ github.ref }}
SHA: ${{ github.sha }}
run: |
# Build the payload with jq so every value is correctly JSON-escaped
# (a slug / ref / repo containing quotes or backslashes can't break out
# of the JSON — the string-interpolation form was a json-injection risk).
BODY=$(jq -nc \
--arg repo "$REPO" --arg ref "$REF" --arg sha "$SHA" \
--arg env "$TARGET_ENV" --arg slug "$SLUG" --arg deleted "$DELETED_SLUGS" \
'{event_type:"tutorial-updated", client_payload:{repository:$repo, ref:$ref, sha:$sha, environment:$env, slug:$slug, deleted_slugs:$deleted}}')
curl -X POST \
-H "Authorization: Bearer ${DISPATCH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
--data "$BODY" \
"https://api.github.com/repos/sap-tutorials/tutorials-ims/dispatches"