Skip to content

Commit a239571

Browse files
Merge issue 75 provenance and deployment fixes
2 parents 1563622 + 9ed769d commit a239571

8 files changed

Lines changed: 71 additions & 12 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,9 @@ jobs:
13031303
PROJECT_TAG: ${{ vars.DEVELOPMENT_RESOURCE_PROJECT_TAG }}
13041304
ENVIRONMENT_TAG: ${{ vars.DEVELOPMENT_RESOURCE_ENVIRONMENT_TAG }}
13051305
FAILURE_INJECTION: ${{ github.event_name == 'push' && 'none' || inputs.failure_injection }}
1306+
DTC_TEST_SAFETY_COMMAND: remote_readonly
1307+
DTC_TEST_TARGET_CLASS: isolated_development
1308+
DTC_TEST_REMOTE_NAMESPACE: deploy-${{ github.run_id }}-${{ github.run_attempt }}
13061309
run: |
13071310
set -euo pipefail
13081311
mkdir -p .tmp/deployment .tmp/deployed-smoke

_docs/adoption/course-platform/copied-files.tsv

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,9 +326,9 @@ courses/management/commands/upsert_datamailer_templates.py courses/management/co
326326
courses/migrations/0001_initial.py courses/migrations/0001_initial.py 19246 e904d5cd75aed95442f7c0dc6298fc7f3f9c2540305b3709ab4efb2a7d1e434b
327327
courses/migrations/0002_alter_enrollment_student.py courses/migrations/0002_alter_enrollment_student.py 603 c8c65468dd1ae5bfbdca343e1411f6df839b57d70ef1cafd4039f93d686df672
328328
courses/migrations/0003_replace_commas_with_linebreaks_in_possible_answers.py courses/migrations/0003_replace_commas_with_linebreaks_in_possible_answers.py 598 99cb2719ea943cb585c01c6fc3c6e6df0ffc84677593520cff67636a7969319b
329-
courses/migrations/0004_update_correct_answer_indexes.py courses/migrations/0004_update_correct_answer_indexes.py 2131 da593bb7fadcc5bad88e146d10f19752e5b7e396bc42ac210f467f6e8c6beead
330-
courses/migrations/0005_update_answers_with_indexes.py courses/migrations/0005_update_answers_with_indexes.py 2262 9e83c943a7cdac243723a99c397f884bd66a4e51577ab98f2d58fc37bd1e6855
331-
courses/migrations/0006_course_first_homework_scored.py courses/migrations/0006_course_first_homework_scored.py 844 4315ee47bf9d7bbea2b4bc0699a0c877249ef4d45397ce4614b489b570d14bb1
329+
courses/migrations/0004_update_correct_answer_indexes.py courses/migrations/0004_update_correct_answer_indexes.py 2155 be6a45f38f50b90de862a0f7182bb427d461b5a1f575a28f51930eae68d65212
330+
courses/migrations/0005_update_answers_with_indexes.py courses/migrations/0005_update_answers_with_indexes.py 2290 da6ede7d56831f0b73f1b8bdf2fa62132162d0c0a0f1cf5a6feb3edda74c123c
331+
courses/migrations/0006_course_first_homework_scored.py courses/migrations/0006_course_first_homework_scored.py 830 22fe9602784e42bc7f9be518936cc2580191c9651ca3cf565caabe4308f74ac1
332332
courses/migrations/0007_enrollment_position_on_leaderboard.py courses/migrations/0007_enrollment_position_on_leaderboard.py 438 0f59d2a2dbd333e1e4c97b634a4f3598603afee27926c09c42c2cc3aa550f98d
333333
courses/migrations/0008_remove_answer_student.py courses/migrations/0008_remove_answer_student.py 346 9dbb6051290e15fb37ed9de4a814dd031749796e8c093233edefed7304759819
334334
courses/migrations/0009_rename_comments_peerreview_problems_comments_and_more.py courses/migrations/0009_rename_comments_peerreview_problems_comments_and_more.py 2350 2e549eeb7945642d751d6e174411c779f45972af091bc31c1b344a606d28b019
@@ -519,11 +519,11 @@ courses/tests/test_datamailer_recipient_list_imports.py courses/tests/test_datam
519519
courses/tests/test_datamailer_recipient_lists.py courses/tests/test_datamailer_recipient_lists.py 7625 18d143b14f852c3a38408b8a49f8cb3c781ab72c46cb719c4dd2745461680715
520520
courses/tests/test_datamailer_registration.py courses/tests/test_datamailer_registration.py 9648 d77d893ad537eec3f012923146510c7153b504d43df4f3fe7412fa8f4d3ecf94
521521
courses/tests/test_datamailer_send_counts.py courses/tests/test_datamailer_send_counts.py 2283 f1bcdb7bcc3349e1b838c11c79bd0266c04617a1448f1168096df74b4325f45a
522-
courses/tests/test_datamailer_signals.py courses/tests/test_datamailer_signals.py 6009 2480b0f5a4afae5c98e49fe901a2752b4f2de2297632360178b1ca87cc9f15a3
522+
courses/tests/test_datamailer_signals.py courses/tests/test_datamailer_signals.py 5966 b96001a1964072eaaa94a2f7774bce93d18652bbdbb6929712a05739aff5d55c
523523
courses/tests/test_datamailer_status.py courses/tests/test_datamailer_status.py 3178 5d1a7e7ea857656da85f384007cffe46f3d4b287be966f4f617dd8fce84134ef
524524
courses/tests/test_datamailer_sync_status.py courses/tests/test_datamailer_sync_status.py 2920 f7752023778c8832991d7490990ed26fe8ca1a061b7e3747e14028edcbd3b8eb
525525
courses/tests/test_datamailer_templates.py courses/tests/test_datamailer_templates.py 2976 c047a8744641105b6c23998912e2ce2c314f5fdab20709f0ff16717e56102b44
526-
courses/tests/test_datamailer_transactional.py courses/tests/test_datamailer_transactional.py 4666 e2d6756924af770aad0dffff0a2eb1727475602cb8e13e6e754b3b13e39664e7
526+
courses/tests/test_datamailer_transactional.py courses/tests/test_datamailer_transactional.py 4649 885c515badaaf349fa9dfcb3ecbbe283d3a384e37ce765f2b6f847b71a46a9b5
527527
courses/tests/test_deadline_reminder_dry_run.py courses/tests/test_deadline_reminder_dry_run.py 1324 112188d0805fc861f1e259fe51406001cdaf8ad2635329155467866a43abb4c8
528528
courses/tests/test_deadline_reminder_failures.py courses/tests/test_deadline_reminder_failures.py 3916 b5c9340bdf36234d9fe0504a018d35ab534c6199f0eef2b61497fe401d74ecd5
529529
courses/tests/test_deadline_reminder_homework.py courses/tests/test_deadline_reminder_homework.py 1479 97de31035215003b43adfb79b5a85a2b246ac6f40ba458df522a6447d544046a

