Skip to content

Commit 69393ae

Browse files
committed
refactor(ci): implement subtree sync workflow to replace dispatch
1 parent 564b893 commit 69393ae

1 file changed

Lines changed: 225 additions & 7 deletions

File tree

.github/workflows/push.yml

Lines changed: 225 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,50 @@
1-
name: Notify Parent Repository
1+
# ═══════════════════════════════════════════════════════════════════════════════
2+
# 组件仓库 GitHub Actions 配置模板
3+
# ═══════════════════════════════════════════════════════════════════════════════
4+
#
5+
# 此文件用于独立组件仓库。
6+
# 当组件仓库的 main 分支收到新提交时,它会直接到主仓库创建一个
7+
# subtree 同步 PR,不再使用 repository_dispatch 通知方案。
8+
#
9+
# 【使用步骤】
10+
# ─────────────────────────────────────────────────────────────────────────────
11+
# 1. 将此文件复制到组件仓库:
12+
# cp scripts/push.yml <component-repo>/.github/workflows/push.yml
13+
#
14+
# 2. 在组件仓库中配置 Secret:
15+
# - Name: PARENT_REPO_TOKEN
16+
# - Value: 具有主仓库写权限的 Classic Personal Access Token
17+
#
18+
# 3. 按需修改下面 env 中的 PARENT_REPO:
19+
# - 例如:rcore-os/tgoskits
20+
#
21+
# 【Token 权限要求】
22+
# ─────────────────────────────────────────────────────────────────────────────
23+
# PARENT_REPO_TOKEN 需要能对主仓库执行:
24+
# - push branch
25+
# - create pull request
26+
#
27+
# Classic Personal Access Token 建议至少包含:
28+
# - repo
29+
#
30+
# 【工作流程】
31+
# ─────────────────────────────────────────────────────────────────────────────
32+
# 组件仓库 main push
33+
# -> clone 主仓库
34+
# -> 从主仓库 scripts/repo/repos.csv 中定位当前组件对应的 subtree 路径
35+
# -> 在主仓库新分支执行 git subtree pull --prefix=<target_dir> <repo_url> <sha>
36+
# -> push 到主仓库
37+
# -> 创建或更新 PR 到主仓库 main
38+
#
39+
# 【注意事项】
40+
# ─────────────────────────────────────────────────────────────────────────────
41+
# - 此工作流只监听组件仓库 main,避免主仓库推送到组件 dev 后形成循环触发。
42+
# - repos.csv 中必须存在当前组件仓库 URL 对应的记录。
43+
# - 如果该 commit 已经被同步到主仓库,工作流会自动跳过。
44+
#
45+
# ═══════════════════════════════════════════════════════════════════════════════
46+
47+
name: Sync To Parent Repository
248

349
on:
450
push:
@@ -7,10 +53,182 @@ on:
753
- zcs
854
workflow_dispatch:
955

