kubepile 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,22 +13,13 @@ Kubepile lets you maintain individual, per-provider kubeconfigs in a
13
13
  `~/.config/kubepile` directory, and compile them into a single, merged
14
14
  kubeconfig.
15
15
 
16
- Each `*.yaml` file is a normal kubeconfig. You can paste in kubeconfigs from
17
- providers without converting them to a kubepile-specific schema. During
18
- `compile`, kubepile reads every file and merges its `clusters`, `users`, and
19
- `contexts` directly into the generated kubeconfig.
20
-
21
- Kubepile automatically ensures the following:
22
-
23
- - No kubepile files set a `current-context`.
24
- - No cluster, user, or context names clash.
16
+ Kubepile exposes a tiny set of commands to manage your configs:
25
17
 
26
- If a new file is added that clashes or sets a `current-context`, kubepile will
27
- intentionally fail compilation with a helpful message explaining which file
28
- broke the kubepile rules.
29
-
30
- Kubepile will never set a `current-context`, out of the design belief that
31
- `current-context` is a dangerous footgun in multi-cluster setups.
18
+ * [`compile`](#compile): compile your Kubepile configs into a merged kubeconfig
19
+ * [`source`](#source): set a specific context as your default for the current
20
+ shell
21
+ * [`split`](#split): decompile an existing, messy kubeconfig into nice, clean
22
+ Kubepile configs
32
23
 
33
24
  ## Install
34
25
 
@@ -45,6 +36,27 @@ kubepile install
45
36
  Once you've installed the shell helper, either start a new shell or re-source
46
37
  your `.zshrc`/`.bashrc`/`.profile`/etc. Kubepile supports Zsh, Bash, and Fish.
47
38
 
39
+ ## Kubepile configs
40
+
41
+ Each `*.yaml` file is a normal kubeconfig. You can paste in kubeconfigs from
42
+ providers without converting them to a kubepile-specific schema. During
43
+ `compile`, kubepile reads every file and merges its `clusters`, `users`, and
44
+ `contexts` directly into the generated kubeconfig.
45
+
46
+ Kubepile automatically ensures the following:
47
+
48
+ - No kubepile files set a `current-context`.
49
+ - No cluster, user, or context names clash.
50
+
51
+ If a new file is added that clashes or sets a `current-context`, kubepile will
52
+ intentionally fail compilation with a helpful message explaining which file
53
+ broke the kubepile rules.
54
+
55
+ Kubepile will never set a `current-context`, out of the design belief that
56
+ setting a global, cross-shell-session `current-context` is a dangerous footgun
57
+ in multi-cluster setups. Instead, use `kubepile source <context>` to
58
+ temporarily set a default context for your current shell session.
59
+
48
60
  ## Compile
49
61
 
50
62
  ```sh
@@ -86,6 +98,12 @@ kubepile source prod
86
98
  # All kubectl commands will use the prod context
87
99
  ```
88
100
 
101
+ To list available contexts:
102
+
103
+ ```sh
104
+ kubepile source --list
105
+ ```
106
+
89
107
  To switch to a different context, just run the `source` command with a new
90
108
  context:
91
109
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubepile",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Compile and split kubeconfig files from ~/.config/kubepile.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/kubepile.js",
package/dist/src/cli.js CHANGED
@@ -4,7 +4,7 @@ import { createInterface } from "node:readline/promises";
4
4
  import { Command } from "@commander-js/extra-typings";
5
5
  import packageJson from "../package.json" with { type: "json" };
6
6
  import { compileToKubeConfig, defaultKubeConfigPath, defaultKubepileDir, splitKubeConfigFile, } from "./kubepile.js";
7
- import { generateShellCommand, installShellIntegration, } from "./shell.js";
7
+ import { generateShellCommand, installShellIntegration, listSourceContextNames, } from "./shell.js";
8
8
  const program = createProgram();
9
9
  if (process.argv.slice(2).length === 0) {
10
10
  program.outputHelp();
@@ -100,8 +100,19 @@ Defaults:
100
100
  program
101
101
  .command("source")
102
102
  .description("Switch to one kube context in the current shell.")
103
- .argument("<context>", "context name to source")
104
- .action(() => {
103
+ .argument("[context]", "context name to source")
104
+ .option("--list", "list available contexts")
105
+ .action(async (context, options) => {
106
+ if (options.list) {
107
+ const contextNames = await listSourceContextNames();
108
+ if (contextNames.length > 0) {
109
+ process.stdout.write(`${contextNames.join("\n")}\n`);
110
+ }
111
+ return;
112
+ }
113
+ if (!context) {
114
+ throw new Error("kubepile source requires a context name or --list.");
115
+ }
105
116
  throw new Error("kubepile source requires shell integration. Run `kubepile install`, start a new shell, then run `kubepile source <context>`.");
106
117
  });
107
118
  return program;
@@ -9,6 +9,9 @@ export interface GenerateShellCommandResult {
9
9
  kubeConfigPath: string;
10
10
  shellCommand: string;
11
11
  }
12
+ export interface ListSourceContextNamesOptions {
13
+ sourcePath?: string;
14
+ }
12
15
  export interface InstallShellIntegrationOptions {
13
16
  shell?: ShellKind;
14
17
  rcFile?: string;
@@ -20,6 +23,7 @@ export interface InstallShellIntegrationResult {
20
23
  updated: boolean;
21
24
  }
22
25
  export declare function generateShellCommand(contextName: string, options?: GenerateShellCommandOptions): Promise<GenerateShellCommandResult>;
26
+ export declare function listSourceContextNames(options?: ListSourceContextNamesOptions): Promise<string[]>;
23
27
  export declare function installShellIntegration(options?: InstallShellIntegrationOptions): Promise<InstallShellIntegrationResult>;
24
28
  export declare function detectCurrentShell(shellPath?: string | undefined): ShellKind;
25
29
  export declare function shellRcFile(shell: ShellKind, homeDir?: string): Promise<string>;
package/dist/src/shell.js CHANGED
@@ -14,7 +14,7 @@ export async function generateShellCommand(contextName, options = {}) {
14
14
  };
15
15
  const tempRoot = await mkdtemp(path.join(options.tempDir ?? os.tmpdir(), "kubepile-source-"));
16
16
  const kubeConfigPath = path.join(tempRoot, "config");
17
- if (!sourceConfig.contexts?.some((context) => context.name === contextName)) {
17
+ if (!contextNamesFromConfig(sourceConfig, sourcePath).includes(contextName)) {
18
18
  throw new Error(`${sourcePath} does not contain context "${contextName}"`);
19
19
  }
20
20
  await writeFile(kubeConfigPath, serializeKubeConfig(contextConfig), {
@@ -28,6 +28,10 @@ export async function generateShellCommand(contextName, options = {}) {
28
28
  : posixSourceCommand(contextName, kubeConfigPath),
29
29
  };
30
30
  }
31
+ export async function listSourceContextNames(options = {}) {
32
+ const sourcePath = options.sourcePath ?? defaultKubeConfigPath();
33
+ return contextNamesFromConfig(await readKubeConfigFile(sourcePath), sourcePath);
34
+ }
31
35
  export async function installShellIntegration(options = {}) {
32
36
  const shell = options.shell ?? detectCurrentShell();
33
37
  const rcFile = options.rcFile ?? await shellRcFile(shell, options.homeDir ?? os.homedir());
@@ -85,6 +89,12 @@ function posixIntegrationFunction(shell) {
85
89
  return `kubepile() {
86
90
  if [ "$1" = "source" ]; then
87
91
  shift
92
+ for arg in "$@"; do
93
+ if [ "$arg" = "--list" ]; then
94
+ command \\kubepile source "$@"
95
+ return
96
+ fi
97
+ done
88
98
  eval "$(command \\kubepile generate-shell-command --shell ${shell} "$@")"
89
99
  else
90
100
  command \\kubepile "$@"
@@ -95,7 +105,11 @@ function fishIntegrationFunction() {
95
105
  return `function kubepile
96
106
  if test (count $argv) -gt 0; and test "$argv[1]" = "source"
97
107
  set -e argv[1]
98
- command kubepile generate-shell-command --shell fish $argv | source
108
+ if contains -- --list $argv
109
+ command kubepile source $argv
110
+ else
111
+ command kubepile generate-shell-command --shell fish $argv | source
112
+ end
99
113
  else
100
114
  command kubepile $argv
101
115
  end
@@ -156,6 +170,20 @@ async function firstExistingPath(filePaths) {
156
170
  }
157
171
  return undefined;
158
172
  }
173
+ function contextNamesFromConfig(config, sourcePath) {
174
+ if (config.contexts === undefined) {
175
+ return [];
176
+ }
177
+ if (!Array.isArray(config.contexts)) {
178
+ throw new Error(`${sourcePath} contexts must be an array`);
179
+ }
180
+ return config.contexts.map((context) => {
181
+ if (typeof context.name !== "string" || context.name.length === 0) {
182
+ throw new Error(`${sourcePath} context name must be a non-empty string`);
183
+ }
184
+ return context.name;
185
+ });
186
+ }
159
187
  function escapeRegExp(value) {
160
188
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
161
189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubepile",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Compile and split kubeconfig files from ~/.config/kubepile.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/kubepile.js",