Skip to content

Commit 3d4fabf

Browse files
authored
chore: add workflow to sync external resources (#44)
* chore: add sync workflow Assisted-By: Cursor Signed-off-by: Frank Kong <frkong@redhat.com> * chore: bump CLI version Signed-off-by: Frank Kong <frkong@redhat.com> * chore: add initial metadata.json file Signed-off-by: Frank Kong <frkong@redhat.com> * chore: use tempfile so it's not included in PR Signed-off-by: Frank Kong <frkong@redhat.com> * chore: update to be able to pull yarn.lock from different branches Signed-off-by: Frank Kong <frkong@redhat.com> --------- Signed-off-by: Frank Kong <frkong@redhat.com>
1 parent b9fcbeb commit 3d4fabf

5 files changed

Lines changed: 260 additions & 2 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: 'Sync External Files'
2+
description: 'Fetch specific files from an external GitHub repository and overwrite local copies when they differ'
3+
4+
inputs:
5+
repository:
6+
description: 'GitHub repository in owner/repo format (e.g., redhat-developer/rhdh)'
7+
required: true
8+
ref:
9+
description: 'Branch, tag, or ref to fetch from'
10+
required: false
11+
default: 'main'
12+
files:
13+
description: 'Newline-separated list of source-path:destination-path file mappings'
14+
required: true
15+
current-hash:
16+
description: 'Previously tracked commit hash. If provided and matches the remote HEAD, the clone and diff are skipped entirely.'
17+
required: false
18+
default: ''
19+
20+
outputs:
21+
changes_detected:
22+
description: 'Whether any file content differed (true/false)'
23+
value: ${{ steps.sync.outputs.changes_detected }}
24+
commit_hash:
25+
description: 'HEAD commit SHA of the fetched ref'
26+
value: ${{ steps.sync.outputs.commit_hash }}
27+
28+
runs:
29+
using: 'composite'
30+
steps:
31+
- name: Sync files from external repository
32+
id: sync
33+
shell: bash
34+
env:
35+
INPUT_REPOSITORY: ${{ inputs.repository }}
36+
INPUT_REF: ${{ inputs.ref }}
37+
INPUT_FILES: ${{ inputs.files }}
38+
INPUT_CURRENT_HASH: ${{ inputs.current-hash }}
39+
run: |
40+
set -euo pipefail
41+
42+
REPO_URL="https://github.com/${INPUT_REPOSITORY}.git"
43+
44+
if [[ -n "$INPUT_CURRENT_HASH" ]]; then
45+
echo "::group::Check remote HEAD for ${INPUT_REPOSITORY}@${INPUT_REF}"
46+
REMOTE_HASH=$(git ls-remote "$REPO_URL" "refs/heads/${INPUT_REF}" | cut -f1)
47+
if [[ -z "$REMOTE_HASH" ]]; then
48+
REMOTE_HASH=$(git ls-remote "$REPO_URL" "${INPUT_REF}" | cut -f1)
49+
fi
50+
echo "Remote HEAD: ${REMOTE_HASH:-unknown}"
51+
echo "Current hash: ${INPUT_CURRENT_HASH}"
52+
echo "::endgroup::"
53+
54+
if [[ "$REMOTE_HASH" == "$INPUT_CURRENT_HASH" ]]; then
55+
echo "Remote hash unchanged - skipping clone and sync"
56+
echo "commit_hash=${INPUT_CURRENT_HASH}" >> "$GITHUB_OUTPUT"
57+
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
58+
exit 0
59+
fi
60+
echo "Remote hash differs - proceeding with sync"
61+
fi
62+
63+
CLONE_DIR=$(mktemp -d)
64+
trap "rm -rf '$CLONE_DIR'" EXIT
65+
66+
# Parse source file paths from mappings for sparse checkout
67+
SOURCE_FILES=()
68+
while IFS= read -r mapping; do
69+
mapping=$(echo "$mapping" | xargs)
70+
[[ -z "$mapping" ]] && continue
71+
src="${mapping%%:*}"
72+
SOURCE_FILES+=("$src")
73+
done <<< "$INPUT_FILES"
74+
75+
if [[ ${#SOURCE_FILES[@]} -eq 0 ]]; then
76+
echo "Error: No file mappings provided"
77+
exit 1
78+
fi
79+
80+
echo "::group::Sparse clone ${INPUT_REPOSITORY}@${INPUT_REF}"
81+
git clone --depth 1 --branch "$INPUT_REF" --filter=blob:none --no-checkout \
82+
"$REPO_URL" "$CLONE_DIR"
83+
84+
pushd "$CLONE_DIR" > /dev/null
85+
git sparse-checkout init --no-cone
86+
git sparse-checkout set "${SOURCE_FILES[@]}"
87+
git checkout
88+
COMMIT_HASH=$(git rev-parse HEAD)
89+
popd > /dev/null
90+
echo "::endgroup::"
91+
92+
echo "Fetched commit: ${COMMIT_HASH}"
93+
echo "commit_hash=${COMMIT_HASH}" >> "$GITHUB_OUTPUT"
94+
95+
CHANGES_DETECTED="false"
96+
97+
echo "::group::Compare and sync files"
98+
while IFS= read -r mapping; do
99+
mapping=$(echo "$mapping" | xargs)
100+
[[ -z "$mapping" ]] && continue
101+
102+
src="${mapping%%:*}"
103+
dest="${mapping#*:}"
104+
src_full="${CLONE_DIR}/${src}"
105+
106+
if [[ ! -f "$src_full" ]]; then
107+
echo "::warning::Source file not found: ${src}"
108+
continue
109+
fi
110+
111+
if [[ ! -f "$dest" ]] || ! diff -q "$src_full" "$dest" > /dev/null 2>&1; then
112+
echo "Updating: ${src} -> ${dest}"
113+
mkdir -p "$(dirname "$dest")"
114+
cp -f "$src_full" "$dest"
115+
CHANGES_DETECTED="true"
116+
else
117+
echo "No changes: ${dest}"
118+
fi
119+
done <<< "$INPUT_FILES"
120+
echo "::endgroup::"
121+
122+
echo "changes_detected=${CHANGES_DETECTED}" >> "$GITHUB_OUTPUT"
123+
124+
if [[ "$CHANGES_DETECTED" == "true" ]]; then
125+
echo "Changes detected - files have been updated"
126+
else
127+
echo "No changes detected - all files are up to date"
128+
fi
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Update External Resources
2+
3+
on:
4+
schedule:
5+
- cron: '0 9 * * 1' # Mondays at 9 AM UTC
6+
workflow_dispatch:
7+
inputs:
8+
rhdh-ref:
9+
description: 'Branch/ref for rhdh (e.g., main, release-1.9)'
10+
required: false
11+
default: 'main'
12+
13+
permissions:
14+
contents: write
15+
pull-requests: write
16+
17+
jobs:
18+
sync-and-pr:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23+
24+
- name: Read current metadata hashes
25+
id: metadata
26+
shell: bash
27+
env:
28+
METADATA_FILE: resources/metadata.json
29+
run: |
30+
echo "scripts_hash=$(jq -r '."export-util-script-hash"' "$METADATA_FILE")" >> "$GITHUB_OUTPUT"
31+
echo "rhdh_hash=$(jq -r '."rhdh-hash"' "$METADATA_FILE")" >> "$GITHUB_OUTPUT"
32+
33+
- name: Sync export-utils scripts
34+
id: sync-scripts
35+
uses: ./.github/actions/sync-external-files
36+
with:
37+
repository: redhat-developer/rhdh-plugin-export-utils
38+
current-hash: ${{ steps.metadata.outputs.scripts_hash }}
39+
files: |
40+
override-sources/override-sources.sh:scripts/override-sources.sh
41+
export-dynamic/export-dynamic.sh:scripts/export-workspace.sh
42+
43+
- name: Sync RHDH yarn.lock
44+
id: sync-rhdh
45+
uses: ./.github/actions/sync-external-files
46+
with:
47+
repository: redhat-developer/rhdh
48+
ref: ${{ inputs.rhdh-ref || 'main' }}
49+
current-hash: ${{ steps.metadata.outputs.rhdh_hash }}
50+
files: |
51+
yarn.lock:resources/rhdh/yarn.lock
52+
53+
- name: Update metadata and create PR
54+
if: steps.sync-scripts.outputs.changes_detected == 'true' || steps.sync-rhdh.outputs.changes_detected == 'true'
55+
env:
56+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57+
SCRIPTS_HASH: ${{ steps.sync-scripts.outputs.commit_hash }}
58+
SCRIPTS_OLD_HASH: ${{ steps.metadata.outputs.scripts_hash }}
59+
RHDH_HASH: ${{ steps.sync-rhdh.outputs.commit_hash }}
60+
RHDH_OLD_HASH: ${{ steps.metadata.outputs.rhdh_hash }}
61+
SCRIPTS_CHANGED: ${{ steps.sync-scripts.outputs.changes_detected }}
62+
RHDH_CHANGED: ${{ steps.sync-rhdh.outputs.changes_detected }}
63+
METADATA_FILE: "resources/metadata.json"
64+
run: |
65+
TMP=$(mktemp)
66+
if [[ "$SCRIPTS_CHANGED" == "true" ]]; then
67+
jq --arg hash "$SCRIPTS_HASH" \
68+
'."export-util-script-hash" = $hash' "$METADATA_FILE" > "$TMP" \
69+
&& mv "$TMP" "$METADATA_FILE"
70+
fi
71+
72+
if [[ "$RHDH_CHANGED" == "true" ]]; then
73+
jq --arg hash "$RHDH_HASH" \
74+
'."rhdh-hash" = $hash' "$METADATA_FILE" > "$TMP" \
75+
&& mv "$TMP" "$METADATA_FILE"
76+
fi
77+
78+
git config user.name "github-actions[bot]"
79+
git config user.email "github-actions[bot]@users.noreply.github.com"
80+
81+
BRANCH="auto/update-external-resources"
82+
git checkout -B "$BRANCH"
83+
git add -A
84+
85+
COMMIT_CHANGES=""
86+
PR_CHANGES=""
87+
if [[ "$SCRIPTS_CHANGED" == "true" ]]; then
88+
COMMIT_CHANGES="${COMMIT_CHANGES}- rhdh-plugin-export-utils scripts (${SCRIPTS_OLD_HASH:0:7} -> ${SCRIPTS_HASH:0:7})"$'\n'
89+
PR_CHANGES="${PR_CHANGES}- **rhdh-plugin-export-utils** scripts: [\`${SCRIPTS_OLD_HASH:0:7}\`](https://github.com/redhat-developer/rhdh-plugin-export-utils/commit/${SCRIPTS_OLD_HASH}) -> [\`${SCRIPTS_HASH:0:7}\`](https://github.com/redhat-developer/rhdh-plugin-export-utils/commit/${SCRIPTS_HASH})"$'\n'
90+
fi
91+
if [[ "$RHDH_CHANGED" == "true" ]]; then
92+
COMMIT_CHANGES="${COMMIT_CHANGES}- rhdh yarn.lock (${RHDH_OLD_HASH:0:7} -> ${RHDH_HASH:0:7})"$'\n'
93+
PR_CHANGES="${PR_CHANGES}- **rhdh** yarn.lock: [\`${RHDH_OLD_HASH:0:7}\`](https://github.com/redhat-developer/rhdh/commit/${RHDH_OLD_HASH}) -> [\`${RHDH_HASH:0:7}\`](https://github.com/redhat-developer/rhdh/commit/${RHDH_HASH})"$'\n'
94+
fi
95+
96+
git commit -m "$(cat <<EOF
97+
chore: update external resources
98+
99+
Updated:
100+
${COMMIT_CHANGES}
101+
EOF
102+
)"
103+
104+
git push -f origin "$BRANCH"
105+
106+
# Reuse existing PR if one is already open for this branch
107+
EXISTING_PR=$(gh pr list --head "$BRANCH" --json number --jq '.[0].number' 2>/dev/null || true)
108+
109+
if [[ -n "$EXISTING_PR" ]]; then
110+
echo "PR #${EXISTING_PR} already exists, branch has been updated"
111+
else
112+
gh pr create \
113+
--title "chore: update external resources" \
114+
--body "$(cat <<EOF
115+
## Description
116+
117+
Automated weekly sync of external resources/scripts.
118+
119+
### What changed
120+
121+
${PR_CHANGES}
122+
EOF
123+
)" \
124+
--base main \
125+
--head "$BRANCH"
126+
fi

default.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# These values can be overridden by additional .env files or environment variables
33

44
# Tooling versions
5-
RHDH_CLI_VERSION="1.9.1"
5+
RHDH_CLI_VERSION="1.10.0"
66

77
# Logging
88
LOG_LEVEL="INFO"

resources/metadata.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"export-util-script-hash": "",
3+
"rhdh-hash": ""
4+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""Version information for RHDH Dynamic Plugin Factory."""
22

3-
__version__ = "1.9.1"
3+
__version__ = "1.10.0"
44

0 commit comments

Comments
 (0)