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
349on :
450 push :
753 - zcs
854 workflow_dispatch :
955
56+ permissions :
57+ contents : read
58+
1059jobs :
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