44 schedule :
55 - cron : ' 0 9 * * 3' # Weekly on Wednesday
66 workflow_dispatch :
7+ inputs :
8+ ref :
9+ description : >-
10+ Ref to sync, for a docs fix that has shipped upstream but not been
11+ released. Any commit-ish the clone can resolve. Leave empty to sync
12+ the latest release, which is what the weekly run does.
13+ required : false
14+ type : string
715
816jobs :
917 sync :
@@ -31,25 +39,70 @@ jobs:
3139 id : check
3240 env :
3341 GH_TOKEN : ${{ steps.app-token.outputs.token }}
42+ # A dispatch input reaches the shell as data, never as script.
43+ INPUT_REF : ${{ inputs.ref }}
3444 run : |
3545 PIN=$(node -p "require('./.sources/upstream.json').synced.find(e => e.repo === 'dfinity/certified-assets').pinned")
3646 echo "pin=$PIN" >> $GITHUB_OUTPUT
3747
38- # Stable releases only, the same pattern the upstream.json watcher uses.
39- # `^v[0-9]` would accept v0.4.0-rc.1 and publish docs for a prerelease.
40- TAG=$(git -C /tmp/certified-assets tag --sort=-version:refname \
41- | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
42- echo "Pinned: $PIN. Latest release: $TAG."
43- echo "tag=$TAG" >> $GITHUB_OUTPUT
48+ if [ -n "$INPUT_REF" ]; then
49+ # A ref given by hand is the maintainer's call, so the release
50+ # checks below do not apply: the reason to pass one is a docs fix
51+ # that is not in a release yet.
52+ #
53+ # Tried as given and then under `origin/`, because a clone creates a
54+ # local branch only for the default branch and leaves every other
55+ # one reachable as `origin/<branch>` alone. A sha and a tag resolve
56+ # on the first attempt.
57+ REF_COMMIT=$(git -C /tmp/certified-assets rev-parse --verify --quiet "${INPUT_REF}^{commit}" \
58+ || git -C /tmp/certified-assets rev-parse --verify --quiet "origin/${INPUT_REF}^{commit}") || {
59+ echo "::error::Cannot resolve ref '$INPUT_REF' in dfinity/certified-assets."
60+ exit 1
61+ }
62+ TAG=$(git -C /tmp/certified-assets rev-parse --short "$REF_COMMIT")
63+ echo "Manual ref: $INPUT_REF resolved to $TAG. Pinned: $PIN."
4464
45- # The pin is allowed to sit ahead of the latest release while a docs
46- # fix has shipped but a release has not (the state this sync started
47- # in). Syncing the tag then would publish older prose.
48- if git -C /tmp/certified-assets merge-base --is-ancestor "$TAG" "$PIN"; then
49- echo "Pin already contains $TAG. Nothing to sync."
50- echo "needed=false" >> $GITHUB_OUTPUT
51- exit 0
65+ # Peel the pin to a commit before comparing. The tags here are
66+ # annotated, so an unpeeled tag name resolves to the tag object and
67+ # would never equal a commit: dispatching the pinned tag would then
68+ # read as a change and rewrite the pin from that tag to its own
69+ # commit sha, moving a tag pin onto a commit for no reason.
70+ PIN_COMMIT=$(git -C /tmp/certified-assets rev-parse --verify --quiet "${PIN}^{commit}") || {
71+ echo "::error::Pin '$PIN' does not resolve in dfinity/certified-assets."
72+ exit 1
73+ }
74+ if [ "$REF_COMMIT" = "$PIN_COMMIT" ]; then
75+ echo "That is the pin already. Nothing to sync."
76+ echo "needed=false" >> $GITHUB_OUTPUT
77+ exit 0
78+ fi
79+
80+ # A ref the pin already contains moves the pin backwards. That is
81+ # what a rollback is, so it runs, but it is also what a mistyped sha
82+ # looks like, so it is not silent: the run warns and the PR body
83+ # says so, which is the difference between the two.
84+ if git -C /tmp/certified-assets merge-base --is-ancestor \
85+ "$REF_COMMIT" "$PIN_COMMIT"; then
86+ echo "::warning::$INPUT_REF is behind the pin $PIN. This will move the pin backwards."
87+ echo "backwards=true" >> $GITHUB_OUTPUT
88+ fi
89+ else
90+ # Stable releases only, the same pattern the upstream.json watcher uses.
91+ # `^v[0-9]` would accept v0.4.0-rc.1 and publish docs for a prerelease.
92+ TAG=$(git -C /tmp/certified-assets tag --sort=-version:refname \
93+ | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
94+ echo "Pinned: $PIN. Latest release: $TAG."
95+
96+ # The pin is allowed to sit ahead of the latest release while a docs
97+ # fix has shipped but a release has not (the state this sync started
98+ # in). Syncing the tag then would publish older prose.
99+ if git -C /tmp/certified-assets merge-base --is-ancestor "$TAG" "$PIN"; then
100+ echo "Pin already contains $TAG. Nothing to sync."
101+ echo "needed=false" >> $GITHUB_OUTPUT
102+ exit 0
103+ fi
52104 fi
105+ echo "tag=$TAG" >> $GITHUB_OUTPUT
53106
54107 # Skip only when a PR is actually open. A branch on its own proves
55108 # nothing: if a previous run pushed and then failed at `gh pr create`,
@@ -68,18 +121,34 @@ jobs:
68121 git push origin --delete "$BRANCH"
69122 fi
70123
71- # A release that ships canister changes without touching docs/ leaves
72- # the synced pages byte-identical, so there is no content to review.
73- # The pin still has to move: it is allowed to sit on a commit only
74- # while no release carries the pages, and skipping here would strand
75- # it on that commit for good. So the PR is opened either way, and the
76- # body says which of the two it is.
77124 CHANGED=$(git -C /tmp/certified-assets diff --name-only "${PIN}..${TAG}" -- docs/)
78- echo "needed=true" >> $GITHUB_OUTPUT
79125 if [ -z "$CHANGED" ]; then
80- echo "No docs/ changes between $PIN and $TAG: advancing the pin only."
81- echo "pin_only=true" >> $GITHUB_OUTPUT
126+ # A release that ships canister changes without touching docs/ leaves
127+ # the synced pages byte-identical, so there is nothing to review. Two
128+ # cases, and only one of them is worth a pull request.
129+ if [ -n "$INPUT_REF" ] || ! git -C /tmp/certified-assets show-ref \
130+ --verify --quiet "refs/tags/${PIN}"; then
131+ # The pin is a commit (or a ref was dispatched by hand). Moving it
132+ # onto the tag is the point: a commit pin is allowed only while no
133+ # release carries the pages, and skipping here would strand it
134+ # there for good.
135+ echo "No docs/ changes between $PIN and $TAG: advancing the pin only."
136+ echo "needed=true" >> $GITHUB_OUTPUT
137+ echo "pin_only=true" >> $GITHUB_OUTPUT
138+ else
139+ # The pin is already a tag, so a pull request would carry an empty
140+ # page diff and a bumped ref, for a reader to review and merge with
141+ # nothing in it. The pin lags the release and stays accurate: it
142+ # says which ref this copy came from, and the copy still matches it.
143+ # The release itself is not lost, since the recipe that deploys this
144+ # canister releases in lockstep and is tracked under `watched`.
145+ echo "No docs/ changes between $PIN and $TAG, and the pin is a tag."
146+ echo "Nothing to sync."
147+ echo "needed=false" >> $GITHUB_OUTPUT
148+ exit 0
149+ fi
82150 else
151+ echo "needed=true" >> $GITHUB_OUTPUT
83152 echo "Changed upstream pages:"
84153 echo "$CHANGED"
85154 echo "pin_only=false" >> $GITHUB_OUTPUT
@@ -158,11 +227,24 @@ jobs:
158227 echo ""
159228 echo "Automated sync of the certified-assets user docs."
160229 echo ""
161- echo "**Release:** \`$TAG\` (pinned from \`$PIN\`)"
230+ if [ -n "$INPUT_REF" ]; then
231+ echo "**Ref:** \`$TAG\` (pinned from \`$PIN\`), dispatched by hand as \`$INPUT_REF\`."
232+ echo "A ref is synced by hand when a docs fix has shipped upstream but not"
233+ echo "been released; the next release moves the pin back onto a tag."
234+ if [ "$BACKWARDS" = "true" ]; then
235+ echo ""
236+ echo "> [!WARNING]"
237+ echo "> This ref is **behind** the pin, so the pin moves backwards and any"
238+ echo "> page below is reverted to the older text. Intended for a rollback."
239+ echo "> If you meant to move forward, close this and dispatch the right ref."
240+ fi
241+ else
242+ echo "**Release:** \`$TAG\` (pinned from \`$PIN\`)"
243+ fi
162244 echo ""
163245 if [ "$PIN_ONLY" = "true" ]; then
164- echo "No page changed in this range. The pin moves off a commit and onto "
165- echo "the release tag, so the only diff is \`source_ref\` on each page."
246+ echo "No page changed in this range, so the only diff is the pin and the "
247+ echo "\`source_ref\` each page records ."
166248 else
167249 echo "**Changed upstream files:**"
168250 while IFS= read -r f; do
@@ -192,3 +274,5 @@ jobs:
192274 PIN : ${{ steps.check.outputs.pin }}
193275 CHANGED : ${{ steps.check.outputs.changed_files }}
194276 PIN_ONLY : ${{ steps.check.outputs.pin_only }}
277+ INPUT_REF : ${{ inputs.ref }}
278+ BACKWARDS : ${{ steps.check.outputs.backwards }}
0 commit comments