Refactor plan
This plan groups the next xcookie cleanup work into chunks that are safe to review and land independently. The current code does not promise a stable Python public API, so these chunks lean toward cleaner internal names, intermediate models, and provider-neutral planning objects rather than compatibility wrappers.
Chunk 0: generated-output safety net
Add invariant tests before deep rewrites. These tests should exercise generated GitHub Actions, GitLab CI, and staged template files without waiting for hosted CI failures.
Parse generated YAML where possible.
Assert no wheel install target contains empty extras such as
[].Assert CI install extras are declared by the target project.
Assert shell-facing generated paths use POSIX slashes.
Assert GitHub and GitLab agree on shared policy: extras, strict/loose variants, wheelhouse names, and artifact names.
Add Windows-path simulation tests that run on any platform.
Chunk 1: typed template registry and staging layer
Replace raw template dictionaries and regex-style substitutions with explicit models.
Add
TemplateInfofor template registry entries.Add
TemplateContextfor replacement values.Normalize raw registry dictionaries as soon as the registry is built.
Route staging through typed attributes instead of ad-hoc dictionary keys.
Replace regex-backed token substitution with simple
str.replaceover an explicit replacement map.Normalize path substitutions that are rendered into portable shell/YAML text.
This chunk reduces Windows escape bugs and makes template staging easier to inspect and type-check.
Chunk 2: raw config versus resolved config
Separate CLI / pyproject inputs from inferred values consumed by builders.
Keep
XCookieConfigas the user-facing scriptconfig object.Add
ResolvedXCookieConfigas a deterministic, builder-facing view.Move tag normalization, OS normalization, repo/module/package name inference, author inference, Python-version expansion, and
use_uvinference into the resolver.Keep compatibility write-back only while older builders still read
config[...]directly.
Chunk 4: thin provider renderers
After CIPlan exists, make GitHub and GitLab builders mostly syntax renderers.
Initial implementation status:
GitHubActionsRendererowns test/release workflow rendering and receives a singleCIPlanfor the whole workflow.GitLabCIRendererowns provider selection and threads the sameCIPlaninto pure-Python and binary GitLab render paths.Module-level builder functions remain as thin wrappers around the renderer classes while existing template call sites are migrated.
Leaf job builders accept an optional plan, so tests and higher-level renderers can avoid reconstructing CI policy in multiple places.
Remaining follow-up work for this chunk:
Move more provider-specific matrix expansion into renderer methods with small typed helper records.
Move deployment and artifact policy into the shared plan once those records exist.
Expand generated-output invariant tests so GitHub/GitLab renderers are compared on plan-derived content rather than exact full YAML text.
Chunk 5: secret rotation plan
Make secret rotation testable without fake queues and shell inspection.
Add
SecretPlanfor provider, GPG transport, trusted publishing, setup command, GPG upload command, repo-secret upload command, and explanatory notes.Test plan generation directly.
Keep
cmd_queueand subprocess execution in a narrow execution layer.
Chunk 6: generated CI helper commands
Move fragile inline shell and python -c snippets into normal Python helpers.
Add
python -m xcookie.ci_helperscommands for artifact discovery, install-target construction, module-path resolution, and artifact validation.Unit-test the helpers directly.
Make generated CI call helpers instead of embedding complex quoting logic.
Chunk 7: split TemplateApplier into focused modules
Make xcookie/main.py a small CLI / compatibility entrypoint.
Suggested layout:
xcookie/config.py XCookieConfig and config loading
xcookie/resolved_config.py ResolvedXCookieConfig
xcookie/applier.py TemplateApplier orchestration
xcookie/template_registry.py TemplateInfo and TemplateContext
xcookie/staging.py staging, rendering, directives
xcookie/secrets.py SecretPlan and rotation execution
xcookie/ci_helpers.py generated CI helper commands
xcookie/builders/ci_plan.py CIPlan and builder
Chunk 8: inline typing pass
Once the structure settles, add inline annotations throughout the refactored internals.
Use
from __future__ import annotationsin touched files.Prefer dataclasses over loose dictionaries.
Use
Literalfor provider names, dependency modes, artifact kinds, and GPG transports.Remove stub files or compatibility annotations that are no longer useful.
Chunk 9: remove legacy paths
After the new paths are stable, delete compatibility and duplication.
Remove provider-specific extra filtering that moved into
CIPlan.Remove regex substitution helpers from staging.
Remove shell-inspection test doubles made obsolete by
SecretPlan.Clean up stale comments describing legacy behavior.