mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
Add init workflow step to bootstrap projects like specify init (#2838)
* Initial plan * Add init workflow step to bootstrap projects like `specify init` * Address review: simplify stderr capture and extract VALID_SCRIPT_TYPES * Address review: fail fast on non-empty dir, stdout fallback, README force fix * Populate exit_code/stdout/stderr in non-empty-dir fast-fail * fix: address three unresolved review comments in InitStep - Use `with os.scandir(...)` context manager so the iterator is always closed even when `any()` short-circuits, preventing file-descriptor leaks in long-running workflow runs. - Guard `os.chdir(prev_cwd)` in the `finally` block with a try/except so an `OSError` (e.g. directory deleted) doesn't bypass returning the captured `StepResult`. - Reject non-string `script` values in `validate()` with a clear error message, rather than silently passing them through to become `--script True` at runtime. * Potential fix for pull request finding 'Empty except' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * fix: remove no_git and branch_numbering options removed upstream The --no-git and --branch-numbering flags were removed from `specify init` on main. Update InitStep to drop these unsupported config fields and fix tests accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: address review — integration defaults, integration_options, engine-owned dirs - Apply DEFAULT_INIT_INTEGRATION fallback when neither step config nor workflow context provides an integration, so output.integration always reflects the actual integration used. - Add integration_options config field to support --integration-options passthrough (required for generic integration and --skills mode). - Exclude .specify/ from the non-empty directory fast-fail check so that here: true works when the engine has already created its run-state directory before steps execute. - Note: mix_stderr=False is not needed — Click 8.2+ captures stderr separately by default and the existing try/except handles access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: implicitly add --force when only engine-owned dirs exist When the workflow engine creates .specify/workflows/runs/ before steps execute, the directory is technically non-empty. Previously, specify init would prompt for confirmation (hanging in unattended mode) unless the user explicitly set force: true. Now the step detects that only engine-owned directories (.specify/) are present and implicitly adds --force so init proceeds without user interaction. Also fixes the test to exercise the implicit-force path rather than passing force: True explicitly (which bypassed the check entirely). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: derive VALID_SCRIPT_TYPES from shared constant, fail fast on OSError, include all resolved fields in output - Derive VALID_SCRIPT_TYPES from SCRIPT_TYPE_CHOICES in _agent_config so the valid set cannot drift from the specify init CLI. - Fail fast with a clear error when os.scandir() raises OSError (e.g. permission denied) instead of silently treating the directory as empty. - Include preset, force, and ignore_agent_tools in all output dicts (both fast-fail and normal paths) for consistent interpolation and debugging downstream. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: populate stderr from stdout on older Click, fix force comment wording - When Click does not expose result.stderr (older versions where stderr is mixed into stdout), use stdout as stderr on non-zero exit so workflows can consistently read steps.<id>.output.stderr for errors. - Update README inline comment for force: wording to say 'when target directory already exists' rather than 'non-empty directory', matching the actual specify init behavior for the project: form. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: build argv flags before early returns, use any() for dir scan - Move argv flag-building (--integration, --script, --preset, --ignore-agent-tools) before the non-empty-dir and OSError early returns so output['argv'] always reflects the complete command. - --force is appended after the check since it may be set implicitly. - Replace list comprehension with any() generator expression to short-circuit without allocating a full list of DirEntry objects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: only treat .specify as engine-owned when it is a real directory A file or symlink named .specify should not be excluded from the non-empty check. Use entry.is_dir(follow_symlinks=False) to ensure only an actual directory is considered engine-owned content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: guard implicit force for engine dirs only, fix integration fallback order - Only set implicit --force when engine-owned directories (.specify/) are actually present. A completely empty directory no longer gets --force added unnecessarily. - Fix integration resolution precedence: resolve step config expression first, then fall back to workflow default (also resolved), then to DEFAULT_INIT_INTEGRATION. Previously, a step expression resolving to falsy would bypass the workflow default entirely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> Co-authored-by: Manfred Riem <mnriem@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -77,13 +77,14 @@ When a `gate` step pauses execution, the engine persists `current_step_index` an
|
||||
|
||||
## Step Types
|
||||
|
||||
The engine ships with 10 built-in step types, each in its own subpackage under `src/specify_cli/workflows/steps/`:
|
||||
The engine ships with 11 built-in step types, each in its own subpackage under `src/specify_cli/workflows/steps/`:
|
||||
|
||||
| Type Key | Class | Purpose | Returns `next_steps`? |
|
||||
|----------|-------|---------|-----------------------|
|
||||
| `command` | `CommandStep` | Invoke an installed Spec Kit command via integration CLI | No |
|
||||
| `prompt` | `PromptStep` | Send an arbitrary inline prompt to integration CLI | No |
|
||||
| `shell` | `ShellStep` | Run a shell command, capture output | No |
|
||||
| `init` | `InitStep` | Bootstrap a project (equivalent to `specify init`) | No |
|
||||
| `gate` | `GateStep` | Interactive human review/approval | No (pauses in CI) |
|
||||
| `if` | `IfThenStep` | Conditional branching (then/else) | Yes |
|
||||
| `switch` | `SwitchStep` | Multi-branch dispatch on expression | Yes |
|
||||
@@ -197,6 +198,7 @@ src/specify_cli/
|
||||
│ └── steps/
|
||||
│ ├── command/ # Dispatch command to AI integration
|
||||
│ ├── shell/ # Run shell command
|
||||
│ ├── init/ # Bootstrap a project (specify init)
|
||||
│ ├── gate/ # Human review checkpoint
|
||||
│ ├── if_then/ # Conditional branching
|
||||
│ ├── prompt/ # Arbitrary inline prompts
|
||||
|
||||
@@ -78,7 +78,7 @@ specify workflow run speckit \
|
||||
|
||||
## Step Types
|
||||
|
||||
Workflows support 10 built-in step types:
|
||||
Workflows support 11 built-in step types:
|
||||
|
||||
### Command Steps (default)
|
||||
|
||||
@@ -114,6 +114,24 @@ Run a shell command and capture output:
|
||||
run: "cd {{ inputs.project_dir }} && npm test"
|
||||
```
|
||||
|
||||
### Init Steps
|
||||
|
||||
Bootstrap a project the same way `specify init` does — scaffolding
|
||||
templates, scripts, shared infrastructure, and the selected coding agent
|
||||
integration. Runs non-interactively (defaults to `--ignore-agent-tools`)
|
||||
and resolves the integration from the step config or the workflow default:
|
||||
|
||||
```yaml
|
||||
- id: bootstrap
|
||||
type: init
|
||||
here: true # or: project: my-project
|
||||
integration: copilot # Optional: defaults to workflow integration
|
||||
integration_options: "--skills" # Optional: extra options for the integration
|
||||
script: sh # Optional: sh or ps
|
||||
force: true # Optional: required when target directory already exists
|
||||
preset: healthcare-compliance # Optional preset ID
|
||||
```
|
||||
|
||||
### Gate Steps
|
||||
|
||||
Pause for human review. The workflow resumes when `specify workflow resume` is called:
|
||||
|
||||
Reference in New Issue
Block a user