已合并
docs: clarify PR branch is based on upstream/master #306
docs: clarify PR branch is based on upstream/master #306
已合并
fuyangchenghu创建于 6月4日
2 个文件变更+24-30
@@ -76,7 +76,7 @@ https://gitcode.com/cann/cann-ops-competitions/tree/master/03_university/CANN-op
76- 完整克隆 fork(非浅克隆,避免 merge 冲突)76- 完整克隆 fork(非浅克隆,避免 merge 冲突)
77- 添加 upstream 远程,fetch + merge 上游最新代码77- 添加 upstream 远程,fetch + merge 上游最新代码
78 - 快进合并优先;分叉时可选创建合并提交;冲突未解决时报错退出78 - 快进合并优先;分叉时可选创建合并提交;冲突未解决时报错退出
79-- 创建 PR 分支 `submit/{school}_{team}/{op_name}`79+- 创建 PR 分支 `submit/{school}_{team}/{op_name}`,基于 `upstream/master` 创建(确保 PR 只包含本次提交,不受 fork master 上其他 commit 影响)
80 - 分支已存在 → 确认后重置到上游最新(确保每次提交都是干净的单 commit)80 - 分支已存在 → 确认后重置到上游最新(确保每次提交都是干净的单 commit)
81- 配置 git user.name / user.email(使用 CLA 签署邮箱)81- 配置 git user.name / user.email(使用 CLA 签署邮箱)
82 82 
@@ -352,7 +352,7 @@ https://clasign.osinfra.cn/sign-cla/68cbd4a3dbabc050b436cdd4/individual
352| 措施 | 说明 |352| 措施 | 说明 |
353|------|------|353|------|------|
354| `curl --config` 文件 | API Token 通过临时配置文件注入(chmod 600),不暴露于进程表 `/proc/*/cmdline` |354| `curl --config` 文件 | API Token 通过临时配置文件注入(chmod 600),不暴露于进程表 `/proc/*/cmdline` |
355-| `git credential.helper=store` | git 认证通过临时 credential store 文件注入chmod 600),凭证通过 `-c` 临时覆盖不修改 .git/config |355+| `oauth2:TOKEN@` URL 直传 | git clone/push 通过 URL 内嵌令牌认证clone 后立即 set-url 移除 .git/config 中的令牌),解决 credential store 对部分用户 push 403 的问题 |
356| `jq` 构造 JSON | 避免变量直接拼入 JSON 字符串,防止注入 |356| `jq` 构造 JSON | 避免变量直接拼入 JSON 字符串,防止注入 |
357| `printf` 替代 heredoc | README/PR Body 用 `printf '%s\n'` 生成,防止变量值中 `$()`/`` ` `` 被命令替换执行 |357| `printf` 替代 heredoc | README/PR Body 用 `printf '%s\n'` 生成,防止变量值中 `$()`/`` ` `` 被命令替换执行 |
358| `printf -v` 替代 `eval` | `read_secret``printf -v` 赋值,避免命令注入 |358| `printf -v` 替代 `eval` | `read_secret``printf -v` 赋值,避免命令注入 |
@@ -90,7 +90,6 @@ write_temp_json() {
90TMPDIR=""90TMPDIR=""
91CURL_CONFIG=""91CURL_CONFIG=""
92GIT_UA="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"92GIT_UA="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
93-GIT_CRED_FILE=""
94cleanup() {93cleanup() {
95 if [[ -n "$TMPDIR" && -d "$TMPDIR" ]]; then94 if [[ -n "$TMPDIR" && -d "$TMPDIR" ]]; then
96 rm -rf "$TMPDIR"95 rm -rf "$TMPDIR"
@@ -101,7 +100,6 @@ trap cleanup EXIT
101TMPDIR=$(mktemp -d)100TMPDIR=$(mktemp -d)
102chmod 700 "$TMPDIR"101chmod 700 "$TMPDIR"
103CURL_CONFIG="${TMPDIR}/.curl_config"102CURL_CONFIG="${TMPDIR}/.curl_config"
104-GIT_CRED_FILE="${TMPDIR}/.git_creds"
105 103 
106for cmd in jq curl python3 git; do104for cmd in jq curl python3 git; do
107 command -v "$cmd" >/dev/null 2>&1 || error "缺少必要依赖: $cmd,请先安装"105 command -v "$cmd" >/dev/null 2>&1 || error "缺少必要依赖: $cmd,请先安装"
@@ -609,10 +607,7 @@ fi
609 607 
610FORK_PUBLIC_URL="https://gitcode.com/${GC_LOGIN_NAME}/${REPO_NAME}.git"608FORK_PUBLIC_URL="https://gitcode.com/${GC_LOGIN_NAME}/${REPO_NAME}.git"
611UPSTREAM_URL="https://gitcode.com/${REPO_OWNER}/${REPO_NAME}.git"609UPSTREAM_URL="https://gitcode.com/${REPO_OWNER}/${REPO_NAME}.git"
612- 610+FORK_AUTH_URL="https://oauth2:${GC_TOKEN}@gitcode.com/${GC_LOGIN_NAME}/${REPO_NAME}.git"
613-# Write git credential file (avoids token in process table and .git/config)
614-printf 'https://oauth2:%s@gitcode.com\n' "$GC_TOKEN" > "$GIT_CRED_FILE"
615-chmod 600 "$GIT_CRED_FILE"
616 611 
617# ============================================================612# ============================================================
618# Step 4: 克隆仓库613# Step 4: 克隆仓库
@@ -621,10 +616,10 @@ step "克隆仓库"
621 616 
622REPO_DIR="${TMPDIR}/repo"617REPO_DIR="${TMPDIR}/repo"
623info "克隆 fork..."618info "克隆 fork..."
624-timeout "$GIT_TIMEOUT" git -c "credential.helper=store --file=${GIT_CRED_FILE}" \619+timeout "$GIT_TIMEOUT" git -c "http.userAgent=${GIT_UA}" \
625- -c "http.userAgent=${GIT_UA}" \620+ clone "$FORK_AUTH_URL" "$REPO_DIR" 2>&1 \
626- clone "$FORK_PUBLIC_URL" "$REPO_DIR" 2>&1 \
627 || error "克隆失败或超时,请检查 GitCode 令牌权限和网络"621 || error "克隆失败或超时,请检查 GitCode 令牌权限和网络"
622+git -C "$REPO_DIR" remote set-url origin "$FORK_PUBLIC_URL"
628 623 
629git -C "$REPO_DIR" remote add upstream "$UPSTREAM_URL" 2>/dev/null || true624git -C "$REPO_DIR" remote add upstream "$UPSTREAM_URL" 2>/dev/null || true
630info "同步上游最新代码..."625info "同步上游最新代码..."
@@ -660,17 +655,17 @@ fi
660 655 
661PR_BRANCH="submit/${TEAM_DIR_NAME}/${OP_BASE_NAME}"656PR_BRANCH="submit/${TEAM_DIR_NAME}/${OP_BASE_NAME}"
662info "创建分支: ${PR_BRANCH}"657info "创建分支: ${PR_BRANCH}"
663-if ! git -C "$REPO_DIR" checkout -b "$PR_BRANCH" 2>/dev/null; then658+BRANCH_BASE="upstream/${TARGET_BRANCH}"
659+if ! git -C "$REPO_DIR" rev-parse "$BRANCH_BASE" >/dev/null 2>&1; then
660+ BRANCH_BASE="origin/${TARGET_BRANCH}"
661+fi
662+if ! git -C "$REPO_DIR" checkout -b "$PR_BRANCH" "$BRANCH_BASE" 2>/dev/null; then
664 if git -C "$REPO_DIR" checkout "$PR_BRANCH" 2>/dev/null; then663 if git -C "$REPO_DIR" checkout "$PR_BRANCH" 2>/dev/null; then
665 warn "分支 ${PR_BRANCH} 已存在,重置为最新上游代码"664 warn "分支 ${PR_BRANCH} 已存在,重置为最新上游代码"
666- RESET_BASE="upstream/${TARGET_BRANCH}"665+ warn "即将执行 git reset --hard ${BRANCH_BASE},这会丢弃分支上的本地修改"
667- if ! git -C "$REPO_DIR" rev-parse "$RESET_BASE" >/dev/null 2>&1; then
668- RESET_BASE="origin/${TARGET_BRANCH}"
669- fi
670- warn "即将执行 git reset --hard ${RESET_BASE},这会丢弃分支上的本地修改"
671 confirm "确认重置" || error "已取消,请手动处理分支后重新运行"666 confirm "确认重置" || error "已取消,请手动处理分支后重新运行"
672- git -C "$REPO_DIR" reset --hard "$RESET_BASE" 2>/dev/null \667+ git -C "$REPO_DIR" reset --hard "$BRANCH_BASE" 2>/dev/null \
673- || error "无法重置分支到 ${RESET_BASE}"668+ || error "无法重置分支到 ${BRANCH_BASE}"
674 else669 else
675 error "无法创建或切换到分支 ${PR_BRANCH}"670 error "无法创建或切换到分支 ${PR_BRANCH}"
676 fi671 fi
@@ -771,7 +766,7 @@ COMMIT_RC=$?
771set -e766set -e
772if [[ "$COMMIT_RC" -ne 0 ]]; then767if [[ "$COMMIT_RC" -ne 0 ]]; then
773 if git -C "$REPO_DIR" diff --cached --quiet 2>/dev/null; then768 if git -C "$REPO_DIR" diff --cached --quiet 2>/dev/null; then
774- warn "没有变更需要提交"769+ error "没有变更需要提交,无法创建 PR"
775 else770 else
776 error "git commit 失败: ${COMMIT_ERR}"771 error "git commit 失败: ${COMMIT_ERR}"
777 fi772 fi
@@ -779,18 +774,14 @@ fi
779 774 
780info "推送到 fork..."775info "推送到 fork..."
781set +e776set +e
782-PUSH_OUTPUT=$(timeout "$GIT_TIMEOUT" git -c "credential.helper=store --file=${GIT_CRED_FILE}" \777+PUSH_OUTPUT=$(timeout "$GIT_TIMEOUT" git -c "http.userAgent=${GIT_UA}" \
783- -c "http.userAgent=${GIT_UA}" \778+ -C "$REPO_DIR" push --force "$FORK_AUTH_URL" "$PR_BRANCH" 2>&1)
784- -C "$REPO_DIR" push --force-with-lease origin "$PR_BRANCH" 2>&1)
785PUSH_RC=$?779PUSH_RC=$?
786set -e780set -e
787if [[ "$PUSH_RC" -ne 0 ]]; then781if [[ "$PUSH_RC" -ne 0 ]]; then
788 error "推送失败 — exit code ${PUSH_RC}: ${PUSH_OUTPUT}"782 error "推送失败 — exit code ${PUSH_RC}: ${PUSH_OUTPUT}"
789fi783fi
790 784 
791-# Clear credential file after last git operation
792-rm -f "$GIT_CRED_FILE"
793- 
794# ============================================================785# ============================================================
795# Step 8: 创建 Pull Request786# Step 8: 创建 Pull Request
796# ============================================================787# ============================================================
@@ -805,8 +796,8 @@ while true; do
805 PR_PAGE_BODY=$(http_body "$EXISTING_PR_RESP")796 PR_PAGE_BODY=$(http_body "$EXISTING_PR_RESP")
806 if [[ -z "$PR_PAGE_BODY" || "$PR_PAGE_BODY" == "[]" ]]; then break; fi797 if [[ -z "$PR_PAGE_BODY" || "$PR_PAGE_BODY" == "[]" ]]; then break; fi
807 EXISTING_PR_BODY=$(echo "$EXISTING_PR_BODY" "$PR_PAGE_BODY" | jq -s 'add')798 EXISTING_PR_BODY=$(echo "$EXISTING_PR_BODY" "$PR_PAGE_BODY" | jq -s 'add')
808- PR_PAGE_LEN=$(echo "$PR_PAGE_BODY" | jq 'length')799+ PR_PAGE_LEN=$(echo "$PR_PAGE_BODY" | jq 'length' 2>/dev/null) || true
809- if [[ "$PR_PAGE_LEN" -lt 100 ]]; then break; fi800+ if [[ "${PR_PAGE_LEN:-0}" -lt 100 ]]; then break; fi
810 PR_PAGE=$((PR_PAGE + 1))801 PR_PAGE=$((PR_PAGE + 1))
811done802done
812if [[ -n "$EXISTING_PR_BODY" ]]; then803if [[ -n "$EXISTING_PR_BODY" ]]; then
@@ -875,8 +866,11 @@ else
875 PR_ERR_MSG=$(echo "$PR_API_RESP" | jq -r '.message // .error // .msg // empty' 2>/dev/null) || true866 PR_ERR_MSG=$(echo "$PR_API_RESP" | jq -r '.message // .error // .msg // empty' 2>/dev/null) || true
876 if [[ "$PR_HTTP" == "400" || "$PR_HTTP" == "409" ]] && echo "$PR_ERR_MSG" | grep -qi "already exists"; then867 if [[ "$PR_HTTP" == "400" || "$PR_HTTP" == "409" ]] && echo "$PR_ERR_MSG" | grep -qi "already exists"; then
877 warn "同分支已有 PR,重新查询..."868 warn "同分支已有 PR,重新查询..."
878- EXISTING_PR_URL2=$(echo "$EXISTING_PR_BODY" | jq -r --arg branch "$PR_BRANCH" --arg user "$GC_LOGIN_NAME" '[.[] | select(.head.ref == $branch and (.head.user.login // .head.user.username) == $user)] | .[0].html_url // .[0].web_url // empty' 2>/dev/null)869+ FRESH_PR_RESP=$(api_call GET "${GC_API}/repos/${REPO_OWNER}/${REPO_NAME}/pulls?state=open&per_page=100&head=${GC_LOGIN_NAME}:${PR_BRANCH}") || true
879- EXISTING_PR_NUM2=$(echo "$EXISTING_PR_BODY" | jq -r --arg branch "$PR_BRANCH" --arg user "$GC_LOGIN_NAME" '[.[] | select(.head.ref == $branch and (.head.user.login // .head.user.username) == $user)] | .[0].number // .[0].iid // empty' 2>/dev/null)870+ FRESH_PR_HTTP=$(http_code "$FRESH_PR_RESP")
871+ FRESH_PR_BODY=$(http_body "$FRESH_PR_RESP")
872+ EXISTING_PR_URL2=$(echo "$FRESH_PR_BODY" | jq -r '.[0].html_url // .[0].web_url // empty' 2>/dev/null)
873+ EXISTING_PR_NUM2=$(echo "$FRESH_PR_BODY" | jq -r '.[0].number // .[0].iid // empty' 2>/dev/null)
880 if [[ -n "$EXISTING_PR_URL2" ]]; then874 if [[ -n "$EXISTING_PR_URL2" ]]; then
881 PR_URL="$EXISTING_PR_URL2"; PR_NUMBER="$EXISTING_PR_NUM2"875 PR_URL="$EXISTING_PR_URL2"; PR_NUMBER="$EXISTING_PR_NUM2"
882 info "已有 PR #${PR_NUMBER}: ${PR_URL}"876 info "已有 PR #${PR_NUMBER}: ${PR_URL}"