Skip to content

Commit 48c00f7

Browse files
committed
add a simple 'autofix' - most of the users need just an update
1 parent 2a4ce87 commit 48c00f7

File tree

2 files changed

+225
-11
lines changed

2 files changed

+225
-11
lines changed

autofix.go

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package iris
2+
3+
import (
4+
"archive/zip"
5+
"bytes"
6+
stdContext "context"
7+
"fmt"
8+
"io"
9+
"io/ioutil"
10+
"os"
11+
"os/exec"
12+
"path/filepath"
13+
"strings"
14+
"time"
15+
16+
"github.com/kataras/golog"
17+
)
18+
19+
const defaultModuleName = "app"
20+
21+
// simple function does not uses AST, it simply replaces import paths,
22+
// creates a go.mod file if not exists and then run the `go mod tidy`
23+
// command to remove old dependencies and install the new ones.
24+
// It does NOT replaces breaking changes.
25+
// The developer SHOULD visit the changelog(HISTORY.md) in order to learn
26+
// everything about the new features and any breaking changes that comes with it.
27+
func tryFix() error {
28+
wdir, err := filepath.Abs(".") // should return the current directory (on both go run & executable).
29+
if err != nil {
30+
return fmt.Errorf("can not resolve current working directory: %w", err)
31+
}
32+
33+
// First of all, backup the current project,
34+
// so any changes can be reverted by the end developer.
35+
backupDest := wdir + "_irisbckp.zip"
36+
golog.Infof("Backup <%s> to <%s>", wdir, backupDest)
37+
38+
err = zipDir(wdir, backupDest)
39+
if err != nil {
40+
return fmt.Errorf("backup dir: %w", err)
41+
}
42+
43+
// go module.
44+
goModFile := filepath.Join(wdir, "go.mod")
45+
if !fileExists(goModFile) {
46+
47+
golog.Warnf("Project is not a go module. Executing <go.mod init app>")
48+
f, err := os.Create(goModFile)
49+
if err != nil {
50+
return fmt.Errorf("go.mod: %w", os.ErrNotExist)
51+
}
52+
53+
fmt.Fprintf(f, "module %s\ngo 1.15\n", defaultModuleName)
54+
f.Close()
55+
}
56+
57+
// contnets replacements.
58+
golog.Infof("Updating...") // note: we will not replace GOPATH project paths.
59+
60+
err = replaceDirContents(wdir, map[string]string{
61+
`"github.com/kataras/iris`: `"github.com/kataras/iris/v12`,
62+
// Note: we could use
63+
// regexp's FindAllSubmatch, take the dir part and replace
64+
// any HandleDir and e.t.c, but we are not going to do this.
65+
// Look the comment of the tryFix() function.
66+
})
67+
if err != nil {
68+
return fmt.Errorf("replace import paths: %w", err)
69+
}
70+
71+
commands := []string{
72+
// "go clean --modcache",
73+
"go env -w GOPROXY=https://goproxy.cn,https://gocenter.io,https://goproxy.io,direct",
74+
"go mod tidy",
75+
}
76+
77+
for _, c := range commands {
78+
if err = runCmd(wdir, c); err != nil {
79+
// print out the command, especially
80+
// with go env -w the user should know it.
81+
// We use that because many of our users are living in China,
82+
// which the default goproxy is blocked).
83+
golog.Infof("$ %s", c)
84+
return fmt.Errorf("command <%s>: %w", c, err)
85+
}
86+
}
87+
88+
return nil
89+
}
90+
91+
func fileExists(path string) bool {
92+
stat, err := os.Stat(path)
93+
if err != nil {
94+
return os.IsExist(err)
95+
}
96+
97+
return !stat.IsDir() && stat.Mode().IsRegular()
98+
}
99+
100+
func runCmd(wdir, c string) error {
101+
ctx, cancel := stdContext.WithTimeout(stdContext.Background(), 2*time.Minute)
102+
defer cancel()
103+
104+
parts := strings.Split(c, " ")
105+
name, args := parts[0], parts[1:]
106+
cmd := exec.CommandContext(ctx, name, args...)
107+
// cmd.Path = wdir
108+
cmd.Stdout = os.Stdout
109+
cmd.Stderr = os.Stderr
110+
return cmd.Run()
111+
}
112+
113+
// zipDir zips a directory, recursively.
114+
// It accepts a source directory and a destination zip file.
115+
func zipDir(src, dest string) error {
116+
folderName := filepath.Base(src)
117+
118+
file, err := os.Create(dest)
119+
if err != nil {
120+
return err
121+
}
122+
defer file.Close()
123+
124+
w := zip.NewWriter(file)
125+
defer w.Close()
126+
127+
walkFunc := func(path string, info os.FileInfo, err error) error {
128+
if err != nil {
129+
return err
130+
}
131+
if info.IsDir() {
132+
return nil
133+
}
134+
file, err := os.Open(path)
135+
if err != nil {
136+
return err
137+
}
138+
defer file.Close()
139+
140+
relPath := filepath.Join(folderName, strings.TrimPrefix(path, src))
141+
f, err := w.Create(relPath)
142+
if err != nil {
143+
return err
144+
}
145+
146+
_, err = io.Copy(f, file)
147+
return err
148+
}
149+
150+
return filepath.Walk(src, walkFunc)
151+
}
152+
153+
func replaceDirContents(target string, replacements map[string]string) error {
154+
walkFunc := func(path string, info os.FileInfo, err error) error {
155+
if err != nil {
156+
return err
157+
}
158+
if info.IsDir() || !info.Mode().IsRegular() {
159+
return nil
160+
}
161+
162+
file, err := os.OpenFile(path, os.O_RDWR, 0666)
163+
if err != nil {
164+
return err
165+
}
166+
defer file.Close()
167+
168+
contents, ioErr := ioutil.ReadAll(file)
169+
if ioErr != nil {
170+
return ioErr
171+
}
172+
173+
replaced := false
174+
for oldContent, newContent := range replacements {
175+
newContents := bytes.ReplaceAll(contents, []byte(oldContent), []byte(newContent))
176+
if len(newContents) > 0 {
177+
replaced = true
178+
contents = newContents[0:]
179+
}
180+
}
181+
182+
if replaced {
183+
file.Truncate(0)
184+
file.Seek(0, 0)
185+
_, err = file.Write(contents)
186+
return err
187+
}
188+
189+
return nil
190+
}
191+
192+
return filepath.Walk(target, walkFunc)
193+
}

