mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-06 15:02:25 +03:00
Compare commits
1 Commits
fix/releas
...
chore/git-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12a3dbdc76 |
@@ -7,6 +7,7 @@ fi
|
||||
|
||||
# Cheap, deterministic local gates (re-enabled). Slower checks (i18n drift,
|
||||
# openapi coverage/security-tiers, env-doc sync) run in CI to keep commits fast.
|
||||
sh scripts/check/check-git-identity.sh
|
||||
npx lint-staged
|
||||
node scripts/check/check-docs-sync.mjs
|
||||
npm run check:any-budget:t11
|
||||
|
||||
75
scripts/check/check-git-identity.sh
Executable file
75
scripts/check/check-git-identity.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env sh
|
||||
# Guard de identidade de commit — previne misattribution de autoria.
|
||||
#
|
||||
# Contexto (ver .mailmap na raiz): este checkout já produziu DUAS janelas de
|
||||
# commits com autoria trocada, ambas por um override de identidade deixado para
|
||||
# trás por uma sessão automatizada:
|
||||
# 1. 2026-08-13..26 — nome "Xiangzhe" + e-mail de @backryun (237 commits)
|
||||
# 2. 2026-08-29..09-02 — nome "Markus Hartung" + e-mail do mantenedor (59 commits)
|
||||
#
|
||||
# Este gate NÃO impõe uma identidade única: contribuidores commitam normalmente
|
||||
# com a sua, e creditar um contribuidor via `--author` continua funcionando.
|
||||
# Ele bloqueia apenas as duas assinaturas do defeito:
|
||||
# (a) um COMMITTER que não é a identidade desta máquina (pega ambas as janelas);
|
||||
# (b) um AUTHOR com o e-mail do mantenedor sob o nome de outra pessoa;
|
||||
# (c) um e-mail explicitamente aposentado (`omniroute.legacyEmail`).
|
||||
#
|
||||
# Ativação — opcional e por máquina; sem ela o gate é inerte:
|
||||
# git config --global omniroute.expectedName "diegosouzapw"
|
||||
# git config --global omniroute.expectedEmail "8016841+diegosouzapw@users.noreply.github.com"
|
||||
# git config --global --add omniroute.legacyEmail "diegosouzapw@users.noreply.github.com"
|
||||
|
||||
expected_name=$(git config --get omniroute.expectedName 2>/dev/null)
|
||||
expected_email=$(git config --get omniroute.expectedEmail 2>/dev/null)
|
||||
legacy_emails=$(git config --get-all omniroute.legacyEmail 2>/dev/null)
|
||||
|
||||
# Sem configuração nesta máquina o gate não opina — contribuidores não são afetados.
|
||||
[ -z "$expected_email" ] && exit 0
|
||||
|
||||
an=$(git var GIT_AUTHOR_IDENT 2>/dev/null | sed 's/ <.*//')
|
||||
ae=$(git var GIT_AUTHOR_IDENT 2>/dev/null | sed 's/.*<//; s/>.*//')
|
||||
cn=$(git var GIT_COMMITTER_IDENT 2>/dev/null | sed 's/ <.*//')
|
||||
ce=$(git var GIT_COMMITTER_IDENT 2>/dev/null | sed 's/.*<//; s/>.*//')
|
||||
|
||||
fail=0
|
||||
|
||||
# (a) O COMMITTER é quem executa o commit — nesta máquina, sempre o dono dela.
|
||||
# Um override de identidade esquecido por uma sessão aparece exatamente aqui,
|
||||
# e foi o que passou despercebido nas duas janelas: em agosto NEM o nome NEM
|
||||
# o e-mail eram do mantenedor, então checar só o e-mail dele não bastaria.
|
||||
if [ "$ce" != "$expected_email" ] || { [ -n "$expected_name" ] && [ "$cn" != "$expected_name" ]; }; then
|
||||
echo "🛑 COMMITTER não é a identidade desta máquina: $cn <$ce>" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# (b) O AUTHOR pode ser um contribuidor (crédito via --author), mas nunca pode
|
||||
# carregar o e-mail do mantenedor sob o nome de outra pessoa.
|
||||
if [ -n "$expected_name" ] && [ "$ae" = "$expected_email" ] && [ "$an" != "$expected_name" ]; then
|
||||
echo "🛑 AUTHOR combina o e-mail do mantenedor com outro nome: $an <$ae>" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# (c) e-mails aposentados que já causaram misattribution.
|
||||
for legacy in $legacy_emails; do
|
||||
if [ "$ae" = "$legacy" ]; then
|
||||
echo "🛑 AUTHOR usa e-mail aposentado: $an <$ae>" >&2
|
||||
fail=1
|
||||
fi
|
||||
if [ "$ce" = "$legacy" ]; then
|
||||
echo "🛑 COMMITTER usa e-mail aposentado: $cn <$ce>" >&2
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
[ "$fail" = "0" ] && exit 0
|
||||
|
||||
cat >&2 <<MSG
|
||||
|
||||
Identidade esperada nesta máquina: $expected_name <$expected_email>
|
||||
Corrija com:
|
||||
git config --global user.name "$expected_name"
|
||||
git config --global user.email "$expected_email"
|
||||
Para creditar um contribuidor, use o E-MAIL DELE (nunca o seu):
|
||||
git commit --author="Nome <email-do-contribuidor>"
|
||||
MSG
|
||||
exit 1
|
||||
139
tests/unit/check-git-identity.test.ts
Normal file
139
tests/unit/check-git-identity.test.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
// Guards the commit-identity gate (scripts/check/check-git-identity.sh): a stale
|
||||
// identity override left behind by an automated session must not be able to sign
|
||||
// commits with the maintainer's e-mail under someone else's name.
|
||||
//
|
||||
// Two real incidents motivate this (see .mailmap at the repo root):
|
||||
// 1. 2026-08-13..26 — name "Xiangzhe" + @backryun's e-mail (237 commits)
|
||||
// 2. 2026-08-29..09-02 — name "Markus Hartung" + the maintainer's e-mail (59 commits)
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const SCRIPT_PATH = fileURLToPath(
|
||||
new URL("../../scripts/check/check-git-identity.sh", import.meta.url)
|
||||
);
|
||||
|
||||
const OWNER_NAME = "diegosouzapw";
|
||||
const OWNER_EMAIL = "8016841+diegosouzapw@users.noreply.github.com";
|
||||
const LEGACY_EMAIL = "diegosouzapw@users.noreply.github.com";
|
||||
|
||||
/** Runs the gate with a synthetic git identity. `configured` toggles the opt-in. */
|
||||
function runGate(
|
||||
identity: {
|
||||
authorName: string;
|
||||
authorEmail: string;
|
||||
committerName: string;
|
||||
committerEmail: string;
|
||||
},
|
||||
configured = true
|
||||
) {
|
||||
const env: Record<string, string> = {
|
||||
...process.env,
|
||||
GIT_AUTHOR_NAME: identity.authorName,
|
||||
GIT_AUTHOR_EMAIL: identity.authorEmail,
|
||||
GIT_COMMITTER_NAME: identity.committerName,
|
||||
GIT_COMMITTER_EMAIL: identity.committerEmail,
|
||||
};
|
||||
if (configured) {
|
||||
// GIT_CONFIG_* is inherited by every child `git` call the script makes,
|
||||
// unlike `git -c`, which would only apply to a single invocation.
|
||||
Object.assign(env, {
|
||||
GIT_CONFIG_COUNT: "3",
|
||||
GIT_CONFIG_KEY_0: "omniroute.expectedName",
|
||||
GIT_CONFIG_VALUE_0: OWNER_NAME,
|
||||
GIT_CONFIG_KEY_1: "omniroute.expectedEmail",
|
||||
GIT_CONFIG_VALUE_1: OWNER_EMAIL,
|
||||
GIT_CONFIG_KEY_2: "omniroute.legacyEmail",
|
||||
GIT_CONFIG_VALUE_2: LEGACY_EMAIL,
|
||||
});
|
||||
}
|
||||
const r = spawnSync("sh", [SCRIPT_PATH], { env, encoding: "utf8" });
|
||||
return { status: r.status, stderr: r.stderr ?? "" };
|
||||
}
|
||||
|
||||
const owner = {
|
||||
authorName: OWNER_NAME,
|
||||
authorEmail: OWNER_EMAIL,
|
||||
committerName: OWNER_NAME,
|
||||
committerEmail: OWNER_EMAIL,
|
||||
};
|
||||
|
||||
test("stays inert when the machine has not opted in", () => {
|
||||
// A contributor who cloned the repo must never be blocked by the maintainer's gate.
|
||||
const r = runGate(
|
||||
{
|
||||
authorName: "Some Contributor",
|
||||
authorEmail: "someone@example.com",
|
||||
committerName: "Some Contributor",
|
||||
committerEmail: "someone@example.com",
|
||||
},
|
||||
false
|
||||
);
|
||||
assert.equal(r.status, 0);
|
||||
});
|
||||
|
||||
test("accepts the maintainer's own identity", () => {
|
||||
assert.equal(runGate(owner).status, 0);
|
||||
});
|
||||
|
||||
test("rejects the maintainer's e-mail carrying another person's name", () => {
|
||||
const r = runGate({
|
||||
authorName: "Markus Hartung",
|
||||
authorEmail: OWNER_EMAIL,
|
||||
committerName: "Markus Hartung",
|
||||
committerEmail: OWNER_EMAIL,
|
||||
});
|
||||
assert.equal(r.status, 1);
|
||||
assert.match(r.stderr, /AUTHOR combina o e-mail do mantenedor/);
|
||||
assert.match(r.stderr, /COMMITTER não é a identidade desta máquina/);
|
||||
});
|
||||
|
||||
test("rejects the retired legacy e-mail — the 2026-08-29 window's exact signature", () => {
|
||||
const r = runGate({
|
||||
authorName: "Markus Hartung",
|
||||
authorEmail: LEGACY_EMAIL,
|
||||
committerName: "Markus Hartung",
|
||||
committerEmail: LEGACY_EMAIL,
|
||||
});
|
||||
assert.equal(r.status, 1);
|
||||
assert.match(r.stderr, /e-mail aposentado/);
|
||||
});
|
||||
|
||||
test("allows crediting a contributor through their OWN e-mail", () => {
|
||||
// `git commit --author="Name <their@email>"` is the sanctioned credit path and
|
||||
// must keep working — the gate targets the maintainer's e-mail, not the name.
|
||||
const r = runGate({
|
||||
authorName: "Markus Hartung",
|
||||
authorEmail: "mail@hartmark.se",
|
||||
committerName: OWNER_NAME,
|
||||
committerEmail: OWNER_EMAIL,
|
||||
});
|
||||
assert.equal(r.status, 0);
|
||||
});
|
||||
|
||||
test("rejects a committer that is not this machine's identity", () => {
|
||||
// The committer is whoever RAN the commit, so on the maintainer's machine it is
|
||||
// always them. A forgotten identity override surfaces here first.
|
||||
const r = runGate({
|
||||
authorName: OWNER_NAME,
|
||||
authorEmail: OWNER_EMAIL,
|
||||
committerName: "Bob.Hou",
|
||||
committerEmail: "houminxi@gmail.com",
|
||||
});
|
||||
assert.equal(r.status, 1);
|
||||
assert.match(r.stderr, /COMMITTER não é a identidade desta máquina/);
|
||||
});
|
||||
|
||||
test("rejects the 2026-08-13 window: neither name nor e-mail is the maintainer's", () => {
|
||||
// Name "Xiangzhe" (@xz-dev) + @backryun's e-mail. Checking only the maintainer's
|
||||
// e-mail would MISS this window entirely — hence the committer-identity rule.
|
||||
const r = runGate({
|
||||
authorName: "Xiangzhe",
|
||||
authorEmail: "bakryun0718@proton.me",
|
||||
committerName: "Xiangzhe",
|
||||
committerEmail: "bakryun0718@proton.me",
|
||||
});
|
||||
assert.equal(r.status, 1);
|
||||
assert.match(r.stderr, /COMMITTER não é a identidade desta máquina/);
|
||||
});
|
||||
Reference in New Issue
Block a user