123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- package iris
- import (
- "archive/zip"
- "bytes"
- stdContext "context"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "time"
- "github.com/kataras/golog"
- )
- const defaultModuleName = "app"
- // simple function does not uses AST, it simply replaces import paths,
- // creates a go.mod file if not exists and then run the `go mod tidy`
- // command to remove old dependencies and install the new ones.
- // It does NOT replaces breaking changes.
- // The developer SHOULD visit the changelog(HISTORY.md) in order to learn
- // everything about the new features and any breaking changes that comes with it.
- func tryFix() error {
- wdir, err := filepath.Abs(".") // should return the current directory (on both go run & executable).
- if err != nil {
- return fmt.Errorf("can not resolve current working directory: %w", err)
- }
- // First of all, backup the current project,
- // so any changes can be reverted by the end developer.
- backupDest := wdir + "_irisbckp.zip"
- golog.Infof("Backup <%s> to <%s>", wdir, backupDest)
- err = zipDir(wdir, backupDest)
- if err != nil {
- return fmt.Errorf("backup dir: %w", err)
- }
- // go module.
- goModFile := filepath.Join(wdir, "go.mod")
- if !fileExists(goModFile) {
- golog.Warnf("Project is not a go module. Executing <go.mod init app>")
- f, err := os.Create(goModFile)
- if err != nil {
- return fmt.Errorf("go.mod: %w", os.ErrNotExist)
- }
- fmt.Fprintf(f, "module %s\ngo 1.15\n", defaultModuleName)
- f.Close()
- }
- // contnets replacements.
- golog.Infof("Updating...") // note: we will not replace GOPATH project paths.
- err = replaceDirContents(wdir, map[string]string{
- `"github.com/kataras/iris`: `"github.com/kataras/iris/v12`,
- // Note: we could use
- // regexp's FindAllSubmatch, take the dir part and replace
- // any HandleDir and e.t.c, but we are not going to do this.
- // Look the comment of the tryFix() function.
- })
- if err != nil {
- return fmt.Errorf("replace import paths: %w", err)
- }
- commands := []string{
- // "go clean --modcache",
- "go env -w GOPROXY=https://goproxy.cn,https://gocenter.io,https://goproxy.io,direct",
- "go mod tidy",
- }
- for _, c := range commands {
- if err = runCmd(wdir, c); err != nil {
- // print out the command, especially
- // with go env -w the user should know it.
- // We use that because many of our users are living in China,
- // which the default goproxy is blocked).
- golog.Infof("$ %s", c)
- return fmt.Errorf("command <%s>: %w", c, err)
- }
- }
- return nil
- }
- func fileExists(path string) bool {
- stat, err := os.Stat(path)
- if err != nil {
- return os.IsExist(err)
- }
- return !stat.IsDir() && stat.Mode().IsRegular()
- }
- func runCmd(wdir, c string) error {
- ctx, cancel := stdContext.WithTimeout(stdContext.Background(), 2*time.Minute)
- defer cancel()
- parts := strings.Split(c, " ")
- name, args := parts[0], parts[1:]
- cmd := exec.CommandContext(ctx, name, args...)
- // cmd.Path = wdir
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
- }
- // zipDir zips a directory, recursively.
- // It accepts a source directory and a destination zip file.
- func zipDir(src, dest string) error {
- folderName := filepath.Base(src)
- file, err := os.Create(dest)
- if err != nil {
- return err
- }
- defer file.Close()
- w := zip.NewWriter(file)
- defer w.Close()
- walkFunc := func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if info.IsDir() {
- return nil
- }
- file, err := os.Open(path)
- if err != nil {
- return err
- }
- defer file.Close()
- relPath := filepath.Join(folderName, strings.TrimPrefix(path, src))
- f, err := w.Create(relPath)
- if err != nil {
- return err
- }
- _, err = io.Copy(f, file)
- return err
- }
- return filepath.Walk(src, walkFunc)
- }
- func replaceDirContents(target string, replacements map[string]string) error {
- walkFunc := func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if info.IsDir() || !info.Mode().IsRegular() {
- return nil
- }
- file, err := os.OpenFile(path, os.O_RDWR, 0666)
- if err != nil {
- return err
- }
- defer file.Close()
- contents, ioErr := ioutil.ReadAll(file)
- if ioErr != nil {
- return ioErr
- }
- replaced := false
- for oldContent, newContent := range replacements {
- newContents := bytes.ReplaceAll(contents, []byte(oldContent), []byte(newContent))
- if len(newContents) > 0 {
- replaced = true
- contents = newContents[0:]
- }
- }
- if replaced {
- file.Truncate(0)
- file.Seek(0, 0)
- _, err = file.Write(contents)
- return err
- }
- return nil
- }
- return filepath.Walk(target, walkFunc)
- }
|