diff --git a/cmd/tx/main.go b/cmd/tx/main.go index 2674ccd..4dd8c59 100644 --- a/cmd/tx/main.go +++ b/cmd/tx/main.go @@ -73,17 +73,31 @@ func Main() { Client: client, } - backUpFilePath, err := txlib.MigrateLegacyConfigFile(&cfg, + backUpFilePath, backUpRootFilePath, err := txlib.MigrateLegacyConfigFile(&cfg, api) if err != nil { return cli.Exit(err, 1) } fmt.Printf( - "Migration ended! We have also created a backup "+ - "file for your previous config file `%s`.\n", + "Migration ended! We have created a backup "+ + "file for your previous `%s` at `%s`.\n", + cfg.Local.Path, backUpFilePath, ) + if backUpRootFilePath != "" { + fmt.Printf( + "Additionally, we have created a backup "+ + "file for your previous `%s` at `%s`.\n", + cfg.Root.Path, + backUpRootFilePath, + ) + } else { + fmt.Printf( + "Additionally, we have created a new `%s` with your token.\n", + cfg.Root.Path, + ) + } return nil }, }, diff --git a/internal/txlib/migrate.go b/internal/txlib/migrate.go index 0434802..69195ff 100644 --- a/internal/txlib/migrate.go +++ b/internal/txlib/migrate.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" "time" + "os" "github.com/transifex/cli/pkg/txapi" @@ -18,11 +19,13 @@ MigrateLegacyConfigFile Edits legacy config files so they contain all the necessary information to use the 3rd version of the API. Steps taken: -1. Check for token setting. +1. Update 'host' field in 'main' section of local configuration to use app.transifex.com +2. If root configuration has www.transifex.com, update it to app.transifex.com instead +3. Check for token setting. If not found check for API token in the old configuration. If not found generate one. -2. Check for rest_hostname setting. If not found add it. -3. Check the section keys are using the legacy format +4. Check for rest_hostname setting. If not found add it. +5. Check the section keys are using the legacy format (`.`) If yes find the organization for each section key and reformat the section key to conform to the new format @@ -30,13 +33,13 @@ Steps taken: */ func MigrateLegacyConfigFile( cfg *config.Config, api jsonapi.Connection, -) (string, error) { +) (string, string, error) { // Backup previous file before doing anything //Read all the contents of the original config file bytesRead, err := ioutil.ReadFile(cfg.Local.Path) if err != nil { - return "", fmt.Errorf("aborting, could not create backup file %w", err) + return "", "", fmt.Errorf("aborting, could not read local configuration %w", err) } //Copy all the contents to the destination file @@ -48,7 +51,42 @@ func MigrateLegacyConfigFile( err = ioutil.WriteFile(backUpFilePath, bytesRead, 0755) if err != nil { - return "", fmt.Errorf("aborting, could not create backup file %w", err) + return "", "", fmt.Errorf("aborting, could not create backup file %w", err) + } + + // Also backup the root configuration file, if it exists + backUpRootFilePath := "" + rootFileCreated := false + if _, err = os.Stat(cfg.Root.Path); err == nil { + bytesRead, err = ioutil.ReadFile(cfg.Root.Path) + if err != nil { + return "", "", fmt.Errorf("aborting, could not read root configuration %w", err) + } + backUpRootFilePath = filepath.Join(filepath.Dir(cfg.Root.Path), + ".transifexrc_"+currentTime.Format("20060102150405")+".bak") + err = ioutil.WriteFile(backUpRootFilePath, bytesRead, 0755) + if err != nil { + return "", "", fmt.Errorf("aborting, could not create backup file %w", err) + } + } else if os.IsNotExist(err) { + fmt.Printf("Root configuration file not found -- creating it at `%s`.\n", cfg.Root.Path) + f, err := os.Create(cfg.Root.Path) + if err != nil { + return "", "", fmt.Errorf("aborting, could not create root configuration %w", err) + } + rootFileCreated = true + defer f.Close() + } else { + return "", "", fmt.Errorf("aborting, could not read root configuration %w", err) + } + + // Update 'host' field in 'main' section of local config to use app.transifex.com + cfg.Local.Host = strings.ReplaceAll(cfg.Local.Host, "www.transifex.com", "app.transifex.com") + + // Update existing root config to use app.transifex.com + for i := range cfg.Root.Hosts { + host := &cfg.Root.Hosts[i] + host.Name = strings.ReplaceAll(host.Name, "www.transifex.com", "app.transifex.com") } // Get the current host @@ -56,6 +94,9 @@ func MigrateLegacyConfigFile( if activeHost == nil { activeHost = &config.Host{} + activeHost.Name = "https://app.transifex.com" + activeHost.RestHostname = "" + activeHost.Token = "" } if activeHost.Token == "" { @@ -78,7 +119,7 @@ func MigrateLegacyConfigFile( var token string _, err := fmt.Scanln(&token) if err != nil { - return "", err + return "", "", err } activeHost.Token = token } @@ -86,10 +127,15 @@ func MigrateLegacyConfigFile( // Save the new rest url if activeHost.RestHostname == "" { - fmt.Printf("No rest_hostname found adding `rest.api.transifex.com `\n") + fmt.Println("No rest_hostname found. Adding `rest.api.transifex.com`") activeHost.RestHostname = "https://rest.api.transifex.com" } + // Save the new root config if we created the file + if rootFileCreated { + cfg.Root.Hosts = append(cfg.Root.Hosts, *activeHost) + } + // Try to update resources currently in config // Internally if config finds a resource without ":" it will treat it as // a migration, read the resource in a special way and create a temp @@ -102,7 +148,7 @@ func MigrateLegacyConfigFile( if resource.OrganizationSlug == "" { organizationSlug, err := getOrganizationSlug(api, &resource) if err != nil { - return "", err + return "", "", err } if organizationSlug == "" { fmt.Printf( @@ -127,9 +173,9 @@ func MigrateLegacyConfigFile( cfg.Local.Resources = resources err = cfg.Save() if err != nil { - return "", fmt.Errorf("%w", err) + return "", "", fmt.Errorf("%w", err) } - return backUpFilePath, nil + return backUpFilePath, backUpRootFilePath, nil } func getOrganizationSlug( diff --git a/internal/txlib/migrate_test.go b/internal/txlib/migrate_test.go index 293f966..fcbb864 100644 --- a/internal/txlib/migrate_test.go +++ b/internal/txlib/migrate_test.go @@ -154,9 +154,9 @@ func TestSuccessfulMigration(t *testing.T) { defer f.Close() _, err2 := f.WriteString(` - [https://app.transifex.com] + [https://www.transifex.com] api_hostname = https://api.transifex.com - hostname = https://app.transifex.com + hostname = https://www.transifex.com username = api password = apassword `) @@ -175,7 +175,7 @@ func TestSuccessfulMigration(t *testing.T) { _, err2 = f.WriteString(` [main] - host = https://app.transifex.com + host = https://www.transifex.com [projslug.ares] file_filter = locale/.po minimum_perc = 0 @@ -198,11 +198,12 @@ func TestSuccessfulMigration(t *testing.T) { api := jsonapi.GetTestConnection(mockData) + assert.Equal(t, cfg.GetActiveHost().Name, "https://www.transifex.com") assert.Equal(t, cfg.GetActiveHost().Token, "") assert.Equal(t, cfg.GetActiveHost().RestHostname, "") assert.Equal(t, cfg.Local.Resources[0].OrganizationSlug, "") - _, err = MigrateLegacyConfigFile(&cfg, api) + _, _, err = MigrateLegacyConfigFile(&cfg, api) if err != nil { t.Error(err) } @@ -214,6 +215,7 @@ func TestSuccessfulMigration(t *testing.T) { t.Error(err) } + assert.Equal(t, cfgReloaded.GetActiveHost().Name, "https://app.transifex.com") assert.Equal(t, cfgReloaded.GetActiveHost().Token, "apassword") assert.Equal(t, cfgReloaded.GetActiveHost().RestHostname, "https://rest.api.transifex.com") @@ -283,9 +285,9 @@ func TestSuccessfulMigrationWithSourceFileConstruction(t *testing.T) { defer f.Close() _, err2 := f.WriteString(` - [https://app.transifex.com] + [https://www.transifex.com] api_hostname = https://api.transifex.com - hostname = https://app.transifex.com + hostname = https://www.transifex.com username = api password = apassword `) @@ -304,7 +306,7 @@ func TestSuccessfulMigrationWithSourceFileConstruction(t *testing.T) { _, err2 = f.WriteString(` [main] - host = https://app.transifex.com + host = https://www.transifex.com [projslug.ares] file_filter = locale/.po minimum_perc = 0 @@ -330,7 +332,7 @@ func TestSuccessfulMigrationWithSourceFileConstruction(t *testing.T) { assert.Equal(t, cfg.GetActiveHost().RestHostname, "") assert.Equal(t, cfg.Local.Resources[0].OrganizationSlug, "") - _, err = MigrateLegacyConfigFile(&cfg, api) + _, _, err = MigrateLegacyConfigFile(&cfg, api) if err != nil { t.Error(err) } @@ -385,9 +387,9 @@ func TestNeedsTokenInRootConfig(t *testing.T) { defer f.Close() _, err2 := f.WriteString(` - [https://app.transifex.com] + [https://www.transifex.com] api_hostname = https://api.transifex.com - hostname = https://app.transifex.com + hostname = https://www.transifex.com username = tk password = apassword `) @@ -406,7 +408,7 @@ func TestNeedsTokenInRootConfig(t *testing.T) { _, err2 = f.WriteString(` [main] - host = https://app.transifex.com + host = https://www.transifex.com [projslug.ares] file_filter = locale/.po minimum_perc = 0 @@ -431,7 +433,7 @@ func TestNeedsTokenInRootConfig(t *testing.T) { r, w, _ := os.Pipe() os.Stdout = w - _, _ = MigrateLegacyConfigFile(&cfg, api) + _, _, _ = MigrateLegacyConfigFile(&cfg, api) w.Close() out, _ := ioutil.ReadAll(r) @@ -451,12 +453,40 @@ func TestNoTransifexRcFile(t *testing.T) { fmt.Println("Delete error:", err) } } + + // Get user's home directory + homeDir, err := os.UserHomeDir() + if err != nil { + t.Fatal(err) + } + userTransifexRcPath := filepath.Join(homeDir, ".transifexrc") + userTransifexRcBackupPath := filepath.Join(homeDir, ".transifexrc.testbak") + + // Check if user's .transifexrc exists and create a backup if needed + if _, err := os.Stat(userTransifexRcPath); err == nil { + err = os.Rename(userTransifexRcPath, userTransifexRcBackupPath) + if err != nil { + t.Fatal(err) + } + } + + // Clean up any ~/.transifexrc that got created during the test, and restore the user's original + defer func() { + os.Remove(userTransifexRcPath) + if _, err = os.Stat(userTransifexRcBackupPath); err == nil { + err = os.Rename(userTransifexRcBackupPath, userTransifexRcPath) + if err != nil { + t.Fatal(err) + } + } + }() + // Requests Data mockData := jsonapi.MockData{ "/organizations": jsonapi.GetMockTextResponse(`{"data": []}`), } - // Create deprecated config & .transifexrc + // Create deprecated config without .transifexrc pkgDir, _ := os.Getwd() tmpDir, err := os.MkdirTemp("", "") if err != nil { @@ -479,7 +509,7 @@ func TestNoTransifexRcFile(t *testing.T) { _, err2 := f.WriteString(` [main] - host = https://app.transifex.com + host = https://www.transifex.com [projslug.ares] file_filter = locale/.po minimum_perc = 0 @@ -503,12 +533,13 @@ func TestNoTransifexRcFile(t *testing.T) { r, w, _ := os.Pipe() os.Stdout = w - _, _ = MigrateLegacyConfigFile(&cfg, api) + _, _, _ = MigrateLegacyConfigFile(&cfg, api) w.Close() out, _ := ioutil.ReadAll(r) os.Stdout = rescueStdout + assert.True(t, strings.Contains(string(out), "Root configuration file not found")) assert.True(t, strings.Contains(string(out), "Please provide an API token to continue.")) } @@ -606,9 +637,9 @@ func TestResourceMigrationFailed(t *testing.T) { defer f.Close() _, err2 := f.WriteString(` - [https://app.transifex.com] + [https://www.transifex.com] api_hostname = https://api.transifex.com - hostname = https://app.transifex.com + hostname = https://www.transifex.com username = api password = apassword `) @@ -627,7 +658,7 @@ func TestResourceMigrationFailed(t *testing.T) { _, err2 = f.WriteString(` [main] - host = https://app.transifex.com + host = https://www.transifex.com [projslug1.ares] file_filter = locale/.po minimum_perc = 10 @@ -661,7 +692,7 @@ func TestResourceMigrationFailed(t *testing.T) { r, w, _ := os.Pipe() os.Stdout = w - _, err = MigrateLegacyConfigFile(&cfg, api) + _, _, err = MigrateLegacyConfigFile(&cfg, api) if err != nil { t.Error(err) } @@ -686,7 +717,7 @@ func TestResourceMigrationFailed(t *testing.T) { string(content), "minimum_perc = 0")) } -func TestBackUpFileCreated(t *testing.T) { +func TestBackUpFilesCreated(t *testing.T) { var afterTest = func(pkgDir string, tmpDir string) { err := os.Chdir(pkgDir) if err != nil { @@ -747,9 +778,9 @@ func TestBackUpFileCreated(t *testing.T) { defer f.Close() _, err2 := f.WriteString(` - [https://app.transifex.com] + [https://www.transifex.com] api_hostname = https://api.transifex.com - hostname = https://app.transifex.com + hostname = https://www.transifex.com username = api password = apassword `) @@ -768,7 +799,7 @@ func TestBackUpFileCreated(t *testing.T) { _, err2 = f.WriteString(` [main] - host = https://app.transifex.com + host = https://www.transifex.com [projslug.ares] file_filter = locale/.po minimum_perc = 0 @@ -789,22 +820,37 @@ func TestBackUpFileCreated(t *testing.T) { api := jsonapi.GetTestConnection(mockData) - backupFilePath, _ := MigrateLegacyConfigFile(&cfg, api) + backupFilePath, backupRootFilePath, _ := MigrateLegacyConfigFile(&cfg, api) - newContent, err := ioutil.ReadFile(filepath.Join(tmpDir, "config")) + newLocalContent, err := ioutil.ReadFile(filepath.Join(tmpDir, "config")) if err != nil { t.Error(err) } - buContent, err := ioutil.ReadFile(filepath.Join(backupFilePath)) + buLocalContent, err := ioutil.ReadFile(filepath.Join(backupFilePath)) if err != nil { t.Error(err) } + newRootContent, err2 := ioutil.ReadFile(filepath.Join(tmpDir, ".transifexrc")) + if err2 != nil { + t.Error(err2) + } + buRootContent, err2 := ioutil.ReadFile(filepath.Join(backupRootFilePath)) + if err2 != nil { + t.Error(err2) + } + if err != nil { - t.Errorf("A backup file was expected %s", err.Error()) + t.Errorf("A backup local config file was expected %s", err.Error()) + } + + if err2 != nil { + t.Errorf("A backup root config file was expected %s", err2.Error()) } - assert.True(t, strings.Contains(string(buContent), "[projslug.ares]")) - assert.True(t, strings.Contains(string(newContent), + assert.True(t, strings.Contains(string(buLocalContent), "[projslug.ares]")) + assert.True(t, strings.Contains(string(newLocalContent), "o:org:p:projslug:r:ares")) + assert.True(t, strings.Contains(string(buRootContent), "[https://www.transifex.com]")) + assert.True(t, strings.Contains(string(newRootContent), "[https://app.transifex.com]")) }