chore(migration): per-repo gitignore/gitleaks/mailmap re-rooting
This commit is contained in:
parent
d4dab96134
commit
b59240ad32
3 changed files with 135 additions and 0 deletions
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env bash
|
||||
# Step 4 — Per-repo config re-rooting (.gitignore, .gitleaks.toml, .gitleaksignore, .mailmap).
|
||||
# Operates on an extracted repo in $WORK/<key>: the monorepo's root config does not travel with
|
||||
# filter-repo's --path (it lives at the monorepo root, F6), so we regenerate it per-repo:
|
||||
# - .gitignore : re-rooted from templates/gitignore.plugin.tmpl (plugins/*/ prefixes dropped; the
|
||||
# global STATE.md/*.local.md/OS patterns kept — C3).
|
||||
# - .gitleaks.toml : the root baseline, retitled per plugin (its allowlist path is already plugin-relative).
|
||||
# - .gitleaksignore : the matching false-positive fingerprint(s) from the root file, re-rooted by dropping
|
||||
# ONLY the `plugins/<key>/` path prefix and keeping the full `:rule-id:line` suffix (M7).
|
||||
# Plugins with no fingerprint get NO .gitleaksignore.
|
||||
# - .mailmap : copied verbatim (F6 — it does not travel with --path).
|
||||
# For the design-system target it also copies sync-design-system.mjs + test (Step 2) and
|
||||
# PLAYGROUND-MAINTENANCE.md into the repo root (playground-examples/ already travels via --path).
|
||||
# Idempotent. NULL push (D8) — operates only inside $WORK/<key>.
|
||||
#
|
||||
# Usage: 20-rehome-config.sh <target-key>
|
||||
set -euo pipefail
|
||||
|
||||
KEY="${1:-}"
|
||||
[ -n "$KEY" ] || { echo "usage: 20-rehome-config.sh <target-key>" >&2; exit 2; }
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)"
|
||||
TEMPLATE="$SCRIPT_DIR/templates/gitignore.plugin.tmpl"
|
||||
WORK="${WORK:-/tmp/polyrepo-migration}"
|
||||
DEST="$WORK/$KEY"
|
||||
|
||||
fail() { printf 'REHOME FAIL: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
# Self-heal: extract the target first if it is not present.
|
||||
if [ ! -d "$DEST/.git" ]; then
|
||||
echo " $KEY not extracted → running 10-extract.sh"
|
||||
WORK="$WORK" bash "$SCRIPT_DIR/10-extract.sh" "$KEY" >/dev/null
|
||||
fi
|
||||
[ -d "$DEST/.git" ] || fail "extract missing after self-heal: $DEST"
|
||||
[ -f "$TEMPLATE" ] || fail "gitignore template missing: $TEMPLATE"
|
||||
|
||||
# 1) .gitignore from the re-rooted template
|
||||
cp "$TEMPLATE" "$DEST/.gitignore"
|
||||
|
||||
# 2) .gitleaks.toml from the root baseline, retitled for the standalone repo
|
||||
if [ -f "$REPO_ROOT/.gitleaks.toml" ]; then
|
||||
sed "s/^title = .*/title = \"$KEY gitleaks config\"/" "$REPO_ROOT/.gitleaks.toml" > "$DEST/.gitleaks.toml"
|
||||
fi
|
||||
|
||||
# 3) .gitleaksignore — re-rooted fingerprints for this plugin (drop only the plugins/<key>/ prefix, M7)
|
||||
rm -f "$DEST/.gitleaksignore"
|
||||
if [ -f "$REPO_ROOT/.gitleaksignore" ]; then
|
||||
MATCHES="$(grep "^plugins/$KEY/" "$REPO_ROOT/.gitleaksignore" || true)"
|
||||
if [ -n "$MATCHES" ]; then
|
||||
{
|
||||
echo "# Re-rooted false-positive fingerprints (from the monorepo .gitleaksignore)"
|
||||
printf '%s\n' "$MATCHES" | sed "s#^plugins/$KEY/##"
|
||||
} > "$DEST/.gitleaksignore"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 4) .mailmap copied verbatim
|
||||
[ -f "$REPO_ROOT/.mailmap" ] && cp "$REPO_ROOT/.mailmap" "$DEST/.mailmap"
|
||||
|
||||
# 5) Design-system target: bring in the sync script + maintenance doc (§9 Q4)
|
||||
if [ "$KEY" = "playground-design-system" ]; then
|
||||
mkdir -p "$DEST/scripts"
|
||||
cp "$REPO_ROOT/scripts/sync-design-system.mjs" "$DEST/scripts/sync-design-system.mjs"
|
||||
cp "$REPO_ROOT/scripts/sync-design-system.test.mjs" "$DEST/scripts/sync-design-system.test.mjs"
|
||||
[ -f "$REPO_ROOT/shared/PLAYGROUND-MAINTENANCE.md" ] && \
|
||||
cp "$REPO_ROOT/shared/PLAYGROUND-MAINTENANCE.md" "$DEST/PLAYGROUND-MAINTENANCE.md"
|
||||
fi
|
||||
|
||||
GLI_STATE="absent"
|
||||
[ -f "$DEST/.gitleaksignore" ] && GLI_STATE="$(wc -l < "$DEST/.gitleaksignore" | tr -d ' ') line(s)"
|
||||
echo "REHOME OK $KEY → .gitignore + .gitleaks.toml + .mailmap; .gitleaksignore: $GLI_STATE"
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Step 4 test — re-rooted .gitignore, M7 gitleaks fingerprint, .mailmap copy, no-fingerprint plugin.
|
||||
// Pattern: plugins/config-audit/tests/lib/*.test.mjs. Runs against freshly extracted repos in $WORK.
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const SCRIPT = path.join(here, '20-rehome-config.sh');
|
||||
const WORK = process.env.WORK || '/tmp/polyrepo-migration';
|
||||
|
||||
function rehome(key) {
|
||||
const r = spawnSync('bash', [SCRIPT, key], { encoding: 'utf8', env: { ...process.env, WORK } });
|
||||
if (r.status !== 0) throw new Error(`rehome ${key} failed (${r.status}):\n${r.stdout}\n${r.stderr}`);
|
||||
return path.join(WORK, key);
|
||||
}
|
||||
|
||||
const llm = rehome('llm-security');
|
||||
const gh = rehome('graceful-handoff');
|
||||
|
||||
test('re-rooted .gitignore ignores .claude/ and carries no plugins/ prefix', () => {
|
||||
const gi = readFileSync(path.join(llm, '.gitignore'), 'utf8');
|
||||
assert.match(gi, /^\.claude\/$/m, '.gitignore should ignore a root-level .claude/');
|
||||
assert.ok(!gi.includes('plugins/'), '.gitignore must not contain a plugins/ prefix');
|
||||
});
|
||||
|
||||
test('llm-security .gitleaksignore fingerprint is re-rooted with rule-id retained (M7)', () => {
|
||||
const gli = readFileSync(path.join(llm, '.gitleaksignore'), 'utf8');
|
||||
assert.match(
|
||||
gli,
|
||||
/^examples\/malicious-skill-demo\/evil-project-health\/lib\/telemetry\.mjs:generic-api-key:18$/m,
|
||||
'fingerprint must be the exact re-rooted path:rule-id:line'
|
||||
);
|
||||
assert.ok(!gli.includes('plugins/llm-security/'), 'the plugins/llm-security/ prefix must be dropped');
|
||||
assert.ok(gli.includes(':generic-api-key:'), 'the rule-id must be retained (else the suppression breaks)');
|
||||
});
|
||||
|
||||
test('.mailmap is copied into the extract', () => {
|
||||
assert.ok(existsSync(path.join(llm, '.mailmap')), '.mailmap should exist in the extract');
|
||||
});
|
||||
|
||||
test('a plugin with no fingerprint (graceful-handoff) gets .gitleaks.toml but no .gitleaksignore', () => {
|
||||
assert.ok(existsSync(path.join(gh, '.gitleaks.toml')), 'graceful-handoff should have .gitleaks.toml');
|
||||
assert.ok(!existsSync(path.join(gh, '.gitleaksignore')), 'graceful-handoff should have no .gitleaksignore');
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Session state files (local only, not tracked)
|
||||
REMEMBER.md
|
||||
TODO.md
|
||||
ROADMAP.md
|
||||
STATE.md
|
||||
*.local.md
|
||||
|
||||
# Session directories (plans, research, execution progress)
|
||||
.claude/
|
||||
|
||||
# Session-generated reports (not release artifacts)
|
||||
reports/*-beskrivelse.*
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
Loading…
Add table
Add a link
Reference in a new issue