Sets the minimum number of Fly machines to 1 (#1535)

This commit is contained in:
Mihovil Ilakovac 2023-11-06 15:47:45 +01:00 committed by GitHub
parent 2b47abd36f
commit 2752f889c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 157 additions and 30 deletions

View File

@ -17,6 +17,10 @@ app todoApp {
}
```
### 🐞 Bug fixes / 🔧 small improvements
- Changed the minimum number of machines that a server app is using when deployed to Fly.io from 0 to 1. This prevents the server app from shutting down when there are no requests to it. There might be some other work that the server is doing e.g. running periodic Jobs or sending e-mails, so we want to make sure that the server is always running.
## 0.11.7
### 🐞 Bug fixes / 🔧 small improvements

View File

@ -21,6 +21,7 @@
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"eslint": "^8.31.0",
"prettier": "^2.8.8",
"typescript": "^4.9.4"
}
},
@ -1504,6 +1505,21 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/ps-tree": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
@ -2964,6 +2980,12 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true
},
"ps-tree": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",

View File

@ -20,6 +20,7 @@
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"eslint": "^8.31.0",
"prettier": "^2.8.8",
"typescript": "^4.9.4"
}
}

View File

@ -9,12 +9,16 @@ export function isYes(str: string): boolean {
}
export function ensureWaspDirLooksRight(thisCommand: Command): void {
const dirContainsWasproot = fs.existsSync(path.join(thisCommand.opts().waspProjectDir, '.wasproot'));
const dirContainsWasproot = fs.existsSync(
path.join(thisCommand.opts().waspProjectDir, '.wasproot'),
);
if (dirContainsWasproot) {
return;
}
waspSays('The supplied Wasp directory does not appear to be a valid Wasp project.');
waspSays(
'The supplied Wasp directory does not appear to be a valid Wasp project.',
);
waspSays('Please double check your path.');
exit(1);
}
@ -38,8 +42,11 @@ function getWaspBuildDir(waspProjectDir: string) {
return path.join(waspProjectDir, '.wasp', 'build');
}
export function ensureDirsInCmdAreAbsoluteAndPresent(thisCommand: Command): void {
const waspProjectDirPath: string | undefined = thisCommand.opts().waspProjectDir;
export function ensureDirsInCmdAreAbsoluteAndPresent(
thisCommand: Command,
): void {
const waspProjectDirPath: string | undefined =
thisCommand.opts().waspProjectDir;
if (waspProjectDirPath) {
if (!path.isAbsolute(waspProjectDirPath)) {
waspSays('The Wasp dir path must be absolute.');
@ -88,6 +95,10 @@ export function waspSays(str: string): void {
console.log('🚀 \x1b[33m ' + str + ' \x1b[0m');
}
export function boldText(str: string): string {
return '\x1b[1m' + str + '\x1b[0m';
}
export function displayWaspRocketImage(): void {
// Escaping backslashes makes it look weird here, but it works in console.
const asciiArt = `
@ -109,7 +120,10 @@ export function getCommandHelp(command: Command): string {
}
function trimUsage(usage: string): string {
return usage.split(/[\r\n]+/)[0].replace('Usage: ', '').replace(' [options]', '');
return usage
.split(/[\r\n]+/)[0]
.replace('Usage: ', '')
.replace(' [options]', '');
}
// There is a theoretical race condition here since we are modifying a global `$`
@ -118,7 +132,9 @@ function trimUsage(usage: string): string {
// times concurrently could change the setting incorrectly.
// However, our pattern of awaiting for both `$` and `silence` calls without any random
// callbacks using either means this interleaving should not ever happen.
export async function silence(cmd: ($hh: Shell) => Promise<ProcessOutput>): Promise<ProcessOutput> {
export async function silence(
cmd: ($hh: Shell) => Promise<ProcessOutput>,
): Promise<ProcessOutput> {
const verboseSetting = $.verbose;
$.verbose = false;
const proc = await cmd($);

View File

@ -4,8 +4,8 @@ import path from 'node:path';
import { CommonOptions } from '../CommonOptions.js';
export interface TomlFilePaths {
serverTomlPath: string;
clientTomlPath: string;
serverTomlPath: string;
clientTomlPath: string;
}
export function getTomlFilePaths(options: CommonOptions): TomlFilePaths {
@ -56,18 +56,30 @@ export function getAppNameFromToml(path: string): string {
return data.app;
}
export function getInferredBasenameFromServerToml(paths: TomlFilePaths): string {
export function getInferredBasenameFromServerToml(
paths: TomlFilePaths,
): string {
const serverName = getAppNameFromToml(paths.serverTomlPath);
return serverName.replace('-server', '');
}
export function getInferredBasenameFromClientToml(paths: TomlFilePaths): string {
export function getInferredBasenameFromClientToml(
paths: TomlFilePaths,
): string {
const clientName = getAppNameFromToml(paths.clientTomlPath);
return clientName.replace('-client', '');
}
export function replaceLineInLocalToml(searchValue: string | RegExp, replaceValue: string): void {
export function replaceLineInLocalToml(
searchValue: string | RegExp,
replaceValue: string,
): void {
const content = fs.readFileSync('fly.toml', 'utf8');
const updatedContent = content.replace(searchValue, replaceValue);
fs.writeFileSync('fly.toml', updatedContent);
}
export function doesLocalTomlContainLine(searchValue: string | RegExp): boolean {
const content = fs.readFileSync('fly.toml', 'utf8');
return content.search(searchValue) !== -1;
}

View File

@ -1,20 +1,32 @@
import { $, cd } from 'zx';
import { $, cd, question } from 'zx';
import crypto from 'crypto';
import {
clientTomlExistsInProject,
copyLocalClientTomlToProject,
copyLocalServerTomlToProject,
deleteLocalToml,
doesLocalTomlContainLine,
getTomlFilePaths,
replaceLineInLocalToml,
serverTomlExistsInProject,
} from '../helpers/tomlFileHelpers.js';
import { createDeploymentInfo, DeploymentInfo } from '../DeploymentInfo.js';
import { SetupOptions } from './SetupOptions.js';
import { cdToClientBuildDir, cdToServerBuildDir, makeIdempotent, getCommandHelp, waspSays } from '../helpers/helpers.js';
import {
cdToClientBuildDir,
cdToServerBuildDir,
makeIdempotent,
getCommandHelp,
waspSays,
boldText,
} from '../helpers/helpers.js';
import { createFlyDbCommand } from '../index.js';
export async function setup(baseName: string, region: string, options: SetupOptions): 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 () => {
@ -24,7 +36,12 @@ export async function setup(baseName: string, region: string, options: SetupOpti
});
const tomlFilePaths = getTomlFilePaths(options);
const deploymentInfo = createDeploymentInfo(baseName, region, options, tomlFilePaths);
const deploymentInfo = createDeploymentInfo(
baseName,
region,
options,
tomlFilePaths,
);
if (serverTomlExistsInProject(tomlFilePaths)) {
waspSays(`${tomlFilePaths.serverTomlPath} exists. Skipping server setup.`);
@ -40,7 +57,11 @@ export async function setup(baseName: string, region: string, options: SetupOpti
await setupClient(deploymentInfo);
}
waspSays(`Don't forget to create your database by running "${getCommandHelp(createFlyDbCommand)}".`);
waspSays(
`Don't forget to create your database by running "${getCommandHelp(
createFlyDbCommand,
)}".`,
);
}
async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
@ -50,8 +71,10 @@ async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
deleteLocalToml();
const launchArgs = [
'--name', deploymentInfo.serverName,
'--region', deploymentInfo.region,
'--name',
deploymentInfo.serverName,
'--region',
deploymentInfo.region,
];
if (deploymentInfo.options.org) {
@ -61,6 +84,31 @@ async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
// This creates the fly.toml file, but does not attempt to deploy.
await $`flyctl launch --no-deploy ${launchArgs}`;
const minMachinesOptionRegex = /min_machines_running = 0/g;
if (!doesLocalTomlContainLine(minMachinesOptionRegex)) {
await question(`\n⚠ There was a possible issue setting up your server app.
We tried modifying your server fly.toml to set ${boldText(
'min_machines_running = 1',
)}, but couldn't find the option ${boldText(
'min_machines_running',
)} in the fly.toml.
We advise that you additionaly check what is the value for "minimal number of machines running" on Fly
for this server app and confirm that it is set to the value you are OK with.
Be aware that if it is set to 0, your server will shut down when there are no requests from the client,
which might be an issue for you if you have recurring Jobs or some other processes that need to keep
running on the server even without external input, in which case we advise keeping "minimal number
of machines running" setting at a number larger than zero.
Contact the Wasp Team at our Discord server if you need help with this: https://discord.gg/rzdnErX
Press any key to continue or Ctrl+C to cancel.`);
} else {
replaceLineInLocalToml(minMachinesOptionRegex, 'min_machines_running = 1');
}
copyLocalServerTomlToProject(deploymentInfo.tomlFilePaths);
const randomString = crypto.randomBytes(32).toString('hex');
@ -74,7 +122,7 @@ async function setupServer(deploymentInfo: DeploymentInfo<SetupOptions>) {
];
if (deploymentInfo.options.serverSecret.length > 0) {
deploymentInfo.options.serverSecret.forEach(secret => {
deploymentInfo.options.serverSecret.forEach((secret) => {
secretsArgs.push(secret);
});
}
@ -92,8 +140,10 @@ async function setupClient(deploymentInfo: DeploymentInfo<SetupOptions>) {
deleteLocalToml();
const launchArgs = [
'--name', deploymentInfo.clientName,
'--region', deploymentInfo.region,
'--name',
deploymentInfo.clientName,
'--region',
deploymentInfo.region,
];
if (deploymentInfo.options.org) {
@ -103,9 +153,26 @@ async function setupClient(deploymentInfo: DeploymentInfo<SetupOptions>) {
// This creates the fly.toml file, but does not attempt to deploy.
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).
replaceLineInLocalToml(/internal_port = \d+/g, 'internal_port = 8043');
const internalPortOptionRegex = /internal_port = \d+/g;
if (!doesLocalTomlContainLine(internalPortOptionRegex)) {
await question(`\n⚠ There was an issue setting up your client app.
We tried modifying your client fly.toml to set ${boldText(
'internal_port = 8043',
)}, but couldn't find the option ${boldText(
'internal_port',
)} in the fly.toml.
This means your client app might not be accessible.
Contact the Wasp Team at our Discord server if you need help with this: https://discord.gg/rzdnErX
Press any key to continue or Ctrl+C to cancel.`);
} else {
// goStatic listens on port 8043 by default, but the default fly.toml
// assumes port 8080 (or 3000, depending on flyctl version).
replaceLineInLocalToml(internalPortOptionRegex, 'internal_port = 8043');
}
copyLocalClientTomlToProject(deploymentInfo.tomlFilePaths);

View File

@ -65,7 +65,6 @@ data-files:
packages/studio/dist/**/*.js
packages/studio/dist/**/*.html
packages/studio/dist/**/*.css
packages/studio/dist/**/*.png
packages/studio/package.json
packages/studio/package-lock.json
data-dir: data/

View File

@ -47,7 +47,11 @@ wasp deploy fly create-db mia
wasp deploy fly deploy
```
The commands above use the app basename `my-wasp-app` and deploy it to the _Miami, Florida (US) region_ (called `mia`).
The commands above use the app basename `my-wasp-app` and deploy it to the _Miami, Florida (US) region_ (called `mia`). Read more about Fly.io regions [here](#flyio-regions).
:::caution Unique Name
Your app name must be unique across all of Fly or deployment will fail.
:::
The basename is used to create all three app tiers, resulting in three separate apps in your Fly dashboard:
@ -55,11 +59,11 @@ The basename is used to create all three app tiers, resulting in three separate
- `my-wasp-app-server`
- `my-wasp-app-db`
:::caution Unique Name
Your app name must be unique across all of Fly or deployment will fail.
:::
You'll notice that Wasp creates two new files in your project root directory:
- `fly-server.toml`
- `fly-client.toml`
Read more about Fly.io regions [here](#flyio-regions).
You should include these files in your version control so that you can deploy your app with a single command in the future.
### Using a Custom Domain For Your App
@ -159,6 +163,8 @@ It accepts the following arguments:
After running `setup`, Wasp creates two new files in your project root directory: `fly-server.toml` and `fly-client.toml`.
You should include these files in your version control.
You **can edit the `fly-server.toml` and `fly-client.toml` files** to further configure your Fly deployments. Wasp will use the TOML files when you run `deploy`.
If you want to maintain multiple apps, you can add the `--fly-toml-dir <abs-path>` option to point to different directories, like "dev" or "staging".
:::caution Execute Only Once