@skyramp/mcp 0.2.7 → 0.2.8
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/build/index.js +5 -2
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -5
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +2 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +114 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +2 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +292 -0
- package/build/prompts/sut-setup/shared.d.ts +39 -0
- package/build/prompts/sut-setup/shared.js +132 -0
- package/build/prompts/test-maintenance/driftAnalysisSections.js +9 -3
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -1
- package/build/prompts/test-recommendation/diffExecutionPlan.js +76 -2
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +91 -0
- package/build/prompts/testbot/testbot-prompts.d.ts +16 -1
- package/build/prompts/testbot/testbot-prompts.js +113 -22
- package/build/prompts/testbot/testbot-prompts.test.js +95 -45
- package/build/resources/sutSetupResource.d.ts +2 -0
- package/build/resources/sutSetupResource.js +45 -0
- package/build/resources/testbotResource.js +2 -2
- package/build/services/TestDiscoveryService.d.ts +2 -2
- package/build/services/TestDiscoveryService.js +22 -12
- package/build/services/TestExecutionService.d.ts +10 -1
- package/build/services/TestExecutionService.js +158 -26
- package/build/services/TestExecutionService.test.js +306 -0
- package/build/tools/executeSkyrampTestTool.js +26 -7
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +1 -1
- package/build/tools/submitReportTool.d.ts +10 -0
- package/build/tools/submitReportTool.js +106 -39
- package/build/tools/submitReportTool.test.js +296 -50
- package/build/tools/test-management/actionsTool.js +21 -7
- package/build/tools/test-management/analyzeChangesTool.d.ts +1 -1
- package/build/tools/test-management/analyzeChangesTool.js +106 -41
- package/build/tools/test-management/analyzeChangesTool.test.js +21 -41
- package/build/tools/test-management/analyzeTestHealthTool.js +16 -9
- package/build/tools/workspace/initializeWorkspaceTool.js +39 -22
- package/build/tools/workspace/serviceUpsert.d.ts +12 -0
- package/build/tools/workspace/serviceUpsert.js +23 -0
- package/build/tools/workspace/serviceUpsert.test.d.ts +1 -0
- package/build/tools/workspace/serviceUpsert.test.js +50 -0
- package/build/types/TestAnalysis.d.ts +2 -1
- package/build/types/TestExecution.d.ts +12 -2
- package/build/types/TestExecution.js +11 -1
- package/build/types/TestbotReport.d.ts +68 -0
- package/build/types/TestbotReport.js +1 -0
- package/build/types/index.d.ts +4 -0
- package/build/types/index.js +3 -0
- package/build/utils/AnalysisStateManager.d.ts +61 -1
- package/build/utils/AnalysisStateManager.js +144 -3
- package/build/utils/AnalysisStateManager.test.js +98 -0
- package/build/utils/docker.test.js +1 -1
- package/build/utils/routeParsers.d.ts +0 -10
- package/build/utils/routeParsers.js +31 -38
- package/build/utils/scenarioDrafting.d.ts +1 -1
- package/build/utils/scenarioDrafting.js +23 -1
- package/build/utils/utils.d.ts +7 -1
- package/build/utils/utils.js +10 -0
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +10 -10
- package/build/workspace/workspace.js +7 -7
- package/build/workspace/workspace.test.js +6 -6
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +31 -13
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +88 -15
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -3
- package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +23 -126
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +79 -19
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/aliasRemapper.js +84 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +15 -4
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-CPZKmjFR.js → codeMirrorModule-D0BjbCb7.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +193 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-B0JOjboO.js → codeMirrorModule-Cqp3cwEJ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-1anWeyDf.js → defaultSettingsView-CYf9adZh.js} +79 -79
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.Cc5029a3.js → index.B_7ywgmr.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.Wo5yvvVh.js → uiMode.u4_8VnCV.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +129 -43
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/aliasRemapper.ts +119 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +33 -10
- package/node_modules/playwright/package.json +1 -1
- package/package.json +10 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.bin/node-which +0 -52
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/is-docker +0 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/mime +0 -46
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.bin/yaml +0 -11
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-CsgPR2wy.js +0 -193
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.CP3Y8Zsb.js +0 -2
package/build/index.js
CHANGED
|
@@ -26,6 +26,7 @@ import { registerBatchScenarioTestTool } from "./tools/generate-tests/generateBa
|
|
|
26
26
|
import { registerMockTool } from "./tools/generate-tests/generateMockRestTool.js";
|
|
27
27
|
import { registerAnalyzeChangesTool, registerAnalyzeTestHealthTool, registerActionsTool, } from "./tools/test-management/index.js";
|
|
28
28
|
import { registerTestbotPrompt } from "./prompts/testbot/testbot-prompts.js";
|
|
29
|
+
import { registerSutSetupResource } from "./resources/sutSetupResource.js";
|
|
29
30
|
import { registerTestbotResource } from "./resources/testbotResource.js";
|
|
30
31
|
import { registerSubmitReportTool } from "./tools/submitReportTool.js";
|
|
31
32
|
import { registerInitializeWorkspaceTool } from "./tools/workspace/initializeWorkspaceTool.js";
|
|
@@ -123,7 +124,9 @@ const prompts = [
|
|
|
123
124
|
if (isTestbotEnabled()) {
|
|
124
125
|
prompts.push(registerTestbotPrompt);
|
|
125
126
|
registerTestbotResource(server);
|
|
126
|
-
logger.info("
|
|
127
|
+
logger.info("Testbot prompt enabled via SKYRAMP_FEATURE_TESTBOT");
|
|
128
|
+
registerSutSetupResource(server);
|
|
129
|
+
logger.info("Testbot SUT setup resource enabled via SKYRAMP_FEATURE_TESTBOT");
|
|
127
130
|
}
|
|
128
131
|
prompts.forEach((registerPrompt) => registerPrompt(server));
|
|
129
132
|
logger.info("All prompts registered successfully");
|
|
@@ -173,7 +176,7 @@ const infrastructureTools = [
|
|
|
173
176
|
];
|
|
174
177
|
if (isTestbotEnabled()) {
|
|
175
178
|
infrastructureTools.push(registerSubmitReportTool);
|
|
176
|
-
logger.info("
|
|
179
|
+
logger.info("Testbot tools enabled via SKYRAMP_FEATURE_TESTBOT");
|
|
177
180
|
}
|
|
178
181
|
infrastructureTools.forEach((registerTool) => registerTool(server));
|
|
179
182
|
// Register Playwright browser tools (trace recording via browser automation)
|
|
@@ -186,21 +186,40 @@ await page.context().addInitScript(() => {
|
|
|
186
186
|
});
|
|
187
187
|
\`\`\`
|
|
188
188
|
|
|
189
|
-
And immediately after **every** \`page.goto()\` call in the test, add exactly these
|
|
189
|
+
And immediately after **every** \`page.goto()\` call in the test, add exactly these three statements — do NOT substitute a different activation approach or different \`waitForFunction\` bodies:
|
|
190
190
|
|
|
191
191
|
\`\`\`typescript
|
|
192
|
+
// Wait for Flutter to bootstrap (confirms Flutter engine is ready before activating)
|
|
193
|
+
await page.waitForFunction(
|
|
194
|
+
() => !!document.querySelector('flt-semantics-placeholder, flt-glass-pane, flutter-view'),
|
|
195
|
+
undefined,
|
|
196
|
+
{ timeout: 30000 }
|
|
197
|
+
);
|
|
192
198
|
// Activate Flutter accessibility semantics by clicking the hidden placeholder
|
|
193
199
|
await page.evaluate(() => { const el = document.querySelector('flt-semantics-placeholder'); if (el) el.dispatchEvent(new MouseEvent('click', { isTrusted: true, bubbles: true, cancelable: true })); });
|
|
194
|
-
// Wait for
|
|
200
|
+
// Wait for the semantics tree to be live: the activation placeholder is gone AND
|
|
201
|
+
// at least one flt-semantics node exists. Requiring a populated tree (not just the
|
|
202
|
+
// placeholder's absence) guards against a vacuous pass when the bootstrap wait
|
|
203
|
+
// above resolved on flt-glass-pane/flutter-view before the placeholder ever mounted.
|
|
195
204
|
await page.waitForFunction(
|
|
196
|
-
() => document.querySelector('flt-semantics-
|
|
205
|
+
() => document.querySelector('flt-semantics-placeholder') === null
|
|
206
|
+
&& document.querySelector('flt-semantics') !== null,
|
|
207
|
+
undefined,
|
|
197
208
|
{ timeout: 20000 }
|
|
198
209
|
);
|
|
199
210
|
\`\`\`
|
|
200
211
|
|
|
201
|
-
|
|
212
|
+
Use exactly the three statements above. Do NOT use the two-argument form \`waitForFunction(fn, { timeout })\` — the timeout must be the third argument with \`undefined\` as the second. If you see an existing body click, \`locator.click\`, or old-style \`waitForFunction\` after a goto, replace the entire block with the exact three statements above.
|
|
213
|
+
|
|
214
|
+
This is required because Flutter web renders to a \`<canvas>\` element (no traditional DOM). The first \`waitForFunction\` ensures Flutter has bootstrapped before activation; the \`dispatchEvent\` activates semantics; the second \`waitForFunction\` confirms the semantics tree is live (placeholder removed **and** \`flt-semantics\` nodes present). The combined gate handles both modes — when accessibility is pre-enabled (semantics active from frame 1, no placeholder ever shown) and when activation depends on the click — without passing prematurely on a frame where neither has happened yet.
|
|
202
215
|
|
|
203
|
-
|
|
216
|
+
**Then wait for the first element you interact with.** The gate above confirms the semantics tree *started* building, but Flutter populates it incrementally — individual widgets (a textbox, a button) can still be a beat behind when the gate passes, especially on a cold start where accessibility was not pre-enabled. The wrapped Skyramp page also enforces a short internal action timeout, so a lagging widget fails fast rather than waiting out the page default. Guard against this: immediately after the activation block (and after any later \`page.goto()\`'s block), add an explicit \`waitFor\` on the **first locator the test interacts with on that page**, before the interaction itself. Example:
|
|
217
|
+
\`\`\`typescript
|
|
218
|
+
// Wait for the specific target to be present before interacting (tree may still be filling in)
|
|
219
|
+
await page.getByRole('textbox', { name: 'Username' }).waitFor({ state: 'visible', timeout: 20000 });
|
|
220
|
+
await page.getByRole('textbox', { name: 'Username' }).fill('admin');
|
|
221
|
+
\`\`\`
|
|
222
|
+
Use the **same locator** that the next interaction uses — do not invent a new selector. This waits for exactly the widget the test needs, which is more reliable than any generic tree-node count (those are app-specific magic numbers); it is the per-target wait that Flutter-web automation guidance recommends over a one-size-fits-all readiness check.
|
|
204
223
|
|
|
205
224
|
**Also check for brittle flt-semantic-node-* selectors:** If the test uses \`page.locator("#flt-semantic-node-N")\` for any interaction (clicks, navigations), replace it with a semantic selector (\`getByRole\`, \`getByText\`, or \`getByLabel\`) — \`flt-semantic-node-N\` IDs change between sessions and across devices, making the test brittle. Example: replace \`page.locator("#flt-semantic-node-78").click()\` with \`page.getByText("Settings").click()\` using the visible label from the same line's context.
|
|
206
225
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { TESTBOT_WORKFLOW_PATH, buildContextBlock, buildCommonSutErrorsSection, buildLocalValidationSection, buildInputsBody, buildSutDefinitionBlock, } from "../shared.js";
|
|
2
|
+
import { getPersonaPrefix } from "../../personas.js";
|
|
3
|
+
import { PromptPlan } from "../../test-recommendation/promptPlan.js";
|
|
4
|
+
// ── Step body builders ────────────────────────────────────────────────────────
|
|
5
|
+
function buildReadSourcesBody(args) {
|
|
6
|
+
return args.sutSourceWorkflowFile
|
|
7
|
+
? `Read the given source GitHub workflow file \`${args.sutSourceWorkflowFile}\` (the user's hint about which steps to lift), then ALSO read the rest of \`.github/workflows/\` and all infrastructure files (docker-compose, Kubernetes manifests, Helm charts) — the full SUT shape often lives across multiple files, so do NOT tunnel into the source workflow alone. Workflows show *how* the SUT is started; infrastructure files show *what* the SUT is. When a workflow step references an infra file (e.g., \`docker compose -f infra/compose.yml up\`, \`helm upgrade <chart>\`, \`kubectl apply -f k8s/\`), open that file too — services, ports, env vars, and dependencies live there.`
|
|
8
|
+
: `Read all GitHub workflow files in \`.github/workflows/\` AND all infrastructure files (docker-compose, Kubernetes manifests, Helm charts). Workflows show *how* the SUT is started; infrastructure files show *what* the SUT is. When a workflow step references an infra file (e.g., \`docker compose -f infra/compose.yml up\`, \`helm upgrade <chart>\`, \`kubectl apply -f k8s/\`), open that file too — services, ports, env vars, and dependencies live there. Pick the source workflow (the one whose SETUP/TEST/TEARDOWN steps will be lifted) once you have the full picture.`;
|
|
9
|
+
}
|
|
10
|
+
function buildClassifyStepsBody() {
|
|
11
|
+
return `From the source workflow, classify each step by its *purpose* — not by its name or surface mechanism (\`env:\`, \`with:\`, and \`\${{ secrets.* }}\` can appear on any category and do not determine it):
|
|
12
|
+
a. SETUP — prepares the SUT (install, build, start services, migrate, seed, configure env or inject secrets for the SUT). Common attributes: \`env:\` blocks (e.g. \`DATABASE_URL\`, build flags), \`with:\` parameters (e.g. \`actions/cache\`, \`actions/setup-node\`), \`\${{ secrets.* }}\` references (e.g. registry login, deploy keys), dependency installs (e.g. \`npm ci\` before build).
|
|
13
|
+
b. TEST — runs the test suite (will be replaced by \`skyramp/testbot\`). Common attributes: \`env:\` blocks (e.g. \`API_BASE_URL\`, \`AUTH_TOKEN\`), \`with:\` parameters (e.g. \`actions/cache\`, custom test-runner actions), \`\${{ secrets.* }}\` references (e.g. test credentials, live-integration keys), dependency installs (e.g. \`npm ci\` right before \`npm test\` in the same step).
|
|
14
|
+
c. TEARDOWN — stops or cleans up.
|
|
15
|
+
d. UNRELATED — does none of those (lint, format, notifications, badges).
|
|
16
|
+
|
|
17
|
+
Output as:
|
|
18
|
+
\`\`\`
|
|
19
|
+
Step: "<step name>" → SETUP | TEST | TEARDOWN | UNRELATED
|
|
20
|
+
\`\`\``;
|
|
21
|
+
}
|
|
22
|
+
function buildSummarizeBody() {
|
|
23
|
+
return `Summarize the SUT, pulling from all sources:
|
|
24
|
+
a. Setup commands needed to bring up all services (may come from multiple workflows / infra files).
|
|
25
|
+
b. Required secrets and env vars (with their \`\${{ secrets.* }}\` references and pass-through style — \`env:\` blocks vs. \`secrets: inherit\`).
|
|
26
|
+
c. Runner needed (\`runs-on\` value; a larger runner for heavy builds — runner names vary by org, do not hardcode).
|
|
27
|
+
d. How an auth token is obtained (script, step output, or secret).`;
|
|
28
|
+
}
|
|
29
|
+
function buildDecideIntroBody() {
|
|
30
|
+
return `The SUT must always be built from the PR's source code — Testbot reuses this workflow for every future PR, so it must validate the code in the PR being tested, not a stale snapshot. Use build: blocks in compose files (or docker build against the PR checkout) so images come from PR source. Pull-only image references and pinned upstream tags are fine for sidecars (databases, queues, caches), but the application services under test must be built locally.
|
|
31
|
+
Evaluate the four patterns in strict priority order — always start with Pattern A and only escalate when it genuinely cannot express the setup. Full semantics for each lifecycle input are in the reference section below.`;
|
|
32
|
+
}
|
|
33
|
+
function buildPatternABody() {
|
|
34
|
+
return `Use when the entire setup — build, start, seed — can be expressed as one or two shell commands (e.g., docker compose up -d --build, make start, ./scripts/start.sh). Do NOT add any GHA steps. Set targetSetupCommand, targetReadyCheckCommand, targetReadyCheckTimeout, and targetTeardownCommand on the Testbot action; leave skipTargetSetup unset.`;
|
|
35
|
+
}
|
|
36
|
+
function buildPatternBBody() {
|
|
37
|
+
return `Use when the source workflow requires GHA-specific steps purely for environment or tooling (e.g., docker/setup-buildx-action, actions/cache, private registry login) but the actual service start can still be expressed as a single command. Copy only those tool/env pre-steps BEFORE the Testbot action. Set targetSetupCommand, targetReadyCheckCommand, targetReadyCheckTimeout, and targetTeardownCommand on the Testbot action; leave skipTargetSetup unset. Preserve every env:, with:, and \${{ secrets.* }} reference from the pre-steps exactly as written.`;
|
|
38
|
+
}
|
|
39
|
+
function buildPatternCBody() {
|
|
40
|
+
return `Use when the service startup itself requires multiple GHA steps that cannot be collapsed into a single command. Copy the source workflow's SETUP steps verbatim BEFORE the Testbot action. Set skipTargetSetup: 'true' and do not set targetSetupCommand. Set targetReadyCheckCommand and targetReadyCheckTimeout on the Testbot action. Add TEARDOWN steps AFTER the Testbot action with if: always(), or set targetTeardownCommand if a single command suffices — pick one, not both. Preserve every env:, with:, and \${{ secrets.* }} reference exactly as written.`;
|
|
41
|
+
}
|
|
42
|
+
function buildPatternDBody() {
|
|
43
|
+
return `Use when teardown is also too complex for a single command. Copy the SETUP steps BEFORE and TEARDOWN steps AFTER the Testbot action (with if: always()). Set skipTargetSetup: 'true'. Do not set targetSetupCommand or targetTeardownCommand. Preserve every env:, with:, and \${{ secrets.* }} reference exactly as written.`;
|
|
44
|
+
}
|
|
45
|
+
function buildEditWorkflowBody() {
|
|
46
|
+
return `Edit \`${TESTBOT_WORKFLOW_PATH}\` (already created by the Testbot installer):
|
|
47
|
+
a. Apply the chosen pattern:
|
|
48
|
+
- Pattern A: set lifecycle inputs on the Testbot action, no GHA steps.
|
|
49
|
+
- Pattern B: add GHA tool/env pre-steps BEFORE the Testbot action, set lifecycle inputs (\`targetSetupCommand\`, \`targetReadyCheckCommand\`, \`targetTeardownCommand\`), leave \`skipTargetSetup\` unset.
|
|
50
|
+
- Pattern C: add GHA SETUP steps BEFORE the Testbot action, set \`skipTargetSetup: 'true'\`, set \`targetReadyCheckCommand\`, add TEARDOWN steps AFTER or set \`targetTeardownCommand\` — do NOT set \`targetSetupCommand\`.
|
|
51
|
+
- Pattern D: add GHA SETUP steps BEFORE and TEARDOWN steps AFTER the Testbot action, set \`skipTargetSetup: 'true'\`, do NOT set \`targetSetupCommand\` or \`targetTeardownCommand\`.
|
|
52
|
+
b. Update \`runs-on\` to match the runner needed by the source workflow's setup — runner names vary by org, do not hardcode (see runner-label guidance in Step 3 above).
|
|
53
|
+
c. Pass all required secrets through — reuse the same \`\${{ secrets.* }}\` references or \`secrets: inherit\`. For GHA steps (Patterns B, C, D), set secrets via \`env:\` on the individual steps that need them; for lifecycle inputs (Patterns A, B), set them on the Testbot action or at the job level.
|
|
54
|
+
d. The basic file already contains the \`skyramp/testbot\` action — REPLACE the original workflow's test-run step with it, do NOT add a duplicate test runner step.`;
|
|
55
|
+
}
|
|
56
|
+
function buildConfigureAuthBody() {
|
|
57
|
+
return `Configure auth — use the lifecycle input reference above for full semantics of \`authTokenCommand\` and \`uiCredentials\`:
|
|
58
|
+
a. If the source workflow exports or seeds an auth token, set \`authTokenCommand\` on the Testbot action (or create \`.skyramp/sut/get-auth-token.sh\`).
|
|
59
|
+
b. If any service requires browser login, set \`uiCredentials\` on the Testbot action.
|
|
60
|
+
c. Omit both when the SUT is unauthenticated.`;
|
|
61
|
+
}
|
|
62
|
+
function buildHandleShapeBody() {
|
|
63
|
+
return `Handle source-workflow shape:
|
|
64
|
+
a. If the original workflow uses \`matrix\` builds, pick ONE configuration for the Testbot workflow (matrix runs would multiply Testbot invocations).
|
|
65
|
+
b. If the original workflow uses \`needs:\` (job dependencies), inline the dependent job's steps into the Testbot job — Testbot runs as a single job.`;
|
|
66
|
+
}
|
|
67
|
+
function buildAdaptRestrictionsBody() {
|
|
68
|
+
return `Do not modify any other GitHub workflow files referenced. The only github workflow file you may edit is \`${TESTBOT_WORKFLOW_PATH}\`; every other workflow under \`.github/workflows/\` (including the source workflow) is read-only reference material.`;
|
|
69
|
+
}
|
|
70
|
+
// ── PromptPlan declaration ────────────────────────────────────────────────────
|
|
71
|
+
const _adaptPlan = new PromptPlan()
|
|
72
|
+
.addPhase("adapt", "", { headerLevel: "hidden", stepFormat: "hash" })
|
|
73
|
+
.step("SCAN", "Understand the System Under Test in this repository", () => "")
|
|
74
|
+
.subStep("READ_SOURCES", "Read workflows and infra files", buildReadSourcesBody)
|
|
75
|
+
.subStep("CLASSIFY_STEPS", "Classify source workflow steps", buildClassifyStepsBody)
|
|
76
|
+
.subStep("SUMMARIZE", "Summarize the SUT", buildSummarizeBody)
|
|
77
|
+
.step("DECIDE", "Decide SUT setup strategy", buildDecideIntroBody)
|
|
78
|
+
.subStep("PATTERN_A", "Pattern A — Testbot lifecycle commands only (try this first, always)", buildPatternABody)
|
|
79
|
+
.subStep("PATTERN_B", "Pattern B — Hybrid: GHA pre-steps for tool/env setup + Testbot lifecycle commands for service start", buildPatternBBody)
|
|
80
|
+
.subStep("PATTERN_C", "Pattern C — GHA steps replace service startup + Testbot lifecycle validation", buildPatternCBody)
|
|
81
|
+
.subStep("PATTERN_D", "Pattern D — Fully wrapped by GHA steps", buildPatternDBody)
|
|
82
|
+
.step("INPUTS", "Plan the Testbot action inputs", () => buildInputsBody())
|
|
83
|
+
.step("ADAPT", "Edit the Testbot workflow and generate SUT files", () => "")
|
|
84
|
+
.subStep("EDIT_WORKFLOW", "Edit the workflow file", buildEditWorkflowBody)
|
|
85
|
+
.subStep("CONFIGURE_AUTH", "Configure auth", buildConfigureAuthBody)
|
|
86
|
+
.subStep("HANDLE_SHAPE", "Handle source-workflow shape", buildHandleShapeBody)
|
|
87
|
+
.subStep("RESTRICTIONS", "What not to do", buildAdaptRestrictionsBody)
|
|
88
|
+
.step("VERIFY", "Verify", () => "")
|
|
89
|
+
.subStep("COMMON_ERRORS", "Common SUT errors", () => buildCommonSutErrorsSection())
|
|
90
|
+
.subStep("LOCAL_VALIDATION", "Local validation", () => buildLocalValidationSection())
|
|
91
|
+
.done();
|
|
92
|
+
// ── Goal section (static prose above the plan) ────────────────────────────────
|
|
93
|
+
function buildGoalSection(args) {
|
|
94
|
+
const sourceLine = args.sutSourceWorkflowFile
|
|
95
|
+
? `The user pointed at \`${args.sutSourceWorkflowFile}\` as the source workflow whose setup/teardown steps should be copied into the Testbot workflow. Use it as your primary source of setup steps — but still read the rest of the repo (other workflows, docker-compose, Kubernetes, Helm) to understand the full SUT shape.`
|
|
96
|
+
: `No specific source workflow was provided. Read ALL available GitHub workflows in this repository to learn how services are built, started, and authenticated, and pick the source workflow whose setup steps best match a SUT for testing (typically an e2e/integration workflow, not lint/release/security workflows).`;
|
|
97
|
+
const thinkingSourceClause = args.sutSourceWorkflowFile
|
|
98
|
+
? `confirms you are using \`${args.sutSourceWorkflowFile}\` as the source of setup steps (or explains why a different workflow is a better fit)`
|
|
99
|
+
: `names the workflow you selected as the source of setup steps and explains why`;
|
|
100
|
+
return `### Goal
|
|
101
|
+
1. Generate working System Under Test (SUT) files that enable the Skyramp Testbot workflow (\`${TESTBOT_WORKFLOW_PATH}\`) to test any PR-specific code in this repository end-to-end for the services supported (REST or gRPC service APIs, queues, frontend UIs).
|
|
102
|
+
2. Source workflow: ${sourceLine}
|
|
103
|
+
3. Required output:
|
|
104
|
+
a. Output a <thinking> block that: (i) ${thinkingSourceClause}, (ii) summarizes what you learned about the SUT — services, secrets, auth, runner size — pulling from ALL workflows and infrastructure files, and (iii) decides how the SUT will be brought up — prefer Testbot lifecycle commands alone (targetSetupCommand, targetReadyCheckCommand, targetTeardownCommand) when the setup can be expressed as a single shell command; add GHA pre-steps only for tooling that cannot run in a shell (Buildx, cache, registry login) while still using lifecycle commands for service start; escalate to full GHA steps replacing service startup only when a single command cannot express it; use fully wrapped GHA steps for both setup and teardown only as a last resort. State clearly which approach was chosen and why.
|
|
105
|
+
b. Adapt the Testbot workflow file (\`${TESTBOT_WORKFLOW_PATH}\`) for bringing up the system under test.`;
|
|
106
|
+
}
|
|
107
|
+
export function getAdaptWorkflowPrompt(args) {
|
|
108
|
+
return `${getPersonaPrefix()}${buildGoalSection(args)}
|
|
109
|
+
|
|
110
|
+
${buildSutDefinitionBlock()}${buildContextBlock(args)}
|
|
111
|
+
|
|
112
|
+
${_adaptPlan.render(args)}
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { buildContextBlock, buildInputsBody, buildCommonSutErrorsSection, buildLocalValidationSection, buildSutDefinitionBlock, } from "../shared.js";
|
|
2
|
+
import { getPersonaPrefix } from "../../personas.js";
|
|
3
|
+
import { PromptPlan } from "../../test-recommendation/promptPlan.js";
|
|
4
|
+
// ── Step body builders ────────────────────────────────────────────────────────
|
|
5
|
+
function buildScanComposeBody(args) {
|
|
6
|
+
const composeFile = args.sutSourceDockerComposeFile?.trim();
|
|
7
|
+
if (composeFile) {
|
|
8
|
+
return `A specific compose file was provided: \`${composeFile}\`. Use it directly — do NOT scan for other compose files and do NOT generate a new one. (Strategy in Step 2 will be REUSE with this file.)`;
|
|
9
|
+
}
|
|
10
|
+
return `Search the repository for existing docker-compose files:
|
|
11
|
+
- \`docker-compose.yml\`, \`docker-compose.yaml\`, \`compose.yml\`, \`compose.yaml\` at root
|
|
12
|
+
- \`docker-compose.*.yml\` variants (e.g., \`docker-compose.dev.yml\`, \`docker-compose.e2e.yml\`)
|
|
13
|
+
- Compose files in subdirectories (\`deploy/\`, \`.docker/\`, etc.)
|
|
14
|
+
|
|
15
|
+
For each compose file found, determine if it is:
|
|
16
|
+
- **Application compose** — builds or runs the actual backend API / frontend UI (has \`build:\` directives pointing to repo source, or services that are the app's entry points). USE THIS.
|
|
17
|
+
- **Infrastructure-only compose** — contains only databases, Redis, message queues, mail servers, object storage. NOT sufficient for SUT setup alone.`;
|
|
18
|
+
}
|
|
19
|
+
function buildDecideStrategyBody(args) {
|
|
20
|
+
const composeFile = args.sutSourceDockerComposeFile?.trim();
|
|
21
|
+
if (composeFile) {
|
|
22
|
+
return `A specific compose file was provided in Step 1 (\`${composeFile}\`). Strategy is REUSE with this file. Confirm your decision:
|
|
23
|
+
|
|
24
|
+
\`\`\`
|
|
25
|
+
Decision: REUSE
|
|
26
|
+
Compose file: ${composeFile}
|
|
27
|
+
Reason: provided by the user
|
|
28
|
+
\`\`\``;
|
|
29
|
+
}
|
|
30
|
+
return `Choose between reuse and generate based on what Step 1 found:
|
|
31
|
+
- If an application docker-compose file was found → strategy is REUSE.
|
|
32
|
+
- If only infrastructure-only compose files exist, or no compose files found → strategy is GENERATE.
|
|
33
|
+
|
|
34
|
+
Output your decision:
|
|
35
|
+
\`\`\`
|
|
36
|
+
Decision: REUSE | GENERATE
|
|
37
|
+
Compose file: <path to file if reusing, or "none" if generating>
|
|
38
|
+
Reason: <brief explanation>
|
|
39
|
+
\`\`\``;
|
|
40
|
+
}
|
|
41
|
+
function buildPrAccurateSutSection() {
|
|
42
|
+
return `The SUT must run code from the PR branch, not a pre-built upstream image. Determine how to build the application from the PR source:
|
|
43
|
+
|
|
44
|
+
1. If the repo uses Docker images for deployment (e.g., \`SERVER_IMAGE\` env var in compose), then build a local image from the PR code and use it. Do NOT use commit-SHA-tagged images from upstream registries (those only exist for the upstream repo's CI, not for forks or testbot PRs).
|
|
45
|
+
2. If the repo builds from source (Go binary, Node app, Python package), then include the build step in the workflow before starting services.
|
|
46
|
+
3. Use a stable base/build image (e.g., the repo's official build container or a standard runtime image).
|
|
47
|
+
4. The built image or binary stays local to the runner — no registry push needed.`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build the reuse instructions body (no section header — the caller supplies it).
|
|
51
|
+
* When `composeFile` is provided the concrete path is interpolated into the
|
|
52
|
+
* `docker compose -f <path>` commands so the agent wires the given file directly;
|
|
53
|
+
* otherwise generic `<path>` placeholders are kept for the discovery flow where
|
|
54
|
+
* the agent picks the file itself.
|
|
55
|
+
*/
|
|
56
|
+
function getReuseDockerComposeInstructions(composeFile) {
|
|
57
|
+
const file = composeFile?.trim();
|
|
58
|
+
const ref = file ?? "<path>";
|
|
59
|
+
const intro = file
|
|
60
|
+
? `Use the provided application docker-compose file \`${file}\` directly. Wire it as \`targetSetupCommand\`.`
|
|
61
|
+
: `You found an existing application docker-compose file. Wire it directly as \`targetSetupCommand\`.`;
|
|
62
|
+
return `${intro}
|
|
63
|
+
|
|
64
|
+
1. Verify the compose file starts the application (not just infrastructure).
|
|
65
|
+
2. Determine the correct command:
|
|
66
|
+
- If the compose uses \`build:\` directives: \`docker compose -f ${ref} up -d --build\`
|
|
67
|
+
- If it uses pre-built images: \`docker compose -f ${ref} up -d\`
|
|
68
|
+
- If it has builder services (one-shot build steps): run builders first, then start runtime services.
|
|
69
|
+
3. Determine the health check command from the compose file's \`healthcheck:\` definitions or from known health endpoints.
|
|
70
|
+
4. Determine the teardown command: \`docker compose -f ${ref} down -v\`
|
|
71
|
+
5. If the compose file defines admin credentials in environment variables, create \`.skyramp/sut/get-auth-token.sh\` using those credentials.
|
|
72
|
+
|
|
73
|
+
These resolved commands are wired onto the \`skyramp/testbot\` action step using the input semantics from Step 3 above.`;
|
|
74
|
+
}
|
|
75
|
+
/** Generate instructions used when no reusable application compose file exists. */
|
|
76
|
+
const GENERATE_DOCKER_COMPOSE_INSTRUCTIONS = `No usable application docker-compose was found. Generate one from scratch.
|
|
77
|
+
|
|
78
|
+
Steps:
|
|
79
|
+
1. Inspect the repository to determine how to build and run the application:
|
|
80
|
+
- Does it have a Dockerfile? → Use \`build: { context: . }\`
|
|
81
|
+
- No Dockerfile but has package.json? → Use \`node:20-alpine\` base image + volume mount + npm install + start
|
|
82
|
+
- No Dockerfile but has requirements.txt/pyproject.toml? → Use \`python:3.12-slim\` base image + pip install + start
|
|
83
|
+
- No Dockerfile but has pom.xml/build.gradle? → Use appropriate JDK image + build + run
|
|
84
|
+
|
|
85
|
+
2. Determine database requirements:
|
|
86
|
+
- If app references PostgreSQL → add postgres service with healthcheck
|
|
87
|
+
- If app references MongoDB → add mongo service with replica set
|
|
88
|
+
- If app references Redis → add redis service
|
|
89
|
+
- Prefer SQLite when the app supports it (simpler, no extra service)
|
|
90
|
+
|
|
91
|
+
3. Generate \`.skyramp/sut/docker-compose.testbot.yml\` with:
|
|
92
|
+
- The compose file must be runnable with: \`docker compose -f .skyramp/sut/docker-compose.testbot.yml --project-directory . up -d --build\`
|
|
93
|
+
- The \`--project-directory .\` means build context is the repository root (NOT .skyramp/sut/)
|
|
94
|
+
- Healthchecks for ALL services (testbot relies on \`--wait\`)
|
|
95
|
+
- Simple testbot credentials in environment (admin@testbot.com / testbot)
|
|
96
|
+
- Generous \`start_period\` (30-120s) for CI cold builds
|
|
97
|
+
- For monorepos: may need a builder service that runs first
|
|
98
|
+
|
|
99
|
+
4. Generate \`.skyramp/sut/get-auth-token.sh\` if the application requires authentication:
|
|
100
|
+
- Script must output ONLY the token to stdout (debug output to stderr)
|
|
101
|
+
- Poll health endpoint before attempting auth (300s timeout)
|
|
102
|
+
- Credentials must match what's in the docker-compose environment variables
|
|
103
|
+
- Use \`set -euo pipefail\` and \`jq\` for JSON parsing
|
|
104
|
+
|
|
105
|
+
<example label="simple-single-service">
|
|
106
|
+
\`\`\`yaml
|
|
107
|
+
# .skyramp/sut/docker-compose.testbot.yml
|
|
108
|
+
services:
|
|
109
|
+
app:
|
|
110
|
+
build:
|
|
111
|
+
context: .
|
|
112
|
+
dockerfile: Dockerfile
|
|
113
|
+
ports:
|
|
114
|
+
- "8055:8055"
|
|
115
|
+
environment:
|
|
116
|
+
SECRET: testbot-secret
|
|
117
|
+
ADMIN_EMAIL: admin@testbot.com
|
|
118
|
+
ADMIN_PASSWORD: testbot
|
|
119
|
+
DB_CLIENT: sqlite3
|
|
120
|
+
healthcheck:
|
|
121
|
+
test: ["CMD", "curl", "-sf", "http://localhost:8055/health"]
|
|
122
|
+
interval: 5s
|
|
123
|
+
timeout: 5s
|
|
124
|
+
retries: 24
|
|
125
|
+
start_period: 15s
|
|
126
|
+
\`\`\`
|
|
127
|
+
</example>
|
|
128
|
+
|
|
129
|
+
<example label="service-with-postgres">
|
|
130
|
+
\`\`\`yaml
|
|
131
|
+
services:
|
|
132
|
+
postgres:
|
|
133
|
+
image: postgres:16-alpine
|
|
134
|
+
environment:
|
|
135
|
+
POSTGRES_DB: app
|
|
136
|
+
POSTGRES_USER: app
|
|
137
|
+
POSTGRES_PASSWORD: testbot
|
|
138
|
+
healthcheck:
|
|
139
|
+
test: ["CMD-SHELL", "pg_isready -U app"]
|
|
140
|
+
interval: 5s
|
|
141
|
+
timeout: 5s
|
|
142
|
+
retries: 10
|
|
143
|
+
|
|
144
|
+
app:
|
|
145
|
+
build:
|
|
146
|
+
context: .
|
|
147
|
+
ports:
|
|
148
|
+
- "3000:3000"
|
|
149
|
+
environment:
|
|
150
|
+
DATABASE_URL: postgres://app:testbot@postgres/app
|
|
151
|
+
depends_on:
|
|
152
|
+
postgres:
|
|
153
|
+
condition: service_healthy
|
|
154
|
+
healthcheck:
|
|
155
|
+
test: ["CMD", "curl", "-sf", "http://localhost:3000/health"]
|
|
156
|
+
interval: 10s
|
|
157
|
+
timeout: 5s
|
|
158
|
+
retries: 30
|
|
159
|
+
start_period: 30s
|
|
160
|
+
\`\`\`
|
|
161
|
+
</example>
|
|
162
|
+
|
|
163
|
+
<example label="monorepo-with-builder">
|
|
164
|
+
\`\`\`yaml
|
|
165
|
+
services:
|
|
166
|
+
builder:
|
|
167
|
+
image: node:20-alpine
|
|
168
|
+
working_dir: /app
|
|
169
|
+
volumes:
|
|
170
|
+
- .:/app
|
|
171
|
+
command: sh -c "corepack enable && pnpm install --frozen-lockfile && pnpm build"
|
|
172
|
+
|
|
173
|
+
postgres:
|
|
174
|
+
image: postgres:16-alpine
|
|
175
|
+
environment:
|
|
176
|
+
POSTGRES_DB: app
|
|
177
|
+
POSTGRES_USER: app
|
|
178
|
+
POSTGRES_PASSWORD: testbot
|
|
179
|
+
healthcheck:
|
|
180
|
+
test: ["CMD-SHELL", "pg_isready -U app"]
|
|
181
|
+
interval: 5s
|
|
182
|
+
timeout: 5s
|
|
183
|
+
retries: 10
|
|
184
|
+
|
|
185
|
+
backend:
|
|
186
|
+
build: { context: . }
|
|
187
|
+
depends_on:
|
|
188
|
+
builder:
|
|
189
|
+
condition: service_completed_successfully
|
|
190
|
+
postgres:
|
|
191
|
+
condition: service_healthy
|
|
192
|
+
ports:
|
|
193
|
+
- "9000:9000"
|
|
194
|
+
environment:
|
|
195
|
+
DATABASE_URL: postgres://app:testbot@postgres/app
|
|
196
|
+
JWT_SECRET: testbot-jwt-secret
|
|
197
|
+
healthcheck:
|
|
198
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:9000/health"]
|
|
199
|
+
interval: 10s
|
|
200
|
+
timeout: 5s
|
|
201
|
+
retries: 30
|
|
202
|
+
start_period: 90s
|
|
203
|
+
|
|
204
|
+
frontend:
|
|
205
|
+
image: node:20-alpine
|
|
206
|
+
working_dir: /app/apps/web
|
|
207
|
+
volumes:
|
|
208
|
+
- .:/app
|
|
209
|
+
depends_on:
|
|
210
|
+
backend:
|
|
211
|
+
condition: service_healthy
|
|
212
|
+
ports:
|
|
213
|
+
- "3000:3000"
|
|
214
|
+
environment:
|
|
215
|
+
NEXT_PUBLIC_API_URL: http://localhost:9000
|
|
216
|
+
command: sh -c "npm run dev -- --hostname 0.0.0.0 --port 3000"
|
|
217
|
+
healthcheck:
|
|
218
|
+
test: ["CMD-SHELL", "bash -c '</dev/tcp/localhost/3000' || exit 1"]
|
|
219
|
+
interval: 10s
|
|
220
|
+
timeout: 5s
|
|
221
|
+
retries: 30
|
|
222
|
+
start_period: 120s
|
|
223
|
+
\`\`\`
|
|
224
|
+
</example>
|
|
225
|
+
|
|
226
|
+
<example label="source-mount-no-dockerfile">
|
|
227
|
+
\`\`\`yaml
|
|
228
|
+
services:
|
|
229
|
+
app:
|
|
230
|
+
image: python:3.12-slim
|
|
231
|
+
working_dir: /app
|
|
232
|
+
volumes:
|
|
233
|
+
- .:/app
|
|
234
|
+
command: sh -c "pip install -e . && uvicorn main:app --host 0.0.0.0 --port 8000"
|
|
235
|
+
ports:
|
|
236
|
+
- "8000:8000"
|
|
237
|
+
healthcheck:
|
|
238
|
+
test: ["CMD", "curl", "-sf", "http://localhost:8000/health"]
|
|
239
|
+
interval: 10s
|
|
240
|
+
timeout: 5s
|
|
241
|
+
retries: 30
|
|
242
|
+
start_period: 60s
|
|
243
|
+
\`\`\`
|
|
244
|
+
</example>
|
|
245
|
+
`;
|
|
246
|
+
function buildApplyStrategyBody(args) {
|
|
247
|
+
const composeFile = args.sutSourceDockerComposeFile?.trim();
|
|
248
|
+
if (composeFile) {
|
|
249
|
+
return `#### Reuse the provided Docker Compose file
|
|
250
|
+
${getReuseDockerComposeInstructions(composeFile)}`;
|
|
251
|
+
}
|
|
252
|
+
return `Apply the strategy selected in Step 2:
|
|
253
|
+
|
|
254
|
+
#### If REUSE selected — Reuse existing Docker Compose
|
|
255
|
+
${getReuseDockerComposeInstructions()}
|
|
256
|
+
|
|
257
|
+
#### If GENERATE selected — Generate new Docker Compose
|
|
258
|
+
${GENERATE_DOCKER_COMPOSE_INSTRUCTIONS}`;
|
|
259
|
+
}
|
|
260
|
+
// ── PromptPlan declaration ────────────────────────────────────────────────────
|
|
261
|
+
const _composePlan = new PromptPlan()
|
|
262
|
+
.addPhase("compose", "", { headerLevel: "hidden", stepFormat: "hash" })
|
|
263
|
+
.step("SCAN", "Understand the System Under Test in this repository", buildScanComposeBody)
|
|
264
|
+
.step("DECIDE", "Decide SUT setup strategy", buildDecideStrategyBody)
|
|
265
|
+
.step("INPUTS", "Plan the Testbot action inputs", () => buildInputsBody())
|
|
266
|
+
.step("BUILD", "Edit the Testbot workflow and generate SUT files", () => "")
|
|
267
|
+
.subStep("PR_ACCURATE", "Build PR-accurate SUT", buildPrAccurateSutSection)
|
|
268
|
+
.subStep("APPLY", "Apply the chosen strategy", buildApplyStrategyBody)
|
|
269
|
+
.step("VERIFY", "Verify", () => "")
|
|
270
|
+
.subStep("COMMON_ERRORS", "Common SUT errors", () => buildCommonSutErrorsSection())
|
|
271
|
+
.subStep("LOCAL_VALIDATION", "Local validation", () => buildLocalValidationSection())
|
|
272
|
+
.done();
|
|
273
|
+
export function getDockerComposePrompt(args) {
|
|
274
|
+
const composeFile = args.sutSourceDockerComposeFile?.trim();
|
|
275
|
+
const taskLine = composeFile
|
|
276
|
+
? `Your task is to find a working System Under Test (SUT) setup using the given Docker Compose file \`${composeFile}\`. You will reuse this file, validate that services start correctly, fix any errors, and wire the working commands into the testbot workflow.`
|
|
277
|
+
: `Your task is to find a working System Under Test (SUT) setup using Docker Compose that builds and deploys the services in this repository. You will either reuse an existing docker-compose file or generate a new one, validate that services start correctly, fix any errors, and wire the working commands into the testbot workflow.`;
|
|
278
|
+
const thinkingStep = composeFile
|
|
279
|
+
? `1. Output a <thinking> block to explain how the provided compose file \`${composeFile}\` starts the application and what commands are needed to bring it up.`
|
|
280
|
+
: `1. Output a <thinking> block to explain whether you found an existing compose file, whether it is application or infrastructure-only, and your decision to reuse or generate.`;
|
|
281
|
+
return `${getPersonaPrefix()}${taskLine}
|
|
282
|
+
|
|
283
|
+
${buildSutDefinitionBlock()}The testbot installer has already created a basic \`.github/workflows/skyramp-testbot.yml\` in this PR with no SUT setup commands. Manage the SUT lifecycle exclusively through inputs on the existing \`skyramp/testbot\` action step — do NOT add separate GHA setup/teardown steps and do NOT set \`skipTargetSetup\`.
|
|
284
|
+
|
|
285
|
+
Before generating any files, you MUST:
|
|
286
|
+
${thinkingStep}
|
|
287
|
+
2. Then proceed with the instructions below.
|
|
288
|
+
${buildContextBlock(args)}
|
|
289
|
+
|
|
290
|
+
${_composePlan.render(args)}
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path of the Testbot workflow file relative to the repo root. Created by the
|
|
3
|
+
* Testbot installer and edited in-place by every SUT setup mode.
|
|
4
|
+
*/
|
|
5
|
+
export declare const TESTBOT_WORKFLOW_PATH = ".github/workflows/skyramp-testbot.yml";
|
|
6
|
+
export declare enum SutSetupMode {
|
|
7
|
+
None = "none",
|
|
8
|
+
AdaptWorkflow = "adapt_workflow",
|
|
9
|
+
DockerCompose = "docker_compose"
|
|
10
|
+
}
|
|
11
|
+
export interface SutPromptArgs {
|
|
12
|
+
repositoryPath: string;
|
|
13
|
+
sutSetupMode: string;
|
|
14
|
+
sutSourceWorkflowFile: string;
|
|
15
|
+
sutSourceDockerComposeFile: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Glossary block rendered right after the first mention of "SUT" in each
|
|
19
|
+
* setup prompt. Shared across modes so the LLM gets a single explicit
|
|
20
|
+
* definition rather than inferring it from context.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildSutDefinitionBlock(): string;
|
|
23
|
+
export declare function buildContextBlock(args: SutPromptArgs): string;
|
|
24
|
+
export declare function buildLocalValidationSection(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the canonical "add to the Testbot workflow" block: every input that
|
|
27
|
+
* controls the SUT lifecycle, auth, and workflow-level setup, followed by the
|
|
28
|
+
* shared What to do / What not to do guidance (including the sutSetupMode
|
|
29
|
+
* bootstrap guard). Shared by every SUT setup mode (adapt_workflow and
|
|
30
|
+
* docker_compose) so the input semantics and rules never drift between prompts.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildAddToTestbotWorkflowSection(): string;
|
|
33
|
+
/**
|
|
34
|
+
* `buildAddToTestbotWorkflowSection()` output with its leading
|
|
35
|
+
* `#### Testbot action inputs ...` header stripped — for use as a PromptPlan
|
|
36
|
+
* step body where the PromptPlan step header replaces the inline `####`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildInputsBody(): string;
|
|
39
|
+
export declare function buildCommonSutErrorsSection(): string;
|