Skip to content

Add option to sync environment file to deno deploy #185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions deployctl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { parseArgs } from "./src/args.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 logsSubcommand from "./src/subcommands/logs.ts";
import { MINIMUM_DENO_VERSION, VERSION } from "./src/version.ts";
import { fetchReleases, getConfigPaths } from "./src/utils/info.ts";
Expand All @@ -20,10 +21,14 @@ 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)
logs View logs for the given project
env Send environment variables defined in a file to a project
`;

if (!semverGreaterThanOrEquals(Deno.version.deno, MINIMUM_DENO_VERSION)) {
Expand Down Expand Up @@ -84,6 +89,8 @@ switch (subcommand) {
case "upgrade":
await upgradeSubcommand(args);
break;
case "env":
await syncEnvSubcommand(args);
case "logs":
await logsSubcommand(args);
break;
Expand Down
2 changes: 2 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export {
red,
yellow,
} from "https://deno.land/std@0.170.0/fmt/colors.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";
export { parse } from "https://deno.land/std@0.195.0/flags/mod.ts";
export { TextLineStream } from "https://deno.land/std@0.170.0/streams/text_line_stream.ts";

Expand Down
88 changes: 88 additions & 0 deletions src/subcommands/sync_env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2021 Deno Land Inc. All rights reserved. MIT license.

import { config, 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] <ENV_FILE>

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<string, any>): Promise<void> {
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();
let envObj: Record<string, string> = {};
try {
envObj = await config({ path: envFile });
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.");
}
10 changes: 10 additions & 0 deletions src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,14 @@ export class API {
getMetadata(): Promise<Metadata> {
return this.#requestJson("/meta");
}

async sendEnv(
projectId: string,
body: Record<string, string>,
): Promise<Response> {
return await this.#requestJson(
`/projects/${projectId}/env`,
{ method: "POST", body },
);
}
}