refactor: clean up cli context
This commit is contained in:
Родитель
a509c897b1
Коммит
53381e4d46
81
readme.md
81
readme.md
|
@ -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();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче