mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Bumps the github-actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/github-script](https://github.com/actions/github-script) and [actions/setup-python](https://github.com/actions/setup-python). Updates `actions/checkout` from 4.4.0 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](11d5960a32...3d3c42e5aa) Updates `actions/github-script` from 7.1.0 to 9.0.0 - [Release notes](https://github.com/actions/github-script/releases) - [Commits](f28e40c7f3...3a2844b7e9) Updates `actions/setup-python` from 5.6.0 to 7.0.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](a26af69be9...5fda3b95a4) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/github-script dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-python dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com>
108 lines
4.1 KiB
YAML
108 lines
4.1 KiB
YAML
name: Flag new top-level entries
|
|
|
|
# BCQuality keeps a deliberately small repository root. New top-level folders
|
|
# or files are almost always unintended — a stray export, a tool's scratch
|
|
# directory, or content that meant to land inside an existing layer (e.g.
|
|
# /community/knowledge/). PR #55 leaked exactly this kind of stray folder.
|
|
#
|
|
# Unlike the custom-layer guard, this workflow does NOT close the PR. It only
|
|
# posts a single advisory comment so a maintainer (and the author) can eyeball
|
|
# the addition. It reads the PR's file LIST via the API and never checks out or
|
|
# runs PR code.
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, reopened, synchronize]
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
issues: write
|
|
|
|
jobs:
|
|
flag:
|
|
if: github.repository == 'microsoft/BCQuality'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Check out repository
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
sparse-checkout: |
|
|
.github/new-top-level-flag.md
|
|
sparse-checkout-cone-mode: false
|
|
|
|
- name: Flag unexpected new top-level entries
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
|
with:
|
|
script: |
|
|
const fs = require('fs');
|
|
|
|
// Known, intended repository root. Anything else added at the root
|
|
// is flagged for a human to eyeball.
|
|
const ALLOWED_DIRS = new Set([
|
|
'.claude-plugin', '.github', 'community', 'custom', 'microsoft', 'skills', 'tools',
|
|
]);
|
|
const ALLOWED_FILES = new Set([
|
|
'.gitignore', 'CODEOWNERS', 'LICENSE', 'README.md',
|
|
'SECURITY.md', 'agent-consumption.md',
|
|
]);
|
|
|
|
const MARKER = '<!-- guard:new-top-level -->';
|
|
const { owner, repo } = context.repo;
|
|
const prNumber = context.payload.pull_request.number;
|
|
|
|
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
owner, repo, pull_number: prNumber, per_page: 100,
|
|
});
|
|
|
|
// Only consider newly-added paths — a new top-level entry can only
|
|
// appear via an added file.
|
|
const added = files
|
|
.filter((f) => f.status === 'added')
|
|
.map((f) => f.filename);
|
|
|
|
const newDirs = new Set();
|
|
const newFiles = new Set();
|
|
for (const p of added) {
|
|
const slash = p.indexOf('/');
|
|
if (slash === -1) {
|
|
// Top-level file.
|
|
if (!ALLOWED_FILES.has(p)) newFiles.add(p);
|
|
} else {
|
|
// Top-level directory.
|
|
const dir = p.slice(0, slash);
|
|
if (!ALLOWED_DIRS.has(dir)) newDirs.add(dir);
|
|
}
|
|
}
|
|
|
|
if (newDirs.size === 0 && newFiles.size === 0) {
|
|
core.info('No unexpected new top-level entries. Nothing to flag.');
|
|
return;
|
|
}
|
|
|
|
// Idempotency: don't re-flag on every synchronize.
|
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
owner, repo, issue_number: prNumber, per_page: 100,
|
|
});
|
|
if (comments.some((c) => c.body && c.body.includes(MARKER))) {
|
|
core.info('Already flagged on this PR. Skipping duplicate comment.');
|
|
return;
|
|
}
|
|
|
|
const lines = [];
|
|
for (const d of [...newDirs].sort()) lines.push(`- 📁 \`${d}/\` (new top-level folder)`);
|
|
for (const f of [...newFiles].sort()) lines.push(`- 📄 \`${f}\` (new top-level file)`);
|
|
const entries = lines.join('\n');
|
|
|
|
core.warning(`Unexpected new top-level entries: ${[...newDirs, ...newFiles].join(', ')}`);
|
|
|
|
let body = fs.readFileSync('.github/new-top-level-flag.md', 'utf8');
|
|
body = body
|
|
.replace(/{{AUTHOR}}/g, context.payload.pull_request.user.login)
|
|
.replace(/{{ENTRIES}}/g, entries);
|
|
|
|
await github.rest.issues.createComment({
|
|
owner, repo, issue_number: prNumber, body,
|
|
});
|
|
|
|
core.info(`Flagged PR #${prNumber}.`);
|