@@ -5,9 +5,12 @@ import (
5
5
"fmt"
6
6
"io/ioutil"
7
7
"os"
8
+ "os/signal"
8
9
"os/user"
9
10
"path/filepath"
10
11
"strings"
12
+ "syscall"
13
+ "time"
11
14
12
15
. "github.com/ForceCLI/force/error"
13
16
"github.com/ForceCLI/force/lib"
@@ -16,19 +19,26 @@ import (
16
19
)
17
20
18
21
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
+
19
38
importCmd .Flags ().BoolVarP (& quiet , "quiet" , "q" , false , "only output failures" )
20
39
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)" )
32
42
RootCmd .AddCommand (importCmd )
33
43
}
34
44
@@ -41,31 +51,29 @@ var importCmd = &cobra.Command{
41
51
force import -checkonly -runalltests
42
52
` ,
43
53
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 )
45
58
},
46
59
Args : cobra .MaximumNArgs (0 ),
47
60
}
48
61
49
62
var (
50
63
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
62
64
quiet bool
65
+ verbose bool
66
+ exitCodeOnTestFailure bool
67
+ suppressUnexpectedError bool
68
+ ignoreCodeCoverageWarnings bool
63
69
)
64
70
65
- func runImport () {
71
+ func sourceDir (cmd * cobra.Command ) string {
72
+ directory , _ := cmd .Flags ().GetString ("directory" )
73
+
66
74
wd , _ := os .Getwd ()
67
- usr , err := user .Current ()
68
75
var dir string
76
+ usr , err := user .Current ()
69
77
70
78
//Manually handle shell expansion short cut
71
79
if err != nil {
@@ -88,13 +96,16 @@ func runImport() {
88
96
if filepath .IsAbs (dir ) {
89
97
root = dir
90
98
}
99
+ return root
100
+ }
91
101
102
+ func runImport (root string , options ForceDeployOptions , reportFormat string ) {
92
103
files := make (ForceMetadataFiles )
93
104
if _ , err := os .Stat (filepath .Join (root , "package.xml" )); os .IsNotExist (err ) {
94
105
ErrorAndExit (" \n " + filepath .Join (root , "package.xml" ) + "\n does not exist" )
95
106
}
96
107
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 {
98
109
if f .Mode ().IsRegular () {
99
110
if f .Name () != ".DS_Store" {
100
111
data , err := ioutil .ReadFile (path )
@@ -109,94 +120,118 @@ func runImport() {
109
120
if err != nil {
110
121
ErrorAndExit (err .Error ())
111
122
}
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
124
123
125
124
if quiet {
126
125
var l quietLogger
127
126
lib .Log = l
128
127
}
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 )
135
131
if err != nil {
136
132
ErrorAndExit (err .Error ())
137
133
}
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 )
138
149
139
- if ! quiet {
140
- fmt .Printf ("\n Successes - %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 \t status: %s\n \t id=%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 )
154
157
}
155
158
}
159
+ result .Details .ComponentFailures = filteredComponentFailures
156
160
}
157
161
158
- if ! quiet {
159
- fmt .Printf ("\n Test 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 ("\n Code Coverage Warnings - %d\n " , len (codeCoverageWarnings ))
181
+ for _ , warning := range codeCoverageWarnings {
182
+ fmt .Printf ("\n %s: %s\n " , warning .Name , warning .Message )
183
+ }
162
184
}
163
- }
164
185
165
- fmt .Printf ("\n Failures - %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 )
171
198
}
172
199
}
200
+ }
173
201
174
- fmt .Printf ("\n Test 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"
178
214
}
215
+ deploymentOptions .RunTests = testsToRun
216
+ return deploymentOptions
217
+ }
179
218
180
- if ! ignoreCodeCoverageWarnings && len (codeCoverageWarnings ) > 0 {
181
- fmt .Printf ("\n Code 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 )
186
222
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
+ }()
200
235
}
201
236
202
237
type quietLogger struct {}
0 commit comments