Skip to content

Commit f7f7027

Browse files
authored
Merge pull request #213 from NDDev-OpenNetwork/fix/released-public-project-projections-20261006
fix(projections): support released policy in public projects
2 parents a4138f9 + 7684e05 commit f7f7027

6 files changed

Lines changed: 118 additions & 11 deletions

File tree

‎.gds/bundle.lock.yaml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ schema_version: 1
44
bundle:
55
version: "0.9.7-dev"
66
release_sequence: 0
7-
source_tree_digest: "sha256:ba259a2260cfc55a79b76c69ea895ab446ea9ae6fe2892a016688713f903e162"
8-
digest: "sha256:9b2dd4f5e413021932c2454c7e482fd790482ec9246e5adaf783dc92780672d9"
7+
source_tree_digest: "sha256:ff0bdb5a3aecc647e785a571e5b08f4d47afdf4b9865139b63670f7636ed0df0"
8+
digest: "sha256:83833e3c808531d699accaa7bde3e31289a631da93553c4fae5d08d9ffe902cd"
99

1010
projection:
11-
input_digest: "sha256:4145cec9f3f37a5c3930907936c97ebab0dd5d3e96a3ee480220aa32255c2ffe"
12-
output_digest: "sha256:2572b8c1c82ccf29b5f7950c788f29cc5504bebf64b141e6b60b743d65366717"
11+
input_digest: "sha256:847d847bf309749351054160ec089ef7f7d22a1969c26a3eb82ca69fa58beb23"
12+
output_digest: "sha256:469c17732e4509ca38c6f2903b590cc0e8670502f965ebf50b4b40833e3f2cb4"
1313
files:
1414
- path: ".gds/compiled-policy.json"
1515
digest: "sha256:9f498788bdc34e52a0ab793c536e0e6a7b360c2e1a20446cbf03ed51986cdc6f"
1616
- path: ".github/workflows/gds-ci.yml"
17-
digest: "sha256:439aac0176476d26063ff3799233a79a1032b905b737aa1b1db56d12f5c9b658"
17+
digest: "sha256:75d8ce3e084bc5b3f1b33ce920b245d635915810cb97fb9887db0eba168d5f7a"

‎.github/workflows/gds-ci.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# GENERATED FILE - DO NOT EDIT DIRECTLY
22
# generator: gds
33
# bundle: 0.9.7-dev
4-
# source-tree-digest: sha256:ba259a2260cfc55a79b76c69ea895ab446ea9ae6fe2892a016688713f903e162
5-
# input-digest: sha256:4145cec9f3f37a5c3930907936c97ebab0dd5d3e96a3ee480220aa32255c2ffe
4+
# source-tree-digest: sha256:ff0bdb5a3aecc647e785a571e5b08f4d47afdf4b9865139b63670f7636ed0df0
5+
# input-digest: sha256:847d847bf309749351054160ec089ef7f7d22a1969c26a3eb82ca69fa58beb23
66
# output-digest: sha256:4ef1ee2fcc42927eaedef9c85f7b421f87e5cc75ff7055ef520216a00f7d4b74
77
# edit-source:
88
# - .gds/repository.yaml

