mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-23 10:14:08 +03:00
Adds --org
and launch secrets to wasp deploy
(#1196)
This commit is contained in:
parent
d5b9d78542
commit
8d5530519b
@ -5,6 +5,9 @@
|
||||
### Bug fixes
|
||||
- `wasp deploy fly launch` now supports the latest `flyctl launch` toml file for the web client (which changed their default structure and port).
|
||||
|
||||
### More `wasp deploy fly` options
|
||||
`wasp deploy fly` now supports a `--org` option, as well as setting secrets during `launch`.
|
||||
|
||||
## v0.10.5
|
||||
|
||||
### Bug fixes
|
||||
|
@ -2,6 +2,7 @@ export interface CommonOptions {
|
||||
waspExe: string;
|
||||
waspProjectDir: string;
|
||||
flyTomlDir?: string;
|
||||
org?: string;
|
||||
}
|
||||
|
||||
export interface DbOptions {
|
||||
@ -13,3 +14,8 @@ export interface DbOptions {
|
||||
export interface LocalBuildOptions {
|
||||
buildLocally: boolean;
|
||||
}
|
||||
|
||||
export interface SecretsOptions {
|
||||
serverSecret: string[];
|
||||
clientSecret: string[];
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { TomlFilePaths } from './helpers/tomlFileHelpers.js';
|
||||
import { CommonOptions } from './CommonOptions.js';
|
||||
|
||||
export type DeploymentInfo = Readonly<{
|
||||
export type DeploymentInfo<CommandOptions extends CommonOptions> = Readonly<{
|
||||
baseName: string;
|
||||
region?: string;
|
||||
options: CommonOptions;
|
||||
options: CommandOptions;
|
||||
tomlFilePaths: TomlFilePaths;
|
||||
clientName: string;
|
||||
clientUrl: string;
|
||||
@ -13,13 +13,13 @@ export type DeploymentInfo = Readonly<{
|
||||
dbName: string;
|
||||
}>;
|
||||
|
||||
export function createDeploymentInfo(
|
||||
export function createDeploymentInfo<CommandOptions extends CommonOptions>(
|
||||
baseName: string,
|
||||
region: string | undefined,
|
||||
options: CommonOptions,
|
||||
options: CommandOptions,
|
||||
tomlFilePaths: TomlFilePaths,
|
||||
): DeploymentInfo {
|
||||
return {
|
||||
): DeploymentInfo<CommandOptions> {
|
||||
return Object.freeze({
|
||||
baseName,
|
||||
region,
|
||||
options,
|
||||
@ -29,5 +29,5 @@ export function createDeploymentInfo(
|
||||
serverName: `${baseName}-server`,
|
||||
serverUrl: `https://${baseName}-server.fly.dev`,
|
||||
dbName: `${baseName}-db`,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { exit } from 'process';
|
||||
import { $, cd } from 'zx';
|
||||
import { CommonOps, getCommonOps } from '../helpers/CommonOps.js';
|
||||
import { buildDirExists, getCommandHelp, waspSays } from '../helpers/helpers.js';
|
||||
import { buildDirExists, waspSays } from '../helpers/helpers.js';
|
||||
import { deleteLocalToml, getTomlFilePaths, localTomlExists } from '../helpers/tomlFileHelpers.js';
|
||||
import { executeFlyCommand } from '../index.js';
|
||||
import { CmdOptions } from './CmdOptions.js';
|
||||
|
||||
// Runs a command by copying down the project toml files, executing it, and copying it back up (just in case).
|
||||
// If the toml file does not exist, some commands will not run with additional args (e.g. -a <appname>).
|
||||
export async function cmd(flyctlArgs: string[], options: CmdOptions): Promise<void> {
|
||||
if (options.org) {
|
||||
flyctlArgs.push('--org', options.org);
|
||||
}
|
||||
|
||||
waspSays(`Running ${options.context} command: flyctl ${flyctlArgs.join(' ')}`);
|
||||
|
||||
if (!buildDirExists(options.waspProjectDir)) {
|
||||
@ -23,7 +26,7 @@ export async function cmd(flyctlArgs: string[], options: CmdOptions): Promise<vo
|
||||
await runFlyctlCommand(commonOps, flyctlArgs);
|
||||
}
|
||||
|
||||
async function runFlyctlCommand(commonOps: CommonOps, flyctlArgs: string[]) {
|
||||
async function runFlyctlCommand(commonOps: CommonOps, flyctlArgs: string[]): Promise<void> {
|
||||
commonOps.cdToBuildDir();
|
||||
deleteLocalToml();
|
||||
if (commonOps.tomlExistsInProject()) {
|
||||
@ -33,8 +36,6 @@ async function runFlyctlCommand(commonOps: CommonOps, flyctlArgs: string[]) {
|
||||
try {
|
||||
await $`flyctl ${flyctlArgs}`;
|
||||
} catch {
|
||||
waspSays('Error running command. Note: many commands require a toml file or a -a option specifying the app name.');
|
||||
waspSays(`If you already have an app, consider running "${getCommandHelp(executeFlyCommand).replace('<cmd...>', 'config save -- -a <app-name>')}".`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,11 @@ export async function createDb(region: string, options: CreateDbOptions): Promis
|
||||
'--initial-cluster-size', options.initialClusterSize,
|
||||
'--volume-size', options.volumeSize,
|
||||
];
|
||||
|
||||
if (deploymentInfo.options.org) {
|
||||
createArgs.push('--org', deploymentInfo.options.org);
|
||||
}
|
||||
|
||||
await $`flyctl postgres create ${createArgs}`;
|
||||
await $`flyctl postgres attach ${deploymentInfo.dbName} -a ${deploymentInfo.serverName}`;
|
||||
|
||||
|
@ -65,7 +65,7 @@ export async function deploy(options: DeployOptions): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function deployServer(deploymentInfo: DeploymentInfo, { buildLocally }: DeployOptions) {
|
||||
async function deployServer(deploymentInfo: DeploymentInfo<DeployOptions>, { buildLocally }: DeployOptions) {
|
||||
waspSays('Deploying your server now...');
|
||||
|
||||
cdToServerBuildDir(deploymentInfo.options.waspProjectDir);
|
||||
@ -79,7 +79,6 @@ async function deployServer(deploymentInfo: DeploymentInfo, { buildLocally }: De
|
||||
exit(1);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
waspSays('Unable to check for DATABASE_URL secret.');
|
||||
exit(1);
|
||||
}
|
||||
@ -97,7 +96,7 @@ async function deployServer(deploymentInfo: DeploymentInfo, { buildLocally }: De
|
||||
waspSays('Server has been deployed!');
|
||||
}
|
||||
|
||||
async function deployClient(deploymentInfo: DeploymentInfo, { buildLocally }: DeployOptions) {
|
||||
async function deployClient(deploymentInfo: DeploymentInfo<DeployOptions>, { buildLocally }: DeployOptions) {
|
||||
waspSays('Deploying your client now...');
|
||||
|
||||
cdToClientBuildDir(deploymentInfo.options.waspProjectDir);
|
||||
|
@ -62,7 +62,6 @@ export async function ensureRegionIsValid(region: string): Promise<void> {
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore any errors while checking. Commands requiring a valid region will still fail if invalid, just not as nicely.
|
||||
console.error(e);
|
||||
waspSays('Unable to validate region before calling flyctl.');
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,13 @@ class FlyCommand extends Command {
|
||||
addLocalBuildOption(): this {
|
||||
return this.option('--build-locally', 'build Docker containers locally instead of remotely', false);
|
||||
}
|
||||
addSecretsOptions(): this {
|
||||
function collect(value: string, previous: string[]) {
|
||||
return previous.concat([value]);
|
||||
}
|
||||
return this.option('--server-secret <serverSecret>', 'secret to set on the server app (of form FOO=BAR)', collect, [])
|
||||
.option('--client-secret <clientSecret>', 'secret to set on the client app (of form FOO=BAR)', collect, []);
|
||||
}
|
||||
}
|
||||
|
||||
const flyLaunchCommand = makeFlyLaunchCommand();
|
||||
@ -53,6 +60,7 @@ export function addFlyCommand(program: Command): void {
|
||||
cmd.addOption(new Option('--wasp-exe <path>', 'Wasp executable (either on PATH or absolute path)').hideHelp().makeOptionMandatory())
|
||||
.addOption(new Option('--wasp-project-dir <dir>', 'absolute path to Wasp project dir').hideHelp().makeOptionMandatory())
|
||||
.option('--fly-toml-dir <dir>', 'absolute path to dir where fly.toml files live')
|
||||
.option('--org <org>', 'Fly org to use (with commands that support it)')
|
||||
.hook('preAction', ensureFlyReady)
|
||||
.hook('preAction', ensureDirsInCmdAreAbsoluteAndPresent)
|
||||
.hook('preAction', ensureWaspDirLooksRight);
|
||||
@ -71,6 +79,7 @@ function makeFlyLaunchCommand(): Command {
|
||||
.addRegionArgument()
|
||||
.addDbOptions()
|
||||
.addLocalBuildOption()
|
||||
.addSecretsOptions()
|
||||
.action(launchFn);
|
||||
}
|
||||
|
||||
@ -79,6 +88,7 @@ function makeFlySetupCommand(): Command {
|
||||
.description('Set up a new app on Fly.io (this does not deploy it)')
|
||||
.addBasenameArgument()
|
||||
.addRegionArgument()
|
||||
.addSecretsOptions()
|
||||
.action(setupFn);
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
import { CommonOptions, DbOptions, LocalBuildOptions } from '../CommonOptions.js';
|
||||
import { CommonOptions, DbOptions, LocalBuildOptions, SecretsOptions } from '../CommonOptions.js';
|
||||
|
||||
export interface LaunchOptions extends CommonOptions, DbOptions, LocalBuildOptions {}
|
||||
export interface LaunchOptions extends CommonOptions, DbOptions, LocalBuildOptions, SecretsOptions { }
|
||||
|
@ -20,7 +20,6 @@ export async function launch(basename: string, region: string, options: LaunchOp
|
||||
try {
|
||||
await setup(basename, region, options);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
waspSays(`There was an error running "${getCommandHelp(flySetupCommand)}". Please review the error and try again (if appropriate).`);
|
||||
exit(1);
|
||||
}
|
||||
@ -28,7 +27,6 @@ export async function launch(basename: string, region: string, options: LaunchOp
|
||||
try {
|
||||
await createDb(region, options);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
waspSays(`There was an error running "${getCommandHelp(createFlyDbCommand)}". Please review the error and try again (if appropriate).`);
|
||||
exit(1);
|
||||
}
|
||||
@ -37,7 +35,6 @@ export async function launch(basename: string, region: string, options: LaunchOp
|
||||
const deployOptions: DeployOptions = { ...options, skipBuild: true };
|
||||
await deploy(deployOptions);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
waspSays(`There was an error running "${getCommandHelp(flyDeployCommand)}". Please review the error and try again (if appropriate).`);
|
||||
exit(1);
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
import { CommonOptions, SecretsOptions } from '../CommonOptions.js';
|
||||
|
||||
export interface SetupOptions extends CommonOptions, SecretsOptions { }
|
@ -10,11 +10,11 @@ import {
|
||||
serverTomlExistsInProject,
|
||||
} from '../helpers/tomlFileHelpers.js';
|
||||
import { createDeploymentInfo, DeploymentInfo } from '../DeploymentInfo.js';
|
||||
import { CommonOptions } from '../CommonOptions.js';
|
||||
import { SetupOptions } from './SetupOptions.js';
|
||||
import { cdToClientBuildDir, cdToServerBuildDir, makeIdempotent, getCommandHelp, waspSays } from '../helpers/helpers.js';
|
||||
import { createFlyDbCommand } from '../index.js';
|
||||
|
||||
export async function setup(baseName: string, region: string, options: CommonOptions): Promise<void> {
|
||||
export async function setup(baseName: string, region: string, options: SetupOptions): Promise<void> {
|
||||
waspSays('Setting up your Wasp app with Fly.io!');
|
||||
|
||||
const buildWasp = makeIdempotent(async () => {
|
||||
@ -43,32 +43,65 @@ export async function setup(baseName: string, region: string, options: CommonOpt
|
||||
waspSays(`Don't forget to create your database by running "${getCommandHelp(createFlyDbCommand)}".`);
|
||||
}
|
||||
|
||||
async function setupServer(deploymentInfo: DeploymentInfo) {
|
||||
async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
|
||||
waspSays(`Setting up server app with name ${deploymentInfo.serverName}`);
|
||||
|
||||
cdToServerBuildDir(deploymentInfo.options.waspProjectDir);
|
||||
deleteLocalToml();
|
||||
|
||||
const launchArgs = [
|
||||
'--name', deploymentInfo.serverName,
|
||||
'--region', deploymentInfo.region,
|
||||
];
|
||||
|
||||
if (deploymentInfo.options.org) {
|
||||
launchArgs.push('--org', deploymentInfo.options.org);
|
||||
}
|
||||
|
||||
// This creates the fly.toml file, but does not attempt to deploy.
|
||||
await $`flyctl launch --no-deploy --name ${deploymentInfo.serverName} --region ${deploymentInfo.region}`;
|
||||
await $`flyctl launch --no-deploy ${launchArgs}`;
|
||||
|
||||
copyLocalServerTomlToProject(deploymentInfo.tomlFilePaths);
|
||||
|
||||
const randomString = crypto.randomBytes(32).toString('hex');
|
||||
await $`flyctl secrets set JWT_SECRET=${randomString} PORT=8080 WASP_WEB_CLIENT_URL=${deploymentInfo.clientUrl}`;
|
||||
|
||||
const secretsArgs = [
|
||||
`JWT_SECRET=${randomString}`,
|
||||
// NOTE: Normally these would just be envars, but flyctl
|
||||
// doesn't provide a way to set envars that persist to fly.toml.
|
||||
'PORT=8080',
|
||||
`WASP_WEB_CLIENT_URL=${deploymentInfo.clientUrl}`,
|
||||
];
|
||||
|
||||
if (deploymentInfo.options.serverSecret.length > 0) {
|
||||
deploymentInfo.options.serverSecret.forEach(secret => {
|
||||
secretsArgs.push(secret);
|
||||
});
|
||||
}
|
||||
|
||||
await $`flyctl secrets set ${secretsArgs}`;
|
||||
|
||||
console.log(''); // `flyctl secrets` does not produce it's own newline.
|
||||
waspSays('Server setup complete!');
|
||||
}
|
||||
|
||||
async function setupClient(deploymentInfo: DeploymentInfo) {
|
||||
async function setupClient(deploymentInfo: DeploymentInfo<SetupOptions>) {
|
||||
waspSays(`Setting up client app with name ${deploymentInfo.clientName}`);
|
||||
|
||||
cdToClientBuildDir(deploymentInfo.options.waspProjectDir);
|
||||
deleteLocalToml();
|
||||
|
||||
const launchArgs = [
|
||||
'--name', deploymentInfo.clientName,
|
||||
'--region', deploymentInfo.region,
|
||||
];
|
||||
|
||||
if (deploymentInfo.options.org) {
|
||||
launchArgs.push('--org', deploymentInfo.options.org);
|
||||
}
|
||||
|
||||
// This creates the fly.toml file, but does not attempt to deploy.
|
||||
await $`flyctl launch --no-deploy --name ${deploymentInfo.clientName} --region ${deploymentInfo.region}`;
|
||||
await $`flyctl launch --no-deploy ${launchArgs}`;
|
||||
|
||||
// goStatic listens on port 8043 by default, but the default fly.toml
|
||||
// assumes port 8080 (or 3000, depending on flyctl version).
|
||||
@ -76,5 +109,9 @@ async function setupClient(deploymentInfo: DeploymentInfo) {
|
||||
|
||||
copyLocalClientTomlToProject(deploymentInfo.tomlFilePaths);
|
||||
|
||||
if (deploymentInfo.options.clientSecret.length > 0) {
|
||||
await $`flyctl secrets set ${deploymentInfo.options.clientSecret}`;
|
||||
}
|
||||
|
||||
waspSays('Client setup complete!');
|
||||
}
|
||||
|
@ -65,11 +65,22 @@ wasp deploy fly cmd secrets list --context server
|
||||
|
||||
:::note
|
||||
If you are deploying an app that requires any other environment variables (like social auth secrets), you will want to set your environment variables up like so:
|
||||
|
||||
During `launch`:
|
||||
```
|
||||
wasp deploy fly launch my-wasp-app mia --server-secret GOOGLE_CLIENT_ID=<...> --server-secret GOOGLE_CLIENT_SECRET=<...>
|
||||
```
|
||||
|
||||
After `launch`/`setup`:
|
||||
```
|
||||
wasp deploy fly cmd secrets set GOOGLE_CLIENT_ID=<...> GOOGLE_CLIENT_SECRET=<...> --context=server
|
||||
```
|
||||
:::
|
||||
|
||||
:::note
|
||||
If you have multiple orgs, you can specify a `--org` option. For example: `wasp deploy fly launch my-wasp-app mia --org hive`
|
||||
:::
|
||||
|
||||
# Manual
|
||||
|
||||
In addition to the CLI, you can deploy a Wasp project by generating the code and then deploying generated code "manually", as explained below.
|
||||
|
Loading…
Reference in New Issue
Block a user