From fa3a647ae6857ffd2f4a6f261e89b0b6f2ddafda Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 18 May 2026 10:33:23 +0200 Subject: [PATCH 1/2] fix(ci): preserve RHDH-specific template patches across upstream syncs The weekly sync workflow's subtree pull silently overwrites RHDH-specific modifications to vendored templates (lightspeed, catalog index images, checksums). Add a sync script that generates a patch of RHDH changes before the pull and re-applies it after, failing loudly if conflicts arise instead of silently dropping changes. --- .../workflows/sync-upstream-backstage.yaml | 40 +---- .gitignore | 3 + CONTRIBUTING.md | 31 ++-- hack/sync-upstream-backstage.sh | 169 ++++++++++++++++++ 4 files changed, 195 insertions(+), 48 deletions(-) create mode 100755 hack/sync-upstream-backstage.sh diff --git a/.github/workflows/sync-upstream-backstage.yaml b/.github/workflows/sync-upstream-backstage.yaml index 8aaf80b6..865f5229 100644 --- a/.github/workflows/sync-upstream-backstage.yaml +++ b/.github/workflows/sync-upstream-backstage.yaml @@ -39,15 +39,12 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - name: Sync upstream Backstage subtree + - name: Sync upstream Backstage subtree and re-apply RHDH patches id: sync run: | BEFORE_SHA=$(git rev-parse HEAD) - git remote add upstream-backstage https://github.com/backstage/charts.git - git fetch upstream-backstage main - git subtree pull --prefix charts/backstage/vendor/backstage upstream-backstage main --squash \ - -m "Squashed sync of upstream Backstage chart" + ./hack/sync-upstream-backstage.sh AFTER_SHA=$(git rev-parse HEAD) @@ -59,36 +56,6 @@ jobs: echo "has_changes=true" >> "$GITHUB_OUTPUT" fi - - name: Apply RHDH-specific changes to vendored chart - if: steps.sync.outputs.has_changes == 'true' - run: | - VENDOR_GITIGNORE="charts/backstage/vendor/backstage/.gitignore" - RHDH_MARKER="# RHDH: track vendored chart dependencies" - - # Fix directory ignore pattern: "charts/*/charts/" ignores the directory itself, - # which prevents un-ignoring files inside it. Change to "charts/*/charts/*" to - # ignore contents instead, allowing the negation pattern to work. - sed -i 's|^charts/\*/charts/$|charts/*/charts/*|' "${VENDOR_GITIGNORE}" - - # Ensure the .gitignore has the RHDH-specific exception to track tgz files - if ! grep -q "${RHDH_MARKER}" "${VENDOR_GITIGNORE}"; then - cat >> "${VENDOR_GITIGNORE}" << 'EOF' - - # RHDH: track vendored chart dependencies - # Since this chart is vendored, we commit its dependencies rather than fetching them at install time - !charts/*/charts/*.tgz - EOF - fi - - # Rebuild vendored chart dependencies to restore tgz files - helm dependency update charts/backstage/vendor/backstage/charts/backstage - - git add "${VENDOR_GITIGNORE}" - git add charts/backstage/vendor/backstage/charts/backstage/charts/*.tgz - if ! git diff --cached --quiet; then - git commit -m "chore: apply RHDH-specific changes to vendored Backstage chart" - fi - - name: Align dependency version and open PR if: steps.sync.outputs.has_changes == 'true' env: @@ -127,8 +94,7 @@ jobs: Updated the vendored Backstage chart to version **${CHART_VERSION}** and aligned the dependency reference in \`charts/backstage/Chart.yaml\`. - > [!CAUTION] - > The subtree pull may have silently overwritten RHDH-specific local changes to the vendored chart. Please review the changes carefully and restore any local modifications if needed. See [CONTRIBUTING.md](../CONTRIBUTING.md#note-on-the-backstage-chart-dependencies) for details. + RHDH-specific template patches were re-applied automatically after the subtree pull. Please review the changes to verify the patches applied cleanly. See [CONTRIBUTING.md](../CONTRIBUTING.md#note-on-the-backstage-chart-dependencies) for details. EOF ) diff --git a/.gitignore b/.gitignore index 64cb1073..e7cb5b51 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ output # Vendored charts need to be updated manually with `helm dependency update` !charts/*/vendor/**/charts/*.tgz + +# Temporary patch file from sync-upstream-backstage.sh conflict resolution +rhdh-vendored.patch diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ee7b30b..c971a864 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,27 +19,36 @@ Unlike standard Helm dependencies that fetch tarballs from a remote repository, ### Developer workflow -To sync with the upstream Backstage repository, add it as a remote on your local machine: +To sync with the upstream Backstage repository, use the [`hack/sync-upstream-backstage.sh`](./hack/sync-upstream-backstage.sh) script: ```bash -git remote add -f upstream-backstage https://github.com/backstage/charts.git +./hack/sync-upstream-backstage.sh ``` -When the upstream Backstage team releases an update, we can pull their changes into our subtree: +The script automatically: +1. Fetches the upstream remote (adding it if needed) +2. Generates a patch of RHDH-specific template modifications (e.g., Lightspeed integration, catalog index images) +3. Performs the subtree pull (which resets vendored files to upstream) +4. Re-applies the RHDH patch on top of the updated upstream +5. Restores `.gitignore` exceptions and vendored `.tgz` dependencies +6. Commits the result -```bash -git fetch upstream-backstage main -git subtree pull --prefix charts/backstage/vendor/backstage upstream-backstage main --squash +You can customize the remote and branch: -# You may also need to update the dependency version under charts/backstage/Chart.yaml +```bash +./hack/sync-upstream-backstage.sh --remote upstream-backstage --ref main ``` -It is important to use `--squash` to avoid pulling the entire commit history of the upstream chart repository. +If the RHDH patch fails to apply (because upstream changed the same lines), the script saves the patch to `rhdh-vendored.patch` in the repo root and exits with an error. To resolve: +1. Review the patch: `cat rhdh-vendored.patch` +2. Try 3-way merge: `git apply --3way rhdh-vendored.patch` +3. Or apply with rejects: `git apply --reject rhdh-vendored.patch`, then resolve any `.rej` files +4. Stage and commit the resolved files, then clean up: `rm rhdh-vendored.patch` -> [!CAUTION] -> **Reviewing subtree syncs:** The subtree pull may silently overwrite RHDH-specific local changes to the vendored chart, even when there are no merge conflicts. This can happen because Git's merge algorithm may auto-resolve changes in favor of upstream. After each sync, carefully review the diff to ensure any local customizations (e.g., `.gitignore` exceptions, template modifications) are preserved. If local changes were lost, restore them manually before merging. +After syncing, you may also need to update the dependency version under `charts/backstage/Chart.yaml` and rebuild the lock file (see below). -*Note: If merge conflicts occur, resolve them in your editor, then `git add` and `git commit` the resolution as a normal merge.* +> [!NOTE] +> The [weekly CI workflow](./.github/workflows/sync-upstream-backstage.yaml) uses this same script to sync automatically and open a PR. ### Sync Lightspeed vendored config files diff --git a/hack/sync-upstream-backstage.sh b/hack/sync-upstream-backstage.sh new file mode 100755 index 00000000..a975fe58 --- /dev/null +++ b/hack/sync-upstream-backstage.sh @@ -0,0 +1,169 @@ +#!/usr/bin/env bash +# +# Sync the vendored Backstage chart from upstream while preserving +# RHDH-specific template modifications. +# +# Usage: +# ./hack/sync-upstream-backstage.sh [OPTIONS] +# +# Options: +# --remote Git remote for upstream Backstage charts (default: upstream-backstage) +# --ref Upstream branch to sync from (default: main) +# --prefix Subtree prefix (default: charts/backstage/vendor/backstage) +# +# The script: +# 1. Fetches the upstream remote +# 2. Generates a patch of RHDH-specific changes to vendored templates +# 3. Performs a git subtree pull (which resets vendored files to upstream) +# 4. Re-applies the RHDH patch +# 5. Applies other RHDH fixups (.gitignore, Helm dependency .tgz files) +# 6. Commits the result +# +# If the RHDH patch fails to apply (e.g. upstream changed the same lines), +# the patch is saved to rhdh-vendored.patch for manual resolution. + +set -euo pipefail + +REMOTE="upstream-backstage" +REF="main" +PREFIX="charts/backstage/vendor/backstage" +UPSTREAM_URL="https://github.com/backstage/charts.git" + +usage() { + sed -n '2,/^$/s/^# \{0,1\}//p' "$0" + exit "${1:-0}" +} + +while [ $# -gt 0 ]; do + case "$1" in + --remote) REMOTE="$2"; shift 2 ;; + --ref) REF="$2"; shift 2 ;; + --prefix) PREFIX="$2"; shift 2 ;; + -h|--help) usage 0 ;; + *) echo "Unknown option: $1" >&2; usage 1 ;; + esac +done + +UPSTREAM_TEMPLATES="charts/backstage/templates" +VENDOR_TEMPLATES="${PREFIX}/charts/backstage/templates" +VENDOR_GITIGNORE="${PREFIX}/.gitignore" + +# ── Ensure upstream remote exists and is fetched ───────────────────── +if ! git remote get-url "$REMOTE" &>/dev/null; then + echo "Adding remote ${REMOTE} -> ${UPSTREAM_URL}" + git remote add "$REMOTE" "$UPSTREAM_URL" +fi +echo "Fetching ${REMOTE}/${REF}..." +git fetch "$REMOTE" "$REF" + +# ── Generate RHDH-specific patch ───────────────────────────────────── +PATCH_FILE=$(mktemp "${TMPDIR:-/tmp}/rhdh-patch.XXXXXX") +cleanup() { rm -f "$PATCH_FILE"; } +trap cleanup EXIT + +echo "Generating RHDH-specific template patches..." + +has_meaningful_diff() { + # A diff is meaningful if added and removed lines differ in content, + # not just in trailing whitespace or newline presence. + local added removed + added=$(sed -n 's/^+//p' "$1" | grep -v '^++' | sed 's/[[:space:]]*$//' | sort) + removed=$(sed -n 's/^-//p' "$1" | grep -v '^--' | sed 's/[[:space:]]*$//' | sort) + [ "$added" != "$removed" ] +} + +for vendored_file in "${VENDOR_TEMPLATES}"/*.yaml; do + [ -f "$vendored_file" ] || continue + filename=$(basename "$vendored_file") + + # Only diff files that also exist upstream; RHDH-only files won't be + # touched by the subtree pull so they don't need patching. + upstream_content=$(git show "${REMOTE}/${REF}:${UPSTREAM_TEMPLATES}/${filename}" 2>/dev/null) || continue + + # Produce a unified diff with paths relative to the repo root so + # git-apply works from the top level. + FILE_DIFF=$(mktemp "${TMPDIR:-/tmp}/rhdh-filediff.XXXXXX") + diff -u <(printf '%s\n' "$upstream_content") "$vendored_file" \ + | sed "1s|^--- .*|--- a/${VENDOR_TEMPLATES}/${filename}| + 2s|^+++ .*|+++ b/${VENDOR_TEMPLATES}/${filename}|" \ + > "$FILE_DIFF" || true # diff exits 1 when files differ + + if [ -s "$FILE_DIFF" ] && has_meaningful_diff "$FILE_DIFF"; then + cat "$FILE_DIFF" >> "$PATCH_FILE" + fi + rm -f "$FILE_DIFF" +done + +if [ -s "$PATCH_FILE" ]; then + patched_files=$(grep -c '^--- a/' "$PATCH_FILE" || true) + echo " Found patches for ${patched_files} file(s)." +else + echo " No RHDH-specific template patches to preserve." +fi + +# ── Subtree pull ───────────────────────────────────────────────────── +BEFORE_SHA=$(git rev-parse HEAD) + +echo "Pulling upstream subtree..." +git subtree pull --prefix "$PREFIX" "$REMOTE" "$REF" --squash \ + -m "Squashed sync of upstream Backstage chart" + +AFTER_SHA=$(git rev-parse HEAD) + +if [ "$BEFORE_SHA" = "$AFTER_SHA" ]; then + echo "No changes from upstream." + exit 0 +fi + +echo "Upstream changes merged." + +# ── Re-apply RHDH patches ─────────────────────────────────────────── +if [ -s "$PATCH_FILE" ]; then + echo "Re-applying RHDH-specific template patches..." + if ! git apply "$PATCH_FILE"; then + cp "$PATCH_FILE" rhdh-vendored.patch + trap - EXIT + echo "" + echo "ERROR: RHDH patch failed to apply cleanly." + echo "The patch has been saved to: rhdh-vendored.patch" + echo "" + echo "To resolve:" + echo " 1. Review the patch: cat rhdh-vendored.patch" + echo " 2. Try with 3-way: git apply --3way rhdh-vendored.patch" + echo " 3. Or with rejects: git apply --reject rhdh-vendored.patch" + echo " 4. Resolve any .rej files, then: git add " + echo " 5. Clean up: rm rhdh-vendored.patch" + exit 1 + fi + echo " RHDH template patches re-applied successfully." +fi + +# ── Apply .gitignore and .tgz fixups ──────────────────────────────── +RHDH_MARKER="# RHDH: track vendored chart dependencies" + +# Fix directory ignore pattern so negation rules work +if [ -f "$VENDOR_GITIGNORE" ]; then + sed -i'' -e 's|^charts/\*/charts/$|charts/*/charts/*|' "$VENDOR_GITIGNORE" + + if ! grep -q "$RHDH_MARKER" "$VENDOR_GITIGNORE"; then + cat >> "$VENDOR_GITIGNORE" </dev/null || true +git add "${VENDOR_TEMPLATES}/" +if ! git diff --cached --quiet; then + git commit -m "chore: apply RHDH-specific changes to vendored Backstage chart" +fi + +echo "Upstream sync complete." From fcc874adb08a185a8d2bf8e5c0321b5846f28614 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Mon, 18 May 2026 10:53:56 +0200 Subject: [PATCH 2/2] fix: address SonarCloud issues in sync script - Use [[ ]] instead of [ ] for conditional tests (S7688) - Assign positional parameter to local variable in has_meaningful_diff (S7679) - Redirect error messages to stderr (S7677) --- hack/sync-upstream-backstage.sh | 41 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/hack/sync-upstream-backstage.sh b/hack/sync-upstream-backstage.sh index a975fe58..0d2a5369 100755 --- a/hack/sync-upstream-backstage.sh +++ b/hack/sync-upstream-backstage.sh @@ -34,7 +34,7 @@ usage() { exit "${1:-0}" } -while [ $# -gt 0 ]; do +while [[ $# -gt 0 ]]; do case "$1" in --remote) REMOTE="$2"; shift 2 ;; --ref) REF="$2"; shift 2 ;; @@ -66,14 +66,15 @@ echo "Generating RHDH-specific template patches..." has_meaningful_diff() { # A diff is meaningful if added and removed lines differ in content, # not just in trailing whitespace or newline presence. + local diff_file="$1" local added removed - added=$(sed -n 's/^+//p' "$1" | grep -v '^++' | sed 's/[[:space:]]*$//' | sort) - removed=$(sed -n 's/^-//p' "$1" | grep -v '^--' | sed 's/[[:space:]]*$//' | sort) - [ "$added" != "$removed" ] + added=$(sed -n 's/^+//p' "$diff_file" | grep -v '^++' | sed 's/[[:space:]]*$//' | sort) + removed=$(sed -n 's/^-//p' "$diff_file" | grep -v '^--' | sed 's/[[:space:]]*$//' | sort) + [[ "$added" != "$removed" ]] } for vendored_file in "${VENDOR_TEMPLATES}"/*.yaml; do - [ -f "$vendored_file" ] || continue + [[ -f "$vendored_file" ]] || continue filename=$(basename "$vendored_file") # Only diff files that also exist upstream; RHDH-only files won't be @@ -88,13 +89,13 @@ for vendored_file in "${VENDOR_TEMPLATES}"/*.yaml; do 2s|^+++ .*|+++ b/${VENDOR_TEMPLATES}/${filename}|" \ > "$FILE_DIFF" || true # diff exits 1 when files differ - if [ -s "$FILE_DIFF" ] && has_meaningful_diff "$FILE_DIFF"; then + if [[ -s "$FILE_DIFF" ]] && has_meaningful_diff "$FILE_DIFF"; then cat "$FILE_DIFF" >> "$PATCH_FILE" fi rm -f "$FILE_DIFF" done -if [ -s "$PATCH_FILE" ]; then +if [[ -s "$PATCH_FILE" ]]; then patched_files=$(grep -c '^--- a/' "$PATCH_FILE" || true) echo " Found patches for ${patched_files} file(s)." else @@ -110,7 +111,7 @@ git subtree pull --prefix "$PREFIX" "$REMOTE" "$REF" --squash \ AFTER_SHA=$(git rev-parse HEAD) -if [ "$BEFORE_SHA" = "$AFTER_SHA" ]; then +if [[ "$BEFORE_SHA" = "$AFTER_SHA" ]]; then echo "No changes from upstream." exit 0 fi @@ -118,21 +119,21 @@ fi echo "Upstream changes merged." # ── Re-apply RHDH patches ─────────────────────────────────────────── -if [ -s "$PATCH_FILE" ]; then +if [[ -s "$PATCH_FILE" ]]; then echo "Re-applying RHDH-specific template patches..." if ! git apply "$PATCH_FILE"; then cp "$PATCH_FILE" rhdh-vendored.patch trap - EXIT - echo "" - echo "ERROR: RHDH patch failed to apply cleanly." - echo "The patch has been saved to: rhdh-vendored.patch" - echo "" - echo "To resolve:" - echo " 1. Review the patch: cat rhdh-vendored.patch" - echo " 2. Try with 3-way: git apply --3way rhdh-vendored.patch" - echo " 3. Or with rejects: git apply --reject rhdh-vendored.patch" - echo " 4. Resolve any .rej files, then: git add " - echo " 5. Clean up: rm rhdh-vendored.patch" + echo "" >&2 + echo "ERROR: RHDH patch failed to apply cleanly." >&2 + echo "The patch has been saved to: rhdh-vendored.patch" >&2 + echo "" >&2 + echo "To resolve:" >&2 + echo " 1. Review the patch: cat rhdh-vendored.patch" >&2 + echo " 2. Try with 3-way: git apply --3way rhdh-vendored.patch" >&2 + echo " 3. Or with rejects: git apply --reject rhdh-vendored.patch" >&2 + echo " 4. Resolve any .rej files, then: git add " >&2 + echo " 5. Clean up: rm rhdh-vendored.patch" >&2 exit 1 fi echo " RHDH template patches re-applied successfully." @@ -142,7 +143,7 @@ fi RHDH_MARKER="# RHDH: track vendored chart dependencies" # Fix directory ignore pattern so negation rules work -if [ -f "$VENDOR_GITIGNORE" ]; then +if [[ -f "$VENDOR_GITIGNORE" ]]; then sed -i'' -e 's|^charts/\*/charts/$|charts/*/charts/*|' "$VENDOR_GITIGNORE" if ! grep -q "$RHDH_MARKER" "$VENDOR_GITIGNORE"; then