Skip to content

Commit 0610401

Browse files
Zachary-wWclaude
andcommitted
ci: add comment-triggered internal CI workflow
Add an internal CI workflow triggered by PR comments or workflow_dispatch to package PR code, upload it to BOS, write a trigger signal, and notify the configured Ruliu group. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 601bcd4 commit 0610401

1 file changed

Lines changed: 286 additions & 0 deletions

File tree

.github/workflows/internal-ci.yml

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
name: LoongForge Internal CI
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
workflow_dispatch:
7+
inputs:
8+
pr_number:
9+
description: 'PR number (for manual trigger)'
10+
required: false
11+
12+
permissions:
13+
contents: read
14+
pull-requests: read
15+
16+
env:
17+
INTERNAL_CI_CONFIG_FILE: ${{ vars.INTERNAL_CI_CONFIG_FILE }}
18+
INTERNAL_CI_RUN_DIR: ${{ vars.INTERNAL_CI_RUN_DIR }}
19+
INTERNAL_CI_RUNNER_LABEL: ${{ vars.INTERNAL_CI_RUNNER_LABEL }}
20+
21+
concurrency:
22+
group: internal-ci-pr-${{ github.event.issue.number || inputs.pr_number || github.run_id }}
23+
cancel-in-progress: true
24+
25+
jobs:
26+
build-and-notify:
27+
if: >-
28+
github.event_name == 'workflow_dispatch' ||
29+
(
30+
github.event_name == 'issue_comment' &&
31+
github.event.issue.pull_request &&
32+
github.event.comment.body == '/loongforge-ci' &&
33+
(
34+
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR","CONTRIBUTOR"]'), github.event.comment.author_association) ||
35+
github.event.comment.user.login == github.event.issue.user.login
36+
)
37+
)
38+
runs-on: [self-hosted, Linux, X64]
39+
steps:
40+
- name: Load config
41+
run: |
42+
set -euo pipefail
43+
44+
: "${INTERNAL_CI_CONFIG_FILE:?INTERNAL_CI_CONFIG_FILE repository variable is required}"
45+
CONFIG_FILE="$INTERNAL_CI_CONFIG_FILE"
46+
if [ ! -f "$CONFIG_FILE" ]; then
47+
echo "配置文件不存在: $CONFIG_FILE"
48+
echo "请先在 self-hosted runner 上创建该文件,并在 repository variables 中设置 INTERNAL_CI_CONFIG_FILE"
49+
exit 1
50+
fi
51+
52+
while IFS= read -r line || [ -n "$line" ]; do
53+
case "$line" in
54+
''|'#'*) continue ;;
55+
esac
56+
57+
key="${line%%=*}"
58+
value="${line#*=}"
59+
if ! printf '%s' "$key" | grep -Eq '^[A-Za-z_][A-Za-z0-9_]*$'; then
60+
echo "配置项名称非法: $key"
61+
exit 1
62+
fi
63+
64+
echo "::add-mask::$value"
65+
printf '%s=%s\n' "$key" "$value" >> "$GITHUB_ENV"
66+
done < "$CONFIG_FILE"
67+
68+
: "${INTERNAL_CI_RUN_DIR:?INTERNAL_CI_RUN_DIR repository variable is required}"
69+
echo "INTERNAL_CI_RUN_DIR=$INTERNAL_CI_RUN_DIR" >> "$GITHUB_ENV"
70+
echo "INTERNAL_CI_RUNNER_LABEL=${INTERNAL_CI_RUNNER_LABEL:-self-hosted runner}" >> "$GITHUB_ENV"
71+
grep -q '^BOS_BUCKET=' "$GITHUB_ENV" || { echo "BOS_BUCKET not found in config"; exit 1; }
72+
grep -q '^BOS_PATH_PREFIX=' "$GITHUB_ENV" || { echo "BOS_PATH_PREFIX not found in config"; exit 1; }
73+
echo "配置已加载"
74+
75+
- name: Get PR info
76+
id: pr-info
77+
env:
78+
GH_TOKEN: ${{ github.token }}
79+
run: |
80+
set -euo pipefail
81+
82+
if [ "${{ github.event_name }}" = "issue_comment" ]; then
83+
PR_NUM="${{ github.event.issue.number }}"
84+
elif [ -n "${{ inputs.pr_number }}" ]; then
85+
PR_NUM="${{ inputs.pr_number }}"
86+
else
87+
echo "No PR number found"
88+
exit 1
89+
fi
90+
91+
if ! printf '%s' "$PR_NUM" | grep -Eq '^[0-9]+$'; then
92+
echo "Invalid PR number: $PR_NUM"
93+
exit 1
94+
fi
95+
96+
PR_DATA=$(curl -fsSL \
97+
-H "Authorization: Bearer $GH_TOKEN" \
98+
-H "Accept: application/vnd.github+json" \
99+
-H "X-GitHub-Api-Version: 2022-11-28" \
100+
"https://api.github.com/repos/$GITHUB_REPOSITORY/pulls/$PR_NUM")
101+
BRANCH=$(printf '%s' "$PR_DATA" | jq -r '.head.ref')
102+
HEAD_SHA=$(printf '%s' "$PR_DATA" | jq -r '.head.sha')
103+
104+
SHA7=$(printf '%s' "$HEAD_SHA" | cut -c1-7)
105+
TIMESTAMP=$(date +%s)
106+
SAFE_BRANCH=$(printf '%s' "$BRANCH" | sed 's#[^A-Za-z0-9._-]#_#g' | cut -c1-120)
107+
108+
echo "pr_num=$PR_NUM" >> "$GITHUB_OUTPUT"
109+
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
110+
echo "safe_branch=$SAFE_BRANCH" >> "$GITHUB_OUTPUT"
111+
echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
112+
echo "sha7=$SHA7" >> "$GITHUB_OUTPUT"
113+
echo "timestamp=$TIMESTAMP" >> "$GITHUB_OUTPUT"
114+
echo "PR #$PR_NUM, branch=$BRANCH, sha7=$SHA7"
115+
116+
- name: Checkout PR code
117+
env:
118+
GH_TOKEN: ${{ github.token }}
119+
PR_NUM: ${{ steps.pr-info.outputs.pr_num }}
120+
run: |
121+
set -euo pipefail
122+
123+
mkdir -p "$GITHUB_WORKSPACE"
124+
find "$GITHUB_WORKSPACE" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
125+
126+
git init "$GITHUB_WORKSPACE"
127+
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/$GITHUB_REPOSITORY.git"
128+
git -C "$GITHUB_WORKSPACE" config --local gc.auto 0
129+
130+
AUTH_HEADER=$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')
131+
git -C "$GITHUB_WORKSPACE" \
132+
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic $AUTH_HEADER" \
133+
fetch --no-tags origin "+refs/pull/${PR_NUM}/head:refs/remotes/origin/pr/${PR_NUM}"
134+
git -C "$GITHUB_WORKSPACE" checkout --force "refs/remotes/origin/pr/${PR_NUM}"
135+
git -C "$GITHUB_WORKSPACE" remote set-url origin "https://github.com/$GITHUB_REPOSITORY.git"
136+
git -C "$GITHUB_WORKSPACE" \
137+
-c "http.https://github.com/.extraheader=AUTHORIZATION: basic $AUTH_HEADER" \
138+
submodule update --init --recursive third_party/Loong-Megatron
139+
140+
- name: Repack code
141+
id: repack
142+
run: |
143+
set -euo pipefail
144+
145+
PR_NUM="${{ steps.pr-info.outputs.pr_num }}"
146+
STAGING_DIR="/tmp/loongforge_ci_${PR_NUM}"
147+
OUTPUT_FILE="/tmp/LoongForge_pr${PR_NUM}.tar.gz"
148+
MEGATRON_SHA=$(git ls-tree HEAD third_party/Loong-Megatron 2>/dev/null | awk '{print $3}')
149+
echo "Loong-Megatron SHA: ${MEGATRON_SHA:-not found}"
150+
151+
rm -rf "$STAGING_DIR"
152+
mkdir -p "$STAGING_DIR/LoongForge"
153+
154+
rsync -a --exclude='.git' --exclude='third_party/Loong-Megatron' ./ "$STAGING_DIR/LoongForge/"
155+
156+
TAR_INPUTS="LoongForge/"
157+
if [ -d "third_party/Loong-Megatron" ]; then
158+
rsync -a --exclude='.git' ./third_party/Loong-Megatron/ "$STAGING_DIR/Loong-Megatron/"
159+
TAR_INPUTS="$TAR_INPUTS Loong-Megatron/"
160+
else
161+
echo "警告:third_party/Loong-Megatron 不存在,跳过"
162+
fi
163+
164+
tar -czf "$OUTPUT_FILE" -C "$STAGING_DIR" $TAR_INPUTS
165+
166+
SIZE=$(du -h "$OUTPUT_FILE" | cut -f1)
167+
echo "Package size: $SIZE"
168+
echo "output_file=$OUTPUT_FILE" >> "$GITHUB_OUTPUT"
169+
170+
- name: Upload to BOS
171+
id: upload
172+
run: |
173+
set -euo pipefail
174+
175+
: "${BOS_BUCKET:?BOS_BUCKET is required}"
176+
: "${BOS_PATH_PREFIX:?BOS_PATH_PREFIX is required}"
177+
178+
BOS_PREFIX="${BOS_PATH_PREFIX%/}"
179+
BOS_PATH="$BOS_PREFIX/${{ steps.pr-info.outputs.safe_branch }}-${{ steps.pr-info.outputs.sha7 }}-${{ steps.pr-info.outputs.timestamp }}/LoongForge.tar.gz"
180+
OUTPUT_FILE="${{ steps.repack.outputs.output_file }}"
181+
PR_NUM="${{ steps.pr-info.outputs.pr_num }}"
182+
LOCAL_RUN_DIR="$INTERNAL_CI_RUN_DIR"
183+
LOCAL_UPLOAD_LOG="$LOCAL_RUN_DIR/pr${PR_NUM}_upload.log"
184+
185+
mkdir -p "$LOCAL_RUN_DIR"
186+
chmod 700 "$LOCAL_RUN_DIR"
187+
bcecmd bos cp "$OUTPUT_FILE" "bos:/$BOS_BUCKET/$BOS_PATH" > "$LOCAL_UPLOAD_LOG" 2>&1
188+
chmod 600 "$LOCAL_UPLOAD_LOG"
189+
190+
BRANCH="${{ steps.pr-info.outputs.branch }}"
191+
SHA7="${{ steps.pr-info.outputs.sha7 }}"
192+
TIMESTAMP="${{ steps.pr-info.outputs.timestamp }}"
193+
BOS_URL="https://$BOS_BUCKET.bj.bcebos.com/$BOS_PATH"
194+
LOCAL_RUN_FILE="$LOCAL_RUN_DIR/pr${PR_NUM}.json"
195+
196+
jq -n \
197+
--argjson pr_number "$PR_NUM" \
198+
--arg branch "$BRANCH" \
199+
--arg sha7 "$SHA7" \
200+
--arg timestamp "$TIMESTAMP" \
201+
--arg bos_url "$BOS_URL" \
202+
--arg updated_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
203+
'{pr_number:$pr_number,branch:$branch,sha7:$sha7,timestamp:$timestamp,bos_url:$bos_url,updated_at:$updated_at}' \
204+
> "$LOCAL_RUN_FILE"
205+
chmod 600 "$LOCAL_RUN_FILE"
206+
207+
echo "Uploaded package metadata written to local file: $LOCAL_RUN_FILE"
208+
209+
- name: Write CI trigger signal
210+
run: |
211+
set -euo pipefail
212+
213+
: "${BOS_BUCKET:?BOS_BUCKET is required}"
214+
: "${BOS_PATH_PREFIX:?BOS_PATH_PREFIX is required}"
215+
216+
PR_NUM="${{ steps.pr-info.outputs.pr_num }}"
217+
BRANCH="${{ steps.pr-info.outputs.branch }}"
218+
SHA7="${{ steps.pr-info.outputs.sha7 }}"
219+
TIMESTAMP="${{ steps.pr-info.outputs.timestamp }}"
220+
BOS_PREFIX="${BOS_PATH_PREFIX%/}"
221+
LOCAL_RUN_DIR="$INTERNAL_CI_RUN_DIR"
222+
LOCAL_RUN_FILE="$LOCAL_RUN_DIR/pr${PR_NUM}.json"
223+
LOCAL_SIGNAL_LOG="$LOCAL_RUN_DIR/pr${PR_NUM}_signal.log"
224+
225+
mkdir -p "$LOCAL_RUN_DIR"
226+
chmod 700 "$LOCAL_RUN_DIR"
227+
jq -n \
228+
--argjson pr_number "$PR_NUM" \
229+
--arg branch "$BRANCH" \
230+
--arg sha7 "$SHA7" \
231+
--arg timestamp "$TIMESTAMP" \
232+
--arg local_result_file "$LOCAL_RUN_FILE" \
233+
--arg updated_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
234+
'{pr_number:$pr_number,branch:$branch,sha7:$sha7,timestamp:$timestamp,local_result_file:$local_result_file,status:"pending",updated_at:$updated_at}' \
235+
> /tmp/ci_trigger.json
236+
237+
bcecmd bos cp /tmp/ci_trigger.json "bos:/$BOS_BUCKET/$BOS_PREFIX/ci_trigger_pr${PR_NUM}.json" > "$LOCAL_SIGNAL_LOG" 2>&1
238+
chmod 600 "$LOCAL_SIGNAL_LOG"
239+
240+
echo "CI trigger signal uploaded"
241+
242+
- name: Notify via Ruliu group
243+
if: always()
244+
run: |
245+
PR_NUM="${{ steps.pr-info.outputs.pr_num }}"
246+
BRANCH="${{ steps.pr-info.outputs.branch }}"
247+
SHA7="${{ steps.pr-info.outputs.sha7 }}"
248+
STATUS="打包上传成功"
249+
LOCAL_RUN_FILE="$INTERNAL_CI_RUN_DIR/pr${PR_NUM}.json"
250+
251+
if [ "${{ steps.upload.outcome }}" != "success" ]; then
252+
STATUS="打包上传失败"
253+
LOCAL_RUN_FILE="N/A"
254+
fi
255+
256+
if [ -z "${RULIU_WEBHOOK:-}" ]; then
257+
echo "RULIU_WEBHOOK 未配置,跳过通知"
258+
exit 0
259+
fi
260+
261+
CONTENT=$(cat <<EOF
262+
##### LoongForge CI 通知
263+
**PR**: #${PR_NUM:-N/A}
264+
**Branch**: ${BRANCH:-N/A}
265+
**Commit**: ${SHA7:-N/A}
266+
**状态**: ${STATUS}
267+
**${INTERNAL_CI_RUNNER_LABEL} 本机结果文件**: ${LOCAL_RUN_FILE}
268+
269+
dodo 将自动触发内部流水线,请稍候...
270+
EOF
271+
)
272+
PAYLOAD=$(jq -n --arg content "$CONTENT" '{message:{body:[{type:"MD",content:$content}]}}')
273+
274+
curl -sS -X POST "$RULIU_WEBHOOK" \
275+
-H 'Content-Type: application/json' \
276+
-d "$PAYLOAD" || echo "如流通知发送失败(不影响主流程)"
277+
278+
- name: Cleanup
279+
if: always()
280+
run: |
281+
PR_NUM="${{ steps.pr-info.outputs.pr_num }}"
282+
if [ -n "$PR_NUM" ]; then
283+
rm -rf "/tmp/loongforge_ci_${PR_NUM}"
284+
rm -f "/tmp/LoongForge_pr${PR_NUM}.tar.gz"
285+
fi
286+
rm -f /tmp/ci_trigger.json

0 commit comments

Comments
 (0)