Skip to content

Commit f7370cf

Browse files
committed
Merge branch 'junit-output'
2 parents 5cdb318 + 94d0850 commit f7370cf

File tree

8 files changed

+421
-181
lines changed

8 files changed

+421
-181
lines changed

command/import.go

Lines changed: 128 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import (
55
"fmt"
66
"io/ioutil"
77
"os"
8+
"os/signal"
89
"os/user"
910
"path/filepath"
1011
"strings"
12+
"syscall"
13+
"time"
1114

1215
. "github.com/ForceCLI/force/error"
1316
"github.com/ForceCLI/force/lib"
@@ -16,19 +19,26 @@ import (
1619
)
1720

1821
func init() {
22+
importCmd.Flags().BoolP("rollbackonerror", "r", false, "roll back deployment on error")
23+
importCmd.Flags().BoolP("runalltests", "t", false, "run all tests (equivalent to --testlevel RunAllTestsInOrg)")
24+
importCmd.Flags().StringP("testlevel", "l", "NoTestRun", "test level")
25+
importCmd.Flags().BoolP("checkonly", "c", false, "check only deploy")
26+
importCmd.Flags().BoolP("purgeondelete", "p", false, "purge metadata from org on delete")
27+
importCmd.Flags().BoolP("allowmissingfiles", "m", false, "set allow missing files")
28+
importCmd.Flags().BoolP("autoupdatepackage", "u", false, "set auto update package")
29+
importCmd.Flags().BoolP("ignorewarnings", "i", false, "ignore warnings")
30+
31+
importCmd.Flags().StringSliceVarP(&testsToRun, "test", "", []string{}, "Test(s) to run")
32+
33+
importCmd.Flags().BoolVarP(&ignoreCodeCoverageWarnings, "ignorecoverage", "w", false, "suppress code coverage warnings")
34+
importCmd.Flags().BoolVarP(&exitCodeOnTestFailure, "erroronfailure", "E", true, "exit with an error code if any tests fail")
35+
importCmd.Flags().BoolVarP(&suppressUnexpectedError, "suppressunexpected", "U", true, `suppress "An unexpected error occurred" messages`)
36+
importCmd.Flags().StringP("directory", "d", "src", "relative path to package.xml")
37+
1938
importCmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "only output failures")
2039
importCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "give more verbose output")
21-
importCmd.Flags().BoolVarP(&rollBackOnErrorFlag, "rollbackonerror", "r", false, "set roll back on error")
22-
importCmd.Flags().BoolVarP(&runAllTestsFlag, "runalltests", "t", false, "set run all tests")
23-
importCmd.Flags().StringVarP(&testLevelFlag, "testLevel", "l", "NoTestRun", "set test level")
24-
importCmd.Flags().BoolVarP(&checkOnlyFlag, "checkonly", "c", false, "set check only")
25-
importCmd.Flags().BoolVarP(&purgeOnDeleteFlag, "purgeondelete", "p", false, "set purge on delete")
26-
importCmd.Flags().BoolVarP(&allowMissingFilesFlag, "allowmissingfiles", "m", false, "set allow missing files")
27-
importCmd.Flags().BoolVarP(&autoUpdatePackageFlag, "autoupdatepackage", "u", false, "set auto update package")
28-
importCmd.Flags().BoolVarP(&ignoreWarningsFlag, "ignorewarnings", "i", false, "set ignore warnings")
29-
importCmd.Flags().BoolVarP(&ignoreCodeCoverageWarnings, "ignorecoverage", "w", false, "suppress code coverage warnings")
30-
importCmd.Flags().StringVarP(&directory, "directory", "d", "metadata", "relative path to package.xml")
31-
importCmd.Flags().StringSliceVarP(&testsToRun, "test", "", []string{}, "Test(s) to run")
40+
41+
importCmd.Flags().StringP("reporttype", "f", "text", "report type format (text or junit)")
3242
RootCmd.AddCommand(importCmd)
3343
}
3444

