Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { findFirstPositionalToken, valueFlagsFor } from "@/lib/command-delegatio
import { findEarlyBootstrapExit } from "@/lib/early-bootstrap.ts";
import { terminalError, applyTerminalColorFromContext } from "@/ui/terminal/styles.ts";
import { maybeShowUpdateNotice } from "@/lib/updater.ts";
import { validateEnvironment } from "@/lib/env.ts";

function buildCliContextFromArgs(args: Record<string, unknown>): CliContext {
return parseGlobalFlagsFromArgs(args);
Expand Down Expand Up @@ -161,6 +162,7 @@ async function bootstrap(): Promise<void> {
setCliContext(earlyContext);

try {
validateEnvironment();
const earlyExit = findEarlyBootstrapExit(rawArgs);
if (earlyExit?.id === "help") {
const [command, parent] = await resolveSubCommandForUsage(main, rawArgs);
Expand Down
10 changes: 6 additions & 4 deletions cli/src/commands/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
terminalSuccess,
} from "@/ui/terminal/styles.ts";
import { buildCompletionSpec } from "@/lib/completion-spec.ts";
import { readEnv } from "@/lib/env.ts";
import {
formatBashCompletion,
formatFishCompletion,
Expand Down Expand Up @@ -107,7 +108,8 @@ function resolveShell(shell: unknown): SupportedShell {
throw new CliError(`Unsupported shell: ${shell}. Use ${formatSupportedShells()}.`);
}

const detectedShell = process.env.SHELL ? getShellName(process.env.SHELL) : "";
const envShell = readEnv("SHELL");
const detectedShell = envShell ? getShellName(envShell) : "";
if (isSupportedShell(detectedShell)) {
return detectedShell;
}
Expand All @@ -118,15 +120,15 @@ function resolveShell(shell: unknown): SupportedShell {
}

function envHome(): string {
return process.env.HOME || homedir();
return readEnv("HOME") ?? homedir();
}

function xdgDataHome(): string {
return process.env.XDG_DATA_HOME || join(envHome(), ".local", "share");
return readEnv("XDG_DATA_HOME") ?? join(envHome(), ".local", "share");
}

function xdgConfigHome(): string {
return process.env.XDG_CONFIG_HOME || join(envHome(), ".config");
return readEnv("XDG_CONFIG_HOME") ?? join(envHome(), ".config");
}

function shellQuote(value: string): string {
Expand Down
7 changes: 4 additions & 3 deletions cli/src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
updateProfile,
} from "@/features/profile/model.ts";
import { terminalSuccess } from "@/ui/terminal/styles.ts";
import { readEnv, setEnv } from "@/lib/env.ts";

function isInteractiveTerminal(): boolean {
return process.stdin.isTTY;
Expand Down Expand Up @@ -143,7 +144,7 @@ export function applyControlPlaneOverride(args: LoginArgs): void {
if (!url) {
return;
}
const envOverride = process.env.ALTERTABLE_MANAGEMENT_API_BASE;
const envOverride = readEnv("ALTERTABLE_MANAGEMENT_API_BASE");
if (envOverride && envOverride !== url) {
throw new ConfigurationError(
`ALTERTABLE_MANAGEMENT_API_BASE=${envOverride} overrides --control-plane-url=${url}. Unset the environment variable or make them match.`,
Expand All @@ -153,10 +154,10 @@ export function applyControlPlaneOverride(args: LoginArgs): void {
// resolveManagementApiRoot re-validates the URL on every read using this env
// var, so set it too — otherwise an http:// root would fail the very next
// read (whoami, and later commands in this process).
process.env.ALTERTABLE_ALLOW_INSECURE_HTTP = "1";
setEnv("ALTERTABLE_ALLOW_INSECURE_HTTP", "1");
}
assertAllowedApiBase(url, { allowInsecureHttp: Boolean(args["allow-insecure-http"]) });
process.env.ALTERTABLE_MANAGEMENT_API_BASE = url;
setEnv("ALTERTABLE_MANAGEMENT_API_BASE", url);
}

// Profile-free on purpose: the minted token — not the current profile's stored
Expand Down
53 changes: 22 additions & 31 deletions cli/src/features/profile/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@/lib/config.ts";
import { getCliContext } from "@/context.ts";
import { ConfigurationError } from "@/lib/errors.ts";
import { CONFIG_ENV_NAMES, isSecretEnv, readEnv } from "@/lib/env.ts";
import type { WhoamiResponse } from "@/features/management/model.ts";
import {
moveProfileSecrets,
Expand Down Expand Up @@ -284,10 +285,10 @@ function parseTimestampMs(raw: string | undefined): string | undefined {
}

function envLakehouseAuthKind(): ProfileLakehouseAuth {
if (process.env.ALTERTABLE_BASIC_AUTH_TOKEN) {
if (readEnv("ALTERTABLE_BASIC_AUTH_TOKEN")) {
return "basic_token";
}
if (process.env.ALTERTABLE_LAKEHOUSE_USERNAME && process.env.ALTERTABLE_LAKEHOUSE_PASSWORD) {
if (readEnv("ALTERTABLE_LAKEHOUSE_USERNAME") && readEnv("ALTERTABLE_LAKEHOUSE_PASSWORD")) {
return "username_password";
}
return "none";
Expand All @@ -304,10 +305,10 @@ function inspectFromEnvProfile(): ProfileInspect {
config_file: "environment variables",
organization: {},
principal: {},
environment: process.env.ALTERTABLE_ENV || undefined,
environment: readEnv("ALTERTABLE_ENV"),
endpoints: {
data_plane: process.env.ALTERTABLE_API_BASE || undefined,
control_plane: process.env.ALTERTABLE_MANAGEMENT_API_BASE || undefined,
data_plane: readEnv("ALTERTABLE_API_BASE"),
control_plane: readEnv("ALTERTABLE_MANAGEMENT_API_BASE"),
},
auth: { management, lakehouse },
status: management !== "none" || lakehouse !== "none" ? "configured" : "empty",
Expand Down Expand Up @@ -513,7 +514,7 @@ export type ConfigureShowData = {
};

function hasEnvManagementCredentials(): boolean {
return Boolean(process.env.ALTERTABLE_API_KEY);
return Boolean(readEnv("ALTERTABLE_API_KEY"));
}

function hasStoredManagementCredentials(profileName: string): boolean {
Expand All @@ -525,11 +526,11 @@ function hasOAuthLogin(profileName: string): boolean {
}

function hasEnvLakehouseCredentials(): boolean {
if (process.env.ALTERTABLE_BASIC_AUTH_TOKEN) {
if (readEnv("ALTERTABLE_BASIC_AUTH_TOKEN")) {
return true;
}
return Boolean(
process.env.ALTERTABLE_LAKEHOUSE_USERNAME && process.env.ALTERTABLE_LAKEHOUSE_PASSWORD,
readEnv("ALTERTABLE_LAKEHOUSE_USERNAME") && readEnv("ALTERTABLE_LAKEHOUSE_PASSWORD"),
);
}

Expand All @@ -549,24 +550,14 @@ export function resolveActiveProfileName(): string {
return resolveWorkingProfile(getCliContext().profile);
}

const ENV_CONFIG_VARS: ReadonlyArray<{ name: string; secret: boolean }> = [
{ name: "ALTERTABLE_API_KEY", secret: true },
{ name: "ALTERTABLE_BASIC_AUTH_TOKEN", secret: true },
{ name: "ALTERTABLE_LAKEHOUSE_USERNAME", secret: false },
{ name: "ALTERTABLE_LAKEHOUSE_PASSWORD", secret: true },
{ name: "ALTERTABLE_ENV", secret: false },
{ name: "ALTERTABLE_API_BASE", secret: false },
{ name: "ALTERTABLE_MANAGEMENT_API_BASE", secret: false },
];

/** The profile-configuring env vars currently set, with secrets masked. */
export function configuredEnvConfig(): Array<{ name: string; display: string }> {
return ENV_CONFIG_VARS.flatMap(({ name, secret }) => {
const value = process.env[name];
return CONFIG_ENV_NAMES.flatMap((name) => {
const value = readEnv(name);
if (!value) {
return [];
}
return [{ name, display: secret ? "set (hidden)" : value }];
return [{ name, display: isSecretEnv(name) ? "set (hidden)" : String(value) }];
});
}

Expand Down Expand Up @@ -609,7 +600,7 @@ function buildManagementCredential(profileName: string): ConfigureManagementCred
configured: true,
mechanism: "management_api_key",
source: "environment",
environment: process.env.ALTERTABLE_ENV ?? configGet("api_key_env", profileName) ?? undefined,
environment: readEnv("ALTERTABLE_ENV") ?? configGet("api_key_env", profileName) ?? undefined,
};
}
if (hasOAuthLogin(profileName)) {
Expand Down Expand Up @@ -653,15 +644,15 @@ function buildLakehouseCredential(profileName: string): ConfigureLakehouseCreden
password: "set",
};
}
if (process.env.ALTERTABLE_BASIC_AUTH_TOKEN) {
if (readEnv("ALTERTABLE_BASIC_AUTH_TOKEN")) {
return {
configured: true,
mechanism: "lakehouse_basic_token",
source: "environment",
};
}
const envUser = process.env.ALTERTABLE_LAKEHOUSE_USERNAME;
if (envUser && process.env.ALTERTABLE_LAKEHOUSE_PASSWORD) {
const envUser = readEnv("ALTERTABLE_LAKEHOUSE_USERNAME");
if (envUser && readEnv("ALTERTABLE_LAKEHOUSE_PASSWORD")) {
return {
configured: true,
mechanism: "lakehouse_username_password",
Expand All @@ -675,13 +666,13 @@ function buildLakehouseCredential(profileName: string): ConfigureLakehouseCreden
function buildConfigureShowOverrides(profileName: string): ConfigureShowOverrides {
const overrides: ConfigureShowOverrides = {};
const storedApiKeyEnv = configGet("api_key_env", profileName);
const envOverride = process.env.ALTERTABLE_ENV;
const envOverride = readEnv("ALTERTABLE_ENV");

if (envOverride && envOverride !== storedApiKeyEnv) {
overrides.environment = envOverride;
overrides.stored_environment = storedApiKeyEnv || null;
}
if (process.env.ALTERTABLE_API_KEY && hasStoredManagementCredentials(profileName)) {
if (readEnv("ALTERTABLE_API_KEY") && hasStoredManagementCredentials(profileName)) {
overrides.api_key = true;
}
return overrides;
Expand Down Expand Up @@ -722,11 +713,11 @@ export function managementPlaneStatusDetail(profileName: string): string | null
}

export function lakehousePlaneStatusDetail(profileName: string): string | null {
if (process.env.ALTERTABLE_BASIC_AUTH_TOKEN) {
if (readEnv("ALTERTABLE_BASIC_AUTH_TOKEN")) {
return "via ALTERTABLE_BASIC_AUTH_TOKEN";
}
const envUser = process.env.ALTERTABLE_LAKEHOUSE_USERNAME;
if (envUser && process.env.ALTERTABLE_LAKEHOUSE_PASSWORD) {
const envUser = readEnv("ALTERTABLE_LAKEHOUSE_USERNAME");
if (envUser && readEnv("ALTERTABLE_LAKEHOUSE_PASSWORD")) {
return envUser;
}
if (!hasStoredLakehouseCredentials(profileName)) {
Expand All @@ -753,7 +744,7 @@ export type ActiveContext = {
};

function resolveEnvironment(showData: ConfigureShowData): string | undefined {
const envOverride = process.env.ALTERTABLE_ENV;
const envOverride = readEnv("ALTERTABLE_ENV");
if (envOverride && envOverride.length > 0) {
return envOverride;
}
Expand Down
11 changes: 6 additions & 5 deletions cli/src/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { configGet } from "@/lib/config.ts";
import { CliError, ConfigurationError, EXIT_CONFIG } from "@/lib/errors.ts";
import { secretGet } from "@/lib/secrets.ts";
import { readEnv } from "@/lib/env.ts";

export function basicAuthToken(user: string, password: string): string {
return Buffer.from(`${user}:${password}`).toString("base64");
Expand Down Expand Up @@ -28,13 +29,13 @@ function storedLakehouseCredentialsExpired(profileName: string): boolean {
}

function lakehouseEnvAuthHeader(): string | undefined {
const envToken = process.env.ALTERTABLE_BASIC_AUTH_TOKEN;
const envToken = readEnv("ALTERTABLE_BASIC_AUTH_TOKEN");
if (envToken) {
return basicAuthHeader(envToken);
}

const envUser = process.env.ALTERTABLE_LAKEHOUSE_USERNAME;
const envPassword = process.env.ALTERTABLE_LAKEHOUSE_PASSWORD;
const envUser = readEnv("ALTERTABLE_LAKEHOUSE_USERNAME");
const envPassword = readEnv("ALTERTABLE_LAKEHOUSE_PASSWORD");
if (envUser && envPassword) {
return basicAuthHeader(basicAuthToken(envUser, envPassword));
}
Expand Down Expand Up @@ -92,7 +93,7 @@ export function getLoginLakehouseCredentials(
}

export function getManagementAuthHeader(profileName: string): string {
const envKey = process.env.ALTERTABLE_API_KEY ?? "";
const envKey = readEnv("ALTERTABLE_API_KEY") ?? "";
if (envKey) {
return `Authorization: Bearer ${envKey}`;
}
Expand All @@ -112,7 +113,7 @@ export function getManagementAuthHeader(profileName: string): string {
}

function managementEnv(profileName: string): string {
return process.env.ALTERTABLE_ENV ?? configGet("api_key_env", profileName);
return readEnv("ALTERTABLE_ENV") ?? configGet("api_key_env", profileName);
}

export function requireManagementEnv(profileName: string): string {
Expand Down
5 changes: 3 additions & 2 deletions cli/src/lib/config-files.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { randomBytes } from "node:crypto";
import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { readEnv } from "@/lib/env.ts";

function trim(value: string): string {
return value.trim();
Expand All @@ -11,11 +12,11 @@ function tempSiblingPath(filePath: string): string {
}

export function configDir(): string {
const override = process.env.ALTERTABLE_CONFIG_HOME;
const override = readEnv("ALTERTABLE_CONFIG_HOME");
if (override) {
return override;
}
const xdg = process.env.XDG_CONFIG_HOME ?? join(process.env.HOME ?? "", ".config");
const xdg = readEnv("XDG_CONFIG_HOME") ?? join(readEnv("HOME") ?? "", ".config");
return join(xdg, "altertable");
}

Expand Down
8 changes: 4 additions & 4 deletions cli/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
kvUnset,
} from "@/lib/config-files.ts";
import { assertAllowedApiBase } from "@/lib/url-policy.ts";
import { readEnv } from "@/lib/env.ts";
import { isQueryLayout, type QueryLayout } from "@/ui/layouts/query.ts";

export { configDir, configFile, credentialsFile, kvGet, kvSet, kvUnset };
Expand All @@ -24,12 +25,11 @@ export function configSetGlobal(key: string, value: string): void {
}

function resolveAllowInsecureHttp(): boolean {
const envFlag = process.env.ALTERTABLE_ALLOW_INSECURE_HTTP ?? "";
return envFlag === "true" || envFlag === "1";
return readEnv("ALTERTABLE_ALLOW_INSECURE_HTTP") ?? false;
}

export function resolveApiBase(profileName: string): string {
let base = process.env.ALTERTABLE_API_BASE ?? "";
let base = readEnv("ALTERTABLE_API_BASE") ?? "";
if (!base) {
base = configGet("api_base", profileName);
}
Expand All @@ -42,7 +42,7 @@ export function resolveApiBase(profileName: string): string {
}

function resolveManagementApiRoot(profileName: string): string {
let root = process.env.ALTERTABLE_MANAGEMENT_API_BASE ?? "";
let root = readEnv("ALTERTABLE_MANAGEMENT_API_BASE") ?? "";
if (!root) {
root = configGet("management_api_base", profileName);
}
Expand Down
Loading
Loading