From 22f1ea6e76e2a0b8c6dd1aa6ae6cc0da37d1116b Mon Sep 17 00:00:00 2001 From: logikonline Date: Mon, 19 Jan 2026 01:03:07 -0500 Subject: [PATCH 1/4] chore(ui): update golangci-lint config and cleanup package docs Updates golangci-lint configuration to v2 format with Go 1.23, streamlines linter settings by removing deprecated options and unnecessary exclusions. Adds package documentation and renames CleanupResult to Result for consistency. Marks unused context parameter with underscore. --- .golangci.yml | 100 ++++---------------------------- internal/pkg/cleanup/cleanup.go | 33 +++++++---- 2 files changed, 32 insertions(+), 101 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 41f9683..deb3f2a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,53 +1,42 @@ +version: "2" + linters: + default: none enable: - - gosimple - - typecheck - govet - errcheck - staticcheck - unused - dupl - #- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time. - - gofmt - misspell - gocritic - bidichk - ineffassign - revive - - gofumpt - - depguard - nakedret - unconvert - wastedassign - nolintlint - - stylecheck - enable-all: false - disable-all: true - fast: false + +formatters: + enable: + - gofmt + - gofumpt run: - go: 1.18 + go: "1.23" timeout: 10m - skip-dirs: - - node_modules - - public - - web_src linters-settings: - stylecheck: - checks: ["all", "-ST1005", "-ST1003"] nakedret: max-func-lines: 0 gocritic: disabled-checks: - ifElseChain - - singleCaseSwitch # Every time this occurred in the code, there was no other way. + - singleCaseSwitch revive: - ignore-generated-header: false severity: warning confidence: 0.8 - errorCode: 1 - warningCode: 1 rules: - name: blank-imports - name: context-as-argument @@ -72,94 +61,25 @@ linters-settings: - name: modifies-value-receiver gofumpt: extra-rules: true - lang-version: "1.18" - depguard: - # TODO: use depguard to replace import checks in gitea-vet - list-type: denylist - # Check the list against standard lib. - include-go-root: true - packages-with-error-message: - - github.com/unknwon/com: "use gitea's util and replacements" issues: exclude-rules: - # Exclude some linters from running on tests files. - path: _test\.go linters: - - gocyclo - errcheck - dupl - - gosec - - unparam - - staticcheck - - path: models/migrations/v - linters: - - gocyclo - - errcheck - - dupl - - gosec - linters: - dupl text: "webhook" - linters: - gocritic text: "`ID' should not be capitalized" - - path: modules/templates/helper.go - linters: - - gocritic - - linters: - - unused - text: "swagger" - - path: contrib/pr/checkout.go - linters: - - errcheck - - path: models/issue.go - linters: - - errcheck - - path: models/migrations/ - linters: - - errcheck - - path: modules/log/ - linters: - - errcheck - - path: routers/api/v1/repo/issue_subscription.go - linters: - - dupl - - path: routers/repo/view.go - linters: - - dupl - - path: models/migrations/ - linters: - - unused - - linters: - - staticcheck - text: "argument x is overwritten before first use" - - path: modules/httplib/httplib.go - linters: - - staticcheck - # Enabling this would require refactoring the methods and how they are called. - - path: models/issue_comment_list.go - linters: - - dupl - linters: - misspell text: '`Unknwon` is a misspelling of `Unknown`' - - path: models/update.go - linters: - - unused - - path: cmd/dump.go - linters: - - dupl - text: "commentFormatting: put a space between `//` and comment text" linters: - gocritic - text: "exitAfterDefer:" linters: - gocritic - - path: modules/graceful/manager_windows.go - linters: - - staticcheck - text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead." - - path: models/user/openid.go - linters: - - golint diff --git a/internal/pkg/cleanup/cleanup.go b/internal/pkg/cleanup/cleanup.go index 880f8d0..28673d6 100644 --- a/internal/pkg/cleanup/cleanup.go +++ b/internal/pkg/cleanup/cleanup.go @@ -1,6 +1,7 @@ // Copyright 2026 MarketAlly. All rights reserved. // SPDX-License-Identifier: MIT +// Package cleanup provides disk cleanup utilities for CI runners. package cleanup import ( @@ -15,18 +16,18 @@ import ( log "github.com/sirupsen/logrus" ) -// CleanupResult contains the results of a cleanup operation -type CleanupResult struct { +// Result contains the results of a cleanup operation. +type Result struct { BytesFreed int64 FilesDeleted int Errors []error Duration time.Duration } -// RunCleanup performs cleanup operations to free disk space -func RunCleanup(ctx context.Context, cfg *config.Config) (*CleanupResult, error) { +// RunCleanup performs cleanup operations to free disk space. +func RunCleanup(_ context.Context, cfg *config.Config) (*Result, error) { start := time.Now() - result := &CleanupResult{} + result := &Result{} log.Info("Starting runner cleanup...") @@ -207,8 +208,15 @@ func cleanTempDir(maxAge time.Duration) (int64, int, error) { return 0, 0, err } - // Only clean files/dirs that look like runner/act artifacts - runnerPatterns := []string{"act-", "runner-", "gitea-", "workflow-", "go-build", "go-link", "node-compile-cache", "npm-", "yarn-", "pnpm-"} + // Only clean files/dirs that look like runner/act artifacts or build tool temp files + runnerPatterns := []string{ + "act-", "runner-", "gitea-", "workflow-", + "go-build", "go-link", + "node-compile-cache", "npm-", "yarn-", "yarn--", "pnpm-", + "ts-node-", "tsx-", "jiti", "v8-compile-cache", + "text-diff-expansion-test", "DiagOutputDir", + "dugite-native-", "reorderCommitMessage-", "squashCommitMessage-", + } for _, entry := range entries { name := entry.Name() isRunner := false @@ -246,10 +254,10 @@ func cleanTempDir(maxAge time.Duration) (int64, int, error) { return bytesFreed, filesDeleted, nil } -// dirSize calculates the total size of a directory +// dirSize calculates the total size of a directory. func dirSize(path string) int64 { var size int64 - filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + _ = filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { if err != nil { return nil } @@ -308,6 +316,9 @@ func cleanBuildCaches(maxAge time.Duration) (int64, int, error) { {filepath.Join(os.Getenv("LOCALAPPDATA"), "Yarn", "Cache"), "Yarn cache (Windows)", 0}, {filepath.Join(os.Getenv("LOCALAPPDATA"), "NuGet", "v3-cache"), "NuGet cache (Windows)", 0}, {filepath.Join(os.Getenv("LOCALAPPDATA"), "pip", "Cache"), "pip cache (Windows)", 0}, + // Windows custom paths used by some CI setups + {"C:\\L\\Yarn", "Yarn global cache (Windows)", 0}, + {filepath.Join(os.TempDir(), "chocolatey"), "Chocolatey temp cache", 0}, } for _, cache := range cacheDirs { @@ -349,13 +360,13 @@ func cleanBuildCaches(maxAge time.Duration) (int64, int, error) { } // Also remove empty directories - filepath.Walk(cache.path, func(path string, info os.FileInfo, err error) error { + _ = filepath.Walk(cache.path, func(path string, info os.FileInfo, err error) error { if err != nil || !info.IsDir() || path == cache.path { return nil } entries, _ := os.ReadDir(path) if len(entries) == 0 { - os.Remove(path) + _ = os.Remove(path) } return nil }) From 63967eb6fa94846f777505d7dedeb50f2b8e9f2c Mon Sep 17 00:00:00 2001 From: logikonline Date: Mon, 19 Jan 2026 01:16:47 -0500 Subject: [PATCH 2/4] style(ui): add package docs and mark unused parameters Adds package-level documentation comments across cmd and internal packages. Marks unused function parameters with underscore prefix to satisfy linter requirements. Replaces if-else chains with switch statements for better readability. Explicitly ignores os.Setenv return value where error handling is not needed. --- cmd/upload-helper/main.go | 1 + internal/app/cmd/cache-server.go | 4 +- internal/app/cmd/cmd.go | 2 + internal/app/cmd/daemon.go | 28 ++-- internal/app/cmd/exec.go | 24 ++-- internal/app/cmd/register.go | 7 +- internal/app/poll/poller.go | 16 ++- internal/app/run/logging.go | 1 + internal/app/run/runner.go | 13 +- internal/pkg/artifact/upload_helper.go | 9 +- internal/pkg/client/client.go | 1 + internal/pkg/client/header.go | 1 + internal/pkg/client/http.go | 2 + internal/pkg/config/config.go | 1 + internal/pkg/config/embed.go | 2 + internal/pkg/config/registration.go | 6 +- internal/pkg/envcheck/bandwidth.go | 4 +- internal/pkg/envcheck/capabilities.go | 172 ++++++++++++------------- internal/pkg/envcheck/docker.go | 3 +- internal/pkg/labels/labels.go | 9 ++ internal/pkg/report/reporter.go | 14 +- internal/pkg/ver/version.go | 2 + main.go | 1 + 23 files changed, 183 insertions(+), 140 deletions(-) diff --git a/cmd/upload-helper/main.go b/cmd/upload-helper/main.go index fa6588e..937b026 100644 --- a/cmd/upload-helper/main.go +++ b/cmd/upload-helper/main.go @@ -1,6 +1,7 @@ // Copyright 2026 MarketAlly. All rights reserved. // SPDX-License-Identifier: MIT +// Package main provides the upload-helper CLI tool for reliable file uploads. package main import ( diff --git a/internal/app/cmd/cache-server.go b/internal/app/cmd/cache-server.go index 98aaaec..726ad9c 100644 --- a/internal/app/cmd/cache-server.go +++ b/internal/app/cmd/cache-server.go @@ -22,8 +22,8 @@ type cacheServerArgs struct { Port uint16 } -func runCacheServer(ctx context.Context, configFile *string, cacheArgs *cacheServerArgs) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { +func runCacheServer(_ context.Context, configFile *string, cacheArgs *cacheServerArgs) func(cmd *cobra.Command, args []string) error { + return func(_ *cobra.Command, _ []string) error { cfg, err := config.LoadDefault(*configFile) if err != nil { return fmt.Errorf("invalid configuration: %w", err) diff --git a/internal/app/cmd/cmd.go b/internal/app/cmd/cmd.go index 62cf431..b2aec1d 100644 --- a/internal/app/cmd/cmd.go +++ b/internal/app/cmd/cmd.go @@ -1,6 +1,7 @@ // Copyright 2022 The Gitea Authors and MarketAlly. All rights reserved. // SPDX-License-Identifier: MIT +// Package cmd provides the CLI commands for gitcaddy-runner. package cmd import ( @@ -15,6 +16,7 @@ import ( "git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/ver" ) +// Execute runs the root command for gitcaddy-runner CLI. func Execute(ctx context.Context) { // ./gitcaddy-runner rootCmd := &cobra.Command{ diff --git a/internal/app/cmd/daemon.go b/internal/app/cmd/daemon.go index a9a6189..0c31c42 100644 --- a/internal/app/cmd/daemon.go +++ b/internal/app/cmd/daemon.go @@ -58,7 +58,7 @@ var ( ) func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { + return func(_ *cobra.Command, _ []string) error { cfg, err := config.LoadDefault(*configFile) if err != nil { return fmt.Errorf("invalid configuration: %w", err) @@ -132,7 +132,7 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu return err } // if dockerSocketPath passes the check, override DOCKER_HOST with dockerSocketPath - os.Setenv("DOCKER_HOST", dockerSocketPath) + _ = os.Setenv("DOCKER_HOST", dockerSocketPath) // empty cfg.Container.DockerHost means act_runner need to find an available docker host automatically // and assign the path to cfg.Container.DockerHost if cfg.Container.DockerHost == "" { @@ -182,21 +182,22 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu capabilities := envcheck.DetectCapabilities(ctx, dockerHost, cfg.Container.WorkdirParent, globalConfig.Runner.Capacity) // Include initial bandwidth result if available capabilities.Bandwidth = bandwidthManager.GetLastResult() - capabilitiesJson := capabilities.ToJSON() - log.Infof("detected capabilities: %s", capabilitiesJson) + capabilitiesJSON := capabilities.ToJSON() + log.Infof("detected capabilities: %s", capabilitiesJSON) // Check disk space and warn if low checkDiskSpaceAndCleanup(ctx, capabilities) // declare the labels of the runner before fetching tasks - resp, err := runner.Declare(ctx, ls.Names(), capabilitiesJson) - if err != nil && connect.CodeOf(err) == connect.CodeUnimplemented { + resp, err := runner.Declare(ctx, ls.Names(), capabilitiesJSON) + switch { + case err != nil && connect.CodeOf(err) == connect.CodeUnimplemented: log.Errorf("Your GitCaddy version is too old to support runner declare, please upgrade to v1.21 or later") return err - } else if err != nil { + case err != nil: log.WithError(err).Error("fail to invoke Declare") return err - } else { + default: log.Infof("runner: %s, with version: %s, with labels: %v, declare successfully", resp.Msg.Runner.Name, resp.Msg.Runner.Version, resp.Msg.Runner.Labels) } @@ -261,14 +262,15 @@ func checkDiskSpaceAndCleanup(ctx context.Context, capabilities *envcheck.Runner usedPercent := capabilities.Disk.UsedPercent freeGB := float64(capabilities.Disk.Free) / (1024 * 1024 * 1024) - if usedPercent >= DiskSpaceCriticalThreshold { + switch { + case usedPercent >= DiskSpaceCriticalThreshold: log.Errorf("CRITICAL: Disk space critically low! %.1f%% used, only %.2f GB free. Runner may fail to execute jobs!", usedPercent, freeGB) // Always try cleanup at critical level triggerAutoCleanup(ctx) - } else if usedPercent >= DiskSpaceAutoCleanupThreshold { + case usedPercent >= DiskSpaceAutoCleanupThreshold: log.Warnf("WARNING: Disk space at %.1f%% used (%.2f GB free). Triggering automatic cleanup.", usedPercent, freeGB) triggerAutoCleanup(ctx) - } else if usedPercent >= DiskSpaceWarningThreshold { + case usedPercent >= DiskSpaceWarningThreshold: log.Warnf("WARNING: Disk space running low. %.1f%% used, %.2f GB free. Consider cleaning up disk space.", usedPercent, freeGB) } } @@ -330,13 +332,13 @@ func periodicCapabilitiesUpdate(ctx context.Context, runner *run.Runner, labelNa capabilities.Bandwidth = bandwidthManager.GetLastResult() } - capabilitiesJson := capabilities.ToJSON() + capabilitiesJSON := capabilities.ToJSON() // Check for disk space warnings checkDiskSpaceAndCleanup(ctx, capabilities) // Send updated capabilities to server - _, err := runner.Declare(ctx, labelNames, capabilitiesJson) + _, err := runner.Declare(ctx, labelNames, capabilitiesJSON) if err != nil { log.WithError(err).Debug("failed to update capabilities") } else { diff --git a/internal/app/cmd/exec.go b/internal/app/cmd/exec.go index 8ed010d..b655bdb 100644 --- a/internal/app/cmd/exec.go +++ b/internal/app/cmd/exec.go @@ -264,7 +264,7 @@ func printList(plan *model.Plan) error { return nil } -func runExecList(ctx context.Context, planner model.WorkflowPlanner, execArgs *executeArgs) error { +func runExecList(_ context.Context, planner model.WorkflowPlanner, execArgs *executeArgs) error { // plan with filtered jobs - to be used for filtering only var filterPlan *model.Plan @@ -286,19 +286,20 @@ func runExecList(ctx context.Context, planner model.WorkflowPlanner, execArgs *e } var err error - if execArgs.job != "" { + switch { + case execArgs.job != "": log.Infof("Preparing plan with a job: %s", execArgs.job) filterPlan, err = planner.PlanJob(execArgs.job) if err != nil { return err } - } else if filterEventName != "" { + case filterEventName != "": log.Infof("Preparing plan for a event: %s", filterEventName) filterPlan, err = planner.PlanEvent(filterEventName) if err != nil { return err } - } else { + default: log.Infof("Preparing plan with all jobs") filterPlan, err = planner.PlanAll() if err != nil { @@ -312,7 +313,7 @@ func runExecList(ctx context.Context, planner model.WorkflowPlanner, execArgs *e } func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { + return func(_ *cobra.Command, _ []string) error { planner, err := model.NewWorkflowPlanner(execArgs.WorkflowsPath(), execArgs.noWorkflowRecurse) if err != nil { return err @@ -331,18 +332,19 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command // collect all events from loaded workflows events := planner.GetEvents() - if len(execArgs.event) > 0 { + switch { + case len(execArgs.event) > 0: log.Infof("Using chosed event for filtering: %s", execArgs.event) eventName = execArgs.event - } else if len(events) == 1 && len(events[0]) > 0 { + case len(events) == 1 && len(events[0]) > 0: log.Infof("Using the only detected workflow event: %s", events[0]) eventName = events[0] - } else if execArgs.autodetectEvent && len(events) > 0 && len(events[0]) > 0 { + case execArgs.autodetectEvent && len(events) > 0 && len(events[0]) > 0: // set default event type to first event from many available // this way user dont have to specify the event. log.Infof("Using first detected workflow event: %s", events[0]) eventName = events[0] - } else { + default: log.Infof("Using default workflow event: push") eventName = "push" } @@ -388,7 +390,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command if err != nil { fmt.Println(err) } - defer os.RemoveAll(tempDir) + defer func() { _ = os.RemoveAll(tempDir) }() execArgs.artifactServerPath = tempDir } @@ -454,7 +456,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command log.Debugf("artifacts server started at %s:%s", execArgs.artifactServerPath, execArgs.artifactServerPort) ctx = common.WithDryrun(ctx, execArgs.dryrun) - executor := r.NewPlanExecutor(plan).Finally(func(ctx context.Context) error { + executor := r.NewPlanExecutor(plan).Finally(func(_ context.Context) error { artifactCancel() return nil }) diff --git a/internal/app/cmd/register.go b/internal/app/cmd/register.go index 5b05bca..9fefcf0 100644 --- a/internal/app/cmd/register.go +++ b/internal/app/cmd/register.go @@ -28,7 +28,7 @@ import ( // runRegister registers a runner to the server func runRegister(ctx context.Context, regArgs *registerArgs, configFile *string) func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { + return func(_ *cobra.Command, _ []string) error { log.SetReportCaller(false) isTerm := isatty.IsTerminal(os.Stdout.Fd()) log.SetFormatter(&log.TextFormatter{ @@ -80,6 +80,7 @@ type registerArgs struct { type registerStage int8 +// Register stage constants define the steps in the registration workflow. const ( StageUnknown registerStage = -1 StageOverwriteLocalConfig registerStage = iota + 1 @@ -250,7 +251,7 @@ func registerInteractive(ctx context.Context, configFile string, regArgs *regist if stage == StageWaitingForRegistration { log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.Labels) if err := doRegister(ctx, cfg, inputs); err != nil { - return fmt.Errorf("Failed to register runner: %w", err) + return fmt.Errorf("failed to register runner: %w", err) } log.Infof("Runner registered successfully.") return nil @@ -311,7 +312,7 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi return err } if err := doRegister(ctx, cfg, inputs); err != nil { - return fmt.Errorf("Failed to register runner: %w", err) + return fmt.Errorf("failed to register runner: %w", err) } log.Infof("Runner registered successfully.") return nil diff --git a/internal/app/poll/poller.go b/internal/app/poll/poller.go index 1d96b53..c08e004 100644 --- a/internal/app/poll/poller.go +++ b/internal/app/poll/poller.go @@ -1,6 +1,7 @@ // Copyright 2023 The Gitea Authors and MarketAlly. All rights reserved. // SPDX-License-Identifier: MIT +// Package poll provides task polling functionality for CI runners. package poll import ( @@ -22,6 +23,7 @@ import ( "git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/envcheck" ) +// Poller handles task polling from the Gitea server. type Poller struct { client client.Client runner *run.Runner @@ -38,6 +40,7 @@ type Poller struct { done chan struct{} } +// New creates a new Poller instance. func New(cfg *config.Config, client client.Client, runner *run.Runner) *Poller { pollingCtx, shutdownPolling := context.WithCancel(context.Background()) @@ -65,6 +68,7 @@ func (p *Poller) SetBandwidthManager(bm *envcheck.BandwidthManager) { p.bandwidthManager = bm } +// Poll starts polling for tasks with the configured capacity. func (p *Poller) Poll() { limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1) wg := &sync.WaitGroup{} @@ -78,6 +82,7 @@ func (p *Poller) Poll() { close(p.done) } +// PollOnce polls for a single task and then exits. func (p *Poller) PollOnce() { limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1) @@ -87,18 +92,19 @@ func (p *Poller) PollOnce() { close(p.done) } +// Shutdown gracefully stops the poller. func (p *Poller) Shutdown(ctx context.Context) error { p.shutdownPolling() select { - // graceful shutdown completed succesfully + // graceful shutdown completed successfully case <-p.done: return nil // our timeout for shutting down ran out case <-ctx.Done(): // when both the timeout fires and the graceful shutdown - // completed succsfully, this branch of the select may + // completed successfully, this branch of the select may // fire. Do a non-blocking check here against the graceful // shutdown status to avoid sending an error if we don't need to. _, ok := <-p.done @@ -110,7 +116,7 @@ func (p *Poller) Shutdown(ctx context.Context) error { p.shutdownJobs() // wait for running jobs to report their status to Gitea - _, _ = <-p.done + <-p.done return ctx.Err() } @@ -173,13 +179,13 @@ func (p *Poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) { caps.Bandwidth = p.bandwidthManager.GetLastResult() } - capsJson := caps.ToJSON() + capsJSON := caps.ToJSON() // Load the version value that was in the cache when the request was sent. v := p.tasksVersion.Load() fetchReq := &runnerv1.FetchTaskRequest{ TasksVersion: v, - CapabilitiesJson: capsJson, + CapabilitiesJson: capsJSON, } resp, err := p.client.FetchTask(reqCtx, connect.NewRequest(fetchReq)) if errors.Is(err, context.DeadlineExceeded) { diff --git a/internal/app/run/logging.go b/internal/app/run/logging.go index d0e6d1c..68071a1 100644 --- a/internal/app/run/logging.go +++ b/internal/app/run/logging.go @@ -1,6 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package run provides the core runner functionality for executing tasks. package run import ( diff --git a/internal/app/run/runner.go b/internal/app/run/runner.go index 77c74e2..34d4040 100644 --- a/internal/app/run/runner.go +++ b/internal/app/run/runner.go @@ -85,6 +85,7 @@ func (r *Runner) CleanStaleJobCaches(maxAge time.Duration) { } } +// NewRunner creates a new Runner with the given configuration, registration, and client. func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client) *Runner { ls := labels.Labels{} for _, v := range reg.Labels { @@ -133,6 +134,7 @@ func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client) } } +// Run executes a task from the server. func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error { if _, ok := r.runningTasks.Load(task.Id); ok { return fmt.Errorf("task %d is already running", task.Id) @@ -161,7 +163,7 @@ func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error { // getDefaultActionsURL // when DEFAULT_ACTIONS_URL == "https://github.com" and GithubMirror is not blank, // it should be set to GithubMirror first. -func (r *Runner) getDefaultActionsURL(ctx context.Context, task *runnerv1.Task) string { +func (r *Runner) getDefaultActionsURL(_ context.Context, task *runnerv1.Task) string { giteaDefaultActionsURL := task.Context.Fields["gitea_default_actions_url"].GetStringValue() if giteaDefaultActionsURL == "https://github.com" && r.cfg.Runner.GithubMirror != "" { return r.cfg.Runner.GithubMirror @@ -219,8 +221,8 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report. preset.Token = t } - if actionsIdTokenRequestUrl := taskContext["actions_id_token_request_url"].GetStringValue(); actionsIdTokenRequestUrl != "" { - r.envs["ACTIONS_ID_TOKEN_REQUEST_URL"] = actionsIdTokenRequestUrl + if actionsIDTokenRequestURL := taskContext["actions_id_token_request_url"].GetStringValue(); actionsIDTokenRequestURL != "" { + r.envs["ACTIONS_ID_TOKEN_REQUEST_URL"] = actionsIDTokenRequestURL r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = taskContext["actions_id_token_request_token"].GetStringValue() task.Secrets["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] } @@ -305,10 +307,11 @@ func (r *Runner) run(ctx context.Context, task *runnerv1.Task, reporter *report. return execErr } -func (r *Runner) Declare(ctx context.Context, labels []string, capabilitiesJson string) (*connect.Response[runnerv1.DeclareResponse], error) { +// Declare sends the runner's labels and capabilities to the server. +func (r *Runner) Declare(ctx context.Context, labels []string, capabilitiesJSON string) (*connect.Response[runnerv1.DeclareResponse], error) { return r.client.Declare(ctx, connect.NewRequest(&runnerv1.DeclareRequest{ Version: ver.Version(), Labels: labels, - CapabilitiesJson: capabilitiesJson, + CapabilitiesJson: capabilitiesJSON, })) } diff --git a/internal/pkg/artifact/upload_helper.go b/internal/pkg/artifact/upload_helper.go index a0dee9e..08b335f 100644 --- a/internal/pkg/artifact/upload_helper.go +++ b/internal/pkg/artifact/upload_helper.go @@ -1,6 +1,7 @@ // Copyright 2026 MarketAlly. All rights reserved. // SPDX-License-Identifier: MIT +// Package artifact provides utilities for handling artifact uploads. package artifact import ( @@ -88,7 +89,7 @@ func (u *UploadHelper) prewarmConnection(url string) error { if err != nil { return err } - resp.Body.Close() + _ = resp.Body.Close() return nil } @@ -98,7 +99,7 @@ func (u *UploadHelper) doUpload(client *http.Client, url, token, filepath string if err != nil { return fmt.Errorf("failed to open file: %w", err) } - defer file.Close() + defer func() { _ = file.Close() }() stat, err := file.Stat() if err != nil { @@ -118,7 +119,7 @@ func (u *UploadHelper) doUpload(client *http.Client, url, token, filepath string if _, err := io.Copy(part, file); err != nil { return fmt.Errorf("failed to copy file to form: %w", err) } - writer.Close() + _ = writer.Close() req, err := http.NewRequest("POST", url, body) if err != nil { @@ -133,7 +134,7 @@ func (u *UploadHelper) doUpload(client *http.Client, url, token, filepath string if err != nil { return fmt.Errorf("upload request failed: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode < 200 || resp.StatusCode >= 300 { respBody, _ := io.ReadAll(resp.Body) diff --git a/internal/pkg/client/client.go b/internal/pkg/client/client.go index 57f91ad..5744ccf 100644 --- a/internal/pkg/client/client.go +++ b/internal/pkg/client/client.go @@ -1,6 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package client provides the HTTP client for communicating with the runner API. package client import ( diff --git a/internal/pkg/client/header.go b/internal/pkg/client/header.go index 24844fa..5bf2fab 100644 --- a/internal/pkg/client/header.go +++ b/internal/pkg/client/header.go @@ -3,6 +3,7 @@ package client +// HTTP header constants for runner authentication and identification. const ( UUIDHeader = "x-runner-uuid" TokenHeader = "x-runner-token" diff --git a/internal/pkg/client/http.go b/internal/pkg/client/http.go index d365a77..65529cc 100644 --- a/internal/pkg/client/http.go +++ b/internal/pkg/client/http.go @@ -63,10 +63,12 @@ func New(endpoint string, insecure bool, uuid, token, version string, opts ...co } } +// Address returns the endpoint URL of the client. func (c *HTTPClient) Address() string { return c.endpoint } +// Insecure returns whether TLS verification is disabled. func (c *HTTPClient) Insecure() bool { return c.insecure } diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 93e8206..1bb6b8b 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -1,6 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package config provides configuration loading and management for the runner. package config import ( diff --git a/internal/pkg/config/embed.go b/internal/pkg/config/embed.go index cf445cf..6ea3641 100644 --- a/internal/pkg/config/embed.go +++ b/internal/pkg/config/embed.go @@ -5,5 +5,7 @@ package config import _ "embed" +// Example contains the example configuration file content. +// //go:embed config.example.yaml var Example []byte diff --git a/internal/pkg/config/registration.go b/internal/pkg/config/registration.go index 414f44f..1d06dab 100644 --- a/internal/pkg/config/registration.go +++ b/internal/pkg/config/registration.go @@ -23,12 +23,13 @@ type Registration struct { Ephemeral bool `json:"ephemeral"` } +// LoadRegistration loads the runner registration from a JSON file. func LoadRegistration(file string) (*Registration, error) { f, err := os.Open(file) if err != nil { return nil, err } - defer f.Close() + defer func() { _ = f.Close() }() var reg Registration if err := json.NewDecoder(f).Decode(®); err != nil { @@ -40,12 +41,13 @@ func LoadRegistration(file string) (*Registration, error) { return ®, nil } +// SaveRegistration saves the runner registration to a JSON file. func SaveRegistration(file string, reg *Registration) error { f, err := os.Create(file) if err != nil { return err } - defer f.Close() + defer func() { _ = f.Close() }() reg.Warning = registrationWarning diff --git a/internal/pkg/envcheck/bandwidth.go b/internal/pkg/envcheck/bandwidth.go index 765d4b0..3c0bf22 100644 --- a/internal/pkg/envcheck/bandwidth.go +++ b/internal/pkg/envcheck/bandwidth.go @@ -121,7 +121,7 @@ func testLatency(ctx context.Context, serverURL string) float64 { if err != nil { return 0 } - resp.Body.Close() + _ = resp.Body.Close() latency := time.Since(start).Seconds() * 1000 // Convert to ms return float64(int(latency*100)) / 100 // Round to 2 decimals @@ -169,7 +169,7 @@ func testDownloadSpeed(ctx context.Context, serverURL string) float64 { } n, _ := io.Copy(io.Discard, resp.Body) - resp.Body.Close() + _ = resp.Body.Close() cancel() duration := time.Since(start) diff --git a/internal/pkg/envcheck/capabilities.go b/internal/pkg/envcheck/capabilities.go index 8812f99..cfc63d0 100644 --- a/internal/pkg/envcheck/capabilities.go +++ b/internal/pkg/envcheck/capabilities.go @@ -83,7 +83,7 @@ type CapabilityFeatures struct { // DetectCapabilities detects the runner's capabilities // workingDir is the directory where builds will run (for disk space detection) func DetectCapabilities(ctx context.Context, dockerHost string, workingDir string, capacity int) *RunnerCapabilities { - cap := &RunnerCapabilities{ + caps := &RunnerCapabilities{ Capacity: capacity, OS: runtime.GOOS, Arch: runtime.GOARCH, @@ -105,40 +105,40 @@ func DetectCapabilities(ctx context.Context, dockerHost string, workingDir strin // Detect Linux distribution if runtime.GOOS == "linux" { - cap.Distro = detectLinuxDistro() + caps.Distro = detectLinuxDistro() } // Detect macOS Xcode/iOS if runtime.GOOS == "darwin" { - cap.Xcode = detectXcode(ctx) + caps.Xcode = detectXcode(ctx) } // Detect Docker - cap.Docker, cap.ContainerRuntime = detectDocker(ctx, dockerHost) - if cap.Docker { - cap.DockerCompose = detectDockerCompose(ctx) - cap.Features.Services = true + caps.Docker, caps.ContainerRuntime = detectDocker(ctx, dockerHost) + if caps.Docker { + caps.DockerCompose = detectDockerCompose(ctx) + caps.Features.Services = true } // Detect common tools - detectTools(ctx, cap) + detectTools(ctx, caps) // Detect build tools - detectBuildTools(ctx, cap) + detectBuildTools(ctx, caps) // Detect package managers - detectPackageManagers(ctx, cap) + detectPackageManagers(ctx, caps) // Detect disk space on the working directory's filesystem - cap.Disk = detectDiskSpace(workingDir) + caps.Disk = detectDiskSpace(workingDir) // Detect CPU load - cap.CPU = detectCPULoad() + caps.CPU = detectCPULoad() // Generate suggested labels based on detected capabilities - cap.SuggestedLabels = generateSuggestedLabels(cap) + caps.SuggestedLabels = generateSuggestedLabels(caps) - return cap + return caps } // detectXcode detects Xcode and iOS development capabilities on macOS @@ -227,18 +227,19 @@ func detectLinuxDistro() *DistroInfo { if err != nil { return nil } - defer file.Close() + defer func() { _ = file.Close() }() distro := &DistroInfo{} scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, "ID=") { + switch { + case strings.HasPrefix(line, "ID="): distro.ID = strings.Trim(strings.TrimPrefix(line, "ID="), "\"") - } else if strings.HasPrefix(line, "VERSION_ID=") { + case strings.HasPrefix(line, "VERSION_ID="): distro.VersionID = strings.Trim(strings.TrimPrefix(line, "VERSION_ID="), "\"") - } else if strings.HasPrefix(line, "PRETTY_NAME=") { + case strings.HasPrefix(line, "PRETTY_NAME="): distro.PrettyName = strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"") } } @@ -251,7 +252,7 @@ func detectLinuxDistro() *DistroInfo { } // generateSuggestedLabels creates industry-standard labels based on capabilities -func generateSuggestedLabels(cap *RunnerCapabilities) []string { +func generateSuggestedLabels(caps *RunnerCapabilities) []string { labels := []string{} seen := make(map[string]bool) @@ -263,7 +264,7 @@ func generateSuggestedLabels(cap *RunnerCapabilities) []string { } // OS labels - switch cap.OS { + switch caps.OS { case "linux": addLabel("linux") addLabel("linux-latest") @@ -276,17 +277,17 @@ func generateSuggestedLabels(cap *RunnerCapabilities) []string { } // Distro labels (Linux only) - if cap.Distro != nil && cap.Distro.ID != "" { - distro := strings.ToLower(cap.Distro.ID) + if caps.Distro != nil && caps.Distro.ID != "" { + distro := strings.ToLower(caps.Distro.ID) addLabel(distro) addLabel(distro + "-latest") } // Xcode/iOS labels (macOS only) - if cap.Xcode != nil { + if caps.Xcode != nil { addLabel("xcode") // Check for SDKs - for _, sdk := range cap.Xcode.SDKs { + for _, sdk := range caps.Xcode.SDKs { sdkLower := strings.ToLower(sdk) if strings.Contains(sdkLower, "ios") { addLabel("ios") @@ -302,24 +303,24 @@ func generateSuggestedLabels(cap *RunnerCapabilities) []string { } } // If simulators available, add simulator label - if len(cap.Xcode.Simulators) > 0 { + if len(caps.Xcode.Simulators) > 0 { addLabel("ios-simulator") } } // Tool-based labels - if _, ok := cap.Tools["dotnet"]; ok { + if _, ok := caps.Tools["dotnet"]; ok { addLabel("dotnet") } - if _, ok := cap.Tools["java"]; ok { + if _, ok := caps.Tools["java"]; ok { addLabel("java") } - if _, ok := cap.Tools["node"]; ok { + if _, ok := caps.Tools["node"]; ok { addLabel("node") } // Build tool labels - for _, tool := range cap.BuildTools { + for _, tool := range caps.BuildTools { switch tool { case "msbuild": addLabel("msbuild") @@ -384,7 +385,7 @@ func detectDocker(ctx context.Context, dockerHost string) (bool, string) { if err != nil { return false, "" } - defer cli.Close() + defer func() { _ = cli.Close() }() timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() @@ -420,7 +421,7 @@ func detectDockerCompose(ctx context.Context) bool { return false } -func detectTools(ctx context.Context, cap *RunnerCapabilities) { +func detectTools(ctx context.Context, caps *RunnerCapabilities) { toolDetectors := map[string]func(context.Context) []string{ "node": detectNodeVersions, "go": detectGoVersions, @@ -439,7 +440,7 @@ func detectTools(ctx context.Context, cap *RunnerCapabilities) { for tool, detector := range toolDetectors { if versions := detector(ctx); len(versions) > 0 { - cap.Tools[tool] = versions + caps.Tools[tool] = versions } } @@ -460,23 +461,23 @@ func detectTools(ctx context.Context, cap *RunnerCapabilities) { for name, cmd := range simpleTools { if v := detectSimpleToolVersion(ctx, cmd); v != "" { - cap.Tools[name] = []string{v} + caps.Tools[name] = []string{v} } } } -func detectBuildTools(ctx context.Context, cap *RunnerCapabilities) { +func detectBuildTools(ctx context.Context, caps *RunnerCapabilities) { switch runtime.GOOS { case "windows": - detectWindowsBuildTools(ctx, cap) + detectWindowsBuildTools(ctx, caps) case "darwin": - detectMacOSBuildTools(ctx, cap) + detectMacOSBuildTools(caps) case "linux": - detectLinuxBuildTools(ctx, cap) + detectLinuxBuildTools(caps) } } -func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { +func detectWindowsBuildTools(ctx context.Context, caps *RunnerCapabilities) { // Check for Visual Studio via vswhere vswherePaths := []string{ `C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe`, @@ -486,7 +487,7 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { if _, err := os.Stat(vswhere); err == nil { cmd := exec.CommandContext(ctx, vswhere, "-latest", "-property", "displayName") if output, err := cmd.Output(); err == nil && len(output) > 0 { - cap.BuildTools = append(cap.BuildTools, "visual-studio") + caps.BuildTools = append(caps.BuildTools, "visual-studio") break } } @@ -503,7 +504,7 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { } for _, msbuild := range msbuildPaths { if _, err := os.Stat(msbuild); err == nil { - cap.BuildTools = append(cap.BuildTools, "msbuild") + caps.BuildTools = append(caps.BuildTools, "msbuild") break } } @@ -517,14 +518,14 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { } for _, iscc := range innoSetupPaths { if _, err := os.Stat(iscc); err == nil { - cap.BuildTools = append(cap.BuildTools, "inno-setup") + caps.BuildTools = append(caps.BuildTools, "inno-setup") break } } // Also check PATH if _, err := exec.LookPath("iscc"); err == nil { - if !contains(cap.BuildTools, "inno-setup") { - cap.BuildTools = append(cap.BuildTools, "inno-setup") + if !contains(caps.BuildTools, "inno-setup") { + caps.BuildTools = append(caps.BuildTools, "inno-setup") } } @@ -535,13 +536,13 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { } for _, nsis := range nsisPaths { if _, err := os.Stat(nsis); err == nil { - cap.BuildTools = append(cap.BuildTools, "nsis") + caps.BuildTools = append(caps.BuildTools, "nsis") break } } if _, err := exec.LookPath("makensis"); err == nil { - if !contains(cap.BuildTools, "nsis") { - cap.BuildTools = append(cap.BuildTools, "nsis") + if !contains(caps.BuildTools, "nsis") { + caps.BuildTools = append(caps.BuildTools, "nsis") } } @@ -552,7 +553,7 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { } for _, wix := range wixPaths { if _, err := os.Stat(wix); err == nil { - cap.BuildTools = append(cap.BuildTools, "wix") + caps.BuildTools = append(caps.BuildTools, "wix") break } } @@ -560,63 +561,63 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) { // Check for signtool (Windows SDK) signtoolPaths, _ := filepath.Glob(`C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe`) if len(signtoolPaths) > 0 { - cap.BuildTools = append(cap.BuildTools, "signtool") + caps.BuildTools = append(caps.BuildTools, "signtool") } } -func detectMacOSBuildTools(ctx context.Context, cap *RunnerCapabilities) { +func detectMacOSBuildTools(caps *RunnerCapabilities) { // Check for xcpretty if _, err := exec.LookPath("xcpretty"); err == nil { - cap.BuildTools = append(cap.BuildTools, "xcpretty") + caps.BuildTools = append(caps.BuildTools, "xcpretty") } // Check for fastlane if _, err := exec.LookPath("fastlane"); err == nil { - cap.BuildTools = append(cap.BuildTools, "fastlane") + caps.BuildTools = append(caps.BuildTools, "fastlane") } // Check for CocoaPods if _, err := exec.LookPath("pod"); err == nil { - cap.BuildTools = append(cap.BuildTools, "cocoapods") + caps.BuildTools = append(caps.BuildTools, "cocoapods") } // Check for Carthage if _, err := exec.LookPath("carthage"); err == nil { - cap.BuildTools = append(cap.BuildTools, "carthage") + caps.BuildTools = append(caps.BuildTools, "carthage") } // Check for SwiftLint if _, err := exec.LookPath("swiftlint"); err == nil { - cap.BuildTools = append(cap.BuildTools, "swiftlint") + caps.BuildTools = append(caps.BuildTools, "swiftlint") } // Check for create-dmg or similar if _, err := exec.LookPath("create-dmg"); err == nil { - cap.BuildTools = append(cap.BuildTools, "create-dmg") + caps.BuildTools = append(caps.BuildTools, "create-dmg") } // Check for Packages (packagesbuild) if _, err := exec.LookPath("packagesbuild"); err == nil { - cap.BuildTools = append(cap.BuildTools, "packages") + caps.BuildTools = append(caps.BuildTools, "packages") } // Check for pkgbuild (built-in) if _, err := exec.LookPath("pkgbuild"); err == nil { - cap.BuildTools = append(cap.BuildTools, "pkgbuild") + caps.BuildTools = append(caps.BuildTools, "pkgbuild") } // Check for codesign (built-in) if _, err := exec.LookPath("codesign"); err == nil { - cap.BuildTools = append(cap.BuildTools, "codesign") + caps.BuildTools = append(caps.BuildTools, "codesign") } // Check for notarytool (built-in with Xcode) if _, err := exec.LookPath("notarytool"); err == nil { - cap.BuildTools = append(cap.BuildTools, "notarytool") + caps.BuildTools = append(caps.BuildTools, "notarytool") } } -func detectLinuxBuildTools(ctx context.Context, cap *RunnerCapabilities) { +func detectLinuxBuildTools(caps *RunnerCapabilities) { // Check for common Linux build tools tools := []string{ "gcc", "g++", "clang", "clang++", @@ -628,54 +629,54 @@ func detectLinuxBuildTools(ctx context.Context, cap *RunnerCapabilities) { for _, tool := range tools { if _, err := exec.LookPath(tool); err == nil { - cap.BuildTools = append(cap.BuildTools, tool) + caps.BuildTools = append(caps.BuildTools, tool) } } } -func detectPackageManagers(ctx context.Context, cap *RunnerCapabilities) { +func detectPackageManagers(_ context.Context, caps *RunnerCapabilities) { switch runtime.GOOS { case "windows": if _, err := exec.LookPath("choco"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "chocolatey") + caps.PackageManagers = append(caps.PackageManagers, "chocolatey") } if _, err := exec.LookPath("scoop"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "scoop") + caps.PackageManagers = append(caps.PackageManagers, "scoop") } if _, err := exec.LookPath("winget"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "winget") + caps.PackageManagers = append(caps.PackageManagers, "winget") } case "darwin": if _, err := exec.LookPath("brew"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "homebrew") + caps.PackageManagers = append(caps.PackageManagers, "homebrew") } if _, err := exec.LookPath("port"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "macports") + caps.PackageManagers = append(caps.PackageManagers, "macports") } case "linux": if _, err := exec.LookPath("apt"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "apt") + caps.PackageManagers = append(caps.PackageManagers, "apt") } if _, err := exec.LookPath("yum"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "yum") + caps.PackageManagers = append(caps.PackageManagers, "yum") } if _, err := exec.LookPath("dnf"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "dnf") + caps.PackageManagers = append(caps.PackageManagers, "dnf") } if _, err := exec.LookPath("pacman"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "pacman") + caps.PackageManagers = append(caps.PackageManagers, "pacman") } if _, err := exec.LookPath("zypper"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "zypper") + caps.PackageManagers = append(caps.PackageManagers, "zypper") } if _, err := exec.LookPath("apk"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "apk") + caps.PackageManagers = append(caps.PackageManagers, "apk") } if _, err := exec.LookPath("snap"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "snap") + caps.PackageManagers = append(caps.PackageManagers, "snap") } if _, err := exec.LookPath("flatpak"); err == nil { - cap.PackageManagers = append(cap.PackageManagers, "flatpak") + caps.PackageManagers = append(caps.PackageManagers, "flatpak") } } } @@ -813,13 +814,8 @@ func detectPwshVersion(ctx context.Context, cmd string) string { timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() - // Use -Command to get version - var c *exec.Cmd - if cmd == "pwsh" { - c = exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()") - } else { - c = exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()") - } + // Use -Command to get version (same command works for both pwsh and powershell) + c := exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()") output, err := c.Output() if err != nil { @@ -1042,15 +1038,9 @@ func getContainerCPUUsage() float64 { } } - // Try reading /proc/stat for this process's CPU usage - if data, err := os.ReadFile("/proc/self/stat"); err == nil { - fields := strings.Fields(string(data)) - if len(fields) >= 15 { - // Fields 14 and 15 are utime and stime (in clock ticks) - // This is cumulative, not instantaneous - // For containers, we'll report 0 rather than misleading host data - } - } + // Note: Reading /proc/self/stat could give us utime and stime (fields 14 and 15), + // but these are cumulative values, not instantaneous. For containers, we report 0 + // rather than misleading host data. return -1 // Unable to determine - caller should handle } diff --git a/internal/pkg/envcheck/docker.go b/internal/pkg/envcheck/docker.go index 65aae4d..1e1e80e 100644 --- a/internal/pkg/envcheck/docker.go +++ b/internal/pkg/envcheck/docker.go @@ -10,6 +10,7 @@ import ( "github.com/docker/docker/client" ) +// CheckIfDockerRunning verifies that the Docker daemon is running and accessible. func CheckIfDockerRunning(ctx context.Context, configDockerHost string) error { opts := []client.Opt{ client.FromEnv, @@ -23,7 +24,7 @@ func CheckIfDockerRunning(ctx context.Context, configDockerHost string) error { if err != nil { return err } - defer cli.Close() + defer func() { _ = cli.Close() }() _, err = cli.Ping(ctx) if err != nil { diff --git a/internal/pkg/labels/labels.go b/internal/pkg/labels/labels.go index 542b52d..974bad0 100644 --- a/internal/pkg/labels/labels.go +++ b/internal/pkg/labels/labels.go @@ -1,6 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package labels provides utilities for parsing and managing runner labels. package labels import ( @@ -8,17 +9,20 @@ import ( "strings" ) +// Label scheme constants define the execution environments. const ( SchemeHost = "host" SchemeDocker = "docker" ) +// Label represents a parsed runner label with name, schema, and optional argument. type Label struct { Name string Schema string Arg string } +// Parse parses a label string in the format "name:schema:arg" and returns a Label. func Parse(str string) (*Label, error) { splits := strings.SplitN(str, ":", 3) label := &Label{ @@ -38,8 +42,10 @@ func Parse(str string) (*Label, error) { return label, nil } +// Labels is a slice of Label pointers. type Labels []*Label +// RequireDocker returns true if any label uses the docker schema. func (l Labels) RequireDocker() bool { for _, label := range l { if label.Schema == SchemeDocker { @@ -49,6 +55,7 @@ func (l Labels) RequireDocker() bool { return false } +// PickPlatform selects the appropriate platform based on the runsOn requirements. func (l Labels) PickPlatform(runsOn []string) string { platforms := make(map[string]string, len(l)) for _, label := range l { @@ -82,6 +89,7 @@ func (l Labels) PickPlatform(runsOn []string) string { return "docker.gitea.com/runner-images:ubuntu-latest" } +// Names returns the names of all labels. func (l Labels) Names() []string { names := make([]string, 0, len(l)) for _, label := range l { @@ -90,6 +98,7 @@ func (l Labels) Names() []string { return names } +// ToStrings converts labels back to their string representation. func (l Labels) ToStrings() []string { ls := make([]string, 0, len(l)) for _, label := range l { diff --git a/internal/pkg/report/reporter.go b/internal/pkg/report/reporter.go index b88773a..50c9044 100644 --- a/internal/pkg/report/reporter.go +++ b/internal/pkg/report/reporter.go @@ -1,6 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package report provides task reporting functionality for communicating with the server. package report import ( @@ -21,6 +22,7 @@ import ( "git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/client" ) +// Reporter handles logging and state reporting for running tasks. type Reporter struct { ctx context.Context cancel context.CancelFunc @@ -42,6 +44,7 @@ type Reporter struct { stopCommandEndToken string } +// NewReporter creates a new Reporter for the given task. func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.Client, task *runnerv1.Task) *Reporter { var oldnew []string if v := task.Context.Fields["token"].GetStringValue(); v != "" { @@ -72,6 +75,7 @@ func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.C return rv } +// ResetSteps initializes the step states with the given number of steps. func (r *Reporter) ResetSteps(l int) { r.stateMu.Lock() defer r.stateMu.Unlock() @@ -82,6 +86,7 @@ func (r *Reporter) ResetSteps(l int) { } } +// Levels returns all log levels that this hook should fire for. func (r *Reporter) Levels() []log.Level { return log.AllLevels } @@ -93,6 +98,7 @@ func appendIfNotNil[T any](s []*T, v *T) []*T { return s } +// Fire processes a log entry and updates the task state accordingly. func (r *Reporter) Fire(entry *log.Entry) error { r.stateMu.Lock() defer r.stateMu.Unlock() @@ -175,6 +181,7 @@ func (r *Reporter) Fire(entry *log.Entry) error { return nil } +// RunDaemon starts the periodic reporting of logs and state. func (r *Reporter) RunDaemon() { if r.closed { return @@ -189,6 +196,7 @@ func (r *Reporter) RunDaemon() { time.AfterFunc(time.Second, r.RunDaemon) } +// Logf adds a formatted log message to the report. func (r *Reporter) Logf(format string, a ...interface{}) { r.stateMu.Lock() defer r.stateMu.Unlock() @@ -205,6 +213,7 @@ func (r *Reporter) logf(format string, a ...interface{}) { } } +// SetOutputs stores the job outputs to be reported to the server. func (r *Reporter) SetOutputs(outputs map[string]string) { r.stateMu.Lock() defer r.stateMu.Unlock() @@ -225,6 +234,7 @@ func (r *Reporter) SetOutputs(outputs map[string]string) { } } +// Close finalizes the report and sends any remaining logs and state. func (r *Reporter) Close(lastWords string) error { r.closed = true @@ -260,6 +270,7 @@ func (r *Reporter) Close(lastWords string) error { }, retry.Context(r.ctx)) } +// ReportLog sends accumulated log rows to the server. func (r *Reporter) ReportLog(noMore bool) error { r.clientM.Lock() defer r.clientM.Unlock() @@ -295,6 +306,7 @@ func (r *Reporter) ReportLog(noMore bool) error { return nil } +// ReportState sends the current task state to the server. func (r *Reporter) ReportState() error { r.clientM.Lock() defer r.clientM.Unlock() @@ -373,7 +385,7 @@ func (r *Reporter) parseResult(result interface{}) (runnerv1.Result, bool) { var cmdRegex = regexp.MustCompile(`^::([^ :]+)( .*)?::(.*)$`) -func (r *Reporter) handleCommand(originalContent, command, parameters, value string) *string { +func (r *Reporter) handleCommand(originalContent, command, _ /* parameters */, value string) *string { if r.stopCommandEndToken != "" && command != r.stopCommandEndToken { return &originalContent } diff --git a/internal/pkg/ver/version.go b/internal/pkg/ver/version.go index 58d195c..31ebcac 100644 --- a/internal/pkg/ver/version.go +++ b/internal/pkg/ver/version.go @@ -1,11 +1,13 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// Package ver provides version information for the runner. package ver // go build -ldflags "-X git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/ver.version=1.2.3" var version = "dev" +// Version returns the current runner version. func Version() string { return version } diff --git a/main.go b/main.go index 0ec436b..6f37f8e 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT +// GitCaddy Runner is a CI/CD runner for Gitea Actions. package main import ( From cb1c1a3264422c5aa6eb08ec63519e459174ca7a Mon Sep 17 00:00:00 2001 From: "David H. Friedel Jr" Date: Fri, 23 Jan 2026 00:53:55 +0000 Subject: [PATCH 3/4] Add LICENSE.md (MIT) --- LICENSE.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..9cad21f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2026 gitcaddy + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. From d388ec551944d222f38570395da191666d1ad6bd Mon Sep 17 00:00:00 2001 From: logikonline Date: Sat, 24 Jan 2026 14:42:10 -0500 Subject: [PATCH 4/4] chore(scanner): add gitsecrets ignore file Initializes .gitsecrets-ignore file to track false positives from secret scanning. Includes documentation header explaining the file format and usage. --- .gitsecrets-ignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitsecrets-ignore diff --git a/.gitsecrets-ignore b/.gitsecrets-ignore new file mode 100644 index 0000000..b85d4e7 --- /dev/null +++ b/.gitsecrets-ignore @@ -0,0 +1,14 @@ +# GitSecrets Ignore File +# This file tracks false positives identified by AI evaluation or manually marked. +# Each line is a JSON object with the following fields: +# - contentHash: SHA256 hash prefix of the secret content +# - patternId: The pattern that detected this secret +# - filePath: Relative path where the secret was found +# - reason: Why this was marked as a false positive +# - confidence: AI confidence level (if from AI evaluation) +# - addedAt: Timestamp when this entry was added +# +# You can safely commit this file to share false positive markers with your team. +# To remove an entry, simply delete the corresponding line. + +{"contentHash":"5af30500c6463ec4","patternId":"password-assignment","filePath":"..\\gitcaddy\\internal\\app\\cmd\\register.go","reason":"Manually marked as false positive","addedAt":1769249840525}