qaify-playwright-reporter 0.10.1 → 0.11.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 +2 -1
- package/dist/ai-env.d.ts +2 -0
- package/dist/ai-env.js +16 -3
- package/dist/analyze.d.ts +2 -0
- package/dist/analyze.js +1 -0
- package/dist/cli.js +23 -22
- package/dist/fix.js +5 -3
- package/dist/usage.d.ts +1 -1
- package/dist/usage.js +13 -1
- package/package.json +1 -1
- package/templates/auto-fix.yml +16 -8
- package/templates/qa.yml +9 -6
- package/templates/qaify-audit.yml +9 -7
- package/templates/qaify-fix.yml +15 -7
package/README.md
CHANGED
|
@@ -66,7 +66,8 @@ Nama bin lama `playwright-reporter` tetap tersedia sebagai alias.
|
|
|
66
66
|
Backend tidak memanggil AI (`QA_AI_MODE=actions`, default). Kegagalan yang tidak dikenali aturan diantrekan, lalu job `analyze` di [`qa.yml`](../.github/workflows/qa.yml) menjalankan Claude Code dengan langganan kamu:
|
|
67
67
|
|
|
68
68
|
1. Di komputer yang sudah login Claude Code: `claude setup-token` → salin token (berlaku 1 tahun).
|
|
69
|
-
2. Simpan
|
|
69
|
+
2. Simpan token itu **sekali** di Qaify → Setelan → Agen AI → "Token langganan Claude", lalu di setiap repo nyalakan "Pakai setelan Agen AI akun saya" (Otomatisasi & AI). Aplikasi audit memakai akun pembuatnya. Workflow ≥ 0.11 mengambilnya lewat `qaify ai-env --oauth` hanya saat tugas berjalan, jadi secret per repo tidak perlu.
|
|
70
|
+
Cara lama tetap jalan: secret `CLAUDE_CODE_OAUTH_TOKEN` di repo yang diuji. Bila keduanya ada, token dari Qaify yang dipakai.
|
|
70
71
|
3. Opsional: variable `QA_FACTORY_AI_MODEL` (default `sonnet`; `opus` lebih teliti tapi lebih cepat menghabiskan kuota).
|
|
71
72
|
|
|
72
73
|
`claude -p` dijalankan di folder kosong dengan semua tool dimatikan, menerima antrean lewat stdin, dan mengembalikan JSON sesuai schema. `ai-submit --finalize` menerapkan hasilnya; kegagalan yang tidak terjawab (login gagal, kuota habis, timeout, PR dari fork tanpa secret) menjadi "belum jelas".
|
package/dist/ai-env.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface AiEnvOptions {
|
|
|
15
15
|
githubRunId?: number;
|
|
16
16
|
envFile: string;
|
|
17
17
|
errorOut?: string;
|
|
18
|
+
/** workflow memuat env file SETELAH membuang secret repo → token langganan Claude dari Qaify boleh diterima */
|
|
19
|
+
oauth?: boolean;
|
|
18
20
|
url?: string;
|
|
19
21
|
token?: string;
|
|
20
22
|
fetch?: typeof fetch;
|
package/dist/ai-env.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// `qaify ai-env`: tepat sebelum langkah AI, tanya Qaify mesin + model + kredensial agen untuk tugas ini.
|
|
2
2
|
// langganan (default / backend lama) → engine=claude_code, tanpa env file (secret CLAUDE_CODE_OAUTH_TOKEN dipakai)
|
|
3
|
+
// langganan + token disimpan di Qaify → (hanya dengan --oauth) env file berisi CLAUDE_CODE_OAUTH_TOKEN akun,
|
|
4
|
+
// menggantikan secret per repo; step Claude membuang secret lalu memuatnya
|
|
3
5
|
// Claude Code + API key Anthropic → env file berisi ANTHROPIC_API_KEY (step Claude memuatnya & membuang secret OAuth)
|
|
4
6
|
// Qaify Agent (OpenAI-compatible) → env file berisi QAIFY_AI_*; dibaca & dihapus `qaify agent`, tidak masuk env bash
|
|
5
7
|
// agen tidak bisa dipakai (key dihapus / budget habis) → engine=none + hasil gagal berbentuk claude.json di --error-out
|
|
@@ -9,6 +11,7 @@ import { dirname } from 'node:path';
|
|
|
9
11
|
/** Id model yang aman masuk ke --model / output step (cermin AgentSettings::MODEL_PATTERN backend). */
|
|
10
12
|
export const MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/@+-]{0,99}$/;
|
|
11
13
|
const ENV_NAME = /^(ANTHROPIC_[A-Z_]+|QAIFY_AI_[A-Z_]+)$/;
|
|
14
|
+
const OAUTH_NAME = 'CLAUDE_CODE_OAUTH_TOKEN';
|
|
12
15
|
const SECRET_NAME = /(KEY|TOKEN|SECRET)/;
|
|
13
16
|
const SUBSCRIPTION = { engine: 'claude_code', auth: 'subscription', model: null, provider: null, hasKey: false, error: null };
|
|
14
17
|
export async function aiEnv(options) {
|
|
@@ -26,7 +29,12 @@ export async function aiEnv(options) {
|
|
|
26
29
|
res = await (options.fetch ?? fetch)(`${url}/api/ingest/ai-env`, {
|
|
27
30
|
method: 'POST',
|
|
28
31
|
headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
29
|
-
body: JSON.stringify({
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
kind: options.kind,
|
|
34
|
+
...(options.id ? { id: Number(options.id) } : {}),
|
|
35
|
+
...(options.githubRunId ? { github_run_id: options.githubRunId } : {}),
|
|
36
|
+
...(options.oauth ? { oauth: true } : {}),
|
|
37
|
+
}),
|
|
30
38
|
signal: AbortSignal.timeout(30_000),
|
|
31
39
|
});
|
|
32
40
|
}
|
|
@@ -51,7 +59,8 @@ export async function aiEnv(options) {
|
|
|
51
59
|
}
|
|
52
60
|
const env = {};
|
|
53
61
|
for (const [name, value] of Object.entries((data?.env ?? {}))) {
|
|
54
|
-
|
|
62
|
+
const allowed = ENV_NAME.test(name) || (options.oauth === true && name === OAUTH_NAME);
|
|
63
|
+
if (!allowed || typeof value !== 'string' || !value)
|
|
55
64
|
continue;
|
|
56
65
|
if (SECRET_NAME.test(name))
|
|
57
66
|
mask(value);
|
|
@@ -67,7 +76,11 @@ export async function aiEnv(options) {
|
|
|
67
76
|
await writeFile(options.envFile, Object.entries(env).map(([k, v]) => `${k}=${shellQuote(v)}\n`).join(''), { mode: 0o600 });
|
|
68
77
|
await chmod(options.envFile, 0o600).catch(() => { });
|
|
69
78
|
}
|
|
70
|
-
const label = engine === 'qaify_agent'
|
|
79
|
+
const label = engine === 'qaify_agent'
|
|
80
|
+
? `Qaify Agent (${data?.provider ?? 'API key'})`
|
|
81
|
+
: env[OAUTH_NAME] ? 'Claude Code (langganan, token dari Qaify)'
|
|
82
|
+
: hasKey ? 'Claude Code (API key Anthropic)'
|
|
83
|
+
: 'Claude Code (langganan, secret repo)';
|
|
71
84
|
log(`qa-factory: agen AI ${label}${model ? `, model ${model}` : ''}`);
|
|
72
85
|
return { engine, auth: data?.auth === 'key' ? 'key' : 'subscription', model, provider: data?.provider ?? null, hasKey, error: null };
|
|
73
86
|
}
|
package/dist/analyze.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface CliArgs {
|
|
|
32
32
|
id: string | undefined;
|
|
33
33
|
envFile: string | undefined;
|
|
34
34
|
errorOut: string | undefined;
|
|
35
|
+
/** ai-env: workflow ≥ 0.11 menerima token langganan Claude dari Qaify (bukan secret repo) */
|
|
36
|
+
oauth: boolean;
|
|
35
37
|
promptFile: string | undefined;
|
|
36
38
|
instructionsFile: string | undefined;
|
|
37
39
|
schemaFile: string | undefined;
|
package/dist/analyze.js
CHANGED
|
@@ -60,6 +60,7 @@ export function parseArgs(argv) {
|
|
|
60
60
|
id: value('id'),
|
|
61
61
|
envFile: value('env-file'),
|
|
62
62
|
errorOut: value('error-out'),
|
|
63
|
+
oauth: rest.includes('--oauth'),
|
|
63
64
|
promptFile: value('prompt-file'),
|
|
64
65
|
instructionsFile: value('instructions-file'),
|
|
65
66
|
schemaFile: value('schema-file'),
|
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// qaify autofix-schema --out schema.json --instructions instructions.txt
|
|
25
25
|
// qaify autofix-submit --fix <id> --results claude.json --patch changes.patch --dir <folder kosong>
|
|
26
26
|
// Agen AI pilihan user (Setelan → Agen AI di Qaify), sebelum langkah AI di semua workflow:
|
|
27
|
-
// qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out claude.json]
|
|
27
|
+
// qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out claude.json] [--oauth]
|
|
28
28
|
// qaify agent --env-file <file> --schema-file schema.json --out claude.json --prompt-file <f>
|
|
29
29
|
// [--instructions-file <f>] [--input-file <f>] [--tools read,list,grep,write,edit,bash] [--max-turns 80] [--mcp-config mcp.json]
|
|
30
30
|
import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
@@ -39,27 +39,27 @@ import { init, initRunner, NEXT_STEPS, RUNNER_NEXT_STEPS } from './init.js';
|
|
|
39
39
|
import { check } from './check.js';
|
|
40
40
|
import { aiEnv } from './ai-env.js';
|
|
41
41
|
import { agentCommand } from './agent/command.js';
|
|
42
|
-
const USAGE = `Pemakaian:
|
|
43
|
-
qaify init [--force]
|
|
44
|
-
qaify check
|
|
45
|
-
qaify analyze --conclusion <${CONCLUSIONS.join('|')}>
|
|
46
|
-
qaify ai-queue --out <file>
|
|
47
|
-
qaify ai-schema --out <file-schema> [--instructions <file>]
|
|
48
|
-
qaify ai-submit --results <file> [--finalize]
|
|
49
|
-
qaify init-runner [--force]
|
|
50
|
-
qaify audit-context --audit <id> --dir <folder>
|
|
51
|
-
qaify audit-schema (--out <file-schema> [--instructions <file>] | --prompt)
|
|
52
|
-
qaify audit-static --dir <folder> --out <file>
|
|
53
|
-
qaify audit-submit --audit <id> --results <file> --evidence <folder> --context <file>
|
|
54
|
-
qaify fix-context --fix <id> --dir <folder>
|
|
55
|
-
qaify fix-schema --out <file-schema> [--instructions <file>]
|
|
56
|
-
qaify fix-deps --dir <folder>
|
|
57
|
-
qaify fix-diff --dir <folder> --out <folder>
|
|
58
|
-
qaify fix-submit --fix <id> --results <file> --patch <file> --dir <folder>
|
|
59
|
-
qaify autofix-context --fix <id> --dir <folder>
|
|
60
|
-
qaify autofix-schema --out <file-schema> [--instructions <file>]
|
|
61
|
-
qaify autofix-submit --fix <id> --results <file> --patch <file> --dir <folder>
|
|
62
|
-
qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out <file>]
|
|
42
|
+
const USAGE = `Pemakaian:
|
|
43
|
+
qaify init [--force]
|
|
44
|
+
qaify check
|
|
45
|
+
qaify analyze --conclusion <${CONCLUSIONS.join('|')}>
|
|
46
|
+
qaify ai-queue --out <file>
|
|
47
|
+
qaify ai-schema --out <file-schema> [--instructions <file>]
|
|
48
|
+
qaify ai-submit --results <file> [--finalize]
|
|
49
|
+
qaify init-runner [--force]
|
|
50
|
+
qaify audit-context --audit <id> --dir <folder>
|
|
51
|
+
qaify audit-schema (--out <file-schema> [--instructions <file>] | --prompt)
|
|
52
|
+
qaify audit-static --dir <folder> --out <file>
|
|
53
|
+
qaify audit-submit --audit <id> --results <file> --evidence <folder> --context <file>
|
|
54
|
+
qaify fix-context --fix <id> --dir <folder>
|
|
55
|
+
qaify fix-schema --out <file-schema> [--instructions <file>]
|
|
56
|
+
qaify fix-deps --dir <folder>
|
|
57
|
+
qaify fix-diff --dir <folder> --out <folder>
|
|
58
|
+
qaify fix-submit --fix <id> --results <file> --patch <file> --dir <folder>
|
|
59
|
+
qaify autofix-context --fix <id> --dir <folder>
|
|
60
|
+
qaify autofix-schema --out <file-schema> [--instructions <file>]
|
|
61
|
+
qaify autofix-submit --fix <id> --results <file> --patch <file> --dir <folder>
|
|
62
|
+
qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out <file>] [--oauth]
|
|
63
63
|
qaify agent --env-file <file> --schema-file <file> --out <file> --prompt-file <file> [--instructions-file <file>] [--input-file <file>] [--tools <daftar>] [--max-turns <n>] [--mcp-config <file>]`;
|
|
64
64
|
const args = parseArgs(process.argv.slice(2));
|
|
65
65
|
const client = createClient();
|
|
@@ -289,6 +289,7 @@ switch (args.command) {
|
|
|
289
289
|
githubRunId: run.github_run_id || undefined,
|
|
290
290
|
envFile: args.envFile,
|
|
291
291
|
errorOut: args.errorOut,
|
|
292
|
+
oauth: args.oauth,
|
|
292
293
|
});
|
|
293
294
|
await writeOutputs({ engine: result.engine, model: result.model ?? '', has_key: String(result.hasKey), provider: result.provider ?? '' });
|
|
294
295
|
break;
|
package/dist/fix.js
CHANGED
|
@@ -438,9 +438,11 @@ export async function fixSubmit(client, fixId, claudeOutput, patch, workDir, opt
|
|
|
438
438
|
return send({
|
|
439
439
|
status: 'failed',
|
|
440
440
|
...(usage ? { usage } : {}),
|
|
441
|
-
reason:
|
|
442
|
-
? `
|
|
443
|
-
:
|
|
441
|
+
reason: usage?.error_code === 'ai_not_logged_in'
|
|
442
|
+
? `Claude Code belum login di runner (${agentError ?? 'tanpa kredensial'}): secret CLAUDE_CODE_OAUTH_TOKEN di repo runner kosong atau kedaluwarsa. Jalankan \`claude setup-token\` lalu isi secret itu, atau pakai API key di Setelan → Agen AI.`
|
|
443
|
+
: agentError
|
|
444
|
+
? `Agen AI gagal: ${agentError}`
|
|
445
|
+
: 'Agen AI tidak menghasilkan hasil perbaikan terstruktur. Cek log step "Perbaiki dengan Claude Code" / "Perbaiki dengan Qaify Agent" di workflow perbaikan: secret CLAUDE_CODE_OAUTH_TOKEN dan kuota langganan, atau API key, saldo & model di Setelan → Agen AI, atau batas giliran.',
|
|
444
446
|
});
|
|
445
447
|
}
|
|
446
448
|
const stats = patchStats(patch);
|
package/dist/usage.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface ClaudeUsage {
|
|
|
14
14
|
/** claude_code | qaify_agent (reporter ≥ 0.10; output Qaify Agent membawa engine & provider) */
|
|
15
15
|
engine?: string;
|
|
16
16
|
provider?: string;
|
|
17
|
-
/** ai_key_invalid | ai_credit_low | model_no_tools | max_turns | …: Qaify tidak mengeskalasi galat key / saldo */
|
|
17
|
+
/** ai_not_logged_in | ai_key_invalid | ai_credit_low | model_no_tools | max_turns | …: Qaify tidak mengeskalasi galat login / key / saldo */
|
|
18
18
|
error_code?: string;
|
|
19
19
|
}
|
|
20
20
|
/** Dari objek hasil `claude -p` yang sudah di-parse; null bila tidak ada data pemakaian sama sekali. */
|
package/dist/usage.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Sisa kuota resmi langganan tidak ada di output ini; yang tercatat hanya pemakaian per panggilan + tanda kena limit.
|
|
3
3
|
// "Claude AI usage limit reached|1762160400", "5-hour limit reached ∙ resets 3pm", "You've hit your limit · resets …"
|
|
4
4
|
const LIMIT_PATTERN = /usage limit|limit reached|hit your (?:usage )?limit|rate[ _-]?limit|resets? (?:at )?\d/i;
|
|
5
|
+
// Claude Code tanpa kredensial (secret CLAUDE_CODE_OAUTH_TOKEN kosong) / token OAuth kedaluwarsa atau dicabut
|
|
6
|
+
const NOT_LOGGED_IN = /not logged in|please run \/login|oauth token (?:has )?(?:expired|been revoked)|invalid oauth/i;
|
|
5
7
|
// API key (Claude Code + ANTHROPIC_API_KEY): key salah / saldo habis bukan limit langganan
|
|
6
8
|
const KEY_INVALID = /invalid (?:x-)?api[ _-]?key|authentication_error|invalid bearer token|unauthorized/i;
|
|
7
9
|
const CREDIT_LOW = /credit balance is too low|insufficient (?:credits|balance|quota)|billing/i;
|
|
@@ -25,7 +27,17 @@ export function parseClaudeUsage(parsed) {
|
|
|
25
27
|
// modelUsage: { "<model-id>": {...} }; ambil model dengan token keluaran terbanyak
|
|
26
28
|
const models = Object.entries((parsed.modelUsage ?? {}));
|
|
27
29
|
const model = models.sort(([, a], [, b]) => (b.outputTokens ?? 0) - (a.outputTokens ?? 0))[0]?.[0] ?? null;
|
|
28
|
-
const errorCode = typeof parsed.error_code === 'string'
|
|
30
|
+
const errorCode = typeof parsed.error_code === 'string'
|
|
31
|
+
? parsed.error_code
|
|
32
|
+
: !isError
|
|
33
|
+
? null
|
|
34
|
+
: NOT_LOGGED_IN.test(text)
|
|
35
|
+
? 'ai_not_logged_in'
|
|
36
|
+
: KEY_INVALID.test(text)
|
|
37
|
+
? 'ai_key_invalid'
|
|
38
|
+
: CREDIT_LOW.test(text)
|
|
39
|
+
? 'ai_credit_low'
|
|
40
|
+
: null;
|
|
29
41
|
return {
|
|
30
42
|
model,
|
|
31
43
|
input_tokens: Number(u?.input_tokens ?? 0) + Number(u?.cache_creation_input_tokens ?? 0) + Number(u?.cache_read_input_tokens ?? 0),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qaify-playwright-reporter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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",
|
package/templates/auto-fix.yml
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
# Butuh di repo ini (sama dengan qa.yml):
|
|
5
5
|
# secret QA_FACTORY_TOKEN token reporter repo ini
|
|
6
6
|
# variable QA_FACTORY_URL URL API Qaify
|
|
7
|
-
# secret CLAUDE_CODE_OAUTH_TOKEN langganan Claude Pro/Max Anda (`claude setup-token`); tidak perlu bila
|
|
8
|
-
#
|
|
9
|
-
# "pakai setelan Agen AI akun
|
|
7
|
+
# secret CLAUDE_CODE_OAUTH_TOKEN opsional: langganan Claude Pro/Max Anda (`claude setup-token`); tidak perlu bila
|
|
8
|
+
# token itu disimpan sekali di Qaify (Setelan → Agen AI) atau agen "Perbaikan test"
|
|
9
|
+
# memakai API key — keduanya butuh admin repo memilih "pakai setelan Agen AI akun
|
|
10
|
+
# saya" di Otomatisasi & AI
|
|
10
11
|
# GitHub App Qaify butuh izin Actions, Contents, dan Pull requests: Read and write di repo ini.
|
|
11
12
|
# Job "fix" menjalankan agen tanpa token push; job "submit" (VM bersih) meng-clone ulang, menerapkan patch, lalu push.
|
|
12
13
|
# Nama file harus tetap auto-fix.yml.
|
|
@@ -56,7 +57,7 @@ jobs:
|
|
|
56
57
|
tools: composer
|
|
57
58
|
|
|
58
59
|
- name: Pasang Qaify CLI
|
|
59
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
60
|
+
run: npm install -g qaify-playwright-reporter@0.11
|
|
60
61
|
|
|
61
62
|
# token clone sekali pakai (read-only, hanya repo ini) dicabut sebelum agen berjalan
|
|
62
63
|
- name: Ambil kegagalan & clone repo
|
|
@@ -87,7 +88,7 @@ jobs:
|
|
|
87
88
|
QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
|
|
88
89
|
run: |
|
|
89
90
|
qaify ai-env --kind fix --id "${{ inputs.fix_attempt_id }}" \
|
|
90
|
-
--env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json"
|
|
91
|
+
--env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json" --oauth
|
|
91
92
|
|
|
92
93
|
# hanya kredensial AI (secret langganan / API key Anthropic dari Qaify); tidak ada token Qaify maupun token push
|
|
93
94
|
- name: Perbaiki dengan Claude Code
|
|
@@ -100,11 +101,18 @@ jobs:
|
|
|
100
101
|
MODEL: ${{ steps.ai.outputs.model || steps.ctx.outputs.model }}
|
|
101
102
|
CI: 'true'
|
|
102
103
|
run: |
|
|
104
|
+
# tanpa secret langganan dan tanpa API key: Claude Code pasti "Not logged in"; catat penyebabnya, jangan dipasang
|
|
105
|
+
if [ ! -f "$RUNNER_TEMP/qaify-ai.env" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
|
106
|
+
echo '::error::Secret CLAUDE_CODE_OAUTH_TOKEN kosong di repo ini dan engineer tidak memakai API key. Jalankan "claude setup-token" lalu isi secret-nya, atau simpan token itu sekali di Qaify (Setelan, Agen AI) agar berlaku untuk semua repo, atau pakai API key di sana.'
|
|
107
|
+
printf '%s' '{"type":"result","subtype":"error_qaify","is_error":true,"error_code":"ai_not_logged_in","result":"Not logged in (token langganan Claude kosong: tidak ada di Qaify maupun secret repo)"}' > ../.qaify/claude.json
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
103
110
|
npm install -g @anthropic-ai/claude-code
|
|
104
|
-
# API key Anthropic dari Qaify menggantikan
|
|
111
|
+
# API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
|
|
105
112
|
if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
|
|
113
|
+
unset CLAUDE_CODE_OAUTH_TOKEN
|
|
106
114
|
set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
|
|
107
|
-
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
115
|
+
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
108
116
|
fi
|
|
109
117
|
claude -p "$(cat ../.qaify/prompt.txt)" \
|
|
110
118
|
--model "$MODEL" \
|
|
@@ -154,7 +162,7 @@ jobs:
|
|
|
154
162
|
- uses: actions/setup-node@v4
|
|
155
163
|
with: { node-version: 22 }
|
|
156
164
|
- name: Pasang Qaify CLI
|
|
157
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
165
|
+
run: npm install -g qaify-playwright-reporter@0.11
|
|
158
166
|
- uses: actions/download-artifact@v4
|
|
159
167
|
continue-on-error: true
|
|
160
168
|
with:
|
package/templates/qa.yml
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# secret QA_FACTORY_TOKEN token reporter dari Pengaturan → Token Reporter
|
|
4
4
|
# variable QA_FACTORY_URL URL API QA Factory
|
|
5
5
|
# secret CLAUDE_CODE_OAUTH_TOKEN opsional, analisis AI dengan langganan Claude Pro/Max Anda (`claude setup-token`);
|
|
6
|
-
#
|
|
6
|
+
# tidak perlu bila token itu disimpan sekali di Qaify → Setelan → Agen AI (atau
|
|
7
|
+
# pakai API key di sana) dan admin repo memilih "pakai setelan Agen AI akun saya"
|
|
7
8
|
# variable QA_FACTORY_AI_MODEL opsional, default sonnet (langganan tanpa model dari Qaify)
|
|
8
9
|
# dan devDependency qaify-playwright-reporter (sudah terpasang bila Anda menjalankan `npx qaify init`).
|
|
9
10
|
# Nama file harus tetap qa.yml.
|
|
@@ -34,8 +35,9 @@ jobs:
|
|
|
34
35
|
|
|
35
36
|
# Status final run & analisis kegagalan. Tetap jalan walau test gagal / di-cancel.
|
|
36
37
|
# Analisis AI memakai langganan Claude Pro/Max Anda lewat Claude Code (QA Factory tidak memanggil AI):
|
|
37
|
-
# claude setup-token → simpan
|
|
38
|
-
# kegagalan tetap diklasifikasi aturan / "belum jelas". Kuota langganan dipakai
|
|
38
|
+
# claude setup-token → simpan sekali di Qaify (Setelan → Agen AI) atau sebagai secret CLAUDE_CODE_OAUTH_TOKEN.
|
|
39
|
+
# Tanpa token (mis. PR dari fork) kegagalan tetap diklasifikasi aturan / "belum jelas". Kuota langganan dipakai
|
|
40
|
+
# bersama pemakaian pribadi.
|
|
39
41
|
analyze:
|
|
40
42
|
needs: test
|
|
41
43
|
if: always()
|
|
@@ -68,7 +70,7 @@ jobs:
|
|
|
68
70
|
id: ai
|
|
69
71
|
if: steps.queue.outputs.count != '0'
|
|
70
72
|
continue-on-error: true
|
|
71
|
-
run: npx qaify ai-env --kind analyze --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$RUNNER_TEMP/qaf/claude.json"
|
|
73
|
+
run: npx qaify ai-env --kind analyze --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$RUNNER_TEMP/qaf/claude.json" --oauth
|
|
72
74
|
|
|
73
75
|
# folder kosong: .claude/ dan .mcp.json milik repo tidak ikut termuat; semua tool dimatikan
|
|
74
76
|
- name: Analisis dengan Claude Code
|
|
@@ -83,10 +85,11 @@ jobs:
|
|
|
83
85
|
MODEL: ${{ steps.ai.outputs.model || vars.QA_FACTORY_AI_MODEL || 'sonnet' }}
|
|
84
86
|
run: |
|
|
85
87
|
npm install -g @anthropic-ai/claude-code
|
|
86
|
-
# API key Anthropic dari Qaify menggantikan
|
|
88
|
+
# API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
|
|
87
89
|
if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
|
|
90
|
+
unset CLAUDE_CODE_OAUTH_TOKEN
|
|
88
91
|
set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
|
|
89
|
-
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
92
|
+
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
90
93
|
fi
|
|
91
94
|
claude -p "Analisis setiap kegagalan test pada input JSON." \
|
|
92
95
|
--model "$MODEL" \
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
# secret QA_FACTORY_TOKEN token reporter repo runner (Pengaturan → Token Reporter)
|
|
5
5
|
# variable QA_FACTORY_URL URL API Qaify
|
|
6
6
|
# secret CLAUDE_CODE_OAUTH_TOKEN opsional: agen yang memakai langganan Claude Pro/Max (`claude setup-token`);
|
|
7
|
-
# tidak perlu bila
|
|
7
|
+
# tidak perlu bila token itu sudah disimpan sekali di Qaify (Setelan → Agen AI,
|
|
8
|
+
# berlaku untuk semua repo) atau semua agen memakai API key
|
|
8
9
|
# variable QA_FACTORY_AUDIT_MODE opsional, `static` / `claude`; hanya dipakai bila Qaify tidak mengirim mode
|
|
9
10
|
# variable QA_FACTORY_AI_MODEL opsional, default sonnet (langganan tanpa model dari Qaify)
|
|
10
11
|
# variable QA_FACTORY_MCP_VERSION opsional, versi @playwright/mcp (default latest)
|
|
@@ -61,7 +62,7 @@ jobs:
|
|
|
61
62
|
tools: composer
|
|
62
63
|
|
|
63
64
|
- name: Pasang Qaify CLI
|
|
64
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
65
|
+
run: npm install -g qaify-playwright-reporter@0.11 playwright-core@1
|
|
65
66
|
|
|
66
67
|
# token clone sekali pakai (read-only, hanya repo FE + BE) dicabut sebelum auditor berjalan
|
|
67
68
|
- name: Ambil konteks & clone repo FE + BE
|
|
@@ -81,7 +82,7 @@ jobs:
|
|
|
81
82
|
QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
|
|
82
83
|
run: |
|
|
83
84
|
qaify ai-env --kind audit --id "${{ inputs.audit_id }}" \
|
|
84
|
-
--env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json"
|
|
85
|
+
--env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json" --oauth
|
|
85
86
|
|
|
86
87
|
# pilihan Qaify → variable QA_FACTORY_AUDIT_MODE → claude bila secret ada; AI tanpa langganan / API key → static
|
|
87
88
|
- name: Tentukan mode audit
|
|
@@ -92,7 +93,7 @@ jobs:
|
|
|
92
93
|
HAS_KEY: ${{ steps.ai.outputs.has_key }}
|
|
93
94
|
ENGINE: ${{ steps.ai.outputs.engine || 'claude_code' }}
|
|
94
95
|
run: |
|
|
95
|
-
mode="${REQUESTED:-$([ "$HAS_CLAUDE" = true ] && echo claude || echo static)}"
|
|
96
|
+
mode="${REQUESTED:-$([ "$HAS_CLAUDE" = true ] || [ "$HAS_KEY" = true ] && echo claude || echo static)}"
|
|
96
97
|
if [ "$mode" != static ] && [ "$mode" != claude ]; then
|
|
97
98
|
echo "::warning::Mode audit tidak dikenal ($mode); memakai static."
|
|
98
99
|
mode=static
|
|
@@ -101,7 +102,7 @@ jobs:
|
|
|
101
102
|
# hasil gagal + penyebabnya sudah ditulis step "Pilih agen AI"
|
|
102
103
|
mode=failed
|
|
103
104
|
elif [ "$mode" = claude ] && [ "$HAS_CLAUDE" != true ] && [ "$HAS_KEY" != true ]; then
|
|
104
|
-
echo "::warning::Mode AI diminta tetapi secret CLAUDE_CODE_OAUTH_TOKEN
|
|
105
|
+
echo "::warning::Mode AI diminta tetapi token langganan Claude kosong (tidak disimpan di Qaify, Setelan → Agen AI, maupun secret CLAUDE_CODE_OAUTH_TOKEN repo) dan agen Auditor tidak memakai API key; audit dijalankan tanpa AI."
|
|
105
106
|
mode=static
|
|
106
107
|
fi
|
|
107
108
|
echo "qa-factory: mode audit $mode ($ENGINE)"
|
|
@@ -137,10 +138,11 @@ jobs:
|
|
|
137
138
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
138
139
|
MODEL: ${{ steps.ai.outputs.model || vars.QA_FACTORY_AI_MODEL || 'sonnet' }}
|
|
139
140
|
run: |
|
|
140
|
-
# API key Anthropic dari Qaify menggantikan
|
|
141
|
+
# API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
|
|
141
142
|
if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
|
|
143
|
+
unset CLAUDE_CODE_OAUTH_TOKEN
|
|
142
144
|
set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
|
|
143
|
-
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
145
|
+
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
144
146
|
fi
|
|
145
147
|
claude -p "$(cat .qaify/prompt.txt)" \
|
|
146
148
|
--model "$MODEL" \
|
package/templates/qaify-fix.yml
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
# Butuh di repo runner ini (sama dengan qaify-audit.yml):
|
|
4
4
|
# secret QA_FACTORY_TOKEN token reporter repo runner
|
|
5
5
|
# variable QA_FACTORY_URL URL API Qaify
|
|
6
|
-
# secret CLAUDE_CODE_OAUTH_TOKEN langganan Claude Pro/Max Anda (`claude setup-token`)
|
|
7
|
-
# memakai langganan
|
|
6
|
+
# secret CLAUDE_CODE_OAUTH_TOKEN opsional: langganan Claude Pro/Max Anda (`claude setup-token`) untuk engineer yang
|
|
7
|
+
# memakai langganan; tidak perlu bila token itu sudah disimpan sekali di Qaify
|
|
8
|
+
# (Setelan → Agen AI, berlaku untuk semua repo) atau semua engineer memakai API key
|
|
8
9
|
# GitHub App Qaify butuh izin Contents: Read and write dan Pull requests: Read and write di repo FE/BE.
|
|
9
10
|
# Engineer (Nanda / Fajar / Pak Yoga) dipilih Qaify per temuan menurut kompleksitas atau pilihan Anda; mesin, model,
|
|
10
11
|
# dan API key-nya dari Setelan → Agen AI (Claude Code dengan langganan / API key Anthropic, atau Qaify Agent dengan
|
|
@@ -59,7 +60,7 @@ jobs:
|
|
|
59
60
|
tools: composer
|
|
60
61
|
|
|
61
62
|
- name: Pasang Qaify CLI
|
|
62
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
63
|
+
run: npm install -g qaify-playwright-reporter@0.11
|
|
63
64
|
|
|
64
65
|
# token clone sekali pakai (read-only, hanya repo target) dicabut sebelum agen berjalan
|
|
65
66
|
- name: Ambil temuan & clone repo target
|
|
@@ -88,7 +89,7 @@ jobs:
|
|
|
88
89
|
QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
|
|
89
90
|
run: |
|
|
90
91
|
qaify ai-env --kind audit_fix --id "${{ inputs.fix_id }}" \
|
|
91
|
-
--env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json"
|
|
92
|
+
--env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json" --oauth
|
|
92
93
|
|
|
93
94
|
# hanya kredensial AI (secret langganan / API key Anthropic dari Qaify); tidak ada token Qaify maupun token push
|
|
94
95
|
- name: Perbaiki dengan Claude Code
|
|
@@ -100,11 +101,18 @@ jobs:
|
|
|
100
101
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
101
102
|
MODEL: ${{ steps.ai.outputs.model || steps.ctx.outputs.model }}
|
|
102
103
|
run: |
|
|
104
|
+
# tanpa secret langganan dan tanpa API key: Claude Code pasti "Not logged in"; catat penyebabnya, jangan dipasang
|
|
105
|
+
if [ ! -f "$RUNNER_TEMP/qaify-ai.env" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
|
|
106
|
+
echo '::error::Secret CLAUDE_CODE_OAUTH_TOKEN kosong di repo runner ini dan engineer tidak memakai API key. Jalankan "claude setup-token" lalu isi secret-nya, atau simpan token itu sekali di Qaify (Setelan, Agen AI) agar berlaku untuk semua repo, atau pakai API key di sana.'
|
|
107
|
+
printf '%s' '{"type":"result","subtype":"error_qaify","is_error":true,"error_code":"ai_not_logged_in","result":"Not logged in (token langganan Claude kosong: tidak ada di Qaify maupun secret repo)"}' > ../.qaify/claude.json
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
103
110
|
npm install -g @anthropic-ai/claude-code
|
|
104
|
-
# API key Anthropic dari Qaify menggantikan
|
|
111
|
+
# API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
|
|
105
112
|
if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
|
|
113
|
+
unset CLAUDE_CODE_OAUTH_TOKEN
|
|
106
114
|
set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
|
|
107
|
-
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
115
|
+
rm -f "$RUNNER_TEMP/qaify-ai.env"
|
|
108
116
|
fi
|
|
109
117
|
claude -p "$(cat ../.qaify/prompt.txt)" \
|
|
110
118
|
--model "$MODEL" \
|
|
@@ -152,7 +160,7 @@ jobs:
|
|
|
152
160
|
- uses: actions/setup-node@v4
|
|
153
161
|
with: { node-version: 22 }
|
|
154
162
|
- name: Pasang Qaify CLI
|
|
155
|
-
run: npm install -g qaify-playwright-reporter@0.
|
|
163
|
+
run: npm install -g qaify-playwright-reporter@0.11
|
|
156
164
|
- uses: actions/download-artifact@v4
|
|
157
165
|
continue-on-error: true
|
|
158
166
|
with:
|