Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
491cd0c
feat: search, load and validates manifests
upils Nov 26, 2025
a6e57f7
feat: adapt to revised strategy
upils Jan 29, 2026
5da0450
feat: implement upgrade
upils Jan 30, 2026
5703751
fix: deletion
upils Jan 30, 2026
26bb2b3
fix: revert inadvertent change
upils Jan 30, 2026
4a14230
fix: check slice collecting error
upils Jan 30, 2026
865c1b8
fix: simplify upgrade and improve deletion
upils Jan 30, 2026
0343ddc
refactor: cleaning
upils Jan 30, 2026
e116424
refactor: refine upgrade
upils Feb 2, 2026
5b9d408
refactor: fsutil handles moving and removing
upils Feb 3, 2026
7861c8d
refactor: improve consistency
upils Feb 3, 2026
27a0d22
style: fix lint error
upils Feb 3, 2026
5f377b0
tests: Move and Remove
upils Feb 3, 2026
f74265c
tests: FindPathsInRelease
upils Feb 3, 2026
799373c
tests: more test cases for Move
upils Feb 3, 2026
dbe3f30
tests: add spread test
upils Feb 3, 2026
becfe42
tests: fix recut spread test
upils Feb 4, 2026
3c994b8
tests: SelectValidManifest
upils Feb 4, 2026
6ebb893
fix: add missing deps
upils Feb 4, 2026
ceac217
tests: recut feature
upils Feb 4, 2026
7e34e32
fix: avoid duplicates in FindPathsInRelease
upils Feb 4, 2026
e1d4e17
ci: rerun
upils Feb 4, 2026
c3db9d4
fix: apply PR suggestions
upils Feb 5, 2026
e1e9c43
refactor: simplify FindPaths and FindPathsInRelease
upils Feb 5, 2026
da567ba
style: lint
upils Feb 5, 2026
2a6c891
fix: remove outdated comment
upils Feb 5, 2026
398e032
test: fix inaccurate tests
upils Feb 5, 2026
7c17f18
refactor: apply PR suggestions
upils Feb 6, 2026
7caf3ba
refactor: revert error message change
upils Feb 6, 2026
a259d1d
fix: gate recut feature behind env var
upils Feb 9, 2026
fad2cb7
refactor: split install from Run
upils Feb 9, 2026
d095953
tests: simplify recut spread test
upils Feb 9, 2026
d0b70cd
tests: rework recut spread tests
upils Feb 9, 2026
83e7af6
refactor: revert now useless changes
upils Feb 9, 2026
d217c43
refactor: simplify
upils Feb 9, 2026
8a7066b
refactor: more cleaning
upils Feb 9, 2026
87d1496
test: wip rework uprade tests
upils Feb 10, 2026
f8e2bd6
fix: ignore unknown manifest schema error
upils Feb 11, 2026
6dd701c
feat: improve upgrade and add tests
upils Feb 11, 2026
8523aa5
tests: simplify TestRunRecut
upils Feb 11, 2026
2ceb21a
fix: rename recut env var
upils Feb 12, 2026
10a1744
docs: add TODO on other file creation bug
upils Feb 12, 2026
6cf317c
refactor: simplify targetDir handling in Run
upils Feb 12, 2026
0d1103b
fix: improve workdir name
upils Feb 12, 2026
78e3f51
fix: clarify intent to remove existing content
upils Feb 12, 2026
d14c056
fix: apply various PR suggestions
upils Feb 13, 2026
4217131
fix: apply PR suggestions
upils Feb 13, 2026
a902099
fix: apply PR suggestions
upils Feb 13, 2026
08b3edd
test: improve regex precision
upils Feb 13, 2026
cf90f0d
refactor: extract and simplify mkParentAll
upils Feb 13, 2026
46f872c
fix: Error when only invalid manifests found
upils Feb 13, 2026
2052ee5
fix: lint
upils Feb 13, 2026
0fbc47a
fix: wrap add context on upgrade errors
upils Feb 13, 2026
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
24 changes: 24 additions & 0 deletions cmd/chisel/cmd_cut.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"os"
"slices"
"time"

Expand All @@ -11,6 +12,7 @@ import (
"github.com/canonical/chisel/internal/cache"
"github.com/canonical/chisel/internal/setup"
"github.com/canonical/chisel/internal/slicer"
"github.com/canonical/chisel/public/manifest"
)

var shortCutHelp = "Cut a tree with selected slices"
Expand Down Expand Up @@ -73,6 +75,27 @@ func (cmd *cmdCut) Execute(args []string) error {
}
}