‎core/app/projection_operations.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,10 @@ func (services *Services) projectionOperationContext(
260260
return projectionContext{}, []domain.Finding{dependencyFinding(path, infoErr)}
261261
}
262262
anchor, findings = manifest.NewLoader(services.Schemas).LoadRepository(repositoryInfo.WorktreeRoot)
263-
if len(findings) == 0 && !isPublicModuleProjection(anchor) {
263+
if len(findings) == 0 && anchor.Classification.VisibilityContract != "public" {
264264
findings = []domain.Finding{{
265265
Code: "GDS_PROJECTION_RELEASE_TARGET_INVALID", Severity: domain.SeverityHigh,
266-
Message: "Released projection sources are accepted only for public modules.",
266+
Message: "Released projection sources are accepted only for public repositories.",
267267
}}
268268
}
269269
var releasedManifest bundle.Manifest
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
)
11+
12+
func TestReleasedPublicProjectRequiresArtifactWithoutFallingBackToEstate(t *testing.T) {
13+
root := releasedProjectFixture(t, "public")
14+
services, err := NewServices(DefaultClock)
15+
if err != nil {
16+
t.Fatal(err)
17+
}
18+
archive := filepath.Join(t.TempDir(), "missing-release.tar.gz")
19+
_, findings := services.projectionOperationContext(context.Background(), root, ProjectionSourceOptions{
20+
BundleArchive: archive, ReleaseEnvelope: archive + ".json",
21+
})
22+
if len(findings) != 1 || findings[0].Code != "GDS_LOCAL_OPERATION_NOT_PROVEN" ||
23+
findings[0].Evidence["path"] != archive {
24+
t.Fatalf("public project did not require its exact released artifact: %#v", findings)
25+
}
26+
if _, err := os.Stat(filepath.Join(root, ".gds", "compiled-policy.json")); !os.IsNotExist(err) {
27+
t.Fatalf("artifact failure wrote a projection: %v", err)
28+
}
29+
}
30+
31+
func TestReleasedPrivateProjectCannotDetachFromEstatePolicy(t *testing.T) {
32+
root := releasedProjectFixture(t, "private")
33+
services, err := NewServices(DefaultClock)
34+
if err != nil {
35+
t.Fatal(err)
36+
}
37+
_, findings := services.projectionOperationContext(context.Background(), root, ProjectionSourceOptions{
38+
BundleArchive: "untrusted.tar.gz", ReleaseEnvelope: "untrusted.json",
39+
})
40+
if len(findings) != 1 || findings[0].Code != "GDS_PROJECTION_RELEASE_TARGET_INVALID" {
41+
t.Fatalf("private project could bypass canonical estate policy: %#v", findings)
42+
}
43+
}
44+
45+
func releasedProjectFixture(t *testing.T, visibility string) string {
46+
t.Helper()
47+
root := t.TempDir()
48+
if err := os.Mkdir(filepath.Join(root, ".gds"), 0o755); err != nil {
49+
t.Fatal(err)
50+
}
51+
anchor := `schema_version: 1
52+
repository:
53+
id: repo_01JEXAMPZ00000000000000001
54+
display_name: example-project
55+
roles: [project]
56+
lifecycle: active
57+
provider:
58+
type: github
59+
installation: installation:example
60+
repository_id: 1234
61+
owner: example-owner
62+
name: example-project
63+
classification:
64+
portfolios: [portfolio:example]
65+
visibility_contract: VISIBILITY
66+
data_classification: VISIBILITY
67+
policy:
68+
profiles: [repository-default]
69+
rollout_ring: standard
70+
git:
71+
default_branch: main
72+
integration: pull-request
73+
branch_model: task-branches
74+
handoff_pr: preferred
75+
cleanup: merged-only
76+
verification:
77+
commands:
78+
test: [git diff --check]
79+
required: [test]
80+
agent:
81+
context_profile: project-default
82+
generated_agents: false
83+
serena:
84+
enabled: false
85+
provenance_required: false
86+
release:
87+
mode: none
88+
`
89+
if err := os.WriteFile(filepath.Join(root, ".gds", "repository.yaml"),
90+
[]byte(strings.ReplaceAll(anchor, "VISIBILITY", visibility)), 0o644); err != nil {
91+
t.Fatal(err)
92+
}
93+
for _, args := range [][]string{
94+
{"init", "--quiet", "--initial-branch=main"},
95+
{"add", ".gds/repository.yaml"},
96+
{"-c", "user.name=Example", "-c", "user.email=example@example.test", "-c", "commit.gpgsign=false",
97+
"commit", "--quiet", "-m", "fixture"},
98+
} {
99+
command := exec.Command("git", append([]string{"-C", root}, args...)...)
100+
if output, err := command.CombinedOutput(); err != nil {
101+
t.Fatalf("fixture Git %v: %v: %s", args, err, output)
102+
}
103+
}
104+
return root
105+
}

‎docs/contracts/cli-v1.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ with leaf provenance. It does not write the compiled document.
143143
### `gds generate repository`
144144

145145
`--bundle-archive` and `--release-envelope` select an immutable released
146-
projection source for a standalone public module. They are an inseparable pair
146+
projection source for a public repository, including a project without the
147+
`module` role. They are an inseparable pair
147148
and apply equally to candidate, check, plan, apply and verify modes. The command
148149
never fetches a mutable URL and never treats an unverified extracted directory
149150
as authority.

‎docs/contracts/projections-v1.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ Development locks use sequence `0`. A released lock requires a positive
7171
sequence and attestation identity digest. The development lock is test
7272
evidence, not a released immutable bundle.
7373

74-
Standalone public modules consume released policy without copying its source
74+
Public repositories, including ordinary projects and standalone modules,
75+
consume released policy without copying its source
7576
tree into every repository:
7677

7778
```bash

0 commit comments

Comments
 (0)