From 1400fbc716394b9ee17d6171de840e3d712acbfc Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 14:27:52 +0100 Subject: [PATCH 01/11] CON-1896 jobs progress --- .../resolve/{resolve.go => fallbacks.go} | 0 cmd/helpers/resolve/output_params.go | 39 ++++++++ cmd/init/cmd_init.go | 4 +- cmd/jobs/cmd_jobs.go | 47 ++++++++++ cmd/jobs/progress/cmd_progress.go | 88 +++++++++++++++++++ cmd/jobs/progress/run.go | 42 +++++++++ cmd/jobs/srv_initializer.go | 31 +++++++ cmd/mt/detect/cmd_detect.go | 3 +- cmd/mt/detect/run.go | 10 ++- cmd/mt/detect/run_test.go | 6 +- cmd/mt/resolve.go | 34 ------- cmd/mt/translate/cmd_translate.go | 3 +- cmd/mt/translate/run.go | 10 ++- cmd/mt/translate/run_test.go | 10 +-- go.mod | 2 +- go.sum | 4 + main.go | 7 ++ output/jobs/static_base.go | 41 +++++++++ output/mt/base.go | 7 -- output/mt/init_render.go | 4 +- output/params.go | 8 ++ services/files/run_push.go | 2 +- services/files/run_status.go | 4 +- services/helpers/glob_files/glob_files.go | 2 - services/jobs/run_progress.go | 79 +++++++++++++++++ services/jobs/service.go | 23 +++++ services/mt/service.go | 3 +- 27 files changed, 443 insertions(+), 70 deletions(-) rename cmd/helpers/resolve/{resolve.go => fallbacks.go} (100%) create mode 100644 cmd/helpers/resolve/output_params.go create mode 100644 cmd/jobs/cmd_jobs.go create mode 100644 cmd/jobs/progress/cmd_progress.go create mode 100644 cmd/jobs/progress/run.go create mode 100644 cmd/jobs/srv_initializer.go create mode 100644 output/jobs/static_base.go create mode 100644 output/params.go create mode 100644 services/jobs/run_progress.go create mode 100644 services/jobs/service.go diff --git a/cmd/helpers/resolve/resolve.go b/cmd/helpers/resolve/fallbacks.go similarity index 100% rename from cmd/helpers/resolve/resolve.go rename to cmd/helpers/resolve/fallbacks.go diff --git a/cmd/helpers/resolve/output_params.go b/cmd/helpers/resolve/output_params.go new file mode 100644 index 0000000..f2472ca --- /dev/null +++ b/cmd/helpers/resolve/output_params.go @@ -0,0 +1,39 @@ +package resolve + +import ( + "github.com/Smartling/smartling-cli/output" + clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" + + "github.com/spf13/cobra" +) + +// OutputParams resolve OutputParams for subcommands +func OutputParams(cmd *cobra.Command, fileConfigMTFileFormat *string) (output.Params, error) { + const outputTemplateFlag = "format" + format, err := cmd.Parent().PersistentFlags().GetString("output") + if err != nil { + return output.Params{}, clierror.UIError{ + Operation: "get output", + Err: err, + Description: "unable to get output param", + } + } + template := FallbackString(cmd.Flags().Lookup(outputTemplateFlag), StringParam{ + FlagName: outputTemplateFlag, + Config: fileConfigMTFileFormat, + }) + + mode, err := cmd.Parent().PersistentFlags().GetString("output-mode") + if err != nil { + return output.Params{}, clierror.UIError{ + Operation: "get output mode", + Err: err, + Description: "unable to get output mode param", + } + } + return output.Params{ + Mode: mode, + Format: format, + Template: template, + }, nil +} diff --git a/cmd/init/cmd_init.go b/cmd/init/cmd_init.go index 50fdbd5..d4c361c 100644 --- a/cmd/init/cmd_init.go +++ b/cmd/init/cmd_init.go @@ -12,9 +12,7 @@ import ( "github.com/spf13/cobra" ) -var ( - dryRun bool -) +var dryRun bool // NewInitCmd creates a new command to initialize the Smartling CLI. func NewInitCmd(srvInitializer SrvInitializer) *cobra.Command { diff --git a/cmd/jobs/cmd_jobs.go b/cmd/jobs/cmd_jobs.go new file mode 100644 index 0000000..a5aff76 --- /dev/null +++ b/cmd/jobs/cmd_jobs.go @@ -0,0 +1,47 @@ +package jobs + +import ( + "fmt" + "slices" + "strings" + + "github.com/spf13/cobra" +) + +const ( + outputFormatFlag = "output" +) + +var ( + outputFormat string + allowedOutputs = []string{ + "json", + "simple", + } + joinedAllowedOutputs = strings.Join(allowedOutputs, ", ") +) + +// NewJobsCmd returns new jobs command +func NewJobsCmd() *cobra.Command { + jobsCmd := &cobra.Command{ + Use: "jobs", + Short: "Handles job subcommands.", + Long: `Handles job subcommands. Subcommands are a high-level abstraction layer over the underlying Job APIs.`, + PreRunE: func(cmd *cobra.Command, args []string) error { + if !slices.Contains(allowedOutputs, outputFormat) { + return fmt.Errorf("invalid output: %s (allowed: %s)", outputFormat, joinedAllowedOutputs) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 && cmd.Flags().NFlag() == 0 { + return cmd.Help() + } + return nil + }, + } + + jobsCmd.PersistentFlags().StringVar(&outputFormat, outputFormatFlag, "simple", "Output format: "+joinedAllowedOutputs) + + return jobsCmd +} diff --git a/cmd/jobs/progress/cmd_progress.go b/cmd/jobs/progress/cmd_progress.go new file mode 100644 index 0000000..09160bd --- /dev/null +++ b/cmd/jobs/progress/cmd_progress.go @@ -0,0 +1,88 @@ +package progress + +import ( + "errors" + "fmt" + "strings" + + rootcmd "github.com/Smartling/smartling-cli/cmd" + "github.com/Smartling/smartling-cli/cmd/helpers/resolve" + jobscmd "github.com/Smartling/smartling-cli/cmd/jobs" + "github.com/Smartling/smartling-cli/output" + clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" + srv "github.com/Smartling/smartling-cli/services/jobs" + + "github.com/spf13/cobra" +) + +const ( + outputFormatFlag = "output" +) + +var ( + outputFormat string + allowedOutputs = []string{ + "json", + "simple", + } + joinedAllowedOutputs = strings.Join(allowedOutputs, ", ") +) + +// NewProgressCmd returns new progress command +func NewProgressCmd(initializer jobscmd.SrvInitializer) *cobra.Command { + progressCmd := &cobra.Command{ + Use: "progress ", + Short: "Get job progress by the translationJobUid or translationJobName.", + Long: `Get job progress by the translationJobUid or translationJobName.`, + Example: ` +# Get job progress + + smartling-cli jobs progress aabbccdd + +`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return clierror.UIError{ + Operation: "check args", + Err: errors.New("wrong argument quantity"), + Description: fmt.Sprintf("expected one argument, got: %d", len(args)), + } + } + var idOrName string + if len(args) == 1 { + idOrName = args[0] + } + + ctx := cmd.Context() + + cnf, err := rootcmd.Config() + if err != nil { + return err + } + + accountUID, err := resolve.FallbackAccount(cmd.Root().PersistentFlags().Lookup("account"), cnf.AccountID) + if err != nil { + return err + } + + params := srv.ProgressParams{ + AccountUID: accountUID, + ProjectUID: cnf.ProjectID, + JobIDOrName: idOrName, + } + format, err := cmd.Parent().PersistentFlags().GetString("output") + if err != nil { + return err + } + outputParams := output.Params{Format: format} + if err != nil { + return err + } + return run(ctx, initializer, params, outputParams) + }, + } + + progressCmd.PersistentFlags().StringVar(&outputFormat, outputFormatFlag, "simple", "Output format: "+joinedAllowedOutputs) + + return progressCmd +} diff --git a/cmd/jobs/progress/run.go b/cmd/jobs/progress/run.go new file mode 100644 index 0000000..7022fbe --- /dev/null +++ b/cmd/jobs/progress/run.go @@ -0,0 +1,42 @@ +package progress + +import ( + "context" + + jobscmd "github.com/Smartling/smartling-cli/cmd/jobs" + "github.com/Smartling/smartling-cli/output" + "github.com/Smartling/smartling-cli/output/jobs" + clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" + "github.com/Smartling/smartling-cli/services/helpers/rlog" + srv "github.com/Smartling/smartling-cli/services/jobs" +) + +func run(ctx context.Context, + initializer jobscmd.SrvInitializer, + params srv.ProgressParams, + outputParams output.Params, +) error { + rlog.Debugf("running progress with params: %v", params) + jobSrv, err := initializer.InitJobSrv() + if err != nil { + return clierror.UIError{ + Operation: "init", + Err: err, + Description: "unable to initialize Jobs service", + } + } + + progressOutput, err := jobSrv.RunProgress(ctx, params) + if err != nil { + return err + } + + if progressOutput.TranslationJobUID == "" { + rlog.Infof("no jobs found for given translationJobUid or translationJobName: %s", params.JobIDOrName) + return nil + } + + outputFormat := jobs.GetOutputFormat(outputParams.Format) + outputFormat.FormatAndRender(progressOutput) + return nil +} diff --git a/cmd/jobs/srv_initializer.go b/cmd/jobs/srv_initializer.go new file mode 100644 index 0000000..b8884ec --- /dev/null +++ b/cmd/jobs/srv_initializer.go @@ -0,0 +1,31 @@ +package jobs + +import ( + rootcmd "github.com/Smartling/smartling-cli/cmd" + srv "github.com/Smartling/smartling-cli/services/jobs" + + jobapi "github.com/Smartling/api-sdk-go/api/job" +) + +// SrvInitializer defines files service initializer +type SrvInitializer interface { + InitJobSrv() (srv.Service, error) +} + +// NewSrvInitializer returns new SrvInitializer implementation +func NewSrvInitializer() SrvInitializer { + return srvInitializer{} +} + +type srvInitializer struct{} + +// InitJobSrv initializes `job` service with the client and configuration. +func (i srvInitializer) InitJobSrv() (srv.Service, error) { + client, err := rootcmd.Client() + if err != nil { + return nil, err + } + jobApi := jobapi.NewJob(client.Client) + jobSrv := srv.NewService(jobApi) + return jobSrv, nil +} diff --git a/cmd/mt/detect/cmd_detect.go b/cmd/mt/detect/cmd_detect.go index b97cef4..a2c720a 100644 --- a/cmd/mt/detect/cmd_detect.go +++ b/cmd/mt/detect/cmd_detect.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/Smartling/smartling-cli/cmd/helpers/resolve" mtcmd "github.com/Smartling/smartling-cli/cmd/mt" output "github.com/Smartling/smartling-cli/output/mt" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" @@ -69,7 +70,7 @@ func NewDetectCmd(initializer mtcmd.SrvInitializer) *cobra.Command { } } - outputParams, err := mtcmd.ResolveOutputParams(cmd, fileConfig.MT.FileFormat) + outputParams, err := resolve.OutputParams(cmd, fileConfig.MT.FileFormat) if err != nil { return err } diff --git a/cmd/mt/detect/run.go b/cmd/mt/detect/run.go index c189e00..18ce58e 100644 --- a/cmd/mt/detect/run.go +++ b/cmd/mt/detect/run.go @@ -6,7 +6,8 @@ import ( "time" mtcmd "github.com/Smartling/smartling-cli/cmd/mt" - output "github.com/Smartling/smartling-cli/output/mt" + "github.com/Smartling/smartling-cli/output" + mtoutput "github.com/Smartling/smartling-cli/output/mt" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" "github.com/Smartling/smartling-cli/services/helpers/rlog" srv "github.com/Smartling/smartling-cli/services/mt" @@ -17,7 +18,8 @@ import ( func run(ctx context.Context, initializer mtcmd.SrvInitializer, params srv.DetectParams, - outputParams output.OutputParams) error { + outputParams output.Params, +) error { rlog.Debugf("running detect with params: %v", params) mtSrv, err := initializer.InitMTSrv() if err != nil { @@ -35,8 +37,8 @@ func run(ctx context.Context, Description: "unable to get input files", } } - var dataProvider output.DetectDataProvider - render := output.InitRender(outputParams, dataProvider, files, 1) + var dataProvider mtoutput.DetectDataProvider + render := mtoutput.InitRender(outputParams, dataProvider, files, 1) renderRun := make(chan struct{}) var runGroup errgroup.Group runGroup.Go(func() error { diff --git a/cmd/mt/detect/run_test.go b/cmd/mt/detect/run_test.go index 3d92010..91976a7 100644 --- a/cmd/mt/detect/run_test.go +++ b/cmd/mt/detect/run_test.go @@ -6,7 +6,7 @@ import ( "testing" cmdmocks "github.com/Smartling/smartling-cli/cmd/mt/mocks" - output "github.com/Smartling/smartling-cli/output/mt" + "github.com/Smartling/smartling-cli/output" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" srv "github.com/Smartling/smartling-cli/services/mt" srvmocks "github.com/Smartling/smartling-cli/services/mt/mocks" @@ -29,7 +29,7 @@ func TestRunGetFilesError(t *testing.T) { initializer.On("InitMTSrv").Return(mtSrv, nil) mtSrv.On("GetFiles", params.InputDirectory, params.FileOrPattern).Return(nil, filesErr) - err := run(ctx, initializer, params, output.OutputParams{}) + err := run(ctx, initializer, params, output.Params{}) assert.Error(t, err) uiErr, ok := err.(clierror.UIError) @@ -56,7 +56,7 @@ func TestRun(t *testing.T) { mtSrv.On("RunDetect", mock.Anything, params, files, mock.Anything). Return([]srv.DetectOutput{}, nil) - err := run(ctx, initializer, params, output.OutputParams{}) + err := run(ctx, initializer, params, output.Params{}) assert.Nil(t, err) } diff --git a/cmd/mt/resolve.go b/cmd/mt/resolve.go index 00e5924..7cdc7b9 100644 --- a/cmd/mt/resolve.go +++ b/cmd/mt/resolve.go @@ -3,45 +3,11 @@ package mt import ( "os" - "github.com/Smartling/smartling-cli/cmd/helpers/resolve" - output "github.com/Smartling/smartling-cli/output/mt" - clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" "github.com/Smartling/smartling-cli/services/helpers/env" "github.com/spf13/cobra" ) -// ResolveOutputParams resolve OutputParams for subcommands -func ResolveOutputParams(cmd *cobra.Command, fileConfigMTFileFormat *string) (output.OutputParams, error) { - const outputTemplateFlag = "format" - format, err := cmd.Parent().PersistentFlags().GetString("output") - if err != nil { - return output.OutputParams{}, clierror.UIError{ - Operation: "get output", - Err: err, - Description: "unable to get output param", - } - } - template := resolve.FallbackString(cmd.Flags().Lookup(outputTemplateFlag), resolve.StringParam{ - FlagName: outputTemplateFlag, - Config: fileConfigMTFileFormat, - }) - - mode, err := cmd.Parent().PersistentFlags().GetString("output-mode") - if err != nil { - return output.OutputParams{}, clierror.UIError{ - Operation: "get output mode", - Err: err, - Description: "unable to get output mode param", - } - } - return output.OutputParams{ - Mode: mode, - Format: format, - Template: template, - }, nil -} - func resolveConfigDirectory(cmd *cobra.Command) string { flag := cmd.Root().PersistentFlags().Lookup("operation-directory") if flag == nil { diff --git a/cmd/mt/translate/cmd_translate.go b/cmd/mt/translate/cmd_translate.go index 0d90be4..2ca0333 100644 --- a/cmd/mt/translate/cmd_translate.go +++ b/cmd/mt/translate/cmd_translate.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/Smartling/smartling-cli/cmd/helpers/resolve" mtcmd "github.com/Smartling/smartling-cli/cmd/mt" output "github.com/Smartling/smartling-cli/output/mt" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" @@ -69,7 +70,7 @@ func NewTranslateCmd(initializer mtcmd.SrvInitializer) *cobra.Command { } } - outputParams, err := mtcmd.ResolveOutputParams(cmd, fileConfig.MT.FileFormat) + outputParams, err := resolve.OutputParams(cmd, fileConfig.MT.FileFormat) if err != nil { return err } diff --git a/cmd/mt/translate/run.go b/cmd/mt/translate/run.go index d7b0eaa..d200807 100644 --- a/cmd/mt/translate/run.go +++ b/cmd/mt/translate/run.go @@ -6,7 +6,8 @@ import ( "time" mtcmd "github.com/Smartling/smartling-cli/cmd/mt" - output "github.com/Smartling/smartling-cli/output/mt" + "github.com/Smartling/smartling-cli/output" + mtoutput "github.com/Smartling/smartling-cli/output/mt" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" "github.com/Smartling/smartling-cli/services/helpers/rlog" srv "github.com/Smartling/smartling-cli/services/mt" @@ -18,7 +19,8 @@ func run(ctx context.Context, initializer mtcmd.SrvInitializer, params srv.TranslateParams, fileOrPattern string, - outputParams output.OutputParams) error { + outputParams output.Params, +) error { rlog.Debugf("running translate with params: %v", params) mtSrv, err := initializer.InitMTSrv() if err != nil { @@ -36,8 +38,8 @@ func run(ctx context.Context, Description: "unable to get input files", } } - var dataProvider output.TranslateDataProvider - render := output.InitRender(outputParams, dataProvider, files, uint8(len(params.TargetLocales))) + var dataProvider mtoutput.TranslateDataProvider + render := mtoutput.InitRender(outputParams, dataProvider, files, uint8(len(params.TargetLocales))) renderRun := make(chan struct{}) var runGroup errgroup.Group runGroup.Go(func() error { diff --git a/cmd/mt/translate/run_test.go b/cmd/mt/translate/run_test.go index 56d3bde..67dfa5b 100644 --- a/cmd/mt/translate/run_test.go +++ b/cmd/mt/translate/run_test.go @@ -3,16 +3,16 @@ package translate import ( "context" "errors" - "github.com/stretchr/testify/mock" "testing" cmdmocks "github.com/Smartling/smartling-cli/cmd/mt/mocks" - output "github.com/Smartling/smartling-cli/output/mt" + "github.com/Smartling/smartling-cli/output" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" srv "github.com/Smartling/smartling-cli/services/mt" srvmocks "github.com/Smartling/smartling-cli/services/mt/mocks" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestRunGetFilesError(t *testing.T) { @@ -29,7 +29,7 @@ func TestRunGetFilesError(t *testing.T) { initializer.On("InitMTSrv").Return(mtSrv, nil) mtSrv.On("GetFiles", params.InputDirectory, fileOrPattern).Return(nil, filesErr) - err := run(ctx, initializer, params, fileOrPattern, output.OutputParams{}) + err := run(ctx, initializer, params, fileOrPattern, output.Params{}) assert.Error(t, err) uiErr, ok := err.(clierror.UIError) @@ -56,7 +56,7 @@ func TestRun(t *testing.T) { mtSrv.On("RunTranslate", mock.Anything, params, files, mock.Anything). Return([]srv.TranslateOutput{}, nil) - err := run(ctx, initializer, params, fileOrPattern, output.OutputParams{}) + err := run(ctx, initializer, params, fileOrPattern, output.Params{}) assert.Nil(t, err) } @@ -87,7 +87,7 @@ func TestRunRunTranslateErr(t *testing.T) { mtSrv.On("RunTranslate", mock.Anything, params, files, mock.Anything). Return([]srv.TranslateOutput{}, err) - errRun := run(ctx, initializer, params, fileOrPattern, output.OutputParams{}) + errRun := run(ctx, initializer, params, fileOrPattern, output.Params{}) assert.NotNil(t, errRun) uiErrRun, ok := errRun.(clierror.UIError) diff --git a/go.mod b/go.mod index 1e45d57..b995c5a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( dario.cat/mergo v1.0.2 - github.com/Smartling/api-sdk-go v0.0.0-20251121172444-c0e20a51c934 + github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 6f6fa80..f428c44 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Smartling/api-sdk-go v0.0.0-20251121172444-c0e20a51c934 h1:CLIk1fG+TwinPSEWQzHI+GIcj1ctMhTTvsvAbxY6JZM= github.com/Smartling/api-sdk-go v0.0.0-20251121172444-c0e20a51c934/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= +github.com/Smartling/api-sdk-go v0.0.0-20251203110833-ec19f746ede1 h1:10NVB18x28aPJpAIvZs6A0zTcu7VFm9OfklK2AVvySQ= +github.com/Smartling/api-sdk-go v0.0.0-20251203110833-ec19f746ede1/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= +github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d h1:6JQ5dLG1eYngPQDvCgvKL+adVlu2W9QtbURQMHCSyP0= +github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= diff --git a/main.go b/main.go index f287052..c5e4871 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,8 @@ import ( "github.com/Smartling/smartling-cli/cmd/files/rename" "github.com/Smartling/smartling-cli/cmd/files/status" initialize "github.com/Smartling/smartling-cli/cmd/init" + "github.com/Smartling/smartling-cli/cmd/jobs" + "github.com/Smartling/smartling-cli/cmd/jobs/progress" "github.com/Smartling/smartling-cli/cmd/mt" "github.com/Smartling/smartling-cli/cmd/mt/detect" "github.com/Smartling/smartling-cli/cmd/mt/translate" @@ -58,6 +60,11 @@ func main() { mtCmd.AddCommand(detect.NewDetectCmd(mtInitializer)) mtCmd.AddCommand(translate.NewTranslateCmd(mtInitializer)) + jobsCmd := jobs.NewJobsCmd() + rootCmd.AddCommand(jobsCmd) + jobInitializer := jobs.NewSrvInitializer() + jobsCmd.AddCommand(progress.NewProgressCmd(jobInitializer)) + if err := rootCmd.Execute(); err != nil { output.RenderAndExitIfErr(err) } diff --git a/output/jobs/static_base.go b/output/jobs/static_base.go new file mode 100644 index 0000000..96d7ea5 --- /dev/null +++ b/output/jobs/static_base.go @@ -0,0 +1,41 @@ +package jobs + +import ( + "fmt" + + "github.com/Smartling/smartling-cli/services/jobs" +) + +// OutputFormat defines behaviour to format and render data +type OutputFormat interface { + FormatAndRender(data jobs.ProgressOutput) +} + +// GetOutputFormat returns OutputFormat for given string +func GetOutputFormat(outputFormat string) OutputFormat { + switch outputFormat { + case "json": + return JsonOutputFormat{} + case "simple": + return SimpleOutputFormat{} + } + return SimpleOutputFormat{} +} + +// JsonOutputFormat is json output format for rendering data as json +type JsonOutputFormat struct{} + +// FormatAndRender marshals table data into JSON and prints it. +func (j JsonOutputFormat) FormatAndRender(data jobs.ProgressOutput) { + fmt.Println(string(data.Json)) +} + +// SimpleOutputFormat is simple output format for rendering data using a text template +type SimpleOutputFormat struct{} + +// FormatAndRender formats the data rows using the stored template string +// and outputs the rendered result +func (s SimpleOutputFormat) FormatAndRender(data jobs.ProgressOutput) { + fmt.Println("Total word count: ", data.TotalWordCount) + fmt.Println("Percent complete: ", data.PercentComplete) +} diff --git a/output/mt/base.go b/output/mt/base.go index b5c32c5..607d37f 100644 --- a/output/mt/base.go +++ b/output/mt/base.go @@ -17,13 +17,6 @@ type Model struct { RowByHeader RowByHeaderName } -// OutputParams defines the output configuration options for rendering data -type OutputParams struct { - Mode string - Format string - Template string -} - // RenderAndExitIfErr renders error and exit iff err func RenderAndExitIfErr(err error) { if err == nil { diff --git a/output/mt/init_render.go b/output/mt/init_render.go index d1da294..0444e96 100644 --- a/output/mt/init_render.go +++ b/output/mt/init_render.go @@ -1,7 +1,9 @@ package mt +import "github.com/Smartling/smartling-cli/output" + // InitRender inits render for mt subcommands -func InitRender(outputParams OutputParams, dataProvider TableDataProvider, files []string, targetLocalesQnt uint8) Renderer { +func InitRender(outputParams output.Params, dataProvider TableDataProvider, files []string, targetLocalesQnt uint8) Renderer { var render Renderer = &Static{} if outputParams.Mode == "dynamic" { render = &Dynamic{} diff --git a/output/params.go b/output/params.go new file mode 100644 index 0000000..78c09a0 --- /dev/null +++ b/output/params.go @@ -0,0 +1,8 @@ +package output + +// Params defines the output configuration options for rendering data +type Params struct { + Mode string + Format string + Template string +} diff --git a/services/files/run_push.go b/services/files/run_push.go index 787c184..e6fef2e 100644 --- a/services/files/run_push.go +++ b/services/files/run_push.go @@ -150,7 +150,7 @@ func (s service) runPushWithJob(ctx context.Context, params PushParams, files [] var jobName string if re := regexp.MustCompile(pattern); params.JobIDOrName != "" && re.MatchString(params.JobIDOrName) { jobUID = params.JobIDOrName - jobNameResponse, err := s.JobApi.GetJob(projectID, jobUID) + jobNameResponse, err := s.JobApi.Get(projectID, jobUID) if err != nil { return clierror.UIError{ Err: err, diff --git a/services/files/run_status.go b/services/files/run_status.go index a2c3f0a..74ffb63 100644 --- a/services/files/run_status.go +++ b/services/files/run_status.go @@ -39,9 +39,9 @@ func (s service) RunStatus(params StatusParams) error { return err } - var tableWriter = table.NewTableWriter(os.Stdout) + tableWriter := table.NewTableWriter(os.Stdout) - var progress = progress.Progress{ + progress := progress.Progress{ Total: len(files), } diff --git a/services/helpers/glob_files/glob_files.go b/services/helpers/glob_files/glob_files.go index fc9343a..c8db7bb 100644 --- a/services/helpers/glob_files/glob_files.go +++ b/services/helpers/glob_files/glob_files.go @@ -134,7 +134,6 @@ func LocallyFunc( return nil }, ) - if err != nil { return nil, hierr.Errorf( err, @@ -197,7 +196,6 @@ func LocallyFn( return nil }, ) - if err != nil { return nil, hierr.Errorf( err, diff --git a/services/jobs/run_progress.go b/services/jobs/run_progress.go new file mode 100644 index 0000000..677bbf7 --- /dev/null +++ b/services/jobs/run_progress.go @@ -0,0 +1,79 @@ +package jobs + +import ( + "context" + "regexp" + + "github.com/Smartling/api-sdk-go/api/job" + api "github.com/Smartling/api-sdk-go/api/mt" +) + +// ProgressParams is the parameters for the RunProgress method. +type ProgressParams struct { + AccountUID api.AccountUID + ProjectUID string + JobIDOrName string +} + +func (p ProgressParams) Validate() error { + if err := p.AccountUID.Validate(); err != nil { + return err + } + return nil +} + +func (s service) RunProgress(ctx context.Context, params ProgressParams) (ProgressOutput, error) { + if err := params.Validate(); err != nil { + return ProgressOutput{}, err + } + + pattern := `^[a-z0-9]{12}$` + var translationJobUID string + if re := regexp.MustCompile(pattern); params.JobIDOrName != "" && re.MatchString(params.JobIDOrName) { + if jb, err := s.job.Get(params.ProjectUID, params.JobIDOrName); err == nil { + translationJobUID = jb.TranslationJobUID + } + } + if translationJobUID == "" { + jobs, err := s.job.GetAllByName(params.ProjectUID, params.JobIDOrName) + if err != nil { + return ProgressOutput{}, err + } + if len(jobs) == 0 { + return ProgressOutput{}, nil + } + job := job.FindFirstJobByName(jobs, params.JobIDOrName) + translationJobUID = job.TranslationJobUID + } + if translationJobUID == "" { + return ProgressOutput{}, nil + } + + progress, err := s.job.Progress(params.ProjectUID, translationJobUID) + if err != nil { + return ProgressOutput{}, err + } + + return ProgressOutput{ + TranslationJobUID: translationJobUID, + TotalWordCount: progress.TotalWordCount, + PercentComplete: progress.PercentComplete, + Json: progress.Json, + }, nil +} + +// ProgressOutput represents the result of a job progress +type ProgressOutput struct { + TranslationJobUID string + TotalWordCount uint32 + PercentComplete uint32 + Json []byte +} + +// DetectUpdates defines updates +type DetectUpdates struct { + ID uint32 + Language *string + Upload *bool + Detect *string +} diff --git a/services/jobs/service.go b/services/jobs/service.go new file mode 100644 index 0000000..9de1b1d --- /dev/null +++ b/services/jobs/service.go @@ -0,0 +1,23 @@ +package jobs + +import ( + "context" + + api "github.com/Smartling/api-sdk-go/api/job" +) + +// Service defines behavior for interacting with Smartling MT. +type Service interface { + RunProgress(ctx context.Context, p ProgressParams) (ProgressOutput, error) +} + +// NewService creates a new implementation of the Service +func NewService(job api.Job) Service { + return service{ + job: job, + } +} + +type service struct { + job api.Job +} diff --git a/services/mt/service.go b/services/mt/service.go index 98dd3dc..bfe34cf 100644 --- a/services/mt/service.go +++ b/services/mt/service.go @@ -21,7 +21,8 @@ type Service interface { // NewService creates a new implementation of the Service func NewService(downloader api.Downloader, fileTranslator api.FileTranslator, - uploader api.Uploader, translationControl api.TranslationControl) Service { + uploader api.Uploader, translationControl api.TranslationControl, +) Service { return service{ downloader: downloader, fileTranslator: fileTranslator, From 33a8474f4518ce971705301ad044f1977a34a68d Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 14:29:49 +0100 Subject: [PATCH 02/11] go mod tidy --- go.mod | 10 +++++----- go.sum | 24 ++++++++++-------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index b995c5a..e1e1049 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/spf13/pflag v1.0.9 github.com/stretchr/testify v1.10.0 github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 - golang.org/x/sync v0.16.0 + golang.org/x/sync v0.18.0 ) require ( @@ -43,9 +43,9 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/zazab/zhash v0.0.0-20221031090444-2b0d50417446 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f428c44..3d884bf 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,6 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Smartling/api-sdk-go v0.0.0-20251121172444-c0e20a51c934 h1:CLIk1fG+TwinPSEWQzHI+GIcj1ctMhTTvsvAbxY6JZM= -github.com/Smartling/api-sdk-go v0.0.0-20251121172444-c0e20a51c934/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= -github.com/Smartling/api-sdk-go v0.0.0-20251203110833-ec19f746ede1 h1:10NVB18x28aPJpAIvZs6A0zTcu7VFm9OfklK2AVvySQ= -github.com/Smartling/api-sdk-go v0.0.0-20251203110833-ec19f746ede1/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d h1:6JQ5dLG1eYngPQDvCgvKL+adVlu2W9QtbURQMHCSyP0= github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -80,20 +76,20 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/zazab/zhash v0.0.0-20221031090444-2b0d50417446 h1:75pcOSsb40+ub185cJI7g5uykl9Uu76rD5ONzK/4s40= github.com/zazab/zhash v0.0.0-20221031090444-2b0d50417446/go.mod h1:NtepZ8TEXErPsmQDMUoN72f8aIy4+xNinSJ3f1giess= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= From a01e411b7ccd7837613b4f02f2b07b8cb3fbb722 Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 14:33:37 +0100 Subject: [PATCH 03/11] updated md docs --- docs/smartling-cli.md | 3 +- docs/smartling-cli_completion.md | 2 +- docs/smartling-cli_completion_bash.md | 2 +- docs/smartling-cli_completion_fish.md | 2 +- docs/smartling-cli_completion_powershell.md | 2 +- docs/smartling-cli_completion_zsh.md | 2 +- docs/smartling-cli_files.md | 2 +- docs/smartling-cli_files_delete.md | 2 +- docs/smartling-cli_files_import.md | 2 +- docs/smartling-cli_files_list.md | 2 +- docs/smartling-cli_files_pull.md | 2 +- docs/smartling-cli_files_push.md | 2 +- docs/smartling-cli_files_rename.md | 2 +- docs/smartling-cli_files_status.md | 2 +- docs/smartling-cli_init.md | 2 +- docs/smartling-cli_jobs.md | 51 +++++++++++++++++ docs/smartling-cli_jobs_progress.md | 61 +++++++++++++++++++++ docs/smartling-cli_mt.md | 2 +- docs/smartling-cli_mt_detect.md | 2 +- docs/smartling-cli_mt_translate.md | 2 +- docs/smartling-cli_projects.md | 2 +- docs/smartling-cli_projects_info.md | 2 +- docs/smartling-cli_projects_list.md | 2 +- docs/smartling-cli_projects_locales.md | 2 +- 24 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 docs/smartling-cli_jobs.md create mode 100644 docs/smartling-cli_jobs_progress.md diff --git a/docs/smartling-cli.md b/docs/smartling-cli.md index 84f7460..c7de9bd 100644 --- a/docs/smartling-cli.md +++ b/docs/smartling-cli.md @@ -43,7 +43,8 @@ smartling-cli [flags] * [smartling-cli completion](smartling-cli_completion.md) - Generate the autocompletion script for the specified shell * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. * [smartling-cli init](smartling-cli_init.md) - Prepares project to work with Smartling +* [smartling-cli jobs](smartling-cli_jobs.md) - Handles job subcommands. * [smartling-cli mt](smartling-cli_mt.md) - File Machine Translations * [smartling-cli projects](smartling-cli_projects.md) - Used to access various projects sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_completion.md b/docs/smartling-cli_completion.md index 4502f15..3dd84d3 100644 --- a/docs/smartling-cli_completion.md +++ b/docs/smartling-cli_completion.md @@ -48,4 +48,4 @@ See each sub-command's help for details on how to use the generated script. * [smartling-cli completion powershell](smartling-cli_completion_powershell.md) - Generate the autocompletion script for powershell * [smartling-cli completion zsh](smartling-cli_completion_zsh.md) - Generate the autocompletion script for zsh -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_completion_bash.md b/docs/smartling-cli_completion_bash.md index 0bd5de5..e619702 100644 --- a/docs/smartling-cli_completion_bash.md +++ b/docs/smartling-cli_completion_bash.md @@ -67,4 +67,4 @@ smartling-cli completion bash * [smartling-cli completion](smartling-cli_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_completion_fish.md b/docs/smartling-cli_completion_fish.md index 185a440..1e3049a 100644 --- a/docs/smartling-cli_completion_fish.md +++ b/docs/smartling-cli_completion_fish.md @@ -58,4 +58,4 @@ smartling-cli completion fish [flags] * [smartling-cli completion](smartling-cli_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_completion_powershell.md b/docs/smartling-cli_completion_powershell.md index e1b77cc..0235ff1 100644 --- a/docs/smartling-cli_completion_powershell.md +++ b/docs/smartling-cli_completion_powershell.md @@ -55,4 +55,4 @@ smartling-cli completion powershell [flags] * [smartling-cli completion](smartling-cli_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_completion_zsh.md b/docs/smartling-cli_completion_zsh.md index eb13feb..2d12eff 100644 --- a/docs/smartling-cli_completion_zsh.md +++ b/docs/smartling-cli_completion_zsh.md @@ -69,4 +69,4 @@ smartling-cli completion zsh [flags] * [smartling-cli completion](smartling-cli_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files.md b/docs/smartling-cli_files.md index ee9126b..c2b95cd 100644 --- a/docs/smartling-cli_files.md +++ b/docs/smartling-cli_files.md @@ -64,4 +64,4 @@ smartling-cli files [flags] * [smartling-cli files rename](smartling-cli_files_rename.md) - Renames given file by old URI into new URI. * [smartling-cli files status](smartling-cli_files_status.md) - Shows file translation status. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_delete.md b/docs/smartling-cli_files_delete.md index 443663c..4798789 100644 --- a/docs/smartling-cli_files_delete.md +++ b/docs/smartling-cli_files_delete.md @@ -76,4 +76,4 @@ smartling-cli files delete [flags] * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_import.md b/docs/smartling-cli_files_import.md index 8d02d4c..9eb0eb1 100644 --- a/docs/smartling-cli_files_import.md +++ b/docs/smartling-cli_files_import.md @@ -81,4 +81,4 @@ smartling-cli files import [flags] * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_list.md b/docs/smartling-cli_files_list.md index be53ded..5ae8b3a 100644 --- a/docs/smartling-cli_files_list.md +++ b/docs/smartling-cli_files_list.md @@ -121,4 +121,4 @@ smartling-cli files list [flags] * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_pull.md b/docs/smartling-cli_files_pull.md index b1917ba..5efb368 100644 --- a/docs/smartling-cli_files_pull.md +++ b/docs/smartling-cli_files_pull.md @@ -160,4 +160,4 @@ smartling-cli files pull [flags] * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_push.md b/docs/smartling-cli_files_push.md index aba4e9a..ec608a8 100644 --- a/docs/smartling-cli_files_push.md +++ b/docs/smartling-cli_files_push.md @@ -151,4 +151,4 @@ smartling-cli files push --job [--authorize] [--locale < * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_rename.md b/docs/smartling-cli_files_rename.md index 2ec55ad..01e2e0d 100644 --- a/docs/smartling-cli_files_rename.md +++ b/docs/smartling-cli_files_rename.md @@ -62,4 +62,4 @@ smartling-cli files rename [flags] * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_files_status.md b/docs/smartling-cli_files_status.md index fb7c687..49f4e8f 100644 --- a/docs/smartling-cli_files_status.md +++ b/docs/smartling-cli_files_status.md @@ -114,4 +114,4 @@ smartling-cli files status [flags] * [smartling-cli files](smartling-cli_files.md) - Used to access various files sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_init.md b/docs/smartling-cli_init.md index ac89ab3..b33ef12 100644 --- a/docs/smartling-cli_init.md +++ b/docs/smartling-cli_init.md @@ -106,4 +106,4 @@ smartling-cli init [flags] * [smartling-cli](smartling-cli.md) - Manage translation files using Smartling CLI. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_jobs.md b/docs/smartling-cli_jobs.md new file mode 100644 index 0000000..f2b342c --- /dev/null +++ b/docs/smartling-cli_jobs.md @@ -0,0 +1,51 @@ +## smartling-cli jobs + +Handles job subcommands. + +### Synopsis + +Handles job subcommands. Subcommands are a high-level abstraction layer over the underlying Job APIs. + +``` +smartling-cli jobs [flags] +``` + +### Options + +``` + -h, --help help for jobs + --output string Output format: json, simple (default "simple") +``` + +### Options inherited from parent commands + +``` + -a, --account string Account ID to operate on. + This option overrides config value "account_id". + -c, --config string Config file in YAML format. + By default CLI will look for file named + "smartling.yml" in current directory and in all + intermediate parents, emulating git behavior. + -k, --insecure Skip HTTPS certificate validation. + --operation-directory string Sets directory to operate on, usually, to store or to + read files. Depends on command. (default ".") + -p, --project string Project ID to operate on. + This option overrides config value "project_id". + --proxy string Use specified URL as proxy server. + --secret string Token Secret which will be used for authentication. + This option overrides config value "secret". + --smartling-url string Specify base Smartling URL, merely for testing + purposes. + --threads uint32 If command can be executed concurrently, it will be + executed for at most of threads. (default 4) + --user string User ID which will be used for authentication. + This option overrides config value "user_id". + -v, --verbose count Verbose logging +``` + +### SEE ALSO + +* [smartling-cli](smartling-cli.md) - Manage translation files using Smartling CLI. +* [smartling-cli jobs progress](smartling-cli_jobs_progress.md) - Get job progress by the translationJobUid or translationJobName. + +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_jobs_progress.md b/docs/smartling-cli_jobs_progress.md new file mode 100644 index 0000000..813058c --- /dev/null +++ b/docs/smartling-cli_jobs_progress.md @@ -0,0 +1,61 @@ +## smartling-cli jobs progress + +Get job progress by the translationJobUid or translationJobName. + +### Synopsis + +Get job progress by the translationJobUid or translationJobName. + +``` +smartling-cli jobs progress [flags] +``` + +### Examples + +``` + +# Get job progress + + smartling-cli jobs progress aabbccdd + + +``` + +### Options + +``` + -h, --help help for progress + --output string Output format: json, simple (default "simple") +``` + +### Options inherited from parent commands + +``` + -a, --account string Account ID to operate on. + This option overrides config value "account_id". + -c, --config string Config file in YAML format. + By default CLI will look for file named + "smartling.yml" in current directory and in all + intermediate parents, emulating git behavior. + -k, --insecure Skip HTTPS certificate validation. + --operation-directory string Sets directory to operate on, usually, to store or to + read files. Depends on command. (default ".") + -p, --project string Project ID to operate on. + This option overrides config value "project_id". + --proxy string Use specified URL as proxy server. + --secret string Token Secret which will be used for authentication. + This option overrides config value "secret". + --smartling-url string Specify base Smartling URL, merely for testing + purposes. + --threads uint32 If command can be executed concurrently, it will be + executed for at most of threads. (default 4) + --user string User ID which will be used for authentication. + This option overrides config value "user_id". + -v, --verbose count Verbose logging +``` + +### SEE ALSO + +* [smartling-cli jobs](smartling-cli_jobs.md) - Handles job subcommands. + +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_mt.md b/docs/smartling-cli_mt.md index b77e75a..10fd014 100644 --- a/docs/smartling-cli_mt.md +++ b/docs/smartling-cli_mt.md @@ -50,4 +50,4 @@ smartling-cli mt [flags] * [smartling-cli mt detect](smartling-cli_mt_detect.md) - Detect the source language of files using Smartling's File MT API. * [smartling-cli mt translate](smartling-cli_mt_translate.md) - Translate files using Smartling's File Machine Translation API. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_mt_detect.md b/docs/smartling-cli_mt_detect.md index 2d3d681..d7882aa 100644 --- a/docs/smartling-cli_mt_detect.md +++ b/docs/smartling-cli_mt_detect.md @@ -69,4 +69,4 @@ smartling-cli mt detect [flags] * [smartling-cli mt](smartling-cli_mt.md) - File Machine Translations -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_mt_translate.md b/docs/smartling-cli_mt_translate.md index 91fd2c2..079e970 100644 --- a/docs/smartling-cli_mt_translate.md +++ b/docs/smartling-cli_mt_translate.md @@ -83,4 +83,4 @@ smartling-cli mt translate [flags] * [smartling-cli mt](smartling-cli_mt.md) - File Machine Translations -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_projects.md b/docs/smartling-cli_projects.md index 3ae1bb4..01775a0 100644 --- a/docs/smartling-cli_projects.md +++ b/docs/smartling-cli_projects.md @@ -49,4 +49,4 @@ smartling-cli projects [flags] * [smartling-cli projects list](smartling-cli_projects_list.md) - Lists projects for current account. * [smartling-cli projects locales](smartling-cli_projects_locales.md) - Display list of target locales. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_projects_info.md b/docs/smartling-cli_projects_info.md index f705ce8..ec7b7b1 100644 --- a/docs/smartling-cli_projects_info.md +++ b/docs/smartling-cli_projects_info.md @@ -72,4 +72,4 @@ smartling-cli projects info [flags] * [smartling-cli projects](smartling-cli_projects.md) - Used to access various projects sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_projects_list.md b/docs/smartling-cli_projects_list.md index 74c2e17..f27ca1d 100644 --- a/docs/smartling-cli_projects_list.md +++ b/docs/smartling-cli_projects_list.md @@ -85,4 +85,4 @@ smartling-cli projects list [flags] * [smartling-cli projects](smartling-cli_projects.md) - Used to access various projects sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 diff --git a/docs/smartling-cli_projects_locales.md b/docs/smartling-cli_projects_locales.md index 19d15d0..22ebeac 100644 --- a/docs/smartling-cli_projects_locales.md +++ b/docs/smartling-cli_projects_locales.md @@ -101,4 +101,4 @@ smartling-cli projects locales [flags] * [smartling-cli projects](smartling-cli_projects.md) - Used to access various projects sub-commands. -###### Auto generated by spf13/cobra on 26-Nov-2025 +###### Auto generated by spf13/cobra on 3-Dec-2025 From 93851012f11a7ba89e545ee085f4bc144acedfc0 Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 17:30:36 +0100 Subject: [PATCH 04/11] CR changes --- cmd/jobs/progress/cmd_progress.go | 27 ++------------------------- services/jobs/run_progress.go | 8 -------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/cmd/jobs/progress/cmd_progress.go b/cmd/jobs/progress/cmd_progress.go index 09160bd..ed71650 100644 --- a/cmd/jobs/progress/cmd_progress.go +++ b/cmd/jobs/progress/cmd_progress.go @@ -3,7 +3,6 @@ package progress import ( "errors" "fmt" - "strings" rootcmd "github.com/Smartling/smartling-cli/cmd" "github.com/Smartling/smartling-cli/cmd/helpers/resolve" @@ -15,19 +14,6 @@ import ( "github.com/spf13/cobra" ) -const ( - outputFormatFlag = "output" -) - -var ( - outputFormat string - allowedOutputs = []string{ - "json", - "simple", - } - joinedAllowedOutputs = strings.Join(allowedOutputs, ", ") -) - // NewProgressCmd returns new progress command func NewProgressCmd(initializer jobscmd.SrvInitializer) *cobra.Command { progressCmd := &cobra.Command{ @@ -41,6 +27,7 @@ func NewProgressCmd(initializer jobscmd.SrvInitializer) *cobra.Command { `, RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() if len(args) != 1 { return clierror.UIError{ Operation: "check args", @@ -48,12 +35,7 @@ func NewProgressCmd(initializer jobscmd.SrvInitializer) *cobra.Command { Description: fmt.Sprintf("expected one argument, got: %d", len(args)), } } - var idOrName string - if len(args) == 1 { - idOrName = args[0] - } - - ctx := cmd.Context() + idOrName := args[0] cnf, err := rootcmd.Config() if err != nil { @@ -75,14 +57,9 @@ func NewProgressCmd(initializer jobscmd.SrvInitializer) *cobra.Command { return err } outputParams := output.Params{Format: format} - if err != nil { - return err - } return run(ctx, initializer, params, outputParams) }, } - progressCmd.PersistentFlags().StringVar(&outputFormat, outputFormatFlag, "simple", "Output format: "+joinedAllowedOutputs) - return progressCmd } diff --git a/services/jobs/run_progress.go b/services/jobs/run_progress.go index 677bbf7..b375023 100644 --- a/services/jobs/run_progress.go +++ b/services/jobs/run_progress.go @@ -69,11 +69,3 @@ type ProgressOutput struct { PercentComplete uint32 Json []byte } - -// DetectUpdates defines updates -type DetectUpdates struct { - ID uint32 - Language *string - Upload *bool - Detect *string -} From 041b889f80a669dcc8841eea11db09d01418aa36 Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 17:43:57 +0100 Subject: [PATCH 05/11] tests --- go.mod | 2 +- go.sum | 2 + tests/cmd/jobs/progress/init_test.go | 61 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/cmd/jobs/progress/init_test.go diff --git a/go.mod b/go.mod index e1e1049..e167cf1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( dario.cat/mergo v1.0.2 - github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d + github.com/Smartling/api-sdk-go v0.0.0-20251203164006-535aa854a7c6 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 3d884bf..7eb3eee 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d h1:6JQ5dLG1eYngPQDvCgvKL+adVlu2W9QtbURQMHCSyP0= github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= +github.com/Smartling/api-sdk-go v0.0.0-20251203164006-535aa854a7c6 h1:Q33HZq0yX6DOJ5RB1mDOsomZA704mt4R7/1VuaXi7u4= +github.com/Smartling/api-sdk-go v0.0.0-20251203164006-535aa854a7c6/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= diff --git a/tests/cmd/jobs/progress/init_test.go b/tests/cmd/jobs/progress/init_test.go new file mode 100644 index 0000000..eb8ef03 --- /dev/null +++ b/tests/cmd/jobs/progress/init_test.go @@ -0,0 +1,61 @@ +package progress + +import ( + "os/exec" + "strings" + "testing" +) + +func TestProgress(t *testing.T) { + subCommands := []string{"jobs", "progress", "CLI uploads"} + expectedOutputs := []string{"Total word count", "Percent complete"} + unexpectedOutputs := []string{"DEBUG", "ERROR"} + + out, err := exec.Command( + "./../../bin/smartling-cli", subCommands...). + CombinedOutput() + if err != nil { + t.Fatalf("error: %v, output: %s", err, string(out)) + } + if len(expectedOutputs) > 0 { + for _, expectedOutput := range expectedOutputs { + if !strings.Contains(string(out), expectedOutput) { + t.Errorf("output: %s\nwithout expected: %s", string(out), expectedOutput) + } + } + } + if len(unexpectedOutputs) > 0 { + for _, unexpectedOutput := range unexpectedOutputs { + if strings.Contains(string(out), unexpectedOutput) { + t.Errorf("output: %s\nwith unexpected: %s", string(out), unexpectedOutput) + } + } + } +} + +func TestProgressJsonFormat(t *testing.T) { + subCommands := []string{"jobs", "progress", "CLI uploads", "--output", "json"} + expectedOutputs := []string{"totalWordCount", "percentComplete", "workflowProgressReportList"} + unexpectedOutputs := []string{"DEBUG", "ERROR"} + + out, err := exec.Command( + "./../../bin/smartling-cli", subCommands...). + CombinedOutput() + if err != nil { + t.Fatalf("error: %v, output: %s", err, string(out)) + } + if len(expectedOutputs) > 0 { + for _, expectedOutput := range expectedOutputs { + if !strings.Contains(string(out), expectedOutput) { + t.Errorf("output: %s\nwithout expected: %s", string(out), expectedOutput) + } + } + } + if len(unexpectedOutputs) > 0 { + for _, unexpectedOutput := range unexpectedOutputs { + if strings.Contains(string(out), unexpectedOutput) { + t.Errorf("output: %s\nwith unexpected: %s", string(out), unexpectedOutput) + } + } + } +} From 217a008b3edc8fc691ea6b638edc81bfe0a00cc5 Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 17:55:05 +0100 Subject: [PATCH 06/11] CR changes --- go.mod | 2 +- go.sum | 2 ++ services/jobs/run_progress.go | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e167cf1..3dc0b15 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( dario.cat/mergo v1.0.2 - github.com/Smartling/api-sdk-go v0.0.0-20251203164006-535aa854a7c6 + github.com/Smartling/api-sdk-go v0.0.0-20251203165231-3d4239a9a340 github.com/charmbracelet/bubbles v0.21.0 github.com/charmbracelet/bubbletea v1.3.6 github.com/charmbracelet/lipgloss v1.1.0 diff --git a/go.sum b/go.sum index 7eb3eee..fb0032c 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d h1:6JQ5dLG1eY github.com/Smartling/api-sdk-go v0.0.0-20251203132206-6cb1a9c0fe6d/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= github.com/Smartling/api-sdk-go v0.0.0-20251203164006-535aa854a7c6 h1:Q33HZq0yX6DOJ5RB1mDOsomZA704mt4R7/1VuaXi7u4= github.com/Smartling/api-sdk-go v0.0.0-20251203164006-535aa854a7c6/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= +github.com/Smartling/api-sdk-go v0.0.0-20251203165231-3d4239a9a340 h1:UkdvEu7EvItSZYwiwEUm+MExiFp/c0vV+Ltfo4qVL/s= +github.com/Smartling/api-sdk-go v0.0.0-20251203165231-3d4239a9a340/go.mod h1:BSUxirtjHu/mZmb41K2sKo8B8C2tfRP3NMCCV6yP27k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= diff --git a/services/jobs/run_progress.go b/services/jobs/run_progress.go index b375023..dd17a37 100644 --- a/services/jobs/run_progress.go +++ b/services/jobs/run_progress.go @@ -42,8 +42,9 @@ func (s service) RunProgress(ctx context.Context, params ProgressParams) (Progre if len(jobs) == 0 { return ProgressOutput{}, nil } - job := job.FindFirstJobByName(jobs, params.JobIDOrName) - translationJobUID = job.TranslationJobUID + if j, found := job.FindFirstJobByName(jobs, params.JobIDOrName); found { + translationJobUID = j.TranslationJobUID + } } if translationJobUID == "" { return ProgressOutput{}, nil From a5b6a01386c76bee7b71c809843e1d95caf7a8c2 Mon Sep 17 00:00:00 2001 From: az-smartling Date: Wed, 3 Dec 2025 17:59:31 +0100 Subject: [PATCH 07/11] added job test to Makefile --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 2817231..c28e8d1 100644 --- a/Makefile +++ b/Makefile @@ -87,6 +87,7 @@ test_unit: test_integration: go test ./tests/cmd/files/push/... go test ./tests/cmd/files/pull/... + go test ./tests/cmd/jobs/progress/... go test ./tests/cmd/files/list/... go test ./tests/cmd/files/status/... go test ./tests/cmd/files/rename/... From e7d6c3c3e1f7510f1244af4be45b0d1733ba1060 Mon Sep 17 00:00:00 2001 From: Dmitry Studynsky Date: Wed, 24 Dec 2025 12:19:10 -0500 Subject: [PATCH 08/11] CON-1896 Update command descriptions. --- cmd/jobs/cmd_jobs.go | 27 ++++++++++- cmd/jobs/progress/cmd_progress.go | 79 +++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/cmd/jobs/cmd_jobs.go b/cmd/jobs/cmd_jobs.go index a5aff76..0e0e248 100644 --- a/cmd/jobs/cmd_jobs.go +++ b/cmd/jobs/cmd_jobs.go @@ -25,8 +25,31 @@ var ( func NewJobsCmd() *cobra.Command { jobsCmd := &cobra.Command{ Use: "jobs", - Short: "Handles job subcommands.", - Long: `Handles job subcommands. Subcommands are a high-level abstraction layer over the underlying Job APIs.`, + Short: "Manage translation jobs and monitor their progress.", + Long: `Translation jobs are the fundamental unit of work in Smartling TMS that organize +content for translation and track it through the translation workflow. + +The jobs command group provides tools to interact with translation jobs, including +monitoring translation progress, viewing job details, and managing job workflows. + +Each job contains one or more files targeted for translation into specific locales, +with defined due dates and workflow steps. Jobs help coordinate translation work between +content owners, project managers, and translators. + +Available options: + --output string Output format: ` + joinedAllowedOutputs + ` (default "simple") + - simple: Human-readable format optimized for terminal display + - json: Raw API response for programmatic processing and automation`, + Example: ` +# View job progress in human-readable format + + smartling-cli jobs progress "Website Q1 2026" + +# Get detailed progress data for automation + + smartling-cli jobs progress aabbccdd --output json + +`, PreRunE: func(cmd *cobra.Command, args []string) error { if !slices.Contains(allowedOutputs, outputFormat) { return fmt.Errorf("invalid output: %s (allowed: %s)", outputFormat, joinedAllowedOutputs) diff --git a/cmd/jobs/progress/cmd_progress.go b/cmd/jobs/progress/cmd_progress.go index ed71650..3d55894 100644 --- a/cmd/jobs/progress/cmd_progress.go +++ b/cmd/jobs/progress/cmd_progress.go @@ -9,6 +9,7 @@ import ( jobscmd "github.com/Smartling/smartling-cli/cmd/jobs" "github.com/Smartling/smartling-cli/output" clierror "github.com/Smartling/smartling-cli/services/helpers/cli_error" + "github.com/Smartling/smartling-cli/services/helpers/help" srv "github.com/Smartling/smartling-cli/services/jobs" "github.com/spf13/cobra" @@ -18,12 +19,82 @@ import ( func NewProgressCmd(initializer jobscmd.SrvInitializer) *cobra.Command { progressCmd := &cobra.Command{ Use: "progress ", - Short: "Get job progress by the translationJobUid or translationJobName.", - Long: `Get job progress by the translationJobUid or translationJobName.`, + Short: "Track translation progress for a specific job.", + Long: `smartling-cli jobs progress [--output json] + +Retrieves real-time translation progress metrics for a specific translation job. +This command is essential for monitoring active translations, estimating completion times, +and tracking workflow progress across multiple locales. + +Progress information includes total word counts, completion percentages, and detailed +per-locale breakdowns showing how content moves through each translation workflow step +(awaiting authorization, in translation, completed, etc.). + +The command accepts either: + • Translation Job UID: 12-character alphanumeric identifier (e.g., aabbccdd1122) + • Translation Job Name: Human-readable name assigned when creating the job + +If multiple jobs share the same name, the most recent active job (not Canceled or Closed) +will be selected. + +Output Formats: + + --output simple (default) + Displays key progress metrics in human-readable format: + - Total word count across all locales + - Overall completion percentage + Best for: Quick status checks, manual monitoring, terminal viewing + + --output json + Returns the complete API response as JSON, including: + - Per-locale progress breakdowns + - Workflow step details (authorized, awaiting, completed, etc.) + - String and word counts at each workflow stage + - Target locale descriptions + Best for: Automation scripts, CI/CD pipelines, custom reporting tools + +Use Cases: + • Monitor active translation projects to estimate delivery times + • Track progress before authorizing next workflow steps + • Build automated alerts when translations reach completion thresholds + • Generate custom progress reports for stakeholders + • Integrate with CI/CD pipelines to gate deployments on translation completion + +Project Configuration: + Project ID must be configured in smartling.yml or specified via --project flag. + Account ID can be configured in smartling.yml or specified via --account flag. + +Authentication is required via user_id and secret in smartling.yml or environment variables. + +Available options:` + help.AuthenticationOptions, Example: ` -# Get job progress +# Check progress using job name + + smartling-cli jobs progress "Website Q1 2026" + +# Check progress using job UID + + smartling-cli jobs progress aabbccdd1122 + +# Get detailed JSON output for automation + + smartling-cli jobs progress "Mobile App Release" --output json + +# Use with specific project + + smartling-cli jobs progress aabbccdd1122 --project 9876543210 + +# Parse JSON output in scripts (example: check if job is 100% complete) + + PROGRESS=$(smartling-cli jobs progress my-job --output json | jq '.percentComplete') + if [ "$PROGRESS" -eq 100 ]; then + echo "Translation complete!" + fi + +# Monitor progress for CI/CD gate - smartling-cli jobs progress aabbccdd + smartling-cli jobs progress "Release v2.0" --output json | \ + jq -e '.percentComplete >= 95' && echo "Ready for deployment" `, RunE: func(cmd *cobra.Command, args []string) error { From df90e7cf6b1c35756418eb6ad103c1ed32bc1970 Mon Sep 17 00:00:00 2001 From: Dmitry Studynsky Date: Wed, 24 Dec 2025 12:19:37 -0500 Subject: [PATCH 09/11] Enhanced CLAUDE.md. Updated Makefile for module vendoring and improved build process. --- CLAUDE.md | 33 +++++++++++++++++++++++++-------- Makefile | 3 ++- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a5bff21..f312de2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -This is the Smartling CLI tool - a command-line interface for managing translation files through the Smartling platform. The CLI provides commands for file operations (push, pull, list, status, rename, delete, import), project management, and machine translation services. +This is the Smartling CLI tool - a command-line interface for managing translation files through the Smartling platform. The CLI provides commands for file operations (push, pull, list, status, rename, delete, import), project management, machine translation services, and job progress tracking. ## Architecture @@ -14,8 +14,9 @@ The codebase follows a layered architecture pattern: - Each command has its own subdirectory with command definition and tests - Service initializers are used to inject dependencies into commands - **services/**: Business logic layer containing service implementations - - Each service corresponds to a command group (files, projects, init, mt) + - Each service corresponds to a command group (files, projects, init, mt, jobs) - Services interact with the Smartling API SDK + - **services/helpers/**: Shared utilities for config management, error handling, progress rendering, format compilation, and thread pooling - **output/**: Rendering and formatting layer for CLI output - **main.go**: Entry point that wires together all commands and their dependencies @@ -32,16 +33,20 @@ go build # Build for current platform ### Testing ```bash -make test_unit # Run unit tests -make test_integration # Run integration tests (requires binary in tests/cmd/bin/) -go test ./cmd/... # Run specific unit tests +make test_unit # Run all unit tests +make test_integration # Run all integration tests (requires binary in tests/cmd/bin/) +go test ./cmd/... # Run all unit tests in cmd/ +go test ./cmd/files/push/ # Run tests for a specific command +go test ./tests/cmd/files/push/... # Run specific integration test +go test -v -run TestSpecificFunction ./cmd/... # Run specific test function ``` ### Code Quality ```bash -make lint # Run revive linter +make lint # Run golangci-lint and revive linter make tidy # Clean up go.mod -make mockery # Generate mocks using mockery +make mockery # Generate mocks using mockery (config: .mockery.yml) +make docs # Generate command documentation ``` ### Package Building @@ -82,4 +87,16 @@ The CLI uses YAML configuration files (smartling.yml) that can be placed in the 4. Output formatting is handled in output/ package 5. Configuration is managed through config helpers -The service initializer pattern allows for clean dependency injection and makes the codebase highly testable. \ No newline at end of file +### Service Initializer Pattern + +Each command group (files, projects, init, mt, jobs) follows the same dependency injection pattern: + +1. **Command Group** (e.g., `cmd/files/cmd_files.go`): Defines the `SrvInitializer` interface and factory function +2. **Service Initializer** (e.g., `cmd/files/cmd_files.go`): Implements the initializer that wires up SDK clients and configuration +3. **Service** (e.g., `services/files/service.go`): Defines the Service interface with business logic methods +4. **Command Implementation** (e.g., `cmd/files/push/cmd_push.go`): Uses the initializer to get service instance and executes operations + +This pattern enables: +- Easy mocking of services in command tests +- Centralized client and configuration setup +- Clear separation between CLI interface and business logic \ No newline at end of file diff --git a/Makefile b/Makefile index c28e8d1..bf17a0f 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ build: darwin windows.exe linux get: go get + go mod vendor clean: rm -rf bin pkg @@ -59,7 +60,7 @@ _pkg-init: $(shell git rev-list --count HEAD).$(shell git rev-parse --short HEAD)) %: - GOOS=$(basename $@) go build -o bin/smartling.$@ + GOOS=$(basename $@) go build -mod=mod -o bin/smartling.$@ docs: go run ./main.go docs From 8d3c4e3a637b6f6e9e31637d34bf574f39f97e61 Mon Sep 17 00:00:00 2001 From: Dmitry Studynsky Date: Wed, 24 Dec 2025 13:20:47 -0500 Subject: [PATCH 10/11] Fix cmd_locates_test. --- cmd/projects/locales/cmd_locates_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/projects/locales/cmd_locates_test.go b/cmd/projects/locales/cmd_locates_test.go index 358c1c4..4287627 100644 --- a/cmd/projects/locales/cmd_locates_test.go +++ b/cmd/projects/locales/cmd_locates_test.go @@ -22,10 +22,10 @@ func TestNewLocaLesCmd(t *testing.T) { Source: false, } projectsSrv.On("RunLocales", params).Run(func(args mock.Arguments) { - if _, err := fmt.Fprintf(buf, "RunLocales was called with %d args", len(args)); err != nil { + if _, err := fmt.Fprintf(buf, "RunLocales was called with %d args\n", len(args)); err != nil { t.Fatal(err) } - if _, err := fmt.Fprintf(buf, "params: %v", args[0]); err != nil { + if _, err := fmt.Fprintf(buf, "params: %v\n", args[0]); err != nil { t.Fatal(err) } }).Return(nil) From bf5a56667d27648964806982fa2e180ce072f774 Mon Sep 17 00:00:00 2001 From: Dmitry Studynsky Date: Wed, 24 Dec 2025 13:39:53 -0500 Subject: [PATCH 11/11] Add 'Run Tests' stage to Jenkins pipeline with JUnit reporting --- CLAUDE.md | 38 +++++++++++++++++++++++++++++++++----- Jenkinsfile | 16 +++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f312de2..bbf5247 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,22 +25,50 @@ The CLI uses dependency injection through service initializers, making it testab ## Common Development Commands ### Build + +The project uses a Makefile-based build system that cross-compiles for multiple platforms: + ```bash make all # Clean, get dependencies, and build for all platforms -make build # Build for darwin, windows, linux -go build # Build for current platform +make build # Build for darwin, windows, linux (outputs to bin/ directory) +go build # Build for current platform only ``` +**Build outputs:** +- `bin/smartling.darwin` - macOS binary +- `bin/smartling.windows.exe` - Windows binary +- `bin/smartling.linux` - Linux binary + +**Build time:** Typically takes 10-30 seconds depending on whether dependencies need to be downloaded. + ### Testing + +The project has two test suites: + +#### Unit Tests +Unit tests cover all command and service logic with mocked dependencies: + ```bash -make test_unit # Run all unit tests -make test_integration # Run all integration tests (requires binary in tests/cmd/bin/) +make test_unit # Run all unit tests (recommended) go test ./cmd/... # Run all unit tests in cmd/ go test ./cmd/files/push/ # Run tests for a specific command +go test -v -run TestSpecificFunction ./cmd/... # Run specific test function with verbose output +``` + +**Expected results:** All unit tests should pass. Test suite includes 13+ test packages covering commands for files, projects, machine translation, and initialization. + +**Test time:** Unit tests typically complete in 2-5 seconds (many results are cached). + +#### Integration Tests +Integration tests require a built binary and test the CLI end-to-end: + +```bash +make test_integration # Run all integration tests (requires binary in tests/cmd/bin/) go test ./tests/cmd/files/push/... # Run specific integration test -go test -v -run TestSpecificFunction ./cmd/... # Run specific test function ``` +**Prerequisites:** Binary must be built and placed in `tests/cmd/bin/` before running integration tests. + ### Code Quality ```bash make lint # Run golangci-lint and revive linter diff --git a/Jenkinsfile b/Jenkinsfile index 457711f..9364073 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,9 +8,23 @@ pipeline { } stages { - stage('Build') { + stage('Run Tests') { steps { sh "docker pull golang" + sh """docker run -t --rm -v \${WORKSPACE}:/go/src/cli -w /go/src/cli golang bash -c ' + go install github.com/jstemmer/go-junit-report/v2@latest && \\ + go test -v ./cmd/... 2>&1 | tee /tmp/test-output.txt | /go/bin/go-junit-report -set-exit-code > test-results.xml + '""" + } + post { + always { + junit 'test-results.xml' + } + } + } + + stage('Build') { + steps { sh "docker run -t --rm -v ${WORKSPACE}:/go/src/cli -w /go/src/cli golang make" } }