142 lines
3.2 KiB
Go
142 lines
3.2 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"github.com/bmatcuk/doublestar/v4"
|
||
|
"golang.org/x/mod/module"
|
||
|
"golang.org/x/mod/zip"
|
||
|
"io"
|
||
|
"log"
|
||
|
"os"
|
||
|
"path"
|
||
|
"path/filepath"
|
||
|
"slices"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type dirFile struct {
|
||
|
filePath, slashPath string
|
||
|
info os.FileInfo
|
||
|
}
|
||
|
|
||
|
func (f dirFile) Path() string { return f.slashPath }
|
||
|
func (f dirFile) Lstat() (os.FileInfo, error) { return f.info, nil }
|
||
|
func (f dirFile) Open() (io.ReadCloser, error) { return os.Open(f.filePath) }
|
||
|
|
||
|
func parseGitignore(wd string) []string {
|
||
|
f := make([]string, 0)
|
||
|
file, err := os.ReadFile(path.Join(wd, ".gitignore"))
|
||
|
if err == nil {
|
||
|
str := bytes.NewBuffer(file).String()
|
||
|
for _, rline := range strings.Split(str, "\n") {
|
||
|
line := strings.TrimSpace(rline)
|
||
|
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") {
|
||
|
continue
|
||
|
}
|
||
|
if strings.HasPrefix(line, "*") {
|
||
|
f = append(f, line)
|
||
|
f = append(f, fmt.Sprintf("**/%s", line))
|
||
|
} else if strings.HasSuffix(line, "/") {
|
||
|
f = append(f, fmt.Sprintf("%s/**", line))
|
||
|
f = append(f, fmt.Sprintf("%s/**/*", line))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
fmt.Println(f)
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
func doWalk(wd string, exGlobs []string) []zip.File {
|
||
|
f := make([]zip.File, 0)
|
||
|
filepath.Walk(wd, func(p string, info os.FileInfo, err error) error {
|
||
|
slashed := filepath.ToSlash(p)
|
||
|
rel, _ := filepath.Rel(wd, slashed)
|
||
|
rel = filepath.ToSlash(rel)
|
||
|
if info.IsDir() {
|
||
|
if p == wd {
|
||
|
return nil
|
||
|
}
|
||
|
switch filepath.Base(p) {
|
||
|
case ".git", ".bzr", ".svn", ".hg":
|
||
|
return filepath.SkipDir
|
||
|
}
|
||
|
if goModInfo, err := os.Lstat(filepath.Join(p, "go.mod")); err == nil && !goModInfo.IsDir() {
|
||
|
return filepath.SkipDir
|
||
|
}
|
||
|
for _, pat := range exGlobs {
|
||
|
|
||
|
ok, _ := doublestar.Match(pat, rel)
|
||
|
if ok {
|
||
|
return filepath.SkipDir
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
if !info.Mode().IsRegular() {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
ok, err := doublestar.Match(fmt.Sprintf("{%s}", strings.Join(exGlobs, ",")), rel)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
if !ok {
|
||
|
if !slices.Contains(maps(f, func(t zip.File) string {
|
||
|
return t.Path()
|
||
|
}), slashed) {
|
||
|
f = append(f, dirFile{
|
||
|
filePath: p,
|
||
|
info: info,
|
||
|
slashPath: rel,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
func maps[T any, R any](slice []T, fn func(t T) R) []R {
|
||
|
ret := make([]R, 0)
|
||
|
for _, e := range slice {
|
||
|
ret = append(ret, fn(e))
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
args := os.Args[1:]
|
||
|
if len(args) < 3 {
|
||
|
log.Fatal("usage: PACKAGE-NAME VERSION PATH [EXCLUSION-GLOBS...]")
|
||
|
}
|
||
|
version := args[1]
|
||
|
if !strings.HasPrefix(version, "v") {
|
||
|
version = fmt.Sprintf("v%s", version)
|
||
|
}
|
||
|
|
||
|
var absPath string
|
||
|
absPath, _ = filepath.Abs(args[2])
|
||
|
excludeGlobs := make([]string, 0)
|
||
|
excludeGlobs = append(excludeGlobs, ".*/*", "build/**")
|
||
|
excludeGlobs = append(excludeGlobs, parseGitignore(absPath)...)
|
||
|
if len(args) >= 4 {
|
||
|
excludeGlobs = append(excludeGlobs, args[3:]...)
|
||
|
}
|
||
|
|
||
|
buildFile := fmt.Sprintf(filepath.Join(absPath, "build", "%s.zip"), version)
|
||
|
walked := doWalk(absPath, excludeGlobs)
|
||
|
_ = os.Remove(buildFile)
|
||
|
f, _ := os.Create(buildFile)
|
||
|
err := zip.Create(f, module.Version{
|
||
|
Version: version,
|
||
|
Path: args[0],
|
||
|
}, walked)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}
|