mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-08-06 00:44:22 +02:00
feat: config command to edit config files (#1140)
Changing a setting after `generate-config` meant hand-editing YAML, which is awkward in provisioning scripts. `config` now edits an existing file in place: ```bash ./gitea-runner -c config.yaml config set runner.capacity 4 ./gitea-runner -c config.yaml config set runner.envs.MY_VAR value ./gitea-runner -c config.yaml config add runner.labels 'ubuntu:docker://node:22' ./gitea-runner -c config.yaml config remove runner.labels 'ubuntu:docker://node:22' ./gitea-runner -c config.yaml config get runner.labels ``` Edits go through the YAML node tree, so comments, key order and the blank lines between top-level sections survive — a test asserts that appending a label to `config.example.yaml` changes nothing but the added line. Keys are resolved by reflecting over the `Config` struct's yaml tags, so an unknown key or a value of the wrong type is rejected before the file is touched. The write is atomic and keeps the file's symlink, owner, mode and line endings. --------- Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com> Co-authored-by: silverwind <me@silverwind.io> Reviewed-on: https://gitea.com/gitea/runner/pulls/1140 Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com> Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: bircni <bircni@icloud.com>
This commit is contained in:
116
internal/app/cmd/config.go
Normal file
116
internal/app/cmd/config.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func loadConfigCmd(configFile *string) *cobra.Command {
|
||||
configCmd := &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Generate, read and edit config files",
|
||||
Args: cobra.MaximumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return cmd.Help()
|
||||
},
|
||||
}
|
||||
|
||||
configCmd.AddCommand(loadGenerateConfigCmd("generate"))
|
||||
|
||||
configCmd.AddCommand(&cobra.Command{
|
||||
Use: "get <key>",
|
||||
Short: "Print the value of a config key",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
file, err := resolveConfigFile(cmd, configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value, err := config.GetValue(file, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(cmd.OutOrStdout(), value)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
for _, sub := range []struct {
|
||||
use string
|
||||
short string
|
||||
edit func(file, key string, values ...string) error
|
||||
}{
|
||||
{"set <key> <value>...", "Set the value of a config key", config.SetValue},
|
||||
{"add <key> <value>...", "Append values to a list config key", config.AddValue},
|
||||
{"remove <key> <value>...", "Remove values from a list config key", config.RemoveValue},
|
||||
} {
|
||||
valueCmd := &cobra.Command{
|
||||
Use: sub.use,
|
||||
Short: sub.short,
|
||||
Args: cobra.MinimumNArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
file, err := resolveConfigFile(cmd, configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sub.edit(file, args[0], args[1:]...)
|
||||
},
|
||||
}
|
||||
valueCmd.Flags().SetInterspersed(false) // so a value such as `--cpus 2` is not parsed as a flag
|
||||
configCmd.AddCommand(valueCmd)
|
||||
}
|
||||
|
||||
return configCmd
|
||||
}
|
||||
|
||||
func loadGenerateConfigCmd(use string) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: use,
|
||||
Short: "Generate an example config file",
|
||||
Args: cobra.MaximumNArgs(0),
|
||||
Run: func(cmd *cobra.Command, _ []string) {
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "%s", config.Example)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var defaultConfigFileNames = []string{"config.yaml", "config.yml"}
|
||||
|
||||
func resolveConfigFile(cmd *cobra.Command, configFile *string) (string, error) {
|
||||
if *configFile != "" {
|
||||
return *configFile, nil
|
||||
}
|
||||
|
||||
var dirs []string
|
||||
if wd, err := os.Getwd(); err == nil {
|
||||
dirs = append(dirs, wd)
|
||||
}
|
||||
if exe, err := os.Executable(); err == nil {
|
||||
if dir := filepath.Dir(exe); !slices.Contains(dirs, dir) {
|
||||
dirs = append(dirs, dir)
|
||||
}
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
for _, name := range defaultConfigFileNames {
|
||||
candidate := filepath.Join(dir, name)
|
||||
if stat, err := os.Stat(candidate); err == nil && !stat.IsDir() {
|
||||
fmt.Fprintf(cmd.ErrOrStderr(), "using config file %q\n", candidate)
|
||||
return candidate, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no %s found in %s, pass one with --config",
|
||||
strings.Join(defaultConfigFileNames, " or "), strings.Join(dirs, " or "))
|
||||
}
|
||||
Reference in New Issue
Block a user