jamesfredley commented on code in PR #98:
URL: 
https://github.com/apache/grails-github-actions/pull/98#discussion_r3522495907


##########
deploy-github-pages/entrypoint.sh:
##########
@@ -130,6 +139,140 @@ is_highest_version() {
   return 0
 }
 
+# Paths this deploy owns. Populated by publish_artifacts and used to reconcile 
the
+# working tree after rebasing onto a concurrent deploy (see 
reconcile_owned_paths).
+#   OWNED_PURGE_PATHS - folders this deploy replaces wholesale 
(PURGE_EXISTING=true):
+#                       on retry, strip anything a concurrent deploy left 
there and
+#                       restore exactly this deploy's content (clean 
last-writer-wins).
+#   OWNED_KEEP_PATHS  - single files this deploy owns outright (e.g. the root
+#                       index.html): on retry, re-assert just that file. Merged
+#                       folders (PURGE_EXISTING=false) are intentionally NOT 
listed
+#                       here - the "-X theirs" rebase already gives correct 
merge
+#                       semantics, and re-asserting them would clobber a 
concurrent
+#                       deploy's independent changes.
+OWNED_PURGE_PATHS=()
+OWNED_KEEP_PATHS=()
+
+# If this release publishes the shared "latest" folder, remember its path and 
this
+# release's generic (X.X.x) version. "latest" must always hold the HIGHEST 
released
+# version, but is_highest_version is evaluated against the checkout taken 
before a
+# concurrent higher release may have landed. reconcile_owned_paths re-checks 
these
+# against the rebased tree so a lower release retried after a higher one does 
not
+# overwrite "latest" with older docs. Empty unless this deploy published 
"latest".
+LATEST_OWNED_PATH=""
+LATEST_GENERIC_VERSION=""
+
+# After a push is rejected we rebase this deploy's commit onto the concurrent 
deploy
+# with "-X theirs", which wins FILE-LEVEL conflicts but cannot remove files 
the other
+# deploy ADDED to a folder we own. Re-assert our owned paths from our original 
commit
+# so shared folders (latest, X.X.x, ...) end up as a clean last-writer-wins 
instead of
+# a union of both deploys' files. Everything the other deploy published 
independently
+# (its own version folders) is untouched and preserved.
+reconcile_owned_paths() {
+  local p changed=false
+
+  # "latest" must hold the HIGHEST released version. If a concurrent, higher
+  # release landed first (its X.X.x folder is now in the just-rebased tree) and
+  # THIS is a lower release, do not overwrite "latest" with our older docs:
+  # restore it to the remote's (higher) content and drop it from our owned set.
+  # Re-run is_highest_version from within TARGET_FOLDER, where the version 
folders
+  # actually live, against the merged tree. Same-minor patches compare equal, 
so
+  # they still fall through to last-writer-wins, which is fine.
+  if [ -n "${LATEST_OWNED_PATH}" ] && [ -n "${LATEST_GENERIC_VERSION}" ]; then
+    if ! ( cd "${TARGET_FOLDER}" && is_highest_version 
"${LATEST_GENERIC_VERSION}" ); then
+      echo "A concurrent higher release now owns ${LATEST_OWNED_PATH}; 
yielding it to the remote."
+      # Strip our rebased copy first so files that exist ONLY in our (lower)
+      # version are removed - otherwise the checkout below would leave them and
+      # "latest" would become a union of both releases instead of exactly the
+      # remote's higher version. FETCH_HEAD is the remote tip fetched by the
+      # preceding `git pull --rebase`, i.e. the concurrent deploy we rebased 
onto.
+      git rm -rf --ignore-unmatch --quiet -- "${LATEST_OWNED_PATH}" >/dev/null 
2>&1 || true
+      git checkout FETCH_HEAD -- "${LATEST_OWNED_PATH}" >/dev/null 2>&1 || true
+      git add -A -- "${LATEST_OWNED_PATH}" >/dev/null 2>&1 || true

Review Comment:
   Good catch - addressed in b170851.
   
   The `latest` yield restore is no longer masked. It now only runs `git 
checkout FETCH_HEAD -- <path>` + `git add -A` when the remote actually 
publishes that path, guarded by `git ls-tree -r FETCH_HEAD -- <path>`:
   
   ```sh
   git rm -rf --ignore-unmatch --quiet -- "${LATEST_OWNED_PATH}" >/dev/null 
2>&1 || true
   local remote_latest
   remote_latest="$(git ls-tree -r FETCH_HEAD -- "${LATEST_OWNED_PATH}")"
   if [ -n "${remote_latest}" ]; then
     git checkout FETCH_HEAD -- "${LATEST_OWNED_PATH}"
     git add -A -- "${LATEST_OWNED_PATH}"
   fi
   ```
   
   If the remote genuinely lacks the path (e.g. a higher release that skipped 
`latest`), the preceding `git rm --ignore-unmatch` already staged the deletion 
and we skip the restore - which also avoids aborting on a now-absent pathspec. 
The `git ls-tree` check is unmasked, so an invalid `FETCH_HEAD` exits non-zero 
and fails the job under `set -e` rather than being mistaken for "path absent". 
A checkout failure on a path the remote does have also aborts.
   



##########
deploy-github-pages/entrypoint.sh:
##########
@@ -130,6 +139,140 @@ is_highest_version() {
   return 0
 }
 
+# Paths this deploy owns. Populated by publish_artifacts and used to reconcile 
the
+# working tree after rebasing onto a concurrent deploy (see 
reconcile_owned_paths).
+#   OWNED_PURGE_PATHS - folders this deploy replaces wholesale 
(PURGE_EXISTING=true):
+#                       on retry, strip anything a concurrent deploy left 
there and
+#                       restore exactly this deploy's content (clean 
last-writer-wins).
+#   OWNED_KEEP_PATHS  - single files this deploy owns outright (e.g. the root
+#                       index.html): on retry, re-assert just that file. Merged
+#                       folders (PURGE_EXISTING=false) are intentionally NOT 
listed
+#                       here - the "-X theirs" rebase already gives correct 
merge
+#                       semantics, and re-asserting them would clobber a 
concurrent
+#                       deploy's independent changes.
+OWNED_PURGE_PATHS=()
+OWNED_KEEP_PATHS=()
+
+# If this release publishes the shared "latest" folder, remember its path and 
this
+# release's generic (X.X.x) version. "latest" must always hold the HIGHEST 
released
+# version, but is_highest_version is evaluated against the checkout taken 
before a
+# concurrent higher release may have landed. reconcile_owned_paths re-checks 
these
+# against the rebased tree so a lower release retried after a higher one does 
not
+# overwrite "latest" with older docs. Empty unless this deploy published 
"latest".
+LATEST_OWNED_PATH=""
+LATEST_GENERIC_VERSION=""
+
+# After a push is rejected we rebase this deploy's commit onto the concurrent 
deploy
+# with "-X theirs", which wins FILE-LEVEL conflicts but cannot remove files 
the other
+# deploy ADDED to a folder we own. Re-assert our owned paths from our original 
commit
+# so shared folders (latest, X.X.x, ...) end up as a clean last-writer-wins 
instead of
+# a union of both deploys' files. Everything the other deploy published 
independently
+# (its own version folders) is untouched and preserved.
+reconcile_owned_paths() {
+  local p changed=false
+
+  # "latest" must hold the HIGHEST released version. If a concurrent, higher
+  # release landed first (its X.X.x folder is now in the just-rebased tree) and
+  # THIS is a lower release, do not overwrite "latest" with our older docs:
+  # restore it to the remote's (higher) content and drop it from our owned set.
+  # Re-run is_highest_version from within TARGET_FOLDER, where the version 
folders
+  # actually live, against the merged tree. Same-minor patches compare equal, 
so
+  # they still fall through to last-writer-wins, which is fine.
+  if [ -n "${LATEST_OWNED_PATH}" ] && [ -n "${LATEST_GENERIC_VERSION}" ]; then
+    if ! ( cd "${TARGET_FOLDER}" && is_highest_version 
"${LATEST_GENERIC_VERSION}" ); then
+      echo "A concurrent higher release now owns ${LATEST_OWNED_PATH}; 
yielding it to the remote."
+      # Strip our rebased copy first so files that exist ONLY in our (lower)
+      # version are removed - otherwise the checkout below would leave them and
+      # "latest" would become a union of both releases instead of exactly the
+      # remote's higher version. FETCH_HEAD is the remote tip fetched by the
+      # preceding `git pull --rebase`, i.e. the concurrent deploy we rebased 
onto.
+      git rm -rf --ignore-unmatch --quiet -- "${LATEST_OWNED_PATH}" >/dev/null 
2>&1 || true
+      git checkout FETCH_HEAD -- "${LATEST_OWNED_PATH}" >/dev/null 2>&1 || true
+      git add -A -- "${LATEST_OWNED_PATH}" >/dev/null 2>&1 || true
+      local kept=() x
+      for x in "${OWNED_PURGE_PATHS[@]}"; do
+        [ "$x" = "${LATEST_OWNED_PATH}" ] || kept+=("$x")
+      done
+      OWNED_PURGE_PATHS=("${kept[@]}")
+      changed=true
+    fi
+  fi
+
+  for p in "${OWNED_PURGE_PATHS[@]}"; do
+    git rm -rf --ignore-unmatch --quiet -- "$p" >/dev/null 2>&1 || true
+    git checkout "$DEPLOY_COMMIT" -- "$p" >/dev/null 2>&1 || true
+    git add -A -- "$p" >/dev/null 2>&1 || true
+    changed=true

Review Comment:
   Addressed in b170851. Removed the `|| true` (and output suppression) from 
the owned-purge restore:
   
   ```sh
   git rm -rf --ignore-unmatch --quiet -- "$p" >/dev/null 2>&1 || true
   git checkout "$DEPLOY_COMMIT" -- "$p"
   git add -A -- "$p"
   ```
   
   These paths are always present in `DEPLOY_COMMIT` (this deploy published 
them), so a `git checkout`/`git add` failure signals a genuinely broken state. 
It now fails the job under `set -e` instead of silently committing the folder's 
removal and publishing broken docs. Only the best-effort pre-clean `git rm 
--ignore-unmatch` stays lenient, since `--ignore-unmatch` already handles the 
expected "not present" case.
   



##########
deploy-github-pages/entrypoint.sh:
##########
@@ -130,6 +139,140 @@ is_highest_version() {
   return 0
 }
 
+# Paths this deploy owns. Populated by publish_artifacts and used to reconcile 
the
+# working tree after rebasing onto a concurrent deploy (see 
reconcile_owned_paths).
+#   OWNED_PURGE_PATHS - folders this deploy replaces wholesale 
(PURGE_EXISTING=true):
+#                       on retry, strip anything a concurrent deploy left 
there and
+#                       restore exactly this deploy's content (clean 
last-writer-wins).
+#   OWNED_KEEP_PATHS  - single files this deploy owns outright (e.g. the root
+#                       index.html): on retry, re-assert just that file. Merged
+#                       folders (PURGE_EXISTING=false) are intentionally NOT 
listed
+#                       here - the "-X theirs" rebase already gives correct 
merge
+#                       semantics, and re-asserting them would clobber a 
concurrent
+#                       deploy's independent changes.
+OWNED_PURGE_PATHS=()
+OWNED_KEEP_PATHS=()
+
+# If this release publishes the shared "latest" folder, remember its path and 
this
+# release's generic (X.X.x) version. "latest" must always hold the HIGHEST 
released
+# version, but is_highest_version is evaluated against the checkout taken 
before a
+# concurrent higher release may have landed. reconcile_owned_paths re-checks 
these
+# against the rebased tree so a lower release retried after a higher one does 
not
+# overwrite "latest" with older docs. Empty unless this deploy published 
"latest".
+LATEST_OWNED_PATH=""
+LATEST_GENERIC_VERSION=""
+
+# After a push is rejected we rebase this deploy's commit onto the concurrent 
deploy
+# with "-X theirs", which wins FILE-LEVEL conflicts but cannot remove files 
the other
+# deploy ADDED to a folder we own. Re-assert our owned paths from our original 
commit
+# so shared folders (latest, X.X.x, ...) end up as a clean last-writer-wins 
instead of
+# a union of both deploys' files. Everything the other deploy published 
independently
+# (its own version folders) is untouched and preserved.
+reconcile_owned_paths() {
+  local p changed=false
+
+  # "latest" must hold the HIGHEST released version. If a concurrent, higher
+  # release landed first (its X.X.x folder is now in the just-rebased tree) and
+  # THIS is a lower release, do not overwrite "latest" with our older docs:
+  # restore it to the remote's (higher) content and drop it from our owned set.
+  # Re-run is_highest_version from within TARGET_FOLDER, where the version 
folders
+  # actually live, against the merged tree. Same-minor patches compare equal, 
so
+  # they still fall through to last-writer-wins, which is fine.
+  if [ -n "${LATEST_OWNED_PATH}" ] && [ -n "${LATEST_GENERIC_VERSION}" ]; then
+    if ! ( cd "${TARGET_FOLDER}" && is_highest_version 
"${LATEST_GENERIC_VERSION}" ); then
+      echo "A concurrent higher release now owns ${LATEST_OWNED_PATH}; 
yielding it to the remote."
+      # Strip our rebased copy first so files that exist ONLY in our (lower)
+      # version are removed - otherwise the checkout below would leave them and
+      # "latest" would become a union of both releases instead of exactly the
+      # remote's higher version. FETCH_HEAD is the remote tip fetched by the
+      # preceding `git pull --rebase`, i.e. the concurrent deploy we rebased 
onto.
+      git rm -rf --ignore-unmatch --quiet -- "${LATEST_OWNED_PATH}" >/dev/null 
2>&1 || true
+      git checkout FETCH_HEAD -- "${LATEST_OWNED_PATH}" >/dev/null 2>&1 || true
+      git add -A -- "${LATEST_OWNED_PATH}" >/dev/null 2>&1 || true
+      local kept=() x
+      for x in "${OWNED_PURGE_PATHS[@]}"; do
+        [ "$x" = "${LATEST_OWNED_PATH}" ] || kept+=("$x")
+      done
+      OWNED_PURGE_PATHS=("${kept[@]}")
+      changed=true
+    fi
+  fi
+
+  for p in "${OWNED_PURGE_PATHS[@]}"; do
+    git rm -rf --ignore-unmatch --quiet -- "$p" >/dev/null 2>&1 || true
+    git checkout "$DEPLOY_COMMIT" -- "$p" >/dev/null 2>&1 || true
+    git add -A -- "$p" >/dev/null 2>&1 || true
+    changed=true
+  done
+  for p in "${OWNED_KEEP_PATHS[@]}"; do
+    git checkout "$DEPLOY_COMMIT" -- "$p" >/dev/null 2>&1 || true
+    git add -A -- "$p" >/dev/null 2>&1 || true
+    changed=true

Review Comment:
   Addressed in b170851. Same fail-safe applied to the `OWNED_KEEP_PATHS` 
restore:
   
   ```sh
   git checkout "$DEPLOY_COMMIT" -- "$p"
   git add -A -- "$p"
   ```
   
   The `|| true` and output suppression are removed, so if restoring an owned 
file (e.g. the root `index.html`) fails, the job fails under `set -e` rather 
than silently pushing a missing file. These paths always exist in 
`DEPLOY_COMMIT`, so a failure here is a genuinely broken state.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to