Refactor environment variables to configuration and registration (#90)
Close #21. Refactor environment variables to configuration file (config.yaml) and registration file (.runner). The old environment variables are still supported, but warning logs will be printed. Like: ```text $ GITEA_DEBUG=true ./act_runner -c config.yaml daemon INFO[0000] Starting runner daemon WARN[0000] env GITEA_DEBUG has been ignored because config file is used $ GITEA_DEBUG=true ./act_runner daemon INFO[0000] Starting runner daemon WARN[0000] env GITEA_DEBUG will be deprecated, please use config file instead ``` Reviewed-on: https://gitea.com/gitea/act_runner/pulls/90 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
028451bf22
commit
c817236aa4
18 changed files with 376 additions and 274 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@ forgejo-runner
|
||||||
.runner
|
.runner
|
||||||
coverage.txt
|
coverage.txt
|
||||||
/gitea-vet
|
/gitea-vet
|
||||||
|
/config.yaml
|
||||||
|
|
||||||
# MS VSCode
|
# MS VSCode
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
@ -42,14 +42,15 @@ type Handler struct {
|
||||||
outboundIP string
|
outboundIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler() (*Handler, error) {
|
func NewHandler(dir, outboundIP string, port uint16) (*Handler, error) {
|
||||||
h := &Handler{}
|
h := &Handler{}
|
||||||
|
|
||||||
dir := "" // TODO: make the dir configurable if necessary
|
if dir == "" {
|
||||||
if home, err := os.UserHomeDir(); err != nil {
|
if home, err := os.UserHomeDir(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
dir = filepath.Join(home, ".cache/actcache")
|
dir = filepath.Join(home, ".cache", "actcache")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -70,7 +71,9 @@ func NewHandler() (*Handler, error) {
|
||||||
}
|
}
|
||||||
h.storage = storage
|
h.storage = storage
|
||||||
|
|
||||||
if ip, err := getOutboundIP(); err != nil {
|
if outboundIP != "" {
|
||||||
|
h.outboundIP = outboundIP
|
||||||
|
} else if ip, err := getOutboundIP(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
h.outboundIP = ip.String()
|
h.outboundIP = ip.String()
|
||||||
|
@ -102,8 +105,7 @@ func NewHandler() (*Handler, error) {
|
||||||
|
|
||||||
h.gcCache()
|
h.gcCache()
|
||||||
|
|
||||||
// TODO: make the port configurable if necessary
|
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) // listen on all interfaces
|
||||||
listener, err := net.Listen("tcp", ":0") // random available port
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
10
client/header.go
Normal file
10
client/header.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
const (
|
||||||
|
UUIDHeader = "x-runner-uuid"
|
||||||
|
TokenHeader = "x-runner-token"
|
||||||
|
VersionHeader = "x-runner-version"
|
||||||
|
)
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/actions-proto-go/ping/v1/pingv1connect"
|
"code.gitea.io/actions-proto-go/ping/v1/pingv1connect"
|
||||||
"code.gitea.io/actions-proto-go/runner/v1/runnerv1connect"
|
"code.gitea.io/actions-proto-go/runner/v1/runnerv1connect"
|
||||||
"codeberg.org/forgejo/runner/core"
|
|
||||||
"github.com/bufbuild/connect-go"
|
"github.com/bufbuild/connect-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,13 +31,13 @@ func New(endpoint string, insecure bool, uuid, token, runnerVersion string, opts
|
||||||
opts = append(opts, connect.WithInterceptors(connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
|
opts = append(opts, connect.WithInterceptors(connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
|
||||||
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
|
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
|
||||||
if uuid != "" {
|
if uuid != "" {
|
||||||
req.Header().Set(core.UUIDHeader, uuid)
|
req.Header().Set(UUIDHeader, uuid)
|
||||||
}
|
}
|
||||||
if token != "" {
|
if token != "" {
|
||||||
req.Header().Set(core.TokenHeader, token)
|
req.Header().Set(TokenHeader, token)
|
||||||
}
|
}
|
||||||
if runnerVersion != "" {
|
if runnerVersion != "" {
|
||||||
req.Header().Set(core.VersionHeader, runnerVersion)
|
req.Header().Set(VersionHeader, runnerVersion)
|
||||||
}
|
}
|
||||||
return next(ctx, req)
|
return next(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
31
cmd/cmd.go
31
cmd/cmd.go
|
@ -1,24 +1,20 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"codeberg.org/forgejo/runner/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// the version of act_runner
|
// the version of act_runner
|
||||||
var version = "develop"
|
var version = "develop"
|
||||||
|
|
||||||
type globalArgs struct {
|
|
||||||
EnvFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Execute(ctx context.Context) {
|
func Execute(ctx context.Context) {
|
||||||
// task := runtime.NewTask("gitea", 0, nil, nil)
|
|
||||||
|
|
||||||
var gArgs globalArgs
|
|
||||||
|
|
||||||
// ./act_runner
|
// ./act_runner
|
||||||
rootCmd := &cobra.Command{
|
rootCmd := &cobra.Command{
|
||||||
Use: "act_runner [event name to run]\nIf no event name passed, will default to \"on: push\"",
|
Use: "act_runner [event name to run]\nIf no event name passed, will default to \"on: push\"",
|
||||||
|
@ -26,9 +22,9 @@ func Execute(ctx context.Context) {
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
Version: version,
|
Version: version,
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
RunE: runDaemon(ctx, gArgs.EnvFile),
|
|
||||||
}
|
}
|
||||||
rootCmd.PersistentFlags().StringVarP(&gArgs.EnvFile, "env-file", "", ".env", "Read in a file of environment variables.")
|
configFile := ""
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "Config file path")
|
||||||
|
|
||||||
// ./act_runner register
|
// ./act_runner register
|
||||||
var regArgs registerArgs
|
var regArgs registerArgs
|
||||||
|
@ -36,11 +32,10 @@ func Execute(ctx context.Context) {
|
||||||
Use: "register",
|
Use: "register",
|
||||||
Short: "Register a runner to the server",
|
Short: "Register a runner to the server",
|
||||||
Args: cobra.MaximumNArgs(0),
|
Args: cobra.MaximumNArgs(0),
|
||||||
RunE: runRegister(ctx, ®Args, gArgs.EnvFile), // must use a pointer to regArgs
|
RunE: runRegister(ctx, ®Args, &configFile), // must use a pointer to regArgs
|
||||||
}
|
}
|
||||||
registerCmd.Flags().BoolVar(®Args.NoInteractive, "no-interactive", false, "Disable interactive mode")
|
registerCmd.Flags().BoolVar(®Args.NoInteractive, "no-interactive", false, "Disable interactive mode")
|
||||||
registerCmd.Flags().StringVar(®Args.InstanceAddr, "instance", "", "Forgejo instance address")
|
registerCmd.Flags().StringVar(®Args.InstanceAddr, "instance", "", "Forgejo instance address")
|
||||||
registerCmd.Flags().BoolVar(®Args.Insecure, "insecure", false, "If check server's certificate if it's https protocol")
|
|
||||||
registerCmd.Flags().StringVar(®Args.Token, "token", "", "Runner token")
|
registerCmd.Flags().StringVar(®Args.Token, "token", "", "Runner token")
|
||||||
registerCmd.Flags().StringVar(®Args.RunnerName, "name", "", "Runner name")
|
registerCmd.Flags().StringVar(®Args.RunnerName, "name", "", "Runner name")
|
||||||
registerCmd.Flags().StringVar(®Args.Labels, "labels", "", "Runner tags, comma separated")
|
registerCmd.Flags().StringVar(®Args.Labels, "labels", "", "Runner tags, comma separated")
|
||||||
|
@ -51,13 +46,23 @@ func Execute(ctx context.Context) {
|
||||||
Use: "daemon",
|
Use: "daemon",
|
||||||
Short: "Run as a runner daemon",
|
Short: "Run as a runner daemon",
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
RunE: runDaemon(ctx, gArgs.EnvFile),
|
RunE: runDaemon(ctx, &configFile),
|
||||||
}
|
}
|
||||||
rootCmd.AddCommand(daemonCmd)
|
rootCmd.AddCommand(daemonCmd)
|
||||||
|
|
||||||
// ./act_runner exec
|
// ./act_runner exec
|
||||||
rootCmd.AddCommand(loadExecCmd(ctx))
|
rootCmd.AddCommand(loadExecCmd(ctx))
|
||||||
|
|
||||||
|
// ./act_runner config
|
||||||
|
rootCmd.AddCommand(&cobra.Command{
|
||||||
|
Use: "generate-config",
|
||||||
|
Short: "Generate an example config file",
|
||||||
|
Args: cobra.MaximumNArgs(0),
|
||||||
|
Run: func(_ *cobra.Command, _ []string) {
|
||||||
|
fmt.Printf("%s", config.Example)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// hide completion command
|
// hide completion command
|
||||||
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
rootCmd.CompletionOptions.HiddenDefaultCmd = true
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -18,22 +18,28 @@ import (
|
||||||
"codeberg.org/forgejo/runner/runtime"
|
"codeberg.org/forgejo/runner/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, args []string) error {
|
func runDaemon(ctx context.Context, configFile *string) func(cmd *cobra.Command, args []string) error {
|
||||||
return func(cmd *cobra.Command, args []string) error {
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
log.Infoln("Starting runner daemon")
|
log.Infoln("Starting runner daemon")
|
||||||
|
|
||||||
_ = godotenv.Load(envFile)
|
cfg, err := config.LoadDefault(*configFile)
|
||||||
cfg, err := config.FromEnviron()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).
|
return fmt.Errorf("invalid configuration: %w", err)
|
||||||
Fatalln("invalid configuration")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initLogging(cfg)
|
initLogging(cfg)
|
||||||
|
|
||||||
|
reg, err := config.LoadRegistration(cfg.Runner.File)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
log.Error("registration file not found, please register the runner first")
|
||||||
|
return err
|
||||||
|
} else if err != nil {
|
||||||
|
return fmt.Errorf("failed to load registration file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// require docker if a runner label uses a docker backend
|
// require docker if a runner label uses a docker backend
|
||||||
needsDocker := false
|
needsDocker := false
|
||||||
for _, l := range cfg.Runner.Labels {
|
for _, l := range reg.Labels {
|
||||||
_, schema, _, _ := runtime.ParseLabel(l)
|
_, schema, _, _ := runtime.ParseLabel(l)
|
||||||
if schema == "docker" {
|
if schema == "docker" {
|
||||||
needsDocker = true
|
needsDocker = true
|
||||||
|
@ -52,40 +58,40 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg
|
||||||
var g errgroup.Group
|
var g errgroup.Group
|
||||||
|
|
||||||
cli := client.New(
|
cli := client.New(
|
||||||
cfg.Client.Address,
|
reg.Address,
|
||||||
cfg.Client.Insecure,
|
cfg.Runner.Insecure,
|
||||||
cfg.Runner.UUID,
|
reg.UUID,
|
||||||
cfg.Runner.Token,
|
reg.Token,
|
||||||
version,
|
version,
|
||||||
)
|
)
|
||||||
|
|
||||||
runner := &runtime.Runner{
|
runner := &runtime.Runner{
|
||||||
Client: cli,
|
Client: cli,
|
||||||
Machine: cfg.Runner.Name,
|
Machine: reg.Name,
|
||||||
ForgeInstance: cfg.Client.Address,
|
ForgeInstance: reg.Address,
|
||||||
Environ: cfg.Runner.Environ,
|
Environ: cfg.Runner.Envs,
|
||||||
Labels: cfg.Runner.Labels,
|
Labels: reg.Labels,
|
||||||
Version: version,
|
Version: version,
|
||||||
}
|
}
|
||||||
|
|
||||||
if handler, err := artifactcache.NewHandler(); err != nil {
|
if *cfg.Cache.Enabled {
|
||||||
log.Errorf("cannot init cache server, it will be disabled: %v", err)
|
if handler, err := artifactcache.NewHandler(cfg.Cache.Dir, cfg.Cache.Host, cfg.Cache.Port); err != nil {
|
||||||
} else {
|
log.Errorf("cannot init cache server, it will be disabled: %v", err)
|
||||||
log.Infof("cache handler listens on: %v", handler.ExternalURL())
|
} else {
|
||||||
runner.CacheHandler = handler
|
log.Infof("cache handler listens on: %v", handler.ExternalURL())
|
||||||
|
runner.CacheHandler = handler
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
poller := poller.New(
|
poller := poller.New(
|
||||||
cli,
|
cli,
|
||||||
runner.Run,
|
runner.Run,
|
||||||
cfg.Runner.Capacity,
|
cfg,
|
||||||
)
|
)
|
||||||
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
l := log.WithField("capacity", cfg.Runner.Capacity).
|
l := log.WithField("capacity", cfg.Runner.Capacity).
|
||||||
WithField("endpoint", cfg.Client.Address).
|
WithField("endpoint", reg.Address)
|
||||||
WithField("os", cfg.Platform.OS).
|
|
||||||
WithField("arch", cfg.Platform.Arch)
|
|
||||||
l.Infoln("polling the remote server")
|
l.Infoln("polling the remote server")
|
||||||
|
|
||||||
if err := poller.Poll(ctx); err != nil {
|
if err := poller.Poll(ctx); err != nil {
|
||||||
|
@ -105,17 +111,22 @@ func runDaemon(ctx context.Context, envFile string) func(cmd *cobra.Command, arg
|
||||||
}
|
}
|
||||||
|
|
||||||
// initLogging setup the global logrus logger.
|
// initLogging setup the global logrus logger.
|
||||||
func initLogging(cfg config.Config) {
|
func initLogging(cfg *config.Config) {
|
||||||
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||||
log.SetFormatter(&log.TextFormatter{
|
log.SetFormatter(&log.TextFormatter{
|
||||||
DisableColors: !isTerm,
|
DisableColors: !isTerm,
|
||||||
FullTimestamp: true,
|
FullTimestamp: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
if cfg.Debug {
|
if l := cfg.Log.Level; l != "" {
|
||||||
log.SetLevel(log.DebugLevel)
|
level, err := log.ParseLevel(l)
|
||||||
}
|
if err != nil {
|
||||||
if cfg.Trace {
|
log.WithError(err).
|
||||||
log.SetLevel(log.TraceLevel)
|
Errorf("invalid log level: %q", l)
|
||||||
|
}
|
||||||
|
if log.GetLevel() != level {
|
||||||
|
log.Infof("log level changed to %v", level)
|
||||||
|
log.SetLevel(level)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,7 +348,7 @@ func runExec(ctx context.Context, execArgs *executeArgs) func(cmd *cobra.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// init a cache server
|
// init a cache server
|
||||||
handler, err := artifactcache.NewHandler()
|
handler, err := artifactcache.NewHandler("", "", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,21 +14,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pingv1 "code.gitea.io/actions-proto-go/ping/v1"
|
pingv1 "code.gitea.io/actions-proto-go/ping/v1"
|
||||||
|
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||||
|
|
||||||
"codeberg.org/forgejo/runner/client"
|
"codeberg.org/forgejo/runner/client"
|
||||||
"codeberg.org/forgejo/runner/config"
|
"codeberg.org/forgejo/runner/config"
|
||||||
"codeberg.org/forgejo/runner/register"
|
|
||||||
"codeberg.org/forgejo/runner/runtime"
|
"codeberg.org/forgejo/runner/runtime"
|
||||||
|
|
||||||
"github.com/bufbuild/connect-go"
|
"github.com/bufbuild/connect-go"
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// runRegister registers a runner to the server
|
// runRegister registers a runner to the server
|
||||||
func runRegister(ctx context.Context, regArgs *registerArgs, envFile 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(cmd *cobra.Command, args []string) error {
|
||||||
log.SetReportCaller(false)
|
log.SetReportCaller(false)
|
||||||
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
isTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||||
|
@ -48,14 +47,13 @@ func runRegister(ctx context.Context, regArgs *registerArgs, envFile string) fun
|
||||||
}
|
}
|
||||||
|
|
||||||
if regArgs.NoInteractive {
|
if regArgs.NoInteractive {
|
||||||
if err := registerNoInteractive(envFile, regArgs); err != nil {
|
if err := registerNoInteractive(*configFile, regArgs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
if err := registerInteractive(envFile); err != nil {
|
if err := registerInteractive(*configFile); err != nil {
|
||||||
// log.Errorln(err)
|
log.Fatal(err)
|
||||||
os.Exit(2)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
@ -74,7 +72,6 @@ func runRegister(ctx context.Context, regArgs *registerArgs, envFile string) fun
|
||||||
type registerArgs struct {
|
type registerArgs struct {
|
||||||
NoInteractive bool
|
NoInteractive bool
|
||||||
InstanceAddr string
|
InstanceAddr string
|
||||||
Insecure bool
|
|
||||||
Token string
|
Token string
|
||||||
RunnerName string
|
RunnerName string
|
||||||
Labels string
|
Labels string
|
||||||
|
@ -102,7 +99,6 @@ var defaultLabels = []string{
|
||||||
|
|
||||||
type registerInputs struct {
|
type registerInputs struct {
|
||||||
InstanceAddr string
|
InstanceAddr string
|
||||||
Insecure bool
|
|
||||||
Token string
|
Token string
|
||||||
RunnerName string
|
RunnerName string
|
||||||
CustomLabels []string
|
CustomLabels []string
|
||||||
|
@ -174,16 +170,17 @@ func (r *registerInputs) assignToNext(stage registerStage, value string) registe
|
||||||
return StageUnknown
|
return StageUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerInteractive(envFile string) error {
|
func registerInteractive(configFile string) error {
|
||||||
var (
|
var (
|
||||||
reader = bufio.NewReader(os.Stdin)
|
reader = bufio.NewReader(os.Stdin)
|
||||||
stage = StageInputInstance
|
stage = StageInputInstance
|
||||||
inputs = new(registerInputs)
|
inputs = new(registerInputs)
|
||||||
)
|
)
|
||||||
|
|
||||||
// check if overwrite local config
|
cfg, err := config.LoadDefault(configFile)
|
||||||
_ = godotenv.Load(envFile)
|
if err != nil {
|
||||||
cfg, _ := config.FromEnviron()
|
return fmt.Errorf("failed to load config: %v", err)
|
||||||
|
}
|
||||||
if f, err := os.Stat(cfg.Runner.File); err == nil && !f.IsDir() {
|
if f, err := os.Stat(cfg.Runner.File); err == nil && !f.IsDir() {
|
||||||
stage = StageOverwriteLocalConfig
|
stage = StageOverwriteLocalConfig
|
||||||
}
|
}
|
||||||
|
@ -199,7 +196,7 @@ func registerInteractive(envFile string) error {
|
||||||
|
|
||||||
if stage == StageWaitingForRegistration {
|
if stage == StageWaitingForRegistration {
|
||||||
log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.CustomLabels)
|
log.Infof("Registering runner, name=%s, instance=%s, labels=%v.", inputs.RunnerName, inputs.InstanceAddr, inputs.CustomLabels)
|
||||||
if err := doRegister(&cfg, inputs); err != nil {
|
if err := doRegister(cfg, inputs); err != nil {
|
||||||
log.Errorf("Failed to register runner: %v", err)
|
log.Errorf("Failed to register runner: %v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Runner registered successfully.")
|
log.Infof("Runner registered successfully.")
|
||||||
|
@ -236,12 +233,13 @@ func printStageHelp(stage registerStage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerNoInteractive(envFile string, regArgs *registerArgs) error {
|
func registerNoInteractive(configFile string, regArgs *registerArgs) error {
|
||||||
_ = godotenv.Load(envFile)
|
cfg, err := config.LoadDefault(configFile)
|
||||||
cfg, _ := config.FromEnviron()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
inputs := ®isterInputs{
|
inputs := ®isterInputs{
|
||||||
InstanceAddr: regArgs.InstanceAddr,
|
InstanceAddr: regArgs.InstanceAddr,
|
||||||
Insecure: regArgs.Insecure,
|
|
||||||
Token: regArgs.Token,
|
Token: regArgs.Token,
|
||||||
RunnerName: regArgs.RunnerName,
|
RunnerName: regArgs.RunnerName,
|
||||||
CustomLabels: defaultLabels,
|
CustomLabels: defaultLabels,
|
||||||
|
@ -258,7 +256,7 @@ func registerNoInteractive(envFile string, regArgs *registerArgs) error {
|
||||||
log.WithError(err).Errorf("Invalid input, please re-run act command.")
|
log.WithError(err).Errorf("Invalid input, please re-run act command.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := doRegister(&cfg, inputs); err != nil {
|
if err := doRegister(cfg, inputs); err != nil {
|
||||||
log.Errorf("Failed to register runner: %v", err)
|
log.Errorf("Failed to register runner: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -272,7 +270,7 @@ func doRegister(cfg *config.Config, inputs *registerInputs) error {
|
||||||
// initial http client
|
// initial http client
|
||||||
cli := client.New(
|
cli := client.New(
|
||||||
inputs.InstanceAddr,
|
inputs.InstanceAddr,
|
||||||
inputs.Insecure,
|
cfg.Runner.Insecure,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
version,
|
version,
|
||||||
|
@ -301,9 +299,36 @@ func doRegister(cfg *config.Config, inputs *registerInputs) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.Runner.Name = inputs.RunnerName
|
reg := &config.Registration{
|
||||||
cfg.Runner.Token = inputs.Token
|
Name: inputs.RunnerName,
|
||||||
cfg.Runner.Labels = inputs.CustomLabels
|
Token: inputs.Token,
|
||||||
_, err := register.New(cli).Register(ctx, cfg.Runner)
|
Address: inputs.InstanceAddr,
|
||||||
return err
|
Labels: inputs.CustomLabels,
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, len(reg.Labels))
|
||||||
|
for i, v := range reg.Labels {
|
||||||
|
l, _, _, _ := runtime.ParseLabel(v)
|
||||||
|
labels[i] = l
|
||||||
|
}
|
||||||
|
// register new runner.
|
||||||
|
resp, err := cli.Register(ctx, connect.NewRequest(&runnerv1.RegisterRequest{
|
||||||
|
Name: reg.Name,
|
||||||
|
Token: reg.Token,
|
||||||
|
AgentLabels: labels,
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("poller: cannot register new runner")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.ID = resp.Msg.Runner.Id
|
||||||
|
reg.UUID = resp.Msg.Runner.Uuid
|
||||||
|
reg.Name = resp.Msg.Runner.Name
|
||||||
|
reg.Token = resp.Msg.Runner.Token
|
||||||
|
|
||||||
|
if err := config.SaveRegistration(cfg.Runner.File, reg); err != nil {
|
||||||
|
return fmt.Errorf("failed to save runner config: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
38
config/config.example.yaml
Normal file
38
config/config.example.yaml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Example configuration file, it's safe to copy this as the default config file without any modification.
|
||||||
|
|
||||||
|
log:
|
||||||
|
# The level of logging, can be trace, debug, info, warn, error, fatal
|
||||||
|
level: info
|
||||||
|
|
||||||
|
runner:
|
||||||
|
# Where to store the registration result.
|
||||||
|
file: .runner
|
||||||
|
# Execute how many tasks concurrently at the same time.
|
||||||
|
capacity: 1
|
||||||
|
# Extra environment variables to run jobs.
|
||||||
|
envs:
|
||||||
|
A_TEST_ENV_NAME_1: a_test_env_value_1
|
||||||
|
A_TEST_ENV_NAME_2: a_test_env_value_2
|
||||||
|
# Extra environment variables to run jobs from a file.
|
||||||
|
# It will be ignored if it's empty or the file doesn't exist.
|
||||||
|
env_file: .env
|
||||||
|
# The timeout for a job to be finished.
|
||||||
|
# Please note that the Gitea instance also has a timeout (3h by default) for the job.
|
||||||
|
# So the job could be stopped by the Gitea instance if it's timeout is shorter than this.
|
||||||
|
timeout: 3h
|
||||||
|
# Whether skip verifying the TLS certificate of the Gitea instance.
|
||||||
|
insecure: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
# Enable cache server to use actions/cache.
|
||||||
|
enabled: true
|
||||||
|
# The directory to store the cache data.
|
||||||
|
# If it's empty, the cache data will be stored in $HOME/.cache/actcache.
|
||||||
|
dir: ""
|
||||||
|
# The host of the cache server.
|
||||||
|
# It's not for the address to listen, but the address to connect from job containers.
|
||||||
|
# So 0.0.0.0 is a bad choice, leave it empty to detect automatically.
|
||||||
|
host: ""
|
||||||
|
# The port of the cache server.
|
||||||
|
# 0 means to use a random available port.
|
||||||
|
port: 0
|
169
config/config.go
169
config/config.go
|
@ -1,115 +1,84 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"path/filepath"
|
||||||
"strconv"
|
"time"
|
||||||
|
|
||||||
"codeberg.org/forgejo/runner/core"
|
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/kelseyhightower/envconfig"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type Config struct {
|
||||||
// Config provides the system configuration.
|
Log struct {
|
||||||
Config struct {
|
Level string `yaml:"level"`
|
||||||
Debug bool `envconfig:"GITEA_DEBUG"`
|
} `yaml:"log"`
|
||||||
Trace bool `envconfig:"GITEA_TRACE"`
|
|
||||||
Client Client
|
|
||||||
Runner Runner
|
|
||||||
Platform Platform
|
|
||||||
}
|
|
||||||
|
|
||||||
Client struct {
|
|
||||||
Address string `ignored:"true"`
|
|
||||||
Insecure bool
|
|
||||||
}
|
|
||||||
|
|
||||||
Runner struct {
|
Runner struct {
|
||||||
UUID string `ignored:"true"`
|
File string `yaml:"file"`
|
||||||
Name string `envconfig:"GITEA_RUNNER_NAME"`
|
Capacity int `yaml:"capacity"`
|
||||||
Token string `ignored:"true"`
|
Envs map[string]string `yaml:"envs"`
|
||||||
Capacity int `envconfig:"GITEA_RUNNER_CAPACITY" default:"1"`
|
EnvFile string `yaml:"env_file"`
|
||||||
File string `envconfig:"FORGEJO_RUNNER_FILE" default:".runner"`
|
Timeout time.Duration `yaml:"timeout"`
|
||||||
Environ map[string]string `envconfig:"GITEA_RUNNER_ENVIRON"`
|
Insecure bool `yaml:"insecure"`
|
||||||
EnvFile string `envconfig:"GITEA_RUNNER_ENV_FILE"`
|
} `yaml:"runner"`
|
||||||
Labels []string `envconfig:"GITEA_RUNNER_LABELS"`
|
Cache struct {
|
||||||
}
|
Enabled *bool `yaml:"enabled"` // pointer to distinguish between false and not set, and it will be true if not set
|
||||||
|
Dir string `yaml:"dir"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port uint16 `yaml:"port"`
|
||||||
|
} `yaml:"cache"`
|
||||||
|
}
|
||||||
|
|
||||||
Platform struct {
|
// LoadDefault returns the default configuration.
|
||||||
OS string `envconfig:"GITEA_PLATFORM_OS"`
|
// If file is not empty, it will be used to load the configuration.
|
||||||
Arch string `envconfig:"GITEA_PLATFORM_ARCH"`
|
func LoadDefault(file string) (*Config, error) {
|
||||||
}
|
cfg := &Config{}
|
||||||
)
|
if file != "" {
|
||||||
|
f, err := os.Open(file)
|
||||||
// FromEnviron returns the settings from the environment.
|
|
||||||
func FromEnviron() (Config, error) {
|
|
||||||
cfg := Config{}
|
|
||||||
if err := envconfig.Process("", &cfg); err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check runner config exist
|
|
||||||
f, err := os.Stat(cfg.Runner.File)
|
|
||||||
if err == nil && !f.IsDir() {
|
|
||||||
jsonFile, _ := os.Open(cfg.Runner.File)
|
|
||||||
defer jsonFile.Close()
|
|
||||||
byteValue, _ := io.ReadAll(jsonFile)
|
|
||||||
var runner core.Runner
|
|
||||||
if err := json.Unmarshal(byteValue, &runner); err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
if runner.UUID != "" {
|
|
||||||
cfg.Runner.UUID = runner.UUID
|
|
||||||
}
|
|
||||||
if runner.Name != "" {
|
|
||||||
cfg.Runner.Name = runner.Name
|
|
||||||
}
|
|
||||||
if runner.Token != "" {
|
|
||||||
cfg.Runner.Token = runner.Token
|
|
||||||
}
|
|
||||||
if len(runner.Labels) != 0 {
|
|
||||||
cfg.Runner.Labels = runner.Labels
|
|
||||||
}
|
|
||||||
if runner.Address != "" {
|
|
||||||
cfg.Client.Address = runner.Address
|
|
||||||
}
|
|
||||||
if runner.Insecure != "" {
|
|
||||||
cfg.Client.Insecure, _ = strconv.ParseBool(runner.Insecure)
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// runner config
|
|
||||||
if cfg.Runner.Environ == nil {
|
|
||||||
cfg.Runner.Environ = map[string]string{
|
|
||||||
"GITHUB_API_URL": cfg.Client.Address + "/api/v1",
|
|
||||||
"GITHUB_SERVER_URL": cfg.Client.Address,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cfg.Runner.Name == "" {
|
|
||||||
cfg.Runner.Name, _ = os.Hostname()
|
|
||||||
}
|
|
||||||
|
|
||||||
// platform config
|
|
||||||
if cfg.Platform.OS == "" {
|
|
||||||
cfg.Platform.OS = runtime.GOOS
|
|
||||||
}
|
|
||||||
if cfg.Platform.Arch == "" {
|
|
||||||
cfg.Platform.Arch = runtime.GOARCH
|
|
||||||
}
|
|
||||||
|
|
||||||
if file := cfg.Runner.EnvFile; file != "" {
|
|
||||||
envs, err := godotenv.Read(file)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cfg, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for k, v := range envs {
|
defer f.Close()
|
||||||
cfg.Runner.Environ[k] = v
|
decoder := yaml.NewDecoder(f)
|
||||||
|
if err := decoder.Decode(&cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compatibleWithOldEnvs(file != "", cfg)
|
||||||
|
|
||||||
|
if cfg.Runner.EnvFile != "" {
|
||||||
|
if stat, err := os.Stat(cfg.Runner.EnvFile); err == nil && !stat.IsDir() {
|
||||||
|
envs, err := godotenv.Read(cfg.Runner.EnvFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read env file %q: %w", cfg.Runner.EnvFile, err)
|
||||||
|
}
|
||||||
|
for k, v := range envs {
|
||||||
|
cfg.Runner.Envs[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Log.Level == "" {
|
||||||
|
cfg.Log.Level = "info"
|
||||||
|
}
|
||||||
|
if cfg.Runner.File == "" {
|
||||||
|
cfg.Runner.File = ".runner"
|
||||||
|
}
|
||||||
|
if cfg.Runner.Capacity <= 0 {
|
||||||
|
cfg.Runner.Capacity = 1
|
||||||
|
}
|
||||||
|
if cfg.Runner.Timeout <= 0 {
|
||||||
|
cfg.Runner.Timeout = 3 * time.Hour
|
||||||
|
}
|
||||||
|
if cfg.Cache.Enabled == nil {
|
||||||
|
b := true
|
||||||
|
cfg.Cache.Enabled = &b
|
||||||
|
}
|
||||||
|
if *cfg.Cache.Enabled {
|
||||||
|
if cfg.Cache.Dir == "" {
|
||||||
|
home, _ := os.UserHomeDir()
|
||||||
|
cfg.Cache.Dir = filepath.Join(home, ".cache", "actcache")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
62
config/deprecated.go
Normal file
62
config/deprecated.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: could be removed in the future. TODO: remove it when Gitea 1.20.0 is released.
|
||||||
|
// Be compatible with old envs.
|
||||||
|
func compatibleWithOldEnvs(fileUsed bool, cfg *Config) {
|
||||||
|
handleEnv := func(key string) (string, bool) {
|
||||||
|
if v, ok := os.LookupEnv(key); ok {
|
||||||
|
if fileUsed {
|
||||||
|
log.Warnf("env %s has been ignored because config file is used", key)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
log.Warnf("env %s will be deprecated, please use config file instead", key)
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := handleEnv("GITEA_DEBUG"); ok {
|
||||||
|
if b, _ := strconv.ParseBool(v); b {
|
||||||
|
cfg.Log.Level = "debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := handleEnv("GITEA_TRACE"); ok {
|
||||||
|
if b, _ := strconv.ParseBool(v); b {
|
||||||
|
cfg.Log.Level = "trace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := handleEnv("GITEA_RUNNER_CAPACITY"); ok {
|
||||||
|
if i, _ := strconv.Atoi(v); i > 0 {
|
||||||
|
cfg.Runner.Capacity = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := handleEnv("GITEA_RUNNER_FILE"); ok {
|
||||||
|
cfg.Runner.File = v
|
||||||
|
}
|
||||||
|
if v, ok := handleEnv("GITEA_RUNNER_ENVIRON"); ok {
|
||||||
|
splits := strings.Split(v, ",")
|
||||||
|
if cfg.Runner.Envs == nil {
|
||||||
|
cfg.Runner.Envs = map[string]string{}
|
||||||
|
}
|
||||||
|
for _, split := range splits {
|
||||||
|
kv := strings.SplitN(split, ":", 2)
|
||||||
|
if len(kv) == 2 && kv[0] != "" {
|
||||||
|
cfg.Runner.Envs[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := handleEnv("GITEA_RUNNER_ENV_FILE"); ok {
|
||||||
|
cfg.Runner.EnvFile = v
|
||||||
|
}
|
||||||
|
}
|
9
config/embed.go
Normal file
9
config/embed.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed config.example.yaml
|
||||||
|
var Example []byte
|
54
config/registration.go
Normal file
54
config/registration.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const registrationWarning = "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner."
|
||||||
|
|
||||||
|
// Registration is the registration information for a runner
|
||||||
|
type Registration struct {
|
||||||
|
Warning string `json:"WARNING"` // Warning message to display, it's always the registrationWarning constant
|
||||||
|
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadRegistration(file string) (*Registration, error) {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var reg Registration
|
||||||
|
if err := json.NewDecoder(f).Decode(®); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reg.Warning = ""
|
||||||
|
|
||||||
|
return ®, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveRegistration(file string, reg *Registration) error {
|
||||||
|
f, err := os.Create(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
reg.Warning = registrationWarning
|
||||||
|
|
||||||
|
enc := json.NewEncoder(f)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
return enc.Encode(reg)
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
const (
|
|
||||||
UUIDHeader = "x-runner-uuid"
|
|
||||||
TokenHeader = "x-runner-token"
|
|
||||||
VersionHeader = "x-runner-version"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Runner struct
|
|
||||||
type Runner struct {
|
|
||||||
ID int64 `json:"id"`
|
|
||||||
UUID string `json:"uuid"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
Address string `json:"address"`
|
|
||||||
Insecure string `json:"insecure"`
|
|
||||||
Labels []string `json:"labels"`
|
|
||||||
}
|
|
3
go.mod
3
go.mod
|
@ -11,7 +11,6 @@ require (
|
||||||
github.com/go-chi/chi/v5 v5.0.8
|
github.com/go-chi/chi/v5 v5.0.8
|
||||||
github.com/go-chi/render v1.0.2
|
github.com/go-chi/render v1.0.2
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
|
||||||
github.com/mattn/go-isatty v0.0.17
|
github.com/mattn/go-isatty v0.0.17
|
||||||
github.com/nektos/act v0.0.0
|
github.com/nektos/act v0.0.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
|
@ -19,6 +18,7 @@ require (
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/term v0.6.0
|
golang.org/x/term v0.6.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
modernc.org/sqlite v1.14.2
|
modernc.org/sqlite v1.14.2
|
||||||
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978
|
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978
|
||||||
xorm.io/xorm v1.3.2
|
xorm.io/xorm v1.3.2
|
||||||
|
@ -91,7 +91,6 @@ require (
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
lukechampine.com/uint128 v1.1.1 // indirect
|
lukechampine.com/uint128 v1.1.1 // indirect
|
||||||
modernc.org/cc/v3 v3.35.18 // indirect
|
modernc.org/cc/v3 v3.35.18 // indirect
|
||||||
modernc.org/ccgo/v3 v3.12.82 // indirect
|
modernc.org/ccgo/v3 v3.12.82 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -302,8 +302,6 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
|
||||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
|
|
|
@ -7,22 +7,23 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||||
"codeberg.org/forgejo/runner/client"
|
|
||||||
|
|
||||||
"github.com/bufbuild/connect-go"
|
"github.com/bufbuild/connect-go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"codeberg.org/forgejo/runner/client"
|
||||||
|
"codeberg.org/forgejo/runner/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrDataLock = errors.New("Data Lock Error")
|
var ErrDataLock = errors.New("Data Lock Error")
|
||||||
|
|
||||||
func New(cli client.Client, dispatch func(context.Context, *runnerv1.Task) error, workerNum int) *Poller {
|
func New(cli client.Client, dispatch func(context.Context, *runnerv1.Task) error, cfg *config.Config) *Poller {
|
||||||
return &Poller{
|
return &Poller{
|
||||||
Client: cli,
|
Client: cli,
|
||||||
Dispatch: dispatch,
|
Dispatch: dispatch,
|
||||||
routineGroup: newRoutineGroup(),
|
routineGroup: newRoutineGroup(),
|
||||||
metric: &metric{},
|
metric: &metric{},
|
||||||
workerNum: workerNum,
|
|
||||||
ready: make(chan struct{}, 1),
|
ready: make(chan struct{}, 1),
|
||||||
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +35,13 @@ type Poller struct {
|
||||||
routineGroup *routineGroup
|
routineGroup *routineGroup
|
||||||
metric *metric
|
metric *metric
|
||||||
ready chan struct{}
|
ready chan struct{}
|
||||||
workerNum int
|
cfg *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Poller) schedule() {
|
func (p *Poller) schedule() {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
if int(p.metric.BusyWorkers()) >= p.workerNum {
|
if int(p.metric.BusyWorkers()) >= p.cfg.Runner.Capacity {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +149,7 @@ func (p *Poller) dispatchTask(ctx context.Context, task *runnerv1.Task) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
runCtx, cancel := context.WithTimeout(ctx, time.Hour)
|
runCtx, cancel := context.WithTimeout(ctx, p.cfg.Runner.Timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
return p.Dispatch(runCtx, task)
|
return p.Dispatch(runCtx, task)
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
package register
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
|
||||||
"codeberg.org/forgejo/runner/client"
|
|
||||||
"codeberg.org/forgejo/runner/config"
|
|
||||||
"codeberg.org/forgejo/runner/core"
|
|
||||||
|
|
||||||
"github.com/bufbuild/connect-go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func New(cli client.Client) *Register {
|
|
||||||
return &Register{
|
|
||||||
Client: cli,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Register struct {
|
|
||||||
Client client.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Register) Register(ctx context.Context, cfg config.Runner) (*core.Runner, error) {
|
|
||||||
labels := make([]string, len(cfg.Labels))
|
|
||||||
for i, v := range cfg.Labels {
|
|
||||||
labels[i] = strings.SplitN(v, ":", 2)[0]
|
|
||||||
}
|
|
||||||
// register new runner.
|
|
||||||
resp, err := p.Client.Register(ctx, connect.NewRequest(&runnerv1.RegisterRequest{
|
|
||||||
Name: cfg.Name,
|
|
||||||
Token: cfg.Token,
|
|
||||||
AgentLabels: labels,
|
|
||||||
}))
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("poller: cannot register new runner")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
data := &core.Runner{
|
|
||||||
ID: resp.Msg.Runner.Id,
|
|
||||||
UUID: resp.Msg.Runner.Uuid,
|
|
||||||
Name: resp.Msg.Runner.Name,
|
|
||||||
Token: resp.Msg.Runner.Token,
|
|
||||||
Address: p.Client.Address(),
|
|
||||||
Insecure: strconv.FormatBool(p.Client.Insecure()),
|
|
||||||
Labels: cfg.Labels,
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := json.MarshalIndent(data, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Error("poller: cannot marshal the json input")
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// store runner config in .runner file
|
|
||||||
return data, os.WriteFile(cfg.File, file, 0o644)
|
|
||||||
}
|
|
Loading…
Reference in a new issue