Skip to content

Bump lodash from 4.17.23 to 4.18.1 #3

Bump lodash from 4.17.23 to 4.18.1

Bump lodash from 4.17.23 to 4.18.1 #3

name: Copyright Validation
on:
# Direct trigger for org-level repository rulesets — works for PRs from forks
# since pull_request_target runs with base repo context. The GITHUB_TOKEN is
# explicitly scoped to least privilege in the job permissions block below.
# Fork code is only read as text by the trusted copyrightcheck.py script;
# no fork code is executed. Worst case from a malicious .copyrightconfig
# is the check passes (policy bypass), not code execution.
pull_request_target:
types: [opened, edited, synchronize, reopened]
# Also support being called as a reusable workflow from individual repos
workflow_call:
jobs:
copyright-check:
name: Validate Copyright Headers
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
# issues: write is needed for the PR comment step (workflow_call path only).
# pull_request_target skips that step, but GitHub Actions has no per-event
# conditional permissions within a single job — splitting into two jobs would
# require artifact sharing and add significant complexity for minimal gain.
issues: write
steps:
- name: Checkout PR head
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: refs/pull/${{ github.event.pull_request.number }}/head
path: target-repo
persist-credentials: false
- name: Checkout pr-workflows repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
repository: ${{ github.repository_owner }}/pr-workflows
ref: main
path: pr-workflows
persist-credentials: false
- name: Setup config
id: setup-config
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
cfg="target-repo/.copyrightconfig"
if [ ! -f "$cfg" ]; then
git clone --depth 1 --branch "$BASE_REF" \
https://x-access-token:${GH_TOKEN}@github.com/${GH_REPO}.git base-repo
[ -f base-repo/.copyrightconfig ] && cfg="base-repo/.copyrightconfig"
fi
[ -f "$cfg" ] || { echo "missing config"; exit 1; }
[ -s "$cfg" ] || { echo "empty config"; exit 1; }
grep -q '^startyear:' "$cfg" || { echo "startyear missing"; exit 1; }
echo "config-file=$cfg" >> $GITHUB_OUTPUT
- name: Set up Python
uses: actions/setup-python@7f4fc3e22c37d6ff65e88745f38bd3157c663f7c # v4
with:
python-version: '3.11'
- name: Get changed files
id: changed-files
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
gh api "repos/${GH_REPO}/pulls/${PR_NUMBER}/files" --paginate \
--jq '.[].filename' | while IFS= read -r f; do
if [ -f "target-repo/$f" ]; then echo "$f"; fi
done > files_to_check.txt
count=$(wc -l < files_to_check.txt | tr -d ' ')
if [ "$count" -eq 0 ]; then echo "skip-validation=true" >> $GITHUB_OUTPUT; else echo "skip-validation=false" >> $GITHUB_OUTPUT; fi
echo "files-count=$count" >> $GITHUB_OUTPUT
- name: Validate
id: validate
if: steps.changed-files.outputs.skip-validation != 'true'
continue-on-error: true
env:
CONFIG_FILE: ${{ steps.setup-config.outputs.config-file }}
run: |
script="pr-workflows/scripts/copyrightcheck.py"
cfg="$CONFIG_FILE"
[ -f "$script" ] || { echo "script missing"; exit 1; }
chmod +x "$script"
export COPYRIGHT_CHECK_COMMIT_SHA=$(git -C target-repo rev-parse HEAD)
python3 "$script" --config "$cfg" --working-dir target-repo \
--files-from-stdin < files_to_check.txt > validation_output.txt 2>&1 \
&& echo "status=success" >> "$GITHUB_OUTPUT" \
|| { echo "status=failed" >> "$GITHUB_OUTPUT"; exit 1; }
- name: Extract Markdown summary
if: always() && steps.changed-files.outputs.skip-validation != 'true'
run: |
awk '/^<<<COPYRIGHT-CHECK:MARKDOWN>>>/{f=1;next}/^<<<END COPYRIGHT-CHECK:MARKDOWN>>>/{f=0}f' validation_output.txt > summary.md
echo '----- BEGIN COPYRIGHT SUMMARY (also in Job Summary) -----'
cat summary.md
echo '----- END COPYRIGHT SUMMARY -----'
cat summary.md >> "$GITHUB_STEP_SUMMARY"
- name: Post / Update PR comment with summary
id: pr-comment
# workflow_call: token is scoped to the calling repo — write access works.
# pull_request_target (org ruleset): token is read-only for the triggering repo —
# createComment/updateComment will 403. Skip and rely on Job Summary instead.
if: always() && steps.changed-files.outputs.skip-validation != 'true' && github.event_name == 'workflow_call'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
env:
VALIDATION_STATUS: ${{ steps.validate.outputs.status }}
with:
script: |
const fs = require('fs');
try {
if (!context.payload.pull_request) { core.warning('No pull_request context; skipping comment.'); core.setOutput('comment_action','unavailable'); return; }
let mdRaw='';
try { mdRaw = fs.readFileSync('summary.md','utf8'); } catch(e){ core.warning('summary.md not found'); core.setOutput('comment_action','unavailable'); return; }
if (!mdRaw.endsWith('\n')) mdRaw += '\n';
if (!mdRaw.includes('<!-- COPYRIGHT-CHECK-COMMENT:')) mdRaw += '<!-- COPYRIGHT-CHECK-COMMENT: v1 -->\n';
const isValid = process.env.VALIDATION_STATUS === 'success';
const number = context.payload.pull_request.number;
const { data: comments } = await github.rest.issues.listComments({ owner: context.repo.owner, repo: context.repo.repo, issue_number: number, per_page: 100 });
const existing = comments.find(c => (c.body||'').includes('<!-- COPYRIGHT-CHECK-COMMENT:'));
if (existing) {
// Updating an existing comment does not send an email notification.
await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: existing.id, body: mdRaw });
core.setOutput('comment_action','updated');
} else if (!isValid) {
await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: number, body: mdRaw });
core.setOutput('comment_action','created');
} else {
core.info('Copyright headers are valid and no prior comment exists; skipping comment to avoid email notification.');
core.setOutput('comment_action','skipped');
}
} catch(e) {
core.error(`Failed to create/update PR comment: ${e.status || ''} ${e.message}`);
if (e.response) core.error(`Response: ${JSON.stringify(e.response.data).slice(0,400)}`);
core.setOutput('comment_action','unavailable');
}
- name: Check for validation errors
if: always() && steps.changed-files.outputs.skip-validation != 'true'
env:
COMMENT_ACTION: ${{ steps.pr-comment.outputs.comment_action }}
VALIDATION_STATUS: ${{ steps.validate.outputs.status }}
run: |
if [ "$VALIDATION_STATUS" != "success" ]; then
if [ "$COMMENT_ACTION" = "updated" ] || [ "$COMMENT_ACTION" = "created" ]; then
echo "::error title=Copyright Validation Failed::See the $COMMENT_ACTION PR comment for detailed results.";
else
echo "::error title=Copyright Validation Failed::Copyright headers are missing or invalid — see the Job Summary for details.";
fi
exit 1
fi
- name: No-op summary
if: steps.changed-files.outputs.skip-validation == 'true'
run: echo "::notice title=Copyright Check::No files to validate"