diff --git a/awss3/awss3.go b/awss3/awss3.go index b72eefc3..faa3c230 100644 --- a/awss3/awss3.go +++ b/awss3/awss3.go @@ -2,7 +2,6 @@ package awss3 import ( "context" - "path/filepath" "github.com/TouchBistro/tb/util" "github.com/aws/aws-sdk-go-v2/aws" @@ -41,7 +40,7 @@ func ListObjectKeysByPrefix(bucket, objKeyPrefix string) ([]string, error) { return keys, nil } -func DownloadObject(bucket, objKey, dstDir string) error { +func DownloadObject(bucket, objKey, dstPath string) error { // Set up AWS Env Vars conf, err := external.LoadDefaultAWSConfig() if err != nil { @@ -62,13 +61,12 @@ func DownloadObject(bucket, objKey, dstDir string) error { defer resp.Body.Close() // Download to a local file. - dlPath := filepath.Join(dstDir, objKey) - nBytes, err := util.DownloadFile(dlPath, resp.Body) + nBytes, err := util.DownloadFile(dstPath, resp.Body) if err != nil { - return errors.Wrapf(err, "failed downloading file to %s", dlPath) + return errors.Wrapf(err, "failed downloading file to %s", dstPath) } - log.Debugf("Wrote %d bytes to %s successfully", nBytes, dlPath) + log.Debugf("Wrote %d bytes to %s successfully", nBytes, dstPath) return nil } diff --git a/cmd/app/app.go b/cmd/app/app.go new file mode 100644 index 00000000..9552c58a --- /dev/null +++ b/cmd/app/app.go @@ -0,0 +1,166 @@ +package app + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/TouchBistro/goutils/fatal" + "github.com/TouchBistro/goutils/spinner" + "github.com/TouchBistro/tb/app" + "github.com/TouchBistro/tb/awss3" + "github.com/TouchBistro/tb/config" + "github.com/TouchBistro/tb/git" + "github.com/TouchBistro/tb/simulator" + "github.com/TouchBistro/tb/util" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var appCmd = &cobra.Command{ + Use: "app", + Short: "tb app allows running and managing different kinds of applications", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + // Put app specific configuration & setup logic here + + // Check if current command is an ios subcommand + isIOSCommand := cmd.Parent().Name() == "ios" + + if isIOSCommand && runtime.GOOS != "darwin" { + fatal.Exit("Error: tb app ios is only supported on macOS") + } + + // Get global flag value + noRegistryPull, err := cmd.Flags().GetBool("no-registry-pull") + if err != nil { + // This is a coding error + fatal.ExitErr(err, "failed to get flag") + } + + // Need to do this explicitly here since we are defining PersistentPreRun + // PersistentPreRun overrides the parent command's one if defined, so the one in root won't be run. + err = config.Init(config.InitOptions{ + UpdateRegistries: !noRegistryPull, + LoadServices: false, + LoadApps: true, + }) + if err != nil { + fatal.ExitErr(err, "Failed to initialize config files") + } + + if isIOSCommand { + err = simulator.LoadSimulators() + if err != nil { + fatal.ExitErr(err, "Failed to find available iOS simulators") + } + } + }, +} + +func AppCmd() *cobra.Command { + return appCmd +} + +func DownloadLatestApp(a app.App, downloadDest string) string { + // Look up the latest build sha for user-specified branch and app. + s3Dir := filepath.Join(a.Name, a.Branch) + log.Infof("Checking objects on aws in bucket %s matching prefix %s...", a.Storage.Bucket, s3Dir) + s3Builds, err := awss3.ListObjectKeysByPrefix(a.Storage.Bucket, s3Dir) + if err != nil { + fatal.ExitErrf(err, "Failed getting keys from s3 in dir %s", s3Dir) + } + if len(s3Builds) == 0 { + fatal.Exitf("could not find any builds for %s", s3Dir) + } else if len(s3Builds) > 1 { + // We only expect one build per branch. If we find two, its likely a bug or some kind of + // race condition from the build-uploading side. + // If this gets clunky we can determine a sort order for the builds. + fatal.Exitf("Got the following builds for this branch %+v. Only expecting one build", s3Builds) + } + + pathToS3Tarball := s3Builds[0] + s3BuildFilename := filepath.Base(pathToS3Tarball) + + // Decide whether or not to pull down a new version. + + localBranchDir := filepath.Join(downloadDest, a.FullName(), a.Branch) + log.Infof("Checking contents at %s to see if we need to download a new version from S3", localBranchDir) + + pattern := fmt.Sprintf("%s/*.app", localBranchDir) + localBuilds, err := filepath.Glob(pattern) + if err != nil { + fatal.ExitErrf(err, "couldn't glob for %s", pattern) + } + + if len(localBuilds) > 1 { + fatal.Exitf("Got the following builds: %+v. Only expecting one build", localBuilds) + } + + // If there is a local build, compare its sha against s3 and github versions + var refreshLocalBuild bool + if len(localBuilds) == 1 { + localBuild := localBuilds[0] + + // If there is a local build, get latest sha from github for desired branch to see if the build available on s3 corresponds to the + // latest commit on the branch. + log.Infof("Checking latest github sha for %s-%s", a.GitRepo, a.Branch) + latestGitsha, err := git.GetBranchHeadSha(a.GitRepo, a.Branch) + if err != nil { + fatal.ExitErrf(err, "Failed getting branch head sha for %s-%s", a.GitRepo, a.Branch) + } + log.Infof("Latest github sha is %s", latestGitsha) + if !strings.HasPrefix(s3BuildFilename, latestGitsha) { + log.Warnf("sha of s3 build %s does not match latest github sha %s for branch %s", s3BuildFilename, latestGitsha, a.Branch) + } + + currentSha := strings.Split(filepath.Base(localBuild), ".")[0] + s3Sha := strings.Split(s3BuildFilename, ".")[0] + + log.Infof("Current local build sha is %s", currentSha) + log.Infof("Latest s3 sha is %s", s3Sha) + + if currentSha == s3Sha { + log.Infoln("Current build sha matches remote sha") + } else { + log.Infoln("Current build sha is different from s3 sha. Deleting local version...") + err := os.RemoveAll(localBranchDir) + if err != nil { + fatal.ExitErrf(err, "failed to delete %s", localBranchDir) + } + + refreshLocalBuild = true + } + } + + // Path where the downloaded app is + dstPath := filepath.Join(downloadDest, a.FullName(), a.Branch, s3BuildFilename) + + // If there are no local builds or if our local build was deemed out of date, download the latest object from S3 + if len(localBuilds) == 0 || refreshLocalBuild { + log.Infof("Downloading %s from bucket %s to %s", pathToS3Tarball, a.Storage.Bucket, downloadDest) + successCh := make(chan string) + failedCh := make(chan error) + go func(successCh chan string, failedCh chan error) { + err = awss3.DownloadObject(a.Storage.Bucket, pathToS3Tarball, dstPath) + if err != nil { + failedCh <- errors.Wrapf(err, "Failed to download a file from s3 from %s to %s", pathToS3Tarball, downloadDest) + return + } + successCh <- pathToS3Tarball + }(successCh, failedCh) + count := 1 + spinner.SpinnerWait(successCh, failedCh, "\t☑ finished downloading %s\n", "failed S3 download", count) + + // Untar, ungzip and cleanup the file + log.Infof("untar-ing %s", dstPath) + err := util.Untar(dstPath, true) + if err != nil { + fatal.ExitErrf(err, "Failed to untar or cleanup app archive at %s", dstPath) + } + } + + return strings.TrimSuffix(dstPath, ".tgz") +} diff --git a/cmd/app/desktop/desktop.go b/cmd/app/desktop/desktop.go new file mode 100644 index 00000000..0b046c44 --- /dev/null +++ b/cmd/app/desktop/desktop.go @@ -0,0 +1,14 @@ +package desktop + +import ( + "github.com/spf13/cobra" +) + +var desktopCmd = &cobra.Command{ + Use: "desktop", + Short: "tb app desktop allows running and managing desktop applications", +} + +func DesktopCmd() *cobra.Command { + return desktopCmd +} diff --git a/cmd/app/desktop/run.go b/cmd/app/desktop/run.go new file mode 100644 index 00000000..8bbf8a83 --- /dev/null +++ b/cmd/app/desktop/run.go @@ -0,0 +1,93 @@ +package desktop + +import ( + "errors" + "os" + "runtime" + + "github.com/TouchBistro/goutils/command" + "github.com/TouchBistro/goutils/fatal" + "github.com/TouchBistro/goutils/file" + appCmd "github.com/TouchBistro/tb/cmd/app" + "github.com/TouchBistro/tb/config" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +type runOptions struct { + branch string +} + +var runOpts runOptions + +var runCmd = &cobra.Command{ + Use: "run", + Args: func(cmd *cobra.Command, args []string) error { + // Verify that the app name was provided as a single arg + if len(args) < 1 { + return errors.New("app name is required as an argument") + } else if len(args) > 1 { + return errors.New("only one argument is accepted") + } + + return nil + }, + Short: "Runs a desktop application", + Long: `Runs a desktop application. + +Examples: +- run the current master build of TouchBistroServer + tb app desktop run TouchBistroServer + +- run the build for a specific branch + tb app desktop run TouchBistroServer --branch task/bug-631/fix-thing`, + Run: func(cmd *cobra.Command, args []string) { + appName := args[0] + a, err := config.LoadedDesktopApps().Get(appName) + if err != nil { + fatal.ExitErrf(err, "%s is not a valid desktop app\n", appName) + } + + // Override branch if one was provided + if runOpts.branch != "" { + a.Branch = runOpts.branch + } + + downloadDest := config.DesktopAppsPath() + // Check disk utilisation by desktop directory + usageBytes, err := file.DirSize(downloadDest) + if err != nil { + fatal.ExitErr(err, "Error checking ios build disk space usage") + } + log.Infof("Current desktop app build disk usage: %.2fGB", float64(usageBytes)/1024.0/1024.0/1024.0) + + appPath := appCmd.DownloadLatestApp(a, downloadDest) + + // Set env vars so they are available in the app process + for k, v := range a.EnvVars { + log.Debugf("Setting %s to %s", k, v) + os.Setenv(k, v) + } + + log.Info("☐ Launching app") + + // TODO probably want to figure out a better way to abstract opening an app cross platform + if runtime.GOOS == "darwin" { + err = command.Exec("open", []string{appPath}, "tb-app-desktop-run-open") + } else { + fatal.Exit("tb app desktop run is not supported on your platform") + } + + if err != nil { + fatal.ExitErrf(err, "failed to run app %s", a.FullName()) + } + + log.Info("☑ Launched app") + log.Info("🎉🎉🎉 Enjoy!") + }, +} + +func init() { + desktopCmd.AddCommand(runCmd) + runCmd.Flags().StringVarP(&runOpts.branch, "branch", "b", "", "The name of the git branch associated build to pull down and run") +} diff --git a/cmd/app/ios/ios.go b/cmd/app/ios/ios.go new file mode 100644 index 00000000..ebe7f02c --- /dev/null +++ b/cmd/app/ios/ios.go @@ -0,0 +1,14 @@ +package ios + +import ( + "github.com/spf13/cobra" +) + +var iosCmd = &cobra.Command{ + Use: "ios", + Short: "tb app ios allows running and managing iOS apps", +} + +func IOSCmd() *cobra.Command { + return iosCmd +} diff --git a/cmd/app/ios/logs.go b/cmd/app/ios/logs.go new file mode 100644 index 00000000..904c1193 --- /dev/null +++ b/cmd/app/ios/logs.go @@ -0,0 +1,67 @@ +package ios + +import ( + "os" + "os/exec" + "path/filepath" + + "github.com/TouchBistro/goutils/command" + "github.com/TouchBistro/goutils/fatal" + "github.com/TouchBistro/tb/simulator" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +type logsOptions struct { + iosVersion string + deviceName string + numberOfLines string +} + +var logOpts logsOptions + +var logsCmd = &cobra.Command{ + Use: "logs", + Short: "Displays logs from the given simulator", + Long: `Displays logs from the given simulator. + +Examples: +- displays the last 10 logs in the default iOS simulator + tb app logs + +- displays the last 20 logs in an iOS 12.4 iPad Air 2 simulator + tb app logs --number 20 --ios-version 12.4 --device iPad Air 2`, + Run: func(cmd *cobra.Command, args []string) { + if logOpts.iosVersion == "" { + logOpts.iosVersion = simulator.GetLatestIOSVersion() + log.Infof("No iOS version provided, defaulting to version %s\n", logOpts.iosVersion) + } + + log.Debugln("☐ Finding device UDID") + + deviceUDID, err := simulator.GetDeviceUDID("iOS "+logOpts.iosVersion, logOpts.deviceName) + if err != nil { + fatal.ExitErr(err, "☒ Failed to get device UUID.\nRun \"xcrun simctl list devices\" to list available simulators.") + } + + log.Debugf("☑ Found device UDID: %s\n", deviceUDID) + + logsPath := filepath.Join(os.Getenv("HOME"), "Library/Logs/CoreSimulator", deviceUDID, "system.log") + log.Infof("Attaching to logs for simulator %s\n\n", logOpts.deviceName) + + err = command.Exec("tail", []string{"-f", "-n", logOpts.numberOfLines, logsPath}, "ios-logs-tail", func(cmd *exec.Cmd) { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + }) + if err != nil { + fatal.ExitErrf(err, "Failed to get logs for simulator %s with iOS version %s", logOpts.deviceName, logOpts.iosVersion) + } + }, +} + +func init() { + iosCmd.AddCommand(logsCmd) + logsCmd.Flags().StringVarP(&logOpts.iosVersion, "ios-version", "i", "", "The iOS version to use") + logsCmd.Flags().StringVarP(&logOpts.deviceName, "device", "d", "iPad Air (3rd generation)", "The name of the device to use") + logsCmd.Flags().StringVarP(&logOpts.numberOfLines, "number", "n", "10", "The number of lines to display") +} diff --git a/cmd/app/ios/run.go b/cmd/app/ios/run.go new file mode 100644 index 00000000..54ec8658 --- /dev/null +++ b/cmd/app/ios/run.go @@ -0,0 +1,150 @@ +package ios + +import ( + "fmt" + "os" + + "github.com/TouchBistro/goutils/fatal" + "github.com/TouchBistro/goutils/file" + appCmd "github.com/TouchBistro/tb/cmd/app" + "github.com/TouchBistro/tb/config" + "github.com/TouchBistro/tb/simulator" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +type runOptions struct { + iosVersion string + deviceName string + dataPath string + branch string +} + +var runOpts runOptions + +var runCmd = &cobra.Command{ + Use: "run", + Args: func(cmd *cobra.Command, args []string) error { + // Verify that the app name was provided as a single arg + if len(args) < 1 { + return errors.New("app name is required as an argument") + } else if len(args) > 1 { + return errors.New("only one argument is accepted") + } + + return nil + }, + Short: "Runs an iOS app build in an iOS Simulator", + Long: `Runs an iOS app build in an iOS Simulator. + +Examples: +- run the current master build of TouchBistro in the default iOS Simulator + tb app ios run TouchBistro + +- run the build for specific branch in an iOS 12.3 iPad Air 2 simulator + tb app ios run TouchBistro --ios-version 12.3 --device iPad Air 2 --branch task/pay-631/fix-thing`, + Run: func(cmd *cobra.Command, args []string) { + appName := args[0] + a, err := config.LoadedIOSApps().Get(appName) + if err != nil { + fatal.ExitErrf(err, "%s is not a valid iOS app\n", appName) + } + + // Override branch if one was provided + if runOpts.branch != "" { + a.Branch = runOpts.branch + } + + if runOpts.iosVersion == "" { + runOpts.iosVersion = simulator.GetLatestIOSVersion() + log.Infof("No iOS version provided, defaulting to version %s\n", runOpts.iosVersion) + } + + downloadDest := config.IOSBuildPath() + // Check disk utilisation by ios directory + usageBytes, err := file.DirSize(downloadDest) + if err != nil { + fatal.ExitErr(err, "Error checking ios build disk space usage") + } + log.Infof("Current ios build disk usage: %.2fGB", float64(usageBytes)/1024.0/1024.0/1024.0) + + appPath := appCmd.DownloadLatestApp(a, downloadDest) + + log.Debugln("☐ Finding device UDID") + deviceUDID, err := simulator.GetDeviceUDID("iOS "+runOpts.iosVersion, runOpts.deviceName) + if err != nil { + fatal.ExitErr(err, "☒ Failed to get device UDID.\nRun \"xcrun simctl list devices\" to list available simulators.") + } + + log.Debugf("☑ Found device UDID: %s\n", deviceUDID) + log.Infof("☐ Booting Simulator %s\n", runOpts.deviceName) + + err = simulator.Boot(deviceUDID) + if err != nil { + fatal.ExitErrf(err, "☒ Failed to boot simulator %s", runOpts.deviceName) + } + + log.Infof("☑ Booted simulator %s\n", runOpts.deviceName) + log.Debugln("☐ Opening simulator app") + + err = simulator.Open() + if err != nil { + fatal.ExitErr(err, "☒ Failed to launch simulator") + } + + log.Debugln("☑ Opened simulator app") + log.Infof("☐ Installing app on %s\n", runOpts.deviceName) + + err = simulator.InstallApp(deviceUDID, appPath) + if err != nil { + fatal.ExitErrf(err, "☒ Failed to install app at path %s on simulator %s", appPath, runOpts.deviceName) + } + + log.Infof("☑ Installed app %s on %s\n", a.BundleID, runOpts.deviceName) + + appDataPath, err := simulator.GetAppDataPath(deviceUDID, a.BundleID) + if err != nil { + fatal.ExitErrf(err, "Failed to get path to data for app %s", a.BundleID) + } + + if runOpts.dataPath != "" { + log.Infoln("☐ Injecting data files into simulator") + + err = file.CopyDirContents(runOpts.dataPath, appDataPath) + if err != nil { + fatal.ExitErrf(err, "☒ Failed to inject data into simulator") + } + + log.Infoln("☑ Injected data into simulator") + } + + log.Info("☐ Setting environment variables") + + for k, v := range a.EnvVars { + log.Debugf("Setting %s to %s", k, v) + // Env vars can be passed to simctl if they are set in the calling environment with a SIMCTL_CHILD_ prefix. + os.Setenv(fmt.Sprintf("SIMCTL_CHILD_%s", k), v) + } + + log.Info("☑ Done setting environment variables") + log.Info("☐ Launching app in simulator") + + err = simulator.LaunchApp(deviceUDID, a.BundleID) + if err != nil { + fatal.ExitErrf(err, "☒ Failed to launch app %s on simulator %s", a.BundleID, runOpts.deviceName) + } + + log.Infof("☑ Launched app %s on %s\n", a.BundleID, runOpts.deviceName) + log.Infof("App data directory is located at: %s\n", appDataPath) + log.Info("🎉🎉🎉 Enjoy!") + }, +} + +func init() { + iosCmd.AddCommand(runCmd) + runCmd.Flags().StringVarP(&runOpts.iosVersion, "ios-version", "i", "", "The iOS version to use") + runCmd.Flags().StringVarP(&runOpts.deviceName, "device", "d", "iPad Air (3rd generation)", "The name of the device to use") + runCmd.Flags().StringVarP(&runOpts.branch, "branch", "b", "", "The name of the git branch associated build to pull down and run") + runCmd.Flags().StringVarP(&runOpts.dataPath, "data-path", "D", "", "The path to a data directory to inject into the simulator") +} diff --git a/cmd/app/list.go b/cmd/app/list.go new file mode 100644 index 00000000..eda4fa1f --- /dev/null +++ b/cmd/app/list.go @@ -0,0 +1,62 @@ +package app + +import ( + "fmt" + "sort" + + "github.com/TouchBistro/tb/config" + "github.com/spf13/cobra" +) + +type listOptions struct { + shouldListIOSApps bool + shouldListDesktopApps bool +} + +var listOpts listOptions + +var listCmd = &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, + Short: "Lists all available apps", + Long: `Lists all available apps. Flags can be used to list only specific types of apps. + +Examples: +- List only iOS apps + tb apps list --ios`, + Run: func(cmd *cobra.Command, args []string) { + // If no flags provided show everything + if !listOpts.shouldListIOSApps && + !listOpts.shouldListDesktopApps { + listOpts.shouldListIOSApps = true + listOpts.shouldListDesktopApps = true + } + + if listOpts.shouldListIOSApps { + fmt.Println("iOS Apps:") + names := config.LoadedIOSApps().Names() + sort.Strings(names) + + for _, n := range names { + fmt.Printf(" - %s\n", n) + } + } + + if listOpts.shouldListDesktopApps { + fmt.Println("Desktop Apps:") + names := config.LoadedDesktopApps().Names() + sort.Strings(names) + + for _, n := range names { + fmt.Printf(" - %s\n", n) + } + } + }, +} + +func init() { + appCmd.AddCommand(listCmd) + listCmd.Flags().BoolVar(&listOpts.shouldListIOSApps, "ios", false, "list iOS apps") + listCmd.Flags().BoolVar(&listOpts.shouldListDesktopApps, "desktop", false, "list desktop apps") +} diff --git a/cmd/ios/ios.go b/cmd/ios/ios.go index c7219ec3..1b3fdd04 100644 --- a/cmd/ios/ios.go +++ b/cmd/ios/ios.go @@ -3,9 +3,11 @@ package ios import ( "runtime" + "github.com/TouchBistro/goutils/color" "github.com/TouchBistro/goutils/fatal" "github.com/TouchBistro/tb/config" "github.com/TouchBistro/tb/simulator" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -30,6 +32,9 @@ var iosCmd = &cobra.Command{ fatal.Exit("Error: tb ios is only supported on macOS") } + log.Infoln(color.Yellow("tb ios is deprecated and will be removed in the next major release")) + log.Infoln(color.Yellow("Please use tb app ios instead")) + // Need to do this explicitly here since we are defining PersistentPreRun // PersistentPreRun overrides the parent command's one if defined, so the one in root won't be run. err := config.Init(config.InitOptions{ diff --git a/cmd/ios/run.go b/cmd/ios/run.go index aa202d92..cf704009 100644 --- a/cmd/ios/run.go +++ b/cmd/ios/run.go @@ -132,13 +132,16 @@ Examples: } } + // Path where the downloaded app is + dstPath := filepath.Join(downloadDest, app.FullName(), branch, s3BuildFilename) + // If there are no local builds or if our local build was deemed out of date, download the latest object from S3 if len(localBuilds) == 0 || refreshLocalBuild { log.Infof("Downloading %s from bucket %s to %s", pathToS3Tarball, app.Storage.Bucket, downloadDest) successCh := make(chan string) failedCh := make(chan error) go func(successCh chan string, failedCh chan error) { - err = awss3.DownloadObject(app.Storage.Bucket, pathToS3Tarball, downloadDest) + err = awss3.DownloadObject(app.Storage.Bucket, pathToS3Tarball, dstPath) if err != nil { failedCh <- errors.Wrapf(err, "Failed to download a file from s3 from %s to %s", pathToS3Tarball, downloadDest) return @@ -149,15 +152,14 @@ Examples: spinner.SpinnerWait(successCh, failedCh, "\t☑ finished downloading %s\n", "failed S3 download", count) // Untar, ungzip and cleanup the file - pathToLocalTarball := filepath.Join(downloadDest, pathToS3Tarball) - log.Infof("untar-ing %s", pathToLocalTarball) - err := util.Untar(pathToLocalTarball, true) + log.Infof("untar-ing %s", dstPath) + err := util.Untar(dstPath, true) if err != nil { - fatal.ExitErrf(err, "Failed to untar or cleanup app archive at %s", pathToLocalTarball) + fatal.ExitErrf(err, "Failed to untar or cleanup app archive at %s", dstPath) } } - appPath := filepath.Join(downloadDest, strings.TrimSuffix(pathToS3Tarball, ".tgz")) + appPath := strings.TrimSuffix(dstPath, ".tgz") log.Debugln("☐ Finding device UDID") deviceUDID, err := simulator.GetDeviceUDID("iOS "+iosVersion, deviceName) diff --git a/cmd/nuke.go b/cmd/nuke.go index 36e6e225..643b895e 100644 --- a/cmd/nuke.go +++ b/cmd/nuke.go @@ -13,15 +13,16 @@ import ( ) type nukeOptions struct { - shouldNukeContainers bool - shouldNukeImages bool - shouldNukeVolumes bool - shouldNukeNetworks bool - shouldNukeRepos bool - shouldNukeConfig bool - shouldNukeIOSBuilds bool - shouldNukeRegistries bool - shouldNukeAll bool + shouldNukeContainers bool + shouldNukeImages bool + shouldNukeVolumes bool + shouldNukeNetworks bool + shouldNukeRepos bool + shouldNukeConfig bool + shouldNukeDesktopApps bool + shouldNukeIOSBuilds bool + shouldNukeRegistries bool + shouldNukeAll bool } var nukeOpts nukeOptions @@ -36,6 +37,7 @@ var nukeCmd = &cobra.Command{ !nukeOpts.shouldNukeNetworks && !nukeOpts.shouldNukeRepos && !nukeOpts.shouldNukeConfig && + !nukeOpts.shouldNukeDesktopApps && !nukeOpts.shouldNukeIOSBuilds && !nukeOpts.shouldNukeRegistries && !nukeOpts.shouldNukeAll { @@ -136,6 +138,15 @@ var nukeCmd = &cobra.Command{ log.Infoln("...done") } + if nukeOpts.shouldNukeDesktopApps || nukeOpts.shouldNukeAll { + log.Infoln("Removing desktop app builds...") + err := os.RemoveAll(config.DesktopAppsPath()) + if err != nil { + fatal.ExitErr(err, "Failed removing desktop app builds.") + } + log.Infoln("...done") + } + if nukeOpts.shouldNukeIOSBuilds || nukeOpts.shouldNukeAll { log.Infoln("Removing ios builds...") err := os.RemoveAll(config.IOSBuildPath()) @@ -178,6 +189,7 @@ func init() { nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeNetworks, "networks", false, "nuke all networks") nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeRepos, "repos", false, "nuke all repos") nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeConfig, "config", false, "nuke all config files") + nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeDesktopApps, "desktop", false, "nuke all downloaded desktop app builds") nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeIOSBuilds, "ios", false, "nuke all downloaded iOS builds") nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeRegistries, "registries", false, "nuke all registries") nukeCmd.Flags().BoolVar(&nukeOpts.shouldNukeAll, "all", false, "nuke everything") diff --git a/cmd/root.go b/cmd/root.go index f899e1ed..b2e51de3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,7 +5,10 @@ import ( "github.com/TouchBistro/goutils/color" "github.com/TouchBistro/goutils/fatal" - "github.com/TouchBistro/tb/cmd/ios" + appCmd "github.com/TouchBistro/tb/cmd/app" + "github.com/TouchBistro/tb/cmd/app/desktop" + "github.com/TouchBistro/tb/cmd/app/ios" + legacyIOSCmd "github.com/TouchBistro/tb/cmd/ios" "github.com/TouchBistro/tb/config" "github.com/TouchBistro/tb/fortune" "github.com/TouchBistro/tb/git" @@ -45,7 +48,8 @@ func init() { rootCmd.PersistentFlags().BoolVar(&rootOpts.noRegistryPull, "no-registry-pull", false, "Don't pull latest version of registries when tb is run") // Add subcommands - rootCmd.AddCommand(ios.IOS()) + appCmd.AppCmd().AddCommand(desktop.DesktopCmd(), ios.IOSCmd()) + rootCmd.AddCommand(appCmd.AppCmd(), legacyIOSCmd.IOS()) cobra.OnInitialize(func() { f := fortune.Random().String() diff --git a/config/config.go b/config/config.go index 27eb7955..1022a239 100644 --- a/config/config.go +++ b/config/config.go @@ -43,6 +43,10 @@ func RegistriesPath() string { return filepath.Join(TBRootPath(), "registries") } +func DesktopAppsPath() string { + return filepath.Join(TBRootPath(), "desktop") +} + func IOSBuildPath() string { return filepath.Join(TBRootPath(), "ios") } diff --git a/config/legacy.go b/config/legacy.go index 32a20a4f..985345ef 100644 --- a/config/legacy.go +++ b/config/legacy.go @@ -238,7 +238,7 @@ func legacyInit() error { registryResult.IOSApps, err = app.NewAppCollection([]app.App{ app.App{ BundleID: "com.touchbistro.TouchBistro", - Branch: "develop", + Branch: "master", GitRepo: "TouchBistro/tb-pos", EnvVars: map[string]string{ "debug.autoAcceptTOS": "true", @@ -263,7 +263,13 @@ func legacyInit() error { }, }) if err != nil { - return errors.Wrapf(err, "failed to create AppCollection") + return errors.Wrapf(err, "failed to create AppCollection for iOS apps") + } + + // Create empty collection so people get a proper error message instead of dereferencing nil pointer + registryResult.DesktopApps, err = app.NewAppCollection(nil) + if err != nil { + return errors.Wrap(err, "failed to create AppCollection for desktop apps") } return nil