forked from molecule-ai/molecule-core
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
155 lines
6.8 KiB
YAML
155 lines
6.8 KiB
YAML
name: Block internal-flavored paths
|
|
|
|
# Hard CI gate. Internal content (positioning, competitive briefs, sales
|
|
# playbooks, PMM/press drip, draft campaigns) lives in Molecule-AI/internal —
|
|
# this public monorepo must never re-acquire those paths. CEO directive
|
|
# 2026-04-23 after a fleet-wide audit found 79 internal files leaked here.
|
|
#
|
|
# Failure mode without this gate: agents (PMM, Research, DevRel, Sales) drop
|
|
# briefs into the easiest path their cwd resolves to (root /research,
|
|
# /marketing, /docs/marketing) and gitignore alone won't catch a `git add -f`
|
|
# or a stale gitignore line. This workflow is the mechanical backstop.
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened]
|
|
push:
|
|
branches: [main, staging]
|
|
# Required for GitHub merge queue: the queue's pre-merge CI run on
|
|
# `gh-readonly-queue/...` refs needs this check to fire so the queue
|
|
# gets a real result instead of stalling forever AWAITING_CHECKS.
|
|
merge_group:
|
|
types: [checks_requested]
|
|
|
|
jobs:
|
|
check:
|
|
name: Block forbidden paths
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
fetch-depth: 2 # need previous commit to diff against on push events
|
|
|
|
# For pull_request events the diff base is github.event.pull_request.base.sha,
|
|
# which may be many commits behind HEAD and therefore absent from the
|
|
# shallow clone above. Fetch it explicitly (depth=1 keeps it fast).
|
|
- name: Fetch PR base SHA (pull_request events only)
|
|
if: github.event_name == 'pull_request'
|
|
run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
|
|
|
|
# For merge_group events the queue's pre-merge ref is a commit on
|
|
# `gh-readonly-queue/...` whose parent is the queue's base_sha.
|
|
# That parent isn't part of the queue branch's shallow clone, so
|
|
# we fetch it explicitly. Mirrors the equivalent step in
|
|
# secret-scan.yml (#2120) — same shallow-clone bug class.
|
|
- name: Fetch merge_group base SHA (merge_group events only)
|
|
if: github.event_name == 'merge_group'
|
|
run: git fetch --depth=1 origin ${{ github.event.merge_group.base_sha }}
|
|
|
|
- name: Refuse if forbidden paths appear
|
|
env:
|
|
# Plumb event-specific SHAs through env so the script doesn't
|
|
# need conditional `${{ ... }}` interpolation per event type.
|
|
# github.event.before/after only exist on push events;
|
|
# merge_group has its own base_sha/head_sha; pull_request has
|
|
# pull_request.base.sha / pull_request.head.sha.
|
|
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
MG_BASE_SHA: ${{ github.event.merge_group.base_sha }}
|
|
MG_HEAD_SHA: ${{ github.event.merge_group.head_sha }}
|
|
PUSH_BEFORE: ${{ github.event.before }}
|
|
PUSH_AFTER: ${{ github.event.after }}
|
|
run: |
|
|
# Paths that must NEVER live in the public monorepo. Add to this
|
|
# list narrowly — broader patterns belong in .gitignore so day-to-day
|
|
# docs work isn't accidentally blocked.
|
|
FORBIDDEN_PATTERNS=(
|
|
"^research/"
|
|
"^marketing/"
|
|
"^docs/marketing/"
|
|
"^comment-[0-9]+\.json$"
|
|
"^test-pmm.*\.(txt|md)$"
|
|
"^tick-reflections.*\.(txt|md)$"
|
|
".*-temp\.(md|txt)$"
|
|
)
|
|
|
|
# Determine the diff base. Each event type stores its SHAs in
|
|
# a different place — see the env block above.
|
|
case "${{ github.event_name }}" in
|
|
pull_request)
|
|
BASE="$PR_BASE_SHA"
|
|
HEAD="$PR_HEAD_SHA"
|
|
;;
|
|
merge_group)
|
|
BASE="$MG_BASE_SHA"
|
|
HEAD="$MG_HEAD_SHA"
|
|
;;
|
|
*)
|
|
BASE="$PUSH_BEFORE"
|
|
HEAD="$PUSH_AFTER"
|
|
;;
|
|
esac
|
|
|
|
# On push events with shallow clones, BASE may be present in
|
|
# the event payload but absent from the local object DB
|
|
# (fetch-depth=2 doesn't always reach the previous commit
|
|
# across true merges). Try fetching it on demand. If the
|
|
# fetch fails — e.g. the SHA was force-overwritten — we fall
|
|
# through to the empty-BASE branch below, which scans the
|
|
# entire tree as if every file were new. Correct, just slow.
|
|
# Same recovery shape as secret-scan.yml (#2120 — incident
|
|
# 2026-04-27 06:50Z block-internal-paths exit 128 with
|
|
# "fatal: bad object <sha>" on staging push).
|
|
if [ -n "$BASE" ] && ! echo "$BASE" | grep -qE '^0+$'; then
|
|
if ! git cat-file -e "$BASE" 2>/dev/null; then
|
|
git fetch --depth=1 origin "$BASE" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Files added or modified in this change.
|
|
if [ -z "$BASE" ] || echo "$BASE" | grep -qE '^0+$' || ! git cat-file -e "$BASE" 2>/dev/null; then
|
|
# New branch / no previous SHA / BASE unreachable — check
|
|
# the entire tree as if every file were new. Slower but
|
|
# correct on first push or post-fetch-failure recovery.
|
|
CHANGED=$(git ls-tree -r --name-only HEAD)
|
|
else
|
|
CHANGED=$(git diff --name-only --diff-filter=AM "$BASE" "$HEAD")
|
|
fi
|
|
|
|
if [ -z "$CHANGED" ]; then
|
|
echo "No changed files to inspect."
|
|
exit 0
|
|
fi
|
|
|
|
OFFENDING=""
|
|
for path in $CHANGED; do
|
|
for pattern in "${FORBIDDEN_PATTERNS[@]}"; do
|
|
if echo "$path" | grep -qE "$pattern"; then
|
|
OFFENDING="${OFFENDING}${path} (matched: ${pattern})\n"
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [ -n "$OFFENDING" ]; then
|
|
echo "::error::Forbidden internal-flavored paths detected:"
|
|
printf "$OFFENDING"
|
|
echo ""
|
|
echo "These paths belong in Molecule-AI/internal, not this public repo."
|
|
echo "See docs/internal-content-policy.md for canonical locations."
|
|
echo ""
|
|
echo "If your file is genuinely public-facing (e.g. a blog post"
|
|
echo "ready to ship), use one of these alternatives instead:"
|
|
echo " • Public-bound blog posts: docs/blog/<slug>.md"
|
|
echo " • Public-bound tutorials: docs/tutorials/<slug>.md"
|
|
echo " • Public devrel content: docs/devrel/<slug>.md"
|
|
echo ""
|
|
echo "If you legitimately need to add a new top-level path that"
|
|
echo "happens to match a forbidden pattern, edit"
|
|
echo ".github/workflows/block-internal-paths.yml and update the"
|
|
echo "FORBIDDEN_PATTERNS list with reviewer signoff."
|
|
exit 1
|
|
fi
|
|
|
|
echo "✓ No forbidden paths in this change."
|