From 23a44c3c155aa1795856978c9da81b4ecea48edc Mon Sep 17 00:00:00 2001 From: Ethan Li Date: Wed, 24 Apr 2024 21:47:41 -0700 Subject: [PATCH] Simplify file exports specification, bump version numbers (#188) --- CHANGELOG.md | 3 +- cmd/forklift/main.go | 8 ++--- docs/specs/00-package.md | 10 +++--- internal/app/forklift/bundles.go | 29 +++++++++--------- internal/app/forklift/pallets-deployments.go | 4 +-- pkg/core/packages-models.go | 9 +++--- pkg/core/packages-resources.go | 32 ++------------------ 7 files changed, 35 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7ca8e1e..9eace686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.7.0-alpha.4 - 2024-04-25 ### Added @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- (Breaking change; spec) The file exports specification has changed so that only a single target path (instead of a list of target paths) can be specified per file export object; accordingly, the field has been renamed from `targets` to `target`. Additionally, if the `source` path is left empty, it is interpreted to have the same value as the `target` path. - (Breaking change; cli) The `--parallel` flag for various subcommands has now been consolidated and moved to the top level (e.g. `forklift --parallel plt cache-img` instead of `forklift plt cache-img --parallel`). Additionally, now the flag is enabled by default (because sequential downloading of images and bringup of Docker containers is so much slower than parallel downloading/bringup); to avoid parallel execution, use `--parallel=false` (e.g. `forklift --parallel=false plt cache-img`). - (Breaking change; cli) `plt clone` no longer deletes the `.git` directory after cloning a pallet, because the new pallet staging functionality makes it feasible to keep a long-running local pallet which can change independently of what is actually applied on a computer. diff --git a/cmd/forklift/main.go b/cmd/forklift/main.go index 14fd4757..86fd5a97 100644 --- a/cmd/forklift/main.go +++ b/cmd/forklift/main.go @@ -95,16 +95,16 @@ const ( palletMinVersion = "v0.4.0" // bundleMinVersion is the minimum supported Forklift version among staged pallet bundles. A // bundle with a lower Forklift version cannot be used. - bundleMinVersion = "v0.7.0-alpha.1" + bundleMinVersion = "v0.7.0" // newBundleVersion is the Forklift version reported in new staged pallet bundles made by Forklift. // Older versions of the Forklift tool cannot use such bundles. - newBundleVersion = "v0.7.0-alpha.1" + newBundleVersion = "v0.7.0" // newStageStoreVersion is the Forklift version reported in a stage store initialized by Forklift. // Older versions of the Forklift tool cannot use the state store. - newStageStoreVersion = "v0.7.0-alpha.1" + newStageStoreVersion = "v0.7.0" // fallbackVersion is the version reported which the Forklift tool reports itself as if its actual // version is unknown. - fallbackVersion = "v0.7.0-alpha.2-dev" + fallbackVersion = "v0.7.1-dev" ) var ( diff --git a/docs/specs/00-package.md b/docs/specs/00-package.md index 16177a24..e4035b2a 100644 --- a/docs/specs/00-package.md +++ b/docs/specs/00-package.md @@ -1268,14 +1268,12 @@ This optional field of the `provides` subsection is an array of file export obje - systemd-service - networking source: enable-interface-forwarding.service - targets: - - overlays/etc/systemd/system/enable-interface-forwarding.service + target: overlays/etc/systemd/system/enable-interface-forwarding.service - description: Symlink to enable the systemd service tags: - systemd-symlink source: enable-interface-forwarding.service.symlink - targets: - - overlays/etc/systemd/system/network-online.target.wants/enable-interface-forwarding.service + target: overlays/etc/systemd/system/network-online.target.wants/enable-interface-forwarding.service ``` A file export object consists of the following fields: @@ -1292,7 +1290,7 @@ A file export object consists of the following fields: - `source` is the path of the file to be exported, relative to the path of the package. The file must exist in the package's directory (or in a subdirectory). - - This field is required. + - This field is optional: if it's not specified, it's assumed to be the path set by the `target` field. - Example: @@ -1300,7 +1298,7 @@ A file export object consists of the following fields: source: dhcp-and-dns.conf ``` -- `targets` is an array of strings which are paths where the file should be exported to (e.g. by copying the file to those paths), relative to an export directory defined by the tool which implements the Forklift packaging specification. +- `target` is the path where the file should be exported to (e.g. by copying the file to that path), relative to an export directory defined by the tool which implements the Forklift packaging specification. - This field is required. diff --git a/internal/app/forklift/bundles.go b/internal/app/forklift/bundles.go index e323322b..20f680b1 100644 --- a/internal/app/forklift/bundles.go +++ b/internal/app/forklift/bundles.go @@ -215,20 +215,21 @@ func (b *FSBundle) WriteFileExports() error { } for _, export := range exports { sourcePath := path.Join(resolved.Pkg.FS.Path(), export.Source) - for _, target := range export.Targets { - exportPath := path.Join(b.getExportsPath(), target) - if err := EnsureExists(filepath.FromSlash(path.Dir(exportPath))); err != nil { - return errors.Wrapf( - err, "couldn't make export directory %s in bundle", path.Dir(exportPath), - ) - } - // TODO: once we upgrade to go1.23, use os.CopyFS instead (see - // https://github.com/golang/go/issues/62484) - if err := cp.Copy( - filepath.FromSlash(sourcePath), filepath.FromSlash(exportPath), - ); err != nil { - return errors.Wrapf(err, "couldn't export file from %s to %s", sourcePath, exportPath) - } + if export.Source == "" { + sourcePath = path.Join(resolved.Pkg.FS.Path(), export.Target) + } + exportPath := path.Join(b.getExportsPath(), export.Target) + if err := EnsureExists(filepath.FromSlash(path.Dir(exportPath))); err != nil { + return errors.Wrapf( + err, "couldn't make export directory %s in bundle", path.Dir(exportPath), + ) + } + // TODO: once we upgrade to go1.23, use os.CopyFS instead (see + // https://github.com/golang/go/issues/62484) + if err := cp.Copy( + filepath.FromSlash(sourcePath), filepath.FromSlash(exportPath), + ); err != nil { + return errors.Wrapf(err, "couldn't export file from %s to %s", sourcePath, exportPath) } } } diff --git a/internal/app/forklift/pallets-deployments.go b/internal/app/forklift/pallets-deployments.go index a1570eac..4ce47ed8 100644 --- a/internal/app/forklift/pallets-deployments.go +++ b/internal/app/forklift/pallets-deployments.go @@ -158,7 +158,7 @@ func (d *ResolvedDepl) DefinesApp() (bool, error) { func (d *ResolvedDepl) GetFileExportTargets() ([]string, error) { exportTargets := make([]string, 0) for _, export := range d.Pkg.Def.Deployment.Provides.FileExports { - exportTargets = append(exportTargets, export.Targets...) + exportTargets = append(exportTargets, export.Target) } enabledFeatures, err := d.EnabledFeatures() if err != nil { @@ -168,7 +168,7 @@ func (d *ResolvedDepl) GetFileExportTargets() ([]string, error) { } for _, feature := range enabledFeatures { for _, export := range feature.Provides.FileExports { - exportTargets = append(exportTargets, export.Targets...) + exportTargets = append(exportTargets, export.Target) } } slices.Sort(exportTargets) diff --git a/pkg/core/packages-models.go b/pkg/core/packages-models.go index 136356d9..10d0734b 100644 --- a/pkg/core/packages-models.go +++ b/pkg/core/packages-models.go @@ -189,8 +189,9 @@ type FileExportRes struct { // Tags is a list of strings associated with the file export. Tags are not considered in checking // resource constraints. Tags []string `yaml:"tags,omitempty"` - // Source is the path in the package of the file to be exported. - Source string `yaml:"source"` - // Targets is a list of paths where the file will be exported to, relative to an export directory. - Targets []string `yaml:"targets"` + // Source is the path in the package of the file to be exported. If omitted, the source path will + // be inferred from the Target path. + Source string `yaml:"source,omitempty"` + // Target is the path where the file will be exported to, relative to an export directory. + Target string `yaml:"target"` } diff --git a/pkg/core/packages-resources.go b/pkg/core/packages-resources.go index 24b8a04c..f76f1d38 100644 --- a/pkg/core/packages-resources.go +++ b/pkg/core/packages-resources.go @@ -344,12 +344,9 @@ func SplitFilesetsByPath(filesetRes []AttachedRes[FilesetRes]) (split []Attached // CheckConflict checks whether the file export resource, represented by the FileExportRes // instance, conflicts with the candidate file export resource. func (r FileExportRes) CheckConflict(candidate FileExportRes) (errs []error) { - if len(r.Targets) == 0 || len(candidate.Targets) == 0 { - errs = append(errs, errors.New("no specified file export targets")) - return errs - } - - errs = append(errs, checkConflictingPathsWithParents(r.Targets, candidate.Targets)...) + errs = append(errs, checkConflictingPathsWithParents( + []string{r.Target}, []string{candidate.Target})..., + ) // Tags should be ignored in checking conflicts return errs @@ -420,26 +417,3 @@ func pathMatchesParent( } return false, "" } - -// SplitFileExportsByTarget produces a slice of file export resources from the input slice, where -// each file export resource in the input slice with multiple paths targets results in multiple -// corresponding file export resources with one target path each. -func SplitFileExportsByTarget( - fileExportRes []AttachedRes[FileExportRes], -) (split []AttachedRes[FileExportRes]) { - split = make([]AttachedRes[FileExportRes], 0, len(fileExportRes)) - for _, fileExport := range fileExportRes { - if len(fileExport.Res.Targets) == 0 { - split = append(split, fileExport) - } - for _, path := range fileExport.Res.Targets { - pathFileExport := fileExport.Res - pathFileExport.Targets = []string{path} - split = append(split, AttachedRes[FileExportRes]{ - Res: pathFileExport, - Source: fileExport.Source, - }) - } - } - return split -}