diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..4c6160f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake ./dev diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/new-release.yml b/.github/workflows/new-release.yml new file mode 100644 index 0000000..cef7065 --- /dev/null +++ b/.github/workflows/new-release.yml @@ -0,0 +1,13 @@ +name: Release new version +on: + release: + types: [released] +permissions: + contents: write +jobs: + update-major-tag: + runs-on: ubuntu-latest + steps: + - uses: actions/publish-action@v0.3.0 + with: + source-tag: ${{ github.event.release.tag_name }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4cc39dc --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,9 @@ +on: + pull_request: +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@V27 + - run: ./test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..334233f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/examples/*/flake.lock +/.direnv/ diff --git a/OPTIONS.md b/OPTIONS.md new file mode 100644 index 0000000..e02d630 --- /dev/null +++ b/OPTIONS.md @@ -0,0 +1,90 @@ +# Options + +These are the options currently declared by `gcloud-nix`. + +## Basic options + +### `gcloud.enable` + +If true, the Google Cloud SDK and any defined `extra-components` are added to the environment. + +Default value: false + +### `gcloud.extra-components` + +An array of IDs of SDK components to add to the environment, if `gcloud.enable` is true. Run `gcloud components list` to see the list of available components and their IDs. + +Default value: `[]` + +## Configuration options + +These options deal with *named configurations*. For more information, run `gcloud topics configuration`. + +### `gcloud.properties` + +An attribute set of attribute sets, whose values are either non-empty strings, `true`, or `false`. Each nested attribute represents a configuration property to set in the enironment, with the top-level attribute names being the section. + +For example, the option declaration `gcloud.properties.core.project = "myproject";` will add the environment variable `CLOUDSDK_CORE_PROJECT=myproject` to the environment, causing the property `core/project` to be set to `myproject` for all commands, while the declaration `gcloud.properties.context-aware.use-client-certificate = true;` will create the variable `CLOUDSDK_CONTEXT_AWARE_USE_CLIENT_CERTIFICATE=1` which will set the property `context_aware/use_client_certificate` to `True`. + +NOTE: section and property names may include the underscore character `_`. Corresponding option names may instead use include the dash character `-`. + +Default value: empty + +### `gcloud.config-directory` + +A non-empty string representing the path to the directory in which to store configuration for the Google Cloud SDK's when it is invoked in the environment. If the option is defined, the environment variable `CLOUDSDK_CONFIG` is defined in the environment with the same value. + +If no directory exists at that path, it will be created. If the directory exists but the user does not have write permissions on it, the Google Cloud SDK may not work correctly. + +Default value: not defined + +### `gcloud.active-configuration-name` + +A non-empty string representing the name of the configuration to use in the environment. + +Default value: not defined + +## Node.js + +[https://nodejs.org/en](https://nodejs.org/en) + +### javascript.node.corepack-shims + +An array of strings which, if `javascript.node.enable` is true, are passed to `corepack enable`, creating [corepack](https://nodejs.org/api/corepack.html) package manager shims. + +Currently, supported values are `"yarn"`, `"pnpm"`, and `"npm"`. + +Default value: `[]` + +### javascript.node.enable + +Whether to add the Node.js runtime to the environment. + +Default value: false + +### javascript.node.env + +If set, then the `NODE_ENV` variable is set to this value in the environment. The only valid values are `"production"`, `"development"`, and `"test"`. + +Default value: unset + +### javascript.node.package + +If `javascript.volta.enable` is true, this package is added to the environment. There are [several alternative packages in nixpkgs](https://search.nixos.org/packages?from=0&size=50&sort=relevance&type=packages&query=nodejs-), such as `nodejs-slim` (which lacks npm), or `nodejs_22` (the latest version in the 22.x line). + +Default value: `pkgs.nodejs` + +## Volta + +[The Hassle-Free JavaScript Tool Manager](https://volta.sh/) + +### javascript.volta.enable + +Whether to add volta to the environment. + +Default value: false +### javascript.volta.home + +A non-empty string. If `javascript.volta.enable` is true, the `VOLTA_HOME` variable is set to this value in the environment. + +Default value: `"$HOME/.volta"` diff --git a/README.md b/README.md new file mode 100644 index 0000000..9908645 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# gcloud-nix + +A [make-shell](https://github.com/nicknovitski/make-shell) module for the Google Cloud SDK, or `gcloud` CLI tool. + +## Why? + +`make-shell` is a modular interface for making nix derivations intended for use by the `nix develop` command. It has it's own lengthy [WHY document](https://github.com/nicknovitski/make-shell/blob/main/WHY.md). + +The Google Cloud SDK nix package has a + + +This is a module for `make-shell` that lets you + +I work on projects which attempts to explain how it might be useful, but I thought that creating this repository might also give an indirect explanation. + +## Installation + +First make sure that you've [installed `make-shell` in your flake](https://github.com/nicknovitski/make-shell/tree/main?tab=readme-ov-file#installation). + +Then, add `gcloud` to your flake inputs. Maybe they'd look like this: +```nix + inputs = { + gcloud-nix.url = "github:nicknovitski/gcloud-nix"; + make-shell.url = "github:nicknovitski/make-shell"; + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; +``` + +Then, add the shell module to your shell's imports. It'll look something like this: +```nix +devShells.default = pkgs.make-shell = { + imports = [inputs.gcloud-nix.shellModules.default]; # add option declarations... + javascript.gcloud.enable = true; # ...and definitions! +} +# or, if you're using flake-parts... +make-shell.imports = [gcloud-nix.shellModules.default]; # shared imports for all `make-shells` attributes +make-shells.default = {gcloud.enable = true;}; +``` + +## Usage + +The options this module declares are documented [the OPTIONS.md file](OPTIONS.md). Although if you know even a little nix, I bet you can read the declarations directly from [the module itself](shell-modules/default.nix) directory. + +## Examples + +See [example/flake.nix](example/flake.nix). diff --git a/dev/flake.lock b/dev/flake.lock new file mode 100644 index 0000000..5f712af --- /dev/null +++ b/dev/flake.lock @@ -0,0 +1,74 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1719994518, + "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "make-shell": { + "locked": { + "lastModified": 1720292412, + "narHash": "sha256-g2+GzvbEBHABtebIYVjoqlzjr2bRe+7LSepeptKhe9M=", + "owner": "nicknovitski", + "repo": "make-shell", + "rev": "e7d523d93f7b7e10758659fdec2e35265b58d7c6", + "type": "github" + }, + "original": { + "owner": "nicknovitski", + "repo": "make-shell", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1720031269, + "narHash": "sha256-rwz8NJZV+387rnWpTYcXaRNvzUSnnF9aHONoJIYmiUQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1719876945, + "narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "make-shell": "make-shell", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/dev/flake.nix b/dev/flake.nix new file mode 100644 index 0000000..113aa92 --- /dev/null +++ b/dev/flake.nix @@ -0,0 +1,28 @@ +{ + inputs = { + flake-parts.url = "github:hercules-ci/flake-parts"; + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + make-shell.url = "github:nicknovitski/make-shell"; + }; + + outputs = + inputs@{ flake-parts, make-shell, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + ]; + imports = [ make-shell.flakeModules.default ]; + perSystem = + { ... }: + { + make-shells.default = + { pkgs, ... }: + { + packages = [ pkgs.nixfmt-rfc-style ]; + }; + }; + }; +} diff --git a/example/flake.nix b/example/flake.nix new file mode 100644 index 0000000..c4c6bfe --- /dev/null +++ b/example/flake.nix @@ -0,0 +1,44 @@ +{ + description = "An example usage of the gcloud-nix shell modules, with flake-parts"; + + inputs = { + flake-parts.url = "github:hercules-ci/flake-parts"; + gcloud-nix.url = "github:nicknovitski/gcloud-nix"; + make-shell.url = "github:nicknovitski/make-shell"; + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = + inputs@{ + flake-parts, + make-shell, + gcloud-nix, + ... + }: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + "aarch64-linux" + "aarch64-darwin" + "x86_64-darwin" + ]; + imports = [ make-shell.flakeModules.default ]; + perSystem = + { ... }: + { + make-shell.imports = [ gcloud-nix.shellModules.default ]; + make-shells.default = + { pkgs, ... }: + { + gcloud = { + enable = true; + extra-components = [ + "gke-gcloud-auth-plugin" + "kubectl" + ]; + properties.core.project = "my-gcp-project"; + }; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6bc4c76 --- /dev/null +++ b/flake.lock @@ -0,0 +1,23 @@ +{ + "nodes": { + "releases": { + "flake": false, + "locked": { + "narHash": "sha256-n7PXmGMESYnNh9MbTthokoEtwXinRvn0+07r90MImNU=", + "type": "file", + "url": "https://nodejs.org/download/release/index.json" + }, + "original": { + "type": "file", + "url": "https://nodejs.org/download/release/index.json" + } + }, + "root": { + "inputs": { + "releases": "releases" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..eac4b58 --- /dev/null +++ b/flake.nix @@ -0,0 +1,9 @@ +{ + description = ""; + + outputs = _: { + shellModules.default = { + imports = [ ./shell-modules ]; + }; + }; +} diff --git a/shell-modules/default.nix b/shell-modules/default.nix new file mode 100644 index 0000000..5a4042d --- /dev/null +++ b/shell-modules/default.nix @@ -0,0 +1,89 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) + filterAttrs + mkEnableOption + mkIf + mkOption + types + ; + inherit (lib.attrsets) attrNames mapAttrs' concatMapAttrs; + cfg = config.gcloud; +in +{ + options.gcloud = { + enable = mkEnableOption "Install the Google Cloud SDK"; + extra-components = mkOption { + description = "IDs of Google Cloud SDK components to install"; + default = [ ]; + example = [ "gke-gcloud-auth-plugin" ]; + type = types.listOf (types.enum (attrNames pkgs.google-cloud-sdk.components)); + }; + package = mkOption { + readOnly = true; + internal = true; + type = types.package; + default = + if cfg.extra-components == [ ] then + pkgs.google-cloud-sdk + else + pkgs.google-cloud-sdk.withExtraComponents ( + builtins.map (c: pkgs.google-cloud-sdk.components.${c}) cfg.extra-components + ); + }; + config-directory = mkOption { + default = null; + description = "Path to the config directory"; + type = types.nullOr types.nonEmptyStr; + }; + active-configuration-name = mkOption { + default = null; + description = "The name of the configuration to use in the shell"; + type = types.nullOr types.nonEmptyStr; + }; + properties = mkOption { + default = { }; + description = "k"; + type = types.attrsOf ( + types.attrsOf ( + types.oneOf [ + types.nonEmptyStr + types.bool + ] + ) + ); + }; + configEnvVars = mkOption { + readOnly = true; + internal = true; + type = types.attrsOf types.nonEmptyStr; + default = + let + toEnvName = s: builtins.replaceStrings [ "-" ] [ "_" ] (lib.strings.toUpper s); + sectionsToEnv = + section: + mapAttrs' ( + property: value: { + inherit value; + name = "CLOUDSDK_${toEnvName section}_${toEnvName property}"; + } + ); + in + concatMapAttrs sectionsToEnv cfg.properties; + }; + }; + config = lib.mkIf cfg.enable { + packages = [ cfg.package ]; + env = + (filterAttrs (_: v: v != null) { + CLOUDSDK_CONFIG = cfg.config-directory; + CLOUDSDK_ACTIVE_CONFIG_NAME = cfg.active-configuration-name; + }) + // cfg.configEnvVars; + }; +} diff --git a/test b/test new file mode 100755 index 0000000..da9f7fe --- /dev/null +++ b/test @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +nix flake check --override-input gcloud-nix ./. ./example