qaify-playwright-reporter 0.7.0 → 0.9.0
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/README.md +22 -7
- package/dist/audit-api.d.ts +118 -0
- package/dist/audit-api.js +628 -0
- package/dist/audit-static.d.ts +15 -6
- package/dist/audit-static.js +101 -80
- package/dist/audit.d.ts +4 -0
- package/dist/audit.js +4 -1
- package/dist/cli.js +21 -4
- package/dist/fix.d.ts +25 -1
- package/dist/fix.js +112 -14
- package/package.json +1 -1
- package/templates/qaify-audit.yml +27 -9
- package/templates/qaify-fix.yml +12 -3
package/dist/fix.js
CHANGED
|
@@ -5,27 +5,28 @@
|
|
|
5
5
|
// fix-context / autofix-context: ambil konteks + token clone read-only, clone repo di commit sumber, cabut token,
|
|
6
6
|
// tulis prompt untuk Claude.
|
|
7
7
|
// claude -p mengubah kode di repo/ (tanpa commit).
|
|
8
|
-
// fix-
|
|
8
|
+
// fix-deps: pengganti Claude untuk advisory dependensi (model qaify-deps): composer/npm update lockfile saja,
|
|
9
|
+
// tanpa AI, hasilnya ditulis dalam bentuk keluaran claude -p.
|
|
10
|
+
// fix-diff: stage perubahan → changes.patch (lockfile & .env dibuang, kecuali lockfile mode deps) → artefak.
|
|
9
11
|
// Job "submit" (VM bersih, kode hasil Claude tidak pernah dijalankan di sini):
|
|
10
12
|
// fix-submit / autofix-submit: guard patch → token push (contents:write, satu repo) → clone ulang → git apply
|
|
11
13
|
// → commit → push → cabut token → hasil ke Qaify (backend membuka PR).
|
|
12
|
-
import { appendFile, mkdir, writeFile } from 'node:fs/promises';
|
|
14
|
+
import { appendFile, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
13
15
|
import { join } from 'node:path';
|
|
14
16
|
import { defaultExec, gitAuthEnv, revokeTokens } from './audit.js';
|
|
15
17
|
import { parseClaudeUsage, readClaudeUsage } from './usage.js';
|
|
16
18
|
/** Model untuk --model Claude Code: alias atau ID versi; selain itu ditolak (nilainya masuk ke shell workflow). */
|
|
17
19
|
const MODEL_PATTERN = /^[a-z0-9][a-z0-9.-]{0,63}$/;
|
|
20
|
+
/** "Model" perbaikan advisory dependensi tanpa AI; cermin config qafactory.audit_fix.deps_model. */
|
|
21
|
+
export const DEPS_MODEL = 'qaify-deps';
|
|
18
22
|
/** Berkas yang tidak boleh diubah perbaikan: workflow (bisa membaca secret repo), rahasia, lockfile. */
|
|
19
23
|
const BLOCKED = [/^\.github\//, /(^|\/)\.env(\.(?!example$)[^/]*)?$/];
|
|
24
|
+
const LOCKFILE = /(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|composer\.lock|bun\.lockb?)$/;
|
|
20
25
|
/**
|
|
21
26
|
* Ikut berubah tanpa sengaja saat Claude menjalankan npm / composer install atau Playwright (laporan & trace bila
|
|
22
27
|
* repo tidak meng-ignore-nya): dibuang dari patch, bukan menggagalkan.
|
|
23
28
|
*/
|
|
24
|
-
const INCIDENTAL = [
|
|
25
|
-
/(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|composer\.lock|bun\.lockb?)$/,
|
|
26
|
-
/(^|\/)\.env(\.(?!example$)[^/]*)?$/,
|
|
27
|
-
/^(test-results|playwright-report|blob-report)\//,
|
|
28
|
-
];
|
|
29
|
+
const INCIDENTAL = [LOCKFILE, /(^|\/)\.env(\.(?!example$)[^/]*)?$/, /^(test-results|playwright-report|blob-report)\//];
|
|
29
30
|
export const FIX_SCHEMA = {
|
|
30
31
|
type: 'object',
|
|
31
32
|
properties: {
|
|
@@ -137,12 +138,13 @@ export async function fixContext(client, fixId, dir, opts = {}) {
|
|
|
137
138
|
const ctx = (await client.get(`${FIX_BASE[kind]}/${encodeURIComponent(fixId)}/context${query}`));
|
|
138
139
|
if (!ctx?.repo?.token) {
|
|
139
140
|
log('qa-factory: konteks perbaikan tidak bisa diambil (perbaikan tidak dikenal / sudah selesai / token runner salah)');
|
|
140
|
-
return { ok: false, model: null };
|
|
141
|
+
return { ok: false, model: null, mode: 'claude' };
|
|
141
142
|
}
|
|
143
|
+
const deps = kind === 'audit' && ctx.deps && /^[@\w][\w./@-]*$/.test(ctx.deps.package) && ['composer', 'npm'].includes(ctx.deps.manager) ? ctx.deps : null;
|
|
142
144
|
if (!MODEL_PATTERN.test(ctx.model)) {
|
|
143
145
|
log(`qa-factory: model "${ctx.model}" tidak valid`);
|
|
144
146
|
await revokeTokens([ctx.repo.token], opts.fetch);
|
|
145
|
-
return { ok: false, model: null };
|
|
147
|
+
return { ok: false, model: null, mode: 'claude' };
|
|
146
148
|
}
|
|
147
149
|
if (process.env.GITHUB_ACTIONS === 'true')
|
|
148
150
|
console.log(`::add-mask::${ctx.repo.token}`);
|
|
@@ -157,26 +159,120 @@ export async function fixContext(client, fixId, dir, opts = {}) {
|
|
|
157
159
|
catch (e) {
|
|
158
160
|
log(`qa-factory: clone gagal: ${e.message.split('\n')[0]}`);
|
|
159
161
|
await revokeTokens([ctx.repo.token], opts.fetch);
|
|
160
|
-
return { ok: false, model: null };
|
|
162
|
+
return { ok: false, model: null, mode: 'claude' };
|
|
161
163
|
}
|
|
162
164
|
await revokeTokens([ctx.repo.token], opts.fetch);
|
|
163
165
|
const { token: _token, ...repo } = ctx.repo;
|
|
164
166
|
await mkdir(join(dir, '.qaify'), { recursive: true });
|
|
167
|
+
if (deps) {
|
|
168
|
+
await writeFile(join(dir, '.qaify', 'deps.json'), JSON.stringify(deps));
|
|
169
|
+
log(`qa-factory: ${ctx.repo.full_name}@${ref.slice(0, 12)} → repo/ (update ${deps.manager} ${deps.package} tanpa AI, percobaan ke-${ctx.attempt_no})`);
|
|
170
|
+
return { ok: true, model: ctx.model, mode: 'deps' };
|
|
171
|
+
}
|
|
165
172
|
await writeFile(join(dir, '.qaify', 'prompt.txt'), kind === 'test' ? testFixPrompt({ ...ctx, repo }) : fixPrompt({ ...ctx, repo }));
|
|
166
173
|
const detail = kind === 'test' ? ctx.failure.category : ctx.complexity;
|
|
167
174
|
log(`qa-factory: ${ctx.repo.full_name}@${ref.slice(0, 12)} → repo/ (model ${ctx.model}, ${detail}, percobaan ke-${ctx.attempt_no})`);
|
|
168
|
-
return { ok: true, model: ctx.model };
|
|
175
|
+
return { ok: true, model: ctx.model, mode: 'claude' };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Advisory dependensi tanpa AI: update paket di lockfile saja (tanpa install / script), cek ulang dengan audit, lalu
|
|
179
|
+
* tulis hasilnya ke <dir>/.qaify/claude.json dalam bentuk keluaran `claude -p` (model qaify-deps, token 0) agar
|
|
180
|
+
* fix-diff / fix-submit tidak berubah. npm: npm update dulu, npm audit fix bila paket masih rentan.
|
|
181
|
+
*/
|
|
182
|
+
export async function fixDeps(dir, exec = defaultExec) {
|
|
183
|
+
const started = Date.now();
|
|
184
|
+
const target = JSON.parse(await readFile(join(dir, '.qaify', 'deps.json'), 'utf8'));
|
|
185
|
+
const repo = join(dir, 'repo');
|
|
186
|
+
const pkg = target.package;
|
|
187
|
+
// composer/npm audit keluar non-0 bila masih ada kerentanan: laporannya tetap di stdout
|
|
188
|
+
const report = (file, args) => exec(file, args).catch((e) => (typeof e?.stdout === 'string' ? e.stdout : ''));
|
|
189
|
+
const composer = target.manager === 'composer';
|
|
190
|
+
const manifest = composer ? 'composer.json' : 'package.json';
|
|
191
|
+
const auditCmd = composer ? 'composer audit --locked' : 'npm audit --package-lock-only';
|
|
192
|
+
const steps = [];
|
|
193
|
+
let vulnerable;
|
|
194
|
+
try {
|
|
195
|
+
if (composer) {
|
|
196
|
+
steps.push(`composer update ${pkg} --with-all-dependencies --no-install`);
|
|
197
|
+
await exec('composer', ['update', pkg, '--with-all-dependencies', '--no-install', '--no-scripts', '--no-interaction', '--no-progress', `--working-dir=${repo}`]);
|
|
198
|
+
vulnerable = stillVulnerable(await report('composer', ['audit', '--locked', '--format=json', '--no-interaction', `--working-dir=${repo}`]), 'composer', pkg);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
steps.push(`npm update ${pkg} --package-lock-only`);
|
|
202
|
+
await exec('npm', ['update', pkg, '--package-lock-only', '--no-fund', '--no-audit', '--prefix', repo]);
|
|
203
|
+
const audit = () => report('npm', ['audit', '--json', '--package-lock-only', '--prefix', repo]);
|
|
204
|
+
vulnerable = stillVulnerable(await audit(), 'npm', pkg);
|
|
205
|
+
if (vulnerable) {
|
|
206
|
+
steps.push('npm audit fix --package-lock-only');
|
|
207
|
+
await exec('npm', ['audit', 'fix', '--package-lock-only', '--no-fund', '--prefix', repo]).catch(() => '');
|
|
208
|
+
vulnerable = stillVulnerable(await audit(), 'npm', pkg);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
const message = String(e.stderr || e.message).trim().split('\n').slice(-3).join(' ');
|
|
214
|
+
return writeDeps(dir, started, { fixed: false, summary: `Gagal memperbarui ${pkg}.`, reason: `${steps.at(-1)} gagal: ${message}`.slice(0, 1000) });
|
|
215
|
+
}
|
|
216
|
+
const changed = (await exec('git', ['-C', repo, 'status', '--porcelain'])).split('\n').map((l) => l.slice(3).trim()).filter(Boolean);
|
|
217
|
+
if (vulnerable) {
|
|
218
|
+
return writeDeps(dir, started, {
|
|
219
|
+
fixed: false,
|
|
220
|
+
summary: `${pkg} masih rentan setelah ${steps.join(' lalu ')}.`,
|
|
221
|
+
reason: `Versi ${pkg} yang sudah diperbaiki berada di luar constraint ${manifest} (kemungkinan perlu naik versi major). Naikkan constraint ${pkg} di ${manifest} secara manual lalu uji aplikasinya.`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (!changed.length) {
|
|
225
|
+
return writeDeps(dir, started, { fixed: false, summary: `Lockfile tidak berubah.`, reason: `Advisory ${pkg} sudah tidak muncul di ${auditCmd} commit ini; temuan mungkin sudah diperbaiki.` });
|
|
226
|
+
}
|
|
227
|
+
return writeDeps(dir, started, {
|
|
228
|
+
fixed: true,
|
|
229
|
+
summary: `- ${pkg} diperbarui dengan ${steps.join(', lalu ')} (tanpa AI).\n- ${auditCmd} tidak lagi melaporkan ${pkg}.`,
|
|
230
|
+
files_changed: changed,
|
|
231
|
+
tests_run: `${auditCmd}: ${pkg} bersih. Test aplikasi dijalankan CI repo pada PR ini.`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/** Masih ada advisory untuk paket ini? npm: hanya high / critical (sama dengan temuan audit-static). */
|
|
235
|
+
export function stillVulnerable(output, manager, pkg) {
|
|
236
|
+
const start = output.search(/[[{]/);
|
|
237
|
+
let data = null;
|
|
238
|
+
try {
|
|
239
|
+
data = start < 0 ? null : JSON.parse(output.slice(start));
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// laporan tidak terbaca: dianggap masih rentan, jangan buka PR yang belum terbukti
|
|
243
|
+
}
|
|
244
|
+
if (!data)
|
|
245
|
+
return true;
|
|
246
|
+
if (manager === 'composer') {
|
|
247
|
+
const list = data.advisories?.[pkg];
|
|
248
|
+
return Array.isArray(list) ? list.length > 0 : !!list && Object.keys(list).length > 0;
|
|
249
|
+
}
|
|
250
|
+
const vuln = data.vulnerabilities?.[pkg];
|
|
251
|
+
return vuln?.severity === 'high' || vuln?.severity === 'critical';
|
|
252
|
+
}
|
|
253
|
+
async function writeDeps(dir, started, result) {
|
|
254
|
+
await writeFile(join(dir, '.qaify', 'claude.json'), JSON.stringify({
|
|
255
|
+
type: 'result',
|
|
256
|
+
subtype: 'success',
|
|
257
|
+
duration_ms: Date.now() - started,
|
|
258
|
+
structured_output: result,
|
|
259
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
260
|
+
modelUsage: { [DEPS_MODEL]: { outputTokens: 0 } },
|
|
261
|
+
}));
|
|
262
|
+
return result;
|
|
169
263
|
}
|
|
170
264
|
/**
|
|
171
265
|
* Perubahan Claude di <dir>/repo → <out>/changes.patch (git diff --binary). Lockfile & .env yang ikut berubah saat
|
|
172
|
-
* menjalankan aplikasi dibuang dari patch
|
|
266
|
+
* menjalankan aplikasi dibuang dari patch, kecuali lockfile pada mode deps (.qaify/deps.json) yang memang hasilnya.
|
|
267
|
+
* Job ini tidak memegang token, jadi aman menjalankan git di repo hasil Claude.
|
|
173
268
|
*/
|
|
174
269
|
export async function fixDiff(dir, out, exec = defaultExec) {
|
|
175
270
|
const repo = join(dir, 'repo');
|
|
176
271
|
await mkdir(out, { recursive: true });
|
|
177
272
|
await exec('git', ['-C', repo, 'add', '-A']);
|
|
273
|
+
const deps = await stat(join(dir, '.qaify', 'deps.json')).then(() => true, () => false);
|
|
178
274
|
const staged = (await exec('git', ['-C', repo, 'diff', '--cached', '--name-only'])).split('\n').filter(Boolean);
|
|
179
|
-
const dropped = staged.filter((f) => INCIDENTAL.some((re) => re.test(f)));
|
|
275
|
+
const dropped = staged.filter((f) => INCIDENTAL.some((re) => re.test(f)) && !(deps && LOCKFILE.test(f)));
|
|
180
276
|
if (dropped.length)
|
|
181
277
|
await exec('git', ['-C', repo, 'reset', '-q', '--', ...dropped]);
|
|
182
278
|
await writeFile(join(out, 'changes.patch'), await exec('git', ['-C', repo, 'diff', '--cached', '--binary']));
|
|
@@ -189,6 +285,7 @@ export function patchStats(patch) {
|
|
|
189
285
|
const paths = new Set();
|
|
190
286
|
let unreadable = 0;
|
|
191
287
|
let changedLines = 0;
|
|
288
|
+
let lockfile = false;
|
|
192
289
|
for (const line of patch.split('\n')) {
|
|
193
290
|
if (line.startsWith('diff --git ')) {
|
|
194
291
|
const header = /^diff --git "?a\/(.+?)"? "?b\/(.+?)"?$/.exec(line);
|
|
@@ -199,8 +296,9 @@ export function patchStats(patch) {
|
|
|
199
296
|
paths.add(header[1]);
|
|
200
297
|
paths.add(header[2]);
|
|
201
298
|
files.add(header[2]);
|
|
299
|
+
lockfile = LOCKFILE.test(header[2]);
|
|
202
300
|
}
|
|
203
|
-
else if (/^(\+(?!\+\+ )|-(?!-- ))/.test(line)) {
|
|
301
|
+
else if (!lockfile && /^(\+(?!\+\+ )|-(?!-- ))/.test(line)) {
|
|
204
302
|
changedLines++;
|
|
205
303
|
}
|
|
206
304
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qaify-playwright-reporter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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",
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
# Butuh di repo runner ini (Settings → Secrets and variables → Actions):
|
|
4
4
|
# secret QA_FACTORY_TOKEN token reporter repo runner (Pengaturan → Token Reporter)
|
|
5
5
|
# variable QA_FACTORY_URL URL API Qaify
|
|
6
|
-
# secret CLAUDE_CODE_OAUTH_TOKEN opsional: audit
|
|
7
|
-
# variable QA_FACTORY_AUDIT_MODE opsional, `static`
|
|
6
|
+
# secret CLAUDE_CODE_OAUTH_TOKEN opsional: perbaikan temuan & audit mode claude (langganan Claude Pro/Max, `claude setup-token`)
|
|
7
|
+
# variable QA_FACTORY_AUDIT_MODE opsional, `static` / `claude`; hanya dipakai bila Qaify tidak mengirim mode
|
|
8
8
|
# variable QA_FACTORY_AI_MODEL opsional, default sonnet
|
|
9
9
|
# variable QA_FACTORY_MCP_VERSION opsional, versi @playwright/mcp (default latest)
|
|
10
10
|
# GitHub App Qaify harus terpasang di repo ini dan di repo FE + BE. Nama file harus tetap qaify-audit.yml.
|
|
11
|
+
# Mode audit dipilih di Qaify (setelan aplikasi, bisa diganti per audit); mode claude tanpa secret jatuh ke static.
|
|
11
12
|
# Mode static (tanpa AI): build, test, composer/npm audit, dan smoke test Playwright; cepat, tanpa kuota AI,
|
|
12
|
-
# tetapi tanpa analisis logika bisnis / kontrak API dari kode.
|
|
13
|
+
# tetapi tanpa analisis logika bisnis / kontrak API dari kode. Temuannya tetap bisa diperbaiki Claude (qaify-fix.yml).
|
|
13
14
|
# Mode claude: satu sesi Claude yang panjang; kuota langganan dipakai bersama pemakaian pribadi.
|
|
14
15
|
name: Qaify audit
|
|
15
16
|
run-name: audit ${{ inputs.audit_id }}
|
|
@@ -42,9 +43,8 @@ jobs:
|
|
|
42
43
|
env: { POSTGRES_PASSWORD: postgres, POSTGRES_DB: app }
|
|
43
44
|
ports: ['5432:5432']
|
|
44
45
|
options: --health-cmd="pg_isready -U postgres" --health-interval=10s --health-timeout=5s --health-retries=10
|
|
45
|
-
# context runner tidak tersedia di env level job; WORK diisi lewat GITHUB_ENV di step pertama
|
|
46
|
-
|
|
47
|
-
AUDIT_MODE: ${{ vars.QA_FACTORY_AUDIT_MODE || (secrets.CLAUDE_CODE_OAUTH_TOKEN != '' && 'claude' || 'static') }}
|
|
46
|
+
# context runner tidak tersedia di env level job; WORK diisi lewat GITHUB_ENV di step pertama,
|
|
47
|
+
# AUDIT_MODE di step "Tentukan mode audit" (mode baru diketahui dari konteks audit)
|
|
48
48
|
steps:
|
|
49
49
|
- name: Siapkan direktori kerja
|
|
50
50
|
run: echo "WORK=$RUNNER_TEMP/qaify" >> "$GITHUB_ENV"
|
|
@@ -58,9 +58,7 @@ jobs:
|
|
|
58
58
|
tools: composer
|
|
59
59
|
|
|
60
60
|
- name: Pasang Qaify CLI
|
|
61
|
-
run:
|
|
62
|
-
npm install -g qaify-playwright-reporter@0.5 playwright-core@1
|
|
63
|
-
if [ "$AUDIT_MODE" = claude ]; then npm install -g @anthropic-ai/claude-code; fi
|
|
61
|
+
run: npm install -g qaify-playwright-reporter@0.9 playwright-core@1
|
|
64
62
|
|
|
65
63
|
# token clone sekali pakai (read-only, hanya repo FE + BE) dicabut sebelum auditor berjalan
|
|
66
64
|
- name: Ambil konteks & clone repo FE + BE
|
|
@@ -70,6 +68,25 @@ jobs:
|
|
|
70
68
|
QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
|
|
71
69
|
run: qaify audit-context --audit "${{ inputs.audit_id }}" --dir "$WORK"
|
|
72
70
|
|
|
71
|
+
# pilihan Qaify → variable QA_FACTORY_AUDIT_MODE → claude bila secret ada; claude tanpa secret → static
|
|
72
|
+
- name: Tentukan mode audit
|
|
73
|
+
if: steps.ctx.outputs.ok == 'true'
|
|
74
|
+
env:
|
|
75
|
+
REQUESTED: ${{ steps.ctx.outputs.mode || vars.QA_FACTORY_AUDIT_MODE }}
|
|
76
|
+
HAS_CLAUDE: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN != '' }}
|
|
77
|
+
run: |
|
|
78
|
+
mode="${REQUESTED:-$([ "$HAS_CLAUDE" = true ] && echo claude || echo static)}"
|
|
79
|
+
if [ "$mode" != static ] && [ "$mode" != claude ]; then
|
|
80
|
+
echo "::warning::Mode audit tidak dikenal ($mode); memakai static."
|
|
81
|
+
mode=static
|
|
82
|
+
fi
|
|
83
|
+
if [ "$mode" = claude ] && [ "$HAS_CLAUDE" != true ]; then
|
|
84
|
+
echo "::warning::Mode claude diminta tetapi secret CLAUDE_CODE_OAUTH_TOKEN kosong; audit dijalankan tanpa AI."
|
|
85
|
+
mode=static
|
|
86
|
+
fi
|
|
87
|
+
echo "qa-factory: mode audit $mode"
|
|
88
|
+
echo "AUDIT_MODE=$mode" >> "$GITHUB_ENV"
|
|
89
|
+
|
|
73
90
|
# tanpa secret apa pun: kode aplikasi (composer/npm script) berjalan di step ini
|
|
74
91
|
- name: Audit tanpa AI (Playwright)
|
|
75
92
|
if: steps.ctx.outputs.ok == 'true' && env.AUDIT_MODE == 'static'
|
|
@@ -80,6 +97,7 @@ jobs:
|
|
|
80
97
|
- name: Siapkan instruksi & Playwright MCP
|
|
81
98
|
if: steps.ctx.outputs.ok == 'true' && env.AUDIT_MODE == 'claude'
|
|
82
99
|
run: |
|
|
100
|
+
npm install -g @anthropic-ai/claude-code
|
|
83
101
|
mkdir -p "$WORK/.qaify"
|
|
84
102
|
qaify audit-schema --out "$WORK/.qaify/schema.json" --instructions "$WORK/.qaify/instructions.txt"
|
|
85
103
|
# Google Chrome sudah ada di runner ubuntu-latest
|
package/templates/qaify-fix.yml
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# secret CLAUDE_CODE_OAUTH_TOKEN langganan Claude Pro/Max Anda (`claude setup-token`); wajib untuk perbaikan
|
|
7
7
|
# GitHub App Qaify butuh izin Contents: Read and write dan Pull requests: Read and write di repo FE/BE.
|
|
8
8
|
# Model (haiku / sonnet / opus atau versi tertentu) dipilih Qaify per temuan menurut kompleksitas atau pilihan Anda.
|
|
9
|
+
# Advisory dependensi (composer.lock / package-lock.json) diperbaiki tanpa Claude: `qaify fix-deps` memperbarui
|
|
10
|
+
# paketnya di lockfile (mode deps), tanpa memakai kuota.
|
|
9
11
|
# Job "fix" menjalankan Claude tanpa token push; job "submit" (VM bersih) meng-clone ulang, menerapkan patch, lalu push.
|
|
10
12
|
# Nama file harus tetap qaify-fix.yml.
|
|
11
13
|
name: Qaify fix
|
|
@@ -54,7 +56,7 @@ jobs:
|
|
|
54
56
|
tools: composer
|
|
55
57
|
|
|
56
58
|
- name: Pasang Qaify CLI & Claude Code
|
|
57
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
59
|
+
run: npm install -g qaify-playwright-reporter@0.9 @anthropic-ai/claude-code
|
|
58
60
|
|
|
59
61
|
# token clone sekali pakai (read-only, hanya repo target) dicabut sebelum Claude berjalan
|
|
60
62
|
- name: Ambil temuan & clone repo target
|
|
@@ -66,9 +68,16 @@ jobs:
|
|
|
66
68
|
qaify fix-context --fix "${{ inputs.fix_id }}" --dir "$WORK"
|
|
67
69
|
qaify fix-schema --out "$WORK/.qaify/schema.json" --instructions "$WORK/.qaify/instructions.txt"
|
|
68
70
|
|
|
71
|
+
# advisory dependensi: update lockfile tanpa AI, tidak ada token apa pun di step ini
|
|
72
|
+
- name: Perbarui dependensi (tanpa AI)
|
|
73
|
+
if: steps.ctx.outputs.ok == 'true' && steps.ctx.outputs.mode == 'deps'
|
|
74
|
+
continue-on-error: true
|
|
75
|
+
timeout-minutes: 15
|
|
76
|
+
run: qaify fix-deps --dir "$WORK"
|
|
77
|
+
|
|
69
78
|
# hanya CLAUDE_CODE_OAUTH_TOKEN yang tersedia di step ini; tidak ada token Qaify maupun token push
|
|
70
79
|
- name: Perbaiki dengan Claude Code (Claude Pro/Max)
|
|
71
|
-
if: steps.ctx.outputs.ok == 'true'
|
|
80
|
+
if: steps.ctx.outputs.ok == 'true' && steps.ctx.outputs.mode != 'deps'
|
|
72
81
|
continue-on-error: true
|
|
73
82
|
timeout-minutes: 45
|
|
74
83
|
working-directory: ${{ runner.temp }}/qaify/repo
|
|
@@ -109,7 +118,7 @@ jobs:
|
|
|
109
118
|
- uses: actions/setup-node@v4
|
|
110
119
|
with: { node-version: 22 }
|
|
111
120
|
- name: Pasang Qaify CLI
|
|
112
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
121
|
+
run: npm install -g qaify-playwright-reporter@0.9
|
|
113
122
|
- uses: actions/download-artifact@v4
|
|
114
123
|
continue-on-error: true
|
|
115
124
|
with:
|