diff --git a/.gds/bundle.lock.yaml b/.gds/bundle.lock.yaml index 900116d..4754da2 100644 --- a/.gds/bundle.lock.yaml +++ b/.gds/bundle.lock.yaml @@ -2,16 +2,16 @@ schema_version: 1 bundle: - version: "0.8.0" - release_sequence: 55 + version: "0.9.4" + release_sequence: 61 channel: "stable" - source_tree_digest: "sha256:059be9ccd7b94dbb2ed1d00de45ea0f33de430325aa15f0646c04eb35fd10249" - digest: "sha256:e582619f2c9bf192b57b761418d52afe520630a074cbdae3ae9199223c33c93e" - attestation_identity_digest: "sha256:e1b787c63de916f750e2cbcbf664967669024e1062964515f612b2c826c9e10d" + source_tree_digest: "sha256:7539972f647cb41bde607b2df27ba7edc6f38944cd03bd3998e0399feb81fc38" + digest: "sha256:d1952b8e599e5b71b7a4e37614817b6ec30081003f1380a906b6a62defe2a332" + attestation_identity_digest: "sha256:bae54be9c903b0ec1d356ce3ae4b54b4226f0ae978c3c8d544fccde2d17bd771" projection: - input_digest: "sha256:166f34ad10913fd08cfcf0aff3310009e8238669bbd20f1951e4e08016bafe31" - output_digest: "sha256:43d4f041251ec9b90a123b8886303595db184b39ce88c3a4a1cc09d528e86b25" + input_digest: "sha256:0157f5aa21537a65b325d65773d2daf0adf217af695e14be5891e277bcf67679" + output_digest: "sha256:c2be75ac7619803edf5f0d1dab0ef67b5d5dccf2e8fae4ed7723118ea3df5609" files: - path: ".gds/compiled-policy.json" - digest: "sha256:fcd7064cf43b09821b1526486228543872d4c14c19c36925201ce1c7711bb8b3" + digest: "sha256:b6ef908aaec4a6dbb5a5359f2009a5e169c38253bbd4e8bc92f1e091796209c7" diff --git a/.gds/compiled-policy.json b/.gds/compiled-policy.json index ff09936..edc7689 100644 --- a/.gds/compiled-policy.json +++ b/.gds/compiled-policy.json @@ -2,8 +2,8 @@ "schema_version": 1, "compiled_policy": { "repository_id": "repo_01M0Q1Y0YVCJNH27FMQ49MH5AW", - "bundle_version": "0.8.0", - "digest": "sha256:a2e19670b1eae0ab68bf0738ab362455c87f179f96748229185609efe3a341a5" + "bundle_version": "0.9.4", + "digest": "sha256:2e97742e077cea6ba1f962aa6896205da7b59e3f63fbbab87e71853d854acc4c" }, "sources": [ { @@ -21,6 +21,14 @@ "distribution": "public", "path": "policies/roles/public-module.yaml", "digest": "sha256:084f7b09dbfd85a386e47cf83ea0015154b6b01fa3cf85a68f04ef407b89ed4b" + }, + { + "id": "continuous-development", + "tier": "stack", + "priority": 500, + "distribution": "public", + "path": "policies/stacks/continuous-development.yaml", + "digest": "sha256:678b6876cd6cf58d7b97e7e0751e7ec9c0d3f75c2c78c5bfbe0d02930774195d" } ], "effective": { @@ -34,6 +42,9 @@ "context": { "private_parent_persistence": "forbidden" }, + "delivery": { + "profile": "continuous-development" + }, "git": { "branch_cleanup": "merged-only", "default_branch": "main", @@ -180,6 +191,13 @@ "file": "policies/base/repository-default.yaml", "operation": "set" }, + "/effective/delivery/profile": { + "source": "continuous-development", + "tier": "stack", + "priority": 500, + "file": "policies/stacks/continuous-development.yaml", + "operation": "set" + }, "/effective/git/branch_cleanup": { "source": "repository-default", "tier": "base", diff --git a/.github/rulesets/tag-semver.json b/.github/rulesets/tag-semver.json index 3b5865f..e1da4b7 100644 --- a/.github/rulesets/tag-semver.json +++ b/.github/rulesets/tag-semver.json @@ -5,7 +5,7 @@ "bypass_actors": [], "conditions": { "ref_name": { - "include": ["refs/tags/[0-9]+.[0-9]+.[0-9]+"], + "include": ["refs/tags/[0-9]*.[0-9]*.[0-9]*"], "exclude": [] } }, diff --git a/tests/test_release_tag_ruleset.py b/tests/test_release_tag_ruleset.py new file mode 100644 index 0000000..70a655d --- /dev/null +++ b/tests/test_release_tag_ruleset.py @@ -0,0 +1,31 @@ +"""The tag selector uses GitHub fnmatch patterns, not regular expressions.""" +import fnmatch +import json +from pathlib import Path +import unittest + + +ROOT = Path(__file__).resolve().parents[1] + + +def matches(pattern, ref): + # GitHub uses FNM_PATHNAME: a wildcard cannot consume a slash. These + # patterns use only ordinary character classes and stars, with no extglob. + patterns, parts = pattern.split("/"), ref.split("/") + return len(patterns) == len(parts) and all(fnmatch.fnmatchcase(part, glob) for glob, part in zip(patterns, parts)) + + +class ReleaseTagProtectionTests(unittest.TestCase): + def test_actual_release_refs_receive_immutable_tag_protection(self): + ruleset = json.loads((ROOT / ".github/rulesets/tag-semver.json").read_text()) + patterns = ruleset["conditions"]["ref_name"]["include"] + self.assertEqual(ruleset["target"], "tag") + self.assertEqual(ruleset["enforcement"], "active") + self.assertEqual(ruleset["bypass_actors"], []) + self.assertEqual({rule["type"] for rule in ruleset["rules"]}, {"deletion", "non_fast_forward", "update", "required_signatures"}) + for ref in ("refs/tags/0.1.2", "refs/tags/12.34.567", "refs/tags/1.2.3-rc.1"): + with self.subTest(ref=ref): + self.assertTrue(any(matches(pattern, ref) for pattern in patterns)) + for ref in ("refs/heads/0.1.2", "refs/tags/main", "refs/tags/preview/0.1.2", "refs/tags/v1"): + with self.subTest(ref=ref): + self.assertFalse(any(matches(pattern, ref) for pattern in patterns))