diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index fa1bff49d6..4480cba12b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,6 +2,7 @@ blank_issues_enabled: false contact_links: - name: Track the Most Upvoted Features url: https://upvotes.getarcane.app/ + about: Track the Most Upvoted Features - name: 💬 Discord url: https://discord.gg/WyXYpdyV3Z about: For help and chatting with the community diff --git a/backend/cli/generate/api_key_test.go b/backend/cli/generate/api_key_test.go index 85473c8903..62897f7d3e 100644 --- a/backend/cli/generate/api_key_test.go +++ b/backend/cli/generate/api_key_test.go @@ -6,7 +6,7 @@ import ( ) func TestAPIKeyCommandAvailableInBackendCLI(t *testing.T) { - cmd := GenerateCmd + cmd := Cmd cmd.SetArgs([]string{"api-key"}) out, err := captureOutput(func() error { diff --git a/backend/cli/generate/generate.go b/backend/cli/generate/generate.go index 95371b9c72..b59d244465 100644 --- a/backend/cli/generate/generate.go +++ b/backend/cli/generate/generate.go @@ -5,4 +5,4 @@ import ( "github.com/spf13/cobra" ) -var GenerateCmd *cobra.Command = cligenerate.GenerateCmd +var Cmd *cobra.Command = cligenerate.Cmd diff --git a/backend/cli/generate/secret_test.go b/backend/cli/generate/secret_test.go index a00e57d768..31f0f576bf 100644 --- a/backend/cli/generate/secret_test.go +++ b/backend/cli/generate/secret_test.go @@ -34,7 +34,7 @@ func captureOutput(fn func() error) (string, error) { } func TestSecretDefaultBase64(t *testing.T) { - cmd := GenerateCmd + cmd := Cmd cmd.SetArgs([]string{"secret"}) out, err := captureOutput(func() error { @@ -81,7 +81,7 @@ func TestSecretDefaultBase64(t *testing.T) { } func TestSecretAllFormatContainsSections(t *testing.T) { - cmd := GenerateCmd + cmd := Cmd cmd.SetArgs([]string{"secret", "-f", "all"}) out, err := captureOutput(func() error { diff --git a/backend/cli/root.go b/backend/cli/root.go index ba7690ceb6..59204e6e0c 100644 --- a/backend/cli/root.go +++ b/backend/cli/root.go @@ -39,7 +39,7 @@ func Execute() { func init() { rootCmd.AddCommand(upgrade.UpgradeCmd) - rootCmd.AddCommand(generate.GenerateCmd) + rootCmd.AddCommand(generate.Cmd) } func getVersion() string { diff --git a/backend/internal/api/diagnostics_handler.go b/backend/internal/api/diagnostics_handler.go index a2348b71be..aaa829460f 100644 --- a/backend/internal/api/diagnostics_handler.go +++ b/backend/internal/api/diagnostics_handler.go @@ -6,7 +6,7 @@ import ( "time" "github.com/getarcaneapp/arcane/backend/internal/middleware" - ws "github.com/getarcaneapp/arcane/backend/pkg/libarcane/ws" + "github.com/getarcaneapp/arcane/backend/pkg/libarcane/ws" "github.com/gin-gonic/gin" ) diff --git a/backend/internal/bootstrap/jobs_bootstrap.go b/backend/internal/bootstrap/jobs_bootstrap.go index e76d544e65..67adac3c17 100644 --- a/backend/internal/bootstrap/jobs_bootstrap.go +++ b/backend/internal/bootstrap/jobs_bootstrap.go @@ -8,47 +8,47 @@ import ( "github.com/getarcaneapp/arcane/backend/internal/config" "github.com/getarcaneapp/arcane/backend/pkg/libarcane" - pkg_scheduler "github.com/getarcaneapp/arcane/backend/pkg/scheduler" + "github.com/getarcaneapp/arcane/backend/pkg/scheduler" ) -func registerJobs(appCtx context.Context, newScheduler *pkg_scheduler.JobScheduler, appServices *Services, appConfig *config.Config) { - autoUpdateJob := pkg_scheduler.NewAutoUpdateJob(appServices.Updater, appServices.Settings) +func registerJobs(appCtx context.Context, newScheduler *scheduler.JobScheduler, appServices *Services, appConfig *config.Config) { + autoUpdateJob := scheduler.NewAutoUpdateJob(appServices.Updater, appServices.Settings) newScheduler.RegisterJob(autoUpdateJob) - imagePollingJob := pkg_scheduler.NewImagePollingJob(appServices.ImageUpdate, appServices.Settings, appServices.Environment) + imagePollingJob := scheduler.NewImagePollingJob(appServices.ImageUpdate, appServices.Settings, appServices.Environment) newScheduler.RegisterJob(imagePollingJob) - environmentHealthJob := pkg_scheduler.NewEnvironmentHealthJob(appServices.Environment, appServices.Settings) + environmentHealthJob := scheduler.NewEnvironmentHealthJob(appServices.Environment, appServices.Settings) if !appConfig.AgentMode { newScheduler.RegisterJob(environmentHealthJob) } - dockerClientRefreshJob := pkg_scheduler.NewDockerClientRefreshJob(appServices.Docker, appServices.Settings) + dockerClientRefreshJob := scheduler.NewDockerClientRefreshJob(appServices.Docker, appServices.Settings) newScheduler.RegisterJob(dockerClientRefreshJob) - analyticsJob := pkg_scheduler.NewAnalyticsJob(appServices.Settings, appServices.KV, nil, appConfig) + analyticsJob := scheduler.NewAnalyticsJob(appServices.Settings, appServices.KV, nil, appConfig) newScheduler.RegisterJob(analyticsJob) // Send initial heartbeat on startup without blocking bootstrap. go analyticsJob.Run(appCtx) - eventCleanupJob := pkg_scheduler.NewEventCleanupJob(appServices.Event, appServices.Settings) + eventCleanupJob := scheduler.NewEventCleanupJob(appServices.Event, appServices.Settings) newScheduler.RegisterJob(eventCleanupJob) - scheduledPruneJob := pkg_scheduler.NewScheduledPruneJob(appServices.System, appServices.Settings, appServices.Notification) + scheduledPruneJob := scheduler.NewScheduledPruneJob(appServices.System, appServices.Settings, appServices.Notification) newScheduler.RegisterJob(scheduledPruneJob) - fsWatcherJob, err := pkg_scheduler.RegisterFilesystemWatcherJob(appCtx, appServices.Project, appServices.Template, appServices.Settings, appConfig.ProjectScanMaxDepth) + fsWatcherJob, err := scheduler.RegisterFilesystemWatcherJob(appCtx, appServices.Project, appServices.Template, appServices.Settings, appConfig.ProjectScanMaxDepth) if err != nil { slog.ErrorContext(appCtx, "Failed to register filesystem watcher job", "error", err) } - gitOpsSyncJob := pkg_scheduler.NewGitOpsSyncJob(appServices.GitOpsSync, appServices.Settings) + gitOpsSyncJob := scheduler.NewGitOpsSyncJob(appServices.GitOpsSync, appServices.Settings) newScheduler.RegisterJob(gitOpsSyncJob) - vulnerabilityScanJob := pkg_scheduler.NewVulnerabilityScanJob(appServices.Vulnerability, appServices.Settings) + vulnerabilityScanJob := scheduler.NewVulnerabilityScanJob(appServices.Vulnerability, appServices.Settings) newScheduler.RegisterJob(vulnerabilityScanJob) - autoHealJob := pkg_scheduler.NewAutoHealJob(appServices.Docker, appServices.Settings, appServices.Event, appServices.Notification) + autoHealJob := scheduler.NewAutoHealJob(appServices.Docker, appServices.Settings, appServices.Event, appServices.Notification) newScheduler.RegisterJob(autoHealJob) setupJobScheduleCallbacks( @@ -73,16 +73,16 @@ func setupJobScheduleCallbacks( lifecycleCtx context.Context, appServices *Services, appConfig *config.Config, - newScheduler *pkg_scheduler.JobScheduler, - imagePollingJob *pkg_scheduler.ImagePollingJob, - autoUpdateJob *pkg_scheduler.AutoUpdateJob, - dockerClientRefreshJob *pkg_scheduler.DockerClientRefreshJob, - environmentHealthJob *pkg_scheduler.EnvironmentHealthJob, - eventCleanupJob *pkg_scheduler.EventCleanupJob, - scheduledPruneJob *pkg_scheduler.ScheduledPruneJob, - gitOpsSyncJob *pkg_scheduler.GitOpsSyncJob, - vulnerabilityScanJob *pkg_scheduler.VulnerabilityScanJob, - autoHealJob *pkg_scheduler.AutoHealJob, + newScheduler *scheduler.JobScheduler, + imagePollingJob *scheduler.ImagePollingJob, + autoUpdateJob *scheduler.AutoUpdateJob, + dockerClientRefreshJob *scheduler.DockerClientRefreshJob, + environmentHealthJob *scheduler.EnvironmentHealthJob, + eventCleanupJob *scheduler.EventCleanupJob, + scheduledPruneJob *scheduler.ScheduledPruneJob, + gitOpsSyncJob *scheduler.GitOpsSyncJob, + vulnerabilityScanJob *scheduler.VulnerabilityScanJob, + autoHealJob *scheduler.AutoHealJob, ) { if appServices.JobSchedule == nil { return @@ -114,16 +114,16 @@ func handleJobScheduleChangeInternal( ctx context.Context, key string, appConfig *config.Config, - newScheduler *pkg_scheduler.JobScheduler, - imagePollingJob *pkg_scheduler.ImagePollingJob, - autoUpdateJob *pkg_scheduler.AutoUpdateJob, - dockerClientRefreshJob *pkg_scheduler.DockerClientRefreshJob, - environmentHealthJob *pkg_scheduler.EnvironmentHealthJob, - eventCleanupJob *pkg_scheduler.EventCleanupJob, - scheduledPruneJob *pkg_scheduler.ScheduledPruneJob, - gitOpsSyncJob *pkg_scheduler.GitOpsSyncJob, - vulnerabilityScanJob *pkg_scheduler.VulnerabilityScanJob, - autoHealJob *pkg_scheduler.AutoHealJob, + newScheduler *scheduler.JobScheduler, + imagePollingJob *scheduler.ImagePollingJob, + autoUpdateJob *scheduler.AutoUpdateJob, + dockerClientRefreshJob *scheduler.DockerClientRefreshJob, + environmentHealthJob *scheduler.EnvironmentHealthJob, + eventCleanupJob *scheduler.EventCleanupJob, + scheduledPruneJob *scheduler.ScheduledPruneJob, + gitOpsSyncJob *scheduler.GitOpsSyncJob, + vulnerabilityScanJob *scheduler.VulnerabilityScanJob, + autoHealJob *scheduler.AutoHealJob, ) { switch key { case "pollingInterval": @@ -168,7 +168,7 @@ func handleJobScheduleChangeInternal( } } -func setupSettingsCallbacks(lifecycleCtx context.Context, appServices *Services, appConfig *config.Config, newScheduler *pkg_scheduler.JobScheduler, imagePollingJob *pkg_scheduler.ImagePollingJob, autoUpdateJob *pkg_scheduler.AutoUpdateJob, environmentHealthJob *pkg_scheduler.EnvironmentHealthJob, fsWatcherJob *pkg_scheduler.FilesystemWatcherJob, scheduledPruneJob *pkg_scheduler.ScheduledPruneJob, vulnerabilityScanJob *pkg_scheduler.VulnerabilityScanJob, autoHealJob *pkg_scheduler.AutoHealJob) { +func setupSettingsCallbacks(lifecycleCtx context.Context, appServices *Services, appConfig *config.Config, newScheduler *scheduler.JobScheduler, imagePollingJob *scheduler.ImagePollingJob, autoUpdateJob *scheduler.AutoUpdateJob, environmentHealthJob *scheduler.EnvironmentHealthJob, fsWatcherJob *scheduler.FilesystemWatcherJob, scheduledPruneJob *scheduler.ScheduledPruneJob, vulnerabilityScanJob *scheduler.VulnerabilityScanJob, autoHealJob *scheduler.AutoHealJob) { appServices.Settings.OnImagePollingSettingsChanged = func(_ context.Context) { if err := newScheduler.RescheduleJob(lifecycleCtx, imagePollingJob); err != nil { slog.WarnContext(lifecycleCtx, "Failed to reschedule image-polling job", "error", err) diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index 3635d94e7c..2bca5e0666 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -141,7 +141,7 @@ func setupRouter(ctx context.Context, cfg *config.Config, appServices *Services) handlers.RegisterWebhookTrigger(apiGroup, appServices.Webhook) //nolint:contextcheck envProxyMiddleware := middleware.NewEnvProxyMiddlewareWithParam( - types.LOCAL_DOCKER_ENVIRONMENT_ID, + types.LocalDockerEnvironmentId, "id", envResolver, createAuthValidator(appServices), diff --git a/backend/internal/common/errors.go b/backend/internal/common/errors.go index b57941d724..fedf090e57 100644 --- a/backend/internal/common/errors.go +++ b/backend/internal/common/errors.go @@ -1211,6 +1211,24 @@ func (e *ApiKeyNotFoundError) Error() string { return "API key not found" } +type ApiKeyExpiredError struct{} + +func (e *ApiKeyExpiredError) Error() string { + return "API key has expired" +} + +type ApiKeyInvalidError struct{} + +func (e *ApiKeyInvalidError) Error() string { + return "invalid API key" +} + +type ApiKeyProtectedError struct{} + +func (e *ApiKeyProtectedError) Error() string { + return "API key is protected" +} + type ApiKeyUpdateError struct { Err error } diff --git a/backend/internal/config/version.go b/backend/internal/config/version.go index 57f629cc94..4072c8fb3b 100644 --- a/backend/internal/config/version.go +++ b/backend/internal/config/version.go @@ -9,6 +9,8 @@ var ( ) // ShortRevision returns the first 8 characters of the revision hash +// +//goland:noinspection GoBoolExpressions func ShortRevision() string { if len(Revision) > 8 { return Revision[:8] diff --git a/backend/internal/database/database.go b/backend/internal/database/database.go index 739c69a3ba..6298a41328 100644 --- a/backend/internal/database/database.go +++ b/backend/internal/database/database.go @@ -83,26 +83,18 @@ func Initialize(ctx context.Context, databaseURL string, options MigrationOption return nil, fmt.Errorf("failed to get sql.DB: %w", err) } - // Determine database provider for migrations + // Determine database provider and choose the correct migration driver. var dbProvider string + var driver database.Driver switch { case strings.HasPrefix(databaseURL, "file:"): dbProvider = "sqlite" + driver, err = sqliteMigrate.WithInstance(sqlDB, &sqliteMigrate.Config{}) case strings.HasPrefix(databaseURL, "postgres"): dbProvider = "postgres" - default: - return nil, fmt.Errorf("unsupported database type in URL: %s", databaseURL) - } - - // Choose the correct driver for migrations - var driver database.Driver - switch dbProvider { - case "sqlite": - driver, err = sqliteMigrate.WithInstance(sqlDB, &sqliteMigrate.Config{}) - case "postgres": driver, err = postgresMigrate.WithInstance(sqlDB, &postgresMigrate.Config{}) default: - return nil, fmt.Errorf("unsupported database provider: %s", dbProvider) + return nil, fmt.Errorf("unsupported database type in URL: %s", databaseURL) } if err != nil { return nil, fmt.Errorf("failed to create migration driver: %w", err) diff --git a/backend/internal/huma/handlers/apikeys.go b/backend/internal/huma/handlers/apikeys.go index a4c8d23727..b120de33c2 100644 --- a/backend/internal/huma/handlers/apikeys.go +++ b/backend/internal/huma/handlers/apikeys.go @@ -256,10 +256,10 @@ func (h *ApiKeyHandler) UpdateApiKey(ctx context.Context, input *UpdateApiKeyInp apiKey, err := h.apiKeyService.UpdateApiKey(ctx, input.ID, input.Body) if err != nil { - if errors.Is(err, services.ErrApiKeyNotFound) { + if _, ok := errors.AsType[*common.ApiKeyNotFoundError](err); ok { return nil, huma.Error404NotFound((&common.ApiKeyNotFoundError{}).Error()) } - if errors.Is(err, services.ErrApiKeyProtected) { + if _, ok := errors.AsType[*common.ApiKeyProtectedError](err); ok { return nil, huma.Error403Forbidden("static API keys cannot be updated") } return nil, huma.Error500InternalServerError((&common.ApiKeyUpdateError{Err: err}).Error()) @@ -285,10 +285,10 @@ func (h *ApiKeyHandler) DeleteApiKey(ctx context.Context, input *DeleteApiKeyInp } if err := h.apiKeyService.DeleteApiKey(ctx, input.ID); err != nil { - if errors.Is(err, services.ErrApiKeyNotFound) { + if _, ok := errors.AsType[*common.ApiKeyNotFoundError](err); ok { return nil, huma.Error404NotFound((&common.ApiKeyNotFoundError{}).Error()) } - if errors.Is(err, services.ErrApiKeyProtected) { + if _, ok := errors.AsType[*common.ApiKeyProtectedError](err); ok { return nil, huma.Error403Forbidden("static API keys cannot be deleted") } return nil, huma.Error500InternalServerError((&common.ApiKeyDeletionError{Err: err}).Error()) diff --git a/backend/internal/huma/handlers/containers.go b/backend/internal/huma/handlers/containers.go index bf4e789b40..cdc2994c2c 100644 --- a/backend/internal/huma/handlers/containers.go +++ b/backend/internal/huma/handlers/containers.go @@ -25,7 +25,7 @@ type ContainerHandler struct { settingsService *services.SettingsService } -// Paginated response +// ContainerPaginatedResponse response type ContainerPaginatedResponse struct { Success bool `json:"success"` Data []containertypes.Summary `json:"data"` @@ -122,7 +122,7 @@ type DeleteContainerOutput struct { Body ContainerActionResponse } -// RegisterContainers registers container endpoints. +// SetAutoUpdateInput sets the auto update value for a container. type SetAutoUpdateInput struct { EnvironmentID string `path:"id" doc:"Environment ID"` ContainerID string `path:"containerId" doc:"Container ID"` diff --git a/backend/internal/huma/handlers/customize.go b/backend/internal/huma/handlers/customize.go index f60d86ec26..49e1d73883 100644 --- a/backend/internal/huma/handlers/customize.go +++ b/backend/internal/huma/handlers/customize.go @@ -80,7 +80,7 @@ func (h *CustomizeHandler) Search(ctx context.Context, input *SearchCustomizeInp results := h.customizeSearchService.Search(input.Body.Query) if !humamw.IsAdminFromContext(ctx) { - filtered := []category.Category{} + var filtered []category.Category for _, cat := range results.Results { if cat.ID != "registries" && cat.ID != "variables" { filtered = append(filtered, cat) @@ -104,7 +104,7 @@ func (h *CustomizeHandler) GetCategories(ctx context.Context, input *GetCustomiz categories := h.customizeSearchService.GetCustomizeCategories() if !humamw.IsAdminFromContext(ctx) { - filtered := []category.Category{} + var filtered []category.Category for _, cat := range categories { if cat.ID != "registries" && cat.ID != "variables" { filtered = append(filtered, cat) diff --git a/backend/internal/huma/handlers/environments.go b/backend/internal/huma/handlers/environments.go index 3f4a127e56..fc0c990c11 100644 --- a/backend/internal/huma/handlers/environments.go +++ b/backend/internal/huma/handlers/environments.go @@ -536,7 +536,7 @@ func (h *EnvironmentHandler) createEnvironmentWithApiKey(ctx context.Context, en func (h *EnvironmentHandler) createEnvironmentLegacy(ctx context.Context, env *models.Environment, user *models.User, body environment.Create) (*CreateEnvironmentOutput, error) { // Legacy pairing flows if (body.AccessToken == nil || *body.AccessToken == "") && body.BootstrapToken != nil && *body.BootstrapToken != "" { - token, err := h.environmentService.PairAgentWithBootstrap(ctx, body.ApiUrl, *body.BootstrapToken) + token, err := h.environmentService.PairAgentWithBootstrap(ctx, body.ApiUrl, *body.BootstrapToken) //nolint:staticcheck if err != nil { slog.ErrorContext(ctx, "Failed to pair with agent", "apiUrl", body.ApiUrl, "error", err.Error()) return nil, huma.Error502BadGateway((&common.AgentPairingError{Err: err}).Error()) @@ -878,7 +878,7 @@ func (h *EnvironmentHandler) handleEnvironmentPairing(ctx context.Context, envir pairingSucceeded := false if isLocalEnv { - return pairingSucceeded, nil + return false, nil } if req.AccessToken == nil && req.BootstrapToken != nil && *req.BootstrapToken != "" { diff --git a/backend/internal/huma/handlers/projects.go b/backend/internal/huma/handlers/projects.go index 09be87b6fe..667eb4dc04 100644 --- a/backend/internal/huma/handlers/projects.go +++ b/backend/internal/huma/handlers/projects.go @@ -13,7 +13,7 @@ import ( humamw "github.com/getarcaneapp/arcane/backend/internal/huma/middleware" "github.com/getarcaneapp/arcane/backend/internal/services" "github.com/getarcaneapp/arcane/backend/pkg/pagination" - projects "github.com/getarcaneapp/arcane/backend/pkg/projects" + "github.com/getarcaneapp/arcane/backend/pkg/projects" "github.com/getarcaneapp/arcane/backend/pkg/utils" "github.com/getarcaneapp/arcane/backend/pkg/utils/mapper" "github.com/getarcaneapp/arcane/types/base" diff --git a/backend/internal/huma/handlers/swarm.go b/backend/internal/huma/handlers/swarm.go index 60c996db78..81a9e2e43c 100644 --- a/backend/internal/huma/handlers/swarm.go +++ b/backend/internal/huma/handlers/swarm.go @@ -1209,7 +1209,7 @@ func (h *SwarmHandler) GetStack(ctx context.Context, input *GetSwarmStackInput) stack, err := h.swarmService.GetStack(ctx, input.EnvironmentID, input.Name) if err != nil { if errdefs.IsNotFound(err) { - return nil, huma.Error404NotFound(("Swarm stack not found")) + return nil, huma.Error404NotFound("Swarm stack not found") } return nil, mapSwarmServiceError(err, "Failed to inspect swarm stack") } @@ -1387,7 +1387,7 @@ func (h *SwarmHandler) RenderStackConfig(ctx context.Context, input *RenderSwarm return &RenderSwarmStackConfigOutput{Body: base.ApiResponse[swarmtypes.StackRenderConfigResponse]{Success: true, Data: *resp}}, nil } -// GetSwarmInfo returns the current swarm cluster metadata for an environment. +// GetSwarmStatus GetSwarmInfo returns the current swarm cluster metadata for an environment. // // It delegates to the swarm service to inspect the local swarm state and maps // service-layer failures to the API's HTTP error model. diff --git a/backend/internal/models/event.go b/backend/internal/models/event.go index a00f840835..4ceaa592d3 100644 --- a/backend/internal/models/event.go +++ b/backend/internal/models/event.go @@ -10,7 +10,6 @@ type ( ) const ( - // Event types EventTypeContainerStart EventType = "container.start" EventTypeContainerStop EventType = "container.stop" EventTypeContainerRestart EventType = "container.restart" @@ -88,7 +87,6 @@ const ( EventTypeWebhookDelete EventType = "webhook.delete" EventTypeWebhookTrigger EventType = "webhook.trigger" - // Event severities EventSeverityInfo EventSeverity = "info" EventSeverityWarning EventSeverity = "warning" EventSeverityError EventSeverity = "error" diff --git a/backend/internal/models/vulnerability_ignore.go b/backend/internal/models/vulnerability_ignore.go index 59f43297cd..2ebed5d90e 100644 --- a/backend/internal/models/vulnerability_ignore.go +++ b/backend/internal/models/vulnerability_ignore.go @@ -52,7 +52,7 @@ func (v *VulnerabilityIgnore) BeforeCreate(db *gorm.DB) error { return nil } -// VulnerabilityIgnoreCompositeKey generates a composite key for deduplication +// CompositeKey generates a composite key for deduplication // This helps prevent duplicate ignore records for the same vulnerability func (v *VulnerabilityIgnore) CompositeKey() string { return v.EnvironmentID + ":" + v.ImageID + ":" + v.VulnerabilityID + ":" + v.PkgName + ":" + v.InstalledVersion diff --git a/backend/internal/services/api_key_service.go b/backend/internal/services/api_key_service.go index 6f655c26a5..1272428fbc 100644 --- a/backend/internal/services/api_key_service.go +++ b/backend/internal/services/api_key_service.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/getarcaneapp/arcane/backend/internal/common" "github.com/getarcaneapp/arcane/backend/internal/database" "github.com/getarcaneapp/arcane/backend/internal/models" "github.com/getarcaneapp/arcane/backend/pkg/pagination" @@ -17,13 +18,6 @@ import ( "gorm.io/gorm" ) -var ( - ErrApiKeyNotFound = errors.New("API key not found") - ErrApiKeyExpired = errors.New("API key has expired") - ErrApiKeyInvalid = errors.New("invalid API key") - ErrApiKeyProtected = errors.New("API key is protected") -) - const ( apiKeyPrefix = "arc_" apiKeyLength = 32 @@ -76,12 +70,12 @@ func normalizeAPIKeyInputInternal(rawKey string) string { func parseAPIKeyPrefixInternal(rawKey string) (string, error) { rawKey = normalizeAPIKeyInputInternal(rawKey) if !strings.HasPrefix(rawKey, apiKeyPrefix) { - return "", ErrApiKeyInvalid + return "", &common.ApiKeyInvalidError{} } prefixLen := len(apiKeyPrefix) + apiKeyPrefixLen if len(rawKey) < prefixLen { - return "", ErrApiKeyInvalid + return "", &common.ApiKeyInvalidError{} } return rawKey[:prefixLen], nil @@ -326,7 +320,7 @@ func (s *ApiKeyService) GetApiKey(ctx context.Context, id string) (*apikey.ApiKe var ak models.ApiKey if err := s.db.WithContext(ctx).Where("id = ?", id).First(&ak).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, ErrApiKeyNotFound + return nil, &common.ApiKeyNotFoundError{} } return nil, fmt.Errorf("failed to get API key: %w", err) } @@ -374,12 +368,12 @@ func (s *ApiKeyService) UpdateApiKey(ctx context.Context, id string, req apikey. var ak models.ApiKey if err := s.db.WithContext(ctx).Where("id = ?", id).First(&ak).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, ErrApiKeyNotFound + return nil, &common.ApiKeyNotFoundError{} } return nil, fmt.Errorf("failed to get API key: %w", err) } if isStaticAPIKeyInternal(ak) { - return nil, ErrApiKeyProtected + return nil, &common.ApiKeyProtectedError{} } if req.Name != nil { @@ -414,12 +408,12 @@ func (s *ApiKeyService) DeleteApiKey(ctx context.Context, id string) error { var apiKey models.ApiKey if err := s.db.WithContext(ctx).Where("id = ?", id).First(&apiKey).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return ErrApiKeyNotFound + return &common.ApiKeyNotFoundError{} } return fmt.Errorf("failed to load API key: %w", err) } if isStaticAPIKeyInternal(apiKey) { - return ErrApiKeyProtected + return &common.ApiKeyProtectedError{} } result := s.db.WithContext(ctx).Delete(&models.ApiKey{}, "id = ?", id) @@ -427,7 +421,7 @@ func (s *ApiKeyService) DeleteApiKey(ctx context.Context, id string) error { return fmt.Errorf("failed to delete API key: %w", result.Error) } if result.RowsAffected == 0 { - return ErrApiKeyNotFound + return &common.ApiKeyNotFoundError{} } return nil } @@ -447,11 +441,11 @@ func (s *ApiKeyService) ValidateApiKey(ctx context.Context, rawKey string) (*mod for _, apiKey := range apiKeys { if err := s.validateApiKeyHash(apiKey.KeyHash, rawKey); err == nil { if apiKey.ExpiresAt != nil && apiKey.ExpiresAt.Before(time.Now()) { - return nil, ErrApiKeyExpired + return nil, &common.ApiKeyExpiredError{} } if apiKey.UserID == nil { - return nil, ErrApiKeyInvalid + return nil, &common.ApiKeyInvalidError{} } s.markApiKeyUsedAsync(ctx, apiKey.ID) @@ -465,7 +459,7 @@ func (s *ApiKeyService) ValidateApiKey(ctx context.Context, rawKey string) (*mod } } - return nil, ErrApiKeyInvalid + return nil, &common.ApiKeyInvalidError{} } func (s *ApiKeyService) GetEnvironmentByApiKey(ctx context.Context, rawKey string) (*string, error) { @@ -483,7 +477,7 @@ func (s *ApiKeyService) GetEnvironmentByApiKey(ctx context.Context, rawKey strin for _, apiKey := range apiKeys { if err := s.validateApiKeyHash(apiKey.KeyHash, rawKey); err == nil { if apiKey.ExpiresAt != nil && apiKey.ExpiresAt.Before(time.Now()) { - return nil, ErrApiKeyExpired + return nil, &common.ApiKeyExpiredError{} } s.markApiKeyUsedAsync(ctx, apiKey.ID) @@ -492,5 +486,5 @@ func (s *ApiKeyService) GetEnvironmentByApiKey(ctx context.Context, rawKey strin } } - return nil, ErrApiKeyInvalid + return nil, &common.ApiKeyInvalidError{} } diff --git a/backend/internal/services/api_key_service_test.go b/backend/internal/services/api_key_service_test.go index f7b6cbf715..e73441fe05 100644 --- a/backend/internal/services/api_key_service_test.go +++ b/backend/internal/services/api_key_service_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/getarcaneapp/arcane/backend/internal/common" glsqlite "github.com/glebarez/sqlite" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -176,7 +177,8 @@ func TestDeleteApiKeyRejectsStaticKey(t *testing.T) { require.NoError(t, err) err = service.DeleteApiKey(ctx, created.ApiKey.ID) - require.ErrorIs(t, err, ErrApiKeyProtected) + var protectedErr *common.ApiKeyProtectedError + require.ErrorAs(t, err, &protectedErr) apiKeys := listAPIKeysForUser(t, db, adminUser.ID) require.Len(t, apiKeys, 1) @@ -196,7 +198,8 @@ func TestUpdateApiKeyRejectsStaticKey(t *testing.T) { Description: new("updated description"), }) require.Nil(t, updated) - require.ErrorIs(t, err, ErrApiKeyProtected) + var protectedErr *common.ApiKeyProtectedError + require.ErrorAs(t, err, &protectedErr) apiKeys := listAPIKeysForUser(t, db, adminUser.ID) require.Len(t, apiKeys, 1) @@ -238,7 +241,8 @@ func TestReconcileDefaultAdminAPIKeyReplacesManagedKeyOnRotation(t *testing.T) { require.NotEqual(t, first[0].ID, second[0].ID) _, err := service.ValidateApiKey(ctx, oldKey) - require.ErrorIs(t, err, ErrApiKeyInvalid) + var invalidErr *common.ApiKeyInvalidError + require.ErrorAs(t, err, &invalidErr) validatedUser, err := service.ValidateApiKey(ctx, newKey) require.NoError(t, err) @@ -324,7 +328,8 @@ func TestReconcileDefaultAdminAPIKeyRejectsInvalidKey(t *testing.T) { adminUser := createDefaultAdminUser(t, ctx, userService) err := service.ReconcileDefaultAdminAPIKey(ctx, "invalid-key") - require.ErrorIs(t, err, ErrApiKeyInvalid) + var invalidErr *common.ApiKeyInvalidError + require.ErrorAs(t, err, &invalidErr) require.Empty(t, listAPIKeysForUser(t, db, adminUser.ID)) } @@ -372,7 +377,8 @@ func TestValidateAPIKeyInvalidDoesNotUpdateLastUsedAt(t *testing.T) { require.NoError(t, err) _, err = service.ValidateApiKey(ctx, invalidateAPIKey(created.Key)) - require.ErrorIs(t, err, ErrApiKeyInvalid) + var invalidErr *common.ApiKeyInvalidError + require.ErrorAs(t, err, &invalidErr) assertAPIKeyLastUsedStable(t, db, created.ApiKey.ID, nil, 500*time.Millisecond) apiKey := fetchAPIKey(t, db, created.ApiKey.ID) @@ -384,7 +390,8 @@ func TestValidateAPIKeyRejectsShortPrefixedInput(t *testing.T) { service, _, _ := setupAPIKeyService(t) _, err := service.ValidateApiKey(ctx, "arc_123") - require.ErrorIs(t, err, ErrApiKeyInvalid) + var invalidErr *common.ApiKeyInvalidError + require.ErrorAs(t, err, &invalidErr) } func TestGetEnvironmentByAPIKeyExpiredDoesNotUpdateLastUsedAt(t *testing.T) { @@ -400,7 +407,8 @@ func TestGetEnvironmentByAPIKeyExpiredDoesNotUpdateLastUsedAt(t *testing.T) { require.NoError(t, err) _, err = service.GetEnvironmentByApiKey(ctx, created.Key) - require.ErrorIs(t, err, ErrApiKeyExpired) + var expiredErr *common.ApiKeyExpiredError + require.ErrorAs(t, err, &expiredErr) assertAPIKeyLastUsedStable(t, db, created.ApiKey.ID, nil, 500*time.Millisecond) apiKey := fetchAPIKey(t, db, created.ApiKey.ID) diff --git a/backend/internal/services/auth_service_test.go b/backend/internal/services/auth_service_test.go index cb7161839d..57606fae79 100644 --- a/backend/internal/services/auth_service_test.go +++ b/backend/internal/services/auth_service_test.go @@ -208,10 +208,10 @@ func TestPersistOidcTokens_SetsFields(t *testing.T) { t.Errorf("expiresAt nil") } // Check approx expiry within [start+7s, start+12s] to allow CI slop - min := start.Add(7 * time.Second) - max := start.Add(12 * time.Second) - if user.OidcAccessTokenExpiresAt.Before(min) || user.OidcAccessTokenExpiresAt.After(max) { - t.Errorf("expiresAt %v not in [%v,%v]", user.OidcAccessTokenExpiresAt, min, max) + authMin := start.Add(7 * time.Second) + authMax := start.Add(12 * time.Second) + if user.OidcAccessTokenExpiresAt.Before(authMin) || user.OidcAccessTokenExpiresAt.After(authMax) { + t.Errorf("expiresAt %v not in [%v,%v]", user.OidcAccessTokenExpiresAt, authMin, authMax) } } diff --git a/backend/internal/services/build_service.go b/backend/internal/services/build_service.go index 5c86e4213b..8988c8c77f 100644 --- a/backend/internal/services/build_service.go +++ b/backend/internal/services/build_service.go @@ -16,7 +16,7 @@ import ( "github.com/getarcaneapp/arcane/backend/internal/database" "github.com/getarcaneapp/arcane/backend/internal/models" buildgit "github.com/getarcaneapp/arcane/backend/pkg/gitutil" - libbuild "github.com/getarcaneapp/arcane/backend/pkg/libarcane/libbuild" + "github.com/getarcaneapp/arcane/backend/pkg/libarcane/libbuild" "github.com/getarcaneapp/arcane/backend/pkg/pagination" buildtypes "github.com/getarcaneapp/arcane/types/builds" imagetypes "github.com/getarcaneapp/arcane/types/image" @@ -553,21 +553,21 @@ func buildToRecord(build models.ImageBuild, includeOutput bool) imagetypes.Build ContextDir: build.ContextDir, Dockerfile: build.Dockerfile, Target: build.Target, - Tags: []string(build.Tags), - Platforms: []string(build.Platforms), + Tags: build.Tags, + Platforms: build.Platforms, BuildArgs: buildArgs, Labels: labels, - CacheFrom: []string(build.CacheFrom), - CacheTo: []string(build.CacheTo), + CacheFrom: build.CacheFrom, + CacheTo: build.CacheTo, NoCache: build.NoCache, Pull: build.Pull, Network: build.BuildNetwork, Isolation: build.Isolation, ShmSize: build.ShmSize, Ulimits: ulimits, - Entitlements: []string(build.Entitlements), + Entitlements: build.Entitlements, Privileged: build.Privileged, - ExtraHosts: []string(build.ExtraHosts), + ExtraHosts: build.ExtraHosts, Push: build.Push, Load: build.Load, Digest: build.Digest, diff --git a/backend/internal/services/container_registry_service.go b/backend/internal/services/container_registry_service.go index b5d691ed41..fd21f2879e 100644 --- a/backend/internal/services/container_registry_service.go +++ b/backend/internal/services/container_registry_service.go @@ -402,7 +402,7 @@ func (s *ContainerRegistryService) GetAllRegistryAuthConfigs(ctx context.Context Password: token, ServerAddress: serverAddress, } - for _, key := range utilsregistry.RegistryAuthLookupKeys(normalizedHost) { + for _, key := range utilsregistry.AuthLookupKeys(normalizedHost) { authConfigs[key] = authConfig } } diff --git a/backend/internal/services/container_registry_service_test.go b/backend/internal/services/container_registry_service_test.go index d901dfb432..a48b0ce3fc 100644 --- a/backend/internal/services/container_registry_service_test.go +++ b/backend/internal/services/container_registry_service_test.go @@ -595,7 +595,7 @@ func TestContainerRegistryService_InspectImageDigest_FallsBackWhenDistributionNo calls++ assert.Equal(t, serverURL.Host+"/team/app:1.2.3", imageRef) assert.Empty(t, options.EncodedRegistryAuth) - return client.DistributionInspectResult{}, errors.New("Error response from daemon: Not Found") + return client.DistributionInspectResult{}, errors.New("error response from daemon: Not Found") }, }, nil }, nil) @@ -632,7 +632,7 @@ func TestContainerRegistryService_InspectImageDigest_FallsBackWhenDistributionFo calls++ assert.Equal(t, serverURL.Host+"/team/app:1.2.3", imageRef) assert.Empty(t, options.EncodedRegistryAuth) - return client.DistributionInspectResult{}, errors.New("Error response from daemon:

