Push DuckTap toward v0.6 quality#16
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughDuckTap releases v0.6.0 with GraphQL introspection discovery, TypeScript/Go/Rust CLI scaffolding, local SQLite data persistence (save/query/search), a publish command for packaging artifacts, enhanced scorecard dimensions, and WebUI target selection with status API. Version bumps, documentation, and comprehensive tests validate the expanded feature ecosystem. ChangesDuckTap v0.6.0 Release
🎯 3 (Moderate) | ⏱️ ~25 minutes
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/ducktap/core/pipeline.py (1)
48-49:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd
graphqlto automatic discovery priority.Line 48 omits
graphql, which prevents automatic GraphQL discovery despite built-in registration. This breaks the “first-class discovery” path unless users force--from graphql.Proposed fix
- for name in ("openapi", "har", "browser-sniff"): + for name in ("openapi", "har", "graphql", "browser-sniff"):🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/ducktap/core/pipeline.py` around lines 48 - 49, The discovery priority tuple in the for-loop (for name in ("openapi", "har", "browser-sniff"): ...) omits the built-in "graphql" provider so GraphQL never gets auto-discovered; update that tuple to include "graphql" (e.g., ("openapi", "har", "graphql", "browser-sniff")) so the loop that does d = discs.get(name) will check and select the GraphQL disco handler during automatic discovery.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/COMPARISON.md`:
- Around line 55-56: Update the phrase in the sentence mentioning "Printing
Press" to use a hyphenated compound modifier for clarity; change "Claude Code
focused experience" to "Claude Code-focused experience" (or "Claude-focused Code
experience") so the sentence reads e.g. "Pick Printing Press if you want the
most mature Claude Code-focused experience today, a single Go binary, and the
larger built-in catalog." Ensure the hyphenated form replaces the existing
dangling modifier around the "Claude Code" phrase.
In `@docs/ROADMAP.md`:
- Around line 37-39: Update the ROADMAP entry so the GraphQL landing version is
consistent: choose the correct release (either v0.3.0 or v0.6.0), then replace
the mismatched occurrence of "landed in v0.6.0" or "landed in v0.3.0" so both
checklist items/readme lines that mention GraphQL show the same version string
("v0.3.0" or "v0.6.0") and ensure the surrounding checklist text (the three
GraphQL bullets) remains semantically identical.
In `@src/ducktap/discovery/graphql.py`:
- Around line 62-63: The code currently ignores GraphQL introspection errors in
the JSON payload; update the logic around response, schema and fields (the block
using response.json().get("data", {}).get("__schema", {}) and fields =
((schema.get("queryType") or {}).get("fields") or [])) to first check
response.json().get("errors") and, if present and non-empty, raise a discovery
failure (e.g., raise an appropriate DiscoveryError or Exception) with the error
details instead of proceeding to build an empty spec; ensure the error includes
the JSON "errors" content and short context so callers can fail fast on
introspection errors.
In `@src/ducktap/generator/language_scaffolds.py`:
- Around line 54-66: The package.json "bin" entry in the TypeScript CLI scaffold
currently points to "./src/index.js" while the generator (_write call that
creates package.json inside TypeScriptCLIGenerator) only emits src/index.ts and
src/commands.ts; update the created JSON so "bin": {bin_name: "./src/index.ts"}
(or alternatively emit a matching src/index.js wrapper) so the installed CLI
points to the actual generated entry point (adjust the _write call that builds
package.json accordingly).
In `@src/ducktap/generator/templates/cli/commands.py.j2`:
- Around line 457-464: data_search currently allows limit <= 0 which can cause
unbounded/invalid queries; add a guard at the start of the data_search function
to reject values less than 1 (e.g. if limit < 1: raise click.BadParameter("limit
must be >= 1") or emit an error and ctx.exit) before calling _get_client or
client.mirror.search so that invalid limits are rejected early.
In `@src/ducktap/webui/app.py`:
- Around line 36-39: The current comprehension that builds _TARGETS using
next(...) will raise StopIteration at import time if DEFAULT_TARGETS contains a
key missing from _TARGET_DETAILS; replace it with a safe lookup: build a dict
_TARGET_LOOKUP from _TARGET_DETAILS and iterate DEFAULT_TARGETS, either
appending a sensible default entry or raising an explicit ValueError with a
clear message like "Missing _TARGET_DETAILS entry for target: {target}" when a
target is absent; update references to _TARGETS to use this validated list.
In `@tests/test_multilang_generators.py`:
- Around line 1-2: The test currently calls subprocess.run(["go", "test",
"./..."], cwd=go_root, ...) without guarding for a missing Go toolchain; modify
the test to first check for the Go binary (e.g., via shutil.which("go")) or wrap
the subprocess.run call in a try/except catching FileNotFoundError and then call
pytest.skip("go not found; skipping test") (or otherwise skip/return) so CI
won’t fail when Go is absent; update the code around the existing subprocess.run
invocation (the proc variable and go_root usage) to perform this guard and skip
behavior.
---
Outside diff comments:
In `@src/ducktap/core/pipeline.py`:
- Around line 48-49: The discovery priority tuple in the for-loop (for name in
("openapi", "har", "browser-sniff"): ...) omits the built-in "graphql" provider
so GraphQL never gets auto-discovered; update that tuple to include "graphql"
(e.g., ("openapi", "har", "graphql", "browser-sniff")) so the loop that does d =
discs.get(name) will check and select the GraphQL disco handler during automatic
discovery.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 905a005b-4867-4962-9a9f-34fb12568e04
⛔ Files ignored due to path filters (1)
docs/img/ducktap-ui.pngis excluded by!**/*.png
📒 Files selected for processing (24)
CHANGELOG.mdREADME.mddocs/COMPARISON.mddocs/ROADMAP.mdpyproject.tomlsrc/ducktap/__init__.pysrc/ducktap/cli.pysrc/ducktap/core/pipeline.pysrc/ducktap/core/plugins.pysrc/ducktap/discovery/graphql.pysrc/ducktap/generator/language_scaffolds.pysrc/ducktap/generator/templates/cli/README.md.j2src/ducktap/generator/templates/cli/commands.py.j2src/ducktap/generator/templates/cli/main.py.j2src/ducktap/generator/templates/cli/mirror.py.j2src/ducktap/verify/scorecard.pysrc/ducktap/webui/app.pytests/test_e2e.pytests/test_generated_cli_runtime.pytests/test_graphql_discovery.pytests/test_multilang_generators.pytests/test_plugins.pytests/test_publish.pytests/test_webui.py
| Pick **Printing Press** if you want the most mature Claude Code focused | ||
| experience today, a single Go binary, and the larger built-in catalog. |
There was a problem hiding this comment.
Use hyphenated compound modifier for readability.
Line 55 should use “Claude Code-focused experience” (or “Claude-focused Code experience”) to avoid the dangling modifier.
✏️ Suggested doc fix
-Pick **Printing Press** if you want the most mature Claude Code focused
+Pick **Printing Press** if you want the most mature Claude Code-focused
experience today, a single Go binary, and the larger built-in catalog.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Pick **Printing Press** if you want the most mature Claude Code focused | |
| experience today, a single Go binary, and the larger built-in catalog. | |
| Pick **Printing Press** if you want the most mature Claude Code-focused | |
| experience today, a single Go binary, and the larger built-in catalog. |
🧰 Tools
🪛 LanguageTool
[grammar] ~55-~55: Use a hyphen to join words.
Context: ... if you want the most mature Claude Code focused experience today, a single Go bi...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/COMPARISON.md` around lines 55 - 56, Update the phrase in the sentence
mentioning "Printing Press" to use a hyphenated compound modifier for clarity;
change "Claude Code focused experience" to "Claude Code-focused experience" (or
"Claude-focused Code experience") so the sentence reads e.g. "Pick Printing
Press if you want the most mature Claude Code-focused experience today, a single
Go binary, and the larger built-in catalog." Ensure the hyphenated form replaces
the existing dangling modifier around the "Claude Code" phrase.
| - [x] GraphQL promoted to a first-class discoverer through introspection | ||
| - [x] GraphQL operations normalized into the same `APISpec` model as OpenAPI/HAR | ||
| - [x] Plugin autoload includes GraphQL by default |
There was a problem hiding this comment.
Resolve GraphQL landing-version inconsistency.
This doc states GraphQL landed in v0.3.0 (Line 37) but also says it landed in v0.6.0 (Line 94). Please align both to one version to keep roadmap history trustworthy.
✏️ Suggested doc fix (if v0.3.0 is correct)
-| GraphQL discovery | landed v0.6.0 |
+| GraphQL discovery | landed v0.3.0 |Also applies to: 94-94
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/ROADMAP.md` around lines 37 - 39, Update the ROADMAP entry so the
GraphQL landing version is consistent: choose the correct release (either v0.3.0
or v0.6.0), then replace the mismatched occurrence of "landed in v0.6.0" or
"landed in v0.3.0" so both checklist items/readme lines that mention GraphQL
show the same version string ("v0.3.0" or "v0.6.0") and ensure the surrounding
checklist text (the three GraphQL bullets) remains semantically identical.
| schema = response.json().get("data", {}).get("__schema", {}) | ||
| fields = ((schema.get("queryType") or {}).get("fields") or []) |
There was a problem hiding this comment.
Fail fast on GraphQL introspection errors in 200 responses.
Line 62 currently ignores the GraphQL errors payload shape, so failed introspection can silently produce an empty spec. Treat errors as a discovery failure.
Proposed fix
- schema = response.json().get("data", {}).get("__schema", {})
+ payload = response.json()
+ if payload.get("errors"):
+ msgs = ", ".join(
+ e.get("message", "unknown GraphQL error")
+ for e in payload.get("errors", [])
+ if isinstance(e, dict)
+ )
+ raise ValueError(f"GraphQL introspection failed: {msgs or 'unknown error'}")
+ schema = payload.get("data", {}).get("__schema", {})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| schema = response.json().get("data", {}).get("__schema", {}) | |
| fields = ((schema.get("queryType") or {}).get("fields") or []) | |
| payload = response.json() | |
| if payload.get("errors"): | |
| msgs = ", ".join( | |
| e.get("message", "unknown GraphQL error") | |
| for e in payload.get("errors", []) | |
| if isinstance(e, dict) | |
| ) | |
| raise ValueError(f"GraphQL introspection failed: {msgs or 'unknown error'}") | |
| schema = payload.get("data", {}).get("__schema", {}) | |
| fields = ((schema.get("queryType") or {}).get("fields") or []) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/ducktap/discovery/graphql.py` around lines 62 - 63, The code currently
ignores GraphQL introspection errors in the JSON payload; update the logic
around response, schema and fields (the block using response.json().get("data",
{}).get("__schema", {}) and fields = ((schema.get("queryType") or
{}).get("fields") or [])) to first check response.json().get("errors") and, if
present and non-empty, raise a discovery failure (e.g., raise an appropriate
DiscoveryError or Exception) with the error details instead of proceeding to
build an empty spec; ensure the error includes the JSON "errors" content and
short context so callers can fail fast on introspection errors.
| _write(root / "package.json", json.dumps({ | ||
| "name": bin_name, | ||
| "version": spec.version, | ||
| "type": "module", | ||
| "bin": {bin_name: "./src/index.js"}, | ||
| "scripts": {"start": "tsx src/index.ts"}, | ||
| "dependencies": {"commander": "^12.0.0"}, | ||
| "devDependencies": {"tsx": "^4.0.0", "typescript": "^5.0.0"}, | ||
| }, indent=2) + "\n", written) | ||
| _write(root / "src" / "commands.ts", "export const commands = " | ||
| + json.dumps(ops, indent=2) + " as const;\n", written) | ||
| _write(root / "src" / "index.ts", f"""#!/usr/bin/env node | ||
| import {{ Command }} from "commander"; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For npm packages, if package.jsonhas"bin": {"mycli": "./src/index.js"}but onlysrc/index.ts exists, will the installed binary work without a build step? What is the recommended approach for TypeScript CLIs using tsx?
💡 Result:
No, the installed binary will not work if the bin field points directly to a .ts file without a build step [1][2]. The npm install process links the file specified in package.json to the system or local node_modules/.bin directory [3]. When executed, this file is run by the Node.js runtime, which does not natively understand TypeScript syntax and will fail to execute a .ts file [2]. To use tsx (or similar tools like ts-node) for a TypeScript CLI, the recommended approach involves these steps: 1. Configure the Shebang: The top of your source file should include a shebang that invokes tsx to ensure the file is executed correctly when called as a command [2]. #!/usr/bin/env -S npx tsx Alternatively, you can create a lightweight JavaScript wrapper file (e.g., bin/cli.js) that includes the shebang and imports your main TypeScript file using tsx or a similar loader [1][2]. 2. Set File Permissions: For the binary to be executable on Unix-like systems, the file must have the execute bit set (chmod +x). Since npm may not preserve these permissions across all environments, many developers handle this by explicitly compiling to a JavaScript file in a dist/ directory, which ensures platform-independent execution [4][5]. 3. Recommended Production Approach: While tsx is excellent for development (zero-config, high speed, and watch mode) [6][7], the standard best practice for publishing npm packages is to compile your TypeScript code into JavaScript using tsc or a bundler (like tsup or esbuild) [5][7]. Your package.json should then point the bin field to the generated JavaScript file in your build output (e.g., ./dist/index.js) [8][5]. This approach ensures that your package is performant, does not require users to have tsx or typescript installed in their own environment, and avoids runtime transpilation overhead [9][7].
Citations:
- 1: https://stackoverflow.com/questions/55820752/use-an-npm-script-as-bin
- 2: https://dev.to/rerodrigues/typescript-scripts-in-the-command-line-using-nodejs-9ad
- 3: https://docs.npmjs.com/cli/v11/configuring-npm/package-json/
- 4: Set as executable files that are intended to be binaries microsoft/TypeScript#37583
- 5: https://dev.to/armorbreak/i-published-my-first-npm-package-heres-everything-i-wish-i-knew-167c
- 6: https://github.com/esbuild-kit/tsx/blob/master/docs/index.md
- 7: https://agentfactory.panaversity.org/docs/TypeScript-Language-Realtime-Interaction/cli-tools-developer-experience/tsx-for-development
- 8: https://dev.to/tigawanna/building-and-publishing-npm-packages-with-typescript-multiple-entry-points-tailwind-tsup-and-npm-9e7
- 9: https://blog.logrocket.com/running-typescript-node-js-tsx-vs-ts-node-vs-native/
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the referenced section
sed -n '1,120p' src/ducktap/generator/language_scaffolds.py | nl -ba | sed -n '45,90p'
# Search for any writes of src/index.js (or similar bin wrappers)
rg -n --fixed-strings 'src/index.js' src/ducktap/generator/language_scaffolds.py src/ducktap/generator -S || true
# Search for writes of index.* in the language scaffold generator(s)
rg -n --regex 'src\/index\.(js|cjs|mjs|ts)' src/ducktap/generator -S || true
# Also search for any "bin" generation beyond package.json snippet
rg -n --fixed-strings '"bin"' src/ducktap/generator -S || trueRepository: zanni098/DuckTap
Length of output: 103
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/ducktap/generator/language_scaffolds.py"
# Print lines 40-90 around the cited region (use sed line addressing)
sed -n '40,90p' "$FILE" | sed -n '1,120p'
# Search for any writes of src/index.js (or similar bin wrappers) within generator code
rg -n --fixed-strings 'src/index.js' src/ducktap/generator -S || true
# Search for writes of index.* variants
rg -n --regex 'src\/index\.(js|cjs|mjs|ts)' src/ducktap/generator -S || true
# Search for "bin" assignment/generation elsewhere in generator
rg -n --fixed-strings '"bin"' src/ducktap/generator -S || true
# Additionally search whole repo for "src/index.js" to rule out other generator/templates
rg -n --fixed-strings 'src/index.js' -S || trueRepository: zanni098/DuckTap
Length of output: 2611
Fix TypeScript CLI bin entry pointing to a non-existent file
TypeScriptCLIGenerator sets package.json "bin" to ./src/index.js (src/ducktap/generator/language_scaffolds.py line 58), but it only generates ./src/index.ts (and ./src/commands.ts). The installed CLI will therefore reference a file that isn’t created. Update "bin" to the generated entry (or generate a matching src/index.js wrapper / build output).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/ducktap/generator/language_scaffolds.py` around lines 54 - 66, The
package.json "bin" entry in the TypeScript CLI scaffold currently points to
"./src/index.js" while the generator (_write call that creates package.json
inside TypeScriptCLIGenerator) only emits src/index.ts and src/commands.ts;
update the created JSON so "bin": {bin_name: "./src/index.ts"} (or alternatively
emit a matching src/index.js wrapper) so the installed CLI points to the actual
generated entry point (adjust the _write call that builds package.json
accordingly).
| @click.option("--limit", type=int, default=20, show_default=True) | ||
| @click.pass_context | ||
| def data_search(ctx: click.Context, text: str, limit: int) -> None: | ||
| client = _get_client(ctx) | ||
| if client.mirror is None: | ||
| click.echo(json.dumps({"error": "local data lake is disabled"}), err=True) | ||
| ctx.exit(10) | ||
| _emit(ctx, client.mirror.search(text, limit=limit), raw=True) |
There was a problem hiding this comment.
Validate --limit lower bound in data search.
limit currently accepts 0/negative values, which can lead to unexpected or effectively unbounded reads. Reject values < 1 before executing the query.
Suggested fix
`@data_group.command`("search")
`@click.argument`("text")
`@click.option`("--limit", type=int, default=20, show_default=True)
`@click.pass_context`
def data_search(ctx: click.Context, text: str, limit: int) -> None:
+ if limit < 1:
+ click.echo(json.dumps({"error": "--limit must be >= 1"}), err=True)
+ ctx.exit(10)
client = _get_client(ctx)
if client.mirror is None:
click.echo(json.dumps({"error": "local data lake is disabled"}), err=True)
ctx.exit(10)
_emit(ctx, client.mirror.search(text, limit=limit), raw=True)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @click.option("--limit", type=int, default=20, show_default=True) | |
| @click.pass_context | |
| def data_search(ctx: click.Context, text: str, limit: int) -> None: | |
| client = _get_client(ctx) | |
| if client.mirror is None: | |
| click.echo(json.dumps({"error": "local data lake is disabled"}), err=True) | |
| ctx.exit(10) | |
| _emit(ctx, client.mirror.search(text, limit=limit), raw=True) | |
| `@click.option`("--limit", type=int, default=20, show_default=True) | |
| `@click.pass_context` | |
| def data_search(ctx: click.Context, text: str, limit: int) -> None: | |
| if limit < 1: | |
| click.echo(json.dumps({"error": "--limit must be >= 1"}), err=True) | |
| ctx.exit(10) | |
| client = _get_client(ctx) | |
| if client.mirror is None: | |
| click.echo(json.dumps({"error": "local data lake is disabled"}), err=True) | |
| ctx.exit(10) | |
| _emit(ctx, client.mirror.search(text, limit=limit), raw=True) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/ducktap/generator/templates/cli/commands.py.j2` around lines 457 - 464,
data_search currently allows limit <= 0 which can cause unbounded/invalid
queries; add a guard at the start of the data_search function to reject values
less than 1 (e.g. if limit < 1: raise click.BadParameter("limit must be >= 1")
or emit an error and ctx.exit) before calling _get_client or
client.mirror.search so that invalid limits are rejected early.
| _TARGETS = [ | ||
| next(details for details in _TARGET_DETAILS if details[0] == target) | ||
| for target in DEFAULT_TARGETS | ||
| ] |
There was a problem hiding this comment.
StopIteration crash if DEFAULT_TARGETS and _TARGET_DETAILS drift.
If a new target is added to DEFAULT_TARGETS but not to _TARGET_DETAILS, next() raises StopIteration at import time, crashing the app with an unhelpful traceback. Provide a default or explicit error.
Proposed fix: add a meaningful fallback or error
_TARGETS = [
- next(details for details in _TARGET_DETAILS if details[0] == target)
+ next(
+ (details for details in _TARGET_DETAILS if details[0] == target),
+ (target, target, ""), # fallback: use target ID as label if not found
+ )
for target in DEFAULT_TARGETS
]Or raise an explicit error during startup:
_TARGET_LOOKUP = {t[0]: t for t in _TARGET_DETAILS}
_TARGETS = []
for target in DEFAULT_TARGETS:
if target not in _TARGET_LOOKUP:
raise ValueError(f"Missing _TARGET_DETAILS entry for target: {target}")
_TARGETS.append(_TARGET_LOOKUP[target])🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/ducktap/webui/app.py` around lines 36 - 39, The current comprehension
that builds _TARGETS using next(...) will raise StopIteration at import time if
DEFAULT_TARGETS contains a key missing from _TARGET_DETAILS; replace it with a
safe lookup: build a dict _TARGET_LOOKUP from _TARGET_DETAILS and iterate
DEFAULT_TARGETS, either appending a sensible default entry or raising an
explicit ValueError with a clear message like "Missing _TARGET_DETAILS entry for
target: {target}" when a target is absent; update references to _TARGETS to use
this validated list.
| import subprocess | ||
| from pathlib import Path |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Go toolchain availability:"
command -v go || echo "go missing"
echo
echo "Relevant test lines:"
rg -n -C2 'subprocess\.run\(\["go",\s*"test",\s*"\./\.\."\]|shutil\.which\("go"\)|pytest\.skip\(' --type=pyRepository: zanni098/DuckTap
Length of output: 130
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="tests/test_multilang_generators.py"
echo "=== File header / around line 1-80 ==="
nl -ba "$FILE" | sed -n '1,120p'
echo
echo "=== Search for go test invocation ==="
rg -n --hidden --no-ignore -S '(\bgo\b|subprocess\.run\(|go test|\["go",\s*"test")' "$FILE"
echo
echo "=== Search for any existing skip/guard logic ==="
rg -n --hidden --no-ignore -S '(pytest\.skip|shutil\.which\("go"\)|which\("go"\)|GO_TOOLCHAIN|toolchain)' "$FILE" || trueRepository: zanni098/DuckTap
Length of output: 160
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="tests/test_multilang_generators.py"
echo "=== First 160 lines of $FILE (with line numbers) ==="
cat -n "$FILE" | sed -n '1,160p'
echo
echo "=== Search for go test invocation ==="
rg -n --hidden --no-ignore -S '(\bgo\b|subprocess\.run\(|go test|\["go",\s*"test")' "$FILE"
echo
echo "=== Search for any existing skip/guard logic ==="
rg -n --hidden --no-ignore -S '(pytest\.skip|shutil\.which\("go"\)|which\("go"\)|GO_TOOLCHAIN|toolchain)' "$FILE" || trueRepository: zanni098/DuckTap
Length of output: 2215
Guard Go toolchain invocation in tests/test_multilang_generators.py to prevent CI crashes when go isn’t installed.
proc = subprocess.run(["go", "test", "./..."], cwd=go_root, ...) at lines 31-32 is unguarded; if go is missing on PATH, subprocess.run raises FileNotFoundError and fails unrelated CI runs.
Proposed fix
import subprocess
+import shutil
from pathlib import Path
+import pytest
from ducktap.core.pipeline import press
@@
- proc = subprocess.run(["go", "test", "./..."], cwd=go_root, capture_output=True, text=True)
+ if shutil.which("go") is None:
+ pytest.skip("go toolchain not available on test runner")
+ proc = subprocess.run(
+ ["go", "test", "./..."], cwd=go_root, capture_output=True, text=True
+ )
assert proc.returncode == 0, proc.stderr🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_multilang_generators.py` around lines 1 - 2, The test currently
calls subprocess.run(["go", "test", "./..."], cwd=go_root, ...) without guarding
for a missing Go toolchain; modify the test to first check for the Go binary
(e.g., via shutil.which("go")) or wrap the subprocess.run call in a try/except
catching FileNotFoundError and then call pytest.skip("go not found; skipping
test") (or otherwise skip/return) so CI won’t fail when Go is absent; update the
code around the existing subprocess.run invocation (the proc variable and
go_root usage) to perform this guard and skip behavior.
Summary
Pushes DuckTap from the v0.2.2 surface toward v0.6.0 with quality-focused product upgrades:
--save,data query,data searchducktap publishdry-run artifact zip + manifest including all generated outputsagent_nativeandlocal_datadimensionsVerification
python -m pytest -q-> 51 passedruff check src tests-> all checks passedducktap press tests/fixtures/petstore.yaml --out ./out-v06-smoke --name petstore-v06-> generated six targetsducktap shipcheck petstore-v06 --out-dir ./out-v06-smoke-> all checks OKducktap scorecard ./out-v06-smoke/petstore-v06.apispec.json --out-dir ./out-v06-smoke --fail-under 80-> 94/Aducktap publish petstore-v06 --out-dir ./out-v06-smoke --dist-dir ./dist-v06-smoke --dry-run-> archive + manifest createdducktap uiathttp://127.0.0.1:8765and refresheddocs/img/ducktap-ui.pngComparison Note
A fresh local
go test ./...onmvanhorn/cli-printing-pressstill shows Windows-sensitive failures ininternal/authdoctorandinternal/browsersniffbefore the run stalled in later packages; the run was interrupted after the repeated failure signal was captured.Summary by CodeRabbit
New Features
publishcommand with dry-run packaging and manifest generationChanged