Skip to content

Commit 5bd3c5e

Browse files
authored
Merge pull request #486 from fujiwara/diff-exit-code
Add diff --exit-code
2 parents 315c90d + 93a4ede commit 5bd3c5e

File tree

5 files changed

+110
-25
lines changed

5 files changed

+110
-25
lines changed

cli.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func ParseCLI(args []string) (string, *CLIOptions, func(), error) {
122122
func CLI(ctx context.Context, parse CLIParseFunc) (int, error) {
123123
sub, opts, usage, err := parse(os.Args[1:])
124124
if err != nil {
125-
return 1, err
125+
return extractExitCodeAndError(err)
126126
}
127127

128128
color.NoColor = !opts.Color
@@ -143,10 +143,8 @@ func CLI(ctx context.Context, parse CLIParseFunc) (int, error) {
143143
}
144144
log.SetOutput(filter)
145145

146-
if err := dispatchCLI(ctx, sub, usage, opts); err != nil {
147-
return 1, err
148-
}
149-
return 0, nil
146+
err = dispatchCLI(ctx, sub, usage, opts)
147+
return extractExitCodeAndError(err)
150148
}
151149

152150
func dispatchCLI(ctx context.Context, sub string, usage func(), opts *CLIOptions) error {

diff.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type DiffOption struct {
2727
Qualifier *string `help:"the qualifier to compare"`
2828
FunctionURL string `help:"path to function-url definition" default:"" env:"LAMBROLL_FUNCTION_URL"`
2929
Ignore string `help:"ignore diff by jq query" default:""`
30+
ExitCode bool `help:"exit with code 2 if there are differences" default:"false"`
3031

3132
ZipOption
3233
}
@@ -93,6 +94,7 @@ func (app *App) Diff(ctx context.Context, opt *DiffOption) error {
9394
remoteJSON, _ := marshalAny(remoteFunc)
9495
newJSON, _ := marshalAny(newFunc)
9596
remoteArn := fullQualifiedFunctionName(app.functionArn(ctx, name), opt.Qualifier)
97+
hasDiff := false
9698

9799
if diff, err := jsondiff.Diff(
98100
&jsondiff.Input{Name: remoteArn, X: remoteJSON},
@@ -101,6 +103,7 @@ func (app *App) Diff(ctx context.Context, opt *DiffOption) error {
101103
); err != nil {
102104
return fmt.Errorf("failed to diff: %w", err)
103105
} else if diff != "" {
106+
hasDiff = true
104107
fmt.Print(coloredDiff(diff))
105108
}
106109

@@ -126,25 +129,33 @@ func (app *App) Diff(ctx context.Context, opt *DiffOption) error {
126129
fmt.Println(color.RedString("---" + app.functionArn(ctx, name)))
127130
fmt.Println(color.GreenString("+++" + "--src=" + opt.Src))
128131
fmt.Println(coloredDiff(ds))
132+
hasDiff = true
129133
}
130134
}
131135

132-
if opt.FunctionURL == "" {
133-
return nil
136+
if opt.FunctionURL != "" {
137+
if d, err := app.diffFunctionURL(ctx, name, opt); err != nil {
138+
return err
139+
} else if d {
140+
hasDiff = true
141+
}
134142
}
135143

136-
if err := app.diffFunctionURL(ctx, name, opt); err != nil {
137-
return err
144+
if hasDiff && opt.ExitCode {
145+
// exit with code 2 if there are differences
146+
// but actually, it's not an error
147+
return ErrDiff
138148
}
139149
return nil
140150
}
141151

142-
func (app *App) diffFunctionURL(ctx context.Context, name string, opt *DiffOption) error {
152+
func (app *App) diffFunctionURL(ctx context.Context, name string, opt *DiffOption) (bool, error) {
143153
var remote, local *types.FunctionUrlConfig
154+
var hasDiff bool
144155

145156
fu, err := app.loadFunctionUrl(opt.FunctionURL, name)
146157
if err != nil {
147-
return fmt.Errorf("failed to load function-url: %w", err)
158+
return hasDiff, fmt.Errorf("failed to load function-url: %w", err)
148159
} else {
149160
fillDefaultValuesFunctionUrlConfig(fu.Config)
150161
local = &types.FunctionUrlConfig{
@@ -170,7 +181,7 @@ func (app *App) diffFunctionURL(ctx context.Context, name string, opt *DiffOptio
170181
// empty
171182
remote = &types.FunctionUrlConfig{}
172183
} else {
173-
return fmt.Errorf("failed to get function url config: %w", err)
184+
return hasDiff, fmt.Errorf("failed to get function url config: %w", err)
174185
}
175186
} else {
176187
log.Println("[debug] FunctionUrlConfig found")
@@ -187,15 +198,16 @@ func (app *App) diffFunctionURL(ctx context.Context, name string, opt *DiffOptio
187198
&jsondiff.Input{Name: fqName, X: r},
188199
&jsondiff.Input{Name: opt.FunctionURL, X: l},
189200
); err != nil {
190-
return fmt.Errorf("failed to diff: %w", err)
201+
return hasDiff, fmt.Errorf("failed to diff: %w", err)
191202
} else if diff != "" {
203+
hasDiff = true
192204
fmt.Print(coloredDiff(diff))
193205
}
194206

195207
// permissions
196208
adds, removes, err := app.calcFunctionURLPermissionsDiff(ctx, fu)
197209
if err != nil {
198-
return err
210+
return hasDiff, err
199211
}
200212
var addsB []byte
201213
for _, in := range adds {
@@ -211,9 +223,10 @@ func (app *App) diffFunctionURL(ctx context.Context, name string, opt *DiffOptio
211223
fmt.Println(color.RedString("--- permissions"))
212224
fmt.Println(color.GreenString("+++ permissions"))
213225
fmt.Print(coloredDiff(ds))
226+
hasDiff = true
214227
}
215228

216-
return nil
229+
return hasDiff, nil
217230
}
218231

219232
func coloredDiff(src string) string {

error.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package lambroll
2+
3+
import "errors"
4+
5+
type ExitCode int
6+
7+
const (
8+
ExitCodeOK = ExitCode(0)
9+
ExitCodeGeneralError = ExitCode(1)
10+
ExitCodeDiffFound = ExitCode(2)
11+
)
12+
13+
var ErrDiff = &ExitError{Code: ExitCodeDiffFound, Err: nil}
14+
15+
type ExitError struct {
16+
Code ExitCode
17+
Err error
18+
}
19+
20+
func (e *ExitError) Error() string {
21+
if e.Err == nil {
22+
return ""
23+
}
24+
return e.Err.Error()
25+
}
26+
27+
func (e *ExitError) Unwrap() error {
28+
return nil
29+
}
30+
31+
// ExtractExitCode extracts exit code from error.
32+
func extractExitCodeAndError(err error) (int, error) {
33+
if err == nil {
34+
return int(ExitCodeOK), nil
35+
}
36+
var e *ExitError
37+
if errors.As(err, &e) {
38+
return int(e.Code), e.Err
39+
}
40+
return int(ExitCodeGeneralError), err
41+
}

error_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package lambroll_test
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/fujiwara/lambroll"
8+
)
9+
10+
var errGeneral = errors.New("error")
11+
12+
var ExitErrorTests = []struct {
13+
err error
14+
code int
15+
extractedError error
16+
}{
17+
{lambroll.ErrDiff, 2, nil},
18+
{errGeneral, 1, errGeneral},
19+
{nil, 0, nil},
20+
}
21+
22+
func TestExitError(t *testing.T) {
23+
for _, tt := range ExitErrorTests {
24+
code, err := lambroll.ExtractExitCodeAndError(tt.err)
25+
if code != tt.code {
26+
t.Errorf("ExtractExitCode(%v) => %d, want %d", tt.err, code, tt.code)
27+
}
28+
if err != nil && tt.extractedError != nil && err != tt.extractedError {
29+
t.Errorf("ExtractExitCode(%v) => %v, want %v", tt.err, err, tt.extractedError)
30+
}
31+
}
32+
}

export_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ import (
66
)
77

88
var (
9-
CreateZipArchive = createZipArchive
10-
ExpandExcludeFile = expandExcludeFile
11-
LoadZipArchive = loadZipArchive
12-
MergeTags = mergeTags
13-
FillDefaultValues = fillDefaultValues
14-
JSONStr = jsonStr
15-
MarshalJSON = marshalJSON
16-
NewFunctionFrom = newFunctionFrom
17-
NewCallerIdentity = newCallerIdentity
18-
Unzip = unzip
9+
CreateZipArchive = createZipArchive
10+
ExpandExcludeFile = expandExcludeFile
11+
LoadZipArchive = loadZipArchive
12+
MergeTags = mergeTags
13+
FillDefaultValues = fillDefaultValues
14+
JSONStr = jsonStr
15+
MarshalJSON = marshalJSON
16+
NewFunctionFrom = newFunctionFrom
17+
NewCallerIdentity = newCallerIdentity
18+
Unzip = unzip
19+
ExtractExitCodeAndError = extractExitCodeAndError
1920
)
2021

2122
type VersionsOutput = versionsOutput

0 commit comments

Comments
 (0)