Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 3 additions & 37 deletions .github/workflows/sync-upstream-backstage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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:
Expand Down Expand Up @@ -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
)

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 20 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
170 changes: 170 additions & 0 deletions hack/sync-upstream-backstage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/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 <name> Git remote for upstream Backstage charts (default: upstream-backstage)
# --ref <branch> Upstream branch to sync from (default: main)
# --prefix <path> 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 diff_file="$1"
local 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
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 "" >&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 <files>" >&2
echo " 5. Clean up: rm rhdh-vendored.patch" >&2
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" <<EOF

${RHDH_MARKER}
# Since this chart is vendored, we commit its dependencies rather than fetching them at install time
!charts/*/charts/*.tgz
EOF
fi
fi

# Rebuild vendored chart dependencies to restore .tgz files
helm dependency update "${PREFIX}/charts/backstage"

# ── Commit RHDH-specific changes ────────────────────────────────────
git add "$VENDOR_GITIGNORE"
git add "${PREFIX}/charts/backstage/charts/"*.tgz 2>/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."
Loading