diff --git a/bin/cmd.js b/bin/cmd.js index 7ed0549..79ad07a 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -import { exec } from 'node:child_process' +import { execFile } from 'node:child_process' import fs from 'node:fs' import http from 'node:http' import https from 'node:https' @@ -76,10 +76,11 @@ function load (sha, cb) { if (parsed != null) { return loadPatch(parsed, cb) } - exec(`git show --quiet --format=medium ${sha}`, (err, stdout, stderr) => { - if (err) return cb(err) - cb(null, stdout.trim()) - }) + execFile('git', ['show', '--no-mailmap', '--quiet', '--format=medium', sha], + (err, stdout, stderr) => { + if (err) return cb(err) + cb(null, stdout.trim()) + }) } function loadPatch (uri, cb) { diff --git a/test/cli-test.js b/test/cli-test.js index bcaea29..11128e2 100644 --- a/test/cli-test.js +++ b/test/cli-test.js @@ -1,8 +1,21 @@ import { test } from 'tap' -import { readFileSync } from 'node:fs' -import { spawn } from 'node:child_process' +import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs' +import { execFileSync, spawn } from 'node:child_process' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' import subsystems from '../lib/rules/subsystem.js' +const cmdPath = fileURLToPath(new URL('../bin/cmd.js', import.meta.url)) + +function git (cwd, args, env = undefined) { + return execFileSync('git', args, { + cwd, + env: env === undefined ? process.env : { ...process.env, ...env }, + encoding: 'utf8' + }) +} + test('Test cli flags', (t) => { t.test('test list-subsystems', (tt) => { const ls = spawn('./bin/cmd.js', ['--list-subsystems'], { @@ -358,5 +371,67 @@ test('Test cli flags', (t) => { }) }) + t.test('test sha ignores mailmap when validating sign-off', (tt) => { + tt.plan(6) + + const repoDir = mkdtempSync(join(tmpdir(), 'core-validate-commit-')) + const env = { + GIT_AUTHOR_NAME: 'Matteo Collina', + GIT_AUTHOR_EMAIL: 'hello@matteocollina.com', + GIT_COMMITTER_NAME: 'Matteo Collina', + GIT_COMMITTER_EMAIL: 'hello@matteocollina.com' + } + + git(repoDir, ['init', '-b', 'main']) + git(repoDir, ['config', 'user.name', 'Test User']) + git(repoDir, ['config', 'user.email', 'test@example.com']) + + writeFileSync(join(repoDir, '.mailmap'), + 'Matteo Collina \n') + writeFileSync(join(repoDir, 'README.md'), 'test\n') + + git(repoDir, ['add', '.mailmap', 'README.md']) + git(repoDir, ['commit', '-m', + 'doc: add mailmap sign-off fixture\n\n' + + 'Signed-off-by: Matteo Collina \n' + + 'PR-URL: https://github.com/nodejs/node/pull/1234\n' + + 'Reviewed-By: Rich Trott ' + ], env) + + const defaultShow = git(repoDir, ['show', '--quiet', '--format=medium', 'HEAD']) + const rawShow = git(repoDir, ['show', '--no-mailmap', '--quiet', '--format=medium', 'HEAD']) + + tt.match(defaultShow, + /Author:\s+Matteo Collina /, + 'git show applies mailmap by default') + tt.match(rawShow, + /Author:\s+Matteo Collina /, + 'git show --no-mailmap preserves the raw author email') + + const ls = spawn(process.execPath, [cmdPath, '--no-validate-metadata', 'HEAD'], { + cwd: repoDir, + env: { ...process.env, FORCE_COLOR: 0 } + }) + let compiledData = '' + let errorData = '' + + ls.stdout.on('data', (data) => { + compiledData += data + }) + + ls.stderr.on('data', (data) => { + errorData += data + }) + + ls.on('close', (code) => { + tt.equal(code, 0, 'CLI exits with zero code on success') + tt.equal(errorData, '', 'no error output') + tt.match(compiledData, /has valid Signed-off-by/, 'sign-off remains valid') + tt.notMatch(compiledData, + /email does not match the commit author email/, + 'mailmap does not trigger a false mismatch warning') + }) + }) + t.end() })