Merge branch 'main' of https://git.marketally.com/gitcaddy/gitcaddy-runner
This commit is contained in:
14
.gitsecrets-ignore
Normal file
14
.gitsecrets-ignore
Normal file
@@ -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}
|
||||||
100
.golangci.yml
100
.golangci.yml
@@ -1,53 +1,42 @@
|
|||||||
|
version: "2"
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
|
default: none
|
||||||
enable:
|
enable:
|
||||||
- gosimple
|
|
||||||
- typecheck
|
|
||||||
- govet
|
- govet
|
||||||
- errcheck
|
- errcheck
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- unused
|
- unused
|
||||||
- dupl
|
- dupl
|
||||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
|
||||||
- gofmt
|
|
||||||
- misspell
|
- misspell
|
||||||
- gocritic
|
- gocritic
|
||||||
- bidichk
|
- bidichk
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- revive
|
- revive
|
||||||
- gofumpt
|
|
||||||
- depguard
|
|
||||||
- nakedret
|
- nakedret
|
||||||
- unconvert
|
- unconvert
|
||||||
- wastedassign
|
- wastedassign
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- stylecheck
|
|
||||||
enable-all: false
|
formatters:
|
||||||
disable-all: true
|
enable:
|
||||||
fast: false
|
- gofmt
|
||||||
|
- gofumpt
|
||||||
|
|
||||||
run:
|
run:
|
||||||
go: 1.18
|
go: "1.23"
|
||||||
timeout: 10m
|
timeout: 10m
|
||||||
skip-dirs:
|
|
||||||
- node_modules
|
|
||||||
- public
|
|
||||||
- web_src
|
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
stylecheck:
|
|
||||||
checks: ["all", "-ST1005", "-ST1003"]
|
|
||||||
nakedret:
|
nakedret:
|
||||||
max-func-lines: 0
|
max-func-lines: 0
|
||||||
gocritic:
|
gocritic:
|
||||||
disabled-checks:
|
disabled-checks:
|
||||||
- ifElseChain
|
- ifElseChain
|
||||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
- singleCaseSwitch
|
||||||
revive:
|
revive:
|
||||||
ignore-generated-header: false
|
|
||||||
severity: warning
|
severity: warning
|
||||||
confidence: 0.8
|
confidence: 0.8
|
||||||
errorCode: 1
|
|
||||||
warningCode: 1
|
|
||||||
rules:
|
rules:
|
||||||
- name: blank-imports
|
- name: blank-imports
|
||||||
- name: context-as-argument
|
- name: context-as-argument
|
||||||
@@ -72,94 +61,25 @@ linters-settings:
|
|||||||
- name: modifies-value-receiver
|
- name: modifies-value-receiver
|
||||||
gofumpt:
|
gofumpt:
|
||||||
extra-rules: true
|
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:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
# Exclude some linters from running on tests files.
|
|
||||||
- path: _test\.go
|
- path: _test\.go
|
||||||
linters:
|
linters:
|
||||||
- gocyclo
|
|
||||||
- errcheck
|
- errcheck
|
||||||
- dupl
|
- dupl
|
||||||
- gosec
|
|
||||||
- unparam
|
|
||||||
- staticcheck
|
|
||||||
- path: models/migrations/v
|
|
||||||
linters:
|
|
||||||
- gocyclo
|
|
||||||
- errcheck
|
|
||||||
- dupl
|
|
||||||
- gosec
|
|
||||||
- linters:
|
- linters:
|
||||||
- dupl
|
- dupl
|
||||||
text: "webhook"
|
text: "webhook"
|
||||||
- linters:
|
- linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
text: "`ID' should not be capitalized"
|
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:
|
- linters:
|
||||||
- misspell
|
- misspell
|
||||||
text: '`Unknwon` is a misspelling of `Unknown`'
|
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"
|
- text: "commentFormatting: put a space between `//` and comment text"
|
||||||
linters:
|
linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
- text: "exitAfterDefer:"
|
- text: "exitAfterDefer:"
|
||||||
linters:
|
linters:
|
||||||
- gocritic
|
- gocritic
|
||||||
- path: modules/graceful/manager_windows.go
|
|
||||||
linters:
|
|
||||||
- staticcheck
|
|
||||||
text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead."
|
|
||||||
- path: models/user/openid.go
|
|
||||||
linters:
|
|
||||||
- golint
|
|
||||||
|
|||||||
18
LICENSE.md
Normal file
18
LICENSE.md
Normal file
@@ -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.
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2026 MarketAlly. All rights reserved.
|
// Copyright 2026 MarketAlly. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package main provides the upload-helper CLI tool for reliable file uploads.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ type cacheServerArgs struct {
|
|||||||
Port uint16
|
Port uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCacheServer(ctx context.Context, configFile *string, cacheArgs *cacheServerArgs) func(cmd *cobra.Command, args []string) error {
|
func runCacheServer(_ context.Context, configFile *string, cacheArgs *cacheServerArgs) 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)
|
cfg, err := config.LoadDefault(*configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid configuration: %w", err)
|
return fmt.Errorf("invalid configuration: %w", err)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2022 The Gitea Authors and MarketAlly. All rights reserved.
|
// Copyright 2022 The Gitea Authors and MarketAlly. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package cmd provides the CLI commands for gitcaddy-runner.
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/ver"
|
"git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/ver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Execute runs the root command for gitcaddy-runner CLI.
|
||||||
func Execute(ctx context.Context) {
|
func Execute(ctx context.Context) {
|
||||||
// ./gitcaddy-runner
|
// ./gitcaddy-runner
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) func(cmd *cobra.Command, args []string) error {
|
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)
|
cfg, err := config.LoadDefault(*configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid configuration: %w", err)
|
return fmt.Errorf("invalid configuration: %w", err)
|
||||||
@@ -132,7 +132,7 @@ func runDaemon(ctx context.Context, daemArgs *daemonArgs, configFile *string) fu
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// if dockerSocketPath passes the check, override DOCKER_HOST with dockerSocketPath
|
// 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
|
// empty cfg.Container.DockerHost means act_runner need to find an available docker host automatically
|
||||||
// and assign the path to cfg.Container.DockerHost
|
// and assign the path to cfg.Container.DockerHost
|
||||||
if 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)
|
capabilities := envcheck.DetectCapabilities(ctx, dockerHost, cfg.Container.WorkdirParent, globalConfig.Runner.Capacity)
|
||||||
// Include initial bandwidth result if available
|
// Include initial bandwidth result if available
|
||||||
capabilities.Bandwidth = bandwidthManager.GetLastResult()
|
capabilities.Bandwidth = bandwidthManager.GetLastResult()
|
||||||
capabilitiesJson := capabilities.ToJSON()
|
capabilitiesJSON := capabilities.ToJSON()
|
||||||
log.Infof("detected capabilities: %s", capabilitiesJson)
|
log.Infof("detected capabilities: %s", capabilitiesJSON)
|
||||||
|
|
||||||
// Check disk space and warn if low
|
// Check disk space and warn if low
|
||||||
checkDiskSpaceAndCleanup(ctx, capabilities)
|
checkDiskSpaceAndCleanup(ctx, capabilities)
|
||||||
|
|
||||||
// declare the labels of the runner before fetching tasks
|
// declare the labels of the runner before fetching tasks
|
||||||
resp, err := runner.Declare(ctx, ls.Names(), capabilitiesJson)
|
resp, err := runner.Declare(ctx, ls.Names(), capabilitiesJSON)
|
||||||
if err != nil && connect.CodeOf(err) == connect.CodeUnimplemented {
|
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")
|
log.Errorf("Your GitCaddy version is too old to support runner declare, please upgrade to v1.21 or later")
|
||||||
return err
|
return err
|
||||||
} else if err != nil {
|
case err != nil:
|
||||||
log.WithError(err).Error("fail to invoke Declare")
|
log.WithError(err).Error("fail to invoke Declare")
|
||||||
return err
|
return err
|
||||||
} else {
|
default:
|
||||||
log.Infof("runner: %s, with version: %s, with labels: %v, declare successfully",
|
log.Infof("runner: %s, with version: %s, with labels: %v, declare successfully",
|
||||||
resp.Msg.Runner.Name, resp.Msg.Runner.Version, resp.Msg.Runner.Labels)
|
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
|
usedPercent := capabilities.Disk.UsedPercent
|
||||||
freeGB := float64(capabilities.Disk.Free) / (1024 * 1024 * 1024)
|
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)
|
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
|
// Always try cleanup at critical level
|
||||||
triggerAutoCleanup(ctx)
|
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)
|
log.Warnf("WARNING: Disk space at %.1f%% used (%.2f GB free). Triggering automatic cleanup.", usedPercent, freeGB)
|
||||||
triggerAutoCleanup(ctx)
|
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)
|
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()
|
capabilities.Bandwidth = bandwidthManager.GetLastResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
capabilitiesJson := capabilities.ToJSON()
|
capabilitiesJSON := capabilities.ToJSON()
|
||||||
|
|
||||||
// Check for disk space warnings
|
// Check for disk space warnings
|
||||||
checkDiskSpaceAndCleanup(ctx, capabilities)
|
checkDiskSpaceAndCleanup(ctx, capabilities)
|
||||||
|
|
||||||
// Send updated capabilities to server
|
// Send updated capabilities to server
|
||||||
_, err := runner.Declare(ctx, labelNames, capabilitiesJson)
|
_, err := runner.Declare(ctx, labelNames, capabilitiesJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Debug("failed to update capabilities")
|
log.WithError(err).Debug("failed to update capabilities")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ func printList(plan *model.Plan) error {
|
|||||||
return nil
|
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
|
// plan with filtered jobs - to be used for filtering only
|
||||||
var filterPlan *model.Plan
|
var filterPlan *model.Plan
|
||||||
|
|
||||||
@@ -286,19 +286,20 @@ func runExecList(ctx context.Context, planner model.WorkflowPlanner, execArgs *e
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if execArgs.job != "" {
|
switch {
|
||||||
|
case execArgs.job != "":
|
||||||
log.Infof("Preparing plan with a job: %s", execArgs.job)
|
log.Infof("Preparing plan with a job: %s", execArgs.job)
|
||||||
filterPlan, err = planner.PlanJob(execArgs.job)
|
filterPlan, err = planner.PlanJob(execArgs.job)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if filterEventName != "" {
|
case filterEventName != "":
|
||||||
log.Infof("Preparing plan for a event: %s", filterEventName)
|
log.Infof("Preparing plan for a event: %s", filterEventName)
|
||||||
filterPlan, err = planner.PlanEvent(filterEventName)
|
filterPlan, err = planner.PlanEvent(filterEventName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
log.Infof("Preparing plan with all jobs")
|
log.Infof("Preparing plan with all jobs")
|
||||||
filterPlan, err = planner.PlanAll()
|
filterPlan, err = planner.PlanAll()
|
||||||
if err != nil {
|
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 {
|
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)
|
planner, err := model.NewWorkflowPlanner(execArgs.WorkflowsPath(), execArgs.noWorkflowRecurse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -331,18 +332,19 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
|||||||
// collect all events from loaded workflows
|
// collect all events from loaded workflows
|
||||||
events := planner.GetEvents()
|
events := planner.GetEvents()
|
||||||
|
|
||||||
if len(execArgs.event) > 0 {
|
switch {
|
||||||
|
case len(execArgs.event) > 0:
|
||||||
log.Infof("Using chosed event for filtering: %s", execArgs.event)
|
log.Infof("Using chosed event for filtering: %s", execArgs.event)
|
||||||
eventName = 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])
|
log.Infof("Using the only detected workflow event: %s", events[0])
|
||||||
eventName = 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
|
// set default event type to first event from many available
|
||||||
// this way user dont have to specify the event.
|
// this way user dont have to specify the event.
|
||||||
log.Infof("Using first detected workflow event: %s", events[0])
|
log.Infof("Using first detected workflow event: %s", events[0])
|
||||||
eventName = events[0]
|
eventName = events[0]
|
||||||
} else {
|
default:
|
||||||
log.Infof("Using default workflow event: push")
|
log.Infof("Using default workflow event: push")
|
||||||
eventName = "push"
|
eventName = "push"
|
||||||
}
|
}
|
||||||
@@ -388,7 +390,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tempDir)
|
defer func() { _ = os.RemoveAll(tempDir) }()
|
||||||
|
|
||||||
execArgs.artifactServerPath = 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)
|
log.Debugf("artifacts server started at %s:%s", execArgs.artifactServerPath, execArgs.artifactServerPort)
|
||||||
|
|
||||||
ctx = common.WithDryrun(ctx, execArgs.dryrun)
|
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()
|
artifactCancel()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import (
|
|||||||
|
|
||||||
// runRegister registers a runner to the server
|
// runRegister registers a runner to the server
|
||||||
func runRegister(ctx context.Context, regArgs *registerArgs, configFile *string) func(*cobra.Command, []string) error {
|
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)
|
log.SetReportCaller(false)
|
||||||
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||||
log.SetFormatter(&log.TextFormatter{
|
log.SetFormatter(&log.TextFormatter{
|
||||||
@@ -80,6 +80,7 @@ type registerArgs struct {
|
|||||||
|
|
||||||
type registerStage int8
|
type registerStage int8
|
||||||
|
|
||||||
|
// Register stage constants define the steps in the registration workflow.
|
||||||
const (
|
const (
|
||||||
StageUnknown registerStage = -1
|
StageUnknown registerStage = -1
|
||||||
StageOverwriteLocalConfig registerStage = iota + 1
|
StageOverwriteLocalConfig registerStage = iota + 1
|
||||||
@@ -250,7 +251,7 @@ func registerInteractive(ctx context.Context, configFile string, regArgs *regist
|
|||||||
if stage == StageWaitingForRegistration {
|
if stage == StageWaitingForRegistration {
|
||||||
log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.Labels)
|
log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.Labels)
|
||||||
if err := doRegister(ctx, cfg, inputs); err != nil {
|
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.")
|
log.Infof("Runner registered successfully.")
|
||||||
return nil
|
return nil
|
||||||
@@ -311,7 +312,7 @@ func registerNoInteractive(ctx context.Context, configFile string, regArgs *regi
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := doRegister(ctx, cfg, inputs); err != nil {
|
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.")
|
log.Infof("Runner registered successfully.")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2023 The Gitea Authors and MarketAlly. All rights reserved.
|
// Copyright 2023 The Gitea Authors and MarketAlly. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package poll provides task polling functionality for CI runners.
|
||||||
package poll
|
package poll
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -22,6 +23,7 @@ import (
|
|||||||
"git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/envcheck"
|
"git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/envcheck"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Poller handles task polling from the Gitea server.
|
||||||
type Poller struct {
|
type Poller struct {
|
||||||
client client.Client
|
client client.Client
|
||||||
runner *run.Runner
|
runner *run.Runner
|
||||||
@@ -38,6 +40,7 @@ type Poller struct {
|
|||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New creates a new Poller instance with the given context for shutdown propagation.
|
||||||
func New(ctx context.Context, cfg *config.Config, client client.Client, runner *run.Runner) *Poller {
|
func New(ctx context.Context, cfg *config.Config, client client.Client, runner *run.Runner) *Poller {
|
||||||
// Inherit from parent context so shutdown signals propagate properly
|
// Inherit from parent context so shutdown signals propagate properly
|
||||||
pollingCtx, shutdownPolling := context.WithCancel(ctx)
|
pollingCtx, shutdownPolling := context.WithCancel(ctx)
|
||||||
@@ -65,6 +68,7 @@ func (p *Poller) SetBandwidthManager(bm *envcheck.BandwidthManager) {
|
|||||||
p.bandwidthManager = bm
|
p.bandwidthManager = bm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Poll starts polling for tasks with the configured capacity.
|
||||||
func (p *Poller) Poll() {
|
func (p *Poller) Poll() {
|
||||||
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
@@ -78,6 +82,7 @@ func (p *Poller) Poll() {
|
|||||||
close(p.done)
|
close(p.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PollOnce polls for a single task and then exits.
|
||||||
func (p *Poller) PollOnce() {
|
func (p *Poller) PollOnce() {
|
||||||
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
|
||||||
|
|
||||||
@@ -87,18 +92,19 @@ func (p *Poller) PollOnce() {
|
|||||||
close(p.done)
|
close(p.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown gracefully stops the poller.
|
||||||
func (p *Poller) Shutdown(ctx context.Context) error {
|
func (p *Poller) Shutdown(ctx context.Context) error {
|
||||||
p.shutdownPolling()
|
p.shutdownPolling()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
// graceful shutdown completed succesfully
|
// graceful shutdown completed successfully
|
||||||
case <-p.done:
|
case <-p.done:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
// our timeout for shutting down ran out
|
// our timeout for shutting down ran out
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// when both the timeout fires and the graceful shutdown
|
// 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
|
// fire. Do a non-blocking check here against the graceful
|
||||||
// shutdown status to avoid sending an error if we don't need to.
|
// shutdown status to avoid sending an error if we don't need to.
|
||||||
_, ok := <-p.done
|
_, ok := <-p.done
|
||||||
@@ -110,7 +116,7 @@ func (p *Poller) Shutdown(ctx context.Context) error {
|
|||||||
p.shutdownJobs()
|
p.shutdownJobs()
|
||||||
|
|
||||||
// wait for running jobs to report their status to Gitea
|
// wait for running jobs to report their status to Gitea
|
||||||
_, _ = <-p.done
|
<-p.done
|
||||||
|
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
@@ -173,13 +179,13 @@ func (p *Poller) fetchTask(ctx context.Context) (*runnerv1.Task, bool) {
|
|||||||
caps.Bandwidth = p.bandwidthManager.GetLastResult()
|
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.
|
// Load the version value that was in the cache when the request was sent.
|
||||||
v := p.tasksVersion.Load()
|
v := p.tasksVersion.Load()
|
||||||
fetchReq := &runnerv1.FetchTaskRequest{
|
fetchReq := &runnerv1.FetchTaskRequest{
|
||||||
TasksVersion: v,
|
TasksVersion: v,
|
||||||
CapabilitiesJson: capsJson,
|
CapabilitiesJson: capsJSON,
|
||||||
}
|
}
|
||||||
resp, err := p.client.FetchTask(reqCtx, connect.NewRequest(fetchReq))
|
resp, err := p.client.FetchTask(reqCtx, connect.NewRequest(fetchReq))
|
||||||
if errors.Is(err, context.DeadlineExceeded) {
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package run provides the core runner functionality for executing tasks.
|
||||||
package run
|
package run
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -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 {
|
func NewRunner(cfg *config.Config, reg *config.Registration, cli client.Client) *Runner {
|
||||||
ls := labels.Labels{}
|
ls := labels.Labels{}
|
||||||
for _, v := range reg.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 {
|
func (r *Runner) Run(ctx context.Context, task *runnerv1.Task) error {
|
||||||
if _, ok := r.runningTasks.Load(task.Id); ok {
|
if _, ok := r.runningTasks.Load(task.Id); ok {
|
||||||
return fmt.Errorf("task %d is already running", task.Id)
|
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
|
// getDefaultActionsURL
|
||||||
// when DEFAULT_ACTIONS_URL == "https://github.com" and GithubMirror is not blank,
|
// when DEFAULT_ACTIONS_URL == "https://github.com" and GithubMirror is not blank,
|
||||||
// it should be set to GithubMirror first.
|
// 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()
|
giteaDefaultActionsURL := task.Context.Fields["gitea_default_actions_url"].GetStringValue()
|
||||||
if giteaDefaultActionsURL == "https://github.com" && r.cfg.Runner.GithubMirror != "" {
|
if giteaDefaultActionsURL == "https://github.com" && r.cfg.Runner.GithubMirror != "" {
|
||||||
return 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
|
preset.Token = t
|
||||||
}
|
}
|
||||||
|
|
||||||
if actionsIdTokenRequestUrl := taskContext["actions_id_token_request_url"].GetStringValue(); 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_URL"] = actionsIDTokenRequestURL
|
||||||
r.envs["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = taskContext["actions_id_token_request_token"].GetStringValue()
|
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"]
|
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
|
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{
|
return r.client.Declare(ctx, connect.NewRequest(&runnerv1.DeclareRequest{
|
||||||
Version: ver.Version(),
|
Version: ver.Version(),
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
CapabilitiesJson: capabilitiesJson,
|
CapabilitiesJson: capabilitiesJSON,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2026 MarketAlly. All rights reserved.
|
// Copyright 2026 MarketAlly. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package artifact provides utilities for handling artifact uploads.
|
||||||
package artifact
|
package artifact
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -88,7 +89,7 @@ func (u *UploadHelper) prewarmConnection(url string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ func (u *UploadHelper) doUpload(client *http.Client, url, token, filepath string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open file: %w", err)
|
return fmt.Errorf("failed to open file: %w", err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
stat, err := file.Stat()
|
stat, err := file.Stat()
|
||||||
if err != nil {
|
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 {
|
if _, err := io.Copy(part, file); err != nil {
|
||||||
return fmt.Errorf("failed to copy file to form: %w", err)
|
return fmt.Errorf("failed to copy file to form: %w", err)
|
||||||
}
|
}
|
||||||
writer.Close()
|
_ = writer.Close()
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, body)
|
req, err := http.NewRequest("POST", url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -133,7 +134,7 @@ func (u *UploadHelper) doUpload(client *http.Client, url, token, filepath string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("upload request failed: %w", err)
|
return fmt.Errorf("upload request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
respBody, _ := io.ReadAll(resp.Body)
|
respBody, _ := io.ReadAll(resp.Body)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2026 MarketAlly. All rights reserved.
|
// Copyright 2026 MarketAlly. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package cleanup provides disk cleanup utilities for CI runners.
|
||||||
package cleanup
|
package cleanup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -15,18 +16,18 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CleanupResult contains the results of a cleanup operation
|
// Result contains the results of a cleanup operation.
|
||||||
type CleanupResult struct {
|
type Result struct {
|
||||||
BytesFreed int64
|
BytesFreed int64
|
||||||
FilesDeleted int
|
FilesDeleted int
|
||||||
Errors []error
|
Errors []error
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunCleanup performs cleanup operations to free disk space
|
// RunCleanup performs cleanup operations to free disk space.
|
||||||
func RunCleanup(ctx context.Context, cfg *config.Config) (*CleanupResult, error) {
|
func RunCleanup(_ context.Context, cfg *config.Config) (*Result, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
result := &CleanupResult{}
|
result := &Result{}
|
||||||
|
|
||||||
log.Info("Starting runner cleanup...")
|
log.Info("Starting runner cleanup...")
|
||||||
|
|
||||||
@@ -207,8 +208,15 @@ func cleanTempDir(maxAge time.Duration) (int64, int, error) {
|
|||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only clean files/dirs that look like runner/act artifacts
|
// 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-", "pnpm-"}
|
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 {
|
for _, entry := range entries {
|
||||||
name := entry.Name()
|
name := entry.Name()
|
||||||
isRunner := false
|
isRunner := false
|
||||||
@@ -246,10 +254,10 @@ func cleanTempDir(maxAge time.Duration) (int64, int, error) {
|
|||||||
return bytesFreed, filesDeleted, nil
|
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 {
|
func dirSize(path string) int64 {
|
||||||
var size 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 {
|
if err != nil {
|
||||||
return 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"), "Yarn", "Cache"), "Yarn cache (Windows)", 0},
|
||||||
{filepath.Join(os.Getenv("LOCALAPPDATA"), "NuGet", "v3-cache"), "NuGet 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},
|
{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 {
|
for _, cache := range cacheDirs {
|
||||||
@@ -349,13 +360,13 @@ func cleanBuildCaches(maxAge time.Duration) (int64, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Also remove empty directories
|
// 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 {
|
if err != nil || !info.IsDir() || path == cache.path {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
entries, _ := os.ReadDir(path)
|
entries, _ := os.ReadDir(path)
|
||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
os.Remove(path)
|
_ = os.Remove(path)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package client provides the HTTP client for communicating with the runner API.
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
package client
|
package client
|
||||||
|
|
||||||
|
// HTTP header constants for runner authentication and identification.
|
||||||
const (
|
const (
|
||||||
UUIDHeader = "x-runner-uuid"
|
UUIDHeader = "x-runner-uuid"
|
||||||
TokenHeader = "x-runner-token"
|
TokenHeader = "x-runner-token"
|
||||||
|
|||||||
@@ -72,10 +72,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 {
|
func (c *HTTPClient) Address() string {
|
||||||
return c.endpoint
|
return c.endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insecure returns whether TLS verification is disabled.
|
||||||
func (c *HTTPClient) Insecure() bool {
|
func (c *HTTPClient) Insecure() bool {
|
||||||
return c.insecure
|
return c.insecure
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package config provides configuration loading and management for the runner.
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -5,5 +5,7 @@ package config
|
|||||||
|
|
||||||
import _ "embed"
|
import _ "embed"
|
||||||
|
|
||||||
|
// Example contains the example configuration file content.
|
||||||
|
//
|
||||||
//go:embed config.example.yaml
|
//go:embed config.example.yaml
|
||||||
var Example []byte
|
var Example []byte
|
||||||
|
|||||||
@@ -23,12 +23,13 @@ type Registration struct {
|
|||||||
Ephemeral bool `json:"ephemeral"`
|
Ephemeral bool `json:"ephemeral"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadRegistration loads the runner registration from a JSON file.
|
||||||
func LoadRegistration(file string) (*Registration, error) {
|
func LoadRegistration(file string) (*Registration, error) {
|
||||||
f, err := os.Open(file)
|
f, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
var reg Registration
|
var reg Registration
|
||||||
if err := json.NewDecoder(f).Decode(®); err != nil {
|
if err := json.NewDecoder(f).Decode(®); err != nil {
|
||||||
@@ -40,12 +41,13 @@ func LoadRegistration(file string) (*Registration, error) {
|
|||||||
return ®, nil
|
return ®, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveRegistration saves the runner registration to a JSON file.
|
||||||
func SaveRegistration(file string, reg *Registration) error {
|
func SaveRegistration(file string, reg *Registration) error {
|
||||||
f, err := os.Create(file)
|
f, err := os.Create(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
reg.Warning = registrationWarning
|
reg.Warning = registrationWarning
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ func testLatency(ctx context.Context, serverURL string) float64 {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
|
||||||
latency := time.Since(start).Seconds() * 1000 // Convert to ms
|
latency := time.Since(start).Seconds() * 1000 // Convert to ms
|
||||||
return float64(int(latency*100)) / 100 // Round to 2 decimals
|
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)
|
n, _ := io.Copy(io.Discard, resp.Body)
|
||||||
resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ type CapabilityFeatures struct {
|
|||||||
// DetectCapabilities detects the runner's capabilities
|
// DetectCapabilities detects the runner's capabilities
|
||||||
// workingDir is the directory where builds will run (for disk space detection)
|
// workingDir is the directory where builds will run (for disk space detection)
|
||||||
func DetectCapabilities(ctx context.Context, dockerHost string, workingDir string, capacity int) *RunnerCapabilities {
|
func DetectCapabilities(ctx context.Context, dockerHost string, workingDir string, capacity int) *RunnerCapabilities {
|
||||||
cap := &RunnerCapabilities{
|
caps := &RunnerCapabilities{
|
||||||
Capacity: capacity,
|
Capacity: capacity,
|
||||||
OS: runtime.GOOS,
|
OS: runtime.GOOS,
|
||||||
Arch: runtime.GOARCH,
|
Arch: runtime.GOARCH,
|
||||||
@@ -105,40 +105,40 @@ func DetectCapabilities(ctx context.Context, dockerHost string, workingDir strin
|
|||||||
|
|
||||||
// Detect Linux distribution
|
// Detect Linux distribution
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
cap.Distro = detectLinuxDistro()
|
caps.Distro = detectLinuxDistro()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect macOS Xcode/iOS
|
// Detect macOS Xcode/iOS
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
cap.Xcode = detectXcode(ctx)
|
caps.Xcode = detectXcode(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect Docker
|
// Detect Docker
|
||||||
cap.Docker, cap.ContainerRuntime = detectDocker(ctx, dockerHost)
|
caps.Docker, caps.ContainerRuntime = detectDocker(ctx, dockerHost)
|
||||||
if cap.Docker {
|
if caps.Docker {
|
||||||
cap.DockerCompose = detectDockerCompose(ctx)
|
caps.DockerCompose = detectDockerCompose(ctx)
|
||||||
cap.Features.Services = true
|
caps.Features.Services = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect common tools
|
// Detect common tools
|
||||||
detectTools(ctx, cap)
|
detectTools(ctx, caps)
|
||||||
|
|
||||||
// Detect build tools
|
// Detect build tools
|
||||||
detectBuildTools(ctx, cap)
|
detectBuildTools(ctx, caps)
|
||||||
|
|
||||||
// Detect package managers
|
// Detect package managers
|
||||||
detectPackageManagers(ctx, cap)
|
detectPackageManagers(ctx, caps)
|
||||||
|
|
||||||
// Detect disk space on the working directory's filesystem
|
// Detect disk space on the working directory's filesystem
|
||||||
cap.Disk = detectDiskSpace(workingDir)
|
caps.Disk = detectDiskSpace(workingDir)
|
||||||
|
|
||||||
// Detect CPU load
|
// Detect CPU load
|
||||||
cap.CPU = detectCPULoad()
|
caps.CPU = detectCPULoad()
|
||||||
|
|
||||||
// Generate suggested labels based on detected capabilities
|
// 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
|
// detectXcode detects Xcode and iOS development capabilities on macOS
|
||||||
@@ -227,18 +227,19 @@ func detectLinuxDistro() *DistroInfo {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer func() { _ = file.Close() }()
|
||||||
|
|
||||||
distro := &DistroInfo{}
|
distro := &DistroInfo{}
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
if strings.HasPrefix(line, "ID=") {
|
switch {
|
||||||
|
case strings.HasPrefix(line, "ID="):
|
||||||
distro.ID = strings.Trim(strings.TrimPrefix(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="), "\"")
|
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="), "\"")
|
distro.PrettyName = strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,7 +252,7 @@ func detectLinuxDistro() *DistroInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generateSuggestedLabels creates industry-standard labels based on capabilities
|
// generateSuggestedLabels creates industry-standard labels based on capabilities
|
||||||
func generateSuggestedLabels(cap *RunnerCapabilities) []string {
|
func generateSuggestedLabels(caps *RunnerCapabilities) []string {
|
||||||
labels := []string{}
|
labels := []string{}
|
||||||
seen := make(map[string]bool)
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
@@ -263,7 +264,7 @@ func generateSuggestedLabels(cap *RunnerCapabilities) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OS labels
|
// OS labels
|
||||||
switch cap.OS {
|
switch caps.OS {
|
||||||
case "linux":
|
case "linux":
|
||||||
addLabel("linux")
|
addLabel("linux")
|
||||||
addLabel("linux-latest")
|
addLabel("linux-latest")
|
||||||
@@ -276,17 +277,17 @@ func generateSuggestedLabels(cap *RunnerCapabilities) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Distro labels (Linux only)
|
// Distro labels (Linux only)
|
||||||
if cap.Distro != nil && cap.Distro.ID != "" {
|
if caps.Distro != nil && caps.Distro.ID != "" {
|
||||||
distro := strings.ToLower(cap.Distro.ID)
|
distro := strings.ToLower(caps.Distro.ID)
|
||||||
addLabel(distro)
|
addLabel(distro)
|
||||||
addLabel(distro + "-latest")
|
addLabel(distro + "-latest")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Xcode/iOS labels (macOS only)
|
// Xcode/iOS labels (macOS only)
|
||||||
if cap.Xcode != nil {
|
if caps.Xcode != nil {
|
||||||
addLabel("xcode")
|
addLabel("xcode")
|
||||||
// Check for SDKs
|
// Check for SDKs
|
||||||
for _, sdk := range cap.Xcode.SDKs {
|
for _, sdk := range caps.Xcode.SDKs {
|
||||||
sdkLower := strings.ToLower(sdk)
|
sdkLower := strings.ToLower(sdk)
|
||||||
if strings.Contains(sdkLower, "ios") {
|
if strings.Contains(sdkLower, "ios") {
|
||||||
addLabel("ios")
|
addLabel("ios")
|
||||||
@@ -302,24 +303,24 @@ func generateSuggestedLabels(cap *RunnerCapabilities) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If simulators available, add simulator label
|
// If simulators available, add simulator label
|
||||||
if len(cap.Xcode.Simulators) > 0 {
|
if len(caps.Xcode.Simulators) > 0 {
|
||||||
addLabel("ios-simulator")
|
addLabel("ios-simulator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool-based labels
|
// Tool-based labels
|
||||||
if _, ok := cap.Tools["dotnet"]; ok {
|
if _, ok := caps.Tools["dotnet"]; ok {
|
||||||
addLabel("dotnet")
|
addLabel("dotnet")
|
||||||
}
|
}
|
||||||
if _, ok := cap.Tools["java"]; ok {
|
if _, ok := caps.Tools["java"]; ok {
|
||||||
addLabel("java")
|
addLabel("java")
|
||||||
}
|
}
|
||||||
if _, ok := cap.Tools["node"]; ok {
|
if _, ok := caps.Tools["node"]; ok {
|
||||||
addLabel("node")
|
addLabel("node")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build tool labels
|
// Build tool labels
|
||||||
for _, tool := range cap.BuildTools {
|
for _, tool := range caps.BuildTools {
|
||||||
switch tool {
|
switch tool {
|
||||||
case "msbuild":
|
case "msbuild":
|
||||||
addLabel("msbuild")
|
addLabel("msbuild")
|
||||||
@@ -384,7 +385,7 @@ func detectDocker(ctx context.Context, dockerHost string) (bool, string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
defer cli.Close()
|
defer func() { _ = cli.Close() }()
|
||||||
|
|
||||||
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -420,7 +421,7 @@ func detectDockerCompose(ctx context.Context) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectTools(ctx context.Context, cap *RunnerCapabilities) {
|
func detectTools(ctx context.Context, caps *RunnerCapabilities) {
|
||||||
toolDetectors := map[string]func(context.Context) []string{
|
toolDetectors := map[string]func(context.Context) []string{
|
||||||
"node": detectNodeVersions,
|
"node": detectNodeVersions,
|
||||||
"go": detectGoVersions,
|
"go": detectGoVersions,
|
||||||
@@ -439,7 +440,7 @@ func detectTools(ctx context.Context, cap *RunnerCapabilities) {
|
|||||||
|
|
||||||
for tool, detector := range toolDetectors {
|
for tool, detector := range toolDetectors {
|
||||||
if versions := detector(ctx); len(versions) > 0 {
|
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 {
|
for name, cmd := range simpleTools {
|
||||||
if v := detectSimpleToolVersion(ctx, cmd); v != "" {
|
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 {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
detectWindowsBuildTools(ctx, cap)
|
detectWindowsBuildTools(ctx, caps)
|
||||||
case "darwin":
|
case "darwin":
|
||||||
detectMacOSBuildTools(ctx, cap)
|
detectMacOSBuildTools(caps)
|
||||||
case "linux":
|
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
|
// Check for Visual Studio via vswhere
|
||||||
vswherePaths := []string{
|
vswherePaths := []string{
|
||||||
`C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe`,
|
`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 {
|
if _, err := os.Stat(vswhere); err == nil {
|
||||||
cmd := exec.CommandContext(ctx, vswhere, "-latest", "-property", "displayName")
|
cmd := exec.CommandContext(ctx, vswhere, "-latest", "-property", "displayName")
|
||||||
if output, err := cmd.Output(); err == nil && len(output) > 0 {
|
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
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,7 +504,7 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
|||||||
}
|
}
|
||||||
for _, msbuild := range msbuildPaths {
|
for _, msbuild := range msbuildPaths {
|
||||||
if _, err := os.Stat(msbuild); err == nil {
|
if _, err := os.Stat(msbuild); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "msbuild")
|
caps.BuildTools = append(caps.BuildTools, "msbuild")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,14 +518,14 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
|||||||
}
|
}
|
||||||
for _, iscc := range innoSetupPaths {
|
for _, iscc := range innoSetupPaths {
|
||||||
if _, err := os.Stat(iscc); err == nil {
|
if _, err := os.Stat(iscc); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "inno-setup")
|
caps.BuildTools = append(caps.BuildTools, "inno-setup")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Also check PATH
|
// Also check PATH
|
||||||
if _, err := exec.LookPath("iscc"); err == nil {
|
if _, err := exec.LookPath("iscc"); err == nil {
|
||||||
if !contains(cap.BuildTools, "inno-setup") {
|
if !contains(caps.BuildTools, "inno-setup") {
|
||||||
cap.BuildTools = append(cap.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 {
|
for _, nsis := range nsisPaths {
|
||||||
if _, err := os.Stat(nsis); err == nil {
|
if _, err := os.Stat(nsis); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "nsis")
|
caps.BuildTools = append(caps.BuildTools, "nsis")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, err := exec.LookPath("makensis"); err == nil {
|
if _, err := exec.LookPath("makensis"); err == nil {
|
||||||
if !contains(cap.BuildTools, "nsis") {
|
if !contains(caps.BuildTools, "nsis") {
|
||||||
cap.BuildTools = append(cap.BuildTools, "nsis")
|
caps.BuildTools = append(caps.BuildTools, "nsis")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +553,7 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
|||||||
}
|
}
|
||||||
for _, wix := range wixPaths {
|
for _, wix := range wixPaths {
|
||||||
if _, err := os.Stat(wix); err == nil {
|
if _, err := os.Stat(wix); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "wix")
|
caps.BuildTools = append(caps.BuildTools, "wix")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -560,63 +561,63 @@ func detectWindowsBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
|||||||
// Check for signtool (Windows SDK)
|
// Check for signtool (Windows SDK)
|
||||||
signtoolPaths, _ := filepath.Glob(`C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe`)
|
signtoolPaths, _ := filepath.Glob(`C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe`)
|
||||||
if len(signtoolPaths) > 0 {
|
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
|
// Check for xcpretty
|
||||||
if _, err := exec.LookPath("xcpretty"); err == nil {
|
if _, err := exec.LookPath("xcpretty"); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "xcpretty")
|
caps.BuildTools = append(caps.BuildTools, "xcpretty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for fastlane
|
// Check for fastlane
|
||||||
if _, err := exec.LookPath("fastlane"); err == nil {
|
if _, err := exec.LookPath("fastlane"); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "fastlane")
|
caps.BuildTools = append(caps.BuildTools, "fastlane")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for CocoaPods
|
// Check for CocoaPods
|
||||||
if _, err := exec.LookPath("pod"); err == nil {
|
if _, err := exec.LookPath("pod"); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "cocoapods")
|
caps.BuildTools = append(caps.BuildTools, "cocoapods")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for Carthage
|
// Check for Carthage
|
||||||
if _, err := exec.LookPath("carthage"); err == nil {
|
if _, err := exec.LookPath("carthage"); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "carthage")
|
caps.BuildTools = append(caps.BuildTools, "carthage")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for SwiftLint
|
// Check for SwiftLint
|
||||||
if _, err := exec.LookPath("swiftlint"); err == nil {
|
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
|
// Check for create-dmg or similar
|
||||||
if _, err := exec.LookPath("create-dmg"); err == nil {
|
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)
|
// Check for Packages (packagesbuild)
|
||||||
if _, err := exec.LookPath("packagesbuild"); err == nil {
|
if _, err := exec.LookPath("packagesbuild"); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "packages")
|
caps.BuildTools = append(caps.BuildTools, "packages")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for pkgbuild (built-in)
|
// Check for pkgbuild (built-in)
|
||||||
if _, err := exec.LookPath("pkgbuild"); err == nil {
|
if _, err := exec.LookPath("pkgbuild"); err == nil {
|
||||||
cap.BuildTools = append(cap.BuildTools, "pkgbuild")
|
caps.BuildTools = append(caps.BuildTools, "pkgbuild")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for codesign (built-in)
|
// Check for codesign (built-in)
|
||||||
if _, err := exec.LookPath("codesign"); err == nil {
|
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)
|
// Check for notarytool (built-in with Xcode)
|
||||||
if _, err := exec.LookPath("notarytool"); err == nil {
|
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
|
// Check for common Linux build tools
|
||||||
tools := []string{
|
tools := []string{
|
||||||
"gcc", "g++", "clang", "clang++",
|
"gcc", "g++", "clang", "clang++",
|
||||||
@@ -628,54 +629,54 @@ func detectLinuxBuildTools(ctx context.Context, cap *RunnerCapabilities) {
|
|||||||
|
|
||||||
for _, tool := range tools {
|
for _, tool := range tools {
|
||||||
if _, err := exec.LookPath(tool); err == nil {
|
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 {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
if _, err := exec.LookPath("choco"); err == nil {
|
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 {
|
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 {
|
if _, err := exec.LookPath("winget"); err == nil {
|
||||||
cap.PackageManagers = append(cap.PackageManagers, "winget")
|
caps.PackageManagers = append(caps.PackageManagers, "winget")
|
||||||
}
|
}
|
||||||
case "darwin":
|
case "darwin":
|
||||||
if _, err := exec.LookPath("brew"); err == nil {
|
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 {
|
if _, err := exec.LookPath("port"); err == nil {
|
||||||
cap.PackageManagers = append(cap.PackageManagers, "macports")
|
caps.PackageManagers = append(caps.PackageManagers, "macports")
|
||||||
}
|
}
|
||||||
case "linux":
|
case "linux":
|
||||||
if _, err := exec.LookPath("apt"); err == nil {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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 {
|
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)
|
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Use -Command to get version
|
// Use -Command to get version (same command works for both pwsh and powershell)
|
||||||
var c *exec.Cmd
|
c := exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()")
|
||||||
if cmd == "pwsh" {
|
|
||||||
c = exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()")
|
|
||||||
} else {
|
|
||||||
c = exec.CommandContext(timeoutCtx, cmd, "-Command", "$PSVersionTable.PSVersion.ToString()")
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := c.Output()
|
output, err := c.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1042,15 +1038,9 @@ func getContainerCPUUsage() float64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try reading /proc/stat for this process's CPU usage
|
// Note: Reading /proc/self/stat could give us utime and stime (fields 14 and 15),
|
||||||
if data, err := os.ReadFile("/proc/self/stat"); err == nil {
|
// but these are cumulative values, not instantaneous. For containers, we report 0
|
||||||
fields := strings.Fields(string(data))
|
// rather than misleading host 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1 // Unable to determine - caller should handle
|
return -1 // Unable to determine - caller should handle
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CheckIfDockerRunning verifies that the Docker daemon is running and accessible.
|
||||||
func CheckIfDockerRunning(ctx context.Context, configDockerHost string) error {
|
func CheckIfDockerRunning(ctx context.Context, configDockerHost string) error {
|
||||||
opts := []client.Opt{
|
opts := []client.Opt{
|
||||||
client.FromEnv,
|
client.FromEnv,
|
||||||
@@ -23,7 +24,7 @@ func CheckIfDockerRunning(ctx context.Context, configDockerHost string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer cli.Close()
|
defer func() { _ = cli.Close() }()
|
||||||
|
|
||||||
_, err = cli.Ping(ctx)
|
_, err = cli.Ping(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package labels provides utilities for parsing and managing runner labels.
|
||||||
package labels
|
package labels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -8,17 +9,20 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Label scheme constants define the execution environments.
|
||||||
const (
|
const (
|
||||||
SchemeHost = "host"
|
SchemeHost = "host"
|
||||||
SchemeDocker = "docker"
|
SchemeDocker = "docker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Label represents a parsed runner label with name, schema, and optional argument.
|
||||||
type Label struct {
|
type Label struct {
|
||||||
Name string
|
Name string
|
||||||
Schema string
|
Schema string
|
||||||
Arg string
|
Arg string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse parses a label string in the format "name:schema:arg" and returns a Label.
|
||||||
func Parse(str string) (*Label, error) {
|
func Parse(str string) (*Label, error) {
|
||||||
splits := strings.SplitN(str, ":", 3)
|
splits := strings.SplitN(str, ":", 3)
|
||||||
label := &Label{
|
label := &Label{
|
||||||
@@ -38,8 +42,10 @@ func Parse(str string) (*Label, error) {
|
|||||||
return label, nil
|
return label, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Labels is a slice of Label pointers.
|
||||||
type Labels []*Label
|
type Labels []*Label
|
||||||
|
|
||||||
|
// RequireDocker returns true if any label uses the docker schema.
|
||||||
func (l Labels) RequireDocker() bool {
|
func (l Labels) RequireDocker() bool {
|
||||||
for _, label := range l {
|
for _, label := range l {
|
||||||
if label.Schema == SchemeDocker {
|
if label.Schema == SchemeDocker {
|
||||||
@@ -49,6 +55,7 @@ func (l Labels) RequireDocker() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PickPlatform selects the appropriate platform based on the runsOn requirements.
|
||||||
func (l Labels) PickPlatform(runsOn []string) string {
|
func (l Labels) PickPlatform(runsOn []string) string {
|
||||||
platforms := make(map[string]string, len(l))
|
platforms := make(map[string]string, len(l))
|
||||||
for _, label := range l {
|
for _, label := range l {
|
||||||
@@ -82,6 +89,7 @@ func (l Labels) PickPlatform(runsOn []string) string {
|
|||||||
return "docker.gitea.com/runner-images:ubuntu-latest"
|
return "docker.gitea.com/runner-images:ubuntu-latest"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Names returns the names of all labels.
|
||||||
func (l Labels) Names() []string {
|
func (l Labels) Names() []string {
|
||||||
names := make([]string, 0, len(l))
|
names := make([]string, 0, len(l))
|
||||||
for _, label := range l {
|
for _, label := range l {
|
||||||
@@ -90,6 +98,7 @@ func (l Labels) Names() []string {
|
|||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToStrings converts labels back to their string representation.
|
||||||
func (l Labels) ToStrings() []string {
|
func (l Labels) ToStrings() []string {
|
||||||
ls := make([]string, 0, len(l))
|
ls := make([]string, 0, len(l))
|
||||||
for _, label := range l {
|
for _, label := range l {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package report provides task reporting functionality for communicating with the server.
|
||||||
package report
|
package report
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -21,6 +22,7 @@ import (
|
|||||||
"git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/client"
|
"git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Reporter handles logging and state reporting for running tasks.
|
||||||
type Reporter struct {
|
type Reporter struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
@@ -42,6 +44,7 @@ type Reporter struct {
|
|||||||
stopCommandEndToken string
|
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 {
|
func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.Client, task *runnerv1.Task) *Reporter {
|
||||||
var oldnew []string
|
var oldnew []string
|
||||||
if v := task.Context.Fields["token"].GetStringValue(); v != "" {
|
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
|
return rv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetSteps initializes the step states with the given number of steps.
|
||||||
func (r *Reporter) ResetSteps(l int) {
|
func (r *Reporter) ResetSteps(l int) {
|
||||||
r.stateMu.Lock()
|
r.stateMu.Lock()
|
||||||
defer r.stateMu.Unlock()
|
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 {
|
func (r *Reporter) Levels() []log.Level {
|
||||||
return log.AllLevels
|
return log.AllLevels
|
||||||
}
|
}
|
||||||
@@ -93,6 +98,7 @@ func appendIfNotNil[T any](s []*T, v *T) []*T {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fire processes a log entry and updates the task state accordingly.
|
||||||
func (r *Reporter) Fire(entry *log.Entry) error {
|
func (r *Reporter) Fire(entry *log.Entry) error {
|
||||||
r.stateMu.Lock()
|
r.stateMu.Lock()
|
||||||
defer r.stateMu.Unlock()
|
defer r.stateMu.Unlock()
|
||||||
@@ -175,6 +181,7 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunDaemon starts the periodic reporting of logs and state.
|
||||||
func (r *Reporter) RunDaemon() {
|
func (r *Reporter) RunDaemon() {
|
||||||
if r.closed {
|
if r.closed {
|
||||||
return
|
return
|
||||||
@@ -199,6 +206,7 @@ func (r *Reporter) RunDaemon() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logf adds a formatted log message to the report.
|
||||||
func (r *Reporter) Logf(format string, a ...interface{}) {
|
func (r *Reporter) Logf(format string, a ...interface{}) {
|
||||||
r.stateMu.Lock()
|
r.stateMu.Lock()
|
||||||
defer r.stateMu.Unlock()
|
defer r.stateMu.Unlock()
|
||||||
@@ -215,6 +223,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) {
|
func (r *Reporter) SetOutputs(outputs map[string]string) {
|
||||||
r.stateMu.Lock()
|
r.stateMu.Lock()
|
||||||
defer r.stateMu.Unlock()
|
defer r.stateMu.Unlock()
|
||||||
@@ -235,6 +244,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 {
|
func (r *Reporter) Close(lastWords string) error {
|
||||||
r.closed = true
|
r.closed = true
|
||||||
|
|
||||||
@@ -270,6 +280,7 @@ func (r *Reporter) Close(lastWords string) error {
|
|||||||
}, retry.Context(r.ctx))
|
}, retry.Context(r.ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReportLog sends accumulated log rows to the server.
|
||||||
func (r *Reporter) ReportLog(noMore bool) error {
|
func (r *Reporter) ReportLog(noMore bool) error {
|
||||||
r.clientM.Lock()
|
r.clientM.Lock()
|
||||||
defer r.clientM.Unlock()
|
defer r.clientM.Unlock()
|
||||||
@@ -305,6 +316,7 @@ func (r *Reporter) ReportLog(noMore bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReportState sends the current task state to the server.
|
||||||
func (r *Reporter) ReportState() error {
|
func (r *Reporter) ReportState() error {
|
||||||
r.clientM.Lock()
|
r.clientM.Lock()
|
||||||
defer r.clientM.Unlock()
|
defer r.clientM.Unlock()
|
||||||
@@ -383,7 +395,7 @@ func (r *Reporter) parseResult(result interface{}) (runnerv1.Result, bool) {
|
|||||||
|
|
||||||
var cmdRegex = regexp.MustCompile(`^::([^ :]+)( .*)?::(.*)$`)
|
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 {
|
if r.stopCommandEndToken != "" && command != r.stopCommandEndToken {
|
||||||
return &originalContent
|
return &originalContent
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package ver provides version information for the runner.
|
||||||
package ver
|
package ver
|
||||||
|
|
||||||
// go build -ldflags "-X git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/ver.version=1.2.3"
|
// go build -ldflags "-X git.marketally.com/gitcaddy/gitcaddy-runner/internal/pkg/ver.version=1.2.3"
|
||||||
var version = "dev"
|
var version = "dev"
|
||||||
|
|
||||||
|
// Version returns the current runner version.
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|||||||
1
main.go
1
main.go
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// GitCaddy Runner is a CI/CD runner for Gitea Actions.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user