2020-11-30 12:11:39 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2020-11-30 17:40:56 +03:00
|
|
|
"net/url"
|
2020-11-30 12:11:39 +03:00
|
|
|
"os"
|
2020-11-30 16:13:29 +03:00
|
|
|
"path/filepath"
|
2020-11-30 17:40:56 +03:00
|
|
|
"strings"
|
2020-11-30 12:11:39 +03:00
|
|
|
|
|
|
|
"github.com/go-git/go-git/v5"
|
2020-11-30 16:13:29 +03:00
|
|
|
"github.com/go-git/go-git/v5/plumbing/filemode"
|
2020-11-30 12:11:39 +03:00
|
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
|
|
"github.com/go-git/go-git/v5/storage/memory"
|
2024-09-26 19:44:12 +03:00
|
|
|
yamlcodec "go.unistack.org/micro-codec-yaml/v3"
|
|
|
|
flagconfig "go.unistack.org/micro-config-flag/v3"
|
|
|
|
"go.unistack.org/micro/v3/config"
|
|
|
|
"go.unistack.org/micro/v3/logger"
|
|
|
|
"go.unistack.org/micro/v3/logger/slog"
|
2020-11-30 12:11:39 +03:00
|
|
|
)
|
|
|
|
|
2024-09-26 19:44:12 +03:00
|
|
|
type Config struct {
|
|
|
|
DstDir string `default:"." flag:"name=dstdir,desc='destination dir',default='.'"`
|
|
|
|
Action string `default:"update" flag:"name=action,desc='action',default='update'"`
|
|
|
|
Clean bool `default:"false" flag:"name=clean,desc='cleaup destination dir',default='false'"`
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var err error
|
|
|
|
|
2024-09-26 19:44:12 +03:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
cfg := &Config{}
|
|
|
|
|
|
|
|
log := slog.NewLogger()
|
|
|
|
if err = log.Init(); err != nil {
|
|
|
|
log.Fatal(ctx, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.DefaultLogger = log
|
|
|
|
|
|
|
|
if err = config.Load(ctx, []config.Config{
|
|
|
|
config.NewConfig(config.Struct(cfg)),
|
|
|
|
flagconfig.NewConfig(config.Struct(cfg), config.Codec(yamlcodec.NewCodec())),
|
|
|
|
}); err != nil {
|
|
|
|
log.Fatal(ctx, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.DstDir == "" || cfg.DstDir == "." {
|
|
|
|
if cfg.DstDir, err = os.Getwd(); err != nil {
|
|
|
|
log.Fatal(ctx, err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Info(ctx, "dstdir not specified, use current dir: %s", cfg.DstDir)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
2024-09-26 19:44:12 +03:00
|
|
|
flagUrl := strings.Join(flag.Args(), " ")
|
2020-11-30 12:11:39 +03:00
|
|
|
|
2020-11-30 17:40:56 +03:00
|
|
|
u, err := url.Parse(flagUrl)
|
|
|
|
if err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, err)
|
2020-11-30 17:40:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var rev string
|
|
|
|
if idx := strings.Index(u.Path, "@"); idx > 0 {
|
|
|
|
rev = u.Path[idx+1:]
|
|
|
|
}
|
|
|
|
|
2020-11-30 12:11:39 +03:00
|
|
|
cloneOpts := &git.CloneOptions{
|
2020-11-30 17:40:56 +03:00
|
|
|
URL: flagUrl,
|
|
|
|
Progress: os.Stdout,
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
2020-11-30 17:40:56 +03:00
|
|
|
|
|
|
|
if len(rev) == 0 {
|
|
|
|
cloneOpts.SingleBranch = true
|
|
|
|
cloneOpts.Depth = 1
|
|
|
|
}
|
|
|
|
|
2020-11-30 12:11:39 +03:00
|
|
|
if err := cloneOpts.Validate(); err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Info(ctx, `try to fetch `+flagUrl)
|
2020-11-30 12:11:39 +03:00
|
|
|
repo, err := git.CloneContext(ctx, memory.NewStorage(), nil, cloneOpts)
|
|
|
|
if err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ref, err := repo.Head()
|
|
|
|
if err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, "failed to get head: %v", err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(ref.Hash())
|
|
|
|
commit, err := repo.CommitObject(ref.Hash())
|
|
|
|
if err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, "failed to get commit: %v", err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
tree, err := commit.Tree()
|
|
|
|
if err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
2024-09-26 19:44:12 +03:00
|
|
|
if err := os.MkdirAll(cfg.DstDir, os.FileMode(0o755)); err != nil {
|
|
|
|
log.Fatal(ctx, "failed to create dir: %v", err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
2024-09-26 19:44:12 +03:00
|
|
|
if cfg.Clean {
|
|
|
|
if err := cleanDir(ctx, cfg.DstDir); err != nil {
|
|
|
|
log.Fatal(ctx, "failed to clean dir: %v", err)
|
|
|
|
}
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
err = tree.Files().ForEach(func(file *object.File) error {
|
|
|
|
if file == nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
return fmt.Errorf("file pointer is nil")
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
2020-11-30 16:13:29 +03:00
|
|
|
|
|
|
|
fmode, err := file.Mode.ToOSFileMode()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch file.Mode {
|
|
|
|
case filemode.Executable:
|
2024-09-26 19:44:12 +03:00
|
|
|
return writeFile(ctx, file, cfg.DstDir, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fmode)
|
2020-11-30 16:13:29 +03:00
|
|
|
case filemode.Dir:
|
2024-09-26 19:44:12 +03:00
|
|
|
return os.MkdirAll(filepath.Join(cfg.DstDir, file.Name), fmode)
|
2020-11-30 16:13:29 +03:00
|
|
|
case filemode.Regular:
|
2024-09-26 19:44:12 +03:00
|
|
|
return writeFile(ctx, file, cfg.DstDir, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fmode)
|
2020-11-30 16:13:29 +03:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("unsupported filetype %v for %s", file.Mode, file.Name)
|
|
|
|
}
|
2020-11-30 12:11:39 +03:00
|
|
|
})
|
|
|
|
if err != nil {
|
2024-09-26 19:44:12 +03:00
|
|
|
log.Fatal(ctx, "failed to exctract file: %v", err)
|
2020-11-30 12:11:39 +03:00
|
|
|
}
|
|
|
|
}
|