Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ jobs:
github-token: smoke-gh-token
zhipu-api-key: smoke-zhipu-token

- name: Run test-value-detector convenience action
uses: ./test-value-detector
with:
install-url: http://127.0.0.1:8765/fake-installer.sh
cache: false
install-attempts: 1
attempts: 1
github-token: smoke-gh-token
zhipu-api-key: smoke-zhipu-token

- name: Run multi-review convenience action
uses: ./multi-review
continue-on-error: true
Expand All @@ -137,7 +147,6 @@ jobs:
install-attempts: 1
github-token: smoke-gh-token
zhipu-api-key: smoke-zhipu-token

- name: Stop fake installer server
if: always()
run: |
Expand Down
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ npx skills add sun-praise/opencode-actions
- `architect-review`: architecture-level PR review focusing on coupling, layering, and structural concerns
- `feature-missing`: audits PR implementation against linked issue spec to find missing features
- `spec-coverage`: cross-references project spec/task files against PR implementation to find planned but unimplemented features
- `test-value-detector`: detects low-value tests in PRs — empty assertions, hardcoded mocks, detached tests, duplicates, missing edge-case coverage
- `github-run-opencode`: one-step wrapper for the common `opencode github run` workflow
- `setup-opencode`: installs OpenCode, restores a dedicated cache, and exports the binary path
- `run-opencode`: runs `opencode` with optional retry logic for flaky GitHub network failures
Expand Down Expand Up @@ -196,6 +197,27 @@ Unlike `feature-missing` (which checks PR self-described scope), `spec-coverage`
opencode-go-api-key: ${{ secrets.OPENCODE_GO_API_KEY }}
```

## test-value-detector

Use this to automatically detect low-value tests in pull requests — tests that pass CI but contribute nothing to code quality.

- identifies empty assertions and always-true conditions
- flags hardcoded mocks that decouple tests from real logic
- detects tests referencing non-existent or mismatched functions
- spots duplicate tests with no additional coverage value
- highlights missing boundary, error, and edge-case coverage
- classifies findings by severity: CRITICAL, MEDIUM, LOW
- shares the same inputs and cache as `review`/`github-run-opencode`

```yaml
- name: Run test value detection
uses: sun-praise/opencode-actions/test-value-detector@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
zhipu-api-key: ${{ secrets.ZHIPU_API_KEY }}
opencode-go-api-key: ${{ secrets.OPENCODE_GO_API_KEY }}
```

### How the review actions differ

| Action | Scope source | What it catches |
Expand All @@ -205,6 +227,11 @@ Unlike `feature-missing` (which checks PR self-described scope), `spec-coverage`
| `architect-review` | PR diff + project conventions | Coupling, layering, module placement, structural concerns |
| `feature-missing` | PR title/body + linked issues | PR self-described scope completeness |
| `spec-coverage` | Project spec/task files | Full planned scope vs implementation |
| `test-value-detector` | PR diff (test files) | Low-value tests, missing coverage |

### Customization

Override the built-in prompt via the `prompt` input to adjust detection focus or severity thresholds for your project.

## setup-opencode

Expand Down Expand Up @@ -261,6 +288,7 @@ uses: sun-praise/opencode-actions/multi-review@v3
uses: sun-praise/opencode-actions/architect-review@v3
uses: sun-praise/opencode-actions/feature-missing@v3
uses: sun-praise/opencode-actions/spec-coverage@v3
uses: sun-praise/opencode-actions/test-value-detector@v3
uses: sun-praise/opencode-actions/github-run-opencode@v3
uses: sun-praise/opencode-actions/setup-opencode@v3
uses: sun-praise/opencode-actions/run-opencode@v3
Expand Down Expand Up @@ -317,7 +345,7 @@ This repository includes a CI workflow that:

- runs `shellcheck` on every bundled shell script
- runs the local shell-based regression suite
- smoke-tests all actions through `uses: ./setup-opencode`, `uses: ./run-opencode`, `uses: ./github-run-opencode`, `uses: ./review`, `uses: ./multi-review`, `uses: ./feature-missing`, `uses: ./spec-coverage`, and `uses: ./architect-review`
- smoke-tests all actions through `uses: ./setup-opencode`, `uses: ./run-opencode`, `uses: ./github-run-opencode`, `uses: ./review`, `uses: ./multi-review`, `uses: ./feature-missing`, `uses: ./spec-coverage`, `uses: ./architect-review`, and `uses: ./test-value-detector`

## Release Policy

Expand All @@ -332,7 +360,7 @@ This repository includes a CI workflow that:
2. Verify `CI` passes on `main`.
3. Create a GitHub release with a semver tag such as `v1.0.0`.
4. Confirm the `Update Major Tag` workflow moved `v1` to that release.
5. Use `owner/repo/review@v3` for the simplest review setup, `owner/repo/multi-review@v3` for multi-agent parallel review, `owner/repo/architect-review@v3` for architecture review, `owner/repo/feature-missing@v3` for PR scope audit, `owner/repo/spec-coverage@v3` for spec coverage audit, `owner/repo/github-run-opencode@v3` for generic `github run`, or `owner/repo/setup-opencode@v3` plus `owner/repo/run-opencode@v3` for more control.
5. Use `owner/repo/review@v3` for the simplest review setup, `owner/repo/multi-review@v3` for multi-agent parallel review, `owner/repo/architect-review@v3` for architecture review, `owner/repo/feature-missing@v3` for PR scope audit, `owner/repo/spec-coverage@v3` for spec coverage audit, `owner/repo/test-value-detector@v3` for low-value test detection, `owner/repo/github-run-opencode@v3` for generic `github run`, or `owner/repo/setup-opencode@v3` plus `owner/repo/run-opencode@v3` for more control.

The initial release-notes template lives at `docs/releases/v1.0.0.md`.

Expand Down
1 change: 1 addition & 0 deletions feature-missing/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ runs:
GITHUB_RUN_OPENCODE_FALLBACK_MODELS: ${{ inputs.fallback-models }}
GITHUB_RUN_OPENCODE_MODEL_TIMEOUT_SECONDS: ${{ inputs.model-timeout-seconds }}
GITHUB_RUN_OPENCODE_FALLBACK_ON_REGEX: ${{ inputs.fallback-on-regex }}
GITHUB_RUN_OPENCODE_EXTRA_ENV: ${{ inputs.extra-env }}
GITHUB_RUN_OPENCODE_PERMISSION: >-
{"edit":"deny","bash":"deny"}
GITHUB_RUN_OPENCODE_CLEANUP_ERROR_COMMENTS: ${{ inputs.cleanup-error-comments }}
Expand Down
38 changes: 25 additions & 13 deletions github-run-opencode/run-github-opencode.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,12 @@ def _main() -> int:
set_env("PROMPT", existing_prompt + zh_instruction)

# Extra env vars from extra-env input
BLOCKED_ENV_KEYS = frozenset({
"PATH", "HOME", "USER", "SHELL", "MODEL", "GITHUB_TOKEN",
"GITHUB_WORKSPACE", "GITHUB_EVENT_PATH", "GITHUB_SHA",
"GITHUB_REPOSITORY", "GITHUB_REF", "GITHUB_RUN_ID",
"GITHUB_ACTIONS", "LD_LIBRARY_PATH", "PYTHONPATH",
})
extra_env_raw = get_env("GITHUB_RUN_OPENCODE_EXTRA_ENV")
allow_sensitive = get_env("GITHUB_RUN_OPENCODE_EXTRA_ENV_ALLOW_SENSITIVE", "false").strip().lower() in ("true", "1", "yes")
if extra_env_raw:
Expand All @@ -616,20 +622,23 @@ def _main() -> int:
continue
key, _, value = line.partition("=")
key = key.strip()
value = value.strip()
if key:
if key.startswith("GITHUB_RUN_OPENCODE_"):
print(f"::error::extra-env key '{key}' starts with reserved prefix 'GITHUB_RUN_OPENCODE_' and is not allowed")
prefix_blocked.append(key)
if not key:
continue
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', key):
print(f"::error::extra-env key '{key}' is not a valid environment variable name (must match [A-Za-z_][A-Za-z0-9_]*)", file=sys.stderr)
sys.exit(1)
if key.startswith("GITHUB_RUN_OPENCODE_"):
print(f"::error::extra-env key '{key}' starts with reserved prefix 'GITHUB_RUN_OPENCODE_' and is not allowed")
prefix_blocked.append(key)
continue
if key in SENSITIVE_ENV_KEYS:
if allow_sensitive:
print(f"::warning::extra-env key '{key}' overrides a sensitive runtime variable (allowed by extra-env-allow-sensitive)")
else:
print(f"::error::extra-env key '{key}' overrides a sensitive runtime variable; set extra-env-allow-sensitive to 'true' to allow")
sensitive_blocked.append(key)
continue
if key in SENSITIVE_ENV_KEYS:
if allow_sensitive:
print(f"::warning::extra-env key '{key}' overrides a sensitive runtime variable (allowed by extra-env-allow-sensitive)")
else:
print(f"::error::extra-env key '{key}' overrides a sensitive runtime variable; set extra-env-allow-sensitive to 'true' to allow")
sensitive_blocked.append(key)
continue
os.environ[key] = value
os.environ[key] = value.strip()
all_blocked = prefix_blocked + sensitive_blocked
if all_blocked:
if prefix_blocked:
Expand All @@ -654,6 +663,9 @@ def _main() -> int:
except json.JSONDecodeError:
print(f"Invalid JSON in GITHUB_RUN_OPENCODE_PERMISSION: {permission_raw}", file=sys.stderr)
sys.exit(1)
if not isinstance(permission, dict):
print(f"GITHUB_RUN_OPENCODE_PERMISSION must be a JSON object, got {type(permission).__name__}", file=sys.stderr)
sys.exit(1)

needs_config = reasoning_effort or enable_thinking.lower() == "true" or permission
if needs_config:
Expand Down
Loading
Loading