mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 20:36:23 +08:00
v0.12.2
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ac2cb5daf5 |
feat(cli): implement specify self upgrade (#2475)
* feat(cli): implement specify self upgrade
* fix(cli): normalize self-upgrade prerelease tags
* fix(cli): tighten self-upgrade diagnostics
* fix(cli): harden self-upgrade verification parsing
* fix(cli): sanitize self-check fallback tags
* fix(cli): harden self-check release display
* fix(cli): validate resolved upgrade tags
* fix(cli): tolerate invalid install metadata
* test(cli): align upgrade network mocks
* fix(cli): respect relative installer paths
* fix(cli): tighten upgrade failure handling
* fix(cli): align installer path diagnostics
* fix(cli): validate release and version output
* fix(cli): clarify source checkout guidance
* fix(cli): harden upgrade detection helpers
* fix(cli): avoid echoing invalid release tags
* fix(cli): tolerate argv path resolve failures
* chore: remove self-upgrade formatting-only diffs
* fix: address self-upgrade review feedback
* fix: address self-upgrade review followups
* fix: address self-upgrade review edge cases
* fix: address self-upgrade review docs
* fix: refine self-upgrade review followups
* fix: address self-upgrade review cleanup
* fix: handle self-upgrade review edge cases
* fix: address self-upgrade review nits
* fix: address follow-up self-upgrade review
* fix: resolve self-upgrade review and Windows CI failures
- README: promote "Optional Commands" to ### so it is a sibling of
"Core Commands" under "Available Slash Commands" (consistent heading
levels; avoids the h2->h4 jump a revert would create).
- _version: allow --tag prerelease/dev and build-metadata suffixes to
compose (e.g. v1.0.0-rc1+build.42), matching PEP 440 / semver; the
Version() check still enforces canonical validity.
- tests: compare resolved argv0 as Path objects instead of POSIX strings
so the assertion holds on Windows; skip the relative-installer-path
executable-bit tests on Windows via a new requires_posix marker (they
rely on chmod/X_OK semantics and chdir-into-tmp teardown that do not
hold there). Add a combined prerelease+build-metadata tag test.
* fix: address second self-upgrade review round
- self_check: clarify that the "up to date" branch is reached only for
parseable latest tags (the unparseable case returns earlier), so the
InvalidVersion fallback assumption is not reintroduced.
- self_upgrade: compare target/current as Version instances directly
instead of re-parsing the canonical strings through _is_newer; the
empty-current case stays explicit via the not-None guard.
- tests: document the intentional broad GH_/GITHUB_ env scrub with a test
asserting non-credential context vars (GH_HOST, GITHUB_REPOSITORY, …) are
stripped from the installer subprocess env — a deliberate fail-safe that
also catches credential-adjacent names without a recognized suffix.
* fix: address third self-upgrade review round
- self_upgrade: unify the no-op short-circuits on packaging Version
equality instead of canonical-string equality. Version("1.0") equals
Version("1.0.0") but their str() forms differ, so the old check could
misreport an equal install as "already on latest release or newer".
Both the unpinned and pinned branches now use Version comparison.
- self_upgrade: compare the verified version as a parsed Version against
the target so a non-version verifier result is a mismatch (exit 2)
rather than a coincidental canonical-string match.
- resolver: map HTTP 429 (Too Many Requests / secondary rate limit) to
the rate-limited category so users get the same actionable token hint
as 403.
- _is_github_credential_env_key: document the precise (intentionally
broad) scrub matching contract in the docstring.
- tests: add a trailing-zero Version-equality regression test and a
parametrized HTTP-status categorization test (429 -> rate limited;
404/502 -> verbatim).
* fix: address fourth self-upgrade review round
- self_upgrade: label a pinned target older than the installed version as
"Downgrading" rather than "Upgrading" so `--tag <older>` is not mistaken
for a forward upgrade.
- resolver: drop the unused `typing.Optional` import and annotate the
`--tag` option as `str | None`, consistent with the rest of the module
(verified Typer resolves it on the supported Python versions).
- _is_github_credential_env_key: add `_PASSWORD` and `_CREDENTIALS` to the
recognized credential suffixes and document that only these shapes are
scrubbed (not blanket coverage).
- tests: assert the precise exit code (1) for the re-raised transient
OSError path; skip the InvalidMetadataError test on Pythons where the
real exception is absent instead of fabricating it; update the pinned
downgrade test to expect the "Downgrading" label.
* fix: accept uppercase V prefix in --tag
Fold a leading uppercase `V` (a common paste) to the canonical lowercase
`v` before validating `--tag`. The remainder of the tag stays
case-sensitive on purpose: the validated value is used verbatim as a git
ref, which is case-sensitive on GitHub, so rewriting label/build-metadata
casing could point at a tag that does not exist. Adds a normalization test.
|
||
|
|
f0998348be |
feat: Config-driven opt-in authentication registry with multi-platform support (#2393)
* Initial plan * feat: add authentication provider registry (GitHub + Azure DevOps) Agent-Logs-Url: https://github.com/github/spec-kit/sessions/da7ecfd0-e1c9-48dc-b692-27be0879e976 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * feat: add try-each-provider HTTP helper and wire all catalog fetches through auth registry - Add authentication/http.py with open_url() that tries each configured provider in registry order, falling through on 401/403 to the next, and finally to unauthenticated - Add build_request() for one-shot request construction - Add configured_providers() to registry __init__ - Remove api_base_url() from AuthProvider ABC (unused) - Remove hosts attribute from providers (no host matching) - Replace _github_http.py usage in ExtensionCatalog and PresetCatalog - Wire IntegrationCatalog and WorkflowCatalog through open_url (were unauthenticated) - Wire _fetch_latest_release_tag() through open_url - Wire all inline --from-url downloads through open_url - Fix unused stub variable flagged by code-quality bot - 49 auth tests (positive + negative), 1805 total tests passing * fix: address review — fix stale docstrings, restore Accept header, add extra_headers to open_url - Fix _open_url() docstrings in extensions.py and presets.py that incorrectly claimed redirect stripping behavior - Add extra_headers parameter to open_url() so callers can pass additional headers (e.g. Accept) that persist across retries - Restore Accept: application/vnd.github+json header in _fetch_latest_release_tag() via extra_headers * feat: config-driven opt-in auth via ~/.specify/auth.json Security-first redesign: no credentials are sent unless the user explicitly creates ~/.specify/auth.json mapping hosts to providers. - Add authentication/config.py: loads and validates auth.json with host-to-provider mappings, supports token/token_env/azure-ad/azure-cli - Refactor AuthProvider ABC: auth_headers(token, scheme) + resolve_token(entry) - Refactor GitHubAuth: bearer scheme only, token from config entry - Refactor AzureDevOpsAuth: 4 schemes (basic-pat, bearer, azure-cli, azure-ad) with dynamic token acquisition for azure-cli and azure-ad - Rewrite authentication/http.py: host matching, redirect stripping, provider fallthrough on 401/403, unauthenticated fallback - Add docs/reference/authentication.md with full reference and template - 1823 tests passing (67 auth-specific) * fix: address review — unused imports, host normalization, provider+scheme validation, security hardening - Remove unused imports (os, field, Any) in config.py - Normalize hosts during load (strip + lowercase) - Validate token/token_env are non-empty strings during load - Validate provider+scheme compatibility during load - Fix extra_headers order: auth headers applied last, cannot be overridden - Remove unused 'tried' variable in http.py - Warn (once) on malformed auth.json instead of silent fallback - URL-encode OAuth2 client credentials body in azure_devops.py - Update 403 message to mention auth.json configuration - Fix registry leak in test_register_duplicate (try/finally) - Fix import style consistency in test_authentication.py - Add azure-cli and azure-ad token acquisition tests (mock subprocess/urlopen) - Add autouse fixture to isolate upgrade tests from real auth.json - 1829 tests passing * fix: reject unknown providers, validate azure-ad fields, strip Authorization from extra_headers - Reject unknown provider keys during auth.json load with clear error message - Validate azure-ad tenant_id/client_id/client_secret_env as non-empty strings - Strip Authorization from extra_headers in both build_request and open_url to prevent accidental or intentional bypass of provider-configured auth - Add tests for unknown provider and incompatible scheme validation - 1831 tests passing * fix: extract shared auth test helpers, global config isolation, align docstring - Move _inject_github_config / make_github_auth_entry to tests/auth_helpers.py to eliminate duplication across test_extensions, test_presets, test_upgrade - Move auth config isolation fixture to global conftest.py (autouse) so ALL tests are isolated from ~/.specify/auth.json, not just test_upgrade - Align load_auth_config docstring with actual behavior: ValueError may be caught by higher-level HTTP helpers that warn and continue unauthenticated - 1831 tests passing * fix: preserve auth header across multi-hop redirect chains - Read Authorization from both headers and unredirected_hdrs in _StripAuthOnRedirect to survive multi-hop chains within allowed hosts - Add test_multi_hop_redirect_within_hosts_preserves_auth - 1832 tests passing * fix: use resolved config path in warning/error messages and patch build_opener in no-network test Agent-Logs-Url: https://github.com/github/spec-kit/sessions/86df9557-54f1-4fe4-a25f-9501cb2356cf Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: assert full resolved config path in rate-limit output test Agent-Logs-Url: https://github.com/github/spec-kit/sessions/86df9557-54f1-4fe4-a25f-9501cb2356cf Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: close HTTPError on 401/403, remove _VALID_AUTH_SCHEMES, catch TimeoutExpired, skip POSIX test on Windows, remove unused import Agent-Logs-Url: https://github.com/github/spec-kit/sessions/a1e29737-dd6e-4287-96c1-509e0c96fb21 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: use stable ~/.specify/auth.json in rate-limit message, skip POSIX permission check on Windows Agent-Logs-Url: https://github.com/github/spec-kit/sessions/4636bcdb-87ae-45d6-9545-a40e4effd617 Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: validate host patterns, cache auth config per-process Agent-Logs-Url: https://github.com/github/spec-kit/sessions/889b58a7-7f8c-47e2-8056-931ebcc671cc Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: clarify _is_valid_host_pattern docstring, clean up test sentinel type Agent-Logs-Url: https://github.com/github/spec-kit/sessions/889b58a7-7f8c-47e2-8056-931ebcc671cc Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * fix: improve _is_valid_host_pattern docstring and test observability Agent-Logs-Url: https://github.com/github/spec-kit/sessions/889b58a7-7f8c-47e2-8056-931ebcc671cc Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mnriem <15701806+mnriem@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
9988a46d96 |
ci: add windows-latest to test matrix (#2233)
* ci: add windows-latest to test matrix Add windows-latest to the pytest job OS matrix so tests run on both Ubuntu and Windows for all Python versions. Closes #2232 * test: skip bash-specific tests on Windows Add sys.platform skip markers to all test classes and methods that execute bash scripts via subprocess, so they are skipped on Windows where bash is not available. Mixed classes with both bash and pwsh tests have markers on individual bash methods only. * test: fix 3 Windows-specific test failures - test_manifest: use platform-appropriate absolute path (C:\ on Windows vs /tmp on POSIX) since /tmp is not absolute on Windows - test_extensions: add agent_scripts.ps entry and platform-conditional assertions for codex skill fallback variant test - test_timestamp_branches: use json.dumps() instead of f-string to properly escape Windows backslash paths in feature.json * test: extract requires_bash marker and fix PS test skip Address PR review feedback: - Define a reusable requires_bash marker in conftest.py and use it across all 3 test files instead of repeating the skipif inline - Move test_powershell_scanner_uses_long_tryparse_for_large_prefixes into its own TestSequentialBranchPowerShell class so it is not incorrectly skipped on Windows by the class-level bash marker * test: use runtime bash check instead of platform check Replace sys.platform == 'win32' with an actual bash invocation test to handle environments where bash exists but is non-functional (e.g., WSL stub on Windows without an installed distro). * test: reject WSL bash, accept only MSYS/MINGW on Windows On Windows, verify uname -s reports MSYS, MINGW, or CYGWIN so the WSL launcher (System32\bash.exe) is rejected — it cannot handle native Windows paths used by test fixtures. Add SPECKIT_TEST_BASH=1 env var escape hatch to force-enable bash tests in non-standard setups. * ci: add comment explaining Windows bash test behavior * test: early-reject WSL launcher, fix remaining f-string JSON - Check resolved bash path for System32 before spawning any subprocess to avoid WSL init prompts and timeout during test collection - Convert remaining feature_json f-string writes to json.dumps() so paths with backslashes produce valid JSON on Windows * test: use bare 'bash' for detection to match test invocation On Windows, subprocess.run(['bash', ...]) uses CreateProcess which searches System32 before PATH — finding WSL bash even when shutil.which('bash') returns Git-for-Windows. Probe with bare 'bash' (same as test helpers) so the detection matches actual test behavior. |
||
|
|
4f9d966beb |
Stage 5: Skills, Generic & Option-Driven Integrations (#1924) (#2052)
* Stage 5: Skills, Generic & Option-Driven Integrations (#1924) Add SkillsIntegration base class and migrate codex, kimi, agy, and generic to the integration system. Integrations: - SkillsIntegration(IntegrationBase) in base.py — creates speckit-<name>/SKILL.md layout matching release ZIP output byte-for-byte - CodexIntegration — .agents/skills/, --skills default=True - KimiIntegration — .kimi/skills/, --skills + --migrate-legacy options, dotted→hyphenated skill directory migration - AgyIntegration — .agent/skills/, skills-only (commands deprecated v1.20.5) - GenericIntegration — user-specified --commands-dir, MarkdownIntegration - All four have update-context.sh/.ps1 scripts - All four registered in INTEGRATION_REGISTRY CLI changes: - --ai <agent> auto-promotes to integration path for all registered agents - Interactive agent selection also auto-promotes (bug fix) - --ai-skills and --ai-commands-dir show deprecation notices on integration path - Next-steps display shows correct skill invocation syntax for skills integrations - agy added to CommandRegistrar.AGENT_CONFIGS Tests: - test_integration_base_skills.py — reusable mixin with setup, frontmatter, directory structure, scripts, CLI auto-promote, and complete file inventory (sh+ps) tests - Per-agent test files: test_integration_{codex,kimi,agy,generic}.py - Kimi legacy migration tests, generic --commands-dir validation - Registry updated with Stage 5 keys - Removed 9 dead-mock tests, moved 4 integration tests to proper locations - Fixed all bare project-name tests to use tmp_path - Fixed 6 pre-existing ANSI escape code test failures in test_extensions.py and test_presets.py 1524 tests pass, 0 failures. * fix: remove unused variable flagged by ruff (F841) * fix: address PR review — integration-type-aware deprecation messages and early generic validation - --ai-skills deprecation message now distinguishes SkillsIntegration ("skills are the default") from command-based integrations ("has no effect") - --ai-commands-dir validation for generic runs even when auto-promoted, giving clear CLI error instead of late ValueError from setup() - Resolves review comments from #2052 * fix: address PR review round 2 - Remove unused SKILL_DESCRIPTIONS dict from base.py (dead code after switching to template descriptions for ZIP parity) - Narrow YAML parse catch from Exception to yaml.YAMLError - Remove unused shutil import from test_integration_kimi.py - Remove unused _REGISTRAR_EXEMPT class attr from test_registry.py - Reword --ai-commands-dir deprecation to be actionable - Update generic validation error to mention both --ai and --integration * fix: address PR review round 3 - Clarify parsed_options forwarding is intentional (all options passed, integrations decide what to use) - Extract _strip_ansi() helper in test_extensions.py and test_presets.py - Remove unused pytest import (test_cli.py), unused locals (test_integration_base_skills.py) - Reword --ai-commands-dir deprecation to be actionable without referencing the not-yet-implemented --integration-options * fix: address PR review round 4 - Reorder kimi migration: run super().setup() first so hyphenated targets exist, then migrate dotted dirs (prevents user content loss) - Move _strip_ansi() to shared tests/conftest.py, import from there in test_extensions.py, test_presets.py, test_ai_skills.py - Remove now-unused re imports from all three test files * fix: address PR review round 5 - Use write_bytes() for LF-only newlines (no CRLF on Windows) - Add --integration-options CLI parameter — raw string passed through to the integration via opts['raw_options']; the integration owns parsing of its own options - GenericIntegration.setup() reads --commands-dir from raw_options when not in parsed_options (supports --integration-options="...") - Skip early --ai-commands-dir validation when --integration-options is provided (integration validates in its own setup()) - Remove parse_integration_options from core — integrations parse their own options * fix: address PR review round 6 - GenericIntegration is now stateless: removed self._commands_dir instance state, overrides setup() directly to compute destination from parsed_options/raw_options on the stack - commands_dest() raises by design (stateless singleton) - _quote() in SkillsIntegration now escapes backslashes and double quotes to produce valid YAML even with special characters * fix: address PR review round 7 - Support --commands-dir=value form in raw_options parsing (not just --commands-dir value with space separator) - Normalize CRLF to LF in write_file_and_record() before encoding - Persist ai_skills=True in init-options.json when using a SkillsIntegration, so extensions/presets emit SKILL.md overrides correctly even without explicit --ai-skills flag |