diff --git a/.gitea/workflows/backport-commoncore.yml b/.gitea/workflows/backport-commoncore.yml index 5f53da29..eee0e527 100644 --- a/.gitea/workflows/backport-commoncore.yml +++ b/.gitea/workflows/backport-commoncore.yml @@ -35,29 +35,53 @@ jobs: local method="$1" local path="$2" local body="${3:-}" + local response_file + local status + + response_file="$(mktemp)" if [[ -n "$body" ]]; then - curl --fail --silent --show-error \ + status="$(curl --silent --show-error \ -X "$method" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ --data "$body" \ - "${GITHUB_API_URL}${path}" + --output "$response_file" \ + --write-out "%{http_code}" \ + "${GITHUB_API_URL}${path}")" else - curl --fail --silent --show-error \ + status="$(curl --silent --show-error \ -X "$method" \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Accept: application/json" \ - "${GITHUB_API_URL}${path}" + --output "$response_file" \ + --write-out "%{http_code}" \ + "${GITHUB_API_URL}${path}")" fi + + if [[ "$status" -lt 200 || "$status" -ge 300 ]]; then + echo "Gitea API ${method} ${path} failed with HTTP ${status}" >&2 + cat "$response_file" >&2 + rm -f "$response_file" + return 1 + fi + + cat "$response_file" + rm -f "$response_file" } repo_path="/repos/${GITHUB_REPOSITORY}" + repo_owner="${GITHUB_REPOSITORY%%/*}" pr_number="$(jq -r '.number' "$GITHUB_EVENT_PATH")" pr_title="$(jq -r '.pull_request.title // ""' "$GITHUB_EVENT_PATH")" event_commit="$(jq -r '.pull_request.merge_commit_sha // .pull_request.merged_commit_id // .commit_id // env.GITHUB_SHA' "$GITHUB_EVENT_PATH")" + if jq -e '.pull_request.labels[]? | select(.name == "no-backport")' "$GITHUB_EVENT_PATH" >/dev/null; then + echo "PR #${pr_number} has label no-backport; skipping automatic backport." + exit 0 + fi + commoncore_files=() page=1 while true; do @@ -113,6 +137,41 @@ jobs: git cat-file -e "${commit}^{commit}" done + apply_commoncore_change() { + local commit="$1" + local parent + local patch_file + + if [[ "$(git rev-list --parents -n 1 "$commit" | wc -w)" -gt 2 ]]; then + parent="${commit}^1" + else + parent="${commit}^" + fi + + patch_file="$(mktemp)" + git diff --binary "$parent" "$commit" -- CommonCore/ > "$patch_file" + + if [[ ! -s "$patch_file" ]]; then + echo "Commit ${commit} has no CommonCore/ diff; skipping it." + rm -f "$patch_file" + return 0 + fi + + if git apply --reverse --check "$patch_file" >/dev/null 2>&1; then + echo "CommonCore/ diff from ${commit} is already present; skipping it." + rm -f "$patch_file" + return 0 + fi + + if git apply --3way --index "$patch_file"; then + rm -f "$patch_file" + return 0 + fi + + rm -f "$patch_file" + return 1 + } + mapfile -t version_branches < <( git for-each-ref --format='%(refname:short)' refs/remotes/origin/version | sed 's#^origin/##' | @@ -142,53 +201,53 @@ jobs: git checkout -B "$head_branch" "origin/${target_branch}" for commit in "${source_commits[@]}"; do - cherry_pick_args=(-x) - if [[ "$(git rev-list --parents -n 1 "$commit" | wc -w)" -gt 2 ]]; then - cherry_pick_args=(-x -m 1) - fi - - if git cherry-pick "${cherry_pick_args[@]}" "$commit"; then + if apply_commoncore_change "$commit"; then continue fi - if [[ -z "$(git status --porcelain)" ]]; then - echo "Cherry-pick of ${commit} produced no changes; skipping it." - git cherry-pick --skip || true - continue - fi - - git cherry-pick --abort || true - failures+=("${target_branch}: cherry-pick of ${commit} failed") - break + echo "Failed to apply CommonCore/ diff from ${commit} to ${target_branch}" >&2 + exit 1 done - if [[ "${failures[*]:-}" == *"${target_branch}:"* ]]; then + if git diff --cached --quiet; then + echo "${target_branch} already contains the CommonCore/ change; no PR needed." git checkout main continue fi - if git diff --quiet "origin/${target_branch}" HEAD; then - echo "${target_branch} already contains the change; no PR needed." - git checkout main - continue - fi + git commit -m "Backport CommonCore changes from #${pr_number}" \ + -m "Source PR: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pulls/${pr_number}" \ + -m "Only CommonCore/ changes are included in this backport." + + git push origin "HEAD:refs/heads/${head_branch}" --force-with-lease if [[ "${#source_commits[@]}" -eq 1 ]]; then source_text="Source commit: \`${source_commits[0]}\`" else source_text="Source commits:" for commit in "${source_commits[@]}"; do - source_text="${source_text} \`${commit}\`" + source_text="${source_text}"$'\n'"- \`${commit}\`" done fi - description="Automated backport of #${pr_number} to ${target_branch} because the source PR changed CommonCore/. Source PR: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pulls/${pr_number}. ${source_text}. This PR will be merged automatically by the Pull Request Build workflow after a successful build." + body="$(cat </dev/null echo "Created backport PR from ${head_branch} to ${target_branch}." git checkout main done