_docs/adoption/course-platform/integration-patched-files.tsv

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ course_platform_templates/socialaccount/authentication_error.html 1240 4a70aaf5e
6464
course_platform_templates/socialaccount/connections.html 3818 a1e0756bfe763150fddefc5018949eb90f7d90e7f5bd9b9eab890e6ae00c62f5 Use the unified DataTalks.Club name in the copied account-connections title without changing connection behavior.
6565
course_platform_templates/socialaccount/login_cancelled.html 1210 607d2d1d5c5c43aa69632030e144817416732482e04ecb0ac54fdd252de8fd71 Use the unified DataTalks.Club name in the copied cancelled-login title without changing recovery behavior.
6666
course_platform_templates/socialaccount/signup.html 1098 45af69b3a47d0100c0f911b76ec661bd8301d924f4b70251f5bd509ac80fb1d0 Use the unified DataTalks.Club name in the copied welcome title without changing signup completion behavior.
67+
courses/migrations/0004_update_correct_answer_indexes.py 2131 da593bb7fadcc5bad88e146d10f19752e5b7e396bc42ac210f467f6e8c6beead Resolve Question through the historical migration registry and use stored legacy question values so migration replay does not import current runtime models.
68+
courses/migrations/0005_update_answers_with_indexes.py 2262 9e83c943a7cdac243723a99c397f884bd66a4e51577ab98f2d58fc37bd1e6855 Resolve Answer through the historical migration registry and use stored legacy question values so migration replay does not import current runtime models.
69+
courses/migrations/0006_course_first_homework_scored.py 844 4315ee47bf9d7bbea2b4bc0699a0c877249ef4d45397ce4614b489b570d14bb1 Resolve Course through the historical migration registry so migration replay does not import the current runtime model.
6770
courses/templates/courses/course.html 9765 ea968d0615a0709b9730d100c07eb6d8ad209e2f454b669882739cc3c491e1ac Point explicit course-role controls to the canonical Studio Courses workspace and use its user-facing name.
6871
courses/templates/courses/leaderboard_score_breakdown.html 11523 dca183fa91e69aca601322802237f9d432c8f319f4058ae0419f68244c40abae Show copied enrollment repair controls only to explicit course roles and point them to Studio Courses.
6972
courses/templates/homework/homework.html 25994 98508d2e6f329423cd2984d4cb856717b6daf7cf4fdae3c95baace2170d0f010 Show copied homework controls only to explicit course roles and point them to Studio Courses.
@@ -72,6 +75,8 @@ courses/templates/projects/project.html 15802 d7853e0f92c64fbd55a161b6a30ef41e34
7275
courses/static/courses.css 46023 16d230d8b7f0878452b88ad1a8799d4be8fed200c44aaf1e3fd945fed6ba4f3b Retain adopted form styling and constrain Studio Courses page-header children to their mobile viewport.
7376
courses/tests/homework_submissions_base.py 6453 1693f470ce19534b1af7f13ad068e53b199c54078e62a7ca97d327f02a6cb113 Use canonical Studio Courses reverse names and the explicit course_operator role in copied homework test helpers.
7477
courses/tests/leaderboard_base.py 7100 c8551e841f4d17d74633e52a8c8cdf1f4a855fd54929d603d2b0cd089f42280b Use the canonical Studio Courses enrollment route in copied leaderboard test helpers.
78+
courses/tests/test_datamailer_signals.py 6009 2480b0f5a4afae5c98e49fe901a2752b4f2de2297632360178b1ca87cc9f15a3 Explicitly enable user-create contact synchronization in the copied signal characterization while the deterministic test default remains fail-closed.
79+
courses/tests/test_datamailer_transactional.py 4666 e2d6756924af770aad0dffff0a2eb1727475602cb8e13e6e754b3b13e39664e7 Explicitly disable transactional dry-run for the copied mocked-client characterization while the deterministic test default remains delivery-safe.
7580
courses/tests/test_homework_submissions_admin_link.py 952 f98c1ad68bd980231127b689c85211625f5d6a71d1202e4762923ea18ece3251 Characterize the copied homework staff link under the Studio name and canonical route name.
7681
courses/tests/test_leaderboard_score_breakdown_admin.py 1646 7a859b4f06e60e2940bd627b78be35e544f936db9ef4bf63742f7170e7faf0ed Characterize copied leaderboard repair controls for an explicit course_operator instead of generic staff.
7782
courses/tests/test_project_submissions_view.py 10666 bb89c8e03b88d6031806b1864e0b5d46e9589beb977c6e2290c96f61255a588d Characterize the copied project Studio link, canonical route name, and explicit operator role.