var mfest *manifest.Manifest
// TODO: Remove this gating once the final upgrading strategy is in place.
if os.Getenv("CHISEL_RECUT_EXPERIMENTAL") != "" {
mfest, err := slicer.SelectValidManifest(cmd.RootDir, release)
if err != nil {
return err
}
if mfest != nil {
err = mfest.IterateSlices("", func(slice *manifest.Slice) error {
sk, err := setup.ParseSliceKey(slice.Name)
if err != nil {
return err
}
sliceKeys = append(sliceKeys, sk)
return nil
})
if err != nil {
return err
}
}
}
selection, err := setup.Select(release, sliceKeys, cmd.Arch)
if err != nil {
return err
Expand Down Expand Up @@ -125,6 +148,7 @@ func (cmd *cmdCut) Execute(args []string) error {
Selection: selection,
Archives: archives,
TargetDir: cmd.RootDir,
Manifest: mfest,
})
return err
}
7 changes: 7 additions & 0 deletions internal/fsutil/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ func createDir(o *CreateOptions) error {
}
err = os.Mkdir(path, o.Mode)
if os.IsExist(err) {
// TODO: Detect if existing content is a file. ErrExist is also returned
// if a file exists at this path, so returning nil here creates an
// inconsistency between our view of the content and the real content on
// disk which is a bug that must be fixed.
return nil
}
return err
Expand All @@ -179,6 +183,9 @@ func createFile(o *CreateOptions) error {
if err != nil {
return err
}
// TODO: Detect if existing content is a symlink and remove it if so. The
// current implementation resolves the symlink and will override the target
// and not the symlink itself which is a bug.
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, o.Mode)
if err != nil {
return err
Expand Down
19 changes: 19 additions & 0 deletions internal/manifestutil/manifestutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"
"io/fs"
"maps"
"path/filepath"
"slices"
"sort"
Expand Down Expand Up @@ -34,6 +35,24 @@ func FindPaths(slices []*setup.Slice) map[string][]*setup.Slice {
return manifestSlices
}

// FindPathsInRelease finds all the paths marked with "generate:manifest"
// for the given release.
func FindPathsInRelease(r *setup.Release) []string {
manifestPaths := make(map[string]struct{})
for _, pkg := range r.Packages {
for _, slice := range pkg.Slices {
for path, info := range slice.Contents {
if info.Generate == setup.GenerateManifest {
dir := strings.TrimSuffix(path, "**")
path = filepath.Join(dir, DefaultFilename)
manifestPaths[path] = struct{}{}
}
}
}
}
return slices.Sorted(maps.Keys(manifestPaths))
}

type WriteOptions struct {
PackageInfo []*archive.PackageInfo
Selection []*setup.Slice
Expand Down
111 changes: 111 additions & 0 deletions internal/manifestutil/manifestutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,121 @@ func (s *S) TestFindPaths(c *C) {
}
}

var findPathsInReleaseTests = []struct {
summary string
release *setup.Release
expected []string
}{{
summary: "Single package with single slice",
release: &setup.Release{
Packages: map[string]*setup.Package{
"package1": {
Name: "package1",
Slices: map[string]*setup.Slice{
"slice1": {
Name: "slice1",
Contents: map[string]setup.PathInfo{
"/folder/**": {
Kind: "generate",
Generate: "manifest",
},
},
},
},
},
},
},
expected: []string{"/folder/manifest.wall"},
}, {
summary: "No slices with generate:manifest",
release: &setup.Release{
Packages: map[string]*setup.Package{
"package1": {
Name: "package1",
Slices: map[string]*setup.Slice{
"slice1": {
Name: "slice1",
Contents: map[string]setup.PathInfo{},
},
},
},
},
},
expected: nil,
}, {
summary: "Multiple packages with multiple slices",
release: &setup.Release{
Packages: map[string]*setup.Package{
"package1": {
Name: "package1",
Slices: map[string]*setup.Slice{
"slice1": {
Name: "slice1",
Contents: map[string]setup.PathInfo{
"/folder/**": {
Kind: "generate",
Generate: "manifest",
},
},
},
"slice2": {
Name: "slice2",
Contents: map[string]setup.PathInfo{
"/folder/**": {
Kind: "generate",
Generate: "manifest",
},
},
},
},
},
"package2": {
Name: "package2",
Slices: map[string]*setup.Slice{
"slice3": {
Name: "slice3",
Contents: map[string]setup.PathInfo{},
},
"slice4": {
Name: "slice4",
Contents: map[string]setup.PathInfo{
"/other-folder/**": {
Kind: "generate",
Generate: "manifest",
},
},
},
},
},
},
},
expected: []string{"/folder/manifest.wall", "/other-folder/manifest.wall"},
}, {
summary: "Empty release",
release: &setup.Release{
Packages: map[string]*setup.Package{},
},
expected: nil,
}}

func (s *S) TestFindPathsInRelease(c *C) {
for _, test := range findPathsInReleaseTests {
c.Logf("Summary: %s", test.summary)

manifestPaths := manifestutil.FindPathsInRelease(test.release)

c.Assert(manifestPaths, HasLen, len(test.expected))
slices.Sort(manifestPaths)
slices.Sort(test.expected)
c.Assert(manifestPaths, DeepEquals, test.expected)
}
}

var slice1 = &setup.Slice{
Package: "package1",
Name: "slice1",
}

var slice2 = &setup.Slice{
Package: "package2",
Name: "slice2",
Expand Down
Loading
Loading