Angular 9 个通用部署问题
Angular 9 universal deployment woes
老实说,我觉得这很烦人,因为它一直在变化。
我在这里用 Angular 的更简单版本解决了这个问题:
但现在这已经过时了。
不再生成 server.js,而是您必须修改 web.config 以指向 main.js 这听起来像是一个改进。
我将 yaml 更新为:
pool:
name: Azure Pipelines
steps:
- task: gittools.gitversion.gitversion-task.GitVersion@5
displayName: GitVersion
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: 12.x
- task: Npm@1
displayName: 'npm install angular cli'
inputs:
command: custom
verbose: false
customCommand: 'install @angular/cli -g'
- task: Npm@1
displayName: 'npm install'
inputs:
verbose: false
- task: Npm@1
displayName: 'npm build'
inputs:
command: custom
verbose: false
customCommand: 'run build:ssr'
- task: CopyFiles@2
displayName: 'Copy dist files to staging'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/dist'
TargetFolder: '$(Build.ArtifactStagingDirectory)/app/dist'
- task: AzureRmWebAppDeployment@4
displayName: 'Azure App Service Deploy: app-name'
inputs:
azureSubscription: 'Pay-As-You-Go (f61dc7cf-0ca2-4982-bbe7-9b6527c2962b)'
WebAppName: r3plica
packageForLinux: '$(Build.ArtifactStagingDirectory)/app'
WebConfigParameters: '-Handler iisnode -NodeStartFile dist/app-name/server/main.js -appType node'
应该就是这样,但当然,事情并没有那么简单。
现在如果我 运行 node dist/app-name/server/main.js 我会得到一个错误。它返回这个:
ReferenceError: Blob is not defined at createBase64WorkerFactory (D:\home\site\wwwroot\dist\app-name\server\main.js:1:1418371)
所以我环顾四周,有人建议我安装 npm install --save-dev blob-polyfill
,然后编辑 server.ts 文件:
import { Blob } from 'blob-polyfill';
global['Blob'] = Blob;
但这似乎并没有起到任何作用。错误仍然存在。
有谁知道我必须做什么?
更新
我决定今天再试一次。
我 运行 npm build:ssr
并将 服务器和浏览器 文件夹复制到本地网络服务器和 运行 node server/main.js
并且它抱怨它不能'在 dist/my-project/browser/index.html 找不到 index.html 文件,这对我有帮助。
所以我将整个 dist 文件夹复制到 wwwroot 和 运行 node dist/my-project/server/main.js
并且它起作用了。
所以我更新了我的管道来做同样的事情。我确认它实际上复制了整个 dist 文件夹,然后我将 web.config 复制到根目录。
我的 web.config 文件如下所示:
<configuration>
<system.web>
<customErrors mode="Off" />
</system.web>
<system.webServer>
<staticContent>
<remove fileExtension=".woff2" />
<mimeMap fileExtension=".woff2" mimeType="font/woff2" />
</staticContent>
<handlers>
<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="dist/my-project/server/main.js" verb="*" modules="iisnode"/>
</handlers>
<httpErrors errorMode="Detailed"></httpErrors>
</system.webServer>
</configuration>
但是当我尝试加载我的网站时,它只是给我一个错误:
HTTP Error 403.14 - Forbidden
A default document is not configured for the requested URL, and directory browsing is not enabled on the server.
这真的很烦人。
我查看了 https://example.scm.azurewebsites.net 并转到调试控制台并输入 node dist/my-project/server/main.js
返回:
Node Express server listening on http://localhost:4000
据我所知,它应该工作正常。
有谁知道为什么不是吗?
对于面临这些问题的任何人,我刚刚解决了它,这是我们的解决方案,但事实很少:
- [Web.config] 节点上下文,我的意思是进程工作目录,在iisnode中工作不同,PWD是目标文件路径,这意味着如果你的main.js 在 dist/server/main.js 中,那么相对于浏览器的路径将不是 dist/browser/ 而是 ../browser/
考虑到在部署期间你将不得不根据这个新结构生成Web.config
-处理程序 iisnode -NodeStartFile dist/server/main.js -appType 节点
[server.ts] - 考虑到这一点,还要考虑根据您的运行时环境设置浏览器路径,这样如果您在生产应该是../browser
[server.ts] - server.ts 中的顺序很重要。 如果您遇到浏览器API问题这是因为"import { AppServerModule } from './main.server';"必须放在之后 多米诺骨牌声明。
这是一个关于 server.ts 的工作示例,它也根据带有区域设置字符串的 url 请求使用 i18n 重定向(既然我也解决了这个 i18n 问题,我可以告诉你值得阅读文档)。
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import { APP_BASE_HREF } from '@angular/common';
import '@angular/localize/init';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import 'zone.js/dist/zone-node';
import { environment } from './environments/environment';
// THIS FIX MOST OF THE COMMON ISSUES WITH SSR:
// FIRST SET THE BROWSER PATH ACCORDING TO RUNTIME ENVIRONMENT
let browserPath;
if (environment.production) {
browserPath = '../browser';
} else {
browserPath = 'dist/browser';
}
const enDistFolder = join(process.cwd(), browserPath + '/en');
// Emulate browser APIs
const domino = require('domino');
const fs = require('fs');
const templateA = fs.readFileSync(join(enDistFolder, 'index.html')).toString();
const win = domino.createWindow(templateA);
console.log('win');
win.Object = Object;
console.log('Object');
win.Math = Math;
console.log('Math');
global['window'] = win;
global['document'] = win.document;
global['Event'] = win.Event;
console.log('declared Global Vars....');
/****************************************************/
/** NOTE THIS: I need to avoid sorting this line */
// USE CTRL+P -> SAVE WITHOUT FORMATTING
import { AppServerModule } from './main.server';
/****************************************************/
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const indexHtml = existsSync(join(browserPath, 'index.original.html')) ? 'index.original.html' : 'index.html';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', browserPath);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(browserPath, {
maxAge: '1y'
}));
server.use('/robots.txt', express.static('/en/robots.txt'));
server.use('/ads.txt', express.static('/en/ads.txt'));
// THE ORIGINAL Universal Requests handler
// // // All regular routes use the Universal engine
// // server.get('*', (req, res) => {
// // res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
// // });
// OUR i18n REQUESTS HANDLER
// All regular routes use the Universal engine
server.get('*', (req, res) => {
// this is for i18n
const supportedLocales = ['en', 'es'];
const defaultLocale = 'es';
const matches = req.url.match(/^\/([a-z]{2}(?:-[A-Z]{2})?)\//);
// check if the requested url has a correct format '/locale' and matches any of the supportedLocales
const locale = (matches && supportedLocales.indexOf(matches[1]) !== -1) ? matches[1] : defaultLocale;
res.render(`${locale}/index.html`, { req });
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './main.server';
我仍然需要在这段代码和我们的应用程序中做一些工作(SSR 和 oauth 问题,另一个有趣的话题)但我想分享它,因为我们花了将近 20 次部署来解决这些问题。
最后的话:如果您在 angular 8 迁移后来到这里,我将很乐意为您提供帮助并给您很好的提示,但老实说,请遵循指南并仔细阅读文档。此外,如果您使用的是 Azure DevOps 管道,则应考虑使用 npm 缓存。我们的 as 很大,我们现在在每个构建过程中节省了超过 12 分钟(这是大量时间,不是吗?)请随时与我联系。
胡安
所以,我又一次设法解决了这个问题。我在里面投入了几个小时,它开始让我发疯。我决定建立一个本地网络服务器 (iss) 并尽我所能。最后,正是这个救了我,因为 iisnode 正在记录错误,我可以看到哪里出了问题。
如果我在您执行 npm run build:ssr
时保持文件夹结构不变,并更新 webconfig 以指向 main.js,如 <add name="iisnode" path="dist/example-project/server/main.js" verb="*" modules="iisnode"/>
你会收到类似这样的错误:
Error: Failed to lookup view "index" in views directory "C:\inetpub\wwwroot\dist\example-project\server\dist\example-project\browser"
从错误中可以看出,它使用了 main.js 所在的相对路径。
从这里您可能可以看到我是如何解决我的问题的。
我更新了我的任务以包含一个新副本,我将 main.js 复制到根目录并更新了我的 web.config 至此:
<add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
.
为了完整起见,这是我的完整 web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^main.js\/debug[\/]?" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="main.js"/>
</rule>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
这是我的 xaml 天蓝色:
pool:
name: Azure Pipelines
steps:
- task: gittools.gitversion.gitversion-task.GitVersion@5
displayName: GitVersion
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: 12.x
checkLatest: true
- task: Npm@1
displayName: 'npm install angular cli'
inputs:
command: custom
verbose: false
customCommand: 'install @angular/cli -g'
- task: Npm@1
displayName: 'npm install'
inputs:
verbose: false
- task: Npm@1
displayName: 'npm build'
inputs:
command: custom
verbose: false
customCommand: 'run build:ssr'
- task: CopyFiles@2
displayName: 'Copy dist files to staging'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/dist'
TargetFolder: '$(Build.ArtifactStagingDirectory)/dist'
- task: CopyFiles@2
displayName: 'Copy web.config'
inputs:
SourceFolder: '$(Build.ArtifactStagingDirectory)/dist/example-project/browser'
Contents: web.config
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: CopyFiles@2
displayName: 'Copy main.js'
inputs:
SourceFolder: '$(Build.ArtifactStagingDirectory)/dist/example-project/server'
Contents: main.js
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: AzureRmWebAppDeployment@4
displayName: 'Azure App Service Deploy: example-project'
inputs:
azureSubscription: 'Your Subscription'
WebAppName: 'example-project'
packageForLinux: '$(Build.ArtifactStagingDirectory)'
enableCustomDeployment: true
RemoveAdditionalFilesFlag: true
老实说,我觉得这很烦人,因为它一直在变化。 我在这里用 Angular 的更简单版本解决了这个问题:
但现在这已经过时了。 不再生成 server.js,而是您必须修改 web.config 以指向 main.js 这听起来像是一个改进。 我将 yaml 更新为:
pool:
name: Azure Pipelines
steps:
- task: gittools.gitversion.gitversion-task.GitVersion@5
displayName: GitVersion
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: 12.x
- task: Npm@1
displayName: 'npm install angular cli'
inputs:
command: custom
verbose: false
customCommand: 'install @angular/cli -g'
- task: Npm@1
displayName: 'npm install'
inputs:
verbose: false
- task: Npm@1
displayName: 'npm build'
inputs:
command: custom
verbose: false
customCommand: 'run build:ssr'
- task: CopyFiles@2
displayName: 'Copy dist files to staging'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/dist'
TargetFolder: '$(Build.ArtifactStagingDirectory)/app/dist'
- task: AzureRmWebAppDeployment@4
displayName: 'Azure App Service Deploy: app-name'
inputs:
azureSubscription: 'Pay-As-You-Go (f61dc7cf-0ca2-4982-bbe7-9b6527c2962b)'
WebAppName: r3plica
packageForLinux: '$(Build.ArtifactStagingDirectory)/app'
WebConfigParameters: '-Handler iisnode -NodeStartFile dist/app-name/server/main.js -appType node'
应该就是这样,但当然,事情并没有那么简单。 现在如果我 运行 node dist/app-name/server/main.js 我会得到一个错误。它返回这个:
ReferenceError: Blob is not defined at createBase64WorkerFactory (D:\home\site\wwwroot\dist\app-name\server\main.js:1:1418371)
所以我环顾四周,有人建议我安装 npm install --save-dev blob-polyfill
,然后编辑 server.ts 文件:
import { Blob } from 'blob-polyfill';
global['Blob'] = Blob;
但这似乎并没有起到任何作用。错误仍然存在。 有谁知道我必须做什么?
更新
我决定今天再试一次。
我 运行 npm build:ssr
并将 服务器和浏览器 文件夹复制到本地网络服务器和 运行 node server/main.js
并且它抱怨它不能'在 dist/my-project/browser/index.html 找不到 index.html 文件,这对我有帮助。
所以我将整个 dist 文件夹复制到 wwwroot 和 运行 node dist/my-project/server/main.js
并且它起作用了。
所以我更新了我的管道来做同样的事情。我确认它实际上复制了整个 dist 文件夹,然后我将 web.config 复制到根目录。 我的 web.config 文件如下所示:
<configuration>
<system.web>
<customErrors mode="Off" />
</system.web>
<system.webServer>
<staticContent>
<remove fileExtension=".woff2" />
<mimeMap fileExtension=".woff2" mimeType="font/woff2" />
</staticContent>
<handlers>
<!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
<add name="iisnode" path="dist/my-project/server/main.js" verb="*" modules="iisnode"/>
</handlers>
<httpErrors errorMode="Detailed"></httpErrors>
</system.webServer>
</configuration>
但是当我尝试加载我的网站时,它只是给我一个错误:
HTTP Error 403.14 - Forbidden
A default document is not configured for the requested URL, and directory browsing is not enabled on the server.
这真的很烦人。
我查看了 https://example.scm.azurewebsites.net 并转到调试控制台并输入 node dist/my-project/server/main.js
返回:
Node Express server listening on http://localhost:4000
据我所知,它应该工作正常。 有谁知道为什么不是吗?
对于面临这些问题的任何人,我刚刚解决了它,这是我们的解决方案,但事实很少:
- [Web.config] 节点上下文,我的意思是进程工作目录,在iisnode中工作不同,PWD是目标文件路径,这意味着如果你的main.js 在 dist/server/main.js 中,那么相对于浏览器的路径将不是 dist/browser/ 而是 ../browser/
考虑到在部署期间你将不得不根据这个新结构生成Web.config
-处理程序 iisnode -NodeStartFile dist/server/main.js -appType 节点
[server.ts] - 考虑到这一点,还要考虑根据您的运行时环境设置浏览器路径,这样如果您在生产应该是../browser
[server.ts] - server.ts 中的顺序很重要。 如果您遇到浏览器API问题这是因为"import { AppServerModule } from './main.server';"必须放在之后 多米诺骨牌声明。
这是一个关于 server.ts 的工作示例,它也根据带有区域设置字符串的 url 请求使用 i18n 重定向(既然我也解决了这个 i18n 问题,我可以告诉你值得阅读文档)。
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import { APP_BASE_HREF } from '@angular/common';
import '@angular/localize/init';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import 'zone.js/dist/zone-node';
import { environment } from './environments/environment';
// THIS FIX MOST OF THE COMMON ISSUES WITH SSR:
// FIRST SET THE BROWSER PATH ACCORDING TO RUNTIME ENVIRONMENT
let browserPath;
if (environment.production) {
browserPath = '../browser';
} else {
browserPath = 'dist/browser';
}
const enDistFolder = join(process.cwd(), browserPath + '/en');
// Emulate browser APIs
const domino = require('domino');
const fs = require('fs');
const templateA = fs.readFileSync(join(enDistFolder, 'index.html')).toString();
const win = domino.createWindow(templateA);
console.log('win');
win.Object = Object;
console.log('Object');
win.Math = Math;
console.log('Math');
global['window'] = win;
global['document'] = win.document;
global['Event'] = win.Event;
console.log('declared Global Vars....');
/****************************************************/
/** NOTE THIS: I need to avoid sorting this line */
// USE CTRL+P -> SAVE WITHOUT FORMATTING
import { AppServerModule } from './main.server';
/****************************************************/
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const indexHtml = existsSync(join(browserPath, 'index.original.html')) ? 'index.original.html' : 'index.html';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', browserPath);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(browserPath, {
maxAge: '1y'
}));
server.use('/robots.txt', express.static('/en/robots.txt'));
server.use('/ads.txt', express.static('/en/ads.txt'));
// THE ORIGINAL Universal Requests handler
// // // All regular routes use the Universal engine
// // server.get('*', (req, res) => {
// // res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
// // });
// OUR i18n REQUESTS HANDLER
// All regular routes use the Universal engine
server.get('*', (req, res) => {
// this is for i18n
const supportedLocales = ['en', 'es'];
const defaultLocale = 'es';
const matches = req.url.match(/^\/([a-z]{2}(?:-[A-Z]{2})?)\//);
// check if the requested url has a correct format '/locale' and matches any of the supportedLocales
const locale = (matches && supportedLocales.indexOf(matches[1]) !== -1) ? matches[1] : defaultLocale;
res.render(`${locale}/index.html`, { req });
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './main.server';
我仍然需要在这段代码和我们的应用程序中做一些工作(SSR 和 oauth 问题,另一个有趣的话题)但我想分享它,因为我们花了将近 20 次部署来解决这些问题。
最后的话:如果您在 angular 8 迁移后来到这里,我将很乐意为您提供帮助并给您很好的提示,但老实说,请遵循指南并仔细阅读文档。此外,如果您使用的是 Azure DevOps 管道,则应考虑使用 npm 缓存。我们的 as 很大,我们现在在每个构建过程中节省了超过 12 分钟(这是大量时间,不是吗?)请随时与我联系。
胡安
所以,我又一次设法解决了这个问题。我在里面投入了几个小时,它开始让我发疯。我决定建立一个本地网络服务器 (iss) 并尽我所能。最后,正是这个救了我,因为 iisnode 正在记录错误,我可以看到哪里出了问题。
如果我在您执行 npm run build:ssr
时保持文件夹结构不变,并更新 webconfig 以指向 main.js,如 <add name="iisnode" path="dist/example-project/server/main.js" verb="*" modules="iisnode"/>
你会收到类似这样的错误:
Error: Failed to lookup view "index" in views directory "C:\inetpub\wwwroot\dist\example-project\server\dist\example-project\browser"
从错误中可以看出,它使用了 main.js 所在的相对路径。 从这里您可能可以看到我是如何解决我的问题的。
我更新了我的任务以包含一个新副本,我将 main.js 复制到根目录并更新了我的 web.config 至此:
<add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
.
为了完整起见,这是我的完整 web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="false" />
<handlers>
<add name="iisnode" path="main.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^main.js\/debug[\/]?" />
</rule>
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}"/>
</rule>
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="main.js"/>
</rule>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/index.html" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>
这是我的 xaml 天蓝色:
pool:
name: Azure Pipelines
steps:
- task: gittools.gitversion.gitversion-task.GitVersion@5
displayName: GitVersion
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: 12.x
checkLatest: true
- task: Npm@1
displayName: 'npm install angular cli'
inputs:
command: custom
verbose: false
customCommand: 'install @angular/cli -g'
- task: Npm@1
displayName: 'npm install'
inputs:
verbose: false
- task: Npm@1
displayName: 'npm build'
inputs:
command: custom
verbose: false
customCommand: 'run build:ssr'
- task: CopyFiles@2
displayName: 'Copy dist files to staging'
inputs:
SourceFolder: '$(Build.SourcesDirectory)/dist'
TargetFolder: '$(Build.ArtifactStagingDirectory)/dist'
- task: CopyFiles@2
displayName: 'Copy web.config'
inputs:
SourceFolder: '$(Build.ArtifactStagingDirectory)/dist/example-project/browser'
Contents: web.config
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: CopyFiles@2
displayName: 'Copy main.js'
inputs:
SourceFolder: '$(Build.ArtifactStagingDirectory)/dist/example-project/server'
Contents: main.js
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: AzureRmWebAppDeployment@4
displayName: 'Azure App Service Deploy: example-project'
inputs:
azureSubscription: 'Your Subscription'
WebAppName: 'example-project'
packageForLinux: '$(Build.ArtifactStagingDirectory)'
enableCustomDeployment: true
RemoveAdditionalFilesFlag: true