Skip to content

feat(workflows): install custom step types from local dirs and archives - #4757

Closed
markuswondrak wants to merge 2 commits into
github:mainfrom
markuswondrak:fix/4695-local-step-install
Closed

markuswondrak wants to merge 2 commits into
github:mainfrom
markuswondrak:fix/4695-local-step-install

Conversation

@markuswondrak

Copy link
Copy Markdown
Contributor

Summary

Implements #4695 only. This replacement for #4754 is based directly on current main; it intentionally excludes the unrelated workflow-composition implementation from #4680.

  • Adds workflow step add --dev <directory> and --from <archive-url> with shared package validation, provenance, trust confirmation, and --force handling.
  • Makes step registry updates atomic, validates persisted metadata, serializes install/remove mutations, and hardens staging/cleanup behavior.
  • Refreshes project-local custom step modules, documents package transport and loading behavior, and adds regression coverage.

Evidence

Regression coverage exercises YAML-native metadata rejection, atomic registry serialization failure, staged metadata changes, archive declaration mismatch after redirects, Rich markup package names, runtime refresh between projects, and bundle step delegation.

Validation

  • .venv/bin/python -m pytest tests/specify_cli/workflows/step tests/specify_cli/workflows/test_custom_steps.py tests/specify_cli/bundles/test_primitives.py tests/specify_cli/bundles/test_references.py -q
    • 185 passed
  • Focused Ruff gate passes.

AI Disclosure

Implementation was generated with OpenCode (models: deepseek-v4.1-flash for the original feature and gpt-5.6-terra for remediation), autonomous mode. The AI authored code, tests, documentation, commits, and this PR description on behalf of @markuswondrak.

Markus added 2 commits September 25, 2026 19:26
…es (github#4695)

`specify workflow step add` gains `--dev <directory>` and `--from <archive-url>` alongside the existing catalog source. All three converge on a new `step/installer.py` domain module that owns package validation (shape, symlink/special-file rejection, 512-file/50 MiB limits), same-filesystem staging with revalidation, atomic commit, `--force` replacement, and source-kind-only registry provenance. Direct URLs require a default-deny trust prompt before any request.

Docs document the local-authoring flow and the deferred bundle-local limitation.

Assisted-by: opencode (model: deepseek-v4.1-flash, autonomous)
Assisted-by: OpenCode (model: gpt-5.6-terra, autonomous)
Copilot AI balanced review requested due to automatic review settings September 25, 2026 17:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Removal staging can reload uninstalled code, directory-copy safety has a symlink race, and unrelated composition documentation is included.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
What changed in this PR

Adds local-directory and archive-URL installation for custom workflow steps, with shared validation, atomic registry persistence, runtime refresh, and removal handling.

Changes:

  • Adds --dev, --from, and --force installation flows.
  • Centralizes package validation, staging, provenance, locking, and cleanup.
  • Expands documentation and regression coverage.
File Description
src/​specify_cli/​workflows/​step/​installer.py Implements shared installation and removal logic.
src/​specify_cli/​workflows/​step/​command_add.py Adds local and archive CLI sources.
src/​specify_cli/​workflows/​step/​command_remove.py Delegates transactional removal.
src/​specify_cli/​workflows/​step/​command_info.py Displays installation provenance.
src/​specify_cli/​workflows/​step/​_helpers.py Wraps shared installer validation.
src/​specify_cli/​workflows/​step/​catalog/​_domain.py Makes registry writes atomic.
src/​specify_cli/​workflows/​__init__.py Refreshes project-local step modules.
docs/​reference/​workflows.md Documents custom packages and composition.
docs/​reference/​bundles.md Documents bundle-local step limitations.
tests/​specify_cli/​workflows/​test_custom_steps.py Tests runtime step refresh.
tests/​specify_cli/​workflows/​step/​test_installer.py Covers installer validation and failures.
tests/​specify_cli/​workflows/​step/​test_command_add.py Covers new installation sources.
tests/​specify_cli/​workflows/​step/​test_command_remove.py Covers removal behavior.
tests/​specify_cli/​workflows/​step/​test_command_info.py Tests provenance output.
tests/​specify_cli/​workflows/​step/​test_command_list.py Updates list-command tests.
tests/​specify_cli/​workflows/​step/​test_command_search.py Updates search-command tests.
tests/​specify_cli/​workflows/​step/​catalog/​test_registry.py Tests atomic registry failure handling.
tests/​specify_cli/​workflows/​step/​catalog/​test_command_list.py Updates catalog-list tests.
tests/​specify_cli/​bundles/​test_primitives.py Tests bundle step delegation.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +545 to +546
if stat.S_ISDIR(mode):
_copy(Path(entry.path), target)
Comment on lines +798 to +802
staged_dir = Path(
tempfile.mkdtemp(
prefix=f".{step_id}.removing-", dir=steps_base_dir
)
)
Comment on lines +691 to +695
### Workflow composition (`type: workflow`)

A `workflow` step runs an installed workflow as a **scoped subtree of the
current run** — there is one run, one run directory, and one process. The
included workflow behaves like a function call: values cross the boundary only
@mnriem mnriem added the triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate label Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants