Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions local-candidates/flow-1.35.1-measured/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,12 +700,19 @@ def prepared_target(inputs: dict) -> tuple[str, str]:
fail("measured adapter does not support this target")
contract = trust.TARGET_CONTRACTS[target]
release = inputs["release"]
# The release's version fixes the repository it came from.
try:
source = trust.release_source(
target, None if release.get("kind") == "deployment" else release.get("version")
)
except trust.TrustError:
fail("measured release differs from the validated target inventory")
if (
inventory["claim_scope"] != contract["claim_scope"]
or release["schema_version"] != "openadapt.production-release-candidate/v1"
or release["kind"] != contract["release_kind"]
or release["source_repository"] != contract["repository"]
or release["source_repository_id"] != contract["repository_id"]
or release["source_repository"] != source["repository"]
or release["source_repository_id"] != source["repository_id"]
or not isinstance(release["source_commit"], str)
or trust.HEX40.fullmatch(release["source_commit"]) is None
or release["artifacts"] != inventory["artifacts"]
Expand Down
6 changes: 3 additions & 3 deletions production-lifecycle-policy.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "schemas/production-lifecycle-policy.schema.json",
"schema_version": "openadapt.production-lifecycle-policy/v3",
"revision": 7,
"revision": 8,
"admission_validity": "until_revoked",
"maximum_release_admission_days": null,
"maximum_workflow_admission_days": null,
Expand All @@ -12,12 +12,12 @@
"lifecycle_feed_schema_version": "openadapt.production-lifecycle-feed/v2",
"lifecycle_feed_ref": "refs/heads/production-lifecycle-feed",
"targets": [
{"id":"agent","display_name":"OpenAdapt Agent","source_repository":"OpenAdaptAI/openadapt-agent","source_repository_id":"1136136670","release_kind":"package","claim_scope":"production_agent","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt-agent"},
{"id":"agent","display_name":"OpenAdapt Agent","source_repository":"OpenAdaptAI/OpenAdapt","source_repository_id":"627024850","release_kind":"package","claim_scope":"production_agent","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt-agent"},
{"id":"capture","display_name":"OpenAdapt Capture","source_repository":"OpenAdaptAI/openadapt-capture","source_repository_id":"1115283835","release_kind":"package","claim_scope":"production_capture","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt-capture"},
{"id":"cloud","display_name":"OpenAdapt Cloud","source_repository":"OpenAdaptAI/openadapt-cloud","source_repository_id":"1300570990","release_kind":"deployment","claim_scope":"production_cloud","required_artifact_kinds":["deployment-manifest"],"package_index_project":null},
{"id":"desktop","display_name":"OpenAdapt Desktop","source_repository":"OpenAdaptAI/openadapt-desktop","source_repository_id":"1171291730","release_kind":"package","claim_scope":"production_desktop","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt-desktop"},
{"id":"docs","display_name":"OpenAdapt Documentation","source_repository":"OpenAdaptAI/openadapt-ops","source_repository_id":"1172011294","release_kind":"deployment","claim_scope":"production_docs","required_artifact_kinds":["deployment-manifest"],"package_index_project":null},
{"id":"flow","display_name":"OpenAdapt Flow","source_repository":"OpenAdaptAI/openadapt-flow","source_repository_id":"1291376938","release_kind":"package","claim_scope":"production_flow","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt-flow"},
{"id":"flow","display_name":"OpenAdapt Flow","source_repository":"OpenAdaptAI/OpenAdapt","source_repository_id":"627024850","release_kind":"package","claim_scope":"production_flow","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt-flow"},
{"id":"openadapt","display_name":"OpenAdapt","source_repository":"OpenAdaptAI/OpenAdapt","source_repository_id":"627024850","release_kind":"package","claim_scope":"production_openadapt","required_artifact_kinds":["python-sdist","python-wheel"],"package_index_project":"openadapt"}
]
}
2 changes: 1 addition & 1 deletion production-release-authority-policy.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"$schema":"schemas/production-release-authority-policy.schema.json","schema_version":"openadapt.production-release-authority-policy/v1","release_app":{"app_id":"4730708","app_slug":"openadapt-release","installation_id":"156835568","bot_user_id":"321543906","bot_login":"openadapt-release[bot]"},"repository_selection":"selected","permissions":{"administration":"read","contents":"write","metadata":"read"},"repositories":[{"target":"agent","repository":"OpenAdaptAI/openadapt-agent","repository_id":"1136136670"},{"target":"capture","repository":"OpenAdaptAI/openadapt-capture","repository_id":"1115283835"},{"target":"cloud","repository":"OpenAdaptAI/openadapt-cloud","repository_id":"1300570990"},{"target":"desktop","repository":"OpenAdaptAI/openadapt-desktop","repository_id":"1171291730"},{"target":"docs","repository":"OpenAdaptAI/openadapt-ops","repository_id":"1172011294"},{"target":"flow","repository":"OpenAdaptAI/openadapt-flow","repository_id":"1291376938"},{"target":"openadapt","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850"}],"tag_rulesets":{"creation_authority":"OpenAdapt policy: release tag creation","immutability":"OpenAdapt policy: immutable release tags"},"support_release_authorities":[{"id":"desktop-managed-ffmpeg-runtime","lifecycle":"Support","repository":"OpenAdaptAI/openadapt-desktop","repository_id":"1171291730","tag_pattern":"refs/tags/ffmpeg-runtime-v*","creation_ruleset":"OpenAdapt policy: FFmpeg runtime tag creation","immutability_ruleset":"OpenAdapt policy: immutable FFmpeg runtime tags"}]}
{"$schema":"schemas/production-release-authority-policy.schema.json","schema_version":"openadapt.production-release-authority-policy/v1","release_app":{"app_id":"4730708","app_slug":"openadapt-release","installation_id":"156835568","bot_user_id":"321543906","bot_login":"openadapt-release[bot]"},"repository_selection":"selected","permissions":{"administration":"read","contents":"write","metadata":"read"},"repositories":[{"target":"agent","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850","package_path":"packages/agent","tag_pattern":"refs/tags/openadapt-agent-v*","creation_ruleset":"OpenAdapt policy: openadapt-agent release tag creation","immutability_ruleset":"OpenAdapt policy: immutable openadapt-agent release tags"},{"target":"capture","repository":"OpenAdaptAI/openadapt-capture","repository_id":"1115283835"},{"target":"cloud","repository":"OpenAdaptAI/openadapt-cloud","repository_id":"1300570990"},{"target":"desktop","repository":"OpenAdaptAI/openadapt-desktop","repository_id":"1171291730"},{"target":"docs","repository":"OpenAdaptAI/openadapt-ops","repository_id":"1172011294"},{"target":"flow","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850","package_path":"packages/flow","tag_pattern":"refs/tags/openadapt-flow-v*","creation_ruleset":"OpenAdapt policy: openadapt-flow release tag creation","immutability_ruleset":"OpenAdapt policy: immutable openadapt-flow release tags"},{"target":"openadapt","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850"}],"tag_rulesets":{"creation_authority":"OpenAdapt policy: release tag creation","immutability":"OpenAdapt policy: immutable release tags"},"support_release_authorities":[{"id":"desktop-managed-ffmpeg-runtime","lifecycle":"Support","repository":"OpenAdaptAI/openadapt-desktop","repository_id":"1171291730","tag_pattern":"refs/tags/ffmpeg-runtime-v*","creation_ruleset":"OpenAdapt policy: FFmpeg runtime tag creation","immutability_ruleset":"OpenAdapt policy: immutable FFmpeg runtime tags"}]}
6 changes: 4 additions & 2 deletions repository-lifecycle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ reviewed_on: 2026-10-09
# openadapt-types repositories are frozen. Release admission is not configured
# here: production-lifecycle-policy.json and
# production-release-authority-policy.json bind each target's source
# repository, and their `flow` and `agent` targets still name the frozen
# repositories that hold the currently admitted releases.
# repository. Their `flow` and `agent` targets name OpenAdapt; releases from
# flow 2.2.0 and agent 2.2.1 on carry openadapt-flow-v* and openadapt-agent-v*
# tags there, and every earlier release stays bound to its frozen repository
# (scripts/production_trust.py, previous_sources).
canonical_product:
launcher: OpenAdapt
engine: OpenAdapt/packages/flow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
"repository": {"type": "string", "pattern": "^OpenAdaptAI/[A-Za-z0-9._-]+$"},
"repository_id": {"$ref": "#/$defs/decimal_id"},
"target": {"enum": ["agent", "capture", "desktop", "flow", "openadapt"]},
"tag": {"type": "string", "pattern": "^v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag_ref": {"type": "string", "pattern": "^refs/tags/v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag": {"type": "string", "pattern": "^(openadapt-agent-|openadapt-flow-)?v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag_ref": {"type": "string", "pattern": "^refs/tags/(openadapt-agent-|openadapt-flow-)?v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag_object_id": {"$ref": "#/$defs/commit"},
"target_commit": {"$ref": "#/$defs/commit"},
"draft_release_id": {"$ref": "#/$defs/decimal_id"},
Expand Down
4 changes: 2 additions & 2 deletions schemas/production-release-authority-policy.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
},
"repositories": {
"const": [
{"target":"agent","repository":"OpenAdaptAI/openadapt-agent","repository_id":"1136136670"},
{"target":"agent","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850","package_path":"packages/agent","tag_pattern":"refs/tags/openadapt-agent-v*","creation_ruleset":"OpenAdapt policy: openadapt-agent release tag creation","immutability_ruleset":"OpenAdapt policy: immutable openadapt-agent release tags"},
{"target":"capture","repository":"OpenAdaptAI/openadapt-capture","repository_id":"1115283835"},
{"target":"cloud","repository":"OpenAdaptAI/openadapt-cloud","repository_id":"1300570990"},
{"target":"desktop","repository":"OpenAdaptAI/openadapt-desktop","repository_id":"1171291730"},
{"target":"docs","repository":"OpenAdaptAI/openadapt-ops","repository_id":"1172011294"},
{"target":"flow","repository":"OpenAdaptAI/openadapt-flow","repository_id":"1291376938"},
{"target":"flow","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850","package_path":"packages/flow","tag_pattern":"refs/tags/openadapt-flow-v*","creation_ruleset":"OpenAdapt policy: openadapt-flow release tag creation","immutability_ruleset":"OpenAdapt policy: immutable openadapt-flow release tags"},
{"target":"openadapt","repository":"OpenAdaptAI/OpenAdapt","repository_id":"627024850"}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
"decimal_id": {"type": "string", "pattern": "^[1-9][0-9]*$"},
"timestamp": {"type": "string", "format": "date-time", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$"},
"version": {"type": "string", "pattern": "^(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag": {"type": "string", "pattern": "^v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag": {"type": "string", "pattern": "^(openadapt-agent-|openadapt-flow-)?v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"package_identity": {
"properties": {
"release_kind": {"const": "package"},
Expand Down Expand Up @@ -134,7 +134,8 @@
"agent": {
"allOf": [
{"$ref": "#/$defs/package_identity"},
{"properties": {"target": {"const": "agent"}, "claim_scope": {"const": "production_agent"}, "source_repository": {"const": "OpenAdaptAI/openadapt-agent"}, "source_repository_id": {"const": "1136136670"}}}
{"properties": {"target": {"const": "agent"}, "claim_scope": {"const": "production_agent"}}},
{"oneOf": [{"properties": {"source_repository": {"const": "OpenAdaptAI/openadapt-agent"}, "source_repository_id": {"const": "1136136670"}, "tag": {"type": "string", "pattern": "^v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"}}}, {"properties": {"source_repository": {"const": "OpenAdaptAI/OpenAdapt"}, "source_repository_id": {"const": "627024850"}, "tag": {"type": "string", "pattern": "^openadapt-agent-v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"}}}]}
]
},
"capture": {
Expand Down Expand Up @@ -164,7 +165,8 @@
"flow": {
"allOf": [
{"$ref": "#/$defs/package_identity"},
{"properties": {"target": {"const": "flow"}, "claim_scope": {"const": "production_flow"}, "source_repository": {"const": "OpenAdaptAI/openadapt-flow"}, "source_repository_id": {"const": "1291376938"}}}
{"properties": {"target": {"const": "flow"}, "claim_scope": {"const": "production_flow"}}},
{"oneOf": [{"properties": {"source_repository": {"const": "OpenAdaptAI/openadapt-flow"}, "source_repository_id": {"const": "1291376938"}, "tag": {"type": "string", "pattern": "^v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"}}}, {"properties": {"source_repository": {"const": "OpenAdaptAI/OpenAdapt"}, "source_repository_id": {"const": "627024850"}, "tag": {"type": "string", "pattern": "^openadapt-flow-v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"}}}]}
]
},
"openadapt": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
"registry_head_sha256",
"trust_state_source_commit"
],
"allOf": [
{"oneOf": [{"properties": {"source_repository": {"const": "OpenAdaptAI/openadapt-flow"}, "source_repository_id": {"const": "1291376938"}, "tag": {"type": "string", "pattern": "^v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"}}}, {"properties": {"source_repository": {"const": "OpenAdaptAI/OpenAdapt"}, "source_repository_id": {"const": "627024850"}, "tag": {"type": "string", "pattern": "^openadapt-flow-v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"}}}]}
],
"properties": {
"schema_version": {"const": "openadapt.qualification-release-verification-receipt/v1"},
"verification_id_sha256": {"$ref": "#/$defs/digest"},
Expand All @@ -55,11 +58,11 @@
"release_sha256": {"$ref": "#/$defs/digest"},
"artifact_inventory_sha256": {"$ref": "#/$defs/digest"},
"release_identity": {"$ref": "qualification-release.schema.json#/$defs/release_identity"},
"source_repository": {"const": "OpenAdaptAI/openadapt-flow"},
"source_repository_id": {"const": "1291376938"},
"source_repository": {"enum": ["OpenAdaptAI/openadapt-flow", "OpenAdaptAI/OpenAdapt"]},
"source_repository_id": {"enum": ["1291376938", "627024850"]},
"source_commit": {"$ref": "#/$defs/commit"},
"version": {"type": "string", "minLength": 5, "maxLength": 64},
"tag": {"type": "string", "pattern": "^v[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?$"},
"tag": {"type": "string", "pattern": "^(openadapt-flow-)?v[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?$"},
"draft_release_id": {"type": "string", "pattern": "^[1-9][0-9]*$"},
"publication_staging_sha256": {"$ref": "#/$defs/digest"},
"authority_state_sha256": {"$ref": "#/$defs/digest"},
Expand Down
10 changes: 5 additions & 5 deletions schemas/qualification-release.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
"decimal_id": {"type": "string", "pattern": "^[1-9][0-9]*$"},
"version": {"type": "string", "pattern": "^(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag": {"type": "string", "pattern": "^v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"tag": {"type": "string", "pattern": "^(openadapt-agent-|openadapt-flow-)?v(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})\\.(0|[1-9][0-9]{0,9})(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$"},
"timestamp": {"type": "string", "format": "date-time", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$"},
"release_identity": {
"type": "object",
Expand Down Expand Up @@ -184,7 +184,7 @@
"additionalProperties": false,
"required": ["include", "exclude"],
"properties": {
"include": {"const": ["refs/tags/v*"]},
"include": {"enum": [["refs/tags/v*"], ["refs/tags/openadapt-agent-v*"], ["refs/tags/openadapt-flow-v*"]]},
"exclude": {"const": []}
}
}
Expand Down Expand Up @@ -216,7 +216,7 @@
{"$ref": "#/$defs/tag_ruleset"},
{"properties": {
"role": {"const": "creation_authority"},
"name": {"const": "OpenAdapt policy: release tag creation"},
"name": {"enum": ["OpenAdapt policy: release tag creation", "OpenAdapt policy: openadapt-agent release tag creation", "OpenAdapt policy: openadapt-flow release tag creation"]},
"bypass_actors": {"const": [{"actor_id": "4730708", "actor_type": "Integration", "bypass_mode": "always"}]},
"rules": {"const": [{"type": "creation"}]}
}}
Expand All @@ -227,7 +227,7 @@
{"$ref": "#/$defs/tag_ruleset"},
{"properties": {
"role": {"const": "immutability"},
"name": {"const": "OpenAdapt policy: immutable release tags"},
"name": {"enum": ["OpenAdapt policy: immutable release tags", "OpenAdapt policy: immutable openadapt-agent release tags", "OpenAdapt policy: immutable openadapt-flow release tags"]},
"bypass_actors": {"const": []},
"rules": {
"type": "array",
Expand Down Expand Up @@ -351,7 +351,7 @@
"additionalProperties": false,
"required": ["ref", "exists"],
"properties": {
"ref": {"type": "string", "pattern": "^refs/tags/v[^/]+$"},
"ref": {"type": "string", "pattern": "^refs/tags/(openadapt-agent-|openadapt-flow-)?v[^/]+$"},
"exists": {"type": "boolean"}
}
},
Expand Down
22 changes: 19 additions & 3 deletions scripts/check_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
load_public_metadata,
render_apply_script,
)
import production_trust
from validate_production_lifecycle import LifecycleError, validate_files

ROOT = Path(__file__).resolve().parents[1]
Expand Down Expand Up @@ -252,6 +253,23 @@ def parse_lifecycle_groups(
return groups


def product_repository_names(policy: dict) -> set[str]:
"""Repository names of every product target, current and earlier.

A target that moved keeps its earlier releases bound to the repository
they came from, so that repository is still a product target repository.
"""
names = {
target["source_repository"].split("/", 1)[1] for target in policy["targets"]
}
names |= {
previous["repository"].split("/", 1)[1]
for contract in production_trust.TARGET_CONTRACTS.values()
for previous in contract.get("previous_sources", ())
}
return names


def metadata_errors(
metadata: PublicMetadata,
product_repositories: set[str],
Expand Down Expand Up @@ -379,9 +397,7 @@ def main() -> int:
(ROOT / "production-lifecycle-policy.json").read_text(encoding="utf-8")
)
expected_profile_targets = tuple(target["id"] for target in policy["targets"])
product_repositories = {
target["source_repository"].split("/", 1)[1] for target in policy["targets"]
}
product_repositories = product_repository_names(policy)
product_section_parts = profile_text.split(f"{PRODUCT_HEADING}\n", maxsplit=1)
if len(product_section_parts) != 2:
errors.append("profile/README.md is missing the Product surfaces section")
Expand Down
Loading
Loading