_docs/adoption/course-platform/integration-patches.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Course platform integration patches
22

3-
Copied CMP files remain byte-identical to the pinned source. Integration is confined to target-owned files:
3+
`copied-files.tsv` records the byte-identical pinned CMP source. Copied destinations remain exact
4+
unless an intentional target overlay is recorded with its current checksum and rationale in
5+
`integration-patched-files.tsv`. Integration is confined to target-owned files and those explicit
6+
overlays:
47

58
- `pyproject.toml` and `uv.lock`: bounded dependencies required by copied imports and E2E tests;
69
- `Makefile` and the Ruff/mypy configuration in `pyproject.toml`: keep byte-frozen adopted Python
@@ -46,6 +49,15 @@ Copied CMP files remain byte-identical to the pinned source. Integration is conf
4649
- `course_management/datamailer_outbox_dispatch.py`: issue #98 replaces the copied row-lock claim
4750
with portable conditional ORM claim and attempt fences while retaining the same delivery,
4851
acknowledgement, and retry outcomes;
52+
- `courses/migrations/0004_update_correct_answer_indexes.py`,
53+
`courses/migrations/0005_update_answers_with_indexes.py`, and
54+
`courses/migrations/0006_course_first_homework_scored.py`: issue #75 resolves models through the
55+
historical migration app registry and uses the stored legacy question values so fresh and
56+
maintained migration replay does not import current runtime models;
57+
- `courses/tests/test_datamailer_signals.py` and
58+
`courses/tests/test_datamailer_transactional.py`: issue #75 explicitly opts the copied
59+
characterizations into their mocked synchronization and transactional-send paths while the
60+
shared deterministic test settings keep contact sync and delivery disabled by default;
4961
- `api/openapi/spec.py`: issue #59 gives the staff-facing copied API schema the unified
5062
DataTalks.Club Courses name without changing its operations or authentication behavior;
5163
- `api/openapi/course_schemas.py`, `api/views/health.py`, and

_docs/adoption/course-platform/verification.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Course-platform adoption verification
22

33
All results below were produced from source commit
4-
`98a235283904b4ef9ad29e196298540756cf1bcc`. Copied characterization and E2E files remained
5-
unchanged; the copy verifier checks their recorded bytes.
4+
`98a235283904b4ef9ad29e196298540756cf1bcc`. The copy verifier checks all 768 pinned-source
5+
checksums, then checks each intentional copied-destination overlay against its separately recorded
6+
target checksum. Copied characterization and E2E files remain unchanged except for the explicit
7+
overlays in `integration-patched-files.tsv`.
68

79
## Characterization results
810

@@ -12,7 +14,7 @@ unchanged; the copy verifier checks their recorded bytes.
1214
| Unified Django suite | 787 passed, 0 skipped, 0 failed | Includes 6 target adoption-contract tests |
1315
| Copied E2E suite | 48 collected: 45 passed, 1 skipped, 2 xfailed, 0 failed | Playwright 1.58, fresh local database, ephemeral local dependency configuration |
1416
| Target foundation Playwright | 3 passed, 0 skipped, 0 failed | Desktop/mobile homepage and anonymous staff sign-in surface |
15-
| Adoption contract | 6 passed | 768 copied checksums, 2 target shim checksums, 89 routes, 13 commands, app/migration identity, generated inventory |
17+
| Adoption contract | 6 passed | 768 pinned-source checksums, copied-destination overlay checksums, 2 target shim checksums, 89 routes, 13 commands, app/migration identity, generated inventory |
1618

1719
The copied E2E suite was first run without a Datamailer preference service. Two account-settings
1820
checks reached the correct page with HTTP 200 but timed out waiting for browser `networkidle`
@@ -40,9 +42,10 @@ leaderboard, cadmin, helper, and fallback-cleanup checks passed.
4042

4143
## Inventory and repository checks
4244

43-
- `uv run python scripts/verify_course_platform_adoption.py`: 768 copied files verified against
44-
the clean pinned checkout and explicit integration-patch state, plus both required target-owned
45-
admin API compatibility shims verified against their per-file checksums and rationales.
45+
- `uv run python scripts/verify_course_platform_adoption.py`: the 768-row source ledger verified
46+
against the clean pinned checkout; every copied destination verified against either that source
47+
checksum or its explicit integration-patch checksum; and both required target-owned admin API
48+
compatibility shims verified against their per-file checksums and rationales.
4649
- `behavior-inventory.md`: 89 routes (9 accounts, 29 compatibility API, 26 cadmin, 25 public
4750
course) and 13 management commands generated from Django's registries and smoke-resolved by the
4851
adoption-contract test.

ci/tests/test_workflows.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,26 @@ def test_aggregate_gate_is_the_release_dependency() -> None:
9999
assert "release-image-" in str(jobs["container"])
100100

101101