403 Forbidden

Request forbidden by administrative rules. ") + return client.DistributionInspectResult{}, errors.New("error response from daemon:

403 Forbidden

Request forbidden by administrative rules. ") }, }, nil }, nil) @@ -698,7 +698,7 @@ func TestContainerRegistryService_InspectImageDigest_RetriesStoredCredentialsAft svc := NewContainerRegistryService(db, func(context.Context) (RegistryDaemonClient, error) { return &fakeRegistryDaemonClient{ distributionInspectFn: func(ctx context.Context, imageRef string, options client.DistributionInspectOptions) (client.DistributionInspectResult, error) { - return client.DistributionInspectResult{}, errors.New("Error response from daemon: Not Found") + return client.DistributionInspectResult{}, errors.New("error response from daemon: Not Found") }, }, nil }, nil) @@ -737,7 +737,7 @@ func TestContainerRegistryService_InspectImageDigest_DoesNotFallbackOnTLSFailure } func TestContainerRegistryService_InspectImageDigest_PreservesDaemonAndFallbackErrors(t *testing.T) { - daemonErr := errors.New("Error response from daemon: Not Found") + daemonErr := errors.New("error response from daemon: Not Found") fallbackErr := errors.New("dial tcp: i/o timeout") svc := NewContainerRegistryService(nil, func(context.Context) (RegistryDaemonClient, error) { @@ -789,7 +789,7 @@ func TestContainerRegistryService_InspectImageDigest_PreservesAnonymousUnauthori svc := NewContainerRegistryService(db, func(context.Context) (RegistryDaemonClient, error) { return &fakeRegistryDaemonClient{ distributionInspectFn: func(ctx context.Context, imageRef string, options client.DistributionInspectOptions) (client.DistributionInspectResult, error) { - return client.DistributionInspectResult{}, errors.New("Error response from daemon: Not Found") + return client.DistributionInspectResult{}, errors.New("error response from daemon: Not Found") }, }, nil }, nil) diff --git a/backend/internal/services/container_service.go b/backend/internal/services/container_service.go index d0e5cd0cee..7378b24edd 100644 --- a/backend/internal/services/container_service.go +++ b/backend/internal/services/container_service.go @@ -1007,7 +1007,7 @@ func paginateContainerProjectGroupsInternal( requestedPage = totalPages } - pageGroups := []containertypes.SummaryGroup{} + var pageGroups []containertypes.SummaryGroup if len(pages) > 0 { pageGroups = pages[requestedPage-1] } diff --git a/backend/internal/services/customize_search_service.go b/backend/internal/services/customize_search_service.go index be39784cf7..d517ed2a76 100644 --- a/backend/internal/services/customize_search_service.go +++ b/backend/internal/services/customize_search_service.go @@ -41,7 +41,7 @@ func (s *CustomizeSearchService) buildCategoriesFromModel() []category.Category // map category id -> list of customizations categories := map[string][]meta.Metadata{} - categoryOrder := []string{} // Track order from first appearance in struct + var categoryOrder []string // Track order from first appearance in struct rt := reflect.TypeFor[models.CustomizeItem]() for field := range rt.Fields() { @@ -84,7 +84,7 @@ func (s *CustomizeSearchService) buildCategoriesFromModel() []category.Category } // Build final category list in struct order - result := []category.Category{} + var result []category.Category for _, catID := range categoryOrder { catMeta := catMetaMap[catID] if catMeta == nil { @@ -123,7 +123,7 @@ func (s *CustomizeSearchService) Search(query string) search.Response { } categories := s.GetCustomizeCategories() - results := []category.Category{} + var results []category.Category for _, cat := range categories { // Check if category matches @@ -132,7 +132,7 @@ func (s *CustomizeSearchService) Search(query string) search.Response { containsKeyword(cat.Keywords, query) // Check individual settings - matchingSettings := []meta.Metadata{} + var matchingSettings []meta.Metadata for _, setting := range cat.Settings { if matchesSetting(setting, query) { matchingSettings = append(matchingSettings, setting) diff --git a/backend/internal/services/dashboard_service.go b/backend/internal/services/dashboard_service.go index c5e514a2a1..942b2af19d 100644 --- a/backend/internal/services/dashboard_service.go +++ b/backend/internal/services/dashboard_service.go @@ -469,9 +469,8 @@ func (s *DashboardService) buildEnvironmentOverviewInternal( } if snapshotErr != nil { - message := snapshotErr.Error() overview.SnapshotState = dashboardtypes.EnvironmentSnapshotStateError - overview.SnapshotError = &message + overview.SnapshotError = new(snapshotErr.Error()) return overview } diff --git a/backend/internal/services/environment_service.go b/backend/internal/services/environment_service.go index 7bfbca3d7a..19593226ab 100644 --- a/backend/internal/services/environment_service.go +++ b/backend/internal/services/environment_service.go @@ -786,7 +786,9 @@ func (s *EnvironmentService) RegenerateEnvironmentApiKey(ctx context.Context, en return nil } -// Deprecated - Use the Api Key flow +// PairAgentWithBootstrap Uses a predefined bootstrap token to register with agents. +// +// Deprecated: PairAgentWithBootstrap will be removed in a future release, Use the API Key flow instead. func (s *EnvironmentService) PairAgentWithBootstrap(ctx context.Context, apiUrl, bootstrapToken string) (string, error) { reqCtx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() diff --git a/backend/internal/services/git_repository_service.go b/backend/internal/services/git_repository_service.go index e17d693c71..f505d14181 100644 --- a/backend/internal/services/git_repository_service.go +++ b/backend/internal/services/git_repository_service.go @@ -10,7 +10,7 @@ import ( "github.com/getarcaneapp/arcane/backend/internal/models" git "github.com/getarcaneapp/arcane/backend/pkg/gitutil" "github.com/getarcaneapp/arcane/backend/pkg/libarcane/crypto" - libbuild "github.com/getarcaneapp/arcane/backend/pkg/libarcane/libbuild" + "github.com/getarcaneapp/arcane/backend/pkg/libarcane/libbuild" "github.com/getarcaneapp/arcane/backend/pkg/libarcane/timeouts" "github.com/getarcaneapp/arcane/backend/pkg/pagination" "github.com/getarcaneapp/arcane/backend/pkg/utils" diff --git a/backend/internal/services/gitops_sync_service.go b/backend/internal/services/gitops_sync_service.go index e2af072706..fd7033b9d4 100644 --- a/backend/internal/services/gitops_sync_service.go +++ b/backend/internal/services/gitops_sync_service.go @@ -1318,14 +1318,12 @@ func (s *GitOpsSyncService) findUniqueProjectDirectoryCandidateInternal(ctx cont } func (s *GitOpsSyncService) createRecoveredProjectFromDirectoryInternal(ctx context.Context, sync *models.GitOpsSync, projectPath string) (*models.Project, error) { - dirName := filepath.Base(projectPath) - reason := "Project recovered from existing GitOps-managed directory" project := &models.Project{ Name: sync.ProjectName, - DirName: &dirName, + DirName: new(filepath.Base(projectPath)), Path: projectPath, Status: models.ProjectStatusUnknown, - StatusReason: &reason, + StatusReason: new("Project recovered from existing GitOps-managed directory"), ServiceCount: 0, RunningCount: 0, GitOpsManagedBy: &sync.ID, diff --git a/backend/internal/services/image_service_test.go b/backend/internal/services/image_service_test.go index ef5ca0c44e..d14c119bea 100644 --- a/backend/internal/services/image_service_test.go +++ b/backend/internal/services/image_service_test.go @@ -179,13 +179,13 @@ func TestGetPullOptionsWithAuth_DBRegistryUsesValidCredentials(t *testing.T) { } func TestShouldRetryAnonymousPullInternal_UnauthorizedWithAuth(t *testing.T) { - err := errors.New(`Error response from daemon: Head "registry-1.docker.io/v2/library/nginx/manifests/latest": unauthorized: incorrect username or password`) + err := errors.New(`error response from daemon: Head "registry-1.docker.io/v2/library/nginx/manifests/latest": unauthorized: incorrect username or password`) assert.True(t, shouldRetryAnonymousPullInternal(client.ImagePullOptions{RegistryAuth: "encoded-auth"}, err)) } func TestShouldRetryAnonymousPullInternal_SkipsRetryWithoutUnauthorizedOrAuth(t *testing.T) { - nonAuthErr := errors.New("Error response from daemon: i/o timeout") + nonAuthErr := errors.New("error response from daemon: i/o timeout") unauthorizedErr := errors.New("unauthorized: authentication required") assert.False(t, shouldRetryAnonymousPullInternal(client.ImagePullOptions{RegistryAuth: "encoded-auth"}, nonAuthErr)) diff --git a/backend/internal/services/image_update_service.go b/backend/internal/services/image_update_service.go index 984d6c7c1f..4972ab78d2 100644 --- a/backend/internal/services/image_update_service.go +++ b/backend/internal/services/image_update_service.go @@ -13,7 +13,7 @@ import ( "github.com/getarcaneapp/arcane/backend/internal/models" "github.com/getarcaneapp/arcane/backend/pkg/libarcane/crypto" "github.com/getarcaneapp/arcane/backend/pkg/utils/imagedigest" - registry "github.com/getarcaneapp/arcane/backend/pkg/utils/registry" + "github.com/getarcaneapp/arcane/backend/pkg/utils/registry" "github.com/getarcaneapp/arcane/types/containerregistry" "github.com/getarcaneapp/arcane/types/imageupdate" "github.com/moby/moby/api/types/image" diff --git a/backend/internal/services/image_update_service_test.go b/backend/internal/services/image_update_service_test.go index 9bd57e4b26..9936ecfae0 100644 --- a/backend/internal/services/image_update_service_test.go +++ b/backend/internal/services/image_update_service_test.go @@ -19,7 +19,7 @@ import ( dockertypescontainer "github.com/moby/moby/api/types/container" dockertypesimage "github.com/moby/moby/api/types/image" "github.com/moby/moby/client" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ref "go.podman.io/image/v5/docker/reference" @@ -513,7 +513,7 @@ func TestImageUpdateService_CheckImageUpdate_UsesRegistryFallback(t *testing.T) registryService := NewContainerRegistryService(db, func(context.Context) (RegistryDaemonClient, error) { return &fakeRegistryDaemonClient{ distributionInspectFn: func(ctx context.Context, imageRef string, options client.DistributionInspectOptions) (client.DistributionInspectResult, error) { - return client.DistributionInspectResult{}, errors.New("Error response from daemon: Not Found") + return client.DistributionInspectResult{}, errors.New("error response from daemon: Not Found") }, }, nil }, nil) @@ -553,7 +553,7 @@ func TestImageUpdateService_CheckMultipleImages_UsesRegistryFallback(t *testing. registryService := NewContainerRegistryService(db, func(context.Context) (RegistryDaemonClient, error) { return &fakeRegistryDaemonClient{ distributionInspectFn: func(ctx context.Context, imageRef string, options client.DistributionInspectOptions) (client.DistributionInspectResult, error) { - return client.DistributionInspectResult{}, errors.New("Error response from daemon:

