qaify-playwright-reporter 0.9.1 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/fix.d.ts CHANGED
@@ -136,12 +136,19 @@ export declare function fixContext(client: IngestClient, fixId: string, dir: str
136
136
  * Advisory dependensi tanpa AI: update paket di lockfile saja (tanpa install / script), cek ulang dengan audit, lalu
137
137
  * tulis hasilnya ke <dir>/.qaify/claude.json dalam bentuk keluaran `claude -p` (model qaify-deps, token 0) agar
138
138
  * fix-diff / fix-submit tidak berubah. npm: npm update dulu, npm audit fix bila paket masih rentan.
139
+ * Paket yang sudah aman di ujung branch (mis. ikut naik lewat PR temuan lain) tidak di-update: already_fixed.
139
140
  */
140
141
  export declare function fixDeps(dir: string, exec?: Exec): Promise<{
141
142
  fixed: boolean;
142
143
  summary: string;
143
144
  reason?: string;
145
+ already_fixed?: boolean;
144
146
  }>;
147
+ /**
148
+ * Paket di composer.lock yang me-require pkg secara langsung. `--with-all-dependencies` hanya memperbarui dependensi
149
+ * pkg; tanpa ini dependennya tertinggal di versi lama yang belum tentu cocok dengan pkg baru.
150
+ */
151
+ export declare function composerDependents(repo: string, pkg: string): Promise<string[]>;
145
152
  /** Masih ada advisory untuk paket ini? npm: hanya high / critical (sama dengan temuan audit-static). */
146
153
  export declare function stillVulnerable(output: string, manager: 'composer' | 'npm', pkg: string): boolean;
147
154
  /**
@@ -176,6 +183,8 @@ export interface ParsedFix {
176
183
  summary: string;
177
184
  reason: string | null;
178
185
  tests_run: string | null;
186
+ /** fix-deps: paket sudah aman sebelum di-update (bukan dari Claude, FIX_SCHEMA tidak memuatnya). */
187
+ already_fixed: boolean;
179
188
  usage: ClaudeUsage | null;
180
189
  }
181
190
  /** Output `claude -p --output-format json --json-schema FIX_SCHEMA`; null = tidak ada hasil terstruktur. */