102+
def test_deploy_smoke_has_exact_readonly_authority_and_pinned_base_url() -> None:
103+
deploy = workflow("ci.yml")["jobs"]["deploy"]
104+
release = next(step for step in deploy["steps"] if step.get("id") == "release")
105+
106+
assert {
107+
name: release["env"][name]
108+
for name in (
109+
"DTC_TEST_SAFETY_COMMAND",
110+
"DTC_TEST_TARGET_CLASS",
111+
"DTC_TEST_REMOTE_NAMESPACE",
112+
)
113+
} == {
114+
"DTC_TEST_SAFETY_COMMAND": "remote_readonly",
115+
"DTC_TEST_TARGET_CLASS": "isolated_development",
116+
"DTC_TEST_REMOTE_NAMESPACE": "deploy-${{ github.run_id }}-${{ github.run_attempt }}",
117+
}
118+
assert "DTC_TEST_BASE_URL" not in release["env"]
119+
assert "--base-url https://web.dtcdev.click" in release["run"]
120+
121+
102122
def test_manual_release_is_full_and_probe_contract_stays_separate() -> None:
103123
jobs = workflow("ci.yml")["jobs"]
104124
assert "manual_dispatch" in (ROOT / "ci" / "classifier.py").read_text(encoding="utf-8")

core/tests/test_course_platform_adoption.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ def test_all_recorded_copies_exist_with_recorded_integration_state(self):
158158
"course_platform_templates/socialaccount/connections.html",
159159
"course_platform_templates/socialaccount/login_cancelled.html",
160160
"course_platform_templates/socialaccount/signup.html",
161+
"courses/migrations/0004_update_correct_answer_indexes.py",
162+
"courses/migrations/0005_update_answers_with_indexes.py",
163+
"courses/migrations/0006_course_first_homework_scored.py",
161164
"courses/templates/courses/course.html",
162165
"courses/templates/courses/leaderboard_score_breakdown.html",
163166
"courses/templates/homework/homework.html",
@@ -166,6 +169,8 @@ def test_all_recorded_copies_exist_with_recorded_integration_state(self):
166169
"courses/static/courses.css",
167170
"courses/tests/homework_submissions_base.py",
168171
"courses/tests/leaderboard_base.py",
172+
"courses/tests/test_datamailer_signals.py",
173+
"courses/tests/test_datamailer_transactional.py",
169174
"courses/tests/test_homework_submissions_admin_link.py",
170175
"courses/tests/test_leaderboard_score_breakdown_admin.py",
171176
"courses/tests/test_project_submissions_view.py",

core/tests/test_web_runtime_coherence.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
WebRuntimeBinding,
2222
)
2323
from deploy.legacy_development_compatibility import ECR_REPOSITORY_URI
24+
from test_support.safety import authorize_from_environment
2425

2526
SOURCE_SHA = "a" * 40
2627
IMAGE_DIGEST = f"sha256:{'b' * 64}"
@@ -317,6 +318,9 @@ def collect_subprocess(
317318
"VERSION": VERSION,
318319
"IMAGE_DIGEST": IMAGE_DIGEST,
319320
"RELEASE_SHA": SOURCE_SHA,
321+
"DTC_TEST_SAFETY_COMMAND": "remote_readonly",
322+
"DTC_TEST_TARGET_CLASS": "isolated_development",
323+
"DTC_TEST_REMOTE_NAMESPACE": "deploy-12345678-1",
320324
}
321325
with (
322326
patch.dict("os.environ", workflow_environment, clear=True),
@@ -336,6 +340,13 @@ def collect_subprocess(
336340
self.assertEqual(environment["DTC_EXPECTED_VERSION"], VERSION)
337341
self.assertEqual(environment["DTC_EXPECTED_SOURCE_SHA"], SOURCE_SHA)
338342
self.assertEqual(environment["DTC_EXPECTED_IMAGE_DIGEST"], IMAGE_DIGEST)
343+
self.assertEqual(environment["DTC_TEST_SAFETY_COMMAND"], "remote_readonly")
344+
self.assertEqual(environment["DTC_TEST_TARGET_CLASS"], "isolated_development")
345+
self.assertEqual(environment["DTC_TEST_REMOTE_NAMESPACE"], "deploy-12345678-1")
346+
self.assertEqual(environment["DTC_TEST_BASE_URL"], "https://web.dtcdev.click")
347+
with patch.dict("os.environ", environment, clear=True):
348+
authorization = authorize_from_environment("remote_readonly")
349+
self.assertEqual(authorization.base_url, "https://web.dtcdev.click")
339350

340351
def test_eventual_visibility_freezes_two_samples_around_public_health(self) -> None:
341352
events: list[str] = []

0 commit comments

Comments
 (0)