tests.test_workflow_sync module¶
Tests for workflow sync module.
- tests.test_workflow_sync.test_reusable_workflows_sorted()[source]¶
Verify reusable workflows are sorted.
- Return type:
- tests.test_workflow_sync.test_all_workflow_files_sorted()[source]¶
Verify all workflow files are sorted.
- Return type:
- tests.test_workflow_sync.test_non_reusable_subset_of_all()[source]¶
Verify non-reusable workflows are a subset of all workflows.
- Return type:
- tests.test_workflow_sync.test_reusable_subset_of_all()[source]¶
Verify reusable workflows are a subset of all workflows.
- Return type:
- tests.test_workflow_sync.test_no_overlap()[source]¶
Verify no overlap between reusable and non-reusable sets.
- Return type:
- tests.test_workflow_sync.test_union_is_all()[source]¶
Verify union of reusable and non-reusable equals all.
- Return type:
- tests.test_workflow_sync.test_reusable_has_workflow_call(filename)[source]¶
Verify all reusable workflows have workflow_call trigger.
- Return type:
- tests.test_workflow_sync.test_non_reusable_no_workflow_call(filename)[source]¶
Verify non-reusable workflows lack workflow_call trigger.
- Return type:
- tests.test_workflow_sync.test_returns_trigger_info(filename)[source]¶
Verify return type is WorkflowTriggerInfo.
- Return type:
- tests.test_workflow_sync.test_autofix_has_secrets()[source]¶
Verify autofix.yaml defines secrets.
- Return type:
- tests.test_workflow_sync.test_changelog_has_secrets()[source]¶
Verify changelog.yaml defines secrets.
- Return type:
- tests.test_workflow_sync.test_release_has_secrets()[source]¶
Verify release.yaml defines secrets.
PYPI_TOKENwas removed once the publish step migrated to OIDC-based Trusted Publishing via thepublish-pypicomposite action invoked from the caller workflow. See pypi/warehouse#11096 for context.- Return type:
- tests.test_workflow_sync.test_unsubscribe_has_secrets()[source]¶
Verify unsubscribe.yaml defines secrets.
- Return type:
- tests.test_workflow_sync.test_unsubscribe_has_call_inputs()[source]¶
Verify unsubscribe.yaml exposes its dispatch inputs to callers.
- Return type:
- tests.test_workflow_sync.test_thin_caller_workflow_call_inputs_stay_minimal()[source]¶
No reusable workflow may grow its forwarded workflow_call inputs unreviewed.
Every input a thin caller forwards widens the blast radius of the cooldown step-back in
repomatic.init_project.resolve_default_pin(): that pin can reference a reusable workflow one release older than the caller was generated from, and GitHub rejects a forwarded input the older workflow does not yet declare. Locking the surface keeps a new input a deliberate act, weighed against the step-back, not an accident. To relax it, add the input to EXPECTED_THIN_CALLER_CALL_INPUTS with that trade-off in mind.- Return type:
- tests.test_workflow_sync.test_unsubscribe_caller_forwards_inputs()[source]¶
Verify the generated caller forwards each input with the right expression.
Boolean inputs are coerced with
== trueso non-dispatch events (schedule), where the caller’sinputscontext is null, pass an explicitfalseinstead of relying on GitHub’s undocumented handling of a null value for a boolean-typed input.- Return type:
- tests.test_workflow_sync.test_caller_with_block_matches_call_inputs(filename)[source]¶
Caller emits a
with:forwarding block only for declared call inputs.- Return type:
- tests.test_workflow_sync.test_no_python_literals_in_yaml(filename)[source]¶
Verify generated YAML contains no Python dict/list literals.
Regression test for a bug where
_render_trigger_valuefell through tostr()on nested dicts, producing{'key': 'value'}instead of block-style YAML.Kept alongside the two linter passes below, which do not subsume it. actionlint rejects a leaked literal only where the workflow schema constrains the value it landed in (a flow mapping under
schedule:, which takes a list); the same leak inside a free-form value, apaths:entry or an input default, is well-formed YAML that both linters accept. This scans the rendered text wherever it appears, and names the defect directly.- Return type:
- tests.test_workflow_sync.test_generated_caller_ends_with_one_newline(filename)[source]¶
Every generated caller ends with exactly one trailing newline.
Nothing downstream of the generator collapses trailing whitespace:
repomatic.init_project._write_managedwrites callers withnormalize=False, because their exact bytes carry downstream-owned jobs verbatim. A stray blank line therefore reaches every consuming repository.release.yamlshipped one for exactly that reason, alone among the callers: its release lane already ends with the blank separator that_generate_release_callerthen appended a second time, so the file ended}}\n\n.- Return type:
- tests.test_workflow_sync.PINNED_CALLER_SHA = '0000000000000000000000000000000000000000'¶
Placeholder commit SHA for the pinned
uses:rendering.actionlint checks the shape of a
uses:reference without resolving it, so a synthetic SHA exercises that branch with no network lookup and no tie to a commit that has to keep existing.
- tests.test_workflow_sync.test_generated_callers_pass_yamllint(tmp_path)[source]¶
Generated thin callers must clear the yamllint rules CI enforces.
A thin caller is the one artifact this repository generates and never materializes into its own tree:
.github/workflows/here holds the canonical workflows, so thelintjob’s yamllint step never sees a generated file. The first checkout that does is a downstream repo, a release later, which makes a regression here something a user reports rather than something CI catches. Running the pinned yamllint over the output moves that verdict back to the commit that introduced it.In practice that is the 120-column cap and little else: a native config replaces yamllint’s defaults rather than layering over them, and the bundled
yamllint.yamlsetsline-lengthalone. It is still the rule worth guarding, being the one a renderer breaches by widening a single interpolated value, and the onetests/test_prepare_release.pyre-implements in Python to cover the freeze path. Any rule the bundled config grows later is covered here for free.Marked
once: it resolves yamllint through uvx, so one runner suffices.- Return type:
- tests.test_workflow_sync.test_generated_callers_pass_actionlint(tmp_path)[source]¶
Generated thin callers must be workflows GitHub will actually accept.
Companion to the yamllint pass, and the stronger half of the pair: it reads the workflow schema, so it sees what neither a YAML parser nor a column cap can. A
uses:reference stripped of its@ref, a${{ }}expression naming a context property that does not exist, a trigger value whose type is wrong for the key it sits under: each parses cleanly, and each fails at startup on the runner. For a generated caller that means failing in someone else’s repository.Marked
once: it downloads the pinned actionlint binary, so one runner suffices.- Return type:
- tests.test_workflow_sync.test_call_inputs_are_dispatch_passthroughs(filename)[source]¶
Verify
workflow_callinputs only mirrorworkflow_dispatchones.All persistent configuration lives in
[tool.repomatic]inpyproject.toml: workflows read config via therepomaticCLI instead of accepting inputs. The onlyworkflow_callinputs allowed are passthroughs of the workflow’s ownworkflow_dispatchinputs, so generated thin callers can forward a manual dispatch to the reusable workflow (see_generate_thin_caller). Type and default must match the dispatch declaration, so both entry points behave identically.- Return type:
- tests.test_workflow_sync.test_changelog_has_workflow_run()[source]¶
Verify changelog.yaml has workflow_run trigger.
- Return type:
- tests.test_workflow_sync.test_autolock_has_schedule()[source]¶
Verify autolock.yaml has schedule trigger.
- Return type:
- tests.test_workflow_sync.test_nonexistent_file()[source]¶
Raise FileNotFoundError for missing workflow.
- Return type:
- tests.test_workflow_sync.test_generates_valid_yaml(filename)[source]¶
Verify generated content is valid YAML.
- Return type:
- tests.test_workflow_sync.test_mirrors_canonical_dispatch(filename)[source]¶
Caller’s workflow_dispatch presence mirrors canonical, no synthesis.
- Return type:
- tests.test_workflow_sync.test_release_thin_caller_synthesizes_triggers()[source]¶
The release.yaml caller synthesizes the standard push + dispatch triggers.
_generate_release_callersynthesizes the standard release triggers rather than mirroring the canonical release.yaml’s own (so a dogfooding-only trigger added upstream never leaks into downstream callers). The weekly full-fleetscheduleis the one canonical trigger carried over, verbatim.- Return type:
- tests.test_workflow_sync.test_release_caller_emits_concurrency()[source]¶
The release.yaml caller carries the entry workflow’s concurrency group.
Unlike a simple thin caller, the multi-lane release caller must declare concurrency on its push-triggered entry: the concurrency-bearing engine lane runs behind
needs: build, so a block there joins its group too late to cancel superseded runs. The group protects release commits via github.sha.- Return type:
- tests.test_workflow_sync.test_correct_uses_ref(filename)[source]¶
Verify correct uses reference in job.
- Return type:
- tests.test_workflow_sync.test_starts_with_document_marker(filename)[source]¶
Verify generated YAML starts with — document marker.
- Return type:
- tests.test_workflow_sync.test_quoted_on_key(filename)[source]¶
Verify the on key is quoted as
"on":.- Return type:
- tests.test_workflow_sync.test_autofix_passes_secrets_explicitly()[source]¶
Verify autofix.yaml thin caller passes secrets explicitly.
- Return type:
- tests.test_workflow_sync.test_changelog_passes_secrets_explicitly()[source]¶
Verify changelog.yaml thin caller passes secrets explicitly.
- Return type:
- tests.test_workflow_sync.test_release_passes_secrets_explicitly()[source]¶
Verify release.yaml thin caller passes secrets explicitly.
PYPI_TOKENis no longer in the secrets surface: PyPI uploads run via OIDC Trusted Publishing inside the caller-sidepublish-pypijob.- Return type:
- tests.test_workflow_sync.test_release_thin_caller_emits_publish_pypi_job()[source]¶
Verify the release.yaml caller emits the publish-pypi job gated on the build lane.
- Return type:
- tests.test_workflow_sync.test_release_thin_caller_emits_build_and_engine_lanes()[source]¶
Verify the release.yaml caller calls both reusable lanes with pinned refs.
The build lane (
_release-build.yaml) feeds publish-pypi; the engine lane (_release-engine.yaml) runs binaries and finalization. Both are pinned to the requested version and the engine lane is gated on the build lane so it can download the run-scoped wheel.- Return type:
- tests.test_workflow_sync.test_release_thin_caller_publish_pypi_uses_sha_pin()[source]¶
Verify SHA-pinned commit form propagates to the publish-pypi action ref.
- Return type:
- tests.test_workflow_sync.test_non_release_thin_caller_omits_publish_pypi_job()[source]¶
Verify non-release thin callers do not gain a publish-pypi job.
- Return type:
- tests.test_workflow_sync.test_release_thin_caller_publish_pypi_omits_checkout()[source]¶
The downstream publish-pypi job should not require an explicit checkout.
The cross-repo composite action
uses:form is fetched by GitHub automatically: droppingactions/checkoutkeeps the generator free of third-party action SHA pins (whichsync-action-pinswould not see, since the generator’s source is.py, not.yaml).- Return type:
- tests.test_workflow_sync.test_release_thin_caller_publish_pypi_sourced_from_release_yaml()[source]¶
The publish-pypi job body must be sourced from the bundled release.yaml.
Deriving it from a
.yamlfile (rather than building it in Python) keeps any future third-party SHA pin in the job body visible tosync-action-pins. The canonical entry carries the job with a local./action ref that the generator reshapes for downstream callers.- Return type:
- tests.test_workflow_sync.test_release_thin_caller_publish_pypi_keeps_canonical_runner()[source]¶
The generated downstream job inherits the canonical runner verbatim.
The body is carried through from release.yaml unchanged except for the checkout drop and the action-ref pin, so downstream callers run on whichever image repomatic itself uses. The expectation is read from that source rather than written here: pinning the literal made this fail as a fleet migration rather than as the drift it exists to catch.
- Return type:
- tests.test_workflow_sync.test_release_thin_caller_rewrites_local_action_ref(monkeypatch)[source]¶
Render rewrites the canonical local action ref and drops the checkout.
The bundled release.yaml dogfoods the in-tree action via a local
./ref (checking itself out first); the downstream caller must instead pin the cross-repo composite action (so its OIDCjob_workflow_refresolves to its own release.yaml) and needs no checkout.- Return type:
- tests.test_workflow_sync.test_release_thin_caller_drops_self_repository_ignores()[source]¶
No downstream caller inherits the canonical entry’s zizmor suppressions.
The canonical
release.yamlkeeps workspace-relativeuses: ./…refs and silences zizmor’sself-repositoryaudit inline on each one. Downstream callers get pinned cross-repo refs, which the audit never fires on, so the suppression is scaffolding: carried through it would trail a second#comment past the version pin and push the line over yamllint’s limit.- Return type:
- tests.test_workflow_sync.test_release_thin_caller_unrecognized_job_raises(monkeypatch)[source]¶
Render fails loudly when release.yaml’s publish-pypi job lacks the action.
A canonical entry reshaped in a way the renderer was not updated to handle (no local action ref in the job) must surface as an error, not a silent no-op that emits a malformed workflow.
- Return type:
- tests.test_workflow_sync.test_lint_passes_secrets_explicitly()[source]¶
Verify lint.yaml thin caller passes secrets explicitly.
- Return type:
- tests.test_workflow_sync.test_custom_version()[source]¶
Verify custom version in uses reference.
- Return type:
- tests.test_workflow_sync.test_custom_repo()[source]¶
Verify custom repo in uses reference.
- Return type:
- tests.test_workflow_sync.test_non_reusable_raises()[source]¶
Raise ValueError for non-reusable workflow.
- Return type:
- tests.test_workflow_sync.test_has_jobs_section()[source]¶
Verify generated YAML has jobs section.
- Return type:
- tests.test_workflow_sync.test_has_name()[source]¶
Verify generated YAML has name field.
- Return type:
- tests.test_workflow_sync.test_identifies_thin_caller(tmp_path)[source]¶
Identify a thin caller workflow.
- Return type:
- tests.test_workflow_sync.test_returns_none_for_non_caller(tmp_path)[source]¶
Return None for non-caller workflow.
- Return type:
- tests.test_workflow_sync.test_returns_none_for_invalid_yaml(tmp_path)[source]¶
Return None for invalid YAML file.
- Return type:
- tests.test_workflow_sync.test_returns_none_for_missing_file(tmp_path)[source]¶
Return None for missing file.
- Return type:
- tests.test_workflow_sync.test_identify_custom_repo(tmp_path)[source]¶
Match with custom repo.
- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_single()[source]¶
Preserve a single extra downstream job.
- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_odd_indent_body_stays_managed()[source]¶
A hand-edited 3-space body line does not truncate the managed job.
The body walk used to require exactly 4+ spaces, so a 3-space line ended it early and the managed job’s tail leaked into the extras, which the next sync duplicated below the regenerated lanes.
- Return type:
- tests.test_workflow_sync.test_extras_define_jobs_ignores_comment_only_tail()[source]¶
A trailing comment after the managed lanes is not a downstream job.
bool(extra)used to gate the explicit-permissions contract, so a repository keeping a plain trailing note below its thin caller was silently flipped onto thepermissions: {}shape it never asked for.- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_treats_publish_pypi_as_managed()[source]¶
Verify the caller-side publish-pypi job is recognized as managed.
When a thin-caller for
release.yamlis regenerated, the publish-pypi job (which useskdeldycke/repomatic/.github/actions/publish-pypi@...) must be treated as part of the managed surface and not preserved as an extra job. Otherwise re-syncing would duplicate it.- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_multiple()[source]¶
Preserve multiple extra downstream jobs.
- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_none()[source]¶
Return empty string when no extra jobs exist.
- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_not_thin_caller()[source]¶
Return empty string for a non-thin-caller workflow.
- Return type:
- tests.test_workflow_sync.test_extract_extra_jobs_invalid_yaml()[source]¶
Return empty string for invalid YAML.
- Return type:
- tests.test_workflow_sync.sync_caller(filename, target, version='v1.2.3', commit_sha=None, paths_spec=None)[source]¶
Render filename onto target, the way
repomatic initsyncs one file.Wraps the single render seam so a test never re-implements the read-render- write dance that
_init_workflowsperforms, which is how the two used to drift apart.- Return type:
- tests.test_workflow_sync.test_thin_caller_sync_preserves_extra_jobs(tmp_path)[source]¶
End-to-end: sync overwrites the managed job but preserves extras.
- Return type:
- tests.test_workflow_sync.test_check_has_workflow_dispatch(tmp_path, body, is_issue, needle, level)[source]¶
The
workflow_dispatchcheck reports presence, absence and unreadability.- Return type:
- tests.test_workflow_sync.test_check_version_pinned(tmp_path, ref, is_issue, needle)[source]¶
A caller must pin the upstream workflow to a tag, never to a branch.
- Return type:
- tests.test_workflow_sync.test_matching_triggers(tmp_path)[source]¶
Pass when triggers match canonical.
- Return type:
- tests.test_workflow_sync.test_missing_trigger(tmp_path)[source]¶
Fail when a trigger is missing.
- Return type:
- tests.test_workflow_sync.test_extra_trigger(tmp_path)[source]¶
Fail when a caller declares a trigger absent from canonical.
- Return type:
- tests.test_workflow_sync.test_caller_with_synthetic_triggers_for_call_only_canonical(tmp_path)[source]¶
Caller with synthesized triggers is clean when canonical has no non-call triggers.
The downstream release.yaml synthesizes push + workflow_dispatch because _release-engine.yaml is call-only. check_triggers_match must not flag those synthesized triggers as “extra” when the canonical defines none.
- Return type:
- tests.test_workflow_sync.ALL_RELEASE_SECRETS = ' secrets:\n REPOMATIC_PAT: ${{ secrets.REPOMATIC_PAT }}\n VIRUSTOTAL_API_KEY: ${{ secrets.VIRUSTOTAL_API_KEY }}\n'¶
Every secret
_release-engine.yamldeclares, passed explicitly.
- tests.test_workflow_sync.test_check_secrets_passed(tmp_path, caller, canonical, secrets_block, is_issue, needle)[source]¶
A caller must forward every secret its canonical workflow declares.
- Return type:
- tests.test_workflow_sync.CLEAN_WORKFLOW = '---\n"on":\n workflow_dispatch:\n push:\njobs:\n build:\n runs-on: ubuntu-latest\n'¶
A workflow the linter finds nothing to say about.
- tests.test_workflow_sync.DISPATCHLESS_WORKFLOW = '---\n"on":\n push:\n'¶
A workflow missing
workflow_dispatch, the linter’s mildest complaint.
- tests.test_workflow_sync.test_run_workflow_lint(tmp_path, body, fatal, exit_code)[source]¶
Lint exit codes across a clean tree, an empty one, and both severities.
- Return type:
- tests.test_workflow_sync.test_run_workflow_lint_missing_directory(tmp_path)[source]¶
A directory that does not exist is an error, not an empty tree.
- Return type:
- tests.test_workflow_sync.test_thin_caller_exempt_from_workflow_dispatch_check(tmp_path)[source]¶
Thin callers wrapping a canonical without workflow_dispatch lint clean.
cancel-runs.yamlandrelease.yamlintentionally lackworkflow_dispatch. A thin caller mirroring them must not be flagged by the standaloneworkflow_dispatchcheck.- Return type:
- tests.test_workflow_sync.test_release_thin_caller_lints_clean(tmp_path)[source]¶
The generated downstream release.yaml lints clean end-to-end.
Regression test: the synthesized push + workflow_dispatch triggers must not be flagged as “extra” by check_triggers_match when the canonical _release-engine.yaml defines no non-call triggers.
- Return type:
- tests.test_workflow_sync.test_every_reusable_workflow_renders(tmp_path, filename)[source]¶
Each reusable workflow renders into a caller carrying the version ref.
- Return type:
- tests.test_workflow_sync.test_sync_overwrites(tmp_path)[source]¶
A file that is not a caller at all is replaced wholesale.
Nothing in it parses as a managed lane, so there are no extras to carry over and the render starts from the canonical workflow.
- Return type:
- tests.test_workflow_sync.test_non_reusable_workflow_refuses_to_render(tmp_path)[source]¶
A workflow with no
workflow_calltrigger cannot become a caller.tests.yamlis copied downstream header-first by_init_workflows, never rendered as a caller, so asking for one is a programming error rather than a file to write.- Return type:
- tests.test_workflow_sync.test_default_level()[source]¶
Verify default annotation level is WARNING.
- Return type:
- tests.test_workflow_sync.BUNDLED_WITH_CONCURRENCY = ('autofix.yaml', 'changelog.yaml', 'debug.yaml', 'docs.yaml', 'labels.yaml', 'lint.yaml', 'metrics.yaml', 'release.yaml', 'tests.yaml')¶
Bundled workflows that define a concurrency block.
- tests.test_workflow_sync.BUNDLED_WITHOUT_CONCURRENCY = ('_release-engine.yaml', 'autolock.yaml', 'cancel-runs.yaml', 'unsubscribe.yaml')¶
Bundled workflows that do not.
_release-engine.yamlis here by design: it delegates concurrency to the push-triggeredrelease.yamlentry that calls it (see_generate_release_caller).
- tests.test_workflow_sync.test_concurrency_present(filename)[source]¶
Verify concurrency is extracted for workflows that define it.
- Return type:
- tests.test_workflow_sync.test_concurrency_absent(filename)[source]¶
Verify concurrency is None for workflows without it.
- Return type:
- tests.test_workflow_sync.test_concurrency_preserves_expressions()[source]¶
Verify raw concurrency preserves
${{ }}expressions.- Return type:
- tests.test_workflow_sync.test_concurrency_preserves_comments()[source]¶
Verify raw concurrency preserves inline comments.
- Return type:
- tests.test_workflow_sync.test_thin_caller_omits_concurrency(filename)[source]¶
Verify simple thin callers never include concurrency.
A simple thin caller’s single job joins the reusable workflow’s own concurrency group immediately when called via
workflow_call, so duplicating the block in the caller is unnecessary.- Return type:
- tests.test_workflow_sync.test_thin_caller_drops_upstream_source_paths(filename)[source]¶
Thin callers drop
repomatic/-prefixed paths even without source_paths.Universal entries (
pyproject.toml, workflow self-refs) are preserved so trigger semantics carry over from the canonical workflow.- Return type:
- tests.test_workflow_sync.test_thin_caller_sha_pinned()[source]¶
Thin caller with
commit_shaproduces@sha # version.- Return type:
- tests.test_workflow_sync.test_thin_caller_sha_none_fallback()[source]¶
Thin caller without
commit_shaproduces@version.- Return type:
- tests.test_workflow_sync.test_thin_caller_sha_pinned_yaml_valid()[source]¶
SHA-pinned thin caller is valid YAML with comment stripped.
- Return type:
- tests.test_workflow_sync.test_identify_canonical_workflow_sha_pinned(tmp_path)[source]¶
identify_canonical_workflowrecognizes SHA-pinned thin callers.- Return type:
- tests.test_workflow_sync.test_check_version_pinned_sha(tmp_path)[source]¶
check_version_pinnedpasses for SHA-pinned refs.- Return type:
- tests.test_workflow_sync.test_header_extraction_tests_yaml()[source]¶
Verify header extraction for tests.yaml.
- Return type:
- tests.test_workflow_sync.test_header_extraction_lint_yaml()[source]¶
Verify header extraction for lint.yaml.
- Return type:
- tests.test_workflow_sync.test_header_extraction_nonexistent()[source]¶
Raise FileNotFoundError for missing workflow.
- Return type:
- tests.test_workflow_sync.test_substitute_source_paths(paths, source_paths, expected)[source]¶
Upstream source globs are retargeted; everything else is kept or dropped.
- Return type:
- tests.test_workflow_sync.test_adapt_trigger_paths(config, filename, spec, expected)[source]¶
Every
PathsSpecknob, applied to one trigger’s config block.- Return type:
- tests.test_workflow_sync.test_thin_caller_release_with_source_paths()[source]¶
Verify release.yaml thin caller has no path filters.
release.yaml only has
push(nopaths:) andworkflow_call, sosource_pathshas no effect.- Return type:
- tests.test_workflow_sync.test_thin_caller_changelog_with_source_paths()[source]¶
Verify changelog.yaml thin caller keeps universal paths unchanged.
changelog.yaml has no upstream source glob, so source_paths has no effect beyond keeping all paths intact.
- Return type:
- tests.test_workflow_sync.test_thin_caller_lint_no_paths_with_source_paths()[source]¶
Verify workflows without paths don’t gain paths from source_paths.
- Return type:
- tests.test_workflow_sync.test_header_with_source_paths_substitutes()[source]¶
Verify header generation replaces upstream source paths.
- Return type:
- tests.test_workflow_sync.test_tests_yaml_runs_on_bundled_assets_upstream_only()[source]¶
Upstream Tests run on a skill or agent edit, downstream Tests do not.
The bundled skills and agents reach the package through symlinks, so an edit to one changes no
repomatic/**path, while the suite reads them all.- Return type:
- tests.test_workflow_sync.test_header_without_source_paths_drops_upstream_glob()[source]¶
Without source_paths, the upstream source glob is dropped from the header.
- Return type:
- tests.test_workflow_sync.test_header_with_extra_paths_appends()[source]¶
extra_pathsare appended to every paths block in the header.- Return type:
- tests.test_workflow_sync.test_header_with_ignore_paths_strips_canonical()[source]¶
ignore_pathsremoves matching entries from every paths block.- Return type:
- tests.test_workflow_sync.test_header_per_workflow_override_replaces_paths_blocks()[source]¶
Per-workflow
pathsoverride replaces every block in the workflow.- Return type:
- tests.test_workflow_sync.test_header_per_workflow_override_does_not_apply_to_other_files()[source]¶
Override scoped to one filename does not affect another workflow’s header.
- Return type:
- tests.test_workflow_sync.test_header_override_quotes_entries_needing_it(entry)[source]¶
Override entries that cannot stand as plain scalars are quoted.
tests.yamlships apaths:block whose entries are all plain, so there is no quote style to inherit. Emitting one of these bare produced a header that stopped parsing, which GitHub answers by ignoring the workflow.- Return type:
- tests.test_workflow_sync.test_needs_yaml_quote(value, expected)[source]¶
Only entries that would not round-trip as plain scalars are flagged.
- Return type:
- tests.test_workflow_sync.test_derive_source_paths_from_name()[source]¶
Derive source paths from [project.name].
- Return type:
- tests.test_workflow_sync.test_derive_source_paths_underscore_name()[source]¶
Derive source paths when name already uses underscores.
- Return type:
- tests.test_workflow_sync.test_derive_source_paths_simple_name()[source]¶
Derive source paths from a simple name without hyphens.
- Return type:
- tests.test_workflow_sync.test_derive_source_paths_no_name()[source]¶
Return empty list when no project name defined.
- Return type:
- tests.test_workflow_sync.test_derive_source_paths_empty_pyproject()[source]¶
Return empty list for empty pyproject data.
- Return type:
- tests.test_workflow_sync.test_resolve_source_paths_explicit_config()[source]¶
Use explicitly configured source paths.
- Return type:
- tests.test_workflow_sync.test_resolve_source_paths_none_derives()[source]¶
Auto-derive when config is None.
- Return type:
- tests.test_workflow_sync.test_resolve_source_paths_empty_list_returns_none()[source]¶
Return None when explicitly set to empty list.
- Return type:
- tests.test_workflow_sync.test_resolve_source_paths_no_name_returns_none()[source]¶
Return None when no project name and no config.
- Return type:
- tests.test_workflow_sync.test_split_yaml_quote(scalar, expected)[source]¶
Strip outer matching quotes; pass through plain scalars.
- Return type:
- tests.test_workflow_sync.test_thin_caller_paths_spec_extra_paths_appends()[source]¶
Thin caller picks up
extra_pathsin every paths-bearing trigger.- Return type:
- tests.test_workflow_sync.test_thin_caller_paths_spec_ignore_strips_canonical()[source]¶
Thin caller strips
ignore_pathsentries from every paths block.- Return type:
- tests.test_workflow_sync.test_thin_caller_paths_spec_per_workflow_override_replaces_wholesale()[source]¶
Per-workflow override replaces the thin caller’s paths list verbatim.
- Return type:
- tests.test_workflow_sync.test_header_ignore_drops_block_when_empty()[source]¶
When
ignore_pathsempties a block, thepaths:key is removed.- Return type:
- tests.test_workflow_sync.test_header_ignore_applies_before_extras()[source]¶
ignore_pathsruns beforeextra_paths: an entry stripped then re-added stays.A canonical entry listed in
ignore_pathsandextra_pathssimultaneously is stripped first and then appended at the tail (not preserved in place).- Return type:
- tests.test_workflow_sync.test_header_paths_ignore_block_untouched_by_knobs()[source]¶
extra_paths/ignore_paths/per-workflow override only targetpaths:.paths-ignore:blocks are written by the canonical workflow as exclusion filters; they are not the trigger gate the knobs are designed for. The header rewriter must leave them alone.- Return type:
- tests.test_workflow_sync.test_header_preserves_canonical_quote_style()[source]¶
Header rewriter preserves quote style for unmodified entries.
- Return type:
- tests.test_workflow_sync.test_canonical_caller_permissions(filename, expected)[source]¶
Union the job-level scopes a caller has to forward.
- Return type:
- tests.test_workflow_sync.test_canonical_caller_permissions_keeps_most_permissive(monkeypatch, levels, expected)[source]¶
Resolve a scope granted at odds across jobs to its strongest level.
Order of appearance must not matter: a narrower grant later in the file cannot walk back a wider one already collected.
- Return type:
- tests.test_workflow_sync.test_generate_thin_caller_omits_permissions_by_default()[source]¶
Leave a caller with no extra jobs exactly as before.
Its single managed job inherits the repository default, which the reusable workflow’s own
permissions:blocks then cap. Pinning{}here would buy nothing and starve that call.- Return type:
- tests.test_workflow_sync.test_generate_thin_caller_emits_both_permission_halves()[source]¶
Pin least privilege at the top and forward the scopes the call needs.
- Return type:
- tests.test_workflow_sync.test_generate_thin_caller_spells_out_an_empty_forward()[source]¶
Emit
permissions: {}on the job when no upstream job asks for a scope.- Return type:
- tests.test_workflow_sync.test_thin_caller_permissions_track_extra_jobs(tmp_path)[source]¶
Add the contract when extra jobs appear, drop it when they go away.
- Return type:
- tests.test_workflow_sync.test_release_caller_always_denies_permissions(tmp_path)[source]¶
The canonical entry’s deny-by-default reaches every downstream copy.
Unlike a plain thin caller, whose contract only appears once extra jobs do,
release.yamlis copied from an entry that always carries the key.- Return type:
- tests.test_workflow_sync.test_release_caller_preserves_downstream_needs(tmp_path, downstream_needs, expected)[source]¶
Carry the consumer’s own
needs:edges across a sync.A repo building a
release-assetsfile gates the engine on the job packing it. That edge sits on a lane this renderer regenerates, so without carrying it over everyrepomatic initwould silently drop the gate. Edges naming a managed lane or a vanished job are dropped instead of echoed back.- Return type:
- tests.test_workflow_sync.test_release_caller_drops_repomatic_local_needs()[source]¶
A canonical
needs:edge on a repomatic-only job never reaches downstream.The canonical entry gates its engine lane on
pack-plugin, a job that exists only upstream: the renderer copies the three managed lanes and leaves it behind. Echoing the edge would emit aneeds:on a job the generated file does not define, which GitHub rejects at startup, breaking the release workflow of every consumer at once.- Return type:
- tests.test_workflow_sync.test_generated_caller_with_extra_jobs_satisfies_lint(tmp_path, monkeypatch)[source]¶
Close the loop the warning opened.
check_workflow_permissionsis what flagged these callers in the first place, and it fails both on a missing top-level key and on a managed call starved by an empty one. A generated caller must clear both at once.- Return type:
- tests.test_workflow_sync.test_thin_caller_rendering_has_a_single_seam()[source]¶
Only
render_thin_caller_for_targetmay render a caller for a file.That helper is where the downstream state a sync must carry over (extra jobs, the consumer’s own
needs:edges) is read and passed on. A second call site is a second place to forget one, which is exactly how theneeds:edge came to be dropped on therepomatic initpath while the suite driving the other copy stayed green.Reach the renderer through the helper. If a caller genuinely needs raw output with no file behind it, it is not a sync and should say so here.
- Return type:
- tests.test_workflow_sync.test_configured_input_reaches_the_generated_caller()[source]¶
A
[tool.repomatic]value lands in the caller, behind the dispatch input.repomatic initis the only moment it can: the reusable workflow runs with no checkout of the calling repository, so nothing downstream can readpyproject.tomlat run time. The dispatch input stays in front of the literal, so a manual run still overrides the configured default.- Return type:
- tests.test_workflow_sync.test_default_config_leaves_the_caller_untouched()[source]¶
Configuring nothing churns no caller: the two renders are identical.
- Return type:
- tests.test_workflow_sync.test_canonical_input_defaults_match_the_config_field(input_name, field_name)[source]¶
The canonical workflow and the config field declare the same default.
The literal is spelled three times in
unsubscribe.yaml: theworkflow_callinput, theworkflow_dispatchinput, and the|| '...'fallback the step applies when a non-dispatch event leavesinputsnull. A default that moves in the config and not in the YAML would leave a repository configuring nothing on a different value than the one the schema documents.- Return type: