1- const { args } = Deno ;
21import {
32 parse ,
43 readZip ,
54 ensureDir ,
65 move ,
76 walk ,
7+ semver ,
88} from "./deps.ts" ;
99
10- const shouldBundle = false ;
10+ const baseExecutableFileName = "worker" ;
11+ const bundleFileName = "worker.bundle.js" ;
12+ const commonDenoOptions = [ "--allow-env" , "--allow-net" , "--allow-read" ] ;
1113const parsedArgs = parse ( Deno . args ) ;
1214
13- if ( parsedArgs [ "help" ] ) {
15+ const bundleStyles = [ "executable" , "jsbundle" , "none" ] ;
16+ const STYLE_EXECUTABLE = 0 ;
17+ const STYLE_JSBUNDLE = 1 ;
18+ const STYLE_NONE = 2 ;
19+
20+ if ( parsedArgs . _ [ 0 ] === "help" ) {
1421 printHelp ( ) ;
1522 Deno . exit ( ) ;
1623}
1724
18- if ( args . length >= 1 && args [ 0 ] === "init" ) {
19- const templateDownloadBranch : string | undefined = args [ 1 ] ;
25+ if ( parsedArgs . _ . length >= 1 && parsedArgs . _ [ 0 ] === "init" ) {
26+ const templateDownloadBranch : string | undefined = parsedArgs ?. _ [ 1 ] ?. toString ( ) ;
2027 await initializeFromTemplate ( templateDownloadBranch ) ;
2128} else if (
22- args . length === 1 && args [ 0 ] === "start" ||
23- args . length === 2 && ` ${ args [ 0 ] } ${ args [ 1 ] } ` === "host start"
29+ parsedArgs . _ . length === 1 && parsedArgs . _ [ 0 ] === "start" ||
30+ parsedArgs . _ . length === 2 && parsedArgs . _ . join ( ' ' ) === "host start"
2431) {
2532 await generateFunctions ( ) ;
2633 await createJSBundle ( ) ;
2734 await runFunc ( "start" ) ;
28- } else if ( args . length === 2 && args [ 0 ] === "publish" ) {
29- const platform = await getAppPlatform ( args [ 1 ] ) ;
30- await updateHostJson ( platform ) ;
31- await downloadBinary ( platform ) ;
35+ } else if ( parsedArgs . _ [ 0 ] === "publish" && parsedArgs . _ . length === 2 ) {
36+ const bundleStyle = parsedArgs [ "bundle-style" ] // use specified bundle style
37+ || ( semver . satisfies ( Deno . version . deno , ">=1.6.0" ) // default style depends on Deno runtime version
38+ ? bundleStyles [ STYLE_EXECUTABLE ] // for v1.6.0 or later
39+ : bundleStyles [ STYLE_JSBUNDLE ] // for others
40+ ) ;
41+ if ( ! bundleStyles . includes ( bundleStyle ) ) {
42+ console . error ( `The value \`${ parsedArgs [ "bundle-style" ] } \` of \`--bundle-style\` option is not acceptable.` )
43+ Deno . exit ( 1 ) ;
44+ } else if ( semver . satisfies ( Deno . version . deno , "<1.6.0" ) && bundleStyle === bundleStyles [ STYLE_EXECUTABLE ] ) {
45+ console . error ( `Deno version v${ Deno . version . deno } doesn't support \`${ bundleStyles [ STYLE_EXECUTABLE ] } \` for bundle style.` ) ;
46+ Deno . exit ( 1 ) ;
47+ }
48+ const appName = parsedArgs . _ [ 1 ] . toString ( ) ;
49+ const slotName = parsedArgs [ "slot" ] ?. toString ( ) ;
50+ const platform = await getAppPlatform ( appName , slotName ) ;
51+ if ( ! [ "windows" , "linux" ] . includes ( platform ) ) {
52+ console . error ( `The value \`${ platform } \` for the function app \`${ appName + ( slotName ? `/${ slotName } ` : "" ) } \` is not valid.` ) ;
53+ Deno . exit ( 1 ) ;
54+ }
55+ await updateHostJson ( platform , bundleStyle ) ;
3256 await generateFunctions ( ) ;
33- await createJSBundle ( ) ;
34- await publishApp ( args [ 1 ] ) ;
57+
58+ if ( bundleStyle === bundleStyles [ STYLE_EXECUTABLE ] ) {
59+ await generateExecutable ( platform ) ;
60+ } else {
61+ await downloadBinary ( platform ) ;
62+ if ( bundleStyle === bundleStyles [ STYLE_JSBUNDLE ] ) await createJSBundle ( ) ;
63+ }
64+ await publishApp ( appName , slotName ) ;
3565} else {
3666 printHelp ( ) ;
3767}
@@ -67,24 +97,49 @@ async function listFiles(dir: string) {
6797 return files ;
6898}
6999
100+ async function generateExecutable ( platformArg ?: string ) {
101+ try {
102+ await Deno . remove ( './bin' , { recursive : true } ) ;
103+ await Deno . remove ( `./${ bundleFileName } ` ) ;
104+ } catch { }
105+
106+ const platform = platformArg || Deno . build . os ;
107+ await Deno . mkdir ( `./bin/${ platform } ` , { recursive : true } ) ;
108+
109+ const cmd = [
110+ "deno" ,
111+ "compile" ,
112+ "--unstable" ,
113+ ...( semver . satisfies ( Deno . version . deno , ">=1.7.1 <1.10.0" ) ? [ "--lite" ] : [ ] ) , // `--lite` option is implemented only between v1.7.1 and v1.9.x
114+ ...commonDenoOptions ,
115+ "--output" ,
116+ `./bin/${ platform } /${ baseExecutableFileName } ` ,
117+ ...( [ 'windows' , 'linux' ] . includes ( platform )
118+ ? [ '--target' , platform === 'windows' ? 'x86_64-pc-windows-msvc' : 'x86_64-unknown-linux-gnu' ]
119+ : [ ]
120+ ) ,
121+ "worker.ts"
122+ ] ;
123+ console . info ( `Running command: ${ cmd . join ( " " ) } ` ) ;
124+ const generateProcess = Deno . run ( { cmd } ) ;
125+ await generateProcess . status ( ) ;
126+ }
127+
70128async function createJSBundle ( ) {
71- if ( shouldBundle ) {
72- const bundleFileName = "worker.bundle.js" ;
73- const cmd = [ "deno" , "bundle" , "--unstable" , "worker.ts" , bundleFileName ] ;
74- console . info ( `Running command: ${ cmd . join ( " " ) } ` ) ;
75- const generateProcess = Deno . run ( { cmd } ) ;
76- await generateProcess . status ( ) ;
77- }
129+ const cmd = [ "deno" , "bundle" , "--unstable" , "worker.ts" , bundleFileName ] ;
130+ console . info ( `Running command: ${ cmd . join ( " " ) } ` ) ;
131+ const generateProcess = Deno . run ( { cmd } ) ;
132+ await generateProcess . status ( ) ;
78133}
79134
80- async function getAppPlatform ( appName : string ) : Promise < string > {
81- console . info ( `Checking platform type of : ${ appName } ...` ) ;
135+ async function getAppPlatform ( appName : string , slotName ?: string ) : Promise < string > {
136+ console . info ( `Checking platform type of : ${ appName + ( slotName ? `/ ${ slotName } ` : "" ) } ...` ) ;
82137 const azResourceCmd = [
83138 "az" ,
84139 "resource" ,
85140 "list" ,
86141 "--resource-type" ,
87- " Microsoft.web/sites" ,
142+ ` Microsoft.web/sites${ slotName ? "/slots" : "" } ` ,
88143 "-o" ,
89144 "json" ,
90145 ] ;
@@ -100,32 +155,8 @@ async function getAppPlatform(appName: string): Promise<string> {
100155
101156 try {
102157 const resource = resources . find ( ( resource : any ) =>
103- resource . name === appName
104- ) ;
105-
106- if ( ( resource . kind as string ) . includes ( "linux" ) ) {
107- return "linux" ;
108- }
109-
110- const azFunctionCmd = [
111- "az" ,
112- "functionapp" ,
113- "config" ,
114- "show" ,
115- "--ids" ,
116- resource . id ,
117- "-o" ,
118- "json" ,
119- ] ;
120- const azFunctionProcess = await runWithRetry (
121- { cmd : azFunctionCmd , stdout : "piped" } ,
122- "az.cmd" ,
123- ) ;
124- const azFunctionOutput = await azFunctionProcess . output ( ) ;
125- const config = JSON . parse (
126- new TextDecoder ( ) . decode ( azFunctionOutput ) ,
158+ resource . name === ( appName + ( slotName ? `/${ slotName } ` : "" ) )
127159 ) ;
128- azFunctionProcess . close ( ) ;
129160
130161 const azFunctionAppSettingsCmd = [
131162 "az" ,
@@ -135,8 +166,12 @@ async function getAppPlatform(appName: string): Promise<string> {
135166 "set" ,
136167 "--ids" ,
137168 resource . id ,
169+ ...( slotName
170+ ? [ "--slot" , slotName ]
171+ : [ ]
172+ ) ,
138173 "--settings" ,
139- "WEBSITE_LOAD_USER_PROFILE=1 " ,
174+ "FUNCTIONS_WORKER_RUNTIME=custom " ,
140175 "-o" ,
141176 "json" ,
142177 ] ;
@@ -147,27 +182,36 @@ async function getAppPlatform(appName: string): Promise<string> {
147182 await azFunctionAppSettingsProcess . status ( ) ;
148183 azFunctionAppSettingsProcess . close ( ) ;
149184
150- return "windows" ;
185+ return ( resource . kind as string ) . includes ( "linux" ) ? "linux" : "windows" ;
151186 } catch {
152- throw new Error ( `Not found: ${ appName } ` ) ;
187+ throw new Error ( `Not found: ${ appName + ( slotName ? `/ ${ slotName } ` : "" ) } ` ) ;
153188 }
154189}
155190
156- async function updateHostJson ( platform : string ) {
157- // update `defaultExecutablePath` in host.json
191+ async function updateHostJson ( platform : string , bundleStyle : string ) {
192+ // update `defaultExecutablePath` and `arguments` in host.json
158193 const hostJsonPath = "./host.json" ;
159194 if ( ! ( await fileExists ( hostJsonPath ) ) ) {
160195 throw new Error ( `\`${ hostJsonPath } \` not found` ) ;
161196 }
162197
163198 const hostJSON : any = await readJson ( hostJsonPath ) ;
164- hostJSON . customHandler . description . defaultExecutablePath = platform === "windows"
165- ? "D:\\home\\site\\wwwroot\\bin\\windows\\deno.exe"
166- : "/home/site/wwwroot/bin/linux/deno" ,
167- await writeJson ( hostJsonPath , hostJSON ) ; // returns a promise
199+ if ( ! hostJSON . customHandler ) hostJSON . customHandler = { } ;
200+ hostJSON . customHandler . description = {
201+ defaultExecutablePath : `bin/${ platform } /${ bundleStyle === bundleStyles [ STYLE_EXECUTABLE ] ? baseExecutableFileName : "deno" } ${ platform === "windows" ? ".exe" : "" } ` ,
202+ arguments : bundleStyle === bundleStyles [ STYLE_EXECUTABLE ]
203+ ? [ ]
204+ : [
205+ "run" ,
206+ ...commonDenoOptions ,
207+ bundleStyle === bundleStyles [ STYLE_JSBUNDLE ] ? bundleFileName : "worker.ts"
208+ ]
209+ } ;
210+
211+ await writeJson ( hostJsonPath , hostJSON ) ; // returns a promise
168212}
169213
170- function writeJson ( path : string , data : object ) : void {
214+ function writeJson ( path : string , data : object ) : void {
171215 Deno . writeTextFileSync ( path , JSON . stringify ( data , null , 2 ) ) ;
172216}
173217
@@ -193,12 +237,14 @@ async function downloadBinary(platform: string) {
193237 await Deno . remove ( entry ) ;
194238 }
195239 }
240+ try {
241+ await Deno . remove ( `./${ bundleFileName } ` ) ;
242+ } catch { }
196243
197244 const binZipPath = `${ binDir } /deno.zip` ;
198245 if ( ! ( await fileExists ( binPath ) ) ) {
199246 const downloadUrl =
200- `https://github.com/denoland/deno/releases/download/v${ Deno . version . deno } /deno-x86_64-${
201- archive [ platform ]
247+ `https://github.com/denoland/deno/releases/download/v${ Deno . version . deno } /deno-x86_64-${ archive [ platform ]
202248 } .zip`;
203249 console . info ( `Downloading deno binary from: ${ downloadUrl } ...` ) ;
204250 // download deno binary (that gets deployed to Azure)
@@ -270,9 +316,7 @@ async function generateFunctions() {
270316 cmd : [
271317 "deno" ,
272318 "run" ,
273- "--allow-net" ,
274- "--allow-env" ,
275- "--allow-read" ,
319+ ...commonDenoOptions ,
276320 "--allow-write" ,
277321 "--unstable" ,
278322 "--no-check" ,
@@ -305,8 +349,7 @@ async function runWithRetry(
305349 } catch ( ex ) {
306350 if ( Deno . build . os === "windows" ) {
307351 console . info (
308- `Could not start ${
309- runOptions . cmd [ 0 ]
352+ `Could not start ${ runOptions . cmd [ 0 ]
310353 } from path, searching for executable...`,
311354 ) ;
312355 const whereCmd = [ "where.exe" , backupCommand ] ;
@@ -335,30 +378,30 @@ async function runWithRetry(
335378 }
336379}
337380
338- async function publishApp ( appName : string ) {
339- await runFunc (
381+ async function publishApp ( appName : string , slotName ?: string ) {
382+ const runFuncArgs = [
340383 "azure" ,
341384 "functionapp" ,
342385 "publish" ,
343- appName ,
344- "--force"
345- ) ;
386+ appName
387+ ] ;
388+ await runFunc ( ... ( slotName ? runFuncArgs . concat ( [ "--slot" , slotName ] ) : runFuncArgs ) ) ;
346389}
347390
348391function printLogo ( ) {
349392 const logo = `
350- @@@@@@@@@@@,
351- @@@@@@@@@@@@@@@@@@@ %%%%%%%%%%%%
352- @@@@@@ @@@@@@@@@@ %%%%%% %%%%%%
353- @@@@@ @ @ *@@@@@ @ %%%%%%%%%%%% @
354- @@@ @@@@@ @@ %%%%%%%%%%%% @@
355- @@@@@ @@@@@ @@@ %%%%%%%%%%%%%%%%%%%%%% @@@
356- @@@@@@@@@@@@@@@ @@@@ @@ %%%%%%%%%%%%%%%%%%%% @@
357- @@@@@@@@@@@@@@ @@@@ @@ %%%%%%%% @@
358- @@@@@@@@@@@@@@ @@@ @@ %%%%%% @@
359- @@@@@@@@@@@@@ @ @@ %%%% @@
360- @@@@@@@@@@@ %%%%
361- @@@@@@@ %%
393+ @@@@@@@@@@@,
394+ @@@@@@@@@@@@@@@@@@@ %%%%%%
395+ @@@@@@ @@@@@@@@@@ %%%%%%
396+ @@@@@ @ @ *@@@@@ @ %%%%%% @
397+ @@@ @@@@@ @@ %%%%%% @@
398+ @@@@@ @@@@@ @@@ %%%%%%%%%%% @@@
399+ @@@@@@@@@@@@@@@ @@@@ @@ %%%%%%%%%% @@
400+ @@@@@@@@@@@@@@ @@@@ @@ %%%% @@
401+ @@@@@@@@@@@@@@ @@@ @@ %%% @@
402+ @@@@@@@@@@@@@ @ @@ %% @@
403+ @@@@@@@@@@@ %%
404+ @@@@@@@ %
362405 ` ;
363406 console . info ( logo ) ;
364407}
@@ -378,7 +421,14 @@ denofunc init
378421denofunc start
379422 Generate functions artifacts and start Azure Functions Core Tools
380423
381- denofunc publish <function_app_name>
424+ denofunc publish <function_app_name> [options]
382425 Publish to Azure
426+ options:
427+ --slot <slot_name> Specify name of the deployment slot
428+ --bundle-style executable|jsbundle|none Select bundle style on deployment
429+
430+ executable: Bundle as one executable(default option for Deno v1.6.0 or later).
431+ jsbundle: Bundle as one javascript worker & Deno runtime
432+ none: No bundle
383433 ` ) ;
384434}
0 commit comments