diff --git a/README.md b/README.md index 021eea4..63f27c8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ## Composition Tool Specification + The composition tool is a command-line tool that helps developers manage dependencies for their projects. It allows developers to specify the dependencies for a project in a "plasma-compose.yaml" file, and then fetches and installs those dependencies @@ -8,28 +9,39 @@ The tool works by recursively fetching and processing the "plasma-compose.yaml" and its dependencies, and then merging the resulting filesystems into a single filesystem. ### CLI + The composition tool is invoked from the command line with the following syntax: launchr compose [options] Where options are: + * -w, --working-dir : The directory where temporary files should be stored during the composition process. Default is the .compose/packages * -s, --skip-not-versioned : Skip not versioned files from source directory (git only) -* --conflicts-verbosity: Log files conflicts in format "[curent-package] - path to file > Selectef from [domain, other package or current-package]" +* --conflicts-verbosity: Log files conflicts in format "[current-package] - path to file > Selected + from [domain, other package or current-package]" +* --interactive: Interactive mode allows to submit user credentials during action (default: true) Example usage - `launchr compose -w=./folder/something -s=1 or -s=true --conflicts-verbosity` -It's important to note that: if same file is present locally and also brought by a package, default strategy is that local file will be taken and package file ignored. [Different strategies](https://github.com/launchrctl/compose/blob/main/example/compose.example.yaml#L18-L35) can be difined to customize this behavior to your needs. - +It's important to note that: if same file is present locally and also brought by a package, default strategy is that +local file will be taken and package file +ignored. [Different strategies](https://github.com/launchrctl/compose/blob/main/example/compose.example.yaml#L18-L35) +can be difined to customize this behavior to your needs. ### `plasma-compose.yaml` File Format -The "plasma-compose.yaml" file is a text file that specifies the dependencies for a package, along with any necessary metadata and sources for those dependencies. + +The "plasma-compose.yaml" file is a text file that specifies the dependencies for a package, along with any necessary +metadata and sources for those dependencies. The file format includes the following elements: + - name: The name of the package. - version: The version number of the package. -- source: The source for the package, including the type of source (Git, HTTP), URL or file path, merge strategy and other metadata. +- source: The source for the package, including the type of source (Git, HTTP), URL or file path, merge strategy and + other metadata. - dependencies: A list of required dependencies. List of strategies: + - overwrite-local-file - remove-extra-local-files - ignore-extra-package-files @@ -38,40 +50,43 @@ List of strategies: Example: ```yaml -name: myproject -version: 1.0.0 +name: example dependencies: -- name: compose-example - source: - type: git - ref: master - tag: 0.0.1 - url: https://github.com/example/compose-example.git - strategy: - - name: remove-extra-local-files - path: - - path/to/remove-extra-local-files - - name: ignore-extra-package-files - path: - - library/inventories/platform_nodes/configuration/dev.yaml - - library/inventories/platform_nodes/configuration/prod.yaml - - library/inventories/platform_nodes/configuration/whatever.yaml + - name: compose-example + source: + type: git + ref: master # branch or tag name + url: https://github.com/example/compose-example.git + strategy: + - name: remove-extra-local-files + path: + - path/to/remove-extra-local-files + - name: ignore-extra-package-files + path: + - library/inventories/platform_nodes/configuration/dev.yaml + - library/inventories/platform_nodes/configuration/prod.yaml + - library/inventories/platform_nodes/configuration/whatever.yaml ``` - ### Fetching and Installing Dependencies -The composition tool fetches and installs dependencies for a package by recursively processing the "plasma-compose.yaml" files for each package and its dependencies. The tool follows these general steps: -1. Fetch the package source code from the specified source location. -2. Extract the package contents to a temporary directory. -3. Process the "plasma-compose.yaml" file for the package, fetching and installing any necessary dependencies recursively. -4. Merge the package filesystem into the final platform filesystem. -5. Repeat steps 1-4 for each package and its dependencies. +The composition tool fetches and installs dependencies for a package by recursively processing the "plasma-compose.yaml" +files for each package and its dependencies. The tool follows these general steps: + +1. Check if package exists locally and is up-to-date. If it's not, remove it from packages dir and proceed to next step. +2. Fetch the package from the specified location. +3. Extract the package contents to a packages directory. +4. Process the "plasma-compose.yaml" file for the package, fetching and installing any necessary dependencies + recursively. +5. Merge the package filesystem into the final platform filesystem. +6. Repeat steps 1-5 for each package and its dependencies. During this process, the composition tool keeps track of the dependencies for each package. ### Plasma-compose commands + it's possible to manipulate plasma-compose.yaml file using commands: + - plasmactl compose:add - plasmactl compose:update - plasmactl compose:delete @@ -84,8 +99,7 @@ For `compose:delete` it's possible to pass list of packaged to delete. In other cases, user will be prompted to CLI form to fill necessary data of packages. -Example of usage - +Examples of usage ``` launchr compose:add --url some-url --type http @@ -93,8 +107,6 @@ launchr compose:add --package package-name --url some-url --ref v1.0.0 launchr compose:update --package package-name --url some-url --ref v1.0.0 launchr compose:add --package package-name --url some-url --ref v1.0.0 --strategy overwrite-local-file --strategy-path "path1|path2" -launchr compose:add --package package-name --url some-url --ref v1.0.0 --strategy overwrite-local-file,remove-extra-local-files --strategy-path "path1|path2,path3|path4" +launchr compose:add --package package-name --url some-url --ref branch --strategy overwrite-local-file,remove-extra-local-files --strategy-path "path1|path2,path3|path4" launchr compose:add --package package-name --url some-url --ref v1.0.0 --strategy overwrite-local-file --strategy-path "path1|path2" --strategy remove-extra-local-files --strategy-path "path3|path4" - - ``` \ No newline at end of file diff --git a/compose/compose.go b/compose/compose.go index 6c57293..6385a31 100644 --- a/compose/compose.go +++ b/compose/compose.go @@ -46,6 +46,12 @@ func CreateComposer(pwd string, opts ComposerOptions, k keyring.Keyring) (*Compo return nil, err } + for _, dep := range config.Dependencies { + if dep.Source.Tag != "" { + launchr.Term().Warning().Printfln("found deprecated field `tag` in `%s` dependency. Use `ref` field for tags or branches.", dep.Name) + } + } + return &Composer{pwd, &opts, config, k}, nil } diff --git a/compose/downloadManager.go b/compose/downloadManager.go index eea59ae..f9bf1fd 100644 --- a/compose/downloadManager.go +++ b/compose/downloadManager.go @@ -8,17 +8,14 @@ import ( const ( // GitType is const for GIT source type download. GitType = "git" - // SourceReferenceTag represents git tag source. - SourceReferenceTag = "tag" - // SourceReferenceBranch represents git branch source. - SourceReferenceBranch = "ref" // HTTPType is const for http source type download. HTTPType = "http" ) // Downloader interface type Downloader interface { - Download(pkg *Package, targetDir string, kw *keyringWrapper) error + Download(pkg *Package, targetDir string) error + EnsureLatest(pkg *Package, downloadPath string) (bool, error) } // DownloadManager struct, provides methods to fetch packages @@ -35,14 +32,14 @@ func CreateDownloadManager(keyring *keyringWrapper) DownloadManager { return DownloadManager{kw: keyring} } -func getDownloaderForPackage(downloadType string) Downloader { +func getDownloaderForPackage(downloadType string, kw *keyringWrapper) Downloader { switch { case downloadType == GitType: - return newGit() + return newGit(kw) case downloadType == HTTPType: - return newHTTP() + return newHTTP(kw) default: - return newGit() + return newGit(kw) } } @@ -85,16 +82,13 @@ func (m DownloadManager) recursiveDownload(yc *YamlCompose, kw *keyringWrapper, packagePath := filepath.Join(targetDir, pkg.GetName(), pkg.GetTarget()) - // Skip package download if it exists in packages dir. - if _, err := os.Stat(packagePath); os.IsNotExist(err) { - err = downloadPackage(pkg, targetDir, kw) - if err != nil { - return packages, err - } + err := downloadPackage(pkg, targetDir, kw) + if err != nil { + return packages, err } // If package has plasma-compose.yaml, proceed with it - if _, err := os.Stat(filepath.Join(packagePath, composeFile)); !os.IsNotExist(err) { + if _, err = os.Stat(filepath.Join(packagePath, composeFile)); !os.IsNotExist(err) { cfg, err := Lookup(os.DirFS(packagePath)) if err == nil { packages, err = m.recursiveDownload(cfg, kw, packages, pkg, targetDir) @@ -111,20 +105,30 @@ func (m DownloadManager) recursiveDownload(yc *YamlCompose, kw *keyringWrapper, } func downloadPackage(pkg *Package, targetDir string, kw *keyringWrapper) error { - downloader := getDownloaderForPackage(pkg.GetType()) + downloader := getDownloaderForPackage(pkg.GetType(), kw) packagePath := filepath.Join(targetDir, pkg.GetName()) downloadPath := filepath.Join(packagePath, pkg.GetTarget()) - if _, err := os.Stat(downloadPath); !os.IsNotExist(err) { - // Skip package download if folder exists in packages dir. + isLatest, err := downloader.EnsureLatest(pkg, downloadPath) + if err != nil { + return err + } + + if isLatest { return nil } + // Ensure old package doesn't exist in case of update. + err = os.RemoveAll(downloadPath) + if err != nil { + return err + } + // temporary if dtype := pkg.GetType(); dtype == HTTPType { downloadPath = packagePath } - err := downloader.Download(pkg, downloadPath, kw) + err = downloader.Download(pkg, downloadPath) return err } diff --git a/compose/forms.go b/compose/forms.go index def51fd..a804c4e 100644 --- a/compose/forms.go +++ b/compose/forms.go @@ -315,7 +315,6 @@ func processStrategiesForm(dependency *Dependency) error { } func preparePackageForm(dependency *Dependency, config *YamlCompose, isAdd bool) *huh.Form { - var refType string uniqueLimit := 1 if isAdd { uniqueLimit = 0 @@ -376,27 +375,11 @@ func preparePackageForm(dependency *Dependency, config *YamlCompose, isAdd bool) }), ), - huh.NewGroup( - huh.NewSelect[string](). - Title("- Select source reference"). - Options( - huh.NewOption("Tag", SourceReferenceTag).Selected(true), - huh.NewOption("Branch", SourceReferenceBranch), - ). - Value(&refType), - ).WithHideFunc(func() bool { return dependency.Source.Type != GitType }), - - huh.NewGroup( - huh.NewInput(). - Title("- Enter Tag"). - Value(&dependency.Source.Tag), - ).WithHideFunc(func() bool { return dependency.Source.Type != GitType || refType != SourceReferenceTag }), - huh.NewGroup( huh.NewInput(). Title("- Enter Ref"). Value(&dependency.Source.Ref), - ).WithHideFunc(func() bool { return dependency.Source.Type != GitType || refType != SourceReferenceBranch }), + ).WithHideFunc(func() bool { return dependency.Source.Type != GitType }), ) } @@ -431,5 +414,4 @@ func sanitizeDependency(dependency *Dependency) { dependency.Name = strings.TrimSpace(dependency.Name) dependency.Source.URL = strings.TrimSpace(dependency.Source.URL) dependency.Source.Ref = strings.TrimSpace(dependency.Source.Ref) - dependency.Source.Tag = strings.TrimSpace(dependency.Source.Tag) } diff --git a/compose/git.go b/compose/git.go index 43ff84b..a1b7de2 100644 --- a/compose/git.go +++ b/compose/git.go @@ -2,7 +2,11 @@ package compose import ( "errors" + "fmt" "os" + "path/filepath" + + "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -12,14 +16,224 @@ import ( "github.com/launchrctl/launchr" ) -type gitDownloader struct{} +type gitDownloader struct { + k *keyringWrapper +} + +func newGit(kw *keyringWrapper) Downloader { + return &gitDownloader{k: kw} +} + +func (g *gitDownloader) fetchRemotes(r *git.Repository, url string, refSpec []config.RefSpec) error { + remotes, errR := r.Remotes() + if errR != nil { + return errR + } + + launchr.Term().Printfln("Fetching remote %s", url) + for _, rem := range remotes { + options := git.FetchOptions{ + //RefSpecs: []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"}, + RefSpecs: refSpec, + Force: true, + } + + auths := []authorizationMode{authorisationNone, authorisationKeyring, authorisationManual} + for _, authType := range auths { + if authType == authorisationNone { + err := rem.Fetch(&options) + if err != nil { + if errors.Is(err, transport.ErrAuthenticationRequired) { + continue + } + + if !errors.Is(err, git.NoErrAlreadyUpToDate) { + return err + } + + return nil + } + } + + if authType == authorisationKeyring { + ci, err := g.k.getForURL(url) + if err != nil { + return err + } + + options.Auth = &http.BasicAuth{ + Username: ci.Username, + Password: ci.Password, + } + + err = rem.Fetch(&options) + if err != nil { + if errors.Is(err, transport.ErrAuthorizationFailed) || errors.Is(err, transport.ErrAuthenticationRequired) { + if g.k.interactive { + launchr.Term().Println("invalid auth, trying manual authorisation") + continue + } + } + + if !errors.Is(err, git.NoErrAlreadyUpToDate) { + return err + } + + return nil + } + } + + if authType == authorisationManual { + ci := keyring.CredentialsItem{} + ci.URL = url + ci, err := g.k.fillCredentials(ci) + if err != nil { + return err + } + + options.Auth = &http.BasicAuth{ + Username: ci.Username, + Password: ci.Password, + } + + err = rem.Fetch(&options) + if err != nil { + if !errors.Is(err, git.NoErrAlreadyUpToDate) { + return err + } + + return nil + } + } + + break + } + } + + return nil +} + +func (g *gitDownloader) EnsureLatest(pkg *Package, downloadPath string) (bool, error) { + if _, err := os.Stat(downloadPath); os.IsNotExist(err) { + // Return False in case package doesn't exist. + return false, nil + } + + r, err := git.PlainOpen(downloadPath) + if err != nil { + launchr.Log().Debug("git init error", "err", err) + return false, nil + } + + head, err := r.Head() + if err != nil { + launchr.Log().Debug("get head error", "err", err) + return false, err + } + + headName := head.Name().Short() + pkgRefName := pkg.GetRef() + remoteRefName := pkgRefName + + if pkg.GetTarget() == TargetLatest && headName != "" { + pkgRefName = headName + remoteRefName = plumbing.HEAD.String() + } + + pullTarget := "" + isLatest := false + if headName == pkgRefName { + pullTarget = "branch" + isLatest, err = g.ensureLatestBranch(r, pkg.GetURL(), pkgRefName, remoteRefName) + if err != nil { + launchr.Term().Warning().Printfln("Couldn't check local branch, marking package %s(%s) as outdated, see debug for detailed error.", pkg.GetName(), pkgRefName) + launchr.Log().Debug("ensure branch error", "err", err) + return isLatest, nil + } + } else { + pullTarget = "tag" + isLatest, err = g.ensureLatestTag(r, pkg.GetURL(), pkgRefName) + if err != nil { + launchr.Term().Warning().Printfln("Couldn't check local tag, marking package %s(%s) as outdated, see debug for detailed error.", pkg.GetName(), pkgRefName) + launchr.Log().Debug("ensure tag error", "err", err) + return isLatest, nil + } + } + + if !isLatest { + launchr.Term().Info().Printfln("Pulling new changes from %s '%s' of %s package", pullTarget, pkgRefName, pkg.GetName()) + } + + return isLatest, nil +} + +func (g *gitDownloader) ensureLatestBranch(r *git.Repository, fetchURL, refName, remoteRefName string) (bool, error) { + refSpec := []config.RefSpec{config.RefSpec(fmt.Sprintf("refs/heads/%s:refs/heads/%s", refName, refName))} + err := g.fetchRemotes(r, fetchURL, refSpec) + if err != nil { + return false, err + } + + br, err := r.Branch(refName) + if err != nil { + return false, err + } + + localRef, err := r.Reference(plumbing.ReferenceName(br.Merge.String()), true) + if err != nil { + return false, err + } + + remote := filepath.Join("refs", "remotes", br.Remote, remoteRefName) + remoteRef, err := r.Reference(plumbing.ReferenceName(remote), false) + if err != nil { + return false, err + } -func newGit() Downloader { - return &gitDownloader{} + return localRef.Hash() == remoteRef.Hash(), nil +} + +func (g *gitDownloader) ensureLatestTag(r *git.Repository, fetchURL, refName string) (bool, error) { + oldTag, err := r.Tag(refName) + if err != nil { + return false, err + } + + head, err := r.Head() + if err != nil { + return false, err + } + + refSpec := []config.RefSpec{config.RefSpec(fmt.Sprintf("refs/tags/%s:refs/tags/%s", refName, refName))} + err = g.fetchRemotes(r, fetchURL, refSpec) + if err != nil { + return false, err + } + + newTag, err := r.Tag(refName) + if err != nil { + return false, err + } + + if oldTag.Hash().String() != newTag.Hash().String() { + return false, err + } + revision := plumbing.Revision(newTag.Name().String()) + tagCommitHash, err := r.ResolveRevision(revision) + if err != nil { + return false, err + } + + commit, err := r.CommitObject(*tagCommitHash) + if err != nil { + return false, err + } + + return commit.ID() == head.Hash(), nil } // Download implements Downloader.Download interface -func (g *gitDownloader) Download(pkg *Package, targetDir string, kw *keyringWrapper) error { +func (g *gitDownloader) Download(pkg *Package, targetDir string) error { launchr.Term().Printfln("git fetch: %s", pkg.GetURL()) url := pkg.GetURL() @@ -27,17 +241,56 @@ func (g *gitDownloader) Download(pkg *Package, targetDir string, kw *keyringWrap return errNoURL } - options := &git.CloneOptions{ + ref := pkg.GetRef() + if ref == "" { + // Try to clone latest master branch. + err := g.tryDownload(targetDir, g.buildOptions(url)) + if err != nil { + return err + } + + return nil + } + + loaded := false + + // As we don't know if ref exists, iterate and try to clone both: tag and branch references. + refs := []plumbing.ReferenceName{plumbing.NewTagReferenceName(ref), plumbing.NewBranchReferenceName(ref)} + for _, r := range refs { + options := g.buildOptions(url) + options.ReferenceName = r + + err := g.tryDownload(targetDir, options) + if err != nil { + noMatchError := git.NoMatchingRefSpecError{} + if errors.Is(err, noMatchError) { + continue + } + + return err + } + + loaded = true + break + } + + if !loaded { + return fmt.Errorf("couldn't find remote ref %s", ref) + } + + return nil +} + +func (g *gitDownloader) buildOptions(url string) *git.CloneOptions { + return &git.CloneOptions{ URL: url, Progress: os.Stdout, SingleBranch: true, } - if pkg.GetRef() != "" { - options.ReferenceName = plumbing.NewBranchReferenceName(pkg.GetRef()) - } else if pkg.GetTag() != "" { - options.ReferenceName = plumbing.NewTagReferenceName(pkg.GetTag()) - } +} +func (g *gitDownloader) tryDownload(targetDir string, options *git.CloneOptions) error { + url := options.URL auths := []authorizationMode{authorisationNone, authorisationKeyring, authorisationManual} for _, authType := range auths { if authType == authorisationNone { @@ -53,7 +306,7 @@ func (g *gitDownloader) Download(pkg *Package, targetDir string, kw *keyringWrap } if authType == authorisationKeyring { - ci, err := kw.getForURL(url) + ci, err := g.k.getForURL(url) if err != nil { return err } @@ -66,7 +319,7 @@ func (g *gitDownloader) Download(pkg *Package, targetDir string, kw *keyringWrap _, err = git.PlainClone(targetDir, false, options) if err != nil { if errors.Is(err, transport.ErrAuthorizationFailed) || errors.Is(err, transport.ErrAuthenticationRequired) { - if kw.interactive { + if g.k.interactive { launchr.Term().Println("invalid auth, trying manual authorisation") continue } @@ -79,7 +332,7 @@ func (g *gitDownloader) Download(pkg *Package, targetDir string, kw *keyringWrap if authType == authorisationManual { ci := keyring.CredentialsItem{} ci.URL = url - ci, err := kw.fillCredentials(ci) + ci, err := g.k.fillCredentials(ci) if err != nil { return err } diff --git a/compose/http.go b/compose/http.go index b593dc6..bbae8f2 100644 --- a/compose/http.go +++ b/compose/http.go @@ -19,7 +19,7 @@ import ( var ( errInvalidFilepath = errors.New("invalid filepath") - errNoURL = errors.New("invalid url") + errNoURL = errors.New("invalid package url") errFailedClose = errors.New("failed to close stream") errRepositoryNotFound = errors.New("repository not found") errAuthenticationRequired = errors.New("authentication required") @@ -33,14 +33,25 @@ var ( rgxPathRoot = regexp.MustCompile(`^[^\/]*`) ) -type httpDownloader struct{} +type httpDownloader struct { + k *keyringWrapper +} + +func newHTTP(kw *keyringWrapper) Downloader { + return &httpDownloader{k: kw} +} -func newHTTP() Downloader { - return &httpDownloader{} +func (h *httpDownloader) EnsureLatest(_ *Package, downloadPath string) (bool, error) { + if _, err := os.Stat(downloadPath); !os.IsNotExist(err) { + // Skip download if package exists. + return true, nil + } + + return false, nil } // Download implements Downloader.Download interface -func (h *httpDownloader) Download(pkg *Package, targetDir string, kw *keyringWrapper) error { +func (h *httpDownloader) Download(pkg *Package, targetDir string) error { url := pkg.GetURL() name := rgxNameFromURL.FindString(url) if name == "" { @@ -92,7 +103,7 @@ func (h *httpDownloader) Download(pkg *Package, targetDir string, kw *keyringWra } if authType == authorisationKeyring { - ci, errGet := kw.getForURL(url) + ci, errGet := h.k.getForURL(url) if errGet != nil { return errGet } @@ -101,7 +112,7 @@ func (h *httpDownloader) Download(pkg *Package, targetDir string, kw *keyringWra resp, err = doRequest(client, req) if err != nil { if errors.Is(err, errAuthorizationFailed) { - if kw.interactive { + if h.k.interactive { launchr.Term().Println("invalid auth, trying manual authorisation") continue } @@ -115,7 +126,7 @@ func (h *httpDownloader) Download(pkg *Package, targetDir string, kw *keyringWra if authType == authorisationManual { ci := keyring.CredentialsItem{} ci.URL = url - ci, errFill := kw.fillCredentials(ci) + ci, errFill := h.k.fillCredentials(ci) if errFill != nil { return errFill } @@ -157,6 +168,8 @@ func (h *httpDownloader) Download(pkg *Package, targetDir string, kw *keyringWra } if archiveRootDir != "" { + defer os.Remove(fpath) + // rename root folder to package name return os.Rename( filepath.Join(targetDir, archiveRootDir), diff --git a/compose/yaml.go b/compose/yaml.go index d02531f..3d4461a 100644 --- a/compose/yaml.go +++ b/compose/yaml.go @@ -9,6 +9,11 @@ import ( "gopkg.in/yaml.v3" ) +const ( + // TargetLatest is fallback to latest master branch. + TargetLatest = "latest" +) + var ( composePermissions uint32 = 0644 ) @@ -87,22 +92,26 @@ func (p *Package) GetURL() string { // GetRef from package source func (p *Package) GetRef() string { - return p.Source.Ref + ref := p.Source.Ref + if ref == "" && p.GetTag() != "" { + ref = p.GetTag() + } + + return ref } -// GetTag from package source +// GetTag from package source. +// Deprecated: use [Package.GetRef] func (p *Package) GetTag() string { return p.Source.Tag } // GetTarget returns a target version of package func (p *Package) GetTarget() string { - target := "latest" - - if p.GetRef() != "" { - target = p.GetRef() - } else if p.GetTag() != "" { - target = p.GetTag() + target := TargetLatest + ref := p.GetRef() + if ref != "" { + target = ref } return target diff --git a/example/compose.example.yaml b/example/compose.example.yaml index c621922..8890207 100644 --- a/example/compose.example.yaml +++ b/example/compose.example.yaml @@ -1,34 +1,27 @@ -name: myproject -version: 1.0.0 +name: example dependencies: - name: package-1 source: type: git url: https://github.com/example/compose-example.git - # ref: branch-name - # tag: tag-name - strategy: null # In case of conflicting file, default strategy is that local file is selected and package file ignored when composing - + ref: "branch-name or tag-name" + strategy: null # In case of conflicting file, default strategy is that local file is selected and package file ignored when composing - name: package-2 source: type: git url: https://github.com/example/compose-example.git - # ref: branch-name - # tag: tag-name + #ref: "branch-name or tag-name" strategy: - name: overwrite-local-file # Works with files only. path: interaction/file-present-in-package-and-domain < File from package will override file from domain - - name: remove-extra-local-files - path: + path: - interaction/extra-local-file.txt < File from domain will be excluded if it does not exists in package - interaction/extra-local-folder < Directory from domain will be excluded if it does not exists in package - - name: ignore-extra-package-files path: - interaction/extra-package-file.txt < File from package will be excluded if it does not exists in domain - interaction/extra-package-folder < Directory from package will be excluded if it does not exists in domain - - name: filter-package-files path: - interaction/filtered-package-file.txt < Only this file will be taken from package if it does not exists in domain diff --git a/plugin.go b/plugin.go index 100febb..7b75fc6 100644 --- a/plugin.go +++ b/plugin.go @@ -143,7 +143,6 @@ func addPackageFlags(cmd *launchr.Command, dependency *compose.Dependency, strat cmd.Flags().StringVarP(&dependency.Name, "package", "", "", "Name of the package") compose.EnumVarP(cmd, &dependency.Source.Type, "type", "", compose.GitType, []string{compose.GitType, compose.HTTPType}, "Type of the package source: git, http") cmd.Flags().StringVarP(&dependency.Source.Ref, "ref", "", "", "Reference of the package source") - cmd.Flags().StringVarP(&dependency.Source.Tag, "tag", "", "", "Tag of the package source") cmd.Flags().StringVarP(&dependency.Source.URL, "url", "", "", "URL of the package source") cmd.Flags().StringSliceVarP(&strategies.Names, "strategy", "", []string{}, "Strategy name") @@ -151,23 +150,13 @@ func addPackageFlags(cmd *launchr.Command, dependency *compose.Dependency, strat } func packagePreRunValidate(cmd *launchr.Command, _ []string) error { - tagChanged := cmd.Flag("tag").Changed - refChanged := cmd.Flag("ref").Changed - if tagChanged && refChanged { - return errors.New("tag and ref cannot be used at the same time") - } - typeFlag, err := cmd.Flags().GetString("type") if err != nil { return err } if typeFlag == compose.HTTPType { - if tagChanged { - launchr.Term().Warning().Println("Tag can't be used with HTTP source") - err = cmd.Flags().Set("tag", "") - } - if refChanged { + if cmd.Flag("ref").Changed { launchr.Term().Warning().Println("Ref can't be used with HTTP source") err = cmd.Flags().Set("ref", "") }