mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-15 13:46:54 +00:00
Migrate pull request automation away from pull_request_target (#2625)
* Migrate pull_request_target workflows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR duplicate check writer review Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix duplicate-check writer artifact handling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make PR duplicate check gh-aw compilable Configure the agentic workflow source to allow fork PR triggers with staged safe outputs, upload a PR context artifact through supported post-steps, and have the workflow_run writer consume that context before publishing validated comments. This lets gh-aw regenerate the lockfile without restoring pull_request_target or privileged PR-code execution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 512eb347-ec89-4250-8bf1-87048974b01d * Harden workflow-run PR writers Bind privileged artifact processing to trusted workflow-run PR identity, serialize same-PR writers, and cap aggregate quality comments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 512eb347-ec89-4250-8bf1-87048974b01d --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 512eb347-ec89-4250-8bf1-87048974b01d
This commit is contained in:
committed by
GitHub
parent
db17698618
commit
925dc83735
@@ -1,29 +1,26 @@
|
||||
name: Contributor Reputation Check
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, edited, ready_for_review]
|
||||
issues:
|
||||
types: [opened, reopened, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
check:
|
||||
issue-check:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event_name == 'issues' &&
|
||||
github.actor != 'dependabot[bot]' &&
|
||||
github.actor != 'github-actions[bot]' &&
|
||||
github.actor != 'copilot-swe-agent[bot]'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
@@ -33,58 +30,48 @@ jobs:
|
||||
env:
|
||||
AGT_REF: v4.1.0
|
||||
run: |
|
||||
mkdir -p /tmp/agt
|
||||
mkdir -p "$RUNNER_TEMP/agt"
|
||||
curl -fsSL "https://raw.githubusercontent.com/microsoft/agent-governance-toolkit/${AGT_REF}/scripts/contributor_check.py" \
|
||||
-o /tmp/agt/contributor_check.py
|
||||
-o "$RUNNER_TEMP/agt/contributor_check.py"
|
||||
curl -fsSL "https://raw.githubusercontent.com/microsoft/agent-governance-toolkit/${AGT_REF}/scripts/credential_audit.py" \
|
||||
-o /tmp/agt/credential_audit.py
|
||||
|
||||
- name: Determine author
|
||||
id: author
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
|
||||
echo "username=${{ github.event.pull_request.user.login }}" >> "$GITHUB_OUTPUT"
|
||||
echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
|
||||
echo "type=pr" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "username=${{ github.event.issue.user.login }}" >> "$GITHUB_OUTPUT"
|
||||
echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT"
|
||||
echo "type=issue" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
-o "$RUNNER_TEMP/agt/credential_audit.py"
|
||||
|
||||
- name: Run profile check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
USERNAME: ${{ github.event.issue.user.login }}
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/contributor-check"
|
||||
set +e
|
||||
python3 /tmp/agt/contributor_check.py \
|
||||
--username "${{ steps.author.outputs.username }}" \
|
||||
python3 "$RUNNER_TEMP/agt/contributor_check.py" \
|
||||
--username "$USERNAME" \
|
||||
--repo "${{ github.repository }}" \
|
||||
--json > /tmp/profile.json 2>/tmp/profile.log
|
||||
--json > "$RUNNER_TEMP/contributor-check/profile.json" 2>"$RUNNER_TEMP/contributor-check/profile.log"
|
||||
status=$?
|
||||
set -e
|
||||
if [ "$status" -ne 0 ] && [ ! -s /tmp/profile.json ]; then
|
||||
if [ "$status" -ne 0 ] && [ ! -s "$RUNNER_TEMP/contributor-check/profile.json" ]; then
|
||||
echo "::warning::Profile check failed"
|
||||
if [ -s /tmp/profile.log ]; then
|
||||
sed -n '1,120p' /tmp/profile.log
|
||||
if [ -s "$RUNNER_TEMP/contributor-check/profile.log" ]; then
|
||||
sed -n '1,120p' "$RUNNER_TEMP/contributor-check/profile.log"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Run credential audit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
USERNAME: ${{ github.event.issue.user.login }}
|
||||
run: |
|
||||
set +e
|
||||
python3 /tmp/agt/credential_audit.py \
|
||||
--username "${{ steps.author.outputs.username }}" \
|
||||
python3 "$RUNNER_TEMP/agt/credential_audit.py" \
|
||||
--username "$USERNAME" \
|
||||
--repo "${{ github.repository }}" \
|
||||
--json > /tmp/cred.json 2>/tmp/cred.log
|
||||
--json > "$RUNNER_TEMP/contributor-check/cred.json" 2>"$RUNNER_TEMP/contributor-check/cred.log"
|
||||
status=$?
|
||||
set -e
|
||||
if [ "$status" -ne 0 ] && [ ! -s /tmp/cred.json ]; then
|
||||
if [ "$status" -ne 0 ] && [ ! -s "$RUNNER_TEMP/contributor-check/cred.json" ]; then
|
||||
echo "::warning::Credential audit failed"
|
||||
if [ -s /tmp/cred.log ]; then
|
||||
sed -n '1,120p' /tmp/cred.log
|
||||
if [ -s "$RUNNER_TEMP/contributor-check/cred.log" ]; then
|
||||
sed -n '1,120p' "$RUNNER_TEMP/contributor-check/cred.log"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -115,8 +102,8 @@ jobs:
|
||||
fi
|
||||
}
|
||||
|
||||
dump_json "Profile check" /tmp/profile.json /tmp/profile.log
|
||||
dump_json "Credential audit" /tmp/cred.json /tmp/cred.log
|
||||
dump_json "Profile check" "$RUNNER_TEMP/contributor-check/profile.json" "$RUNNER_TEMP/contributor-check/profile.log"
|
||||
dump_json "Credential audit" "$RUNNER_TEMP/contributor-check/cred.json" "$RUNNER_TEMP/contributor-check/cred.log"
|
||||
|
||||
- name: Resolve check risks
|
||||
id: results
|
||||
@@ -154,8 +141,8 @@ jobs:
|
||||
esac
|
||||
}
|
||||
|
||||
profile_risk=$(extract_risk /tmp/profile.json UNKNOWN)
|
||||
credential_risk=$(extract_risk /tmp/cred.json UNKNOWN)
|
||||
profile_risk=$(extract_risk "$RUNNER_TEMP/contributor-check/profile.json" UNKNOWN)
|
||||
credential_risk=$(extract_risk "$RUNNER_TEMP/contributor-check/cred.json" UNKNOWN)
|
||||
|
||||
echo "profile=$profile_risk" >> "$GITHUB_OUTPUT"
|
||||
echo "credential=$credential_risk" >> "$GITHUB_OUTPUT"
|
||||
@@ -185,23 +172,21 @@ jobs:
|
||||
|
||||
- name: Sync risk comment
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
RISK: ${{ steps.overall.outputs.risk }}
|
||||
PROFILE_RISK: ${{ steps.results.outputs.profile }}
|
||||
CREDENTIAL_RISK: ${{ steps.results.outputs.credential }}
|
||||
run: |
|
||||
number="${{ steps.author.outputs.number }}"
|
||||
risk="${{ steps.overall.outputs.risk }}"
|
||||
profile="${{ steps.results.outputs.profile }}"
|
||||
cred="${{ steps.results.outputs.credential }}"
|
||||
marker="<!-- agt-contributor-check -->"
|
||||
comment_ids=$(
|
||||
gh api "repos/${{ github.repository }}/issues/$number/comments" --paginate \
|
||||
gh api "repos/${{ github.repository }}/issues/$NUMBER/comments" --paginate \
|
||||
| jq -r --arg marker "$marker" '.[] | select((.user.login // "") == "github-actions[bot]" and ((.body // "") | contains($marker))) | .id'
|
||||
)
|
||||
comment_id=$(printf "%s\n" "$comment_ids" | sed -n '1p')
|
||||
|
||||
if [ "$risk" != "MEDIUM" ] && [ "$risk" != "HIGH" ]; then
|
||||
if [ "$RISK" != "MEDIUM" ] && [ "$RISK" != "HIGH" ]; then
|
||||
if [ -n "$comment_id" ]; then
|
||||
# Keep one canonical comment thread by removing all matching comments
|
||||
# when risk drops below MEDIUM.
|
||||
while IFS= read -r id; do
|
||||
[ -z "$id" ] && continue
|
||||
gh api --method DELETE "repos/${{ github.repository }}/issues/comments/$id" \
|
||||
@@ -211,16 +196,16 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$risk" = "HIGH" ]; then icon="🔴"; else icon="🟡"; fi
|
||||
if [ "$RISK" = "HIGH" ]; then icon="🔴"; else icon="🟡"; fi
|
||||
|
||||
body=$(cat <<EOF
|
||||
$marker
|
||||
$icon **Contributor Reputation Check: $risk risk**
|
||||
$icon **Contributor Reputation Check: $RISK risk**
|
||||
|
||||
| Check | Risk |
|
||||
|-------|------|
|
||||
| Profile | $profile |
|
||||
| Credential audit | $cred |
|
||||
| Profile | $PROFILE_RISK |
|
||||
| Credential audit | $CREDENTIAL_RISK |
|
||||
|
||||
Maintainers: please review this contributor before merging.
|
||||
See the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for full details.
|
||||
@@ -230,38 +215,36 @@ jobs:
|
||||
|
||||
if [ -n "$comment_id" ]; then
|
||||
gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$comment_id" -f body="$body"
|
||||
# Clean up any stale duplicates after updating the canonical comment.
|
||||
printf "%s\n" "$comment_ids" | sed '1d' | while IFS= read -r id; do
|
||||
[ -z "$id" ] && continue
|
||||
gh api --method DELETE "repos/${{ github.repository }}/issues/comments/$id" >/dev/null 2>&1 || true
|
||||
done
|
||||
else
|
||||
gh api --method POST "repos/${{ github.repository }}/issues/$number/comments" -f body="$body"
|
||||
gh api --method POST "repos/${{ github.repository }}/issues/$NUMBER/comments" -f body="$body"
|
||||
fi
|
||||
|
||||
- name: Sync risk label
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
RISK: ${{ steps.overall.outputs.risk }}
|
||||
run: |
|
||||
number="${{ steps.author.outputs.number }}"
|
||||
risk="${{ steps.overall.outputs.risk }}"
|
||||
|
||||
for label in needs-review:MEDIUM needs-review:HIGH; do
|
||||
if [ "$label" != "needs-review:$risk" ]; then
|
||||
gh api --method DELETE "repos/${{ github.repository }}/issues/$number/labels/$label" >/dev/null 2>&1 || true
|
||||
if [ "$label" != "needs-review:$RISK" ]; then
|
||||
gh api --method DELETE "repos/${{ github.repository }}/issues/$NUMBER/labels/$label" >/dev/null 2>&1 || true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$risk" != "MEDIUM" ] && [ "$risk" != "HIGH" ]; then
|
||||
if [ "$RISK" != "MEDIUM" ] && [ "$RISK" != "HIGH" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gh label create "needs-review:$risk" \
|
||||
--description "Contributor reputation check flagged $risk risk" \
|
||||
gh label create "needs-review:$RISK" \
|
||||
--description "Contributor reputation check flagged $RISK risk" \
|
||||
--color "FFA500" --force 2>/dev/null || true
|
||||
|
||||
gh api --method POST "repos/${{ github.repository }}/issues/$number/labels" \
|
||||
-f labels[]="needs-review:$risk" >/dev/null
|
||||
gh api --method POST "repos/${{ github.repository }}/issues/$NUMBER/labels" \
|
||||
-f labels[]="needs-review:$RISK" >/dev/null
|
||||
|
||||
- name: Job summary
|
||||
if: always()
|
||||
@@ -269,10 +252,187 @@ jobs:
|
||||
risk="${{ steps.overall.outputs.risk }}"
|
||||
case "$risk" in HIGH) icon="🔴" ;; MEDIUM) icon="🟡" ;; LOW) icon="✅" ;; *) icon="❓" ;; esac
|
||||
{
|
||||
echo "## $icon Contributor Check: \`${{ steps.author.outputs.username }}\`"
|
||||
echo "## $icon Contributor Check: \`${{ github.event.issue.user.login }}\`"
|
||||
echo "| Check | Risk |"
|
||||
echo "|-------|------|"
|
||||
echo "| Profile | ${{ steps.results.outputs.profile }} |"
|
||||
echo "| Credential | ${{ steps.results.outputs.credential }} |"
|
||||
echo "| **Overall** | **$risk** |"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
pr-check:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.actor != 'dependabot[bot]' &&
|
||||
github.actor != 'github-actions[bot]' &&
|
||||
github.actor != 'copilot-swe-agent[bot]'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Fetch AGT check scripts
|
||||
env:
|
||||
AGT_REF: v4.1.0
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/agt"
|
||||
curl -fsSL "https://raw.githubusercontent.com/microsoft/agent-governance-toolkit/${AGT_REF}/scripts/contributor_check.py" \
|
||||
-o "$RUNNER_TEMP/agt/contributor_check.py"
|
||||
curl -fsSL "https://raw.githubusercontent.com/microsoft/agent-governance-toolkit/${AGT_REF}/scripts/credential_audit.py" \
|
||||
-o "$RUNNER_TEMP/agt/credential_audit.py"
|
||||
|
||||
- name: Run profile check
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
USERNAME: ${{ github.event.pull_request.user.login }}
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/contributor-check"
|
||||
set +e
|
||||
python3 "$RUNNER_TEMP/agt/contributor_check.py" \
|
||||
--username "$USERNAME" \
|
||||
--repo "${{ github.repository }}" \
|
||||
--json > "$RUNNER_TEMP/contributor-check/profile.json" 2>"$RUNNER_TEMP/contributor-check/profile.log"
|
||||
status=$?
|
||||
set -e
|
||||
if [ "$status" -ne 0 ] && [ ! -s "$RUNNER_TEMP/contributor-check/profile.json" ]; then
|
||||
echo "::warning::Profile check failed"
|
||||
if [ -s "$RUNNER_TEMP/contributor-check/profile.log" ]; then
|
||||
sed -n '1,120p' "$RUNNER_TEMP/contributor-check/profile.log"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Run credential audit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
USERNAME: ${{ github.event.pull_request.user.login }}
|
||||
run: |
|
||||
set +e
|
||||
python3 "$RUNNER_TEMP/agt/credential_audit.py" \
|
||||
--username "$USERNAME" \
|
||||
--repo "${{ github.repository }}" \
|
||||
--json > "$RUNNER_TEMP/contributor-check/cred.json" 2>"$RUNNER_TEMP/contributor-check/cred.log"
|
||||
status=$?
|
||||
set -e
|
||||
if [ "$status" -ne 0 ] && [ ! -s "$RUNNER_TEMP/contributor-check/cred.json" ]; then
|
||||
echo "::warning::Credential audit failed"
|
||||
if [ -s "$RUNNER_TEMP/contributor-check/cred.log" ]; then
|
||||
sed -n '1,120p' "$RUNNER_TEMP/contributor-check/cred.log"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Resolve check risks
|
||||
id: results
|
||||
run: |
|
||||
extract_risk() {
|
||||
file="$1"
|
||||
fallback="$2"
|
||||
|
||||
if [ ! -s "$file" ]; then
|
||||
echo "$fallback"
|
||||
return
|
||||
fi
|
||||
|
||||
risk=$(
|
||||
jq -r '
|
||||
[
|
||||
.risk,
|
||||
.overall_risk,
|
||||
.overallRisk,
|
||||
.result.risk,
|
||||
.result.overall_risk,
|
||||
.result.overallRisk
|
||||
]
|
||||
| map(select(. != null and . != ""))
|
||||
| .[0] // empty
|
||||
' "$file" 2>/dev/null \
|
||||
| tr "[:lower:]" "[:upper:]" \
|
||||
| tr -d "\r"
|
||||
)
|
||||
|
||||
case "$risk" in
|
||||
HIGH|MEDIUM|LOW|NONE|UNKNOWN) echo "$risk" ;;
|
||||
"") echo "$fallback" ;;
|
||||
*) echo "$fallback" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
profile_risk=$(extract_risk "$RUNNER_TEMP/contributor-check/profile.json" UNKNOWN)
|
||||
credential_risk=$(extract_risk "$RUNNER_TEMP/contributor-check/cred.json" UNKNOWN)
|
||||
|
||||
echo "profile=$profile_risk" >> "$GITHUB_OUTPUT"
|
||||
echo "credential=$credential_risk" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Compute overall risk
|
||||
id: overall
|
||||
run: |
|
||||
risk_to_num() {
|
||||
case "$1" in
|
||||
HIGH) echo 3 ;;
|
||||
MEDIUM) echo 2 ;;
|
||||
LOW|NONE) echo 1 ;;
|
||||
UNKNOWN|"") echo 0 ;;
|
||||
*) echo 0 ;;
|
||||
esac
|
||||
}
|
||||
p=$(risk_to_num "${{ steps.results.outputs.profile }}")
|
||||
c=$(risk_to_num "${{ steps.results.outputs.credential }}")
|
||||
max=$p; [ "$c" -gt "$max" ] && max=$c
|
||||
case "$max" in
|
||||
3) r="HIGH" ;;
|
||||
2) r="MEDIUM" ;;
|
||||
1) r="LOW" ;;
|
||||
*) r="UNKNOWN" ;;
|
||||
esac
|
||||
echo "risk=$r" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Write PR result artifact
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
USERNAME: ${{ github.event.pull_request.user.login }}
|
||||
PROFILE_RISK: ${{ steps.results.outputs.profile }}
|
||||
CREDENTIAL_RISK: ${{ steps.results.outputs.credential }}
|
||||
OVERALL_RISK: ${{ steps.overall.outputs.risk }}
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/contributor-check-result"
|
||||
jq -n \
|
||||
--arg schema_version "contributor-check-result/v1" \
|
||||
--arg event "pull_request" \
|
||||
--argjson pr_number "$PR_NUMBER" \
|
||||
--arg head_sha "$PR_HEAD_SHA" \
|
||||
--arg username "$USERNAME" \
|
||||
--arg profile_risk "$PROFILE_RISK" \
|
||||
--arg credential_risk "$CREDENTIAL_RISK" \
|
||||
--arg overall_risk "$OVERALL_RISK" \
|
||||
--arg run_id "$GITHUB_RUN_ID" \
|
||||
'{schema_version:$schema_version,event:$event,pr_number:$pr_number,head_sha:$head_sha,username:$username,profile_risk:$profile_risk,credential_risk:$credential_risk,overall_risk:$overall_risk,run_id:$run_id}' \
|
||||
> "$RUNNER_TEMP/contributor-check-result/result.json"
|
||||
|
||||
- name: Upload PR result artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: contributor-check-result
|
||||
path: ${{ runner.temp }}/contributor-check-result/result.json
|
||||
if-no-files-found: error
|
||||
retention-days: 3
|
||||
|
||||
- name: Job summary
|
||||
if: always()
|
||||
run: |
|
||||
risk="${{ steps.overall.outputs.risk }}"
|
||||
case "$risk" in HIGH) icon="🔴" ;; MEDIUM) icon="🟡" ;; LOW) icon="✅" ;; *) icon="❓" ;; esac
|
||||
{
|
||||
echo "## $icon Contributor Check: \`${{ github.event.pull_request.user.login }}\`"
|
||||
echo "| Check | Risk |"
|
||||
echo "|-------|------|"
|
||||
echo "| Profile | ${{ steps.results.outputs.profile }} |"
|
||||
echo "| Credential | ${{ steps.results.outputs.credential }} |"
|
||||
echo "| **Overall** | **$risk** |"
|
||||
echo ""
|
||||
echo "PR label/comment synchronization is handled by the workflow_run writer after PR state is re-fetched."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
Reference in New Issue
Block a user