Skip to content

Commit

Permalink
Fix mixed DB token env vars (#11894)
Browse files Browse the repository at this point in the history
* Fix mixed DB token env vars

* Test env combinations

* Fix linter issues

* Fix linter issues

---------

Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com>
  • Loading branch information
Fryuni and Princesseuh authored Sep 3, 2024
1 parent cd54210 commit cc820c5
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/empty-spoons-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/db': patch
---

Fixes mixed environment variable for app token when using DB commands with libSQL remote.
4 changes: 2 additions & 2 deletions packages/db/src/core/cli/commands/execute/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { existsSync } from 'node:fs';
import { getManagedAppTokenOrExit } from '@astrojs/studio';
import { LibsqlError } from '@libsql/client';
import type { AstroConfig } from 'astro';
import { green } from 'kleur/colors';
Expand All @@ -16,6 +15,7 @@ import {
} from '../../../integration/vite-plugin-db.js';
import { bundleFile, importBundledFile } from '../../../load-file.js';
import type { DBConfig } from '../../../types.js';
import { getManagedRemoteToken } from '../../../utils.js';

export async function cmd({
astroConfig,
Expand All @@ -40,7 +40,7 @@ export async function cmd({

let virtualModContents: string;
if (flags.remote) {
const appToken = await getManagedAppTokenOrExit(flags.token);
const appToken = await getManagedRemoteToken(flags.token);
virtualModContents = getStudioVirtualModContents({
tables: dbConfig.tables ?? {},
appToken: appToken.token,
Expand Down
16 changes: 10 additions & 6 deletions packages/db/src/core/cli/commands/push/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getManagedAppTokenOrExit } from '@astrojs/studio';
import type { AstroConfig } from 'astro';
import { sql } from 'drizzle-orm';
import prompts from 'prompts';
Expand All @@ -7,7 +6,7 @@ import { createRemoteDatabaseClient } from '../../../../runtime/index.js';
import { safeFetch } from '../../../../runtime/utils.js';
import { MIGRATION_VERSION } from '../../../consts.js';
import type { DBConfig, DBSnapshot } from '../../../types.js';
import { type Result, getRemoteDatabaseInfo } from '../../../utils.js';
import { type RemoteDatabaseInfo, type Result, getManagedRemoteToken, getRemoteDatabaseInfo } from '../../../utils.js';
import {
createCurrentSnapshot,
createEmptySnapshot,
Expand All @@ -26,8 +25,12 @@ export async function cmd({
}) {
const isDryRun = flags.dryRun;
const isForceReset = flags.forceReset;
const appToken = await getManagedAppTokenOrExit(flags.token);
const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
const dbInfo = getRemoteDatabaseInfo();
const appToken = await getManagedRemoteToken(flags.token, dbInfo);
const productionSnapshot = await getProductionCurrentSnapshot({
dbInfo,
appToken: appToken.token,
});
const currentSnapshot = createCurrentSnapshot(dbConfig);
const isFromScratch = !productionSnapshot;
const { queries: migrationQueries, confirmations } = await getMigrationQueries({
Expand Down Expand Up @@ -68,6 +71,7 @@ export async function cmd({
console.log(`Pushing database schema updates...`);
await pushSchema({
statements: migrationQueries,
dbInfo,
appToken: appToken.token,
isDryRun,
currentSnapshot: currentSnapshot,
Expand All @@ -80,11 +84,13 @@ export async function cmd({

async function pushSchema({
statements,
dbInfo,
appToken,
isDryRun,
currentSnapshot,
}: {
statements: string[];
dbInfo: RemoteDatabaseInfo;
appToken: string;
isDryRun: boolean;
currentSnapshot: DBSnapshot;
Expand All @@ -99,8 +105,6 @@ async function pushSchema({
return new Response(null, { status: 200 });
}

const dbInfo = getRemoteDatabaseInfo();

return dbInfo.type === 'studio'
? pushToStudio(requestBody, appToken, dbInfo.url)
: pushToDb(requestBody, appToken, dbInfo.url);
Expand Down
5 changes: 2 additions & 3 deletions packages/db/src/core/cli/commands/shell/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getManagedAppTokenOrExit } from '@astrojs/studio';
import type { AstroConfig } from 'astro';
import { sql } from 'drizzle-orm';
import type { Arguments } from 'yargs-parser';
Expand All @@ -10,7 +9,7 @@ import { normalizeDatabaseUrl } from '../../../../runtime/index.js';
import { DB_PATH } from '../../../consts.js';
import { SHELL_QUERY_MISSING_ERROR } from '../../../errors.js';
import type { DBConfigInput } from '../../../types.js';
import { getAstroEnv, getRemoteDatabaseInfo } from '../../../utils.js';
import { getAstroEnv, getManagedRemoteToken, getRemoteDatabaseInfo } from '../../../utils.js';

export async function cmd({
flags,
Expand All @@ -27,7 +26,7 @@ export async function cmd({
}
const dbInfo = getRemoteDatabaseInfo();
if (flags.remote) {
const appToken = await getManagedAppTokenOrExit(flags.token);
const appToken = await getManagedRemoteToken(flags.token, dbInfo);
const db = createRemoteDatabaseClient({
dbType: dbInfo.type,
remoteUrl: dbInfo.url,
Expand Down
10 changes: 7 additions & 3 deletions packages/db/src/core/cli/commands/verify/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getManagedAppTokenOrExit } from '@astrojs/studio';
import type { AstroConfig } from 'astro';
import type { Arguments } from 'yargs-parser';
import type { DBConfig } from '../../../types.js';
Expand All @@ -9,6 +8,7 @@ import {
getMigrationQueries,
getProductionCurrentSnapshot,
} from '../../migration-queries.js';
import { getManagedRemoteToken, getRemoteDatabaseInfo } from '../../../utils.js';

export async function cmd({
dbConfig,
Expand All @@ -19,8 +19,12 @@ export async function cmd({
flags: Arguments;
}) {
const isJson = flags.json;
const appToken = await getManagedAppTokenOrExit(flags.token);
const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
const dbInfo = getRemoteDatabaseInfo();
const appToken = await getManagedRemoteToken(flags.token, dbInfo);
const productionSnapshot = await getProductionCurrentSnapshot({
dbInfo,
appToken: appToken.token,
});
const currentSnapshot = createCurrentSnapshot(dbConfig);
const { queries: migrationQueries, confirmations } = await getMigrationQueries({
oldSnapshot: productionSnapshot || createEmptySnapshot(),
Expand Down
11 changes: 5 additions & 6 deletions packages/db/src/core/cli/migration-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import type {
ResolvedIndexes,
TextColumn,
} from '../types.js';
import { type Result, getRemoteDatabaseInfo } from '../utils.js';
import type { RemoteDatabaseInfo, Result } from '../utils.js';

const sqlite = new SQLiteAsyncDialect();
const genTempTableName = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10);
Expand Down Expand Up @@ -425,13 +425,12 @@ function hasRuntimeDefault(column: DBColumn): column is DBColumnWithDefault {
}

export function getProductionCurrentSnapshot(options: {
dbInfo: RemoteDatabaseInfo;
appToken: string;
}): Promise<DBSnapshot | undefined> {
const dbInfo = getRemoteDatabaseInfo();

return dbInfo.type === 'studio'
? getStudioCurrentSnapshot(options.appToken, dbInfo.url)
: getDbCurrentSnapshot(options.appToken, dbInfo.url);
return options.dbInfo.type === 'studio'
? getStudioCurrentSnapshot(options.appToken, options.dbInfo.url)
: getDbCurrentSnapshot(options.appToken, options.dbInfo.url);
}

async function getDbCurrentSnapshot(
Expand Down
26 changes: 13 additions & 13 deletions packages/db/src/core/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
import { mkdir, writeFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { type ManagedAppToken, getManagedAppTokenOrExit } from '@astrojs/studio';
import type { ManagedAppToken } from '@astrojs/studio';
import { LibsqlError } from '@libsql/client';
import type { AstroConfig, AstroIntegration } from 'astro';
import { blue, yellow } from 'kleur/colors';
Expand All @@ -20,7 +20,7 @@ import { CONFIG_FILE_NAMES, DB_PATH } from '../consts.js';
import { EXEC_DEFAULT_EXPORT_ERROR, EXEC_ERROR } from '../errors.js';
import { resolveDbConfig } from '../load-file.js';
import { SEED_DEV_FILE_NAME } from '../queries.js';
import { type VitePlugin, getDbDirectoryUrl } from '../utils.js';
import { type VitePlugin, getDbDirectoryUrl, getManagedRemoteToken } from '../utils.js';
import { fileURLIntegration } from './file-url.js';
import { getDtsContent } from './typegen.js';
import {
Expand All @@ -32,7 +32,7 @@ import {
} from './vite-plugin-db.js';

function astroDBIntegration(): AstroIntegration {
let connectToStudio = false;
let connectToRemote = false;
let configFileDependencies: string[] = [];
let root: URL;
let appToken: ManagedAppToken | undefined;
Expand Down Expand Up @@ -72,12 +72,12 @@ function astroDBIntegration(): AstroIntegration {

let dbPlugin: VitePlugin | undefined = undefined;
const args = parseArgs(process.argv.slice(3));
connectToStudio = process.env.ASTRO_INTERNAL_TEST_REMOTE || args['remote'];
connectToRemote = process.env.ASTRO_INTERNAL_TEST_REMOTE || args['remote'];

if (connectToStudio) {
appToken = await getManagedAppTokenOrExit();
if (connectToRemote) {
appToken = await getManagedRemoteToken();
dbPlugin = vitePluginDb({
connectToStudio,
connectToStudio: connectToRemote,
appToken: appToken.token,
tables,
root: config.root,
Expand Down Expand Up @@ -116,7 +116,7 @@ function astroDBIntegration(): AstroIntegration {
configFileDependencies = dependencies;

const localDbUrl = new URL(DB_PATH, config.root);
if (!connectToStudio && !existsSync(localDbUrl)) {
if (!connectToRemote && !existsSync(localDbUrl)) {
await mkdir(dirname(fileURLToPath(localDbUrl)), { recursive: true });
await writeFile(localDbUrl, '');
}
Expand All @@ -143,9 +143,9 @@ function astroDBIntegration(): AstroIntegration {
// Wait for dev server log before showing "connected".
setTimeout(() => {
logger.info(
connectToStudio ? 'Connected to remote database.' : 'New local database created.',
connectToRemote ? 'Connected to remote database.' : 'New local database created.',
);
if (connectToStudio) return;
if (connectToRemote) return;

const localSeedPaths = SEED_DEV_FILE_NAME.map(
(name) => new URL(name, getDbDirectoryUrl(root)),
Expand All @@ -160,7 +160,7 @@ function astroDBIntegration(): AstroIntegration {
},
'astro:build:start': async ({ logger }) => {
if (
!connectToStudio &&
!connectToRemote &&
!databaseFileEnvDefined() &&
(output === 'server' || output === 'hybrid')
) {
Expand All @@ -170,15 +170,15 @@ function astroDBIntegration(): AstroIntegration {
throw new AstroDbError(message, hint);
}

logger.info('database: ' + (connectToStudio ? yellow('remote') : blue('local database.')));
logger.info('database: ' + (connectToRemote ? yellow('remote') : blue('local database.')));
},
'astro:build:setup': async ({ vite }) => {
tempViteServer = await getTempViteServer({ viteConfig: vite });
seedHandler.execute = async (fileUrl) => {
await executeSeedFile({ fileUrl, viteServer: tempViteServer! });
};
},
'astro:build:done': async ({}) => {
'astro:build:done': async ({ }) => {
await appToken?.destroy();
await tempViteServer?.close();
},
Expand Down
18 changes: 17 additions & 1 deletion packages/db/src/core/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getAstroStudioEnv } from '@astrojs/studio';
import { getAstroStudioEnv, getManagedAppTokenOrExit, type ManagedAppToken } from '@astrojs/studio';
import type { AstroConfig, AstroIntegration } from 'astro';
import { loadEnv } from 'vite';
import './types.js';
Expand Down Expand Up @@ -37,6 +37,22 @@ export function getRemoteDatabaseInfo(): RemoteDatabaseInfo {
};
}

export function getManagedRemoteToken(token?: string, dbInfo?: RemoteDatabaseInfo): Promise<ManagedAppToken> {
dbInfo ??= getRemoteDatabaseInfo();

if (dbInfo.type === 'studio') {
return getManagedAppTokenOrExit(token)
}

const astroEnv = getAstroEnv();

return Promise.resolve({
token: token ?? astroEnv.ASTRO_DB_APP_TOKEN,
renew: () => Promise.resolve(),
destroy: () => Promise.resolve(),
});
}

export function getDbDirectoryUrl(root: URL | string) {
return new URL('db/', root);
}
Expand Down
5 changes: 4 additions & 1 deletion packages/db/test/basics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { after, before, describe, it } from 'node:test';
import { load as cheerioLoad } from 'cheerio';
import testAdapter from '../../astro/test/test-adapter.js';
import { loadFixture } from '../../astro/test/test-utils.js';
import { setupRemoteDbServer } from './test-utils.js';
import { clearEnvironment, setupRemoteDbServer } from './test-utils.js';

describe('astro:db', () => {
let fixture;
Expand All @@ -19,6 +19,7 @@ describe('astro:db', () => {
let devServer;

before(async () => {
clearEnvironment();
devServer = await fixture.startDevServer();
});

Expand Down Expand Up @@ -98,6 +99,7 @@ describe('astro:db', () => {
let remoteDbServer;

before(async () => {
clearEnvironment();
remoteDbServer = await setupRemoteDbServer(fixture.config);
devServer = await fixture.startDevServer();
});
Expand Down Expand Up @@ -178,6 +180,7 @@ describe('astro:db', () => {
let remoteDbServer;

before(async () => {
clearEnvironment();
process.env.ASTRO_STUDIO_APP_TOKEN = 'some token';
remoteDbServer = await setupRemoteDbServer(fixture.config);
await fixture.build();
Expand Down
3 changes: 2 additions & 1 deletion packages/db/test/static-remote.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { load as cheerioLoad } from 'cheerio';
import { loadFixture } from '../../astro/test/test-utils.js';
import { setupRemoteDbServer } from './test-utils.js';
import { clearEnvironment, setupRemoteDbServer } from './test-utils.js';

describe('astro:db', () => {
let fixture;
Expand Down Expand Up @@ -44,6 +44,7 @@ describe('astro:db', () => {
let remoteDbServer;

before(async () => {
clearEnvironment();
process.env.ASTRO_DB_REMOTE_URL = `memory:`;
await fixture.build();
});
Expand Down
12 changes: 12 additions & 0 deletions packages/db/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ export async function setupRemoteDbServer(astroConfig) {
};
}

/**
* Clears the environment variables related to Astro DB and Astro Studio.
*/
export function clearEnvironment() {
const keys = Array.from(Object.keys(process.env));
for (const key of keys) {
if (key.startsWith('ASTRO_DB_') || key.startsWith('ASTRO_STUDIO_')) {
delete process.env[key];
}
}
}

function createRemoteDbServer() {
const dbClient = createClient({
url: ':memory:',
Expand Down
Loading

0 comments on commit cc820c5

Please sign in to comment.