Skip to content

Commit fcd14aa

Browse files
gustavobertoiclaude
andcommitted
feat(template): versioned OCI template registry — push/add/update/diff/verify (spec 19)
Add internal/registry: a pure-Go (oras-go/v2, CGO-free) OCI template-registry seam behind a TargetResolver interface (real remote.Repository w/ docker creds; tests round-trip through an in-memory oras store, no network). Deterministic bundle tar packaging (content-addressed digest), mandatory digest verification on pull, and a cosign Verifier/Signer seam (shells cosign, matching internal/selfupdate's CGO-free precedent; mock-able for tests). Graduate the template.go stubs to real commands: push, add (tag→digest pin + verify + digest-keyed cache + lockfile entry, lock-guarded), update (re-resolve w/ --dry-run), diff (render-diff pinned vs remote), verify (re-pull + signature), and ls. Store config gains a digest-pinned `templates:` lockfile; remote templates are chained into the generate/lint/test source (embedded < store < remote), digest-pinned and offline-first so generation stays byte-deterministic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 15fe475 commit fcd14aa

18 files changed

Lines changed: 2269 additions & 13 deletions

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ require (
2727
github.com/moby/moby/api v1.54.2
2828
github.com/moby/moby/client v0.4.1
2929
github.com/nats-io/nats.go v1.52.0
30+
github.com/opencontainers/go-digest v1.0.0
31+
github.com/opencontainers/image-spec v1.1.1
3032
github.com/spf13/cobra v1.10.2
3133
github.com/twmb/franz-go v1.21.4
3234
github.com/twmb/franz-go/pkg/kadm v1.18.0
@@ -35,6 +37,7 @@ require (
3537
golang.org/x/sync v0.20.0
3638
golang.org/x/term v0.44.0
3739
modernc.org/sqlite v1.52.0
40+
oras.land/oras-go/v2 v2.6.1
3841
)
3942

4043
require (
@@ -95,8 +98,6 @@ require (
9598
github.com/nats-io/nkeys v0.4.15 // indirect
9699
github.com/nats-io/nuid v1.0.1 // indirect
97100
github.com/ncruces/go-strftime v1.0.0 // indirect
98-
github.com/opencontainers/go-digest v1.0.0 // indirect
99-
github.com/opencontainers/image-spec v1.1.1 // indirect
100101
github.com/pierrec/lz4/v4 v4.1.26 // indirect
101102
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
102103
github.com/rivo/uniseg v0.4.7 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,5 +298,7 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
298298
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
299299
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
300300
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
301+
oras.land/oras-go/v2 v2.6.1 h1:bonOEkjLfp8tt6qXWRRWP6p1F+9octchOf2EqnWB4Zs=
302+
oras.land/oras-go/v2 v2.6.1/go.mod h1:dhtFrFOuZuDtAVeZ9FUnaa5zfzplG3ZnFX9/uH1J/Yk=
301303
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
302304
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=

internal/cli/generate.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,26 @@ import (
1313
)
1414

1515
// builtinSource is the template source used by generation and the template
16-
// tooling: custom templates in the store (~/.devstack/templates) override the
17-
// embedded built-ins by name; the embedded set is always the fallback.
16+
// tooling. Resolution priority is embedded < store < remote (first match wins in
17+
// the chain, so the highest-priority source is listed first): a digest-pinned
18+
// REMOTE template (spec 19) overrides a store template, which overrides an
19+
// embedded built-in of the same name. A cold/missing remote cache contributes
20+
// nothing, keeping generation offline-first and deterministic (with no remote
21+
// templates registered the chain is byte-identical to the pre-spec-19 behavior).
1822
func builtinSource() template.TemplateSource {
1923
embedded := template.NewFSSource(templates.FS)
24+
var chain []template.TemplateSource
25+
if remote := remoteTemplateSource(); remote != nil {
26+
chain = append(chain, remote)
27+
}
2028
if dir := userTemplatesDir(); dir != "" {
21-
return template.NewChainSource(template.NewFSSource(os.DirFS(dir)), embedded)
29+
chain = append(chain, template.NewFSSource(os.DirFS(dir)))
30+
}
31+
if len(chain) == 0 {
32+
return embedded
2233
}
23-
return embedded
34+
chain = append(chain, embedded)
35+
return template.NewChainSource(chain...)
2436
}
2537

2638
// newGenerateCmd wires `devstack generate` — the M1 deterministic pipeline entry

internal/cli/template.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ func newTemplateCmd(g *GlobalOpts) *cobra.Command {
2727
newTemplateTestCmd(g),
2828
newTemplateInitCmd(g),
2929
newTemplateNewCmd(g),
30-
// Reserved remote-registry verbs (spec 19, v2) — tree-only stubs so
31-
// help/completions stay consistent (spec 26 / spec 07).
32-
stub("push", "Publish a template to a remote registry", "v2 (spec 19)"),
33-
stub("add", "Add a remote template source", "v2 (spec 19)"),
34-
stub("update", "Update cached remote templates", "v2 (spec 19)"),
35-
stub("diff", "Diff a local template against its remote", "v2 (spec 19)"),
36-
stub("verify", "Verify a remote template's signature", "v2 (spec 19)"),
30+
// Versioned OCI template registry (spec 19).
31+
newTemplatePushCmd(g),
32+
newTemplateAddCmd(g),
33+
newTemplateUpdateCmd(g),
34+
newTemplateDiffCmd(g),
35+
newTemplateVerifyCmd(g),
36+
newTemplateLsCmd(g),
3737
)
3838
return cmd
3939
}

0 commit comments

Comments
 (0)