package main import ( "context" "flag" "fmt" "go.unistack.org/unistack-org/pkgdash/internal" "io" "net/url" "os" "sort" "strings" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage/memory" flagconfig "go.unistack.org/micro-config-flag/v4" "go.unistack.org/micro/v4/config" "go.unistack.org/micro/v4/logger" "golang.org/x/mod/modfile" "golang.org/x/mod/module" ) type Config struct { Action string `flag:"name=action,desc='action to run',default='add'"` Url string `flag:"name=url,desc='url or repo',default=''"` CheckInterval string `default:"*/10 * * * *"` } func main() { var err error ctx, cancel := context.WithCancel(context.Background()) defer cancel() cfg := &Config{} if err = config.Load(ctx, []config.Config{ config.NewConfig(config.Struct(cfg)), flagconfig.NewConfig(config.Struct(cfg)), }); err != nil { logger.Fatal(ctx, err) } flagUrl := flag.Arg(0) u, err := url.Parse(flagUrl) if err != nil { logger.Fatal(ctx, err) } var rev string if idx := strings.Index(u.Path, "@"); idx > 0 { rev = u.Path[idx+1:] } cloneOpts := &git.CloneOptions{ URL: flagUrl, Progress: os.Stdout, } if len(rev) == 0 { cloneOpts.SingleBranch = true cloneOpts.Depth = 1 } if err := cloneOpts.Validate(); err != nil { logger.Fatal(ctx, err) } repo, err := git.CloneContext(ctx, memory.NewStorage(), nil, cloneOpts) if err != nil { logger.Fatal(ctx, err) } ref, err := repo.Head() if err != nil { logger.Fatalf(ctx, "failed to get head: %v", err) } commit, err := repo.CommitObject(ref.Hash()) if err != nil { logger.Fatalf(ctx, "failed to get commit: %v", err) } tree, err := commit.Tree() if err != nil { logger.Fatal(ctx, err) } err = tree.Files().ForEach(func(file *object.File) error { if file == nil { return fmt.Errorf("file pointer is nil") } switch file.Mode { case filemode.Regular: if strings.HasSuffix(file.Name, "go.mod") { mvs, err := Direct(file) if err != nil { return err } internal.Updates(internal.UpdateOptions{ Pre: false, Major: false, Cached: false, Modules: mvs, OnUpdate: func(u internal.Update) { if u.Err != nil { fmt.Fprintf(os.Stderr, "%s: failed: %v\n", u.Module.Path, u.Err) } else { fmt.Printf("%s current:%s => latest:%v\n", u.Module.Path, u.Module.Version, u.Version) } }, }) } return nil default: return nil } }) if err != nil { logger.Fatal(ctx, "failed to exctract file: %v", err) } } func Direct(file *object.File) ([]module.Version, error) { r, err := file.Reader() if err != nil { return nil, err } defer r.Close() data, err := io.ReadAll(r) if err != nil { return nil, err } modfile, err := modfile.ParseLax("go.mod", data, nil) if err != nil { return nil, err } var mods []module.Version for _, req := range modfile.Require { // if !req.Indirect { mods = append(mods, req.Mod) //} } sort.Slice(mods, func(i, j int) bool { return mods[i].Path < mods[j].Path }) return mods, nil } /* func Periodic(ctx context.Context, db *sqlx.DB) error { db. return nil } */