@@ -41,31 +51,29 @@ var importCmd = &cobra.Command{
4151
force import -checkonly -runalltests
4252
`,
4353
Run: func(cmd *cobra.Command, args []string) {
44-
runImport()
54+
options := getDeploymentOptions(cmd)
55+
srcDir := sourceDir(cmd)
56+
reportFormat, _ := cmd.Flags().GetString("reporttype")
57+
runImport(srcDir, options, reportFormat)
4558
},
4659
Args: cobra.MaximumNArgs(0),
4760
}
4861

4962
var (
5063
testsToRun metaName
51-
rollBackOnErrorFlag bool
52-
runAllTestsFlag bool
53-
testLevelFlag string
54-
checkOnlyFlag bool
55-
purgeOnDeleteFlag bool
56-
allowMissingFilesFlag bool
57-
autoUpdatePackageFlag bool
58-
ignoreWarningsFlag bool
59-
ignoreCodeCoverageWarnings bool
60-
directory string
61-
verbose bool
6264
quiet bool
65+
verbose bool
66+
exitCodeOnTestFailure bool
67+
suppressUnexpectedError bool
68+
ignoreCodeCoverageWarnings bool
6369
)
6470

65-
func runImport() {
71+
func sourceDir(cmd *cobra.Command) string {
72+
directory, _ := cmd.Flags().GetString("directory")
73+
6674
wd, _ := os.Getwd()
67-
usr, err := user.Current()
6875
var dir string
76+
usr, err := user.Current()
6977

7078
//Manually handle shell expansion short cut
7179
if err != nil {
@@ -88,13 +96,16 @@ func runImport() {
8896
if filepath.IsAbs(dir) {
8997
root = dir
9098
}
99+
return root
100+
}
91101

102+
func runImport(root string, options ForceDeployOptions, reportFormat string) {
92103
files := make(ForceMetadataFiles)
93104
if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
94105
ErrorAndExit(" \n" + filepath.Join(root, "package.xml") + "\ndoes not exist")
95106
}
96107

97-
err = filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
108+
err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
98109
if f.Mode().IsRegular() {
99110
if f.Name() != ".DS_Store" {
100111
data, err := ioutil.ReadFile(path)
@@ -109,94 +120,118 @@ func runImport() {
109120
if err != nil {
110121
ErrorAndExit(err.Error())
111122
}
112-
var DeploymentOptions ForceDeployOptions
113-
DeploymentOptions.AllowMissingFiles = allowMissingFilesFlag
114-
DeploymentOptions.AutoUpdatePackage = autoUpdatePackageFlag
115-
DeploymentOptions.CheckOnly = checkOnlyFlag
116-
DeploymentOptions.IgnoreWarnings = ignoreWarningsFlag
117-
DeploymentOptions.PurgeOnDelete = purgeOnDeleteFlag
118-
DeploymentOptions.RollbackOnError = rollBackOnErrorFlag
119-
DeploymentOptions.TestLevel = testLevelFlag
120-
if runAllTestsFlag {
121-
DeploymentOptions.TestLevel = "RunAllTestsInOrg"
122-
}
123-
DeploymentOptions.RunTests = testsToRun
124123

125124
if quiet {
126125
var l quietLogger
127126
lib.Log = l
128127
}
129-
result, err := force.Metadata.Deploy(files, DeploymentOptions)
130-
problems := result.Details.ComponentFailures
131-
successes := result.Details.ComponentSuccesses
132-
testFailures := result.Details.RunTestResult.TestFailures
133-
testSuccesses := result.Details.RunTestResult.TestSuccesses
134-
codeCoverageWarnings := result.Details.RunTestResult.CodeCoverageWarnings
128+
129+
startTime := time.Now()
130+
deployId, err := force.Metadata.StartDeploy(files, options)
135131
if err != nil {
136132
ErrorAndExit(err.Error())
137133
}
134+
stopDeployUponSignal(force, deployId)
135+
var result ForceCheckDeploymentStatusResult
136+
for {
137+
result, err = force.Metadata.CheckDeployStatus(deployId)
138+
if err != nil {
139+
ErrorAndExit(err.Error())
140+
}
141+
if result.Done {
142+
break
143+
}
144+
Log.Info(result)
145+
time.Sleep(5000 * time.Millisecond)
146+
}
147+
endTime := time.Now()
148+
duration := endTime.Sub(startTime)
138149

139-
if !quiet {
140-
fmt.Printf("\nSuccesses - %d\n", len(successes))
141-
if verbose {
142-
for _, success := range successes {
143-
if success.FullName != "package.xml" {
144-
verb := "unchanged"
145-
if success.Changed {
146-
verb = "changed"
147-
} else if success.Deleted {
148-
verb = "deleted"
149-
} else if success.Created {
150-
verb = "created"
151-
}
152-
fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
153-
}
150+
junitOutput := reportFormat == "junit"
151+
152+
if suppressUnexpectedError {
153+
filteredComponentFailures := result.Details.ComponentFailures[:0]
154+
for _, f := range result.Details.ComponentFailures {
155+
if !strings.HasPrefix(f.Problem, `An unexpected error occurred. Please include this ErrorId`) {
156+
filteredComponentFailures = append(filteredComponentFailures, f)
154157
}
155158
}
159+
result.Details.ComponentFailures = filteredComponentFailures
156160
}
157161

158-
if !quiet {
159-
fmt.Printf("\nTest Successes - %d\n", len(testSuccesses))
160-
for _, failure := range testSuccesses {
161-
fmt.Printf(" [PASS] %s::%s\n", failure.Name, failure.MethodName)
162+
switch {
163+
case quiet:
164+
case junitOutput:
165+
output, err := result.ToJunit(duration.Seconds())
166+
if err != nil {
167+
ErrorAndExit(err.Error())
168+
}
169+
fmt.Println(output)
170+
if result.HasComponentFailures() || (result.HasTestFailures() && exitCodeOnTestFailure) || !result.Success {
171+
os.Exit(1)
172+
}
173+
return
174+
default:
175+
output := result.ToString(duration.Seconds(), verbose)
176+
fmt.Println(output)
177+
178+
codeCoverageWarnings := result.Details.RunTestResult.CodeCoverageWarnings
179+
if !ignoreCodeCoverageWarnings && len(codeCoverageWarnings) > 0 {
180+
fmt.Printf("\nCode Coverage Warnings - %d\n", len(codeCoverageWarnings))
181+
for _, warning := range codeCoverageWarnings {
182+
fmt.Printf("\n %s: %s\n", warning.Name, warning.Message)
183+
}
162184
}
163-
}
164185

165-
fmt.Printf("\nFailures - %d\n", len(problems))
166-
for _, problem := range problems {
167-
if problem.FullName == "" {
168-
fmt.Println(problem.Problem)
169-
} else {
170-
fmt.Printf("\"%s\", line %d: %s %s\n", problem.FullName, problem.LineNumber, problem.ProblemType, problem.Problem)
186+
if result.HasComponentFailures() {
187+
err = errors.New("Some components failed deployment")
188+
} else if result.HasTestFailures() && exitCodeOnTestFailure {
189+
err = errors.New("Some tests failed")
190+
} else if !result.Success {
191+
err = errors.New(fmt.Sprintf("Status: %s, Status Code: %s, Error Message: %s", result.Status, result.ErrorStatusCode, result.ErrorMessage))
192+
}
193+
if err != nil {
194+
ErrorAndExit(err.Error())
195+
}
196+
if !quiet {
197+
fmt.Printf("Imported from %s\n", root)
171198
}
172199
}
200+
}
173201

174-
fmt.Printf("\nTest Failures - %d\n", len(testFailures))
175-
for _, failure := range testFailures {
176-
fmt.Printf("\n [FAIL] %s::%s: %s\n", failure.Name, failure.MethodName, failure.Message)
177-
fmt.Println(failure.StackTrace)
202+
func getDeploymentOptions(cmd *cobra.Command) ForceDeployOptions {
203+
var deploymentOptions ForceDeployOptions
204+
deploymentOptions.AllowMissingFiles, _ = cmd.Flags().GetBool("allowmissingfiles")
205+
deploymentOptions.AutoUpdatePackage, _ = cmd.Flags().GetBool("autoupdatepackage")
206+
deploymentOptions.CheckOnly, _ = cmd.Flags().GetBool("checkonly")
207+
deploymentOptions.IgnoreWarnings, _ = cmd.Flags().GetBool("ignorewarnings")
208+
deploymentOptions.PurgeOnDelete, _ = cmd.Flags().GetBool("purgeondelete")
209+
deploymentOptions.RollbackOnError, _ = cmd.Flags().GetBool("rollbackonerror")
210+
deploymentOptions.TestLevel, _ = cmd.Flags().GetString("testlevel")
211+
runAllTests, _ := cmd.Flags().GetBool("runalltests")
212+
if runAllTests {
213+
deploymentOptions.TestLevel = "RunAllTestsInOrg"
178214
}
215+
deploymentOptions.RunTests = testsToRun
216+
return deploymentOptions
217+
}
179218

180-
if !ignoreCodeCoverageWarnings && len(codeCoverageWarnings) > 0 {
181-
fmt.Printf("\nCode Coverage Warnings - %d\n", len(codeCoverageWarnings))
182-
for _, warning := range codeCoverageWarnings {
183-
fmt.Printf("\n %s: %s\n", warning.Name, warning.Message)
184-
}
185-
}
219+
func stopDeployUponSignal(force *Force, deployId string) {
220+
sigs := make(chan os.Signal, 1)
221+
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
186222

187-
if len(problems) > 0 {
188-
err = errors.New("Some components failed deployment")
189-
} else if len(testFailures) > 0 {
190-
err = errors.New("Some tests failed")
191-
} else if !result.Success {
192-
err = errors.New(fmt.Sprintf("Status: %s, Status Code: %s, Error Message: %s", result.Status, result.ErrorStatusCode, result.ErrorMessage))
193-
}
194-
if err != nil {
195-
ErrorAndExit(err.Error())
196-
}
197-
if !quiet {
198-
fmt.Printf("Imported from %s\n", root)
199-
}
223+
go func() {
224+
interuptsReceived := 0
225+
for {
226+
<-sigs
227+
if interuptsReceived > 0 {
228+
os.Exit(1)
229+
}
230+
fmt.Fprintf(os.Stderr, "Cancelling deploy %s\n", deployId)
231+
force.Metadata.CancelDeploy(deployId)
232+
interuptsReceived++
233+
}
234+
}()
200235
}
201236

202237
type quietLogger struct{}

0 commit comments

Comments
 (0)