diff --git a/artifactory/commands/transferconfig/transferconfig.go b/artifactory/commands/transferconfig/transferconfig.go index 42af06f40..6dd897d39 100644 --- a/artifactory/commands/transferconfig/transferconfig.go +++ b/artifactory/commands/transferconfig/transferconfig.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "io" "net/http" "os" "strings" @@ -43,6 +44,7 @@ type TransferConfigCommand struct { force bool verbose bool preChecks bool + interactive bool sourceWorkingDir string targetWorkingDir string } @@ -55,6 +57,11 @@ func (tcc *TransferConfigCommand) CommandName() string { return "rt_transfer_config" } +func (tcc *TransferConfigCommand) SetInteractive(value bool) *TransferConfigCommand { + tcc.interactive = value + return tcc +} + func (tcc *TransferConfigCommand) SetDryRun(dryRun bool) *TransferConfigCommand { tcc.dryRun = dryRun return tcc @@ -137,6 +144,65 @@ func (tcc *TransferConfigCommand) Run() (err error) { return } + if tcc.interactive { + tcc.LogTitle("Phase 3.5/5 - Modify configuration interactively") + + // filename := "config_" + time.Now().Format(general.SafeDateFileFormat) + ".zip" + filename := "config_*.zip" + + // open output file + fo, err2 := os.CreateTemp("", filename) + if err2 != nil { + log.Error("Failed to create temporary file ", filename, " ", err2) + return + } + + // close fo on exit and check for its returned error + defer func() { + if err := fo.Close(); err != nil { + // log.Error(err) + return + } + + err := os.Remove(filename) + if err != nil { + log.Error("Failed to remove temporary file ", filename, " ", err) + return + } + }() + + // write file to disk + writtenToDisk, err3 := fo.Write(archiveConfig.Bytes()) + + if err3 != nil { + log.Error("Failed to write temporary file ", filename, " ", err3) + return + } + + log.Debug("Wrote ", writtenToDisk, " bytes ", len(archiveConfig.Bytes())) + fo.Sync() + + log.Info("Waiting for you to modify the file:") + log.Info(filename) + log.Info("Press any key to continue...") + + // TODO: yield execution back? + // or separate the phases and add two calls in the consumer + fmt.Scanln() + + // Read file from disk + buf := bytes.NewBuffer(nil) + readFromDisk, err3 := io.Copy(buf, fo) + + if err3 != nil { + log.Error("Failed to read temporary file ", filename, " ", err3) + return + } + + log.Debug("Read ", readFromDisk, " bytes ", len(buf.Bytes()), " ", len(archiveConfig.Bytes())) + archiveConfig = buf + } + // Import the archive to the target Artifactory tcc.LogTitle("Phase 4/5 - Import configuration to the target Artifactory") err = tcc.importToTargetArtifactory(archiveConfig) diff --git a/general/utils.go b/general/utils.go index 3aee57434..3ab807399 100644 --- a/general/utils.go +++ b/general/utils.go @@ -1,15 +1,17 @@ package general import ( - "github.com/jfrog/jfrog-cli-core/v2/common/commands" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" - "github.com/jfrog/jfrog-client-go/utils/errorutils" "net" "net/url" "strings" + + "github.com/jfrog/jfrog-cli-core/v2/common/commands" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-client-go/utils/errorutils" ) const defaultServerId = "default-server" +const SafeDateFileFormat = "2006-01-02_15-04-05" // Deduce the server ID from the URL and add server details to config. func ConfigServerWithDeducedId(server *config.ServerDetails, interactive, webLogin bool) error {