diff options
author | Sandro <sandro.jaeckel@gmail.com> | 2023-07-17 21:46:12 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-17 21:46:12 +0200 |
commit | 61dfd95022a48fc2b8518bcdbf1a96503b07da94 (patch) | |
tree | 4e18eacda1abb2119c54d6e2dda50fcb7afcab05 /nixos/modules/services/continuous-integration | |
parent | a6aae555e2ae851ccf86c2b18df693258b0a35de (diff) | |
parent | 75f419f222d5458a8a946dd137f7657b57ec4776 (diff) |
Merge pull request #236778 from awakesecurity/jsoo1/buildkite-agent-hooks
nixos/buildkite-agents: simplify service definition
Diffstat (limited to 'nixos/modules/services/continuous-integration')
-rw-r--r-- | nixos/modules/services/continuous-integration/buildkite-agents.nix | 261 |
1 files changed, 100 insertions, 161 deletions
diff --git a/nixos/modules/services/continuous-integration/buildkite-agents.nix b/nixos/modules/services/continuous-integration/buildkite-agents.nix index a40b939a16c74..a35ca4168074f 100644 --- a/nixos/modules/services/continuous-integration/buildkite-agents.nix +++ b/nixos/modules/services/continuous-integration/buildkite-agents.nix @@ -1,64 +1,49 @@ { config, lib, pkgs, ... }: -with lib; - let cfg = config.services.buildkite-agents; - mkHookOption = { name, description, example ? null }: { - inherit name; - value = mkOption { - default = null; - description = lib.mdDoc description; - type = types.nullOr types.lines; - } // (lib.optionalAttrs (example != null) { inherit example; }); - }; - mkHookOptions = hooks: listToAttrs (map mkHookOption hooks); - - hooksDir = cfg: let - mkHookEntry = name: value: '' - cat > $out/${name} <<'EOF' - #! ${pkgs.runtimeShell} - set -e - ${value} - EOF - chmod 755 $out/${name} + hooksDir = hooks: + let + mkHookEntry = name: text: '' + ln --symbolic ${pkgs.writeShellApplication { inherit name text; }}/bin/${name} $out/${name} + ''; + in + pkgs.runCommandLocal "buildkite-agent-hooks" { } '' + mkdir $out + ${lib.concatStringsSep "\n" (lib.mapAttrsToList mkHookEntry hooks)} ''; - in pkgs.runCommand "buildkite-agent-hooks" { preferLocalBuild = true; } '' - mkdir $out - ${concatStringsSep "\n" (mapAttrsToList mkHookEntry (filterAttrs (n: v: v != null) cfg.hooks))} - ''; buildkiteOptions = { name ? "", config, ... }: { options = { - enable = mkOption { + enable = lib.mkOption { default = true; - type = types.bool; + type = lib.types.bool; description = lib.mdDoc "Whether to enable this buildkite agent"; }; - package = mkOption { + package = lib.mkOption { default = pkgs.buildkite-agent; - defaultText = literalExpression "pkgs.buildkite-agent"; + defaultText = lib.literalExpression "pkgs.buildkite-agent"; description = lib.mdDoc "Which buildkite-agent derivation to use"; - type = types.package; + type = lib.types.package; }; - dataDir = mkOption { + dataDir = lib.mkOption { default = "/var/lib/buildkite-agent-${name}"; description = lib.mdDoc "The workdir for the agent"; - type = types.str; + type = lib.types.str; }; - runtimePackages = mkOption { + runtimePackages = lib.mkOption { default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]; - defaultText = literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]"; + defaultText = lib.literalExpression "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]"; description = lib.mdDoc "Add programs to the buildkite-agent environment"; - type = types.listOf types.package; + type = lib.types.listOf lib.types.package; }; - tokenPath = mkOption { - type = types.path; + tokenPath = lib.mkOption { + type = lib.types.path; description = lib.mdDoc '' The token from your Buildkite "Agents" page. @@ -67,25 +52,25 @@ let ''; }; - name = mkOption { - type = types.str; + name = lib.mkOption { + type = lib.types.str; default = "%hostname-${name}-%n"; description = lib.mdDoc '' The name of the agent as seen in the buildkite dashboard. ''; }; - tags = mkOption { - type = types.attrsOf (types.either types.str (types.listOf types.str)); - default = {}; - example = { queue = "default"; docker = "true"; ruby2 ="true"; }; + tags = lib.mkOption { + type = lib.types.attrsOf (lib.types.either lib.types.str (lib.types.listOf lib.types.str)); + default = { }; + example = { queue = "default"; docker = "true"; ruby2 = "true"; }; description = lib.mdDoc '' Tags for the agent. ''; }; - extraConfig = mkOption { - type = types.lines; + extraConfig = lib.mkOption { + type = lib.types.lines; default = ""; example = "debug=true"; description = lib.mdDoc '' @@ -93,8 +78,8 @@ let ''; }; - privateSshKeyPath = mkOption { - type = types.nullOr types.path; + privateSshKeyPath = lib.mkOption { + type = lib.types.nullOr lib.types.path; default = null; ## maximum care is taken so that secrets (ssh keys and the CI token) ## don't end up in the Nix store. @@ -108,67 +93,25 @@ let ''; }; - hooks = mkHookOptions [ - { name = "checkout"; - description = '' - The `checkout` hook script will replace the default checkout routine of the - bootstrap.sh script. You can use this hook to do your own SCM checkout - behaviour - ''; } - { name = "command"; - description = '' - The `command` hook script will replace the default implementation of running - the build command. - ''; } - { name = "environment"; - description = '' - The `environment` hook will run before all other commands, and can be used - to set up secrets, data, etc. Anything exported in hooks will be available - to the build script. - - Note: the contents of this file will be copied to the world-readable - Nix store. - ''; - example = '' - export SECRET_VAR=`head -1 /run/keys/secret` - ''; } - { name = "post-artifact"; - description = '' - The `post-artifact` hook will run just after artifacts are uploaded - ''; } - { name = "post-checkout"; - description = '' - The `post-checkout` hook will run after the bootstrap script has checked out - your projects source code. - ''; } - { name = "post-command"; - description = '' - The `post-command` hook will run after the bootstrap script has run your - build commands - ''; } - { name = "pre-artifact"; - description = '' - The `pre-artifact` hook will run just before artifacts are uploaded - ''; } - { name = "pre-checkout"; - description = '' - The `pre-checkout` hook will run just before your projects source code is - checked out from your SCM provider - ''; } - { name = "pre-command"; - description = '' - The `pre-command` hook will run just before your build command runs - ''; } - { name = "pre-exit"; - description = '' - The `pre-exit` hook will run just before your build job finishes - ''; } - ]; + hooks = lib.mkOption { + type = lib.types.attrsOf lib.types.lines; + default = { }; + example = lib.literalExpression '' + { + environment = ''' + export SECRET_VAR=`head -1 /run/keys/secret` + '''; + }''; + description = lib.mdDoc '' + "Agent" hooks to install. + See <https://buildkite.com/docs/agent/v3/hooks> for possible options. + ''; + }; - hooksPath = mkOption { - type = types.path; - default = hooksDir config; - defaultText = literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`"; + hooksPath = lib.mkOption { + type = lib.types.path; + default = hooksDir config.hooks; + defaultText = lib.literalMD "generated from {option}`services.buildkite-agents.<name>.hooks`"; description = lib.mdDoc '' Path to the directory storing the hooks. Consider using {option}`services.buildkite-agents.<name>.hooks.<name>` @@ -176,10 +119,10 @@ let ''; }; - shell = mkOption { - type = types.str; + shell = lib.mkOption { + type = lib.types.str; default = "${pkgs.bash}/bin/bash -e -c"; - defaultText = literalExpression ''"''${pkgs.bash}/bin/bash -e -c"''; + defaultText = lib.literalExpression ''"''${pkgs.bash}/bin/bash -e -c"''; description = lib.mdDoc '' Command that buildkite-agent 3 will execute when it spawns a shell. ''; @@ -190,9 +133,9 @@ let mapAgents = function: lib.mkMerge (lib.mapAttrsToList function enabledAgents); in { - options.services.buildkite-agents = mkOption { - type = types.attrsOf (types.submodule buildkiteOptions); - default = {}; + options.services.buildkite-agents = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule buildkiteOptions); + default = { }; description = lib.mdDoc '' Attribute set of buildkite agents. The attribute key is combined with the hostname and a unique integer to @@ -213,23 +156,24 @@ in }; }); config.users.groups = mapAgents (name: cfg: { - "buildkite-agent-${name}" = {}; + "buildkite-agent-${name}" = { }; }); config.systemd.services = mapAgents (name: cfg: { - "buildkite-agent-${name}" = - { description = "Buildkite Agent"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ]; - environment = config.networking.proxy.envVars // { - HOME = cfg.dataDir; - NIX_REMOTE = "daemon"; - }; + "buildkite-agent-${name}" = { + description = "Buildkite Agent"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = cfg.runtimePackages ++ [ cfg.package pkgs.coreutils ]; + environment = config.networking.proxy.envVars // { + HOME = cfg.dataDir; + NIX_REMOTE = "daemon"; + }; - ## NB: maximum care is taken so that secrets (ssh keys and the CI token) - ## don't end up in the Nix store. - preStart = let + ## NB: maximum care is taken so that secrets (ssh keys and the CI token) + ## don't end up in the Nix store. + preStart = + let sshDir = "${cfg.dataDir}/.ssh"; tagStr = name: value: if lib.isList value @@ -237,44 +181,39 @@ in else "${name}=${value}"; tagsStr = lib.concatStringsSep "," (lib.mapAttrsToList tagStr cfg.tags); in - optionalString (cfg.privateSshKeyPath != null) '' - mkdir -m 0700 -p "${sshDir}" - install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa" - '' + '' - cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF - token="$(cat ${toString cfg.tokenPath})" - name="${cfg.name}" - shell="${cfg.shell}" - tags="${tagsStr}" - build-path="${cfg.dataDir}/builds" - hooks-path="${cfg.hooksPath}" - ${cfg.extraConfig} - EOF - ''; + lib.optionalString (cfg.privateSshKeyPath != null) '' + mkdir -m 0700 -p "${sshDir}" + install -m600 "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa" + '' + '' + cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF + token="$(cat ${toString cfg.tokenPath})" + name="${cfg.name}" + shell="${cfg.shell}" + tags="${tagsStr}" + build-path="${cfg.dataDir}/builds" + hooks-path="${cfg.hooksPath}" + ${cfg.extraConfig} + EOF + ''; - serviceConfig = - { ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg"; - User = "buildkite-agent-${name}"; - RestartSec = 5; - Restart = "on-failure"; - TimeoutSec = 10; - # set a long timeout to give buildkite-agent a chance to finish current builds - TimeoutStopSec = "2 min"; - KillMode = "mixed"; - }; + serviceConfig = { + ExecStart = "${cfg.package}/bin/buildkite-agent start --config ${cfg.dataDir}/buildkite-agent.cfg"; + User = "buildkite-agent-${name}"; + RestartSec = 5; + Restart = "on-failure"; + TimeoutSec = 10; + # set a long timeout to give buildkite-agent a chance to finish current builds + TimeoutStopSec = "2 min"; + KillMode = "mixed"; }; + }; }); - config.assertions = mapAgents (name: cfg: [ - { assertion = cfg.hooksPath == (hooksDir cfg) || all (v: v == null) (attrValues cfg.hooks); - message = '' - Options `services.buildkite-agents.${name}.hooksPath' and - `services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive. - ''; - } - ]); - - imports = [ - (mkRemovedOptionModule [ "services" "buildkite-agent"] "services.buildkite-agent has been upgraded from version 2 to version 3 and moved to an attribute set at services.buildkite-agents. Please consult the 20.03 release notes for more information.") - ]; + config.assertions = mapAgents (name: cfg: [{ + assertion = cfg.hooksPath != hooksDir cfg.hooks -> cfg.hooks == { }; + message = '' + Options `services.buildkite-agents.${name}.hooksPath' and + `services.buildkite-agents.${name}.hooks.<name>' are mutually exclusive. + ''; + }]); } |