From a6100258eef100f52207d9f94d76259ded9c988e Mon Sep 17 00:00:00 2001 From: Dallin Drollinger Date: Thu, 29 Sep 2022 12:30:08 -0600 Subject: [PATCH 1/3] added env subprocess to push vars from file to project --- deployctl.ts | 8 +++ src/subcommands/sync_env.ts | 98 +++++++++++++++++++++++++++++++++++++ src/utils/api.ts | 10 ++++ 3 files changed, 116 insertions(+) create mode 100644 src/subcommands/sync_env.ts diff --git a/deployctl.ts b/deployctl.ts index b2ea32d2..27aa8f8f 100755 --- a/deployctl.ts +++ b/deployctl.ts @@ -6,6 +6,7 @@ import { parseArgs, semverGreaterThanOrEquals } from "./deps.ts"; import { error } from "./src/error.ts"; import deploySubcommand from "./src/subcommands/deploy.ts"; import upgradeSubcommand from "./src/subcommands/upgrade.ts"; +import syncEnvSubcommand from "./src/subcommands/sync_env.ts"; import { MINIMUM_DENO_VERSION, VERSION } from "./src/version.ts"; import { fetchReleases, getConfigPaths } from "./src/utils/info.ts"; @@ -18,9 +19,13 @@ To deploy a local script: To deploy a remote script: deployctl deploy --project=helloworld https://deno.land/x/deploy/examples/hello.js +To send local env vars a project: + deployctl env --project=helloworld ./.env + SUBCOMMANDS: deploy Deploy a script with static files to Deno Deploy upgrade Upgrade deployctl to the given version (defaults to latest) + env Send environment variables defined in a file to a project `; if (!semverGreaterThanOrEquals(Deno.version.deno, MINIMUM_DENO_VERSION)) { @@ -104,6 +109,9 @@ switch (subcommand) { case "upgrade": await upgradeSubcommand(args); break; + case "env": + await syncEnvSubcommand(args); + break; default: if (args.version) { console.log(`deployctl ${VERSION}`); diff --git a/src/subcommands/sync_env.ts b/src/subcommands/sync_env.ts new file mode 100644 index 00000000..4b142158 --- /dev/null +++ b/src/subcommands/sync_env.ts @@ -0,0 +1,98 @@ +// Copyright 2021 Deno Land Inc. All rights reserved. MIT license. + +import { wait } from "../../deps.ts"; +import { error } from "../error.ts"; +import { API } from "../utils/api.ts"; + +const help = `deployctl env +Sync environment variable with Deno Deploy. + +USAGE: + deployctl env [OPTIONS] + +OPTIONS: + -h, --help Prints help information + -p, --project=NAME The project to deploy to + --token=TOKEN The API token to use (defaults to DENO_DEPLOY_TOKEN env var) +`; + +export interface Args { + help: boolean; + token: string | null; + project: string | null; +} + +// deno-lint-ignore no-explicit-any +export default async function (rawArgs: Record): Promise { + const args: Args = { + help: !!rawArgs.help, + token: rawArgs.token ? String(rawArgs.token) : null, + project: rawArgs.project ? String(rawArgs.project) : null, + }; + const envFile: string | null = typeof rawArgs._[0] === "string" + ? rawArgs._[0] + : null; + if (args.help) { + console.log(help); + Deno.exit(0); + } + const token = args.token ?? Deno.env.get("DENO_DEPLOY_TOKEN") ?? null; + if (token === null) { + console.error(help); + error("Missing access token. Set via --token or DENO_DEPLOY_TOKEN."); + } + if (envFile === null) { + console.error(help); + error("No enivronment file specifier given."); + } + if (rawArgs._.length > 1) { + console.error(help); + error("Too many positional arguments given."); + } + if (args.project === null) { + console.error(help); + error("Missing project name."); + } + + const projectSpinner = wait("Fetching project information...").start(); + const api = API.fromToken(token); + const project = await api.getProject(args.project); + if (project === null) { + projectSpinner.fail("Project not found."); + Deno.exit(1); + } + projectSpinner.succeed(`Project: ${project!.name}`); + + const fileSpinner = wait("Reading env file...").start(); + const envObj: Record = {}; + try { + const varsText = await Deno.readTextFile(envFile); + if (!varsText) { + fileSpinner.info("File is empty."); + Deno.exit(1); + } + varsText.replace( + /(\w+)=(.+)/g, + function (_$0: string, $1: string, $2: string) { + envObj[$1] = $2; + }, + ); + if (Object.keys(envObj).length === 0) { + fileSpinner.info("File did not contain any variables."); + Deno.exit(1); + } + } catch { + fileSpinner.fail(`Could not load file: ${envFile}`); + Deno.exit(1); + } + fileSpinner.succeed(`File Loaded: ${envFile}`); + + const sendSpinner = wait("Sending env variables...").start(); + try { + await api.sendEnv(project!.id, envObj); + } catch { + sendSpinner.fail("Failed to send variables."); + Deno.exit(1); + } + sendSpinner.succeed("Env variables sent."); +} diff --git a/src/utils/api.ts b/src/utils/api.ts index c90057f3..322718a6 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -156,4 +156,14 @@ export class API { { method: "POST", body: form }, ); } + + async sendEnv( + projectId: string, + body: Record, + ): Promise { + return await this.#requestJson( + `/projects/${projectId}/env`, + { method: "POST", body }, + ); + } } From 90ec253de26de2561ec92cf254351a2a590a57c1 Mon Sep 17 00:00:00 2001 From: Dallin Drollinger Date: Thu, 29 Sep 2022 16:06:47 -0600 Subject: [PATCH 2/3] switched to use std dotenv library --- deps.ts | 1 + src/subcommands/sync_env.ts | 17 ++++------------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/deps.ts b/deps.ts index 23614a57..61fc7af2 100644 --- a/deps.ts +++ b/deps.ts @@ -16,6 +16,7 @@ export { } from "https://deno.land/std@0.148.0/fmt/colors.ts"; export { parse as parseArgs } from "https://deno.land/std@0.148.0/flags/mod.ts"; export { LineStream } from "https://deno.land/std@0.148.0/streams/delimiter.ts"; +export { config } from "https://deno.land/std@0.148.0/dotenv/mod.ts"; // x/semver export { diff --git a/src/subcommands/sync_env.ts b/src/subcommands/sync_env.ts index 4b142158..4a9a2734 100644 --- a/src/subcommands/sync_env.ts +++ b/src/subcommands/sync_env.ts @@ -1,6 +1,6 @@ // Copyright 2021 Deno Land Inc. All rights reserved. MIT license. -import { wait } from "../../deps.ts"; +import { config, wait } from "../../deps.ts"; import { error } from "../error.ts"; import { API } from "../utils/api.ts"; @@ -64,19 +64,9 @@ export default async function (rawArgs: Record): Promise { projectSpinner.succeed(`Project: ${project!.name}`); const fileSpinner = wait("Reading env file...").start(); - const envObj: Record = {}; + let envObj: Record = {}; try { - const varsText = await Deno.readTextFile(envFile); - if (!varsText) { - fileSpinner.info("File is empty."); - Deno.exit(1); - } - varsText.replace( - /(\w+)=(.+)/g, - function (_$0: string, $1: string, $2: string) { - envObj[$1] = $2; - }, - ); + envObj = await config({ path: envFile }); if (Object.keys(envObj).length === 0) { fileSpinner.info("File did not contain any variables."); Deno.exit(1); @@ -87,6 +77,7 @@ export default async function (rawArgs: Record): Promise { } fileSpinner.succeed(`File Loaded: ${envFile}`); + console.log(envObj); const sendSpinner = wait("Sending env variables...").start(); try { await api.sendEnv(project!.id, envObj); From 25346e20e0a3bd98d008a67d43ac399751163530 Mon Sep 17 00:00:00 2001 From: Dallin Drollinger Date: Thu, 29 Sep 2022 19:07:12 -0600 Subject: [PATCH 3/3] removed console log --- src/subcommands/sync_env.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/subcommands/sync_env.ts b/src/subcommands/sync_env.ts index 4a9a2734..eb466277 100644 --- a/src/subcommands/sync_env.ts +++ b/src/subcommands/sync_env.ts @@ -77,7 +77,6 @@ export default async function (rawArgs: Record): Promise { } fileSpinner.succeed(`File Loaded: ${envFile}`); - console.log(envObj); const sendSpinner = wait("Sending env variables...").start(); try { await api.sendEnv(project!.id, envObj);