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
14 changes: 7 additions & 7 deletions .gds/bundle.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
schema_version: 1

bundle:
version: "0.9.2-dev"
version: "0.9.3-dev"
release_sequence: 0
channel: "development"
source_tree_digest: "sha256:0c885d2d0f12f74a4ea83a0cfcdab9d7458c815a89bb555f8dfe8aa8030b5366"
digest: "sha256:0442b90d82ad533ad4cc3e8647f3948dc00feddd2dd529f6665ee4baa59f4419"
source_tree_digest: "sha256:dbbd338cb723846e6f140f16fd2e76594fea9377bf792c03fbae60a54e88ee01"
digest: "sha256:d8b7f50891831664b429e19e5df040fcc7e1fd6d1b544cf8c8ebf3147cc2afef"

projection:
input_digest: "sha256:6990797ab8ff381018560945ee46b08f218e45c29d9330611e4aedc72269ea81"
output_digest: "sha256:6e34a0f22dbff41e27b75cee5aee8166b00acea9573fed77ca622e9e4209e6cc"
input_digest: "sha256:a56667c2156bdae3807b57a647c5f92c07223bf37b0edefbb4a50eb0038bd3e1"
output_digest: "sha256:5a94f145b67e94ed66d769850bed7a5e2a9ea29439d5aebcd400fab18eb49ec1"
files:
- path: ".gds/compiled-policy.json"
digest: "sha256:f22de75f2bb598a052f336948b85dbcf10c368d83dca323b518eee96ff6f8d42"
digest: "sha256:26f4268e1b6df625045487c49b80342e94c5c6a81f2d8dc966f1a3a05021ac37"
- path: ".github/workflows/gds-ci.yml"
digest: "sha256:e2c0d4349fe9b3cfba25794893c7aeda31e92347cb367484d83aaf2aea50828d"
digest: "sha256:e12974ce96d1fccf477eaab11d2def7a22240df5d0a4e5bfb78c5c2b70e8fc7d"
4 changes: 2 additions & 2 deletions .gds/compiled-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"schema_version": 1,
"compiled_policy": {
"repository_id": "repo_01M0EZ7TB3KNXNSP78Z8M64WXG",
"bundle_version": "0.9.2-dev",
"digest": "sha256:dafc90cc077d4cf850bdf756eb99a6bfb7c532832c0fc43fb4fa8d60ded37d69"
"bundle_version": "0.9.3-dev",
"digest": "sha256:497a28d43aad2a561c0144598da07c453bd1d48de59f02a5321c88f03f27e291"
},
"sources": [
{
Expand Down
8 changes: 4 additions & 4 deletions .gds/repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ git:

ci:
profile: "go"
go_version: "1.26.7"
go_version: "1.27.1"
build_command: "go build -trimpath ./core/cmd/gds"
test_command: "go test ./..."
timeout_minutes: 30
workflow_ref: "NDDev-OpenNetwork/ci-workflows/.github/workflows/go-ci.yml@bc9c7919de1c39d947a8709a92856925051bae05"
workflow_ref: "NDDev-OpenNetwork/ci-workflows/.github/workflows/go-ci.yml@a624689c897c5f4733cf475159e77bba3a2b98c7"
# A public repository, so GitHub-hosted runners are unmetered and a fork's
# pull request cannot reach anything of ours. Self-hosted hardware here would
# turn a fork PR into remote code execution on it.
Expand Down Expand Up @@ -109,8 +109,8 @@ verification:
- "test"
- "build"
required_contexts:
- "GDS fast / go (1.26.7)"
- "GDS PR required / go (1.26.7)"
- "GDS fast / go (1.27.1)"
- "GDS PR required / go (1.27.1)"

agent:
context_profile: "project-default"
Expand Down
4 changes: 2 additions & 2 deletions .github/rulesets/branch-main.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@
"do_not_enforce_on_create": false,
"required_status_checks": [
{
"context": "GDS fast / go (1.26.7)"
"context": "GDS fast / go (1.27.1)"
},
{
"context": "GDS PR required / go (1.26.7)"
"context": "GDS PR required / go (1.27.1)"
}
]
}
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/gds-ci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# GENERATED FILE - DO NOT EDIT DIRECTLY
# generator: gds
# bundle: 0.9.2-dev
# source-tree-digest: sha256:0c885d2d0f12f74a4ea83a0cfcdab9d7458c815a89bb555f8dfe8aa8030b5366
# input-digest: sha256:6990797ab8ff381018560945ee46b08f218e45c29d9330611e4aedc72269ea81
# output-digest: sha256:8c045e745cc69b731bc695a4a9d58a48c10f1ab7dd85b7354db7bfd0e072711c
# bundle: 0.9.3-dev
# source-tree-digest: sha256:dbbd338cb723846e6f140f16fd2e76594fea9377bf792c03fbae60a54e88ee01
# input-digest: sha256:a56667c2156bdae3807b57a647c5f92c07223bf37b0edefbb4a50eb0038bd3e1
# output-digest: sha256:15b87303cd0692fd8e5d88281771dded9fdc2fc027d600681155cb4e71f42386
# edit-source:
# - .gds/repository.yaml
# - policies/base/repository-default.yaml
Expand Down Expand Up @@ -34,15 +34,15 @@ jobs:
name: GDS fast
permissions:
contents: read
uses: NDDev-OpenNetwork/ci-workflows/.github/workflows/go-ci.yml@bc9c7919de1c39d947a8709a92856925051bae05
uses: NDDev-OpenNetwork/ci-workflows/.github/workflows/go-ci.yml@a624689c897c5f4733cf475159e77bba3a2b98c7
with:
fetch_depth: 0
# Public hosted CI uses GitHub's service cache; the pinned reusable
# workflow owns cache keys and invalidation, so no runner-local state is trusted.
cache: true

runner: "ubuntu-latest"
go_version: "1.26.7"
go_version: "1.27.1"
# Declared fast commands own their build scope; the complete build is below.
build_command: ""
test_command: "scripts/validate_shell.sh && scripts/validate_go_core.sh --fast"
Expand All @@ -54,12 +54,12 @@ jobs:
needs: fast
permissions:
contents: read
uses: NDDev-OpenNetwork/ci-workflows/.github/workflows/go-ci.yml@bc9c7919de1c39d947a8709a92856925051bae05
uses: NDDev-OpenNetwork/ci-workflows/.github/workflows/go-ci.yml@a624689c897c5f4733cf475159e77bba3a2b98c7
with:
fetch_depth: 0
cache: true
runner: "ubuntu-latest"
go_version: "1.26.7"
go_version: "1.27.1"
build_command: "go build -trimpath ./core/cmd/gds"
test_command: "go test ./... && python3 -m pip install --quiet --require-hashes -r requirements/test.txt && python3 -m pytest"
timeout_minutes: 30
7 changes: 4 additions & 3 deletions .github/workflows/platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ jobs:
# `macos-latest` is Apple Silicon, so it covers darwin/arm64. `ubuntu-24.04-arm`
# covers linux/arm64. Both are standard hosted runners and unmetered on public
# repositories; macOS-XL and larger runners are billed even here and are not used.
# darwin/amd64 stays uncovered: it needs `macos-13`, the last Intel image, whose
# retirement is already announced — worth adding only if that target is still
# shipped when this is next revisited.
# Standard Intel macOS runners remain available as macos-15-intel. Together
# with the main Linux X64 workflow these jobs execute all four shipped targets.
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
Expand All @@ -51,6 +50,8 @@ jobs:
runner: macos-latest
- name: linux-arm64
runner: ubuntu-24.04-arm
- name: darwin-amd64
runner: macos-15-intel
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-bundle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
permissions:
contents: read
env:
GOTOOLCHAIN: go1.26.7
GOTOOLCHAIN: go1.27.1
VERSION: ${{ inputs.version }}
RELEASE_SEQUENCE: ${{ inputs.release_sequence }}
CHANNEL: ${{ inputs.channel }}
Expand All @@ -74,7 +74,7 @@ jobs:
- name: Install exact Go toolchain
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: "1.26.7"
go-version: "1.27.1"
cache: true

- name: Run release gates
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ topology.
## Local development

```bash
GOTOOLCHAIN=go1.26.7 go build -trimpath -o /tmp/gds ./core/cmd/gds
GOTOOLCHAIN=go1.26.7 go build -trimpath -o /tmp/gds-codex-runtime-driver \
GOTOOLCHAIN=go1.27.1 go build -trimpath -o /tmp/gds ./core/cmd/gds
GOTOOLCHAIN=go1.27.1 go build -trimpath -o /tmp/gds-codex-runtime-driver \
./core/cmd/gds-codex-runtime-driver
/tmp/gds --json context
/tmp/gds --json status
Expand Down
2 changes: 1 addition & 1 deletion core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Root pytest discovery is limited by `pytest.ini` to `tests/`; it never collects
tests from independent workspace repositories. The full Go validator
runs module integrity, vet, unit/integration/race tests, schemas, and CGo-free
cross-builds for macOS and Linux on arm64 and amd64. It requires the exact
source-registered release builder (`go1.26.7`). Quick validation may run on an
source-registered release builder (`go1.27.1`). Quick validation may run on an
older local toolchain but leaves release evidence `NOT_PROVEN`.

`gds-assurance` is a separate release-gate binary. It requires a clean source
Expand Down
7 changes: 7 additions & 0 deletions core/app/module_verify_lanes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func TestCleanupPendingStopsLaterLanesInSource(t *testing.T) {
}

func TestFailedRemoveWorktreeDoesNotDeleteWorkspace(t *testing.T) {
// Production must retain a checkout whose removal was not proven. Keep that
// deliberately retained fixture inside the test's own temporary lifetime.
t.Setenv("TMPDIR", t.TempDir())
root, oid := moduleVerifyRepository(t)
runner, err := gitprovider.NewMutationRunner()
if err != nil {
Expand Down Expand Up @@ -60,6 +63,10 @@ func TestFailedRemoveWorktreeDoesNotDeleteWorkspace(t *testing.T) {
if !foundCleanup {
t.Fatalf("cleanup finding missing: %#v", findings)
}
retained, err := filepath.Glob(filepath.Join(os.TempDir(), "gds-module-verify-*", "checkout", "fixture.txt"))
if err != nil || len(retained) != 1 {
t.Fatalf("failed cleanup did not preserve the workspace: %v, %v", retained, err)
}
}

func TestFailedRemoveWorktreeSourceContract(t *testing.T) {
Expand Down
22 changes: 3 additions & 19 deletions core/app/projection_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,6 @@ func (services *Services) PlanRepositoryProjection(
Plan: plan, StatePath: statePath, Candidate: current.candidate,
})
envelope.Scope["repository_id"] = current.repositoryID
// A lock generated over uncommitted sources binds worktree content: the
// moment those files are committed, the canonical source digest moves and
// the freshly applied lock reads as GDS_CONTEXT_POLICY_SOURCE_DIGEST_
// MISMATCH. That cost two regenerate round-trips in one day before it was
// named. The plan still works -- a deliberate worktree materialization is
// legitimate -- so this is a warning at the one moment an operator reads
// findings, not a refusal.
if status, statusErr := services.Git.InspectStatus(ctx, current.root); statusErr == nil &&
status.Changes.Staged+status.Changes.Unstaged+status.Changes.Conflicted > 0 {
envelope.Findings = append(envelope.Findings, domain.Finding{
Code: "GDS_PROJECTION_SOURCES_UNCOMMITTED", Severity: domain.SeverityMedium,
Message: "Tracked sources are uncommitted: a lock applied from this plan will mismatch the canonical source digest once they are committed. Commit first, then plan, then apply.",
})
}
return envelope
}

Expand Down Expand Up @@ -305,11 +291,9 @@ func (services *Services) projectionOperationContext(
if err != nil {
return projectionContext{}, []domain.Finding{dependencyFinding(path, err)}
}
if _, err := services.Git.CommittedSourceOID(
ctx, repositoryInfo.WorktreeRoot, []string{".gds/repository.yaml"},
); err != nil {
return projectionContext{}, []domain.Finding{dependencyFinding(path, err)}
}
// The exact manifest bytes are bound below by ManifestDigest, and the
// observer rechecks them before apply. Requiring an earlier anchor commit
// would prevent an anchor edit and its projections from landing atomically.
if !source.released() {
// Trace metadata only; see the equivalent note in services.go. An
// uncommitted canonical source must not block generation, because that
Expand Down
63 changes: 63 additions & 0 deletions core/cli/projection_staged_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cli

import (
"os"
"path/filepath"
"strings"
"testing"
)

func TestStagedAnchorAndProjectionsLandInOneCommit(t *testing.T) {
root := testEstateRoot(t)
t.Setenv("GDS_ESTATE_ROOT", root)
anchorPath := filepath.Join(root, ".gds", "repository.yaml")
raw, err := os.ReadFile(anchorPath)
if err != nil {
t.Fatal(err)
}
raw = []byte(strings.Replace(string(raw), `display_name: "github-device-sync"`, `display_name: "staged projection fixture"`, 1))
if err := os.WriteFile(anchorPath, raw, 0o644); err != nil {
t.Fatal(err)
}
runSessionGit(t, root, "add", ".gds/repository.yaml")
base := []string{"--json", "--cwd", root, "generate", "repository",
"--state-path", sessionStatePath(t), "--device-id", "device_01JEXAMPZ00000000000000000",
"--session-id", "staged-projection"}
exit, planned, stderr := executeJSON(t, append(base, "--plan")...)
if exit != 0 || len(planned.Findings) != 0 {
t.Fatalf("plan exit=%d stderr=%s result=%#v", exit, stderr, planned)
}
planID := syncPlanID(t, planned.Data)

// An edit after planning must not be authorized by the earlier plan.
changed := append(append([]byte(nil), raw...), []byte("\n# changed after planning\n")...)
if err := os.WriteFile(anchorPath, changed, 0o644); err != nil {
t.Fatal(err)
}
exit, refused, _ := executeJSON(t, append(base, "--apply", planID)...)
if exit == 0 || refused.Mutation.Completed {
t.Fatalf("changed manifest was applied: %#v", refused)
}
if err := os.WriteFile(anchorPath, raw, 0o644); err != nil {
t.Fatal(err)
}
// A refused attempt has its own journal; plan again against restored input.
exit, planned, stderr = executeJSON(t, append(base, "--plan")...)
if exit != 0 {
t.Fatalf("replan exit=%d stderr=%s result=%#v", exit, stderr, planned)
}
exit, applied, stderr := executeJSON(t, append(base, "--apply", syncPlanID(t, planned.Data))...)
if exit != 0 || !applied.Mutation.Completed {
t.Fatalf("apply exit=%d stderr=%s result=%#v", exit, stderr, applied)
}
exit, verified, stderr := executeJSON(t, append(base, "--verify", applied.OperationID)...)
if exit != 0 {
t.Fatalf("verify exit=%d stderr=%s result=%#v", exit, stderr, verified)
}
runSessionGit(t, root, "add", ".")
runSessionGit(t, root, "commit", "-qm", "stage anchor and generated projections together")
exit, checked, stderr := executeJSON(t, "--json", "--cwd", root, "generate", "repository", "--check")
if exit != 0 || len(checked.Findings) != 0 {
t.Fatalf("post-commit drift: exit=%d stderr=%s result=%#v", exit, stderr, checked)
}
}
2 changes: 1 addition & 1 deletion core/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
// Version is the development default; a release build overrides it with the
// exact tag via -X. It carries the -dev suffix so an unstamped binary can
// never claim to be a released one.
var Version = "0.9.2-dev"
var Version = "0.9.3-dev"

type options struct {
json bool
Expand Down
4 changes: 1 addition & 3 deletions core/cli/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ func TestGenerateRepositoryReturnsCandidateWithoutMutation(t *testing.T) {
if envelope.Mutation.Attempted || envelope.Mutation.Completed {
t.Fatalf("mutation = %#v", envelope.Mutation)
}
// A committed tree plans without the uncommitted-sources warning; the
// warning's loud half fires only on dirty tracked files, where a lock
// applied now would mismatch the canonical digest after the commit.
// Content-addressed generation does not require a preparatory source commit.
for _, finding := range envelope.Findings {
if finding.Code == "GDS_PROJECTION_SOURCES_UNCOMMITTED" {
t.Fatalf("clean tree warned about uncommitted sources: %#v", finding)
Expand Down
2 changes: 1 addition & 1 deletion core/cmd/gds-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (

// version is the development default; the release builder stamps the exact
// tag via -X main.version. The -dev suffix keeps an unstamped binary honest.
var version = "0.9.2-dev"
var version = "0.9.3-dev"

func main() {
ctx, stop := signal.NotifyContext(
Expand Down
2 changes: 1 addition & 1 deletion core/compiler/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
// policy-owner checkout. It tracks the current release line with a -dev
// suffix so a development bundle is dated honestly; the development channel
// field, not this string, is what classifies the bundle.
const DevelopmentBundleVersion = "0.9.2-dev"
const DevelopmentBundleVersion = "0.9.3-dev"

type PolicySource struct {
SchemaVersion int `json:"schema_version"`
Expand Down
2 changes: 1 addition & 1 deletion core/domain/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type VerificationPolicy struct {
// protected branch enforces, as the anchor claims them.
//
// It is a separate vocabulary from Commands on purpose. A required context
// is a check run name -- "govulncheck", "ci-gate", "GDS fast / go (1.26.7)"
// is a check run name -- "govulncheck", "ci-gate", "GDS fast / go (1.27.1)"
// -- and not a command, so no derivation connects the two. Stating the set
// is what makes it comparable with what the provider actually enforces; the
// alternative, inferring a gate from the commands beside it, would produce a
Expand Down
2 changes: 1 addition & 1 deletion core/releasebuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ func releaseEnvironment(goos string, goarch string, cache string, home string) [
"CGO_ENABLED=0", "GOAMD64=v1", "GOARM64=v8.0", "GOENV=off",
"GOCACHE="+cache, "GOMODCACHE="+filepath.Join(home, "go", "pkg", "mod"),
"GOPROXY=https://proxy.golang.org,direct", "GOSUMDB=sum.golang.org",
"GOTELEMETRY=off", "GOTOOLCHAIN=go1.26.7", "GOWORK=off",
"GOTELEMETRY=off", "GOTOOLCHAIN=go1.27.1", "GOWORK=off",
"GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_NOSYSTEM=1", "GIT_TERMINAL_PROMPT=0",
"HOME="+home, "LANG=C", "LC_ALL=C", "TZ=UTC",
)
Expand Down
2 changes: 1 addition & 1 deletion core/releasebuilder/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/NDDev-OpenNetwork/github-device-sync/core/bundle"
)

const ExpectedGoVersion = "go1.26.7"
const ExpectedGoVersion = "go1.27.1"

type Target = bundle.ReleaseTarget

Expand Down
12 changes: 6 additions & 6 deletions core/workspace/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,12 @@ func ResolvePlacement(
}
if len(matches) == 0 {
return Placement{
DeviceID: descriptor.Device.ID, RepositoryID: anchor.Repository.ID,
Mode: descriptor.Materialization.DefaultMode,
}, []domain.Finding{workspaceFinding(
"GDS_WORKSPACE_PLACEMENT_NOT_SELECTED",
"Repository does not match a device materialization assignment.", anchor.Repository.ID,
)}
DeviceID: descriptor.Device.ID, RepositoryID: anchor.Repository.ID,
Mode: descriptor.Materialization.DefaultMode,
}, []domain.Finding{workspaceFinding(
"GDS_WORKSPACE_PLACEMENT_NOT_SELECTED",
"Repository does not match a device materialization assignment.", anchor.Repository.ID,
)}
}
if len(matches) != 1 {
return Placement{}, []domain.Finding{workspaceFinding(
Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0014-go-production-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Implement the production `gds` CLI and portable control-plane core in Go.

- Set the module language baseline to Go 1.25 so the two currently supported
Go release families can build it. Pin the release builder separately to the
exact verified toolchain, currently Go 1.26.7. A builder older than Go 1.26.7
exact verified toolchain, currently Go 1.27.1. A builder older than Go 1.27.1
is release-blocked; accepting a later builder requires a source-register
update and the same release gates.
- Build release artifacts with `CGO_ENABLED=0` for macOS and Linux on amd64 and
Expand Down
Loading