403 Forbidden

Request forbidden by administrative rules. ") + return client.DistributionInspectResult{}, errors.New("error response from daemon:

403 Forbidden

Request forbidden by administrative rules. ") }, }, nil }, nil) @@ -594,7 +594,7 @@ func TestImageUpdateService_CheckMultipleImages_PersistsRefScopedErrorsWhenLocal registryService := NewContainerRegistryService(db, func(context.Context) (RegistryDaemonClient, error) { return &fakeRegistryDaemonClient{ distributionInspectFn: func(ctx context.Context, imageRef string, options client.DistributionInspectOptions) (client.DistributionInspectResult, error) { - return client.DistributionInspectResult{}, errors.New("Error response from daemon: Not Found") + return client.DistributionInspectResult{}, errors.New("error response from daemon: Not Found") }, }, nil }, nil) diff --git a/backend/internal/services/project_service.go b/backend/internal/services/project_service.go index 2f31238318..2e6044003b 100644 --- a/backend/internal/services/project_service.go +++ b/backend/internal/services/project_service.go @@ -23,7 +23,7 @@ import ( "github.com/getarcaneapp/arcane/backend/internal/database" "github.com/getarcaneapp/arcane/backend/internal/models" dockerutil "github.com/getarcaneapp/arcane/backend/pkg/dockerutil" - libbuild "github.com/getarcaneapp/arcane/backend/pkg/libarcane/libbuild" + "github.com/getarcaneapp/arcane/backend/pkg/libarcane/libbuild" "github.com/getarcaneapp/arcane/backend/pkg/libarcane/timeouts" libupdater "github.com/getarcaneapp/arcane/backend/pkg/libarcane/updater" "github.com/getarcaneapp/arcane/backend/pkg/pagination" @@ -1126,13 +1126,11 @@ func buildProjectUpdateInfoSummaryInternal( if strings.TrimSpace(info.Error) != "" { summary.ErrorCount++ if summary.ErrorMessage == nil { - errMsg := strings.TrimSpace(info.Error) - summary.ErrorMessage = &errMsg + summary.ErrorMessage = new(strings.TrimSpace(info.Error)) } } if !info.CheckTime.IsZero() && (latestCheckTime == nil || info.CheckTime.After(*latestCheckTime)) { - checkTime := info.CheckTime - latestCheckTime = &checkTime + latestCheckTime = new(info.CheckTime) } } @@ -1968,7 +1966,7 @@ func (s *ProjectService) buildServiceImageForDeploy( project.Services[serviceName] = updatedSvc } - if _, err := s.buildService.BuildImage(ctx, types.LOCAL_DOCKER_ENVIRONMENT_ID, buildReq, progressWriter, serviceName, user); err != nil { + if _, err := s.buildService.BuildImage(ctx, types.LocalDockerEnvironmentId, buildReq, progressWriter, serviceName, user); err != nil { return err } @@ -2338,7 +2336,7 @@ func (s *ProjectService) buildProjectServicesInternal(ctx context.Context, proje } buildCount++ - if _, err := s.buildService.BuildImage(ctx, types.LOCAL_DOCKER_ENVIRONMENT_ID, buildReq, progressWriter, name, user); err != nil { + if _, err := s.buildService.BuildImage(ctx, types.LocalDockerEnvironmentId, buildReq, progressWriter, name, user); err != nil { return err } } diff --git a/backend/internal/services/settings_search_service.go b/backend/internal/services/settings_search_service.go index feb14850d1..ed74d5dc10 100644 --- a/backend/internal/services/settings_search_service.go +++ b/backend/internal/services/settings_search_service.go @@ -41,7 +41,7 @@ func (s *SettingsSearchService) buildCategoriesFromModel() []category.Category { // map category id -> list of settings categories := map[string][]meta.Metadata{} - categoryOrder := []string{} // Track order from first appearance in struct + var categoryOrder []string // Track order from first appearance in struct rt := reflect.TypeFor[models.Settings]() for field := range rt.Fields() { @@ -89,7 +89,7 @@ func (s *SettingsSearchService) buildCategoriesFromModel() []category.Category { } // Build final category list in struct order - results := []category.Category{} + var results []category.Category for _, catID := range categoryOrder { catMeta := catMetaMap[catID] if catMeta == nil { diff --git a/backend/internal/services/settings_service_test.go b/backend/internal/services/settings_service_test.go index 93aa46be18..2c5fab9c5d 100644 --- a/backend/internal/services/settings_service_test.go +++ b/backend/internal/services/settings_service_test.go @@ -449,11 +449,9 @@ func TestSettingsService_UpdateSettings_PruneModesDoNotTriggerScheduledPruneCall callbackCalls++ } - imageMode := "all" - containerUntil := "24h" _, err = svc.UpdateSettings(ctx, settings.Update{ - PruneImageMode: &imageMode, - PruneContainerUntil: &containerUntil, + PruneImageMode: new("all"), + PruneContainerUntil: new("24h"), }) require.NoError(t, err) require.Equal(t, 0, callbackCalls) @@ -470,9 +468,8 @@ func TestSettingsService_UpdateSettings_ScheduledPruneScheduleTriggersCallback(t callbackCalls++ } - enabled := "true" _, err = svc.UpdateSettings(ctx, settings.Update{ - ScheduledPruneEnabled: &enabled, + ScheduledPruneEnabled: new("true"), }) require.NoError(t, err) require.Equal(t, 1, callbackCalls) diff --git a/backend/internal/services/swarm_service.go b/backend/internal/services/swarm_service.go index 30319eeeb3..6fc4712238 100644 --- a/backend/internal/services/swarm_service.go +++ b/backend/internal/services/swarm_service.go @@ -2107,7 +2107,7 @@ func (s *SwarmService) deleteStackSourceInternal(ctx context.Context, environmen environmentDir := filepath.Dir(stackSourceDir) if environmentDir != rootDir { if err := os.Remove(environmentDir); err != nil && !errors.Is(err, os.ErrNotExist) { - if errno, ok := errors.AsType[syscall.Errno](err); ok && (errno == syscall.ENOTEMPTY || errno == syscall.EACCES) { + if errno, ok := errors.AsType[syscall.Errno](err); ok && (errors.Is(errno, syscall.ENOTEMPTY) || errors.Is(errno, syscall.EACCES)) { slog.DebugContext(ctx, "swarm stack source environment directory cleanup skipped", "dir", environmentDir, "error", err) return nil } diff --git a/backend/internal/services/template_service.go b/backend/internal/services/template_service.go index 77ddc5a4bf..047a2b77f5 100644 --- a/backend/internal/services/template_service.go +++ b/backend/internal/services/template_service.go @@ -325,15 +325,15 @@ func (s *TemplateService) DeleteTemplate(ctx context.Context, id string) error { baseDir, err := projects.GetTemplatesDirectory(ctx) if err != nil { return fmt.Errorf("failed to get templates directory: %w", err) - } else { - templatePath := filepath.Join(baseDir, existing.Name) - - if stat, err := os.Stat(templatePath); err == nil && stat.IsDir() { - composeFile := filepath.Join(templatePath, "compose.yaml") - if _, err := os.Stat(composeFile); err == nil { - if err := os.RemoveAll(templatePath); err != nil { - return fmt.Errorf("failed to delete template directory: %w", err) - } + } + + templatePath := filepath.Join(baseDir, existing.Name) + + if stat, err := os.Stat(templatePath); err == nil && stat.IsDir() { + composeFile := filepath.Join(templatePath, "compose.yaml") + if _, err := os.Stat(composeFile); err == nil { + if err := os.RemoveAll(templatePath); err != nil { + return fmt.Errorf("failed to delete template directory: %w", err) } } } @@ -1061,7 +1061,7 @@ func (s *TemplateService) GetGlobalVariables(ctx context.Context) ([]env.Variabl } defer func() { _ = file.Close() }() - vars := []env.Variable{} + var vars []env.Variable scanner := bufio.NewScanner(file) lineNum := 0 diff --git a/backend/internal/services/vulnerability_service.go b/backend/internal/services/vulnerability_service.go index d6ea69c79a..ec8aa72e36 100644 --- a/backend/internal/services/vulnerability_service.go +++ b/backend/internal/services/vulnerability_service.go @@ -2355,9 +2355,9 @@ func logTrivyContainerStartupRequestInternal(ctx context.Context, scope string, resources := containertypes.Resources{} autoRemove := false - mounts := []string{} + var mounts []string networkMode := "" - securityOpts := []string{} + var securityOpts []string privileged := false if hostConfig != nil { resources = hostConfig.Resources diff --git a/backend/pkg/libarcane/edge/http_client.go b/backend/pkg/libarcane/edge/http_client.go index 96804e4205..d297386d8b 100644 --- a/backend/pkg/libarcane/edge/http_client.go +++ b/backend/pkg/libarcane/edge/http_client.go @@ -12,19 +12,19 @@ import ( // EdgeAwareClient is an HTTP client that automatically routes requests // to edge environments through the active edge tunnel instead of direct HTTP. // Works with both gRPC and WebSocket tunnel transports. -type EdgeAwareClient struct { +type AwareClient struct { httpClient *http.Client } // NewEdgeAwareClient creates a new edge-aware HTTP client -func NewEdgeAwareClient(timeout time.Duration) *EdgeAwareClient { - return &EdgeAwareClient{ +func NewEdgeAwareClient(timeout time.Duration) *AwareClient { + return &AwareClient{ httpClient: &http.Client{Timeout: timeout}, } } // EdgeResponse wraps the response from either direct HTTP or tunnel request -type EdgeResponse struct { +type Response struct { StatusCode int Body []byte Headers map[string]string @@ -43,7 +43,7 @@ type EdgeResponse struct { // - body: request body (can be nil) // // Returns EdgeResponse with status code, body bytes, and headers -func (c *EdgeAwareClient) DoForEnvironment( +func (c *AwareClient) DoForEnvironment( ctx context.Context, envID string, isEdge bool, @@ -52,7 +52,7 @@ func (c *EdgeAwareClient) DoForEnvironment( path string, headers map[string]string, body []byte, -) (*EdgeResponse, error) { +) (*Response, error) { // For edge environments with active tunnels, route through tunnel if isEdge && HasActiveTunnel(envID) { return c.doViaTunnel(ctx, envID, method, path, headers, body) @@ -70,14 +70,14 @@ func (c *EdgeAwareClient) DoForEnvironment( } // doViaTunnel routes the request through the edge tunnel -func (c *EdgeAwareClient) doViaTunnel( +func (c *AwareClient) doViaTunnel( ctx context.Context, envID string, method string, path string, headers map[string]string, body []byte, -) (*EdgeResponse, error) { +) (*Response, error) { tunnel, ok := GetRegistry().Get(envID) if !ok { return nil, fmt.Errorf("no active tunnel for environment %s", envID) @@ -92,7 +92,7 @@ func (c *EdgeAwareClient) doViaTunnel( return nil, fmt.Errorf("tunnel request failed: %w", err) } - return &EdgeResponse{ + return &Response{ StatusCode: statusCode, Body: respBody, Headers: respHeaders, @@ -100,13 +100,13 @@ func (c *EdgeAwareClient) doViaTunnel( } // doDirectHTTP makes a direct HTTP request -func (c *EdgeAwareClient) doDirectHTTP( +func (c *AwareClient) doDirectHTTP( ctx context.Context, method string, url string, headers map[string]string, body []byte, -) (*EdgeResponse, error) { +) (*Response, error) { var bodyReader io.Reader if body != nil { bodyReader = bytes.NewReader(body) @@ -141,7 +141,7 @@ func (c *EdgeAwareClient) doDirectHTTP( } } - return &EdgeResponse{ + return &Response{ StatusCode: resp.StatusCode, Body: respBody, Headers: respHeaders, @@ -151,8 +151,7 @@ func (c *EdgeAwareClient) doDirectHTTP( // DefaultEdgeAwareClient is a singleton client with reasonable defaults var DefaultEdgeAwareClient = NewEdgeAwareClient(30 * time.Second) -// DoRequest is a convenience function for making edge-aware requests -// using the default client +// DoEdgeAwareRequest is a convenience function for making edge-aware requests using the default client func DoEdgeAwareRequest( ctx context.Context, envID string, @@ -162,6 +161,6 @@ func DoEdgeAwareRequest( path string, headers map[string]string, body []byte, -) (*EdgeResponse, error) { +) (*Response, error) { return DefaultEdgeAwareClient.DoForEnvironment(ctx, envID, isEdge, method, url, path, headers, body) } diff --git a/backend/pkg/libarcane/libbuild/builder_buildkit.go b/backend/pkg/libarcane/libbuild/builder_buildkit.go index a2cce4ffd3..f7dd14c0a4 100644 --- a/backend/pkg/libarcane/libbuild/builder_buildkit.go +++ b/backend/pkg/libarcane/libbuild/builder_buildkit.go @@ -11,6 +11,7 @@ import ( dockerutils "github.com/getarcaneapp/arcane/backend/pkg/dockerutil" imagetypes "github.com/getarcaneapp/arcane/types/image" buildkit "github.com/moby/buildkit/client" + "github.com/tonistiigi/fsutil" ) func parseBuildkitCacheEntriesInternal(values []string) []buildkit.CacheOptionsEntry { @@ -99,6 +100,39 @@ func normalizeEntitlementsInternal(entitlements []string, privileged bool) []str return out } +func resolveBuildkitDockerfileMountInternal(contextDir, dockerfilePath string) (string, string) { + dockerfileRelDir := filepath.Dir(filepath.FromSlash(dockerfilePath)) + if dockerfileRelDir == "." { + return contextDir, dockerfilePath + } + + return filepath.Join(contextDir, dockerfileRelDir), filepath.Base(dockerfilePath) +} + +func createBuildkitLocalMountsInternal(contextDir, dockerfileDir string) (map[string]fsutil.FS, error) { + contextMount, err := fsutil.NewFS(contextDir) + if err != nil { + return nil, fmt.Errorf("failed to create context mount: %w", err) + } + + if dockerfileDir == contextDir { + return map[string]fsutil.FS{ + "context": contextMount, + "dockerfile": contextMount, + }, nil + } + + dockerfileMount, err := fsutil.NewFS(dockerfileDir) + if err != nil { + return nil, fmt.Errorf("failed to create dockerfile mount: %w", err) + } + + return map[string]fsutil.FS{ + "context": contextMount, + "dockerfile": dockerfileMount, + }, nil +} + func (b *builder) buildSolveOptInternal(ctx context.Context, req imagetypes.BuildRequest, providerName string) (buildkit.SolveOpt, <-chan error, func(), error) { fsInput, err := prepareBuildFilesystemInputInternal(req) if err != nil { @@ -110,11 +144,11 @@ func (b *builder) buildSolveOptInternal(ctx context.Context, req imagetypes.Buil return buildkit.SolveOpt{}, nil, nil, err } - dockerfileDir := contextDir - dockerfileFilename := dockerfilePath - if dockerfileRelDir := filepath.Dir(filepath.FromSlash(dockerfilePath)); dockerfileRelDir != "." { - dockerfileDir = filepath.Join(contextDir, dockerfileRelDir) - dockerfileFilename = filepath.Base(dockerfilePath) + dockerfileDir, dockerfileFilename := resolveBuildkitDockerfileMountInternal(contextDir, dockerfilePath) + localMounts, err := createBuildkitLocalMountsInternal(contextDir, dockerfileDir) + if err != nil { + cleanup() + return buildkit.SolveOpt{}, nil, nil, err } frontendAttrs := map[string]string{ @@ -144,12 +178,9 @@ func (b *builder) buildSolveOptInternal(ctx context.Context, req imagetypes.Buil } solveOpt := buildkit.SolveOpt{ - Frontend: "dockerfile.v0", - FrontendAttrs: frontendAttrs, - LocalDirs: map[string]string{ - "context": contextDir, - "dockerfile": dockerfileDir, - }, + Frontend: "dockerfile.v0", + FrontendAttrs: frontendAttrs, + LocalMounts: localMounts, CacheImports: parseBuildkitCacheEntriesInternal(req.CacheFrom), CacheExports: parseBuildkitCacheEntriesInternal(req.CacheTo), AllowedEntitlements: normalizeEntitlementsInternal(req.Entitlements, req.Privileged), diff --git a/backend/pkg/libarcane/libbuild/builder_buildkit_test.go b/backend/pkg/libarcane/libbuild/builder_buildkit_test.go index 19fb164fbf..a9000a1e2d 100644 --- a/backend/pkg/libarcane/libbuild/builder_buildkit_test.go +++ b/backend/pkg/libarcane/libbuild/builder_buildkit_test.go @@ -34,16 +34,24 @@ func TestBuildSolveOptInternal_StagesInlineDockerfile(t *testing.T) { assert.Nil(t, loadErrCh) assert.Equal(t, ".arcane.inline.Dockerfile", solveOpt.FrontendAttrs["filename"]) - contextPath := solveOpt.LocalDirs["context"] - dockerfileDir := solveOpt.LocalDirs["dockerfile"] - assert.NotEmpty(t, contextPath) - assert.Equal(t, contextPath, dockerfileDir) + contextMount := solveOpt.LocalMounts["context"] + dockerfileMount := solveOpt.LocalMounts["dockerfile"] + require.NotNil(t, contextMount) + require.NotNil(t, dockerfileMount) - contents, err := os.ReadFile(filepath.Join(dockerfileDir, solveOpt.FrontendAttrs["filename"])) + dockerfileReader, err := dockerfileMount.Open(solveOpt.FrontendAttrs["filename"]) + require.NoError(t, err) + defer dockerfileReader.Close() + + contents, err := io.ReadAll(dockerfileReader) require.NoError(t, err) assert.Equal(t, "FROM alpine:3.20\nCOPY app.txt /app.txt\n", string(contents)) - appContents, err := os.ReadFile(filepath.Join(contextPath, "app.txt")) + appReader, err := contextMount.Open("app.txt") + require.NoError(t, err) + defer appReader.Close() + + appContents, err := io.ReadAll(appReader) require.NoError(t, err) assert.Equal(t, "hello\n", string(appContents)) } diff --git a/backend/pkg/libarcane/libbuild/builder_docker.go b/backend/pkg/libarcane/libbuild/builder_docker.go index f2855f659f..e67adec980 100644 --- a/backend/pkg/libarcane/libbuild/builder_docker.go +++ b/backend/pkg/libarcane/libbuild/builder_docker.go @@ -584,7 +584,7 @@ func readDockerignoreInternal(contextDir string) []string { } defer file.Close() - patterns := []string{} + var patterns []string scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) diff --git a/backend/pkg/libarcane/startup/startup.go b/backend/pkg/libarcane/startup/startup.go index f554f7f338..63b2a4ff3d 100644 --- a/backend/pkg/libarcane/startup/startup.go +++ b/backend/pkg/libarcane/startup/startup.go @@ -386,28 +386,28 @@ func cronToMinutes(fields []string) (int, bool, string) { return 0, false, "" } - sec, min, hour, day, month, weekday := fields[0], fields[1], fields[2], fields[3], fields[4], fields[5] + sec, minute, hour, day, month, weekday := fields[0], fields[1], fields[2], fields[3], fields[4], fields[5] - if mins, ok, warn := tryConvertSecondStep(sec, min, hour, day, month, weekday); ok { + if mins, ok, warn := tryConvertSecondStep(sec, minute, hour, day, month, weekday); ok { return mins, ok, warn } - if mins, ok, warn := tryConvertMinuteOrHour(sec, min, hour, day, month, weekday); ok { + if mins, ok, warn := tryConvertMinuteOrHour(sec, minute, hour, day, month, weekday); ok { return mins, ok, warn } - if mins, ok, warn := tryConvertDayStep(sec, min, hour, day, month, weekday); ok { + if mins, ok, warn := tryConvertDayStep(sec, minute, hour, day, month, weekday); ok { return mins, ok, warn } return 0, false, "" } -func tryConvertSecondStep(sec, min, hour, day, month, weekday string) (int, bool, string) { +func tryConvertSecondStep(sec, minute, hour, day, month, weekday string) (int, bool, string) { step, ok := parseCronStep(sec) if !ok { return 0, false, "" } - if min == "*" && hour == "*" && day == "*" && month == "*" && weekday == "*" { + if minute == "*" && hour == "*" && day == "*" && month == "*" && weekday == "*" { minutes := max((step+59)/60, 1) warn := "" if step%60 != 0 { @@ -418,18 +418,18 @@ func tryConvertSecondStep(sec, min, hour, day, month, weekday string) (int, bool return 0, false, "" } -func tryConvertMinuteOrHour(sec, min, hour, day, month, weekday string) (int, bool, string) { +func tryConvertMinuteOrHour(sec, minute, hour, day, month, weekday string) (int, bool, string) { if (sec != "0" && sec != "*") || day != "*" || month != "*" || weekday != "*" { return 0, false, "" } - if step, ok := parseCronStep(min); ok && hour == "*" { + if step, ok := parseCronStep(minute); ok && hour == "*" { return step, true, "" } - if min == "*" && hour == "*" { + if minute == "*" && hour == "*" { return 1, true, "" } - if min == "0" { + if minute == "0" { if step, ok := parseCronStep(hour); ok && day == "*" { return step * 60, true, "" } @@ -440,8 +440,8 @@ func tryConvertMinuteOrHour(sec, min, hour, day, month, weekday string) (int, bo return 0, false, "" } -func tryConvertDayStep(sec, min, hour, day, month, weekday string) (int, bool, string) { - if (sec == "0" || sec == "*") && min == "0" && hour == "0" && month == "*" && weekday == "*" { +func tryConvertDayStep(sec, minute, hour, day, month, weekday string) (int, bool, string) { + if (sec == "0" || sec == "*") && minute == "0" && hour == "0" && month == "*" && weekday == "*" { if step, ok := parseCronStep(day); ok { return step * 1440, true, "" } diff --git a/backend/pkg/libarcane/updater/digest_test.go b/backend/pkg/libarcane/updater/digest_test.go index 64c67be6aa..e672d111aa 100644 --- a/backend/pkg/libarcane/updater/digest_test.go +++ b/backend/pkg/libarcane/updater/digest_test.go @@ -3,7 +3,7 @@ package updater import ( "testing" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" ) diff --git a/backend/pkg/libarcane/updater/labels.go b/backend/pkg/libarcane/updater/labels.go index e7453cde87..6476edd414 100644 --- a/backend/pkg/libarcane/updater/labels.go +++ b/backend/pkg/libarcane/updater/labels.go @@ -3,14 +3,17 @@ package updater import "strings" const ( - // Core labels - LabelArcane = "com.getarcaneapp.arcane" // Identifies the Arcane container itself - LabelArcaneAgent = "com.getarcaneapp.arcane.agent" // Identifies an Arcane agent container - LabelUpdater = "com.getarcaneapp.arcane.updater" // Enable/disable updates (true/false) - - // Dependency labels - LabelDependsOn = "com.getarcaneapp.arcane.depends-on" // Comma-separated list of container names this depends on - LabelStopSignal = "com.getarcaneapp.arcane.stop-signal" // Custom stop signal (e.g., SIGINT) + // LabelArcane Identifies the Arcane container itself + LabelArcane = "com.getarcaneapp.arcane" + // LabelArcaneAgent Identifies an Arcane agent container + LabelArcaneAgent = "com.getarcaneapp.arcane.agent" + // LabelUpdater Enable/disable updates (true/false) + LabelUpdater = "com.getarcaneapp.arcane.updater" + + // LabelDependsOn Comma-separated list of container names the selected container depends on + LabelDependsOn = "com.getarcaneapp.arcane.depends-on" + // LabelStopSignal Custom stop signal (e.g., SIGINT) + LabelStopSignal = "com.getarcaneapp.arcane.stop-signal" ) // IsArcaneContainer checks if the container is the Arcane application itself diff --git a/backend/pkg/libarcane/ws/hub_test.go b/backend/pkg/libarcane/ws/hub_test.go index 7be11cc56c..2ee325a488 100644 --- a/backend/pkg/libarcane/ws/hub_test.go +++ b/backend/pkg/libarcane/ws/hub_test.go @@ -337,7 +337,7 @@ func TestHub_ConcurrentOperations(t *testing.T) { }, goroutines) for i := range goroutines { _, pairs[i].sc, pairs[i].cleanup = newTestWSPair(t) - defer pairs[i].cleanup() + t.Cleanup(pairs[i].cleanup) } for i := range goroutines { diff --git a/backend/pkg/pagination/filters.go b/backend/pkg/pagination/filters.go index 399667bf6f..b29687b1b7 100644 --- a/backend/pkg/pagination/filters.go +++ b/backend/pkg/pagination/filters.go @@ -41,7 +41,7 @@ func filterFn[T any](items []T, filters map[string]string, accessors []FilterAcc return items } - results := []T{} + var results []T for _, item := range items { if itemMatches(item, filters, accessors) { results = append(results, item) diff --git a/backend/pkg/pagination/search.go b/backend/pkg/pagination/search.go index 3b9ba0fa2c..59b3d4d524 100644 --- a/backend/pkg/pagination/search.go +++ b/backend/pkg/pagination/search.go @@ -4,7 +4,7 @@ import ( "strings" ) -// Return any error to skip the field (for when matching an unknown state on an enum) +// SearchAccessor Will Return any error to skip the field (for when matching an unknown state on an enum) // // Note: returning ("", nil) will match! type SearchAccessor[T any] = func(T) (string, error) @@ -20,7 +20,7 @@ func searchFn[T any](items []T, params SearchQuery, accessors []SearchAccessor[T return items } - results := []T{} + var results []T for iIdx := range items { for aIdx := range accessors { diff --git a/backend/pkg/projects/includes.go b/backend/pkg/projects/includes.go index 967cf14b8d..578510b48f 100644 --- a/backend/pkg/projects/includes.go +++ b/backend/pkg/projects/includes.go @@ -167,11 +167,14 @@ func ValidateIncludePathForWrite(projectDir, includePath string) (string, error) // Resolve symlinks in the include path to prevent symlink-based path traversal attacks evalPath := absFullPath - if evalFullPath, err := filepath.EvalSymlinks(absFullPath); err == nil { + evalFullPath, err := filepath.EvalSymlinks(absFullPath) + if err == nil { evalPath = evalFullPath } else if !errors.Is(err, os.ErrNotExist) { return "", fmt.Errorf("failed to resolve include path: %w", err) - } else { + } + + if errors.Is(err, os.ErrNotExist) { // File doesn't exist yet - evaluate parent directory symlinks dir := filepath.Dir(absFullPath) if evalDir, err := filepath.EvalSymlinks(dir); err == nil { diff --git a/backend/pkg/scheduler/analytics_job.go b/backend/pkg/scheduler/analytics_job.go index 12b0a2f60b..ac0e4f5b60 100644 --- a/backend/pkg/scheduler/analytics_job.go +++ b/backend/pkg/scheduler/analytics_job.go @@ -12,13 +12,13 @@ import ( "sync" "time" - backoff "github.com/cenkalti/backoff/v5" + "github.com/cenkalti/backoff/v5" "github.com/getarcaneapp/arcane/backend/internal/config" "github.com/getarcaneapp/arcane/backend/internal/services" ) const ( - AnalyticsJobName = "analytics-heartbeat" + analyticsJobName = "analytics-heartbeat" defaultHeartbeatEndpoint = "https://checkin.getarcane.app/heartbeat" devHeartbeatEndpoint = "http://localhost:8080/heartbeat" analyticsHeartbeatLastAttemptKey = "analytics.heartbeat.last_attempt_at" @@ -60,7 +60,7 @@ func NewAnalyticsJob( } func (j *AnalyticsJob) Name() string { - return AnalyticsJobName + return analyticsJobName } func (j *AnalyticsJob) Schedule(_ context.Context) string { @@ -108,7 +108,7 @@ func (j *AnalyticsJob) Run(ctx context.Context) { ctx, "sending analytics heartbeat", "jobName", - AnalyticsJobName, + analyticsJobName, "version", payload.Version, "instanceID", @@ -172,7 +172,7 @@ func (j *AnalyticsJob) Run(ctx context.Context) { ctx, "analytics heartbeat sent successfully", "jobName", - AnalyticsJobName, + analyticsJobName, "version", payload.Version, "instanceID", @@ -225,7 +225,7 @@ func (j *AnalyticsJob) claimHeartbeatAttemptWindowInternal(ctx context.Context) ctx, "skipping analytics heartbeat; already attempted within dedupe window", "jobName", - AnalyticsJobName, + analyticsJobName, "lastAttemptAt", lastAttemptAt, "nextEligibleAt", diff --git a/backend/pkg/scheduler/vulnerability_scan_job.go b/backend/pkg/scheduler/vulnerability_scan_job.go index 126c59d342..6f17433cf9 100644 --- a/backend/pkg/scheduler/vulnerability_scan_job.go +++ b/backend/pkg/scheduler/vulnerability_scan_job.go @@ -91,7 +91,7 @@ func (j *VulnerabilityScanJob) Run(ctx context.Context) { slog.InfoContext(ctx, "cleaned up orphaned vulnerability scan records", "deleted", deleted) } - scanned, failed, err := j.vulnerabilityService.ScanAllImages(ctx, types.LOCAL_DOCKER_ENVIRONMENT_ID, vulnerabilityScanSystemUser) + scanned, failed, err := j.vulnerabilityService.ScanAllImages(ctx, types.LocalDockerEnvironmentId, vulnerabilityScanSystemUser) if err != nil { slog.ErrorContext(ctx, "scheduled vulnerability scan failed", "error", err) return diff --git a/backend/pkg/utils/distribution/distribution_test.go b/backend/pkg/utils/distribution/distribution_test.go index 5d201c381b..3f2d85d3a0 100644 --- a/backend/pkg/utils/distribution/distribution_test.go +++ b/backend/pkg/utils/distribution/distribution_test.go @@ -7,7 +7,7 @@ import ( "net/http/httptest" "testing" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/backend/pkg/utils/imagedigest/digest.go b/backend/pkg/utils/imagedigest/digest.go index d6e6987911..d63f1ef908 100644 --- a/backend/pkg/utils/imagedigest/digest.go +++ b/backend/pkg/utils/imagedigest/digest.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" ) func Normalize(value string) (string, error) { diff --git a/backend/pkg/utils/imagedigest/digest_test.go b/backend/pkg/utils/imagedigest/digest_test.go index 6015b9b3ef..3b6a023ed9 100644 --- a/backend/pkg/utils/imagedigest/digest_test.go +++ b/backend/pkg/utils/imagedigest/digest_test.go @@ -3,7 +3,7 @@ package imagedigest import ( "testing" - digest "github.com/opencontainers/go-digest" + "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/backend/pkg/utils/jwtclaims/jwt.go b/backend/pkg/utils/jwtclaims/jwt.go index 9b0bb992f9..f5c88e136e 100644 --- a/backend/pkg/utils/jwtclaims/jwt.go +++ b/backend/pkg/utils/jwtclaims/jwt.go @@ -89,12 +89,13 @@ func CheckOrGenerateJwtSecret(jwtSecret string) []byte { if jwtSecret != "" { secretBytes = []byte(jwtSecret) return secretBytes - } else { - secretBytes = make([]byte, 32) - if _, err := rand.Read(secretBytes); err != nil { - panic(fmt.Errorf("failed to generate random JWT secret: %w", err)) - } } + + secretBytes = make([]byte, 32) + if _, err := rand.Read(secretBytes); err != nil { + panic(fmt.Errorf("failed to generate random JWT secret: %w", err)) + } + return secretBytes } diff --git a/backend/pkg/utils/reflection.go b/backend/pkg/utils/reflection.go index e937ac88c4..931ca8de7a 100644 --- a/backend/pkg/utils/reflection.go +++ b/backend/pkg/utils/reflection.go @@ -28,7 +28,7 @@ func ParseMetaTag(tag string) map[string]string { // ParseKeywords parses a comma-separated keywords string into a slice // Returns an empty slice if the input is empty or contains only whitespace func ParseKeywords(keywordsStr string) []string { - keywords := []string{} + var keywords []string if k := strings.TrimSpace(keywordsStr); k != "" { for kk := range strings.SplitSeq(k, ",") { if t := strings.TrimSpace(kk); t != "" { diff --git a/backend/pkg/utils/registry/helpers.go b/backend/pkg/utils/registry/helpers.go index 3043955b22..d385e4e898 100644 --- a/backend/pkg/utils/registry/helpers.go +++ b/backend/pkg/utils/registry/helpers.go @@ -95,7 +95,7 @@ func DecodeAuthHeader(authEncoded string) (dockerregistry.AuthConfig, error) { return *cfg, nil } -func RegistryAuthLookupKeys(url string) []string { +func AuthLookupKeys(url string) []string { normalizedHost := NormalizeRegistryForComparison(url) if normalizedHost == "" { return nil diff --git a/backend/pkg/utils/registry/helpers_test.go b/backend/pkg/utils/registry/helpers_test.go index c68cd7a9bd..bb7d78c800 100644 --- a/backend/pkg/utils/registry/helpers_test.go +++ b/backend/pkg/utils/registry/helpers_test.go @@ -81,7 +81,7 @@ func TestDecodeAuthHeader_InvalidInput(t *testing.T) { } func TestRegistryAuthLookupKeys(t *testing.T) { - assert.Equal(t, []string{"ghcr.io"}, RegistryAuthLookupKeys("https://GHCR.IO/")) - assert.Equal(t, []string{"docker.io", "index.docker.io", "registry-1.docker.io"}, RegistryAuthLookupKeys("https://index.docker.io/v1/")) - assert.Nil(t, RegistryAuthLookupKeys(" ")) + assert.Equal(t, []string{"ghcr.io"}, AuthLookupKeys("https://GHCR.IO/")) + assert.Equal(t, []string{"docker.io", "index.docker.io", "registry-1.docker.io"}, AuthLookupKeys("https://index.docker.io/v1/")) + assert.Nil(t, AuthLookupKeys(" ")) } diff --git a/cli/go.mod b/cli/go.mod index 8c733d4a4e..baef921d95 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -24,7 +24,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/colorprofile v0.4.3 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect - github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20260416161146-9c68a866306c // indirect github.com/charmbracelet/x/ansi v0.11.7 // indirect github.com/charmbracelet/x/termios v0.1.1 // indirect github.com/charmbracelet/x/windows v0.2.2 // indirect @@ -43,8 +43,8 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.4.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mattn/go-isatty v0.0.21 // indirect + github.com/mattn/go-shellwords v1.0.13 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/moby/api v1.54.2 // indirect github.com/moby/moby/client v0.4.1 // indirect @@ -61,7 +61,7 @@ require ( github.com/xhit/go-str2duration/v2 v2.1.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect diff --git a/cli/go.sum b/cli/go.sum index 4ede7ef899..9511f8c1f2 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -16,8 +16,8 @@ github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468 h1:Q9fO0y1Zo5KB/5Vu8JZoLGm1N3RzF9bNj3Ao3xoR+Ac= -github.com/charmbracelet/ultraviolet v0.0.0-20260416155717-489999b90468/go.mod h1:bAAz7dh/FTYfC+oiHavL4mX1tOIBZ0ZwYjSi3qE6ivM= +github.com/charmbracelet/ultraviolet v0.0.0-20260416161146-9c68a866306c h1:a+Q3cOt8vEb6ETG/st32Qjm8R5fdI9wSKb3tqPISnoY= +github.com/charmbracelet/ultraviolet v0.0.0-20260416161146-9c68a866306c/go.mod h1:bAAz7dh/FTYfC+oiHavL4mX1tOIBZ0ZwYjSi3qE6ivM= github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI= github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ= github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA= @@ -44,6 +44,7 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= @@ -78,12 +79,12 @@ github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs= +github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.13 h1:DC0OMEpGjm6LfNFU4ckYcvbQKyp2vE8atyFGXNtDcf4= +github.com/mattn/go-shellwords v1.0.13/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= @@ -130,8 +131,8 @@ 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= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= @@ -152,7 +153,6 @@ golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8t golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= diff --git a/cli/internal/config/config.go b/cli/internal/config/config.go index 6d2cd3b76d..de8c84bd0c 100644 --- a/cli/internal/config/config.go +++ b/cli/internal/config/config.go @@ -124,7 +124,7 @@ func DefaultConfig() *types.Config { // ConfigPath returns the absolute path to the configuration file. // The config file is located at ~/.config/arcanecli.yml. // Returns an error if the user's home directory cannot be determined. -func ConfigPath() (string, error) { +func Path() (string, error) { if customConfigPath != "" { return customConfigPath, nil } @@ -170,7 +170,7 @@ func SetConfigPath(path string) error { // If the config file does not exist, default values are returned. // Returns an error if the file exists but cannot be read or parsed. func Load() (*types.Config, error) { - path, err := ConfigPath() + path, err := Path() if err != nil { return nil, err } @@ -230,7 +230,7 @@ func Load() (*types.Config, error) { // The config directory is created if it does not exist. // The file is created with 0600 permissions for security. func Save(c *types.Config) error { - path, err := ConfigPath() + path, err := Path() if err != nil { return err } @@ -311,7 +311,7 @@ func Save(c *types.Config) error { // not already exist. It returns true when a file is created, or false when an // existing file is left unchanged. func InitDefaultFile() (bool, error) { - path, err := ConfigPath() + path, err := Path() if err != nil { return false, err } @@ -370,7 +370,7 @@ func InitDefaultFile() (bool, error) { // original file from its previous location. If no config file exists, it // returns moved=false and no error. func BackupFile() (backupPath string, moved bool, err error) { - path, err := ConfigPath() + path, err := Path() if err != nil { return "", false, err } diff --git a/cli/pkg/admin/apikeys/cmd.go b/cli/pkg/admin/apikeys/cmd.go index dab8200e6c..e706f945b0 100644 --- a/cli/pkg/admin/apikeys/cmd.go +++ b/cli/pkg/admin/apikeys/cmd.go @@ -27,8 +27,7 @@ var ( apikeyUpdateExpiresAt string ) -// ApiKeysCmd is the parent command for API key operations -var ApiKeysCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "api-keys", Aliases: []string{"apikey", "keys", "key"}, Short: "Manage API keys", @@ -63,12 +62,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "NAME", "DESCRIPTION", "CREATED", "LAST USED"} @@ -144,12 +138,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("API key created successfully") @@ -200,12 +189,7 @@ var deleteCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("API key deleted successfully") @@ -236,12 +220,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("API Key Details") @@ -299,12 +278,10 @@ var updateCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("API key updated successfully") @@ -313,11 +290,11 @@ var updateCmd = &cobra.Command{ } func init() { - ApiKeysCmd.AddCommand(listCmd) - ApiKeysCmd.AddCommand(createCmd) - ApiKeysCmd.AddCommand(getCmd) - ApiKeysCmd.AddCommand(updateCmd) - ApiKeysCmd.AddCommand(deleteCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(createCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(deleteCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of API keys to show") diff --git a/cli/pkg/admin/cmd.go b/cli/pkg/admin/cmd.go index c671a04518..6d1e4c7910 100644 --- a/cli/pkg/admin/cmd.go +++ b/cli/pkg/admin/cmd.go @@ -8,16 +8,15 @@ import ( "github.com/spf13/cobra" ) -// AdminCmd is the parent command for administrative operations. -var AdminCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "admin", Aliases: []string{"adm"}, Short: "Administration & platform management", } func init() { - AdminCmd.AddCommand(users.UsersCmd) - AdminCmd.AddCommand(apikeys.ApiKeysCmd) - AdminCmd.AddCommand(events.EventsCmd) - AdminCmd.AddCommand(notifications.NotificationsCmd) + Cmd.AddCommand(users.Cmd) + Cmd.AddCommand(apikeys.Cmd) + Cmd.AddCommand(events.Cmd) + Cmd.AddCommand(notifications.Cmd) } diff --git a/cli/pkg/admin/events/cmd.go b/cli/pkg/admin/events/cmd.go index 42bab6f229..0d4f98da82 100644 --- a/cli/pkg/admin/events/cmd.go +++ b/cli/pkg/admin/events/cmd.go @@ -20,8 +20,7 @@ var ( jsonOutput bool ) -// EventsCmd is the parent command for event operations -var EventsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "events", Aliases: []string{"event", "evt"}, Short: "Manage events", @@ -56,12 +55,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "TYPE", "RESOURCE", "USER", "TIMESTAMP"} @@ -118,12 +112,7 @@ var listEnvCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "TYPE", "RESOURCE", "USER", "TIMESTAMP"} @@ -190,9 +179,9 @@ var deleteCmd = &cobra.Command{ } func init() { - EventsCmd.AddCommand(listCmd) - EventsCmd.AddCommand(listEnvCmd) - EventsCmd.AddCommand(deleteCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(listEnvCmd) + Cmd.AddCommand(deleteCmd) listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of events to show") listCmd.Flags().IntVar(&startFlag, "start", 0, "Offset for pagination") diff --git a/cli/pkg/admin/notifications/cmd.go b/cli/pkg/admin/notifications/cmd.go index 78f8df9c5b..f40f2facf7 100644 --- a/cli/pkg/admin/notifications/cmd.go +++ b/cli/pkg/admin/notifications/cmd.go @@ -18,8 +18,7 @@ var ( notifForceFlag bool ) -// NotificationsCmd is the parent command for notification operations -var NotificationsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "notifications", Aliases: []string{"notif", "notify"}, Short: "Manage notifications", @@ -57,12 +56,7 @@ var appriseGetCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Apprise Configuration") @@ -97,12 +91,7 @@ var settingsGetCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "PROVIDER", "ENABLED"} @@ -142,12 +131,10 @@ var appriseTestCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Apprise notification test successful") @@ -213,12 +200,10 @@ var testProviderCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Notification test for %s successful", args[0]) @@ -227,9 +212,9 @@ var testProviderCmd = &cobra.Command{ } func init() { - NotificationsCmd.AddCommand(appriseCmd) - NotificationsCmd.AddCommand(settingsCmd) - NotificationsCmd.AddCommand(testProviderCmd) + Cmd.AddCommand(appriseCmd) + Cmd.AddCommand(settingsCmd) + Cmd.AddCommand(testProviderCmd) appriseCmd.AddCommand(appriseGetCmd) appriseCmd.AddCommand(appriseTestCmd) diff --git a/cli/pkg/admin/users/cmd.go b/cli/pkg/admin/users/cmd.go index b88709d4bd..ea29b98e76 100644 --- a/cli/pkg/admin/users/cmd.go +++ b/cli/pkg/admin/users/cmd.go @@ -38,8 +38,7 @@ var ( userUpdateRoles []string ) -// UsersCmd is the parent command for user operations -var UsersCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "users", Aliases: []string{"user", "usr"}, Short: "Manage users", @@ -74,12 +73,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "USERNAME", "DISPLAY NAME", "EMAIL", "ROLES"} @@ -158,12 +152,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("User %s created successfully", result.Data.Username) @@ -197,12 +186,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("User Details") @@ -256,12 +240,10 @@ var updateCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("User updated successfully") @@ -307,11 +289,11 @@ var deleteCmd = &cobra.Command{ } func init() { - UsersCmd.AddCommand(listCmd) - UsersCmd.AddCommand(createCmd) - UsersCmd.AddCommand(getCmd) - UsersCmd.AddCommand(updateCmd) - UsersCmd.AddCommand(deleteCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(createCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(deleteCmd) listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of users to show") listCmd.Flags().IntVar(&startFlag, "start", 0, "Offset for pagination") diff --git a/cli/pkg/alerts/cmd.go b/cli/pkg/alerts/cmd.go index f76876be9f..ba4f7f516b 100644 --- a/cli/pkg/alerts/cmd.go +++ b/cli/pkg/alerts/cmd.go @@ -15,8 +15,7 @@ import ( var debugAllGood bool -// AlertsCmd shows dashboard alerts/action items. -var AlertsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "alerts", Aliases: []string{"alert", "actionitems"}, Short: "Show dashboard alerts", @@ -96,6 +95,6 @@ func formatActionItemKind(kind dashboardtypes.ActionItemKind) string { } func init() { - AlertsCmd.Flags().Bool("json", false, "Output in JSON format") - AlertsCmd.Flags().BoolVar(&debugAllGood, "debug-all-good", false, "Force no alerts (debug mode)") + Cmd.Flags().Bool("json", false, "Output in JSON format") + Cmd.Flags().BoolVar(&debugAllGood, "debug-all-good", false, "Force no alerts (debug mode)") } diff --git a/cli/pkg/auth/cmd.go b/cli/pkg/auth/cmd.go index c9ffda6a61..35f8a73371 100644 --- a/cli/pkg/auth/cmd.go +++ b/cli/pkg/auth/cmd.go @@ -20,8 +20,8 @@ import ( var jsonOutput bool -// AuthCmd is the parent command for authentication operations -var AuthCmd = &cobra.Command{ +// Cmd is the parent command for authentication operations +var Cmd = &cobra.Command{ Use: "auth", Aliases: []string{"authentication"}, Short: "Authentication operations", @@ -133,18 +133,13 @@ var loginCmd = &cobra.Command{ } if cmdutil.JSONOutputEnabled(cmd) || jsonOutput { - resultBytes, err := json.MarshalIndent(map[string]any{ + return cmdutil.PrintJSON(map[string]any{ "success": tokenResult.Success, "token": tokenResult.Token, "refreshToken": tokenResult.RefreshToken, "expiresAt": tokenResult.ExpiresAt, "user": tokenResult.User, - }, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + }) } cfg, err := config.Load() @@ -159,7 +154,7 @@ var loginCmd = &cobra.Command{ } output.Success("Login successful") - path, _ := config.ConfigPath() + path, _ := config.Path() output.KeyValue("JWT token saved to config", path) return nil } @@ -198,16 +193,14 @@ var logoutCmd = &cobra.Command{ if cmdutil.JSONOutputEnabled(cmd) || jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Logout successful") - path, _ := config.ConfigPath() + path, _ := config.Path() output.KeyValue("JWT token cleared from config", path) return nil }, @@ -238,12 +231,7 @@ var meCmd = &cobra.Command{ } if cmdutil.JSONOutputEnabled(cmd) || jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Current User") @@ -310,12 +298,10 @@ var passwordCmd = &cobra.Command{ if cmdutil.JSONOutputEnabled(cmd) || jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Password changed successfully") @@ -370,16 +356,11 @@ var refreshCmd = &cobra.Command{ } if cmdutil.JSONOutputEnabled(cmd) || jsonOutput { - resultBytes, err := json.MarshalIndent(map[string]any{ + return cmdutil.PrintJSON(map[string]any{ "token": result.Data.Token, "refreshToken": result.Data.RefreshToken, "expiresAt": result.Data.ExpiresAt, - }, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + }) } // Save new JWT token to config @@ -393,7 +374,7 @@ var refreshCmd = &cobra.Command{ } output.Success("Token refreshed successfully") - path, _ := config.ConfigPath() + path, _ := config.Path() output.KeyValue("New JWT token saved to config", path) return nil }, @@ -428,12 +409,7 @@ var oidcStatusCmd = &cobra.Command{ } if cmdutil.JSONOutputEnabled(cmd) || jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } output.Header("OIDC Status") @@ -445,12 +421,12 @@ var oidcStatusCmd = &cobra.Command{ } func init() { - AuthCmd.AddCommand(loginCmd) - AuthCmd.AddCommand(logoutCmd) - AuthCmd.AddCommand(meCmd) - AuthCmd.AddCommand(passwordCmd) - AuthCmd.AddCommand(refreshCmd) - AuthCmd.AddCommand(oidcStatusCmd) + Cmd.AddCommand(loginCmd) + Cmd.AddCommand(logoutCmd) + Cmd.AddCommand(meCmd) + Cmd.AddCommand(passwordCmd) + Cmd.AddCommand(refreshCmd) + Cmd.AddCommand(oidcStatusCmd) // Login command flags loginCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") diff --git a/cli/pkg/config/cmd.go b/cli/pkg/config/cmd.go index 7a26bd7ff3..2d755af624 100644 --- a/cli/pkg/config/cmd.go +++ b/cli/pkg/config/cmd.go @@ -25,8 +25,7 @@ var ( setResourceLimit []string ) -// ConfigCmd is the command for managing API configuration -var ConfigCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "config", Short: "Manage Arcane CLI's Configuration", } @@ -40,7 +39,7 @@ var configShowCmd = &cobra.Command{ return fmt.Errorf("failed to load config: %w", err) } - path, _ := config.ConfigPath() + path, _ := config.Path() fmt.Printf("Config file: %s\n\n", path) fmt.Printf("Server URL: %s\n", maskIfEmpty(cfg.ServerURL, "(not set)")) fmt.Printf("API Key: %s\n", maskAPIKey(cfg.APIKey)) @@ -142,7 +141,7 @@ Legacy flag syntax (flags shown below) is still supported: return fmt.Errorf("failed to save config: %w", err) } - path, _ := config.ConfigPath() + path, _ := config.Path() fmt.Printf("\nConfiguration saved to %s\n", path) return nil @@ -153,7 +152,7 @@ var configPathCmd = &cobra.Command{ Use: "path", Short: "Print the config file path", RunE: func(cmd *cobra.Command, args []string) error { - path, err := config.ConfigPath() + path, err := config.Path() if err != nil { return err } @@ -215,7 +214,7 @@ var configInitCmd = &cobra.Command{ If the config file already exists, this command is a no-op and does not overwrite it.`, RunE: func(cmd *cobra.Command, args []string) error { - path, err := config.ConfigPath() + path, err := config.Path() if err != nil { return err } @@ -244,7 +243,7 @@ var configBackupCmd = &cobra.Command{ This removes the original config file from its previous path.`, RunE: func(cmd *cobra.Command, args []string) error { - path, err := config.ConfigPath() + path, err := config.Path() if err != nil { return err } @@ -265,12 +264,12 @@ This removes the original config file from its previous path.`, } func init() { - ConfigCmd.AddCommand(configShowCmd) - ConfigCmd.AddCommand(configSetCmd) - ConfigCmd.AddCommand(configInitCmd) - ConfigCmd.AddCommand(configBackupCmd) - ConfigCmd.AddCommand(configPathCmd) - ConfigCmd.AddCommand(configTestCmd) + Cmd.AddCommand(configShowCmd) + Cmd.AddCommand(configSetCmd) + Cmd.AddCommand(configInitCmd) + Cmd.AddCommand(configBackupCmd) + Cmd.AddCommand(configPathCmd) + Cmd.AddCommand(configTestCmd) configSetCmd.Flags().StringVar(&setServerURL, "server-url", "", "Arcane server URL (e.g., http://localhost:3553)") configSetCmd.Flags().StringVar(&setAPIKey, "api-key", "", "API key for authentication") diff --git a/cli/pkg/containers/cmd.go b/cli/pkg/containers/cmd.go index 40b1ffa849..ea7935e598 100644 --- a/cli/pkg/containers/cmd.go +++ b/cli/pkg/containers/cmd.go @@ -50,8 +50,7 @@ var ( const maxPromptOptions = 20 -// ContainersCmd is the parent command for container operations -var ContainersCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "containers", Aliases: []string{"container", "c"}, Short: "Manage containers", @@ -98,12 +97,7 @@ func runContainersList(cmd *cobra.Command, forceHasUpdateFilter bool) error { } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } effectiveUpdatesFilter := strings.TrimSpace(containersUpdatesFilter) @@ -240,12 +234,7 @@ var containersGetCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(resolved, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(resolved) } output.Header("Container Details") @@ -287,12 +276,7 @@ var containersStartCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s started successfully", containerDisplayName(resolved)) @@ -329,12 +313,7 @@ var containersStopCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s stopped successfully", containerDisplayName(resolved)) @@ -371,12 +350,7 @@ var containersRestartCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s restarted successfully", containerDisplayName(resolved)) @@ -416,12 +390,7 @@ var containersUpdateCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s updated successfully", containerDisplayName(resolved)) @@ -460,12 +429,7 @@ var containersRedeployCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s redeployed successfully", containerDisplayName(resolved)) @@ -524,12 +488,7 @@ var containersDeleteCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s deleted successfully", displayName) @@ -560,12 +519,7 @@ var containersCountsCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Container Status Counts") @@ -688,12 +642,7 @@ var containersCreateCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Container %s created successfully", result.Data.Name) @@ -706,17 +655,17 @@ var containersCreateCmd = &cobra.Command{ } func init() { - ContainersCmd.AddCommand(containersListCmd) - ContainersCmd.AddCommand(containersUpdatesCmd) - ContainersCmd.AddCommand(containersGetCmd) - ContainersCmd.AddCommand(containersStartCmd) - ContainersCmd.AddCommand(containersStopCmd) - ContainersCmd.AddCommand(containersRestartCmd) - ContainersCmd.AddCommand(containersUpdateCmd) - ContainersCmd.AddCommand(containersRedeployCmd) - ContainersCmd.AddCommand(containersDeleteCmd) - ContainersCmd.AddCommand(containersCountsCmd) - ContainersCmd.AddCommand(containersCreateCmd) + Cmd.AddCommand(containersListCmd) + Cmd.AddCommand(containersUpdatesCmd) + Cmd.AddCommand(containersGetCmd) + Cmd.AddCommand(containersStartCmd) + Cmd.AddCommand(containersStopCmd) + Cmd.AddCommand(containersRestartCmd) + Cmd.AddCommand(containersUpdateCmd) + Cmd.AddCommand(containersRedeployCmd) + Cmd.AddCommand(containersDeleteCmd) + Cmd.AddCommand(containersCountsCmd) + Cmd.AddCommand(containersCreateCmd) // Create command flags containersCreateCmd.Flags().StringVarP(&containerCreateFile, "file", "f", "", "JSON config file for container creation") diff --git a/cli/pkg/doctor/cmd.go b/cli/pkg/doctor/cmd.go index 529ba78fdb..68f4a084d6 100644 --- a/cli/pkg/doctor/cmd.go +++ b/cli/pkg/doctor/cmd.go @@ -1,10 +1,10 @@ package doctor import ( - "encoding/json" "fmt" "strings" + "github.com/getarcaneapp/arcane/cli/internal/cmdutil" "github.com/getarcaneapp/arcane/cli/internal/config" "github.com/getarcaneapp/arcane/cli/internal/output" runtimectx "github.com/getarcaneapp/arcane/cli/internal/runtime" @@ -24,8 +24,7 @@ type report struct { var jsonOutput bool -// DoctorCmd runs environment and connection diagnostics. -var DoctorCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "doctor", Short: "Run CLI diagnostics", SilenceUsage: true, @@ -45,7 +44,7 @@ var DoctorCmd = &cobra.Command{ } rep := report{Healthy: true} - path, _ := config.ConfigPath() + path, _ := config.Path() rep.Checks = append(rep.Checks, checkResult{ Name: "config_path", Status: "ok", @@ -103,23 +102,19 @@ var DoctorCmd = &cobra.Command{ } if jsonOutput || app.IsJSON() { - b, err := json.MarshalIndent(rep, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal doctor output: %w", err) - } - fmt.Println(string(b)) + return cmdutil.PrintJSON(rep) + } + + output.Header("Arcane CLI Diagnostics") + rows := make([][]string, 0, len(rep.Checks)) + for _, item := range rep.Checks { + rows = append(rows, []string{item.Name, item.Status, item.Details}) + } + output.Table([]string{"CHECK", "STATUS", "DETAILS"}, rows) + if rep.Healthy { + output.Success("Doctor checks passed") } else { - output.Header("Arcane CLI Diagnostics") - rows := make([][]string, 0, len(rep.Checks)) - for _, item := range rep.Checks { - rows = append(rows, []string{item.Name, item.Status, item.Details}) - } - output.Table([]string{"CHECK", "STATUS", "DETAILS"}, rows) - if rep.Healthy { - output.Success("Doctor checks passed") - } else { - output.Warning("Doctor found issues") - } + output.Warning("Doctor found issues") } if !rep.Healthy { @@ -130,5 +125,5 @@ var DoctorCmd = &cobra.Command{ } func init() { - DoctorCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") + Cmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") } diff --git a/cli/pkg/environments/cmd.go b/cli/pkg/environments/cmd.go index 97da6a108f..5bcf2cc1c7 100644 --- a/cli/pkg/environments/cmd.go +++ b/cli/pkg/environments/cmd.go @@ -39,8 +39,7 @@ var ( enabledStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#6d28d9")) ) -// EnvironmentsCmd is the parent command for environment operations -var EnvironmentsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "environments", Aliases: []string{"environment", "env", "e"}, Short: "Manage environments", @@ -75,12 +74,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "NAME", "API URL", "STATUS", "ENABLED"} @@ -165,12 +159,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(buildEnvironmentPayloadInternal(result.Data), "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(buildEnvironmentPayloadInternal(result.Data)) } output.Header("Environment Details") @@ -205,12 +194,10 @@ var testCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Environment connection test successful") @@ -374,12 +361,7 @@ var updateCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(buildEnvironmentPayloadInternal(result.Data), "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(buildEnvironmentPayloadInternal(result.Data)) } output.Success("Environment updated successfully") @@ -426,12 +408,7 @@ var versionCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } output.Header("Environment Version") @@ -475,13 +452,13 @@ func buildEnvironmentPayloadInternal(env environment.Environment) map[string]any } func init() { - EnvironmentsCmd.AddCommand(listCmd) - EnvironmentsCmd.AddCommand(getCmd) - EnvironmentsCmd.AddCommand(testCmd) - EnvironmentsCmd.AddCommand(deleteCmd) - EnvironmentsCmd.AddCommand(switchCmd) - EnvironmentsCmd.AddCommand(updateCmd) - EnvironmentsCmd.AddCommand(versionCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(testCmd) + Cmd.AddCommand(deleteCmd) + Cmd.AddCommand(switchCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(versionCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of environments to show") diff --git a/cli/pkg/generate/api_key.go b/cli/pkg/generate/api_key.go index f19013a5ae..6c18cf3af7 100644 --- a/cli/pkg/generate/api_key.go +++ b/cli/pkg/generate/api_key.go @@ -23,10 +23,10 @@ var apiKeyCmd = &cobra.Command{ } func init() { - GenerateCmd.AddCommand(apiKeyCmd) + Cmd.AddCommand(apiKeyCmd) } -func GenerateAPIKey() (string, error) { +func APIKey() (string, error) { keyBytes := make([]byte, apiKeyLength) if _, err := crand.Read(keyBytes); err != nil { return "", fmt.Errorf("failed to generate API key: %w", err) @@ -36,7 +36,7 @@ func GenerateAPIKey() (string, error) { } func generateAPIKeyOutputInternal() error { - apiKey, err := GenerateAPIKey() + apiKey, err := APIKey() if err != nil { return err } diff --git a/cli/pkg/generate/api_key_test.go b/cli/pkg/generate/api_key_test.go index 0c7fec83c6..d2617d81d6 100644 --- a/cli/pkg/generate/api_key_test.go +++ b/cli/pkg/generate/api_key_test.go @@ -8,7 +8,7 @@ import ( ) func TestAPIKeyDefaultOutput(t *testing.T) { - cmd := gen.GenerateCmd + cmd := gen.Cmd cmd.SetArgs([]string{"api-key"}) out, err := captureOutput(func() error { @@ -31,7 +31,7 @@ func TestAPIKeyDefaultOutput(t *testing.T) { } func TestGenerateAPIKeyProducesArcanePrefix(t *testing.T) { - apiKey, err := gen.GenerateAPIKey() + apiKey, err := gen.APIKey() if err != nil { t.Fatalf("GenerateAPIKey failed: %v", err) } diff --git a/cli/pkg/generate/certs.go b/cli/pkg/generate/certs.go index 4c2d5b3a38..7b66f57a74 100644 --- a/cli/pkg/generate/certs.go +++ b/cli/pkg/generate/certs.go @@ -53,8 +53,8 @@ var tlsCmd = &cobra.Command{ } func init() { - GenerateCmd.AddCommand(mtlsCmd) - GenerateCmd.AddCommand(tlsCmd) + Cmd.AddCommand(mtlsCmd) + Cmd.AddCommand(tlsCmd) mtlsCmd.Flags().StringVar(&mtlsOutDir, "out-dir", "./edge-mtls", "directory to write generated edge mTLS assets") mtlsCmd.Flags().StringVar(&mtlsEnvID, "env-id", "local-edge", "environment identifier to embed in the generated edge agent certificate") diff --git a/cli/pkg/generate/certs_test.go b/cli/pkg/generate/certs_test.go index 4d5d6ed1c5..da45472a50 100644 --- a/cli/pkg/generate/certs_test.go +++ b/cli/pkg/generate/certs_test.go @@ -16,7 +16,7 @@ import ( func TestGenerateMTLSCommandWritesECDSAP384Assets(t *testing.T) { outDir := t.TempDir() - cmd := gen.GenerateCmd + cmd := gen.Cmd cmd.SetArgs([]string{"mtls", "--out-dir", outDir, "--env-id", "env-123", "--app-url", "https://manager.example.com"}) _, err := captureOutput(func() error { @@ -39,7 +39,7 @@ func TestGenerateMTLSCommandWritesECDSAP384Assets(t *testing.T) { func TestGenerateTLSCommandWritesECDSAP384ServerCert(t *testing.T) { outDir := t.TempDir() - cmd := gen.GenerateCmd + cmd := gen.Cmd cmd.SetArgs([]string{"tls", "--out-dir", outDir, "--common-name", "localhost", "--host", "localhost", "--host", "127.0.0.1", "--cert-name", "local-manager.crt", "--key-name", "local-manager.key"}) _, err := captureOutput(func() error { @@ -64,7 +64,7 @@ func TestGenerateTLSCommandWritesECDSAP384ServerCert(t *testing.T) { func TestGenerateTLSCommandOverwritesCertificateAtomically(t *testing.T) { outDir := t.TempDir() - cmd := gen.GenerateCmd + cmd := gen.Cmd cmd.SetArgs([]string{"tls", "--out-dir", outDir, "--common-name", "localhost", "--host", "localhost", "--cert-name", "server.crt", "--key-name", "server.key"}) _, err := captureOutput(func() error { _, err := cmd.ExecuteC() diff --git a/cli/pkg/generate/generate.go b/cli/pkg/generate/generate.go index cdaf9dfa01..e48a13426e 100644 --- a/cli/pkg/generate/generate.go +++ b/cli/pkg/generate/generate.go @@ -4,7 +4,7 @@ import ( "github.com/spf13/cobra" ) -var GenerateCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "generate", Aliases: []string{"gen", "g"}, Short: "Generate secrets and bootstrap credentials for Arcane", diff --git a/cli/pkg/generate/secret.go b/cli/pkg/generate/secret.go index 4297ba39b0..37034941e0 100644 --- a/cli/pkg/generate/secret.go +++ b/cli/pkg/generate/secret.go @@ -24,7 +24,7 @@ var secretCmd = &cobra.Command{ } func init() { - GenerateCmd.AddCommand(secretCmd) + Cmd.AddCommand(secretCmd) secretCmd.Flags().StringVarP(&secretFormat, "format", "f", "base64", "output format: base64, hex, env, docker, all") secretCmd.Flags().IntVarP(&secretLength, "length", "l", 32, "secret length in bytes (default: 32 for AES-256)") } diff --git a/cli/pkg/generate/secret_test.go b/cli/pkg/generate/secret_test.go index 37cf2b5093..a0010ebbcd 100644 --- a/cli/pkg/generate/secret_test.go +++ b/cli/pkg/generate/secret_test.go @@ -36,7 +36,7 @@ func captureOutput(fn func() error) (string, error) { } func TestSecretDefaultBase64(t *testing.T) { - cmd := gen.GenerateCmd + cmd := gen.Cmd cmd.SetArgs([]string{"secret"}) out, err := captureOutput(func() error { @@ -83,7 +83,7 @@ func TestSecretDefaultBase64(t *testing.T) { } func TestSecretAllFormatContainsSections(t *testing.T) { - cmd := gen.GenerateCmd + cmd := gen.Cmd cmd.SetArgs([]string{"secret", "-f", "all"}) out, err := captureOutput(func() error { diff --git a/cli/pkg/gitops/cmd.go b/cli/pkg/gitops/cmd.go index 798fd2a264..274ff35795 100644 --- a/cli/pkg/gitops/cmd.go +++ b/cli/pkg/gitops/cmd.go @@ -38,8 +38,7 @@ var ( const maxPromptOptions = 20 -// GitopsCmd is the parent command for gitops sync operations -var GitopsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "gitops", Aliases: []string{"gitops-syncs", "gs"}, Short: "Manage GitOps syncs", @@ -74,12 +73,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "NAME", "BRANCH", "AUTO-SYNC", "LAST STATUS", "LAST SYNC"} @@ -159,12 +153,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("GitOps sync %s created successfully (ID: %s)", result.Data.Name, result.Data.ID) @@ -204,12 +193,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(resolved, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(resolved) } output.Header("GitOps Sync Details") @@ -361,12 +345,7 @@ var statusCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("GitOps Sync Status") @@ -419,12 +398,7 @@ var syncCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } if result.Data.Success { @@ -469,12 +443,7 @@ var filesCmd = &cobra.Command{ files := result.Data.Files if jsonOutput { - resultBytes, err := json.MarshalIndent(files, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(files) } headers := []string{"NAME", "TYPE", "PATH", "SIZE"} @@ -527,12 +496,7 @@ var importCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Import completed: %d succeeded, %d failed", result.Data.SuccessCount, result.Data.FailedCount) @@ -547,15 +511,15 @@ var importCmd = &cobra.Command{ } func init() { - GitopsCmd.AddCommand(listCmd) - GitopsCmd.AddCommand(createCmd) - GitopsCmd.AddCommand(getCmd) - GitopsCmd.AddCommand(updateCmd) - GitopsCmd.AddCommand(deleteCmd) - GitopsCmd.AddCommand(statusCmd) - GitopsCmd.AddCommand(syncCmd) - GitopsCmd.AddCommand(filesCmd) - GitopsCmd.AddCommand(importCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(createCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(deleteCmd) + Cmd.AddCommand(statusCmd) + Cmd.AddCommand(syncCmd) + Cmd.AddCommand(filesCmd) + Cmd.AddCommand(importCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of syncs to show") diff --git a/cli/pkg/images/cmd.go b/cli/pkg/images/cmd.go index 7da6ef08a0..b358037259 100644 --- a/cli/pkg/images/cmd.go +++ b/cli/pkg/images/cmd.go @@ -69,8 +69,7 @@ var ( const maxPromptOptions = 20 -// ImagesCmd is the parent command for image operations -var ImagesCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "images", Aliases: []string{"image", "i"}, Short: "Manage images", @@ -151,12 +150,7 @@ var imagesListCmd = &cobra.Command{ result.Pagination.TotalItems = int64(len(result.Data)) if cmdutil.JSONOutputEnabled(cmd) { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "REPOSITORY:TAG", "SIZE", "IN USE"} @@ -704,7 +698,7 @@ var imagesUploadCmd = &cobra.Command{ } func init() { - ImagesCmd.AddCommand(imagesListCmd) + Cmd.AddCommand(imagesListCmd) imagesListCmd.Flags().IntVarP(&imagesLimit, "limit", "n", 0, "Number of images to show (server default 20)") imagesListCmd.Flags().IntVar(&imagesStart, "start", 0, "Offset for pagination") imagesListCmd.Flags().StringVar(&imagesSort, "sort", "", "Field to sort by") @@ -713,20 +707,20 @@ func init() { imagesListCmd.Flags().BoolVar(&imagesInUseOnly, "inuse", false, "Only show images currently in use") imagesListCmd.Flags().BoolVar(&imagesUnusedOnly, "unused", false, "Only show images not in use") - ImagesCmd.AddCommand(imagesGetCmd) + Cmd.AddCommand(imagesGetCmd) - ImagesCmd.AddCommand(imagesRemoveCmd) + Cmd.AddCommand(imagesRemoveCmd) imagesRemoveCmd.Flags().BoolVarP(&removeForce, "force", "f", false, "Force removal of image") - ImagesCmd.AddCommand(imagesPullCmd) + Cmd.AddCommand(imagesPullCmd) - ImagesCmd.AddCommand(imagesPruneCmd) + Cmd.AddCommand(imagesPruneCmd) imagesPruneCmd.Flags().BoolVar(&pruneDangling, "dangling", false, "Only remove dangling images") - ImagesCmd.AddCommand(imagesCountsCmd) - ImagesCmd.AddCommand(updates.UpdatesCmd) + Cmd.AddCommand(imagesCountsCmd) + Cmd.AddCommand(updates.Cmd) - ImagesCmd.AddCommand(imagesUploadCmd) + Cmd.AddCommand(imagesUploadCmd) } func resolveImageID(ctx context.Context, c *client.Client, identifier string, allowPrompt bool) (string, error) { diff --git a/cli/pkg/images/updates/cmd.go b/cli/pkg/images/updates/cmd.go index 249b551c57..fc638b123a 100644 --- a/cli/pkg/images/updates/cmd.go +++ b/cli/pkg/images/updates/cmd.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/getarcaneapp/arcane/cli/internal/client" + "github.com/getarcaneapp/arcane/cli/internal/cmdutil" "github.com/getarcaneapp/arcane/cli/internal/output" "github.com/getarcaneapp/arcane/cli/internal/types" "github.com/getarcaneapp/arcane/types/base" @@ -14,8 +15,7 @@ import ( var jsonOutput bool -// UpdatesCmd is the parent command for image update operations. -var UpdatesCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "updates", Short: "Check for image updates", } @@ -42,12 +42,7 @@ var checkCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Image Update Check Results") @@ -86,12 +81,7 @@ var checkAllCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Check All Results") @@ -131,12 +121,7 @@ var checkImageCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Image Update Status") @@ -174,12 +159,7 @@ var summaryCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Image Updates Summary") @@ -192,10 +172,10 @@ var summaryCmd = &cobra.Command{ } func init() { - UpdatesCmd.AddCommand(checkCmd) - UpdatesCmd.AddCommand(checkAllCmd) - UpdatesCmd.AddCommand(checkImageCmd) - UpdatesCmd.AddCommand(summaryCmd) + Cmd.AddCommand(checkCmd) + Cmd.AddCommand(checkAllCmd) + Cmd.AddCommand(checkImageCmd) + Cmd.AddCommand(summaryCmd) checkCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") checkAllCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") diff --git a/cli/pkg/jobs/cmd.go b/cli/pkg/jobs/cmd.go index b15419e870..6b992f49d2 100644 --- a/cli/pkg/jobs/cmd.go +++ b/cli/pkg/jobs/cmd.go @@ -15,8 +15,7 @@ import ( var jsonOutput bool -// JobsCmd is the parent command for job schedule operations. -var JobsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "jobs", Aliases: []string{"job"}, Short: "Manage background jobs", @@ -47,12 +46,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - b, err := json.MarshalIndent(cfg, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(b)) - return nil + return cmdutil.PrintJSON(cfg) } output.Header("Job Schedules") @@ -101,12 +95,7 @@ var updateCmd = &cobra.Command{ } if jsonOutput { - b, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(b)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Job schedules updated") @@ -117,8 +106,8 @@ var updateCmd = &cobra.Command{ } func init() { - JobsCmd.AddCommand(getCmd) - JobsCmd.AddCommand(updateCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(updateCmd) getCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") updateCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") diff --git a/cli/pkg/networks/cmd.go b/cli/pkg/networks/cmd.go index 4780169a50..3ac3c903be 100644 --- a/cli/pkg/networks/cmd.go +++ b/cli/pkg/networks/cmd.go @@ -30,8 +30,7 @@ var ( const maxPromptOptions = 20 -// NetworksCmd is the parent command for network operations -var NetworksCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "networks", Aliases: []string{"network", "net", "n"}, Short: "Manage networks", @@ -71,12 +70,7 @@ var listCmd = &cobra.Command{ result.Pagination.TotalItems = int64(len(result.Data)) if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "NAME", "DRIVER", "SCOPE", "CREATED", "IN USE"} @@ -131,12 +125,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } display := resolvedName @@ -205,12 +194,7 @@ var deleteCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Network %s deleted successfully", display) @@ -240,12 +224,7 @@ var countsCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Network Usage Counts") @@ -292,12 +271,7 @@ var pruneCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Networks pruned successfully") @@ -307,11 +281,11 @@ var pruneCmd = &cobra.Command{ } func init() { - NetworksCmd.AddCommand(listCmd) - NetworksCmd.AddCommand(getCmd) - NetworksCmd.AddCommand(deleteCmd) - NetworksCmd.AddCommand(countsCmd) - NetworksCmd.AddCommand(pruneCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(deleteCmd) + Cmd.AddCommand(countsCmd) + Cmd.AddCommand(pruneCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of networks to show") diff --git a/cli/pkg/projects/cmd.go b/cli/pkg/projects/cmd.go index 039a590737..b500fbe501 100644 --- a/cli/pkg/projects/cmd.go +++ b/cli/pkg/projects/cmd.go @@ -40,8 +40,7 @@ var ( const maxPromptOptions = 20 -// ProjectsCmd is the parent command for project operations -var ProjectsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "projects", Aliases: []string{"project", "proj", "p"}, Short: "Manage projects", @@ -88,12 +87,7 @@ func runProjectsList(cmd *cobra.Command, forceHasUpdateFilter bool) error { } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } effectiveUpdatesFilter := strings.TrimSpace(projectsUpdatesFilter) @@ -254,12 +248,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(resolved, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(resolved) } output.Header("Project Details") @@ -474,12 +463,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Project %s created successfully", result.Data.Name) @@ -544,12 +528,7 @@ var updateCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Project %s updated successfully", resolved.Name) @@ -597,12 +576,7 @@ var updateIncludesCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Include file %s updated successfully for project %s", filepath.Base(includesFile), resolved.Name) @@ -632,12 +606,7 @@ var countsCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Project Counts") @@ -649,19 +618,19 @@ var countsCmd = &cobra.Command{ } func init() { - ProjectsCmd.AddCommand(listCmd) - ProjectsCmd.AddCommand(updatesCmd) - ProjectsCmd.AddCommand(getCmd) - ProjectsCmd.AddCommand(upCmd) - ProjectsCmd.AddCommand(downCmd) - ProjectsCmd.AddCommand(restartCmd) - ProjectsCmd.AddCommand(redeployCmd) - ProjectsCmd.AddCommand(pullCmd) - ProjectsCmd.AddCommand(countsCmd) - ProjectsCmd.AddCommand(destroyCmd) - ProjectsCmd.AddCommand(createCmd) - ProjectsCmd.AddCommand(updateCmd) - ProjectsCmd.AddCommand(updateIncludesCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(updatesCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(upCmd) + Cmd.AddCommand(downCmd) + Cmd.AddCommand(restartCmd) + Cmd.AddCommand(redeployCmd) + Cmd.AddCommand(pullCmd) + Cmd.AddCommand(countsCmd) + Cmd.AddCommand(destroyCmd) + Cmd.AddCommand(createCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(updateIncludesCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of projects to show") diff --git a/cli/pkg/registries/cmd.go b/cli/pkg/registries/cmd.go index 80110ae714..58f4e4c3cb 100644 --- a/cli/pkg/registries/cmd.go +++ b/cli/pkg/registries/cmd.go @@ -26,7 +26,7 @@ var ( registryUpdateDisabled bool ) -var RegistriesCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "registries", Aliases: []string{"registry", "reg"}, Short: "Manage container registries", @@ -61,12 +61,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "URL", "USERNAME", "ENABLED", "INSECURE"} @@ -116,12 +111,10 @@ var syncCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Registries synced successfully") @@ -151,12 +144,10 @@ var testCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Registry connection test successful") @@ -187,12 +178,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Registry Details") @@ -253,12 +239,10 @@ var updateCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Registry updated successfully") @@ -304,12 +288,12 @@ var deleteCmd = &cobra.Command{ } func init() { - RegistriesCmd.AddCommand(listCmd) - RegistriesCmd.AddCommand(getCmd) - RegistriesCmd.AddCommand(syncCmd) - RegistriesCmd.AddCommand(testCmd) - RegistriesCmd.AddCommand(updateCmd) - RegistriesCmd.AddCommand(deleteCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(syncCmd) + Cmd.AddCommand(testCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(deleteCmd) listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of registries to show") listCmd.Flags().IntVar(&startFlag, "start", 0, "Offset for pagination") diff --git a/cli/pkg/repos/cmd.go b/cli/pkg/repos/cmd.go index a21b0bd3dc..8873cd89b4 100644 --- a/cli/pkg/repos/cmd.go +++ b/cli/pkg/repos/cmd.go @@ -29,8 +29,7 @@ var ( const maxPromptOptions = 20 -// ReposCmd is the parent command for git repository operations. -var ReposCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "repos", Aliases: []string{"repo", "git-repositories", "git-repos"}, Short: "Manage git repositories", @@ -97,12 +96,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"ID", "NAME", "URL", "AUTH TYPE", "ENABLED", "CREATED"} @@ -182,12 +176,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Repository created successfully") @@ -217,12 +206,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(resolved, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(resolved) } output.Header("Repository Details") @@ -315,12 +299,7 @@ var updateCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Repository updated successfully") @@ -402,12 +381,10 @@ var testCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Repository connection test successful") @@ -443,12 +420,7 @@ var branchesCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } headers := []string{"BRANCH", "DEFAULT"} @@ -511,12 +483,7 @@ var filesCmd = &cobra.Command{ files := result.Data.Files if jsonOutput { - resultBytes, err := json.MarshalIndent(files, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(files) } headers := []string{"NAME", "TYPE", "PATH", "SIZE"} @@ -561,12 +528,10 @@ var syncCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Repositories synced successfully") @@ -644,15 +609,15 @@ func resolveGitRepository(ctx context.Context, c *client.Client, identifier stri } func init() { - ReposCmd.AddCommand(listCmd) - ReposCmd.AddCommand(createCmd) - ReposCmd.AddCommand(getCmd) - ReposCmd.AddCommand(updateCmd) - ReposCmd.AddCommand(deleteCmd) - ReposCmd.AddCommand(testCmd) - ReposCmd.AddCommand(branchesCmd) - ReposCmd.AddCommand(filesCmd) - ReposCmd.AddCommand(syncCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(createCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(deleteCmd) + Cmd.AddCommand(testCmd) + Cmd.AddCommand(branchesCmd) + Cmd.AddCommand(filesCmd) + Cmd.AddCommand(syncCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of repositories to show") diff --git a/cli/pkg/root.go b/cli/pkg/root.go index 7969d4f18c..cfd60b9c86 100644 --- a/cli/pkg/root.go +++ b/cli/pkg/root.go @@ -223,28 +223,30 @@ func init() { rootCmd.PersistentFlags().BoolVar(&logJSONOutput, "log-json", false, "Log in JSON format") rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "Print version information") - rootCmd.AddCommand(configClient.ConfigCmd) - rootCmd.AddCommand(completion.NewCommand(rootCmd)) - rootCmd.AddCommand(doctor.DoctorCmd) - rootCmd.AddCommand(generate.GenerateCmd) - rootCmd.AddCommand(version.VersionCmd) - rootCmd.AddCommand(auth.AuthCmd) - rootCmd.AddCommand(alerts.AlertsCmd) - rootCmd.AddCommand(containers.ContainersCmd) - rootCmd.AddCommand(images.ImagesCmd) - rootCmd.AddCommand(volumes.VolumesCmd) - rootCmd.AddCommand(networks.NetworksCmd) - rootCmd.AddCommand(projects.ProjectsCmd) - rootCmd.AddCommand(environments.EnvironmentsCmd) - rootCmd.AddCommand(registries.RegistriesCmd) - rootCmd.AddCommand(repos.ReposCmd) - rootCmd.AddCommand(templates.TemplatesCmd) - rootCmd.AddCommand(settings.SettingsCmd) - rootCmd.AddCommand(jobs.JobsCmd) - rootCmd.AddCommand(system.SystemCmd) - rootCmd.AddCommand(updater.UpdaterCmd) - rootCmd.AddCommand(admin.AdminCmd) - rootCmd.AddCommand(gitops.GitopsCmd) + rootCmd.AddCommand( + configClient.Cmd, + completion.NewCommand(rootCmd), + doctor.Cmd, + generate.Cmd, + version.Cmd, + auth.Cmd, + alerts.Cmd, + containers.Cmd, + images.Cmd, + volumes.Cmd, + networks.Cmd, + projects.Cmd, + environments.Cmd, + registries.Cmd, + repos.Cmd, + templates.Cmd, + settings.Cmd, + jobs.Cmd, + system.Cmd, + updater.Cmd, + admin.Cmd, + gitops.Cmd, + ) } func renderCommandHelp(cmd *cobra.Command) { diff --git a/cli/pkg/settings/cmd.go b/cli/pkg/settings/cmd.go index bb4c0a6e1a..5dd3de9d85 100644 --- a/cli/pkg/settings/cmd.go +++ b/cli/pkg/settings/cmd.go @@ -15,8 +15,7 @@ import ( var jsonOutput bool -// SettingsCmd is the parent command for settings operations -var SettingsCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "settings", Aliases: []string{"setting"}, Short: "Manage settings", @@ -45,12 +44,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"KEY", "TYPE", "VALUE"} @@ -102,12 +96,10 @@ var updateCmd = &cobra.Command{ if jsonOutput { var result any - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result) } output.Success("Settings updated successfully") @@ -137,12 +129,7 @@ var publicCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"KEY", "TYPE", "VALUE"} @@ -158,9 +145,9 @@ var publicCmd = &cobra.Command{ } func init() { - SettingsCmd.AddCommand(listCmd) - SettingsCmd.AddCommand(updateCmd) - SettingsCmd.AddCommand(publicCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(publicCmd) listCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") diff --git a/cli/pkg/system/cmd.go b/cli/pkg/system/cmd.go index fed117495a..c8935500e3 100644 --- a/cli/pkg/system/cmd.go +++ b/cli/pkg/system/cmd.go @@ -16,8 +16,7 @@ import ( var jsonOutput bool -// SystemCmd is the parent command for system operations -var SystemCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "system", Aliases: []string{"sys"}, Short: "System operations", @@ -51,12 +50,7 @@ var pruneCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("System Prune Results") @@ -87,12 +81,7 @@ var dockerInfoCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Docker Info") @@ -167,12 +156,10 @@ var startStoppedCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Started all stopped containers") @@ -217,12 +204,7 @@ var convertCmd = &cobra.Command{ "envVars": result.EnvVars, "serviceName": result.ServiceName, } - resultBytes, err := json.MarshalIndent(out, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(out) } output.Header("Conversion Result") @@ -275,12 +257,10 @@ var upgradeCmd = &cobra.Command{ if jsonOutput { var result base.ApiResponse[any] - if err := json.NewDecoder(resp.Body).Decode(&result); err == nil { - if resultBytes, err := json.MarshalIndent(result.Data, "", " "); err == nil { - fmt.Println(string(resultBytes)) - } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to parse response: %w", err) } - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("System upgrade initiated") @@ -318,12 +298,7 @@ var upgradeCheckCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } output.Header("Upgrade Check") @@ -337,14 +312,14 @@ var upgradeCheckCmd = &cobra.Command{ } func init() { - SystemCmd.AddCommand(pruneCmd) - SystemCmd.AddCommand(dockerInfoCmd) - SystemCmd.AddCommand(containersStartAllCmd) - SystemCmd.AddCommand(containersStopAllCmd) - SystemCmd.AddCommand(startStoppedCmd) - SystemCmd.AddCommand(convertCmd) - SystemCmd.AddCommand(upgradeCmd) - SystemCmd.AddCommand(upgradeCheckCmd) + Cmd.AddCommand(pruneCmd) + Cmd.AddCommand(dockerInfoCmd) + Cmd.AddCommand(containersStartAllCmd) + Cmd.AddCommand(containersStopAllCmd) + Cmd.AddCommand(startStoppedCmd) + Cmd.AddCommand(convertCmd) + Cmd.AddCommand(upgradeCmd) + Cmd.AddCommand(upgradeCheckCmd) pruneCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") dockerInfoCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") diff --git a/cli/pkg/templates/cmd.go b/cli/pkg/templates/cmd.go index 96042c84ad..569e1c2969 100644 --- a/cli/pkg/templates/cmd.go +++ b/cli/pkg/templates/cmd.go @@ -51,8 +51,7 @@ var ( templateRegUpdateDisabled bool ) -// TemplatesCmd is the parent command for template operations -var TemplatesCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "templates", Aliases: []string{"template", "tpl"}, Short: "Manage Docker Compose templates", @@ -119,12 +118,7 @@ var listCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(jsonPayload, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(jsonPayload) } headers := []string{"NAME", "CUSTOM", "REMOTE", "DESCRIPTION"} @@ -174,12 +168,7 @@ var defaultCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Default Templates") @@ -212,12 +201,7 @@ var contentCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Template Content") @@ -258,12 +242,7 @@ var registriesCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } headers := []string{"ID", "NAME", "URL", "ENABLED"} @@ -310,12 +289,7 @@ var variablesCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } headers := []string{"KEY", "VALUE"} @@ -423,12 +397,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(resolved, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(resolved) } tpl := *resolved @@ -770,13 +739,13 @@ func minInt(values ...int) int { if len(values) == 0 { return 0 } - min := values[0] + minValue := values[0] for _, value := range values[1:] { - if value < min { - min = value + if value < minValue { + minValue = value } } - return min + return minValue } func maxInt(a, b int) int { @@ -855,12 +824,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Template created successfully") @@ -914,12 +878,7 @@ var updateCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Template updated successfully") @@ -1012,12 +971,7 @@ var defaultsSaveCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Default templates saved successfully") @@ -1060,12 +1014,7 @@ var variablesUpdateCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Template variables updated successfully") @@ -1103,12 +1052,7 @@ var registriesUpdateCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Template registry updated successfully") @@ -1142,12 +1086,7 @@ var fetchCmd = &cobra.Command{ if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return fmt.Errorf("failed to parse response: %w", err) } - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Remote templates fetched successfully") @@ -1156,21 +1095,21 @@ var fetchCmd = &cobra.Command{ } func init() { - TemplatesCmd.AddCommand(listCmd) - TemplatesCmd.AddCommand(defaultCmd) - TemplatesCmd.AddCommand(contentCmd) - TemplatesCmd.AddCommand(registriesCmd) - TemplatesCmd.AddCommand(variablesCmd) - TemplatesCmd.AddCommand(deleteCmd) - TemplatesCmd.AddCommand(deleteRegistryCmd) - TemplatesCmd.AddCommand(getCmd) - TemplatesCmd.AddCommand(createCmd) - TemplatesCmd.AddCommand(updateCmd) - TemplatesCmd.AddCommand(downloadCmd) - TemplatesCmd.AddCommand(defaultsSaveCmd) - TemplatesCmd.AddCommand(variablesUpdateCmd) - TemplatesCmd.AddCommand(registriesUpdateCmd) - TemplatesCmd.AddCommand(fetchCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(defaultCmd) + Cmd.AddCommand(contentCmd) + Cmd.AddCommand(registriesCmd) + Cmd.AddCommand(variablesCmd) + Cmd.AddCommand(deleteCmd) + Cmd.AddCommand(deleteRegistryCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(createCmd) + Cmd.AddCommand(updateCmd) + Cmd.AddCommand(downloadCmd) + Cmd.AddCommand(defaultsSaveCmd) + Cmd.AddCommand(variablesUpdateCmd) + Cmd.AddCommand(registriesUpdateCmd) + Cmd.AddCommand(fetchCmd) listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of templates to show") listCmd.Flags().IntVar(&startFlag, "start", 0, "Offset for pagination") diff --git a/cli/pkg/updater/cmd.go b/cli/pkg/updater/cmd.go index 1bfd280c73..9f773b07f4 100644 --- a/cli/pkg/updater/cmd.go +++ b/cli/pkg/updater/cmd.go @@ -6,6 +6,7 @@ import ( "time" "github.com/getarcaneapp/arcane/cli/internal/client" + "github.com/getarcaneapp/arcane/cli/internal/cmdutil" "github.com/getarcaneapp/arcane/cli/internal/output" "github.com/getarcaneapp/arcane/cli/internal/types" "github.com/getarcaneapp/arcane/types/base" @@ -15,8 +16,7 @@ import ( var jsonOutput bool -// UpdaterCmd is the parent command for updater operations -var UpdaterCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "updater", Aliases: []string{"upd"}, Short: "Auto-updater operations", @@ -44,12 +44,7 @@ var statusCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Updater Status") @@ -84,12 +79,7 @@ var runCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Updater Results") @@ -124,12 +114,7 @@ var historyCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } headers := []string{"CHECKED", "UPDATED", "FAILED", "DURATION"} @@ -150,9 +135,9 @@ var historyCmd = &cobra.Command{ } func init() { - UpdaterCmd.AddCommand(statusCmd) - UpdaterCmd.AddCommand(runCmd) - UpdaterCmd.AddCommand(historyCmd) + Cmd.AddCommand(statusCmd) + Cmd.AddCommand(runCmd) + Cmd.AddCommand(historyCmd) statusCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") runCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format") diff --git a/cli/pkg/version/cmd.go b/cli/pkg/version/cmd.go index b393457202..08f8f6f830 100644 --- a/cli/pkg/version/cmd.go +++ b/cli/pkg/version/cmd.go @@ -14,8 +14,7 @@ import ( "github.com/spf13/cobra" ) -// VersionCmd gets the server version -var VersionCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "version", Short: "Get the Arcane server version", SilenceUsage: true, diff --git a/cli/pkg/volumes/cmd.go b/cli/pkg/volumes/cmd.go index 15caa46c0f..2a3df28d60 100644 --- a/cli/pkg/volumes/cmd.go +++ b/cli/pkg/volumes/cmd.go @@ -37,8 +37,7 @@ var ( const maxPromptOptions = 20 -// VolumesCmd is the parent command for volume operations -var VolumesCmd = &cobra.Command{ +var Cmd = &cobra.Command{ Use: "volumes", Aliases: []string{"volume", "vol", "v"}, Short: "Manage volumes", @@ -78,12 +77,7 @@ var listCmd = &cobra.Command{ result.Pagination.TotalItems = int64(len(result.Data)) if jsonOutput { - resultBytes, err := json.MarshalIndent(result, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result) } headers := []string{"NAME", "DRIVER", "MOUNTPOINT", "CREATED", "IN USE"} @@ -126,12 +120,7 @@ var getCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(resolved, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(resolved) } output.Header("Volume Details") @@ -213,12 +202,7 @@ var countsCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Volume Usage Counts") @@ -267,12 +251,7 @@ var pruneCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Volumes pruned successfully") @@ -302,12 +281,7 @@ var sizesCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Volume Sizes") @@ -349,12 +323,7 @@ var usageCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Header("Volume Usage: %s", resolved.Name) @@ -418,12 +387,7 @@ var createCmd = &cobra.Command{ } if jsonOutput { - resultBytes, err := json.MarshalIndent(result.Data, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal JSON: %w", err) - } - fmt.Println(string(resultBytes)) - return nil + return cmdutil.PrintJSON(result.Data) } output.Success("Volume %s created successfully", result.Data.Name) @@ -435,14 +399,14 @@ var createCmd = &cobra.Command{ } func init() { - VolumesCmd.AddCommand(listCmd) - VolumesCmd.AddCommand(getCmd) - VolumesCmd.AddCommand(deleteCmd) - VolumesCmd.AddCommand(countsCmd) - VolumesCmd.AddCommand(pruneCmd) - VolumesCmd.AddCommand(sizesCmd) - VolumesCmd.AddCommand(usageCmd) - VolumesCmd.AddCommand(createCmd) + Cmd.AddCommand(listCmd) + Cmd.AddCommand(getCmd) + Cmd.AddCommand(deleteCmd) + Cmd.AddCommand(countsCmd) + Cmd.AddCommand(pruneCmd) + Cmd.AddCommand(sizesCmd) + Cmd.AddCommand(usageCmd) + Cmd.AddCommand(createCmd) // List command flags listCmd.Flags().IntVarP(&limitFlag, "limit", "n", 20, "Number of volumes to show") diff --git a/types/apikey/apikey.go b/types/apikey/apikey.go index bbb165d105..12c952f04c 100644 --- a/types/apikey/apikey.go +++ b/types/apikey/apikey.go @@ -2,7 +2,7 @@ package apikey import "time" -// Create represents the request body for creating an API key. +// CreateApiKey represents the request body for creating an API key. type CreateApiKey struct { Name string `json:"name" minLength:"1" maxLength:"255" doc:"Name of the API key" example:"My API Key"` Description *string `json:"description,omitempty" maxLength:"1000" doc:"Optional description of the API key"` @@ -29,7 +29,7 @@ type ApiKeyCreatedDto struct { Key string `json:"key" doc:"The full API key secret (only shown once)"` } -// Update represents the request body for updating an API key. +// UpdateApiKey represents the request body for updating an API key. type UpdateApiKey struct { Name *string `json:"name,omitempty" maxLength:"255" doc:"New name for the API key"` Description *string `json:"description,omitempty" maxLength:"1000" doc:"New description for the API key"` diff --git a/types/container/container.go b/types/container/container.go index 55452116e2..1a416b764a 100644 --- a/types/container/container.go +++ b/types/container/container.go @@ -6,7 +6,7 @@ import ( "strings" "time" - containerregistry "github.com/getarcaneapp/arcane/types/containerregistry" + "github.com/getarcaneapp/arcane/types/containerregistry" imagetypes "github.com/getarcaneapp/arcane/types/image" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/network" diff --git a/types/containerregistry/container_registry.go b/types/containerregistry/container_registry.go index 748b9a1cd9..7388e89d2d 100644 --- a/types/containerregistry/container_registry.go +++ b/types/containerregistry/container_registry.go @@ -2,7 +2,7 @@ package containerregistry import "time" -// Registry represents a container registry in API responses. +// ContainerRegistry represents a container registry in API responses. type ContainerRegistry struct { // ID of the container registry. // diff --git a/types/go.mod b/types/go.mod index a65f0f00f4..6cb70afc6e 100644 --- a/types/go.mod +++ b/types/go.mod @@ -22,7 +22,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mattn/go-shellwords v1.0.13 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect @@ -30,11 +30,9 @@ require ( github.com/sirupsen/logrus v1.9.4 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect golang.org/x/sync v0.20.0 // indirect diff --git a/types/go.sum b/types/go.sum index d76261b47e..0a2b70587a 100644 --- a/types/go.sum +++ b/types/go.sum @@ -33,8 +33,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.13 h1:DC0OMEpGjm6LfNFU4ckYcvbQKyp2vE8atyFGXNtDcf4= +github.com/mattn/go-shellwords v1.0.13/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= @@ -57,8 +57,7 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8 github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= diff --git a/types/image/image.go b/types/image/image.go index dc8d051730..aaba3eb49c 100644 --- a/types/image/image.go +++ b/types/image/image.go @@ -5,7 +5,7 @@ import ( "strings" "time" - containerregistry "github.com/getarcaneapp/arcane/types/containerregistry" + "github.com/getarcaneapp/arcane/types/containerregistry" "github.com/getarcaneapp/arcane/types/vulnerability" "github.com/moby/moby/api/types/image" ) diff --git a/types/imageupdate/image_update.go b/types/imageupdate/image_update.go index 94ca0808b7..bd4c92d80d 100644 --- a/types/imageupdate/image_update.go +++ b/types/imageupdate/image_update.go @@ -3,7 +3,7 @@ package imageupdate import ( "time" - containerregistry "github.com/getarcaneapp/arcane/types/containerregistry" + "github.com/getarcaneapp/arcane/types/containerregistry" ) type Response struct { diff --git a/types/meta/template_meta.go b/types/meta/template_meta.go index aa197abd4f..0f004fee44 100644 --- a/types/meta/template_meta.go +++ b/types/meta/template_meta.go @@ -1,6 +1,6 @@ package meta -// Template represents metadata about a template. +// TemplateMeta represents metadata about a template. type TemplateMeta struct { // Version of the template. // diff --git a/types/template/template.go b/types/template/template.go index 4cdbb7a237..4d7a34f9bc 100644 --- a/types/template/template.go +++ b/types/template/template.go @@ -101,7 +101,7 @@ type RemoteRegistry struct { Templates []RemoteTemplate `json:"templates"` } -// Registry represents a local registry configuration. +// TemplateRegistry represents a local registry configuration. type TemplateRegistry struct { BaseRegistry diff --git a/types/types.go b/types/types.go index 750651d18e..666ea55227 100644 --- a/types/types.go +++ b/types/types.go @@ -1,5 +1,5 @@ package types const ( - LOCAL_DOCKER_ENVIRONMENT_ID = "0" + LocalDockerEnvironmentId = "0" )