56+
permissions:
57+
contents: read
58+
1059
jobs:
11-
notify-parent:
12-
name: Notify Parent Repository
13-
# 调用 axci 仓库的可复用工作流
14-
uses: arceos-hypervisor/axci/.github/workflows/push.yml@main
15-
secrets:
16-
PARENT_REPO_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }}
60+
sync-to-parent:
61+
runs-on: ubuntu-latest
62+
env:
63+
# 需要修改为主仓库路径
64+
PARENT_REPO: rcore-os/tgoskits
65+
PARENT_BASE_BRANCH: main
66+
SYNC_BRANCH_PREFIX: subtree-sync
67+
68+
steps:
69+
- name: Checkout parent repository
70+
uses: actions/checkout@v5
71+
with:
72+
repository: ${{ env.PARENT_REPO }}
73+
ref: ${{ env.PARENT_BASE_BRANCH }}
74+
fetch-depth: 0
75+
token: ${{ secrets.PARENT_REPO_TOKEN }}
76+
path: parent-repo
77+
78+
- name: Configure Git
79+
run: |
80+
git config --global user.name "github-actions[bot]"
81+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
82+
83+
- name: Resolve component mapping from parent repos.csv
84+
id: mapping
85+
env:
86+
COMPONENT_URL: ${{ github.server_url }}/${{ github.repository }}
87+
COMPONENT_NAME: ${{ github.event.repository.name }}
88+
working-directory: parent-repo
89+
run: |
90+
python3 - <<'PY' >> "$GITHUB_OUTPUT"
91+
import csv
92+
import os
93+
import sys
94+
from pathlib import Path
95+
96+
def normalize(url: str) -> str:
97+
return url.strip().rstrip("/").removesuffix(".git")
98+
99+
component_url = normalize(os.environ["COMPONENT_URL"])
100+
component_name = os.environ["COMPONENT_NAME"]
101+
csv_path = Path("scripts/repo/repos.csv")
102+
103+
with csv_path.open(newline="", encoding="utf-8") as f:
104+
reader = csv.DictReader(f)
105+
for row in reader:
106+
repo_url = normalize(row.get("url", ""))
107+
if repo_url == component_url:
108+
target_dir = (row.get("target_dir") or "").strip()
109+
if not target_dir:
110+
print("target_dir is empty for matched component", file=sys.stderr)
111+
sys.exit(1)
112+
print(f"repo_url={repo_url}")
113+
print(f"target_dir={target_dir}")
114+
print(f"repo_name={component_name}")
115+
sys.exit(0)
116+
117+
print(
118+
f"Component URL '{component_url}' was not found in scripts/repo/repos.csv",
119+
file=sys.stderr,
120+
)
121+
sys.exit(1)
122+
PY
123+
124+
echo "Resolved component mapping for ${COMPONENT_NAME}"
125+
126+
- name: Prepare sync branch
127+
id: branch
128+
env:
129+
REPO_NAME: ${{ steps.mapping.outputs.repo_name }}
130+
GIT_SHA: ${{ github.sha }}
131+
working-directory: parent-repo
132+
run: |
133+
SHORT_SHA="${GIT_SHA::12}"
134+
SYNC_BRANCH="${SYNC_BRANCH_PREFIX}/${REPO_NAME}-main"
135+
echo "sync_branch=${SYNC_BRANCH}" >> "$GITHUB_OUTPUT"
136+
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
137+
git checkout -B "${SYNC_BRANCH}" "origin/${PARENT_BASE_BRANCH}"
138+
139+
- name: Import component commit with git subtree
140+
env:
141+
COMPONENT_URL: ${{ steps.mapping.outputs.repo_url }}
142+
TARGET_DIR: ${{ steps.mapping.outputs.target_dir }}
143+
REPO_NAME: ${{ steps.mapping.outputs.repo_name }}
144+
GIT_SHA: ${{ github.sha }}
145+
working-directory: parent-repo
146+
run: |
147+
git subtree pull \
148+
--prefix="${TARGET_DIR}" \
149+
"${COMPONENT_URL}" \
150+
"${GIT_SHA}" \
151+
-m "sync(${REPO_NAME}): import ${GIT_SHA} from component main"
152+
153+
- name: Check whether parent repository changed
154+
id: changed
155+
working-directory: parent-repo
156+
run: |
157+
git fetch origin "${PARENT_BASE_BRANCH}" --quiet
158+
AHEAD_COUNT=$(git rev-list --count "origin/${PARENT_BASE_BRANCH}"..HEAD)
159+
echo "ahead_count=${AHEAD_COUNT}" >> "$GITHUB_OUTPUT"
160+
if [ "${AHEAD_COUNT}" -eq 0 ]; then
161+
echo "has_changes=false" >> "$GITHUB_OUTPUT"
162+
echo "No subtree updates were produced."
163+
else
164+
echo "has_changes=true" >> "$GITHUB_OUTPUT"
165+
echo "Parent repo has ${AHEAD_COUNT} new commit(s) to propose."
166+
fi
167+
168+
- name: Push sync branch
169+
if: steps.changed.outputs.has_changes == 'true'
170+
env:
171+
SYNC_BRANCH: ${{ steps.branch.outputs.sync_branch }}
172+
working-directory: parent-repo
173+
run: |
174+
git push --force-with-lease origin "HEAD:${SYNC_BRANCH}"
175+
176+
- name: Create or update pull request
177+
if: steps.changed.outputs.has_changes == 'true'
178+
env:
179+
GH_TOKEN: ${{ secrets.PARENT_REPO_TOKEN }}
180+
SYNC_BRANCH: ${{ steps.branch.outputs.sync_branch }}
181+
REPO_NAME: ${{ steps.mapping.outputs.repo_name }}
182+
TARGET_DIR: ${{ steps.mapping.outputs.target_dir }}
183+
GIT_SHA: ${{ github.sha }}
184+
working-directory: parent-repo
185+
run: |
186+
EXISTING_PR=$(gh pr list \
187+
--repo "${PARENT_REPO}" \
188+
--base "${PARENT_BASE_BRANCH}" \
189+
--head "${SYNC_BRANCH}" \
190+
--state open \
191+
--json number \
192+
--jq '.[0].number // empty')
193+
194+
if [ -n "${EXISTING_PR}" ]; then
195+
echo "PR #${EXISTING_PR} already exists for ${SYNC_BRANCH}"
196+
gh pr comment "${EXISTING_PR}" \
197+
--repo "${PARENT_REPO}" \
198+
--body "Updated subtree sync branch with component commit \`${GIT_SHA}\`."
199+
else
200+
gh pr create \
201+
--repo "${PARENT_REPO}" \
202+
--base "${PARENT_BASE_BRANCH}" \
203+
--head "${SYNC_BRANCH}" \
204+
--title "sync(${REPO_NAME}): import ${GIT_SHA}" \
205+
--body "$(cat <<EOF
206+
Sync component \`${REPO_NAME}\` into \`${TARGET_DIR}\`.
207+
208+
Source repository: \`${{ github.repository }}\`
209+
Source branch: \`main\`
210+
Source commit: \`${GIT_SHA}\`
211+
212+
This PR was created automatically by the component repository workflow using \`git subtree pull\`.
213+
EOF
214+
)"
215+
fi
216+
217+
- name: Create summary
218+
if: always()
219+
env:
220+
COMPONENT_REPO: ${{ github.repository }}
221+
COMPONENT_SHA: ${{ github.sha }}
222+
TARGET_DIR: ${{ steps.mapping.outputs.target_dir }}
223+
SYNC_BRANCH: ${{ steps.branch.outputs.sync_branch }}
224+
HAS_CHANGES: ${{ steps.changed.outputs.has_changes }}
225+
run: |
226+
echo "## Parent Sync Summary" >> "$GITHUB_STEP_SUMMARY"
227+
echo "" >> "$GITHUB_STEP_SUMMARY"
228+
echo "- **Component Repo**: ${COMPONENT_REPO}" >> "$GITHUB_STEP_SUMMARY"
229+
echo "- **Component Commit**: \`${COMPONENT_SHA}\`" >> "$GITHUB_STEP_SUMMARY"
230+
echo "- **Parent Repo**: ${PARENT_REPO}" >> "$GITHUB_STEP_SUMMARY"
231+
echo "- **Parent Base Branch**: ${PARENT_BASE_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
232+
echo "- **Subtree Path**: ${TARGET_DIR:-<unresolved>}" >> "$GITHUB_STEP_SUMMARY"
233+
echo "- **Sync Branch**: ${SYNC_BRANCH:-<not-created>}" >> "$GITHUB_STEP_SUMMARY"
234+
echo "- **Changes Produced**: ${HAS_CHANGES:-false}" >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)