mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-10 03:47:03 +02:00
a209757ad2
with this patch, a config like: ```yaml archives: - format: tar.gz # this name template makes the OS and Arch compatible with the results of uname. name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} {{- if .Arm }}v{{ .Arm }}{{ end }} rlcp: true files: - src: "build/**/*" dst: . nfpms: - package_name: foo contents: - src: "build/**/*" dst: usr/share/foo formats: - apk ``` will eval this: <img width="1384" alt="CleanShot 2022-12-21 at 22 21 00@2x" src="https://user-images.githubusercontent.com/245435/209034244-7c31b5f7-cfcd-4825-bb2f-7dd463c5286a.png"> as much as I would like to make this the default, it would be a breaking change, so we really can't do it. If `dst` is empty, it'll have the same behavior as before (no rlcp), and if `strip_parent` is set, it will also still have the same behavior. Finally, if the format is binary, `rlcp` is ignored too (as it doesn't make sense). So, this only changes if: - your format is not binary; and - you have files with `src` and `dst` set Then, goreleaser will warn you to set `rlcp: true`. ## todo - [x] docs - [x] more tests probably - [x] any ideas for a better name for the new config option? fixes #3655 Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>
145 lines
3.6 KiB
Go
145 lines
3.6 KiB
Go
// Package archivefiles can evaluate a list of config.Files into their final form.
|
|
package archivefiles
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/caarlos0/log"
|
|
"github.com/goreleaser/fileglob"
|
|
"github.com/goreleaser/goreleaser/internal/tmpl"
|
|
"github.com/goreleaser/goreleaser/pkg/config"
|
|
)
|
|
|
|
// Eval evaluates the given list of files to their final form.
|
|
func Eval(template *tmpl.Template, rlcp bool, files []config.File) ([]config.File, error) {
|
|
var result []config.File
|
|
for _, f := range files {
|
|
replaced, err := template.Apply(f.Source)
|
|
if err != nil {
|
|
return result, fmt.Errorf("failed to apply template %s: %w", f.Source, err)
|
|
}
|
|
|
|
files, err := fileglob.Glob(replaced)
|
|
if err != nil {
|
|
return result, fmt.Errorf("globbing failed for pattern %s: %w", replaced, err)
|
|
}
|
|
|
|
f.Info.Owner, err = template.Apply(f.Info.Owner)
|
|
if err != nil {
|
|
return result, fmt.Errorf("failed to apply template %s: %w", f.Info.Owner, err)
|
|
}
|
|
f.Info.Group, err = template.Apply(f.Info.Group)
|
|
if err != nil {
|
|
return result, fmt.Errorf("failed to apply template %s: %w", f.Info.Group, err)
|
|
}
|
|
f.Info.MTime, err = template.Apply(f.Info.MTime)
|
|
if err != nil {
|
|
return result, fmt.Errorf("failed to apply template %s: %w", f.Info.MTime, err)
|
|
}
|
|
if f.Info.MTime != "" {
|
|
f.Info.ParsedMTime, err = time.Parse(time.RFC3339Nano, f.Info.MTime)
|
|
if err != nil {
|
|
return result, fmt.Errorf("failed to parse %s: %w", f.Info.MTime, err)
|
|
}
|
|
}
|
|
|
|
// the prefix may not be a complete path or may use glob patterns, in that case use the parent directory
|
|
prefix := replaced
|
|
if _, err := os.Stat(prefix); errors.Is(err, fs.ErrNotExist) || fileglob.ContainsMatchers(prefix) {
|
|
prefix = filepath.Dir(longestCommonPrefix(files))
|
|
}
|
|
|
|
for _, file := range files {
|
|
dst, err := destinationFor(f, prefix, file, rlcp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = append(result, config.File{
|
|
Source: file,
|
|
Destination: dst,
|
|
Info: f.Info,
|
|
})
|
|
}
|
|
}
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Destination < result[j].Destination
|
|
})
|
|
|
|
return unique(result), nil
|
|
}
|
|
|
|
// remove duplicates
|
|
func unique(in []config.File) []config.File {
|
|
var result []config.File
|
|
exist := map[string]string{}
|
|
for _, f := range in {
|
|
if current := exist[f.Destination]; current != "" {
|
|
log.Warnf(
|
|
"file '%s' already exists in archive as '%s' - '%s' will be ignored",
|
|
f.Destination,
|
|
current,
|
|
f.Source,
|
|
)
|
|
continue
|
|
}
|
|
exist[f.Destination] = f.Source
|
|
result = append(result, f)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func destinationFor(f config.File, prefix, path string, rlcp bool) (string, error) {
|
|
if f.StripParent {
|
|
return filepath.Join(f.Destination, filepath.Base(path)), nil
|
|
}
|
|
|
|
if rlcp && f.Destination != "" {
|
|
relpath, err := filepath.Rel(prefix, path)
|
|
if err != nil {
|
|
// since prefix is a prefix of src a relative path should always be found
|
|
return "", err
|
|
}
|
|
return filepath.ToSlash(filepath.Join(f.Destination, relpath)), nil
|
|
}
|
|
|
|
return filepath.Join(f.Destination, path), nil
|
|
}
|
|
|
|
// longestCommonPrefix returns the longest prefix of all strings the argument
|
|
// slice. If the slice is empty the empty string is returned.
|
|
// copied from nfpm
|
|
func longestCommonPrefix(strs []string) string {
|
|
if len(strs) == 0 {
|
|
return ""
|
|
}
|
|
lcp := strs[0]
|
|
for _, str := range strs {
|
|
lcp = strlcp(lcp, str)
|
|
}
|
|
return lcp
|
|
}
|
|
|
|
// copied from nfpm
|
|
func strlcp(a, b string) string {
|
|
var min int
|
|
if len(a) > len(b) {
|
|
min = len(b)
|
|
} else {
|
|
min = len(a)
|
|
}
|
|
for i := 0; i < min; i++ {
|
|
if a[i] != b[i] {
|
|
return a[0:i]
|
|
}
|
|
}
|
|
return a[0:min]
|
|
}
|