iris.go

+32-11
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
requestLogger "github.com/kataras/iris/middleware/logger"
2323
"github.com/kataras/iris/middleware/recover"
2424
"github.com/kataras/iris/view"
25+
"github.com/kataras/pio"
2526

2627
"github.com/kataras/golog"
2728
"github.com/kataras/tunnel"
@@ -31,18 +32,38 @@ import (
3132
const Version = "stale"
3233

3334
func init() {
34-
golog.Fatal(`You have installed an invalid version. Install with:
35-
go get -u github.com/kataras/iris/v12@latest
36-
37-
If your Open Source project depends on that pre-go1.9 version please open an issue
38-
at https://github.com/kataras/iris/issues/new and share your repository with us,
39-
we will upgrade your project's code base to the latest version for free.
40-
41-
If you have a commercial project that you cannot share publically, please contact with
42-
@kataras at https://chat.iris-go.com. Assistance will be provided to you and your colleagues
43-
for free.
44-
`)
35+
fmt.Println(`You have installed an invalid version. Install with:
36+
go get -u github.com/kataras/iris/v12@latest
37+
38+
If your Open Source project depends on that pre-go1.9 version please open an issue
39+
at https://github.com/kataras/iris/issues/new and share your repository with us,
40+
we will upgrade your project's code base to the latest version for free.
41+
42+
If you have a commercial project that you cannot share publically, please contact with
43+
@kataras at https://chat.iris-go.com. Assistance will be provided to you and your colleagues
44+
for free.
45+
`)
46+
47+
fmt.Print("Run ")
48+
pio.WriteRich(os.Stdout, "autofix", pio.Green, pio.Underline)
49+
fmt.Print("? (Y/n): ")
50+
var input string
51+
_, err := fmt.Scanln(&input)
52+
if err != nil {
53+
golog.Fatalf("can not take input from user: %v", err)
54+
}
55+
input = strings.ToLower(input)
56+
if input == "" || input == "y" {
57+
err := tryFix()
58+
if err != nil {
59+
golog.Fatalf("autofix: %v", err)
60+
}
4561

62+
golog.Infof("OK. Restart the application manually now.")
63+
os.Exit(0)
64+
} else {
65+
os.Exit(-1)
66+
}
4667
}
4768

4869
// Byte unit helpers.

0 commit comments

Comments
 (0)