/
main.go
176 lines (146 loc) · 4.54 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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
if len(os.Args) != 2 {
return fmt.Errorf("exactly one directory name must be provided to run post-processing on")
}
dirPath := os.Args[1]
log.Printf("running post processing for Go SDK on directory %v", dirPath)
files = make(map[string]fs.FileInfo)
err := filepath.Walk(dirPath, walkFiles)
if err != nil {
return fmt.Errorf("could not read directory %v: %v", dirPath, err)
}
for path, file := range files {
fileBytes, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("could not read file %v: %v", file.Name(), err)
}
fileContents := string(fileBytes)
if len(fileContents) < 1 {
return fmt.Errorf("input file %v must not be empty", file.Name())
}
fileContents = fixKiotaNonDeterminism(fileContents, file.Name())
err = os.WriteFile(path, []byte(fileContents), 0600)
if err != nil {
return err
}
}
err = os.Remove(filepath.Join(dirPath, "go.mod"))
if err != nil {
return fmt.Errorf("could not remove go.mod file: %v", err)
}
err = os.Remove(filepath.Join(dirPath, "go.sum"))
if err != nil {
return fmt.Errorf("could not remove go.sum file: %v", err)
}
cmd := exec.Command("go", "mod", "init", "github.com/octokit/go-sdk")
cmd.Dir = dirPath
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err = cmd.Run()
_ = stdout.String()
stderrOutput := stderr.String()
if err != nil {
return fmt.Errorf("could not initialize Go module: %v\nfull error log:\n%s", err, stderrOutput)
}
cmd = exec.Command("kiota", "info", "-l", "Go", "--json")
cmd.Dir = dirPath
stdout.Reset()
stderr.Reset()
output, err := cmd.Output()
if err != nil {
fmt.Printf("could not run kiota info: %v\nfull error log:\n%s", err, stderr.String())
}
// parse the json returned by kiota info, extract the "dependencies" field,
// and construct a "go get" command for each with the "name" and "version" subfields used.
// this code could result in exceptions if the format of the command isn't changed.
// if/when we know for sure that it's stabilized, this can be refactored to use structs
// and not all of these interface{}s and sleazy casting
var infoResult map[string]interface{}
if err := json.Unmarshal(output, &infoResult); err != nil {
return fmt.Errorf("could not parse kiota info output: %v", err)
}
deps := infoResult["dependencies"].([]interface{})
depsToInstall := make([]string, len(deps))
for i, d := range deps {
dep := d.(map[string]interface{})
name := dep["name"].(string)
version := dep["version"].(string)
fullDep := fmt.Sprintf("%s@%s", name, version)
depsToInstall[i] = fullDep
}
for _, dep := range depsToInstall {
cmd = exec.Command("go", "get", dep)
cmd.Dir = dirPath
_, err := cmd.Output()
if err != nil {
return fmt.Errorf("could not get dependencies: %v", err)
}
fmt.Printf("installed dependency %s\n", dep)
}
cmd = exec.Command("go", "mod", "tidy")
cmd.Dir = dirPath
stdout.Reset()
stderr.Reset()
_, err = cmd.Output()
if err != nil {
fmt.Printf("could not run go mod tidy: %v\nfull error log:\n%s", err, stderr.String())
}
return nil
}
// TODO(kfcampbell) fix this sleazy global
var files map[string]fs.FileInfo
func walkFiles(path string, info fs.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error walking files: %v", err)
}
// skip the .git directory
if strings.Contains(path, ".git") {
return nil
}
// skip other non-dot directories
if info.IsDir() {
return nil
}
files[path] = info
return nil
}
// see https://github.com/microsoft/kiota/issues/3700
func fixKiotaNonDeterminism(inputFile string, fileName string) string {
if !strings.Contains(fileName, "item_starred_repository.go") {
return inputFile
}
// the extra space at the top of this string is because the Kiota
// generation leaves it in, but saving this file causes the Go formatter
// to remove the space and then the string won't match
toReplace := `// ItemStarredRepositoryable` + " " + `
type ItemStarredRepositoryable interface {
IAdditionalDataHolder
}`
replaceWith := `// ItemStarredRepositoryable
type ItemStarredRepositoryable interface {
i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder
i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable
}`
if strings.Contains(inputFile, toReplace) {
inputFile = strings.ReplaceAll(inputFile, toReplace, replaceWith)
}
return inputFile
}