gopack/main.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)
}
}