@@ -17,6 +17,7 @@ package foreach
17
17
18
18
import (
19
19
"errors"
20
+ "fmt"
20
21
"os"
21
22
"path"
22
23
"strings"
@@ -34,7 +35,15 @@ import (
34
35
var exec executor.Executor = executor .NewRealExecutor ()
35
36
36
37
var (
37
- repoFile string = "repos.txt"
38
+ repoFile = "repos.txt"
39
+
40
+ overallResultsDirectory string
41
+
42
+ successfulResultsDirectory string
43
+ successfulReposFileName string
44
+
45
+ failedResultsDirectory string
46
+ failedReposFileName string
38
47
)
39
48
40
49
func formatArguments (arguments []string ) string {
@@ -49,8 +58,7 @@ func NewForeachCmd() *cobra.Command {
49
58
cmd := & cobra.Command {
50
59
Use : "foreach [flags] -- COMMAND [ARGUMENT...]" ,
51
60
Short : "Run COMMAND against each working copy" ,
52
- Long :
53
- `Run COMMAND against each working copy. Make sure to include a
61
+ Long : `Run COMMAND against each working copy. Make sure to include a
54
62
double hyphen -- with space on both sides before COMMAND, as this
55
63
marks that no further options should be interpreted by turbolift.` ,
56
64
RunE : runE ,
@@ -83,6 +91,10 @@ func runE(c *cobra.Command, args []string) error {
83
91
// the user something they could copy and paste.
84
92
prettyArgs := formatArguments (args )
85
93
94
+ setupOutputFiles (dir .Name , prettyArgs )
95
+
96
+ logger .Printf ("Logs for all executions will be stored under %s" , overallResultsDirectory )
97
+
86
98
var doneCount , skippedCount , errorCount int
87
99
for _ , repo := range dir .Repos {
88
100
repoDirPath := path .Join ("work" , repo .OrgName , repo .RepoName ) // i.e. work/org/repo
@@ -99,9 +111,11 @@ func runE(c *cobra.Command, args []string) error {
99
111
err := exec .Execute (execActivity .Writer (), repoDirPath , args [0 ], args [1 :]... )
100
112
101
113
if err != nil {
114
+ emitOutcomeToFiles (repo , failedReposFileName , failedResultsDirectory , execActivity .Logs (), logger )
102
115
execActivity .EndWithFailure (err )
103
116
errorCount ++
104
117
} else {
118
+ emitOutcomeToFiles (repo , successfulReposFileName , successfulResultsDirectory , execActivity .Logs (), logger )
105
119
execActivity .EndWithSuccessAndEmitLogs ()
106
120
doneCount ++
107
121
}
@@ -113,5 +127,55 @@ func runE(c *cobra.Command, args []string) error {
113
127
logger .Warnf ("turbolift foreach completed with %s %s(%s, %s, %s)\n " , colors .Red ("errors" ), colors .Normal (), colors .Green (doneCount , " OK" ), colors .Yellow (skippedCount , " skipped" ), colors .Red (errorCount , " errored" ))
114
128
}
115
129
130
+ logger .Printf ("Logs for all executions have been stored under %s" , overallResultsDirectory )
131
+ logger .Printf ("Names of successful repos have been written to %s" , successfulReposFileName )
132
+ logger .Printf ("Names of failed repos have been written to %s" , failedReposFileName )
133
+
116
134
return nil
117
135
}
136
+
137
+ // sets up a temporary directory to store success/failure logs etc
138
+ func setupOutputFiles (campaignName string , command string ) {
139
+ overallResultsDirectory , _ = os .MkdirTemp ("" , fmt .Sprintf ("turbolift-foreach-%s-" , campaignName ))
140
+ successfulResultsDirectory = path .Join (overallResultsDirectory , "successful" )
141
+ failedResultsDirectory = path .Join (overallResultsDirectory , "failed" )
142
+ _ = os .MkdirAll (successfulResultsDirectory , 0755 )
143
+ _ = os .MkdirAll (failedResultsDirectory , 0755 )
144
+
145
+ successfulReposFileName = path .Join (successfulResultsDirectory , "repos.txt" )
146
+ failedReposFileName = path .Join (failedResultsDirectory , "repos.txt" )
147
+
148
+ // create the files
149
+ successfulReposFile , _ := os .Create (successfulReposFileName )
150
+ failedReposFile , _ := os .Create (failedReposFileName )
151
+ defer successfulReposFile .Close ()
152
+ defer failedReposFile .Close ()
153
+
154
+ _ , _ = successfulReposFile .WriteString (fmt .Sprintf ("# This file contains the list of repositories that were successfully processed by turbolift foreach\n # for the command: %s\n " , command ))
155
+ _ , _ = failedReposFile .WriteString (fmt .Sprintf ("# This file contains the list of repositories that failed to be processed by turbolift foreach\n # for the command: %s\n " , command ))
156
+ }
157
+
158
+ func emitOutcomeToFiles (repo campaign.Repo , reposFileName string , logsDirectoryParent string , executionLogs string , logger * logging.Logger ) {
159
+ // write the repo name to the repos file
160
+ reposFile , _ := os .OpenFile (reposFileName , os .O_RDWR | os .O_APPEND , 0644 )
161
+ defer reposFile .Close ()
162
+ _ , err := reposFile .WriteString (repo .FullRepoName + "\n " )
163
+ if err != nil {
164
+ logger .Errorf ("Failed to write repo name to %s: %s" , reposFile .Name (), err )
165
+ }
166
+
167
+ // write logs to a file under the logsParent directory, in a directory structure that mirrors that of the work directory
168
+ logsDir := path .Join (logsDirectoryParent , repo .FullRepoName )
169
+ logsFile := path .Join (logsDir , "logs.txt" )
170
+ err = os .MkdirAll (logsDir , 0755 )
171
+ if err != nil {
172
+ logger .Errorf ("Failed to create directory %s: %s" , logsDir , err )
173
+ }
174
+
175
+ logs , _ := os .Create (logsFile )
176
+ defer logs .Close ()
177
+ _ , err = logs .WriteString (executionLogs )
178
+ if err != nil {
179
+ logger .Errorf ("Failed to write logs to %s: %s" , logsFile , err )
180
+ }
181
+ }
0 commit comments