-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
158 lines (129 loc) · 3.45 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/kelseyhightower/envconfig"
)
var VERSION string
var jsRegex = regexp.MustCompile(`\.js$`)
var newLineRegex = regexp.MustCompile(`\n`)
type config struct {
Branch string `required:"true"`
Dir string `default:"./"`
Token string `required:"true"`
ApiURL string `default:"https://screeps.com/api/user/code"`
Timeout time.Duration `default:"10s"`
// TODO idea: add an option to automatically switch to the branch - POST /api/user/set-active-branch
}
func main() {
var c config
err := envconfig.Process("INPUT", &c)
if err != nil {
log.Println(err)
os.Exit(1)
}
payload, err := preparePayload(c)
if err != nil {
log.Println(err)
os.Exit(1)
}
err = send(payload, c)
if err != nil {
log.Println(err)
os.Exit(1)
}
}
func preparePayload(c config) (io.Reader, error) {
fr := filesReader{modules: make(map[string]string)}
err := filepath.Walk(c.Dir, fr.read)
if err != nil {
return nil, fmt.Errorf("unable to walk files: %w", err)
}
return fr.ToJson(c.Branch), nil
}
type filesReader struct {
modules map[string]string
}
func (r *filesReader) read(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if !jsRegex.MatchString(path) {
return nil
}
log.Println("Adding:", path)
b, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("unable to read file [%s]: %w", path, err)
}
fileContent := string(b)
fileContent = newLineRegex.ReplaceAllString(fileContent, `\n`)
fileContent = strings.ReplaceAll(fileContent, `"`, `\"`)
module := jsRegex.ReplaceAllString(path, "")
r.modules[module] = fileContent
return nil
}
func (r *filesReader) ToJson(branch string) io.Reader {
sb := strings.Builder{}
sb.WriteString("{")
sb.WriteString(`"branch":"`)
sb.WriteString(branch)
sb.WriteString(`","modules":{`)
for module, fileContent := range r.modules {
sb.WriteString(`"`)
sb.WriteString(module)
sb.WriteString(`":"`)
sb.WriteString(fileContent)
sb.WriteString(`", `)
}
sb.WriteString(`"empty":""`) // just a lazy way of not dealing with dangling comma
sb.WriteString("}}")
return bytes.NewBufferString(sb.String())
}
func send(payload io.Reader, c config) error {
client := &http.Client{
Timeout: c.Timeout,
}
req, err := prepareRequest(payload, c.ApiURL, c.Token)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("unable to send the code: %w", err)
}
if resp.StatusCode >= http.StatusBadRequest {
respBody, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("unable to send the code: Status: %d, Response: %s", resp.StatusCode, respBody)
}
var errBody errorBody
_ = json.NewDecoder(resp.Body).Decode(&errBody)
if errBody.Error != "" {
return fmt.Errorf("API returned an error: Status: %d, Response: %s", resp.StatusCode, errBody.Error)
}
log.Println("Code pushed successfully")
return nil
}
type errorBody struct {
Error string `json:"error"`
}
func prepareRequest(payload io.Reader, apiURL, token string) (*http.Request, error) {
req, err := http.NewRequest(http.MethodPost, apiURL, payload)
if err != nil {
return nil, fmt.Errorf("unable to prepare request: %w", err)
}
req.Header.Add("Content-Type", "application/json; charset=utf-8")
req.Header.Add("X-Token", token)
req.Header.Add("User-Agent", "screeps-pusher/"+VERSION)
return req, nil
}