feat(files): support creating intermediate directories
This commit is contained in:
parent
b40723b0b8
commit
93b22a886a
3 changed files with 62 additions and 14 deletions
37
pkg/files.go
37
pkg/files.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ----- Globals ---------------------------------------------------------------
|
// ----- Globals ---------------------------------------------------------------
|
||||||
|
@ -20,22 +21,24 @@ func init() {
|
||||||
|
|
||||||
// ----- Public ----------------------------------------------------------------
|
// ----- Public ----------------------------------------------------------------
|
||||||
type FilePromise struct {
|
type FilePromise struct {
|
||||||
chain []Promise
|
chain []Promise
|
||||||
contents Value
|
contents Value
|
||||||
err error
|
dirPermissions *Permissions
|
||||||
filename Value
|
err error
|
||||||
permissions *Permissions
|
filename Value
|
||||||
status Status
|
permissions *Permissions
|
||||||
|
status Status
|
||||||
}
|
}
|
||||||
|
|
||||||
func File(filename any) *FilePromise {
|
func File(filename any) *FilePromise {
|
||||||
return &FilePromise{
|
return &FilePromise{
|
||||||
chain: nil,
|
chain: nil,
|
||||||
contents: nil,
|
contents: nil,
|
||||||
err: nil,
|
dirPermissions: nil,
|
||||||
filename: interfaceToTemplateValue(filename),
|
err: nil,
|
||||||
permissions: nil,
|
filename: interfaceToTemplateValue(filename),
|
||||||
status: PROMISED,
|
permissions: nil,
|
||||||
|
status: PROMISED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +47,11 @@ func (f *FilePromise) Contents(contents any) *FilePromise {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FilePromise) DirectoriesPermissions(p *Permissions) *FilePromise {
|
||||||
|
f.dirPermissions = p
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FilePromise) Permissions(p *Permissions) *FilePromise {
|
func (f *FilePromise) Permissions(p *Permissions) *FilePromise {
|
||||||
f.permissions = p
|
f.permissions = p
|
||||||
return f
|
return f
|
||||||
|
@ -67,6 +75,11 @@ func (f *FilePromise) Promise() Promise {
|
||||||
|
|
||||||
func (f *FilePromise) Resolve() {
|
func (f *FilePromise) Resolve() {
|
||||||
filename := f.filename.String()
|
filename := f.filename.String()
|
||||||
|
if f.dirPermissions != nil {
|
||||||
|
if f.status, f.err = makeDirectoriesHierarchy(filepath.Dir(filename), f.dirPermissions); f.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if f.contents != nil {
|
if f.contents != nil {
|
||||||
var sumFile []byte
|
var sumFile []byte
|
||||||
sumFile, f.err = sha256sumOfFile(filename)
|
sumFile, f.err = sha256sumOfFile(filename)
|
||||||
|
|
26
pkg/utils.go
26
pkg/utils.go
|
@ -2,6 +2,10 @@ package gonf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
var builtinTemplateFunctions = map[string]any{
|
var builtinTemplateFunctions = map[string]any{
|
||||||
|
@ -20,6 +24,28 @@ func FilterSlice[T any](slice *[]T, predicate func(T) bool) {
|
||||||
*slice = (*slice)[:i] // or truncated out of the slice
|
*slice = (*slice)[:i] // or truncated out of the slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We cannot just use os.MakedirAll because we need to set the user:group on every intermediate directories created
|
||||||
|
func makeDirectoriesHierarchy(dir string, perms *Permissions) (Status, error) {
|
||||||
|
if _, err := os.Lstat(dir); err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
if status, err := makeDirectoriesHierarchy(filepath.Dir(dir), perms); err != nil {
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
m, err := perms.mode.Int()
|
||||||
|
if err != nil {
|
||||||
|
return BROKEN, err
|
||||||
|
}
|
||||||
|
os.Mkdir(dir, fs.FileMode(m))
|
||||||
|
perms.resolve(dir)
|
||||||
|
return REPAIRED, nil
|
||||||
|
} else {
|
||||||
|
return BROKEN, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return KEPT, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func sha256sum(contents []byte) []byte {
|
func sha256sum(contents []byte) []byte {
|
||||||
h := sha256.New()
|
h := sha256.New()
|
||||||
h.Write(contents)
|
h.Write(contents)
|
||||||
|
|
|
@ -26,12 +26,21 @@ var apt_norecommends []byte
|
||||||
var sources_list []byte
|
var sources_list []byte
|
||||||
|
|
||||||
func Promise() {
|
func Promise() {
|
||||||
|
rootDir := gonf.ModeUserGroup(0755, "root", "root")
|
||||||
rootRO := gonf.ModeUserGroup(0444, "root", "root")
|
rootRO := gonf.ModeUserGroup(0444, "root", "root")
|
||||||
gonf.Default("debian-release", "stable")
|
gonf.Default("debian-release", "stable")
|
||||||
gonf.AppendVariable("debian-extra-sources", "# Extra sources")
|
gonf.AppendVariable("debian-extra-sources", "# Extra sources")
|
||||||
apt_update := gonf.Command("apt-get", "update", "-qq")
|
apt_update := gonf.Command("apt-get", "update", "-qq")
|
||||||
gonf.File("/etc/apt/sources.list").Permissions(rootRO).Template(sources_list).Promise().IfRepaired(apt_update)
|
gonf.File("/etc/apt/sources.list").
|
||||||
gonf.File("/etc/apt/apt.conf.d/99_norecommends").Permissions(rootRO).Template(apt_norecommends).Promise()
|
Permissions(rootRO).
|
||||||
|
Template(sources_list).
|
||||||
|
Promise().
|
||||||
|
IfRepaired(apt_update)
|
||||||
|
gonf.File("/etc/apt/apt.conf.d/99_norecommends").
|
||||||
|
DirectoriesPermissions(rootDir).
|
||||||
|
Permissions(rootRO).
|
||||||
|
Template(apt_norecommends).
|
||||||
|
Promise()
|
||||||
gonf.SetPackagesConfiguration(packages_install, packages_list, apt_update)
|
gonf.SetPackagesConfiguration(packages_install, packages_list, apt_update)
|
||||||
gonf.Service("opensmtpd").State("enabled", "started").Promise()
|
gonf.Service("opensmtpd").State("enabled", "started").Promise()
|
||||||
systemd.Promise()
|
systemd.Promise()
|
||||||
|
|
Loading…
Add table
Reference in a new issue