зеркало из
1
0
Форкнуть 0

refactor: clean up cli context

This commit is contained in:
Wassim Chegham 2022-04-15 20:46:02 +02:00 коммит произвёл Wassim Chegham
Родитель a509c897b1
Коммит 53381e4d46
7 изменённых файлов: 129 добавлений и 70 удалений

Просмотреть файл

@ -160,9 +160,7 @@ A **Deployment Token** is required in order to make a deployment! Read the steps
### Deployment token
Before deploying your application to Azure Static Web Apps, you need to create a new application instance. This is done by following the instructions in the [Azure Static Web Apps documentation](https://docs.microsoft.com/en-us/azure/static-web-apps/get-started-portal).
Once your application instance is ready, you can get a deployment token from:
The CLI supports Deployment token. This is usually useful when deploying from a CI/CD environment. You can get a deployment token either from:
- The [Azure portal](https://portal.azure.com/): **Home → Static Web App → Your Instance → Overview → Manage deployment token**
@ -172,7 +170,13 @@ Once your application instance is ready, you can get a deployment token from:
az staticwebapp secrets list --name <application-name> --query "properties.apiKey"
```
You can then use that value with the `--deployment-token <token>`, or you can create an environment variable called `SWA_CLI_DEPLOYMENT_TOKEN` and set it to the deployment token. Read the next section for more details.
- If you are using the [Azure Static Web Apps CLI (this project)](aka.ms/swa/cli-local-development), you can get the deployment token of your project using the following command:
```bash
swa deploy --print-token
```
You can then use that value with the `--deployment-token <token>` (e.g. from a CI/CD environment), or you can create an environment variable called `SWA_CLI_DEPLOYMENT_TOKEN` and set it to the deployment token. Read the next section for more details.
**IMPORTANT:** Don't store the deployment token in a public repository. It should be kept secret!
@ -185,10 +189,11 @@ You can deploy a front-end application (without an API) to Azure Static Web Apps
**Option 1:** From build folder you would like to deploy, run the deploy command:
```bash
swa deploy --deployment-token <token>
cd build/
swa deploy
```
> Note: the current folder must contain the static content of your app to be deployed!
> Note: the "build" folder must contain the static content of your app to be deployed!
**Option 2:** You can also deploy a specific folder:
@ -197,7 +202,7 @@ swa deploy --deployment-token <token>
2. Deploy your app:
```bash
swa deploy ./my-dist --deployment-token <token>
swa deploy ./my-dist
```
### Deploy a front-end app with an API
@ -219,7 +224,7 @@ To deploy both the front-end app and an API to Azure Static Web Apps, use the fo
3. Deploy your app:
```bash
swa deploy ./my-dist --api-location ./api --deployment-token <token>
swa deploy ./my-dist --api-location ./api
```
### Deploy a Blazor app
@ -235,41 +240,26 @@ dotnet publish -c Release
2. From the root of your project, run the deploy command:
```bash
swa deploy ./Client/bin/Release/net6.0/publish/wwwroot --api-location ./Api --deployment-token <token>
swa deploy ./Client/bin/Release/net6.0/publish/wwwroot --api-location ./Api
```
3.a (Optional) create a `swa-cli.config.json` file at the root of your project with the following content:
```json
{
"configurations": {
"deploy": {
"context": "./",
"apiLocation": "./Api",
"outputLocation": "Client/bin/Release/net6.0/publish/wwwroot"
}
}
}
```
3.b Deploy your app using the configuration file:
```bash
swa deploy --deployment-token <token>
```
### Deploy using the swa-cli.config.json
### Deploy using the `swa-cli.config.json`
If you are using a [`swa-cli.config.json`](#swa-cli.config.json) configuration file in your project and have a single configuration entry, for example:
```json
{
"configurations": {
"app": {
"my-app": {
"appLocation": "./",
"context": "./",
"outputLocation": "./front-end",
"apiLocation": "./api"
"apiLocation": "api",
"outputLocation": "frontend",
"start": {
"context": "frontend"
},
"deploy": {
"context": "frontend"
}
}
}
}
@ -288,7 +278,7 @@ swa deploy
If you have multiple configuration entries, you can provide the entry ID to specify which one to use:
```bash
swa deploy otherapp
swa deploy my-otherapp
```
## Use a runtime configuration file (staticwebapp.config.json)
@ -332,6 +322,10 @@ If you need to override the default values for the `swa` command, you can provid
| `--print-config` | Print all resolved options | `false` | `--print-config` or `--print-config=true` |
| `--swa-config-location` | The directory where the `staticwebapp.config.json` file is located | `./` | `--swa-config-location=./app` |
### Subcommand `swa login` options
TODO
### Subcommand `swa start` options
If you need to override the default values for the `swa start` subcommand, you can provide the following options:
@ -355,13 +349,16 @@ If you need to override the default values for the `swa start` subcommand, you c
If you need to override the default values for the `swa deploy` subcommand, you can provide the following options:
| Option | Description | Default | Example |
| -------------------- | -------------------------------------------------------------- | --------- | ----------------------------------------- |
| `--api-location` | The folder containing the source code of the API application | `./api` | `--api-location="./api"` |
| `--deployment-token` | The secret toekn used to authenticate with the Static Web Apps | | `--deployment-token="123"` |
| `--dry-run` | Simulate a deploy process without actually running it | `false` | `--dry-run` |
| `--print-token` | print the deployment token | `false` | `--print-token` |
| `--env` | the type of deployment environment where to deploy the project | `preview` | `--env="production"` or `--env="preview"` |
| Option | Description | Default | Example |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------- |
| `--api-location` | The folder containing the source code of the API application | `./api` | `--api-location="./api"` |
| `--deployment-token` | The secret toekn used to authenticate with the Static Web Apps | | `--deployment-token="123"` |
| `--dry-run` | Simulate a deploy process without actually running it | `false` | `--dry-run` |
| `--print-token` | print the deployment token | `false` | `--print-token` |
| `--env` | the type of deployment environment where to deploy the project | `preview` | `--env="production"` or `--env="preview"` |
| `--print-token` | Print the deployment token. Usefull when using `--deployment-token` on CI/CD <br> Note: this command does not run the deployment process. | `false` | `--print-token` |
The deploy command does also support the same options as the `swa login` command.
<a id="swa-cli.config.json"></a>

Просмотреть файл

@ -16,7 +16,7 @@ import {
} from "../../core";
import { chooseOrCreateProjectDetails, getStaticSiteDeployment } from "../../core/account";
import { cleanUp, getDeployClientPath } from "../../core/deploy-client";
import { getSwaEnvList, swaCLIEnv } from "../../core/env";
import { swaCLIEnv } from "../../core/env";
import { addSharedLoginOptionsToCommand, login } from "./login";
const packageInfo = require(path.join(__dirname, "..", "..", "..", "package.json"));
@ -60,10 +60,12 @@ Examples:
addSharedLoginOptionsToCommand(deployCommand);
}
export async function deploy(deployContext: string, options: SWACLIConfig) {
export async function deploy(_deployContext: string | undefined, options: SWACLIConfig) {
const { SWA_CLI_DEPLOYMENT_TOKEN, SWA_CLI_DEBUG } = swaCLIEnv();
const isVerboseEnabled = SWA_CLI_DEBUG === "silly";
// TODO: get rid of _deployContext
if (options.dryRun) {
logger.warn("***********************************************************************");
logger.warn("* WARNING: Running in dry run mode. This project will not be deployed *");
@ -71,14 +73,34 @@ export async function deploy(deployContext: string, options: SWACLIConfig) {
logger.warn("");
}
const frontendFolder = path.resolve(process.cwd(), deployContext);
// make sure outputLocation is set
if (options.outputLocation) {
options.outputLocation = path.resolve(options.outputLocation);
}
// make sure appLocation is set
if (!options.appLocation) {
options.appLocation = path.resolve(process.cwd());
}
// make sure outputLocation is set
if (!options.outputLocation) {
options.outputLocation = path.resolve(process.cwd());
}
// if folder exists, deploy from a specific build folder (outputLocation), relative to appLocation
if (!fs.existsSync(options.outputLocation)) {
logger.error(`The folder "${options.outputLocation}" is not found. Exit.`, true);
return;
}
logger.log(`Deploying front-end files from folder:`);
logger.log(` ${chalk.green(frontendFolder)}`);
logger.log(` ${chalk.green(options.outputLocation)}`);
logger.log(``);
// if --api-location is provided, use it as the api folder
if (options.apiLocation) {
const userApiFolder = path.resolve(process.cwd(), options.apiLocation!);
const userApiFolder = path.resolve(options.appLocation as string, options.apiLocation!);
if (!fs.existsSync(userApiFolder)) {
logger.error(`The provided API folder ${userApiFolder} does not exist. Abort.`, true);
return;
@ -94,6 +116,7 @@ export async function deploy(deployContext: string, options: SWACLIConfig) {
if (fs.existsSync(defaultApiFolder)) {
logger.warn(
`An API folder was found at ".${
// TODO: should handle ./Api and ./api
path.sep + path.basename(defaultApiFolder)
}" but the --api-location option was not provided. The API will not be deployed.\n`
);
@ -113,9 +136,6 @@ export async function deploy(deployContext: string, options: SWACLIConfig) {
logger.silly(`No deployment token found. Trying interactive login...`);
try {
logger.silly(options);
logger.silly(getSwaEnvList());
const { credentialChain, subscriptionId } = await login({
...options,
});
@ -220,11 +240,11 @@ export async function deploy(deployContext: string, options: SWACLIConfig) {
const deployClientEnv: StaticSiteClientEnv = {
DEPLOYMENT_ACTION: options.dryRun ? "close" : "upload",
DEPLOYMENT_PROVIDER: `swa-cli-${packageInfo.version}`,
REPOSITORY_BASE: deployContext,
REPOSITORY_BASE: options.appLocation,
SKIP_APP_BUILD: "true",
SKIP_API_BUILD: "true",
DEPLOYMENT_TOKEN: deploymentToken,
APP_LOCATION: deployContext,
APP_LOCATION: options.appLocation,
OUTPUT_LOCATION: options.outputLocation,
API_LOCATION: options.apiLocation,
VERBOSE: isVerboseEnabled ? "true" : "false",

Просмотреть файл

@ -59,6 +59,7 @@ export async function init(name: string | undefined, options: SWACLIConfig, show
};
projectConfig = await promptConfigSettings(disablePrompts, projectConfig);
logger.silly(projectConfig);
// printFrameworkConfig(projectConfig);
@ -107,7 +108,7 @@ function convertToCliConfig(config: FrameworkConfig): SWACLIConfig {
apiBuildCommand: config.apiBuildCommand,
run: config.devServerCommand,
start: {
context: config.devServerUrl || config.appLocation,
context: config.devServerUrl || config.outputLocation,
},
deploy: {
context: config.outputLocation,

Просмотреть файл

@ -105,16 +105,19 @@ export async function start(startContext: string | undefined, options: SWACLICon
logger.silly(`Resolved port number: ${resolvedPortNumber}`);
options.port = resolvedPortNumber;
// start context should never be undefined but we'll check anyway!
logger.silly(`Resolving outputLocation...`);
// start context should never be undefined (will default to ./) but we'll check anyway!
// if the user didn't provide a context, use the current directory
if (!startContext) {
startContext = DEFAULT_CONFIG.outputLocation;
startContext = options.outputLocation;
} else {
if (isHttpUrl(startContext)) {
useAppDevServer = startContext;
options.outputLocation = useAppDevServer;
} else {
let outputLocationAbsolute = path.resolve(options.appLocation as string, startContext);
let outputLocationAbsolute = path.resolve(options.appLocation as string, (options.outputLocation as string) || startContext);
// if folder exists, start the emulator from a specific build folder (outputLocation), relative to appLocation
if (fs.existsSync(outputLocationAbsolute)) {
options.outputLocation = outputLocationAbsolute;
@ -129,6 +132,9 @@ export async function start(startContext: string | undefined, options: SWACLICon
}
}
logger.silly(`Resolved outputLocation:`);
logger.silly(` ${options.outputLocation}`);
if (options.apiLocation) {
// resolves to the absolute path of the apiLocation
let apiLocationAbsolute = path.resolve(options.appLocation as string, options.apiLocation);
@ -163,6 +169,9 @@ export async function start(startContext: string | undefined, options: SWACLICon
userWorkflowConfig = readWorkflowFile({
userWorkflowConfig,
});
logger.silly(`User workflow config:`);
logger.silly(userWorkflowConfig!);
} catch (err) {
logger.warn(``);
logger.warn(`Error reading workflow configuration:`);
@ -225,7 +234,7 @@ export async function start(startContext: string | undefined, options: SWACLICon
days: 365,
commonName: options.host,
organization: `Azure Static Web Apps CLI ${packageInfo.version}`,
organizationUnit: "Engineering",
organizationUnit: "Azure Engineering",
emailAddress: `secure@microsoft.com`,
});
options.sslCert = pemFilepath;
@ -315,8 +324,10 @@ export async function start(startContext: string | undefined, options: SWACLICon
await result
.then(
(code: CloseEvent[]) => {
logger.silly(`SWA emulator exited with code ${code.values().next().value}`);
(errorEvent: CloseEvent[]) => {
const killedCommand = errorEvent.filter((event) => event.killed).pop();
const exitCode = killedCommand?.exitCode;
logger.silly(`SWA emulator exited with code ${exitCode}`);
process.exit();
},
(errorEvent: CloseEvent[]) => {

Просмотреть файл

@ -93,8 +93,11 @@ export function validateUserWorkflowConfig(userWorkflowConfig: Partial<GithubAct
let apiLocation = undefined;
let outputLocation = undefined;
logger.silly(`Validating user workflow config (BEFORE):`);
logger.silly(userWorkflowConfig!);
if (userWorkflowConfig?.appLocation) {
appLocation = path.normalize(path.join(process.cwd(), userWorkflowConfig.appLocation || `.${path.sep}`));
appLocation = path.resolve(userWorkflowConfig.appLocation);
if (path.isAbsolute(userWorkflowConfig.appLocation)) {
appLocation = userWorkflowConfig.appLocation;
}
@ -105,7 +108,7 @@ export function validateUserWorkflowConfig(userWorkflowConfig: Partial<GithubAct
apiLocation = userWorkflowConfig.apiLocation;
} else {
// use the user's config and construct an absolute path
apiLocation = path.normalize(path.join(process.cwd(), userWorkflowConfig.apiLocation));
apiLocation = path.resolve(userWorkflowConfig.apiLocation);
}
if (path.isAbsolute(userWorkflowConfig.apiLocation)) {
@ -118,13 +121,20 @@ export function validateUserWorkflowConfig(userWorkflowConfig: Partial<GithubAct
if (isHttpUrl(userWorkflowConfig.outputLocation)) {
outputLocation = userWorkflowConfig.outputLocation;
} else {
outputLocation = path.normalize(path.join(process.cwd(), userWorkflowConfig.outputLocation || `.${path.sep}`));
outputLocation = path.resolve(userWorkflowConfig.outputLocation);
if (path.isAbsolute(userWorkflowConfig.outputLocation)) {
outputLocation = userWorkflowConfig.outputLocation;
}
}
}
logger.silly(`Validating user workflow config (AFTER):`);
logger.silly({
appLocation,
apiLocation,
outputLocation,
});
return {
appLocation,
apiLocation,

Просмотреть файл

@ -17,12 +17,18 @@ export function readWorkflowFile({ userWorkflowConfig }: { userWorkflowConfig?:
| undefined {
let isAppDevServer = false;
let isApiDevServer = false;
logger.silly(`Trying to read workflow config with values:`);
logger.silly(userWorkflowConfig!);
if (userWorkflowConfig) {
// is dev servers? Skip reading workflow file
isAppDevServer = isHttpUrl(userWorkflowConfig?.outputLocation!);
isApiDevServer = isHttpUrl(userWorkflowConfig?.apiLocation!);
if (isAppDevServer && isApiDevServer) {
return userWorkflowConfig && validateUserWorkflowConfig(userWorkflowConfig);
logger.silly(`Detected dev server configuration`);
return userWorkflowConfig;
}
}
@ -30,6 +36,8 @@ export function readWorkflowFile({ userWorkflowConfig }: { userWorkflowConfig?:
// does the config folder exist?
if (fs.existsSync(githubActionFolder) === false) {
logger.silly(`No workflow config folder found at ${githubActionFolder}`);
// no github actions folder found
return userWorkflowConfig && validateUserWorkflowConfig(userWorkflowConfig);
}
@ -42,7 +50,9 @@ export function readWorkflowFile({ userWorkflowConfig }: { userWorkflowConfig?:
.pop();
// does the config file exist?
if (!githubActionFile || fs.existsSync(githubActionFile)) {
if (!githubActionFile || fs.existsSync(githubActionFile) === false) {
logger.silly(`No workflow config file found at ${githubActionFile}`);
// no SWA workflow file found
return userWorkflowConfig && validateUserWorkflowConfig(userWorkflowConfig);
}
@ -88,13 +98,21 @@ export function readWorkflowFile({ userWorkflowConfig }: { userWorkflowConfig?:
// extract the user's config and set defaults
let {
app_build_command = DEFAULT_CONFIG.appBuildCommand,
api_build_command = DEFAULT_CONFIG.apiBuildCommand,
app_location = DEFAULT_CONFIG.appLocation,
output_location = DEFAULT_CONFIG.outputLocation,
api_location = DEFAULT_CONFIG.apiLocation,
app_build_command = userWorkflowConfig?.appBuildCommand || DEFAULT_CONFIG.appBuildCommand,
api_build_command = userWorkflowConfig?.apiBuildCommand || DEFAULT_CONFIG.apiBuildCommand,
app_location = userWorkflowConfig?.appLocation || DEFAULT_CONFIG.appLocation,
output_location = userWorkflowConfig?.outputLocation || DEFAULT_CONFIG.outputLocation,
api_location = userWorkflowConfig?.apiLocation || DEFAULT_CONFIG.apiLocation,
} = swaBuildConfig.with;
logger.silly({
app_build_command,
api_build_command,
app_location,
output_location,
api_location,
});
// the following locations (extracted from the config) should be under the user's project folder:
// - app_location
// - api_location

Просмотреть файл

@ -9,7 +9,7 @@ import open from "open";
import { DEFAULT_CONFIG } from "../config";
import { address, hostnameToIpAdress, isHttpUrl, logger, logRequest, registerProcessExit, validateDevServerConfig } from "../core";
import { HAS_API, IS_API_DEV_SERVER, IS_APP_DEV_SERVER, SWA_CLI_API_URI, SWA_CLI_APP_PROTOCOL } from "../core/constants";
import { swaCLIEnv } from "../core/env";
import { getSwaEnvList, swaCLIEnv } from "../core/env";
import { validateFunctionTriggers } from "./handlers/function.handler";
import { handleUserConfig, onConnectionLost, requestMiddleware } from "./middlewares/request.middleware";
@ -125,6 +125,8 @@ function onServerStart(server: https.Server | http.Server, socketConnection: net
// start SWA proxy server
(async () => {
logger.silly(getSwaEnvList());
let socketConnection: net.Socket | undefined;
const localIpAdress = await internalIp.v4();