package/dist/fix.js CHANGED
@@ -191,6 +191,7 @@ export async function fixContext(client, fixId, dir, opts = {}) {
191
191
  * Advisory dependensi tanpa AI: update paket di lockfile saja (tanpa install / script), cek ulang dengan audit, lalu
192
192
  * tulis hasilnya ke <dir>/.qaify/claude.json dalam bentuk keluaran `claude -p` (model qaify-deps, token 0) agar
193
193
  * fix-diff / fix-submit tidak berubah. npm: npm update dulu, npm audit fix bila paket masih rentan.
194
+ * Paket yang sudah aman di ujung branch (mis. ikut naik lewat PR temuan lain) tidak di-update: already_fixed.
194
195
  */
195
196
  export async function fixDeps(dir, exec = defaultExec) {
196
197
  const started = Date.now();
@@ -202,18 +203,31 @@ export async function fixDeps(dir, exec = defaultExec) {
202
203
  const composer = target.manager === 'composer';
203
204
  const manifest = composer ? 'composer.json' : 'package.json';
204
205
  const auditCmd = composer ? 'composer audit --locked' : 'npm audit --package-lock-only';
206
+ const audit = () => composer
207
+ ? report('composer', ['audit', '--locked', '--format=json', '--no-interaction', `--working-dir=${repo}`])
208
+ : report('npm', ['audit', '--json', '--package-lock-only', '--prefix', repo]);
205
209
  const steps = [];
210
+ // tanpa cek ini update dependennya (mis. laravel/framework) tetap mengubah lockfile → PR yang tidak diperlukan
211
+ if (!stillVulnerable(await audit(), target.manager, pkg)) {
212
+ return writeDeps(dir, started, {
213
+ fixed: false,
214
+ already_fixed: true,
215
+ summary: `${pkg} sudah aman di branch ini.`,
216
+ reason: `${auditCmd} di ujung branch tidak lagi melaporkan advisory ${pkg} (kemungkinan ikut diperbarui lewat perubahan lain). Lockfile tidak diubah.`,
217
+ });
218
+ }
206
219
  let vulnerable;
207
220
  try {
208
221
  if (composer) {
209
- steps.push(`composer update ${pkg} --with-all-dependencies --no-install`);
210
- await exec('composer', ['update', pkg, '--with-all-dependencies', '--no-install', '--no-scripts', '--no-interaction', '--no-progress', `--working-dir=${repo}`]);
211
- vulnerable = stillVulnerable(await report('composer', ['audit', '--locked', '--format=json', '--no-interaction', `--working-dir=${repo}`]), 'composer', pkg);
222
+ // paket yang me-require pkg ikut naik: mis. laravel/framework baru butuh laravel/sanctum yang mengenal perubahannya
223
+ const packages = [pkg, ...(await composerDependents(repo, pkg))];
224
+ steps.push(`composer update ${packages.join(' ')} --with-all-dependencies --no-install`);
225
+ await exec('composer', ['update', ...packages, '--with-all-dependencies', '--no-install', '--no-scripts', '--no-interaction', '--no-progress', `--working-dir=${repo}`]);
226
+ vulnerable = stillVulnerable(await audit(), 'composer', pkg);
212
227
  }
213
228
  else {
214
229
  steps.push(`npm update ${pkg} --package-lock-only`);
215
230
  await exec('npm', ['update', pkg, '--package-lock-only', '--no-fund', '--no-audit', '--prefix', repo]);
216
- const audit = () => report('npm', ['audit', '--json', '--package-lock-only', '--prefix', repo]);
217
231
  vulnerable = stillVulnerable(await audit(), 'npm', pkg);
218
232
  if (vulnerable) {
219
233
  steps.push('npm audit fix --package-lock-only');
@@ -244,6 +258,22 @@ export async function fixDeps(dir, exec = defaultExec) {
244
258
  tests_run: `${auditCmd}: ${pkg} bersih. Test aplikasi dijalankan CI repo pada PR ini.`,
245
259
  });
246
260
  }
261
+ /**
262
+ * Paket di composer.lock yang me-require pkg secara langsung. `--with-all-dependencies` hanya memperbarui dependensi
263
+ * pkg; tanpa ini dependennya tertinggal di versi lama yang belum tentu cocok dengan pkg baru.
264
+ */
265
+ export async function composerDependents(repo, pkg) {
266
+ let lock;
267
+ try {
268
+ lock = JSON.parse(await readFile(join(repo, 'composer.lock'), 'utf8'));
269
+ }
270
+ catch {
271
+ return [];
272
+ }
273
+ return [...(lock.packages ?? []), ...(lock['packages-dev'] ?? [])]
274
+ .filter((p) => p.name !== pkg && p.require && pkg in p.require)
275
+ .map((p) => p.name);
276
+ }
247
277
  /** Masih ada advisory untuk paket ini? npm: hanya high / critical (sama dengan temuan audit-static). */
248
278
  export function stillVulnerable(output, manager, pkg) {
249
279
  const start = output.search(/[[{]/);
@@ -351,6 +381,7 @@ export function parseFixOutput(raw) {
351
381
  summary: out.summary,
352
382
  reason: typeof out.reason === 'string' && out.reason ? out.reason : null,
353
383
  tests_run: typeof out.tests_run === 'string' && out.tests_run ? out.tests_run : null,
384
+ already_fixed: out.already_fixed === true,
354
385
  usage: parseClaudeUsage(parsed),
355
386
  };
356
387
  }
@@ -381,6 +412,9 @@ export async function fixSubmit(client, fixId, claudeOutput, patch, workDir, opt
381
412
  const stats = patchStats(patch);
382
413
  const usage = parsed.usage ? { ...parsed.usage, files_changed: stats.files.length, lines_changed: stats.changedLines } : undefined;
383
414
  const summary = [parsed.summary, parsed.tests_run ? `Verifikasi: ${parsed.tests_run}` : null].filter(Boolean).join('\n\n');
415
+ // no_change + already_fixed: backend lama tetap menerimanya (gave_up), backend baru menutup temuannya
416
+ if (parsed.already_fixed)
417
+ return send({ status: 'no_change', already_fixed: true, summary, reason: parsed.reason ?? parsed.summary, usage });
384
418
  if (!parsed.fixed)
385
419
  return send({ status: 'failed', summary, reason: parsed.reason ?? parsed.summary, usage });
386
420
  if (!patch.trim())
@@ -435,7 +469,7 @@ export async function fixSubmit(client, fixId, claudeOutput, patch, workDir, opt
435
469
  step = 'di-commit';
436
470
  await git(['commit', '-q', '--no-verify', '-m', `fix(qaify): ${grant.title}`.slice(0, 200), '-m', parsed.summary], commitEnv);
437
471
  step = 'di-push';
438
- await git(['push', '-q', '--no-verify', url, `HEAD:refs/heads/${grant.branch}`], auth);
472
+ await git(['push', '-q', '--no-verify', url, `${grant.force ? '+' : ''}HEAD:refs/heads/${grant.branch}`], auth);
439
473
  }
440
474
  catch (e) {
441
475
  await revokeTokens([grant.token], opts.fetch);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qaify-playwright-reporter",
3
- "version": "0.9.1",
3
+ "version": "0.9.4",
4
4
  "description": "Reporter Playwright untuk QA Factory (Qaify): kirim hasil test dan artefak ke /api/ingest, tanpa pernah menggagalkan test.",
5
5
  "license": "MIT",
6
6
  "author": "ahmdkenz",