diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc4c9a..3465953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,14 @@ # Cambios -Los cambios de cada versión de `parche-cli` y de la Action `Pepo08/parche@v0`. En npm están la 0.1.1, la 0.5.1, de la 0.6.0 a la 0.7.0, la 0.7.2, la 0.7.3 y de la 0.7.5 en adelante. La 0.1.0 y de la 0.2.0 a la 0.5.0 no se publicaron en npm; lo que traían está incluido en la 0.5.1. La 0.7.1 y la 0.7.4 tampoco: salieron junto con la 0.7.2 y la 0.7.5. La fecha de cada versión es la del cambio; la de publicación figura en npm y en los [Releases](https://github.com/Pepo08/parche/releases). +Los cambios de cada versión de `parche-cli` y de la Action `Pepo08/parche@v0`. En npm están la 0.1.1, la 0.5.1, de la 0.6.0 a la 0.7.0, la 0.7.2, la 0.7.3, de la 0.7.5 a la 0.8.0 y de la 0.8.5 en adelante. La 0.1.0 y de la 0.2.0 a la 0.5.0 no se publicaron en npm; lo que traían está incluido en la 0.5.1. La 0.7.1 y la 0.7.4 tampoco: salieron junto con la 0.7.2 y la 0.7.5. De la 0.8.1 a la 0.8.4, tampoco: salieron juntas con la 0.8.5. La fecha de cada versión es la del cambio; la de publicación figura en npm y en los [Releases](https://github.com/Pepo08/parche/releases). +## 0.8.5 — 2026-11-04 + +`fix` y `pr` frenan con un flag que no conocen, antes de tocar nada. En npm, esta versión trae también todo lo de la 0.8.1 a la 0.8.4. + +### Corregido +- **`parche fix . --dry-run` escribía en el código sin avisar.** `--dry-run` es de `pr`, y la CLI aceptaba cualquier flag en cualquier comando. Ahora `fix` lo rechaza y dice qué usar: `parche scan` para ver qué cambiaría, o `parche pr --dry-run` para armar la rama sin abrir el PR. +- **Un flag mal escrito ya no pasa de largo en `fix` ni en `pr`.** `fix --pakc ` aplicaba todos los packs y `pr --dryrun` podía abrir el PR de verdad. Ahora frenan y listan las opciones. El resto de los comandos lee los flags como antes, y la Action no cambia. ## 0.8.4 — 2026-11-04 Mercado Pago: el checklist del PR deja de pedir un mail de prueba que la documentación oficial no pide. La detección y el arreglo no cambian. diff --git a/bin/parche.js b/bin/parche.js index 856d660..36e2870 100755 --- a/bin/parche.js +++ b/bin/parche.js @@ -50,6 +50,23 @@ function parseArgs(argv) { return args; } +// Los comandos que cambian algo: un flag que no conocen (`fix --dry-run`, que es de `pr`; `pr --dryrun`) frena antes de tocar nada. +const WRITE_FLAGS = { + fix: ['pack'], + pr: ['pack', 'dry-run', 'verify', 'push-remote', 'author'], +}; + +function checkFlags(cmd, args) { + const known = WRITE_FLAGS[cmd]; + const unknown = known ? Object.keys(args).filter((k) => k !== '_' && !known.includes(k)) : []; + if (!unknown.length) return; + if (cmd === 'fix' && unknown.includes('dry-run')) { + throw new Error('`parche fix` no tiene --dry-run: escribe en el código. No se tocó nada. Para ver qué cambiaría, usá `parche scan`; para armar la rama sin abrir el PR, `parche pr --dry-run`.'); + } + const list = (keys) => keys.map((k) => `--${k}`).join(', '); + throw new Error(`\`parche ${cmd}\` no conoce ${list(unknown)}. No se tocó nada. Opciones: ${list(known)}.`); +} + function summary(findings) { const c = counts(findings); return ['auto', 'needs-review', 'needs-input', 'blocked', 'detect-only'] @@ -164,6 +181,7 @@ async function main() { return 0; } if (!cmd || args.help) { console.log(HELP); return 0; } + checkFlags(cmd, args); if (cmd === 'packs') { for (const p of loadPacks()) { diff --git a/package-lock.json b/package-lock.json index f219f9f..b7cef53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "parche-cli", - "version": "0.8.4", + "version": "0.8.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "parche-cli", - "version": "0.8.4", + "version": "0.8.5", "license": "MIT", "dependencies": { "@babel/parser": "^7.28.0", diff --git a/package.json b/package.json index 2b6b130..50fa7e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parche-cli", - "version": "0.8.4", + "version": "0.8.5", "description": "Dependabot para APIs: detecta cambios de cualquier API de terceros en tu código (ARCA, Mercado Pago, OpenAI, Anthropic) y abre el PR con el arreglo.", "keywords": [ "api", diff --git a/test/flags-cli.test.js b/test/flags-cli.test.js new file mode 100644 index 0000000..ce33fa3 --- /dev/null +++ b/test/flags-cli.test.js @@ -0,0 +1,89 @@ +// 0.8.5: `fix` y `pr` cambian cosas, y `parseArgs` acepta cualquier flag. `parche fix . --dry-run` (el flag es de `pr`) +// escribía en el código sin avisar, `fix --pakc ` aplicaba todos los packs y `pr --dryrun` podía abrir el PR de verdad. +// Ahora un flag que no conocen frena antes de tocar nada; el resto de los comandos lee los flags como antes. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { execFileSync, spawnSync } from 'node:child_process'; + +const HERE = path.dirname(fileURLToPath(import.meta.url)); +const BIN = path.join(HERE, '..', 'bin', 'parche.js'); +const ARCA = 'arca.wsfe.condicion-iva-receptor'; +const AUTHOR = 't '; +const VOUCHER = "export const emitir = (afip) => afip.ElectronicBilling.createVoucher({\n CbteTipo: 6,\n DocTipo: 99,\n DocNro: 0,\n});\n"; + +function repo() { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'parche-flags-')); + fs.mkdirSync(path.join(root, 'src')); + fs.writeFileSync(path.join(root, 'src', 'f.js'), VOUCHER); + const g = (...a) => execFileSync('git', a, { cwd: root, encoding: 'utf8' }); + g('init', '-q', '-b', 'main'); + g('-c', 'user.name=t', '-c', 'user.email=t@t', 'add', '-A'); + g('-c', 'user.name=t', '-c', 'user.email=t@t', 'commit', '-q', '-m', 'init'); + return { root, g }; +} +const parche = (...a) => spawnSync('node', [BIN, ...a], { encoding: 'utf8' }); + +function untouched({ root, g }) { + assert.equal(fs.readFileSync(path.join(root, 'src', 'f.js'), 'utf8'), VOUCHER); + assert.equal(g('status', '--porcelain').trim(), ''); + assert.equal(g('branch', '--format=%(refname:short)').trim(), 'main'); + assert.ok(!fs.existsSync(path.join(root, '.parche'))); +} + +test('fix --dry-run no toca nada y dice qué usar', () => { + const r = repo(); + const out = parche('fix', r.root, '--dry-run'); + assert.equal(out.status, 1); + assert.match(out.stderr, /`parche fix` no tiene --dry-run/); + assert.match(out.stderr, /`parche scan`/); + assert.match(out.stderr, /`parche pr --dry-run`/); + untouched(r); +}); + +test('fix con un flag mal escrito no aplica todos los packs', () => { + const r = repo(); + const out = parche('fix', r.root, '--pakc', ARCA); + assert.equal(out.status, 1); + assert.match(out.stderr, /`parche fix` no conoce --pakc/); + assert.match(out.stderr, /--pack/); + untouched(r); +}); + +test('pr con un flag mal escrito no arma ninguna rama', () => { + const r = repo(); + const out = parche('pr', r.root, '--dryrun', '--author', AUTHOR); + assert.equal(out.status, 1); + assert.match(out.stderr, /`parche pr` no conoce --dryrun/); + assert.match(out.stderr, /--dry-run/); + untouched(r); +}); + +test('fix --pack sigue aplicando el arreglo', () => { + const { root } = repo(); + const out = parche('fix', root, '--pack', ARCA); + assert.equal(out.status, 0, out.stderr); + assert.notEqual(fs.readFileSync(path.join(root, 'src', 'f.js'), 'utf8'), VOUCHER); +}); + +test('pr acepta lo que le pasa la Action (--pack, --verify y --dry-run) y --author', () => { + const { root } = repo(); + const out = parche('pr', root, '--pack', ARCA, '--verify', '--dry-run', '--author', AUTHOR); + assert.doesNotMatch(out.stderr, /no conoce/); + assert.equal(out.status, 0, out.stderr); +}); + +test('el flag de pr para pushear a un fork no se toma como desconocido', () => { + const { root } = repo(); + const out = parche('pr', root, '--pack', ARCA, '--dry-run', '--push-remote', 'fork', '--author', AUTHOR); + assert.doesNotMatch(out.stderr, /no conoce/); +}); + +test('scan sigue sin fijarse en los flags que no usa', () => { + const { root } = repo(); + const out = parche('scan', root, '--dry-run'); + assert.equal(out.status, 0, out.stderr); +});