-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(foreach): run against previously failed or successful repos #147
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,15 +35,19 @@ import ( | |
var exec executor.Executor = executor.NewRealExecutor() | ||
|
||
var ( | ||
repoFile = "repos.txt" | ||
repoFile string | ||
successful bool | ||
failed bool | ||
|
||
overallResultsDirectory string | ||
|
||
successfulResultsDirectory string | ||
successfulReposFileName string | ||
successfulReposSymlink = ".latest_successful" | ||
|
||
failedResultsDirectory string | ||
failedReposFileName string | ||
failedReposSymlink = ".latest_failed" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. those could be const? |
||
) | ||
|
||
func formatArguments(arguments []string) string { | ||
|
@@ -54,6 +58,17 @@ func formatArguments(arguments []string) string { | |
return strings.Join(quotedArgs, " ") | ||
} | ||
|
||
func moreThanOne(args ...bool) bool { | ||
b := map[bool]int{ | ||
false: 0, | ||
true: 0, | ||
} | ||
for _, v := range args { | ||
b[v] += 1 | ||
} | ||
return b[true] > 1 | ||
} | ||
|
||
func NewForeachCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "foreach [flags] -- COMMAND [ARGUMENT...]", | ||
|
@@ -65,7 +80,9 @@ marks that no further options should be interpreted by turbolift.`, | |
Args: cobra.MinimumNArgs(1), | ||
} | ||
|
||
cmd.Flags().StringVar(&repoFile, "repos", "repos.txt", "A file containing a list of repositories to clone.") | ||
cmd.Flags().StringVar(&repoFile, "repos", "", "A file containing a list of repositories to clone.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure this is needed - this mutex between repos.txt and successful/failed seems complicated, should we just ignore the repoFile if --successful or --failed is set? |
||
cmd.Flags().BoolVar(&successful, "successful", false, "Indication of whether to run against previously successful repos only.") | ||
cmd.Flags().BoolVar(&failed, "failed", false, "Indication of whether to run against previously failed repos only.") | ||
|
||
return cmd | ||
} | ||
|
@@ -77,6 +94,23 @@ func runE(c *cobra.Command, args []string) error { | |
return errors.New("Use -- to separate command") | ||
} | ||
|
||
customRepoFile := repoFile != "" | ||
if moreThanOne(successful, failed, customRepoFile) { | ||
return errors.New("only one repositories flag or option may be specified: either --successful; --failed; or --repos <file>") | ||
} | ||
if successful { | ||
var err error | ||
if repoFile, err = os.Readlink(successfulReposSymlink); err != nil { | ||
return errors.New("no previous successful foreach logs found") | ||
} | ||
} else if failed { | ||
var err error | ||
if repoFile, err = os.Readlink(failedReposSymlink); err != nil { | ||
return errors.New("no previous failed foreach logs found") | ||
} | ||
} else if !customRepoFile { | ||
repoFile = "repos.txt" | ||
} | ||
readCampaignActivity := logger.StartActivity("Reading campaign data (%s)", repoFile) | ||
options := campaign.NewCampaignOptions() | ||
options.RepoFilename = repoFile | ||
|
@@ -91,7 +125,7 @@ func runE(c *cobra.Command, args []string) error { | |
// the user something they could copy and paste. | ||
prettyArgs := formatArguments(args) | ||
|
||
setupOutputFiles(dir.Name, prettyArgs) | ||
setupOutputFiles(dir.Name, prettyArgs, logger) | ||
|
||
logger.Printf("Logs for all executions will be stored under %s", overallResultsDirectory) | ||
|
||
|
@@ -128,14 +162,14 @@ func runE(c *cobra.Command, args []string) error { | |
} | ||
|
||
logger.Printf("Logs for all executions have been stored under %s", overallResultsDirectory) | ||
logger.Printf("Names of successful repos have been written to %s", successfulReposFileName) | ||
logger.Printf("Names of failed repos have been written to %s", failedReposFileName) | ||
logger.Printf("Names of successful repos have been written to %s. Use --successful to run the next foreach command against these repos", successfulReposFileName) | ||
logger.Printf("Names of failed repos have been written to %s. Use --failed to run the next foreach command against these repos", failedReposFileName) | ||
|
||
return nil | ||
} | ||
|
||
// sets up a temporary directory to store success/failure logs etc | ||
func setupOutputFiles(campaignName string, command string) { | ||
func setupOutputFiles(campaignName string, command string, logger *logging.Logger) { | ||
overallResultsDirectory, _ = os.MkdirTemp("", fmt.Sprintf("turbolift-foreach-%s-", campaignName)) | ||
successfulResultsDirectory = path.Join(overallResultsDirectory, "successful") | ||
failedResultsDirectory = path.Join(overallResultsDirectory, "failed") | ||
|
@@ -151,6 +185,27 @@ func setupOutputFiles(campaignName string, command string) { | |
defer successfulReposFile.Close() | ||
defer failedReposFile.Close() | ||
|
||
if _, err := os.Lstat(successfulReposSymlink); err == nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can prob just override it? |
||
err := os.Remove(successfulReposSymlink) | ||
if err != nil { | ||
logger.Warnf("Failed to remove previous symlink for successful repos: %v", err) | ||
} | ||
} | ||
err := os.Symlink(successfulReposFileName, successfulReposSymlink) | ||
if err != nil { | ||
logger.Warnf("Failed to create symlink for successful repos: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. warn or fail? |
||
} | ||
if _, err := os.Lstat(failedReposSymlink); err == nil { | ||
err := os.Remove(failedReposSymlink) | ||
if err != nil { | ||
logger.Warnf("Failed to remove previous symlink for failed repos: %v", err) | ||
} | ||
} | ||
err = os.Symlink(failedReposFileName, failedReposSymlink) | ||
if err != nil { | ||
logger.Warnf("Failed to create symlink for failed repos: %v", err) | ||
} | ||
|
||
_, _ = 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)) | ||
_, _ = 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)) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we definitely need two symlinks? If we have our output directories to be something like:
Then we could just use one symlink, to the
/some_temp_dir_from_os/some_random_bit
path and infer the rest as needed.