问题
I've got a custom batch deployment script for an Azure Function which is ending prematurely without erroring out. I'm hoping someone can help me understand what is going on.
The problematic portion appears to be in the "Pre-deployment" section of the script:
for /F %%f in ('git.exe diff --name-only %PREVIOUS_SCM_COMMIT_ID% %SCM_COMMIT_ID% ^| grep package.json') do (
SET PACKAGEJSON=%%~f
SET PKGFOLDER=!DEPLOYMENT_SOURCE!\!PACKAGEJSON:package.json=!
echo "NPM Install: !PKGFOLDER!package.json"
pushd "!PKGFOLDER!"
npm install --production --progress=false --cache-min=432000
npm install --save json-loader --progress=false --cache-min=432000
IF !ERRORLEVEL! NEQ 0 goto error
popd
)
If a package.json has not been modified between the last two commits, the script skips this section as expected and continues to executing the "Deployment" section.
If a package.json HAS been modified, the code works as expected at first and it runs the npm installs. Afterwards, however, it ends without an error and does not continue on to the deployment section. There is no additional output from the script after the last npm install runs.
Can anyone help me understand what is wrong? This looks correct to me.
The full script is below, and it is a portion of the following project: https://github.com/securityvoid/.deploy
@if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off
@echo Started: %date% %time%
:: ----------------------
:: KUDU Deployment Script
:: Version: 1.0.12
:: ----------------------
:: Prerequisites
:: -------------
:: Verify node.js installed
where node 2>nul >nul
IF %ERRORLEVEL% NEQ 0 (
echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment.
goto error
)
:: Setup
:: -----
setlocal enabledelayedexpansion
SET ARTIFACTS=%~dp0%..\artifacts
IF NOT DEFINED DEPLOYMENT_SOURCE (
SET DEPLOYMENT_SOURCE=%~dp0%.
)
echo "Deployment Source: %DEPLOYMENT_SOURCE%"
IF NOT DEFINED DEPLOYMENT_DIST (
SET DEPLOYMENT_DIST=%DEPLOYMENT_SOURCE%\dist
) ELSE (
ECHO "Deployement Dist already set"
)
echo "Deployment Dist: %DEPLOYMENT_DIST%"
IF NOT DEFINED DEPLOYMENT_TARGET (
SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot
)
echo "Deployment Target: %DEPLOYMENT_TARGET%"
IF NOT DEFINED NEXT_MANIFEST_PATH (
SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest
IF NOT DEFINED PREVIOUS_MANIFEST_PATH (
SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest
)
)
IF NOT DEFINED KUDU_SYNC_CMD (
:: Install kudu sync
echo Installing Kudu Sync
call npm install kudusync -g --silent
IF !ERRORLEVEL! NEQ 0 goto error
:: Locally just running "kuduSync" would also work
SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd
)
for /F "tokens=5 delims=.\" %%a in ("%PREVIOUS_MANIFEST_PATH%") do SET PREVIOUS_SCM_COMMIT_ID=%%a
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Pre-Deployment
:: ----------
@echo "Initiating Pre-Deployment: %date% %time%"
@echo "Previous Commit: %PREVIOUS_SCM_COMMIT_ID% Current Commit: %SCM_COMMIT_ID%"
for /F %%f in ('git.exe diff --name-only %PREVIOUS_SCM_COMMIT_ID% %SCM_COMMIT_ID% ^| grep package.json') do (
SET PACKAGEJSON=%%~f
SET PKGFOLDER=!DEPLOYMENT_SOURCE!\!PACKAGEJSON:package.json=!
echo "NPM Install: !PKGFOLDER!package.json"
pushd "!PKGFOLDER!"
npm install --production --progress=false --cache-min=432000
npm install --save json-loader --progress=false --cache-min=432000
IF !ERRORLEVEL! NEQ 0 goto error
popd
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Deployment
:: ----------
@echo "Initiating Deployment: %date% %time%"
:: 1. Build Script
node %DEPLOYMENT_SOURCE%\.deploy\deploy.js
:: 2. KuduSync
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_DIST%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd"
IF !ERRORLEVEL! NEQ 0 goto error
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
goto end
:: Execute command routine that will echo out when error
:ExecuteCmd
setlocal
set _CMD_=%*
call %_CMD_%
if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_%
exit /b %ERRORLEVEL%
:error
endlocal
echo An error has occurred during web site deployment.
call :exitSetErrorLevel
call :exitFromFunction 2>nul
:exitSetErrorLevel
exit /b 1
:exitFromFunction
()
:end
endlocal
echo Finished successfully.
回答1:
The source of the problem is that npm
is a batch file. When a batch file (your batch file) invokes another batch file, the execution flow is transferred to the called one, and once it ends its work, the execution flow does not return to the caller.
This behaviour changes if the invocation is done using the call
command.
call npm ....
The execution control is transfered to the called batch file and on end the execution flow returns to the caller.
note: The exposed behaviour is a simplification of the undelaying process. Batch files are executed inside a memory "context". Without the call
command, the called batch file replaces the caller "context", with the call
command, a new "context" is created.
This leaves another question: if the invocation of the batch npm
batch file transfer execution flow and it does not return, why the second npm
command is executed?
While executing a batch file (or command lines), blocks of code (code enclosed in parenthesis) are loaded into memory and parsed as a whole. Your for
command is placed in memory, and all the commands included in its do
clause will continue running until the loop ends.
note: Not relevant in in this case, but once the first npm
has been invoked (without call
) and the caller batch "context" has been discarded (replaced by the called context), the rest of the commands in the for
loop are executed (they are still in memory) in command line context and not in batch context, and as the "context" was discarded, the setlocal
has been reverted and variable changes, directoy changes and delayed expansion are discarded.
来源:https://stackoverflow.com/questions/42538237/azure-function-azure-website-custom-deploy-script-ending-prematurely