chore: import upstream snapshot with attribution
Auto Update PR / update-prs (push) Has been cancelled
CI / format-check (push) Has been cancelled
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / live-api-tests (push) Has been cancelled
CI / plugin-integration-test (push) Has been cancelled
CI / ollama-integration-test (push) Has been cancelled
CI / test-fork-pr (push) Has been cancelled
Auto Update PR / update-prs (push) Has been cancelled
CI / format-check (push) Has been cancelled
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / live-api-tests (push) Has been cancelled
CI / plugin-integration-test (push) Has been cancelled
CI / ollama-integration-test (push) Has been cancelled
CI / test-fork-pr (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
name: Validate PR template
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check PR author permissions
|
||||
id: check
|
||||
if: github.event_name == 'pull_request_target' && github.event.pull_request.draft == false
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
const {owner, repo} = context.repo;
|
||||
const actor = pr.user.login;
|
||||
const authorType = pr.user.type;
|
||||
|
||||
// Check if PR author is a bot (e.g., Dependabot)
|
||||
if (authorType === 'Bot') {
|
||||
core.setOutput('skip_validation', 'true');
|
||||
console.log(`Skipping validation for bot-authored PR: ${actor}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a community provider PR (only modifies COMMUNITY_PROVIDERS.md)
|
||||
const { data: files } = await github.rest.pulls.listFiles({
|
||||
owner, repo,
|
||||
pull_number: pr.number
|
||||
});
|
||||
|
||||
const isCommunityProviderPR = files.length === 1 &&
|
||||
files[0].filename === 'COMMUNITY_PROVIDERS.md';
|
||||
|
||||
if (isCommunityProviderPR) {
|
||||
core.setOutput('is_community_provider', 'true');
|
||||
console.log('Community provider PR detected - relaxed validation will apply');
|
||||
} else {
|
||||
core.setOutput('is_community_provider', 'false');
|
||||
}
|
||||
|
||||
// Get permission level
|
||||
try {
|
||||
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner, repo, username: actor
|
||||
});
|
||||
|
||||
const permission = data.permission; // admin|maintain|write|triage|read|none
|
||||
console.log(`Actor ${actor} has permission level: ${permission}`);
|
||||
|
||||
// Check if user has write+ permissions
|
||||
if (['admin', 'maintain', 'write'].includes(permission)) {
|
||||
core.setOutput('skip_validation', 'true');
|
||||
console.log(`Skipping validation for maintainer: ${actor} (${permission})`);
|
||||
} else {
|
||||
core.setOutput('skip_validation', 'false');
|
||||
console.log(`Validation required for: ${actor} (${permission})`);
|
||||
}
|
||||
} catch (e) {
|
||||
// If we can't determine permissions, require validation
|
||||
core.setOutput('skip_validation', 'false');
|
||||
core.warning(`Permission lookup failed: ${e.message}`);
|
||||
}
|
||||
|
||||
- name: Validate PR template
|
||||
if: |
|
||||
github.event_name == 'pull_request_target' &&
|
||||
github.event.pull_request.draft == false &&
|
||||
steps.check.outputs.skip_validation != 'true'
|
||||
env:
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
IS_COMMUNITY_PROVIDER: ${{ steps.check.outputs.is_community_provider }}
|
||||
run: |
|
||||
printf '%s\n' "$PR_BODY" | tr -d '\r' > body.txt
|
||||
|
||||
# Required sections from the template
|
||||
required=( "# Description" "# How Has This Been Tested?" "# Checklist" )
|
||||
err=0
|
||||
|
||||
# Check for required sections
|
||||
for h in "${required[@]}"; do
|
||||
grep -Fq "$h" body.txt || { echo "::error::$h missing"; err=1; }
|
||||
done
|
||||
|
||||
# Check for issue reference - relaxed for community provider PRs
|
||||
if [ "$IS_COMMUNITY_PROVIDER" = "true" ]; then
|
||||
# For community provider PRs, accept either "Fixes #" or "Related to #" (case-insensitive)
|
||||
if ! grep -Eiq '(Fixes #[0-9]+|Related to #[0-9]+)' body.txt; then
|
||||
echo "::error::Issue reference missing (need 'Fixes #NNN' or 'Related to #NNN')"
|
||||
err=1
|
||||
fi
|
||||
else
|
||||
# For other PRs, require "Fixes #" with a number
|
||||
if ! grep -Eq 'Fixes #[0-9]+' body.txt; then
|
||||
echo "::error::Missing 'Fixes #NNN' reference"
|
||||
err=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for placeholder text that should be replaced
|
||||
grep -Eiq 'Replace this with|Choose one:' body.txt && {
|
||||
echo "::error::Template placeholders still present"; err=1;
|
||||
}
|
||||
|
||||
# Also check for the unmodified issue number placeholder
|
||||
grep -Fq 'Fixes #[issue number]' body.txt && {
|
||||
echo "::error::Issue number placeholder not updated"; err=1;
|
||||
}
|
||||
|
||||
exit $err
|
||||
|
||||
- name: Log skip reason
|
||||
if: |
|
||||
github.event_name == 'pull_request_target' &&
|
||||
(github.event.pull_request.draft == true ||
|
||||
steps.check.outputs.skip_validation == 'true')
|
||||
run: |
|
||||
echo "Skipping PR template validation. Draft: ${{ github.event.pull_request.draft }}; skip_validation: ${{ steps.check.outputs.skip_validation || 'N/A' }}"
|
||||
Reference in New Issue
Block a user