已合并
docs: clarify PR branch is based on upstream/master #306
fuyangchenghu创建于 6月4日
docs: clarify PR branch is based on upstream/master #306
已合并
共 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() { | |||
| 90 | TMPDIR="" | 90 | TMPDIR="" |
| 91 | CURL_CONFIG="" | 91 | CURL_CONFIG="" |
| 92 | GIT_UA="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" | 92 | GIT_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="" | ||
| 94 | cleanup() { | 93 | cleanup() { |
| 95 | if [[ -n "$TMPDIR" && -d "$TMPDIR" ]]; then | 94 | if [[ -n "$TMPDIR" && -d "$TMPDIR" ]]; then |
| 96 | rm -rf "$TMPDIR" | 95 | rm -rf "$TMPDIR" |
| @@ -101,7 +100,6 @@ trap cleanup EXIT | |||
| 101 | TMPDIR=$(mktemp -d) | 100 | TMPDIR=$(mktemp -d) |
| 102 | chmod 700 "$TMPDIR" | 101 | chmod 700 "$TMPDIR" |
| 103 | CURL_CONFIG="${TMPDIR}/.curl_config" | 102 | CURL_CONFIG="${TMPDIR}/.curl_config" |
| 104 | -GIT_CRED_FILE="${TMPDIR}/.git_creds" | ||
| 105 | 103 | ||
| 106 | for cmd in jq curl python3 git; do | 104 | for 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 | ||
| 610 | FORK_PUBLIC_URL="https://gitcode.com/${GC_LOGIN_NAME}/${REPO_NAME}.git" | 608 | FORK_PUBLIC_URL="https://gitcode.com/${GC_LOGIN_NAME}/${REPO_NAME}.git" |
| 611 | UPSTREAM_URL="https://gitcode.com/${REPO_OWNER}/${REPO_NAME}.git" | 609 | UPSTREAM_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 | ||
| 622 | REPO_DIR="${TMPDIR}/repo" | 617 | REPO_DIR="${TMPDIR}/repo" |
| 623 | info "克隆 fork..." | 618 | info "克隆 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 | ||
| 629 | git -C "$REPO_DIR" remote add upstream "$UPSTREAM_URL" 2>/dev/null || true | 624 | git -C "$REPO_DIR" remote add upstream "$UPSTREAM_URL" 2>/dev/null || true |
| 630 | info "同步上游最新代码..." | 625 | info "同步上游最新代码..." |
| @@ -660,17 +655,17 @@ fi | |||
| 660 | 655 | ||
| 661 | PR_BRANCH="submit/${TEAM_DIR_NAME}/${OP_BASE_NAME}" | 656 | PR_BRANCH="submit/${TEAM_DIR_NAME}/${OP_BASE_NAME}" |
| 662 | info "创建分支: ${PR_BRANCH}" | 657 | info "创建分支: ${PR_BRANCH}" |
| 663 | -if ! git -C "$REPO_DIR" checkout -b "$PR_BRANCH" 2>/dev/null; then | 658 | +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; then | 663 | 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 | else | 669 | else |
| 675 | error "无法创建或切换到分支 ${PR_BRANCH}" | 670 | error "无法创建或切换到分支 ${PR_BRANCH}" |
| 676 | fi | 671 | fi |
| @@ -771,7 +766,7 @@ COMMIT_RC=$? | |||
| 771 | set -e | 766 | set -e |
| 772 | if [[ "$COMMIT_RC" -ne 0 ]]; then | 767 | if [[ "$COMMIT_RC" -ne 0 ]]; then |
| 773 | if git -C "$REPO_DIR" diff --cached --quiet 2>/dev/null; then | 768 | if git -C "$REPO_DIR" diff --cached --quiet 2>/dev/null; then |
| 774 | - warn "没有变更需要提交" | 769 | + error "没有变更需要提交,无法创建 PR" |
| 775 | else | 770 | else |
| 776 | error "git commit 失败: ${COMMIT_ERR}" | 771 | error "git commit 失败: ${COMMIT_ERR}" |
| 777 | fi | 772 | fi |
| @@ -779,18 +774,14 @@ fi | |||
| 779 | 774 | ||
| 780 | info "推送到 fork..." | 775 | info "推送到 fork..." |
| 781 | set +e | 776 | set +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) | ||
| 785 | PUSH_RC=$? | 779 | PUSH_RC=$? |
| 786 | set -e | 780 | set -e |
| 787 | if [[ "$PUSH_RC" -ne 0 ]]; then | 781 | if [[ "$PUSH_RC" -ne 0 ]]; then |
| 788 | error "推送失败 — exit code ${PUSH_RC}: ${PUSH_OUTPUT}" | 782 | error "推送失败 — exit code ${PUSH_RC}: ${PUSH_OUTPUT}" |
| 789 | fi | 783 | fi |
| 790 | 784 | ||
| 791 | -# Clear credential file after last git operation | ||
| 792 | -rm -f "$GIT_CRED_FILE" | ||
| 793 | - | ||
| 794 | # ============================================================ | 785 | # ============================================================ |
| 795 | # Step 8: 创建 Pull Request | 786 | # 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; fi | 797 | 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; fi | 800 | + if [[ "${PR_PAGE_LEN:-0}" -lt 100 ]]; then break; fi |
| 810 | PR_PAGE=$((PR_PAGE + 1)) | 801 | PR_PAGE=$((PR_PAGE + 1)) |
| 811 | done | 802 | done |
| 812 | if [[ -n "$EXISTING_PR_BODY" ]]; then | 803 | if [[ -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) || true | 866 | 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"; then | 867 | 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" ]]; then | 874 | 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}" |