engineering-process 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {engineering_process-0.2.0/engineering_process.egg-info → engineering_process-0.3.0}/PKG-INFO +33 -1
- {engineering_process-0.2.0 → engineering_process-0.3.0}/README.md +32 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/VERSIONING.md +24 -1
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/__init__.py +1 -1
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/lifecycle.py +36 -1
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/publication.py +57 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/release.py +0 -4
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/release_candidate.py +67 -2
- {engineering_process-0.2.0 → engineering_process-0.3.0/engineering_process.egg-info}/PKG-INFO +33 -1
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process.egg-info/SOURCES.txt +5 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/pyproject.toml +1 -1
- engineering_process-0.3.0/release.json +54 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/templates/adopt-process.py +27 -2
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_adoption_runner.py +61 -1
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_lifecycle.py +62 -0
- engineering_process-0.3.0/tests/test_process_install.py +235 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_publication.py +53 -0
- engineering_process-0.3.0/tests/test_publish_event.py +57 -0
- engineering_process-0.3.0/tests/test_pypi_publication.py +156 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_release.py +43 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_release_candidate.py +157 -1
- engineering_process-0.3.0/tests/test_release_preparation.py +68 -0
- engineering_process-0.3.0/tests/test_release_qualification.py +99 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_self_hosting.py +217 -12
- engineering_process-0.2.0/release.json +0 -114
- {engineering_process-0.2.0 → engineering_process-0.3.0}/LICENSE +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/MANIFEST.in +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/PRODUCTION_STANDARD.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/bundles.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/__main__.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/_download_worker.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/_supervisor_posix.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/_supervisor_windows.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/_windows_job.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/adoption.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/artifact_attestation.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/bootstrap.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/bundles.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/cli.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/contracts.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/distribution.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/distribution_verify.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/environment.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/evidence.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/git.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/git_attributes.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/helper_launch.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/impact.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/managed.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/markdown.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/requirements-build.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/requirements-dev.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/requirements-release.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/requirements-runtime.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/runner.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/runtime.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/skills.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/supervision.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/supplemental.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/syncing.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/tooling.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process.egg-info/dependency_links.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process.egg-info/entry_points.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process.egg-info/requires.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process.egg-info/top_level.txt +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/adoption-migration.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/change.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/plan.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/project.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/release-change.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/release.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/examples/review.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/assess-design/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/build-frontend/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/build-frontend-foundation/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/change-api/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/cross-repo-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/define-change-contract/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/design-module/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/evolve-process/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/finish-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/govern-ui/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/implement-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/implement-module/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/implement-use-case/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/integrate-mcp/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/maintain-docs/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/plan-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/publish-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/review-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/run-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/run-change/references/execution.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/run-project-command/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/specify-use-case/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/process_assets/skills/verify-change/SKILL.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/adoption-migration.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/change.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/evidence-receipt.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/lifecycle.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/plan.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/process-lock.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/project.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/release-change.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/release.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/review.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/reviewer-attestation.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/supplemental-verification.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/schemas/verification.schema.json +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/setup.cfg +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/templates/AGENTS.process.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/templates/PULL_REQUEST_TEMPLATE.md +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/templates/adopt-process-windows-job.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_adoption.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_artifact_attestation.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_bootstrap.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_bundles.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_cli.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_contracts.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_distribution.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_distribution_verify.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_environment.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_evals.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_impact.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_runner.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_runtime.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_schemas.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_skills.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_source_checkout.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_supervision.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_supplemental.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_syncing.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_tooling.py +0 -0
- {engineering_process-0.2.0 → engineering_process-0.3.0}/tests/test_windows_job.py +0 -0
{engineering_process-0.2.0/engineering_process.egg-info → engineering_process-0.3.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Agent-neutral end-to-end engineering lifecycle and deterministic process CLI
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/phuongnse/engineering-process
|
|
@@ -136,6 +136,26 @@ attribute files are rejected by existing managed-tree ownership and content chec
|
|
|
136
136
|
External Git overrides that alter a checkout still fail byte-exact distribution
|
|
137
137
|
attestation. A consumer never authors or maintains process skills locally.
|
|
138
138
|
|
|
139
|
+
CI installs the pinned authority through the repository-root
|
|
140
|
+
`phuongnse/engineering-process` action from the same governed release. Consumers pin
|
|
141
|
+
the action with the release commit's full object id and retain the human-readable
|
|
142
|
+
`v<SemVer>` annotation; floating tags and copied installer implementations are not
|
|
143
|
+
supported. The action reads the consumer-owned `requirements/process.txt` and changes
|
|
144
|
+
no version or source decision: it preserves the complete hash lock, public PyPI,
|
|
145
|
+
binary-only policy, sanitized pip environment, exact-version-only propagation retry,
|
|
146
|
+
bounded output and time, and cross-platform descendant cleanup. The action source is
|
|
147
|
+
resolved only from `github.action_path`, so an untrusted consumer checkout cannot
|
|
148
|
+
replace the installer.
|
|
149
|
+
|
|
150
|
+
Project-owned CI workflows remain local because they select the project's commands
|
|
151
|
+
and evidence. Reusable installation and publication grammar belong to this
|
|
152
|
+
distribution; product, architecture, dependency, documentation, and acceptance
|
|
153
|
+
checks remain with the consumer. The managed `.process/adopt-process.py` and Windows
|
|
154
|
+
Job Object sidecar are intentional bootstrap snapshots, not consumer implementations:
|
|
155
|
+
Renovate must install and verify a target authority before that target exists in the
|
|
156
|
+
checkout, and `processctl sync --check` compares those bytes with the pinned
|
|
157
|
+
distribution.
|
|
158
|
+
|
|
139
159
|
For an existing consumer, a published version is adopted through one Renovate draft.
|
|
140
160
|
The managed runner installs the target authority from the complete hash lock outside
|
|
141
161
|
the checkout and atomically updates the process lock and managed assets. If the
|
|
@@ -386,6 +406,13 @@ processctl change verify --change-id issue-123 --profile review \
|
|
|
386
406
|
--actor worker --context worker-session --actor-kind agent
|
|
387
407
|
~~~
|
|
388
408
|
|
|
409
|
+
If later remote evidence or a source correction invalidates that verified checkpoint
|
|
410
|
+
before review, commit the correction and run `change implement` again. The CLI
|
|
411
|
+
preserves the earlier evidence, records `verification-invalidated`, increments the
|
|
412
|
+
cycle, and requires every profile again. It accepts this transition only when the
|
|
413
|
+
recorded verification is actually stale; a current verified checkpoint cannot use it
|
|
414
|
+
to bypass independent review.
|
|
415
|
+
|
|
389
416
|
After the phase becomes `verified`, a separate reviewer context registers its
|
|
390
417
|
assignment:
|
|
391
418
|
|
|
@@ -504,6 +531,11 @@ authenticates who produced it.
|
|
|
504
531
|
draft contains the new lock, managed contracts, skill snapshots, and any
|
|
505
532
|
target-version consumer-owned project migration; after CI and fresh-context
|
|
506
533
|
independent review, merge is the end of adoption.
|
|
534
|
+
- The repository-root GitHub Action is the shared CI bootstrap surface. Consumers pin
|
|
535
|
+
its full governed release commit, while the exact Python authority remains selected
|
|
536
|
+
exclusively by their hash-locked `requirements/process.txt`. The action invokes the
|
|
537
|
+
producer-owned installer from its immutable action checkout and never downloads or
|
|
538
|
+
executes helper source from the consumer branch.
|
|
507
539
|
- Versioned JSON schemas define change, plan, verification, review, lifecycle,
|
|
508
540
|
completion-related artifacts, release-change fragments, and the release
|
|
509
541
|
classification contract. The generated Release PR gate binds that contract to the
|
|
@@ -101,6 +101,26 @@ attribute files are rejected by existing managed-tree ownership and content chec
|
|
|
101
101
|
External Git overrides that alter a checkout still fail byte-exact distribution
|
|
102
102
|
attestation. A consumer never authors or maintains process skills locally.
|
|
103
103
|
|
|
104
|
+
CI installs the pinned authority through the repository-root
|
|
105
|
+
`phuongnse/engineering-process` action from the same governed release. Consumers pin
|
|
106
|
+
the action with the release commit's full object id and retain the human-readable
|
|
107
|
+
`v<SemVer>` annotation; floating tags and copied installer implementations are not
|
|
108
|
+
supported. The action reads the consumer-owned `requirements/process.txt` and changes
|
|
109
|
+
no version or source decision: it preserves the complete hash lock, public PyPI,
|
|
110
|
+
binary-only policy, sanitized pip environment, exact-version-only propagation retry,
|
|
111
|
+
bounded output and time, and cross-platform descendant cleanup. The action source is
|
|
112
|
+
resolved only from `github.action_path`, so an untrusted consumer checkout cannot
|
|
113
|
+
replace the installer.
|
|
114
|
+
|
|
115
|
+
Project-owned CI workflows remain local because they select the project's commands
|
|
116
|
+
and evidence. Reusable installation and publication grammar belong to this
|
|
117
|
+
distribution; product, architecture, dependency, documentation, and acceptance
|
|
118
|
+
checks remain with the consumer. The managed `.process/adopt-process.py` and Windows
|
|
119
|
+
Job Object sidecar are intentional bootstrap snapshots, not consumer implementations:
|
|
120
|
+
Renovate must install and verify a target authority before that target exists in the
|
|
121
|
+
checkout, and `processctl sync --check` compares those bytes with the pinned
|
|
122
|
+
distribution.
|
|
123
|
+
|
|
104
124
|
For an existing consumer, a published version is adopted through one Renovate draft.
|
|
105
125
|
The managed runner installs the target authority from the complete hash lock outside
|
|
106
126
|
the checkout and atomically updates the process lock and managed assets. If the
|
|
@@ -351,6 +371,13 @@ processctl change verify --change-id issue-123 --profile review \
|
|
|
351
371
|
--actor worker --context worker-session --actor-kind agent
|
|
352
372
|
~~~
|
|
353
373
|
|
|
374
|
+
If later remote evidence or a source correction invalidates that verified checkpoint
|
|
375
|
+
before review, commit the correction and run `change implement` again. The CLI
|
|
376
|
+
preserves the earlier evidence, records `verification-invalidated`, increments the
|
|
377
|
+
cycle, and requires every profile again. It accepts this transition only when the
|
|
378
|
+
recorded verification is actually stale; a current verified checkpoint cannot use it
|
|
379
|
+
to bypass independent review.
|
|
380
|
+
|
|
354
381
|
After the phase becomes `verified`, a separate reviewer context registers its
|
|
355
382
|
assignment:
|
|
356
383
|
|
|
@@ -469,6 +496,11 @@ authenticates who produced it.
|
|
|
469
496
|
draft contains the new lock, managed contracts, skill snapshots, and any
|
|
470
497
|
target-version consumer-owned project migration; after CI and fresh-context
|
|
471
498
|
independent review, merge is the end of adoption.
|
|
499
|
+
- The repository-root GitHub Action is the shared CI bootstrap surface. Consumers pin
|
|
500
|
+
its full governed release commit, while the exact Python authority remains selected
|
|
501
|
+
exclusively by their hash-locked `requirements/process.txt`. The action invokes the
|
|
502
|
+
producer-owned installer from its immutable action checkout and never downloads or
|
|
503
|
+
executes helper source from the consumer branch.
|
|
472
504
|
- Versioned JSON schemas define change, plan, verification, review, lifecycle,
|
|
473
505
|
completion-related artifacts, release-change fragments, and the release
|
|
474
506
|
classification contract. The generated Release PR gate binds that contract to the
|
|
@@ -71,6 +71,13 @@ verification manifest and project-adoption migration each follow that rule. Addi
|
|
|
71
71
|
optional `timeoutSeconds` evidence to verification schema 2 preserves existing
|
|
72
72
|
schema-2 documents and therefore does not advance that artifact's schema version.
|
|
73
73
|
|
|
74
|
+
Every generator must be qualified against the exact released reader that consumes its
|
|
75
|
+
output. For a lifecycle artifact, validation at creation is insufficient: CI runs the
|
|
76
|
+
generated change, plan, review, completion, and exported evidence through public N-1
|
|
77
|
+
up to the final consuming transition. Review schema and quality mappings derive from
|
|
78
|
+
the registered change contract instead of being maintained as an independent version
|
|
79
|
+
constant.
|
|
80
|
+
|
|
74
81
|
Package `schemaImpact` is `unchanged`, `additive`, or `breaking` for the combined
|
|
75
82
|
release. Additive schema capability requires at least a capability release; a
|
|
76
83
|
breaking schema requires an incompatible release.
|
|
@@ -91,13 +98,29 @@ Release, self-adoption, and consumer adoption are separate changes:
|
|
|
91
98
|
checkpoint. Merge completes adoption; there is no post-merge synchronization.
|
|
92
99
|
5. N+1 governs only changes that begin after the adoption checkpoint.
|
|
93
100
|
|
|
101
|
+
The repository-root GitHub Action and Python package are two surfaces of the same
|
|
102
|
+
governed release checkpoint. A consumer invocation pins the action by the release
|
|
103
|
+
commit's full object id and keeps the `v<SemVer>` annotation; Renovate groups that
|
|
104
|
+
GitHub Action identity with the direct Python authority update. The action does not
|
|
105
|
+
select the package version. It installs only the consumer's complete hash lock, so
|
|
106
|
+
the process lock, Python artifacts, managed assets, action source, and reviewed tag
|
|
107
|
+
remain independently checkable parts of one release identity.
|
|
108
|
+
|
|
109
|
+
Consumer repositories do not own copies of reusable installation or publication
|
|
110
|
+
algorithms. They retain declarative process/project locks, exact project commands,
|
|
111
|
+
and managed bootstrap snapshots required before a target version is installed. A
|
|
112
|
+
clean-cutover PR may delete an obsolete local helper only after it pins an immutable
|
|
113
|
+
public action checkpoint; no compatibility shim or dual execution path is retained.
|
|
114
|
+
|
|
94
115
|
Renovate PRs are generated adoption candidates, not trusted adoption evidence.
|
|
95
116
|
Automerge is forbidden for process-authority updates. A PR that changes only a
|
|
96
117
|
requirement pin, omits generated hashes or managed assets, or requires a post-merge
|
|
97
118
|
step must fail closed.
|
|
98
119
|
|
|
99
120
|
`requirements/process.in` owns the direct public pin and `requirements/process.txt`
|
|
100
|
-
is its pip-compile hash lock.
|
|
121
|
+
is its pip-compile hash lock. The lock generator is pinned, and the committed lock must
|
|
122
|
+
cover compatible binary artifacts for every supported Python/OS runner rather than
|
|
123
|
+
only the platform that compiled it. Renovate's exact allowlisted post-upgrade command runs
|
|
101
124
|
`.process/adopt-process.py`; that managed runner creates a bounded temporary
|
|
102
125
|
environment outside the checkout, rejects symlink, junction, or reparse input in
|
|
103
126
|
every supplied lock-path component, and makes one stable private snapshot. Component
|
|
@@ -624,10 +624,45 @@ def _begin_implementation_unlocked(
|
|
|
624
624
|
kind: str,
|
|
625
625
|
) -> dict[str, Any]:
|
|
626
626
|
state = load_state(project_root, change_id)
|
|
627
|
-
_require_phase(state, "planned", "implementing", "changes-requested")
|
|
628
627
|
_contract(project_root, state)
|
|
629
628
|
_plan(project_root, state)
|
|
630
629
|
actor = _actor(actor_id, context_id, kind)
|
|
630
|
+
if state["phase"] == "verified":
|
|
631
|
+
source = source_state(project_root)
|
|
632
|
+
checkpoints = {item["checkpoint"] for item in state["verification"]}
|
|
633
|
+
fingerprints = {
|
|
634
|
+
item["workspaceFingerprint"] for item in state["verification"]
|
|
635
|
+
}
|
|
636
|
+
current = (
|
|
637
|
+
source["dirty"] is False
|
|
638
|
+
and source["checkpoint"] is not None
|
|
639
|
+
and source["fingerprint"] is not None
|
|
640
|
+
and checkpoints == {source["checkpoint"]}
|
|
641
|
+
and fingerprints == {source["fingerprint"]}
|
|
642
|
+
)
|
|
643
|
+
if current:
|
|
644
|
+
raise ContractError(
|
|
645
|
+
"current verified change must enter independent review before "
|
|
646
|
+
"another implementation cycle"
|
|
647
|
+
)
|
|
648
|
+
previous_cycle = state["cycle"]
|
|
649
|
+
previous_verification = list(state["verification"])
|
|
650
|
+
state["cycle"] += 1
|
|
651
|
+
state["implementationActors"] = []
|
|
652
|
+
state["verification"] = []
|
|
653
|
+
state["reviewAssignment"] = None
|
|
654
|
+
state["review"] = None
|
|
655
|
+
_event(
|
|
656
|
+
state,
|
|
657
|
+
"verification-invalidated",
|
|
658
|
+
actor,
|
|
659
|
+
cycle=state["cycle"],
|
|
660
|
+
previousCycle=previous_cycle,
|
|
661
|
+
previousVerification=previous_verification,
|
|
662
|
+
reason="source-changed-after-verification",
|
|
663
|
+
)
|
|
664
|
+
else:
|
|
665
|
+
_require_phase(state, "planned", "implementing", "changes-requested")
|
|
631
666
|
if state["phase"] == "changes-requested":
|
|
632
667
|
state["cycle"] += 1
|
|
633
668
|
state["implementationActors"] = []
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import base64
|
|
4
|
+
import binascii
|
|
5
|
+
import json
|
|
3
6
|
import re
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
|
|
@@ -243,12 +246,66 @@ def managed_pull_request_visibility_issues(body: str) -> list[str]:
|
|
|
243
246
|
|
|
244
247
|
|
|
245
248
|
PROJECT_EXTENSION_HEADING = "## Project-specific requirements"
|
|
249
|
+
RENOVATE_DEBUG_COMMENT_RE = re.compile(
|
|
250
|
+
r"^<!--renovate-debug:(?P<payload>[A-Za-z0-9+/]{1,4096}={0,2})-->$"
|
|
251
|
+
)
|
|
252
|
+
RENOVATE_DEBUG_KEYS = {
|
|
253
|
+
"createdInVer",
|
|
254
|
+
"updatedInVer",
|
|
255
|
+
"targetBranch",
|
|
256
|
+
"labels",
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _strip_renovate_debug_comment(extension: str) -> tuple[str, list[str]]:
|
|
261
|
+
lines = extension.strip().splitlines()
|
|
262
|
+
matches = [
|
|
263
|
+
(index, match)
|
|
264
|
+
for index, line in enumerate(lines)
|
|
265
|
+
if (match := RENOVATE_DEBUG_COMMENT_RE.fullmatch(line)) is not None
|
|
266
|
+
]
|
|
267
|
+
if not matches:
|
|
268
|
+
return extension, []
|
|
269
|
+
if len(matches) != 1 or matches[0][0] != len(lines) - 1:
|
|
270
|
+
return extension, ["Renovate metadata comment must appear exactly once at the end"]
|
|
271
|
+
_, match = matches[0]
|
|
272
|
+
try:
|
|
273
|
+
decoded = base64.b64decode(match.group("payload"), validate=True)
|
|
274
|
+
if len(decoded) > 3072:
|
|
275
|
+
raise ValueError("payload exceeds 3072 decoded bytes")
|
|
276
|
+
metadata = json.loads(decoded.decode("utf-8"))
|
|
277
|
+
except (binascii.Error, UnicodeDecodeError, json.JSONDecodeError, ValueError):
|
|
278
|
+
return extension, ["Renovate metadata comment must contain bounded base64 JSON"]
|
|
279
|
+
if not isinstance(metadata, dict) or set(metadata) != RENOVATE_DEBUG_KEYS:
|
|
280
|
+
return extension, ["Renovate metadata comment has an unexpected JSON contract"]
|
|
281
|
+
if any(
|
|
282
|
+
not isinstance(metadata[key], str)
|
|
283
|
+
or not metadata[key]
|
|
284
|
+
or len(metadata[key]) > 64
|
|
285
|
+
for key in ("createdInVer", "updatedInVer")
|
|
286
|
+
):
|
|
287
|
+
return extension, ["Renovate metadata versions must be bounded strings"]
|
|
288
|
+
if metadata["targetBranch"] != "main":
|
|
289
|
+
return extension, ["Renovate metadata must target protected main"]
|
|
290
|
+
labels = metadata["labels"]
|
|
291
|
+
if (
|
|
292
|
+
not isinstance(labels, list)
|
|
293
|
+
or len(labels) > 50
|
|
294
|
+
or any(not isinstance(label, str) or len(label) > 100 for label in labels)
|
|
295
|
+
):
|
|
296
|
+
return extension, ["Renovate metadata labels must be a bounded string list"]
|
|
297
|
+
return "\n".join(lines[:-1]).strip(), []
|
|
246
298
|
|
|
247
299
|
|
|
248
300
|
def validate_project_extensions(body: str, *, allow_pending: bool) -> list[str]:
|
|
249
301
|
body = _normalized_markdown(body)
|
|
250
302
|
_, end = _managed_span(body)
|
|
251
303
|
extension = body[end:]
|
|
304
|
+
if not extension.strip():
|
|
305
|
+
return []
|
|
306
|
+
extension, metadata_issues = _strip_renovate_debug_comment(extension)
|
|
307
|
+
if metadata_issues:
|
|
308
|
+
return metadata_issues
|
|
252
309
|
if not extension.strip():
|
|
253
310
|
return []
|
|
254
311
|
if contains_raw_html(extension):
|
|
@@ -118,10 +118,6 @@ def validate_reviewed_checkpoint(
|
|
|
118
118
|
],
|
|
119
119
|
)
|
|
120
120
|
if reviewed_checkpoint != release_checkpoint:
|
|
121
|
-
_git(
|
|
122
|
-
project_root,
|
|
123
|
-
["merge-base", "--is-ancestor", reviewed_checkpoint, release_checkpoint],
|
|
124
|
-
)
|
|
125
121
|
reviewed_tree = _git(
|
|
126
122
|
project_root,
|
|
127
123
|
["rev-parse", "--verify", f"{reviewed_checkpoint}^{{tree}}"],
|
{engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process/release_candidate.py
RENAMED
|
@@ -232,7 +232,7 @@ def _release_lifecycle_documents(
|
|
|
232
232
|
) -> tuple[bytes, bytes]:
|
|
233
233
|
change_id = f"release-{version.replace('.', '-')}"
|
|
234
234
|
contract = {
|
|
235
|
-
"schemaVersion":
|
|
235
|
+
"schemaVersion": 3,
|
|
236
236
|
"id": change_id,
|
|
237
237
|
"summary": f"Authorize and publish {project} {version}",
|
|
238
238
|
"source": "Automatically aggregated reviewed release-change fragments",
|
|
@@ -259,6 +259,71 @@ def _release_lifecycle_documents(
|
|
|
259
259
|
},
|
|
260
260
|
],
|
|
261
261
|
"requiredProfiles": ["development", "review"],
|
|
262
|
+
"quality": {
|
|
263
|
+
"standard": "production-v1",
|
|
264
|
+
"assessments": [
|
|
265
|
+
{
|
|
266
|
+
"dimension": "compatibility",
|
|
267
|
+
"status": "applicable",
|
|
268
|
+
"rationale": "The release must preserve its declared compatibility and exact identity surfaces.",
|
|
269
|
+
"criteria": ["ac-identity"],
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"dimension": "correctness",
|
|
273
|
+
"status": "applicable",
|
|
274
|
+
"rationale": "The derived identity and published bytes must match the reviewed release contract.",
|
|
275
|
+
"criteria": ["ac-identity", "ac-publication"],
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"dimension": "maintainability",
|
|
279
|
+
"status": "applicable",
|
|
280
|
+
"rationale": "Independent review verifies the generated contract and deterministic release owner.",
|
|
281
|
+
"criteria": ["ac-review"],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"dimension": "observability",
|
|
285
|
+
"status": "applicable",
|
|
286
|
+
"rationale": "Release evidence and attestations expose the exact publication outcome.",
|
|
287
|
+
"criteria": ["ac-publication"],
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"dimension": "operability",
|
|
291
|
+
"status": "applicable",
|
|
292
|
+
"rationale": "The reviewed release must publish and recover through the declared automated workflow.",
|
|
293
|
+
"criteria": ["ac-publication"],
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"dimension": "performance",
|
|
297
|
+
"status": "not-applicable",
|
|
298
|
+
"rationale": "Release authorization adds no product runtime or scaling behavior.",
|
|
299
|
+
"criteria": [],
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"dimension": "privacy",
|
|
303
|
+
"status": "not-applicable",
|
|
304
|
+
"rationale": "Release metadata contains no personal or sensitive data processing.",
|
|
305
|
+
"criteria": [],
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"dimension": "reliability",
|
|
309
|
+
"status": "applicable",
|
|
310
|
+
"rationale": "Immutable assets and exact external-state checks must fail closed and recover deterministically.",
|
|
311
|
+
"criteria": ["ac-publication"],
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"dimension": "security",
|
|
315
|
+
"status": "applicable",
|
|
316
|
+
"rationale": "Independent review and merge remain the only publication authorization boundary.",
|
|
317
|
+
"criteria": ["ac-review"],
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"dimension": "supply-chain",
|
|
321
|
+
"status": "applicable",
|
|
322
|
+
"rationale": "The release binds source, version, artifacts, attestations, and registry bytes.",
|
|
323
|
+
"criteria": ["ac-identity", "ac-publication"],
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
262
327
|
"signOff": {
|
|
263
328
|
"required": False,
|
|
264
329
|
"status": "not-required",
|
|
@@ -268,7 +333,7 @@ def _release_lifecycle_documents(
|
|
|
268
333
|
contract_bytes = _canonical_json_bytes(contract)
|
|
269
334
|
contract_digest = f"sha256:{hashlib.sha256(contract_bytes).hexdigest()}"
|
|
270
335
|
plan = {
|
|
271
|
-
"schemaVersion":
|
|
336
|
+
"schemaVersion": 2,
|
|
272
337
|
"changeId": change_id,
|
|
273
338
|
"contractDigest": contract_digest,
|
|
274
339
|
"approach": "Validate the generated release contract and every declared surface, build from the exact reviewed tree, and permit publication only after independent approval and merge.",
|
{engineering_process-0.2.0 → engineering_process-0.3.0/engineering_process.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: engineering-process
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Agent-neutral end-to-end engineering lifecycle and deterministic process CLI
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/phuongnse/engineering-process
|
|
@@ -136,6 +136,26 @@ attribute files are rejected by existing managed-tree ownership and content chec
|
|
|
136
136
|
External Git overrides that alter a checkout still fail byte-exact distribution
|
|
137
137
|
attestation. A consumer never authors or maintains process skills locally.
|
|
138
138
|
|
|
139
|
+
CI installs the pinned authority through the repository-root
|
|
140
|
+
`phuongnse/engineering-process` action from the same governed release. Consumers pin
|
|
141
|
+
the action with the release commit's full object id and retain the human-readable
|
|
142
|
+
`v<SemVer>` annotation; floating tags and copied installer implementations are not
|
|
143
|
+
supported. The action reads the consumer-owned `requirements/process.txt` and changes
|
|
144
|
+
no version or source decision: it preserves the complete hash lock, public PyPI,
|
|
145
|
+
binary-only policy, sanitized pip environment, exact-version-only propagation retry,
|
|
146
|
+
bounded output and time, and cross-platform descendant cleanup. The action source is
|
|
147
|
+
resolved only from `github.action_path`, so an untrusted consumer checkout cannot
|
|
148
|
+
replace the installer.
|
|
149
|
+
|
|
150
|
+
Project-owned CI workflows remain local because they select the project's commands
|
|
151
|
+
and evidence. Reusable installation and publication grammar belong to this
|
|
152
|
+
distribution; product, architecture, dependency, documentation, and acceptance
|
|
153
|
+
checks remain with the consumer. The managed `.process/adopt-process.py` and Windows
|
|
154
|
+
Job Object sidecar are intentional bootstrap snapshots, not consumer implementations:
|
|
155
|
+
Renovate must install and verify a target authority before that target exists in the
|
|
156
|
+
checkout, and `processctl sync --check` compares those bytes with the pinned
|
|
157
|
+
distribution.
|
|
158
|
+
|
|
139
159
|
For an existing consumer, a published version is adopted through one Renovate draft.
|
|
140
160
|
The managed runner installs the target authority from the complete hash lock outside
|
|
141
161
|
the checkout and atomically updates the process lock and managed assets. If the
|
|
@@ -386,6 +406,13 @@ processctl change verify --change-id issue-123 --profile review \
|
|
|
386
406
|
--actor worker --context worker-session --actor-kind agent
|
|
387
407
|
~~~
|
|
388
408
|
|
|
409
|
+
If later remote evidence or a source correction invalidates that verified checkpoint
|
|
410
|
+
before review, commit the correction and run `change implement` again. The CLI
|
|
411
|
+
preserves the earlier evidence, records `verification-invalidated`, increments the
|
|
412
|
+
cycle, and requires every profile again. It accepts this transition only when the
|
|
413
|
+
recorded verification is actually stale; a current verified checkpoint cannot use it
|
|
414
|
+
to bypass independent review.
|
|
415
|
+
|
|
389
416
|
After the phase becomes `verified`, a separate reviewer context registers its
|
|
390
417
|
assignment:
|
|
391
418
|
|
|
@@ -504,6 +531,11 @@ authenticates who produced it.
|
|
|
504
531
|
draft contains the new lock, managed contracts, skill snapshots, and any
|
|
505
532
|
target-version consumer-owned project migration; after CI and fresh-context
|
|
506
533
|
independent review, merge is the end of adoption.
|
|
534
|
+
- The repository-root GitHub Action is the shared CI bootstrap surface. Consumers pin
|
|
535
|
+
its full governed release commit, while the exact Python authority remains selected
|
|
536
|
+
exclusively by their hash-locked `requirements/process.txt`. The action invokes the
|
|
537
|
+
producer-owned installer from its immutable action checkout and never downloads or
|
|
538
|
+
executes helper source from the consumer branch.
|
|
507
539
|
- Versioned JSON schemas define change, plan, verification, review, lifecycle,
|
|
508
540
|
completion-related artifacts, release-change fragments, and the release
|
|
509
541
|
classification contract. The generated Release PR gate binds that contract to the
|
{engineering_process-0.2.0 → engineering_process-0.3.0}/engineering_process.egg-info/SOURCES.txt
RENAMED
|
@@ -109,9 +109,14 @@ tests/test_environment.py
|
|
|
109
109
|
tests/test_evals.py
|
|
110
110
|
tests/test_impact.py
|
|
111
111
|
tests/test_lifecycle.py
|
|
112
|
+
tests/test_process_install.py
|
|
112
113
|
tests/test_publication.py
|
|
114
|
+
tests/test_publish_event.py
|
|
115
|
+
tests/test_pypi_publication.py
|
|
113
116
|
tests/test_release.py
|
|
114
117
|
tests/test_release_candidate.py
|
|
118
|
+
tests/test_release_preparation.py
|
|
119
|
+
tests/test_release_qualification.py
|
|
115
120
|
tests/test_runner.py
|
|
116
121
|
tests/test_runtime.py
|
|
117
122
|
tests/test_schemas.py
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 3,
|
|
3
|
+
"previousVersion": "0.2.1",
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"classification": "minor",
|
|
6
|
+
"compatibility": "backward-compatible",
|
|
7
|
+
"schemaImpact": "unchanged",
|
|
8
|
+
"migration": null,
|
|
9
|
+
"identity": {
|
|
10
|
+
"package": "engineering-process",
|
|
11
|
+
"distribution": "engineering_process",
|
|
12
|
+
"tag": "v0.3.0",
|
|
13
|
+
"releaseName": "v0.3.0",
|
|
14
|
+
"runtimeVersion": {
|
|
15
|
+
"path": "engineering_process/__init__.py",
|
|
16
|
+
"variable": "VERSION"
|
|
17
|
+
},
|
|
18
|
+
"artifacts": [
|
|
19
|
+
"engineering_process-0.3.0-py3-none-any.whl",
|
|
20
|
+
"engineering_process-0.3.0.tar.gz"
|
|
21
|
+
],
|
|
22
|
+
"receiptAsset": "engineering-process-v0.3.0-evidence.json",
|
|
23
|
+
"authorizationAsset": null
|
|
24
|
+
},
|
|
25
|
+
"provenance": {
|
|
26
|
+
"mode": "governed",
|
|
27
|
+
"statement": "The public N-1 lifecycle receipt and reviewed Release PR authorize this release.",
|
|
28
|
+
"lifecycleReceipt": {
|
|
29
|
+
"asset": "engineering-process-v0.3.0-evidence.json",
|
|
30
|
+
"project": "engineering-process",
|
|
31
|
+
"changeId": "release-0-3-0",
|
|
32
|
+
"cycle": 1
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"changes": [
|
|
36
|
+
{
|
|
37
|
+
"id": "consumer-process-boundary-cleanup",
|
|
38
|
+
"type": "capability",
|
|
39
|
+
"surfaces": [
|
|
40
|
+
"ci-bootstrap",
|
|
41
|
+
"workflow"
|
|
42
|
+
],
|
|
43
|
+
"rationale": "Provide one immutable producer-owned CI installation action so consumers can remove copied process algorithms without weakening exact hash locks or bounded registry propagation recovery."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "reopen-stale-verification",
|
|
47
|
+
"type": "fix",
|
|
48
|
+
"surfaces": [
|
|
49
|
+
"lifecycle"
|
|
50
|
+
],
|
|
51
|
+
"rationale": "Allow a verified change invalidated by later remote evidence or a source correction to begin a new implementation cycle while preserving prior evidence and still forbidding current verified work from bypassing independent review."
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -500,6 +500,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
500
500
|
type=Path,
|
|
501
501
|
default=Path("requirements/process.txt"),
|
|
502
502
|
)
|
|
503
|
+
parser.add_argument(
|
|
504
|
+
"--check",
|
|
505
|
+
action="store_true",
|
|
506
|
+
help="Validate through the installed hash-locked authority without applying",
|
|
507
|
+
)
|
|
503
508
|
args = parser.parse_args(argv)
|
|
504
509
|
project_root = Path(os.path.abspath(os.fspath(args.project_root)))
|
|
505
510
|
requirements_source, requirements_root = _requirements_binding(
|
|
@@ -566,7 +571,25 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
566
571
|
target_version = _installed_process_version(
|
|
567
572
|
python, cwd=environment_root
|
|
568
573
|
)
|
|
569
|
-
if
|
|
574
|
+
if args.check:
|
|
575
|
+
output = _run(
|
|
576
|
+
[
|
|
577
|
+
str(python),
|
|
578
|
+
"-I",
|
|
579
|
+
"-c",
|
|
580
|
+
"from engineering_process.cli import main; "
|
|
581
|
+
"raise SystemExit(main())",
|
|
582
|
+
"adoption",
|
|
583
|
+
"check",
|
|
584
|
+
"--project-root",
|
|
585
|
+
str(project_root),
|
|
586
|
+
"--requirements-lock",
|
|
587
|
+
str(requirements_source),
|
|
588
|
+
"--json",
|
|
589
|
+
],
|
|
590
|
+
cwd=environment_root,
|
|
591
|
+
)
|
|
592
|
+
elif target_version == current_version:
|
|
570
593
|
output = json.dumps(
|
|
571
594
|
{
|
|
572
595
|
"requirementsDigest": requirements_digest,
|
|
@@ -615,9 +638,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
615
638
|
if (
|
|
616
639
|
not isinstance(result, dict)
|
|
617
640
|
or result.get("requirementsDigest") != requirements_digest
|
|
641
|
+
or result.get("version") != target_version
|
|
618
642
|
):
|
|
619
643
|
raise RuntimeError(
|
|
620
|
-
"adoption authority did not attest the
|
|
644
|
+
"adoption authority did not attest the installed version and "
|
|
645
|
+
"private requirements snapshot"
|
|
621
646
|
)
|
|
622
647
|
sys.stdout.write(output)
|
|
623
648
|
return 0
|
|
@@ -262,7 +262,12 @@ class AdoptionRunnerTests(unittest.TestCase):
|
|
|
262
262
|
self.assertEqual(observed_snapshot, [snapshot])
|
|
263
263
|
self.assertTrue(source.samefile(checkout))
|
|
264
264
|
self.assertEqual(digest, expected)
|
|
265
|
-
return json.dumps(
|
|
265
|
+
return json.dumps(
|
|
266
|
+
{
|
|
267
|
+
"requirementsDigest": digest,
|
|
268
|
+
"version": "0.1.1",
|
|
269
|
+
}
|
|
270
|
+
)
|
|
266
271
|
return ""
|
|
267
272
|
|
|
268
273
|
with (
|
|
@@ -281,6 +286,61 @@ class AdoptionRunnerTests(unittest.TestCase):
|
|
|
281
286
|
),
|
|
282
287
|
)
|
|
283
288
|
|
|
289
|
+
def test_check_uses_installed_authority_even_when_target_is_adopted(self):
|
|
290
|
+
runner = load_runner()
|
|
291
|
+
content = b"--only-binary :all:\nengineering-process==0.2.0\n"
|
|
292
|
+
digest = "sha256:" + hashlib.sha256(content).hexdigest()
|
|
293
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
294
|
+
root = Path(directory)
|
|
295
|
+
self.write_process_lock(root, "0.2.0")
|
|
296
|
+
source = root / "requirements" / "process.txt"
|
|
297
|
+
source.parent.mkdir()
|
|
298
|
+
source.write_bytes(content)
|
|
299
|
+
adoption_commands: list[list[str]] = []
|
|
300
|
+
|
|
301
|
+
def fake_run(argv, *, cwd):
|
|
302
|
+
del cwd
|
|
303
|
+
if any("engineering_process.VERSION" in part for part in argv):
|
|
304
|
+
return "0.2.0\n"
|
|
305
|
+
if "adoption" in argv:
|
|
306
|
+
adoption_commands.append(argv)
|
|
307
|
+
self.assertIn("check", argv)
|
|
308
|
+
self.assertNotIn("apply", argv)
|
|
309
|
+
supplied = Path(argv[argv.index("--requirements-lock") + 1])
|
|
310
|
+
self.assertTrue(source.samefile(supplied))
|
|
311
|
+
return json.dumps(
|
|
312
|
+
{
|
|
313
|
+
"requirementsDigest": digest,
|
|
314
|
+
"status": "passed",
|
|
315
|
+
"version": "0.2.0",
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
return ""
|
|
319
|
+
|
|
320
|
+
with (
|
|
321
|
+
mock.patch.object(runner, "_run", side_effect=fake_run),
|
|
322
|
+
mock.patch.object(runner.sys.stdout, "write"),
|
|
323
|
+
):
|
|
324
|
+
self.assertEqual(
|
|
325
|
+
0,
|
|
326
|
+
runner.main(
|
|
327
|
+
[
|
|
328
|
+
"--project-root",
|
|
329
|
+
str(root),
|
|
330
|
+
"--requirements-lock",
|
|
331
|
+
"requirements/process.txt",
|
|
332
|
+
"--check",
|
|
333
|
+
]
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
self.assertEqual(1, len(adoption_commands))
|
|
338
|
+
self.assertIn("-I", adoption_commands[0])
|
|
339
|
+
self.assertIn(
|
|
340
|
+
"from engineering_process.cli import main; raise SystemExit(main())",
|
|
341
|
+
adoption_commands[0],
|
|
342
|
+
)
|
|
343
|
+
|
|
284
344
|
def test_main_noops_when_the_hash_locked_target_is_already_adopted(self):
|
|
285
345
|
runner = load_runner()
|
|
286
346
|
content = b"--only-binary :all:\nengineering-process==0.1.1\n"
|