forked from multimediallc/codeowners-plus
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
185 lines (163 loc) · 5.03 KB
/
Copy pathmain.go
File metadata and controls
185 lines (163 loc) · 5.03 KB
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
177
178
179
180
181
182
183
184
185
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"strconv"
"strings"
"testing"
"github.com/multimediallc/codeowners-plus/internal/app"
)
// Flags holds the command line flags
type Flags struct {
Token *string
ApiUrl *string
RepoDir *string
PR *int
Repo *string
Verbose *bool
Quiet *bool
Workspace *string
}
var (
flags = &Flags{
Token: flag.String("token", getEnv("INPUT_GITHUB-TOKEN", ""), "GitHub authentication token"),
// Fall back to GITHUB_API_URL (always set by the runner) when the input is
// empty — e.g. github-api-url passed an unset expression, or the binary run
// outside the composite action. Without it, a GHE run would silently hit the
// public GitHub API.
ApiUrl: flag.String("api-url", firstNonEmpty(getEnv("INPUT_GITHUB-API-URL", ""), getEnv("GITHUB_API_URL", "")), "GitHub API base URL (for GitHub Enterprise, e.g. https://ghe.example.com/api/v3)"),
RepoDir: flag.String("dir", getEnv("GITHUB_WORKSPACE", "/"), "Path to local Git repo"),
PR: flag.Int("pr", ignoreError(strconv.Atoi(getEnv("INPUT_PR", ""))), "Pull Request number"),
Repo: flag.String("repo", getEnv("INPUT_REPOSITORY", ""), "GitHub repo name"),
Verbose: flag.Bool("v", ignoreError(strconv.ParseBool(getEnv("INPUT_VERBOSE", "0"))), "Verbose output"),
Quiet: flag.Bool("quiet", ignoreError(strconv.ParseBool(getEnv("INPUT_QUIET", "0"))), "Disable PR comments and review requests"),
// -dir cannot stand in for this: it falls back to "/", which would put every absolute path inside the checkout.
Workspace: flag.String("workspace", getEnv("GITHUB_WORKSPACE", ""), "Path to the checkout, which hook paths may not live under"),
}
WarningBuffer = bytes.NewBuffer([]byte{})
InfoBuffer = bytes.NewBuffer([]byte{})
)
// initFlags initializes and parses command line flags
func initFlags(flags *Flags) error {
// Only parse flags if we're not testing
if !testing.Testing() {
flag.Parse()
}
// Validate required flags
badFlags := make([]string, 0, 4)
if *flags.Token == "" {
badFlags = append(badFlags, "token")
}
if *flags.PR == 0 {
badFlags = append(badFlags, "pr")
}
if *flags.Repo == "" {
badFlags = append(badFlags, "repo")
}
if len(badFlags) > 0 {
return fmt.Errorf("required flags or environment variables not set: %s", badFlags)
}
return nil
}
// Helper functions
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}
// firstNonEmpty is used to chain env var fallbacks where an empty value means
// "not set" (the composite action exports INPUT_* vars even when blank).
func firstNonEmpty(values ...string) string {
for _, v := range values {
if strings.TrimSpace(v) != "" {
return v
}
}
return ""
}
func ignoreError[V any, E error](res V, _ E) V {
return res
}
func outputAndExit(w io.Writer, shouldFail bool, message string) {
_, err := WarningBuffer.WriteTo(w)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing warning buffer: %v\n", err)
}
if *flags.Verbose {
_, err := InfoBuffer.WriteTo(w)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing info buffer: %v\n", err)
}
}
_, _ = fmt.Fprint(w, message)
if testing.Testing() {
return
}
if shouldFail {
os.Exit(1)
} else {
os.Exit(0)
}
}
// writeGITHUBOUTPUT writes the OutputData to the GITHUB_OUTPUT file in the correct format
func writeGITHUBOUTPUT(outputData *app.OutputData) error {
githubOutput := os.Getenv("GITHUB_OUTPUT")
if githubOutput == "" {
return nil // No GITHUB_OUTPUT environment variable set
}
// Marshal the entire OutputData to JSON
jsonData, err := json.Marshal(outputData)
if err != nil {
return fmt.Errorf("error marshaling JSON output: %w", err)
}
// Use GitHub Actions delimiter approach for robust handling of special characters
output := fmt.Sprintf("data<<EOF\n%s\nEOF\n", string(jsonData))
err = os.WriteFile(githubOutput, []byte(output), 0644)
if err != nil {
return fmt.Errorf("error writing to GITHUB_OUTPUT: %w", err)
}
return nil
}
func main() {
err := initFlags(flags)
if err != nil {
outputAndExit(os.Stderr, true, fmt.Sprintln(err))
}
cfg := app.Config{
Token: *flags.Token,
ApiUrl: *flags.ApiUrl,
RepoDir: *flags.RepoDir,
PR: *flags.PR,
Repo: *flags.Repo,
Verbose: *flags.Verbose,
Quiet: *flags.Quiet,
Workspace: *flags.Workspace,
InfoBuffer: InfoBuffer,
WarningBuffer: WarningBuffer,
}
app, err := app.New(cfg)
if err != nil {
outputAndExit(os.Stderr, true, fmt.Sprintf("Failed to initialize app: %v\n", err))
}
outputData, err := app.Run()
if err != nil {
outputAndExit(os.Stderr, true, fmt.Sprintln(err))
}
// Write JSON output to GITHUB_OUTPUT if the environment variable is set
if err := writeGITHUBOUTPUT(outputData); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
var w io.Writer
if outputData.Success {
w = os.Stdout
} else {
w = os.Stderr
}
shouldFail := !outputData.Success && app.Conf.Enforcement.FailCheck
outputAndExit(w, shouldFail, outputData.Message)
}