bcquality/.github/workflows/flag-new-top-level.yml
dependabot[bot] 85b21c1499
Bump the github-actions group with 3 updates
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>
2026-08-14 08:25:49 +00:00

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}.`);