mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-10-31 08:34:01 -05:00
606 lines
29 KiB
Batchfile
606 lines
29 KiB
Batchfile
@ECHO off
|
|
SETLOCAL
|
|
|
|
SET WD=%CD%
|
|
SET "BUILDROOT=%WD%\win-build"
|
|
SET "TMPDIR=%WD%\win-temp"
|
|
SET "DISTROOT=%WD%\dist"
|
|
|
|
SET CMDOPTIONS=""
|
|
IF "%1" == "clean" SET CMDOPTIONS="VALID"
|
|
IF "%1" == "" SET CMDOPTIONS="VALID"
|
|
|
|
IF NOT %CMDOPTIONS%=="VALID" (
|
|
GOTO USAGE
|
|
)
|
|
|
|
IF "%1" == "clean" (
|
|
CALL :CLEAN
|
|
EXIT /B %ERRORLEVEL%
|
|
)
|
|
|
|
REM Main build sequence
|
|
REM
|
|
REM The clean runs first because :FETCH_PYTHON unpacks into the temp directory
|
|
REM that it removes, and the fetch runs before :SET_ENVIRONMENT because the
|
|
REM latter has no interpreter of its own to ask about the build.
|
|
CALL :CLEAN || EXIT /B 1
|
|
CALL :FETCH_PYTHON || EXIT /B 1
|
|
CALL :SET_ENVIRONMENT
|
|
CALL :VALIDATE_ENVIRONMENT || EXIT /B 1
|
|
CALL :CREATE_VIRTUAL_ENV || EXIT /B 1
|
|
CALL :CREATE_PYTHON_ENV || EXIT /B 1
|
|
CALL :CREATE_RUNTIME_ENV || EXIT /B 1
|
|
CALL :SIGN_COMPONENTS || EXIT /B 1
|
|
CALL :GENERATE_SBOM || EXIT /B 1
|
|
CALL :CREATE_INSTALLER || EXIT /B 1
|
|
CALL :VERIFY_SIGNATURE || EXIT /B 1
|
|
|
|
EXIT /B %ERRORLEVEL%
|
|
REM Main build sequence Ends
|
|
|
|
|
|
:CLEAN
|
|
ECHO Removing build directory...
|
|
IF EXIST "%BUILDROOT%" RD "%BUILDROOT%" /S /Q > nul || EXIT /B 1
|
|
|
|
ECHO Removing tmp directory...
|
|
IF EXIST "%TMPDIR%" RD "%TMPDIR%" /S /Q > nul || EXIT /B 1
|
|
|
|
ECHO Removing installer build directory...
|
|
IF EXIST "%WD%\pkg\win32\Output" rd "%WD%\pkg\win32\Output" /S /Q > nul || EXIT /B 1
|
|
|
|
ECHO Removing installer configuration script...
|
|
IF EXIST DEL /q "%WD%\pkg\win32\installer.iss" > nul || EXIT /B 1
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
:FETCH_PYTHON
|
|
REM Fetch the Python this build uses, rather than using whatever happens to
|
|
REM be installed on the machine running it. Which interpreter pgAdmin ships
|
|
REM is a decision recorded in pkg\python-version.txt, not a property of the
|
|
REM build agent, and this is the only place on Windows that reads it.
|
|
REM
|
|
REM The distribution comes from astral-sh/python-build-standalone: it is
|
|
REM relocatable, published per exact CPython version, and is a plain tarball
|
|
REM rather than an installer, so unpacking it raises no UAC prompt. That
|
|
REM matters on the signing host, where the desktop session is logged in
|
|
REM automatically and an elevation dialog waits forever for somebody who is
|
|
REM not there.
|
|
REM
|
|
REM curl.exe and tar.exe have shipped in Windows since 1803, so this needs
|
|
REM nothing installed to bootstrap itself, not even a Python.
|
|
ECHO Reading the Python version...
|
|
SET "PYTHON_VERSION="
|
|
SET "PYTHON_BUILD_STANDALONE_TAG="
|
|
FOR /F "eol=# usebackq tokens=1,2 delims==" %%a IN ("%WD%\pkg\python-version.txt") DO (
|
|
IF "%%a" == "PYTHON_VERSION" SET "PYTHON_VERSION=%%b"
|
|
IF "%%a" == "PYTHON_BUILD_STANDALONE_TAG" SET "PYTHON_BUILD_STANDALONE_TAG=%%b"
|
|
)
|
|
|
|
IF "%PYTHON_VERSION%" == "" (
|
|
ECHO PYTHON_VERSION is not set in pkg\python-version.txt.
|
|
EXIT /B 1
|
|
)
|
|
IF "%PYTHON_BUILD_STANDALONE_TAG%" == "" (
|
|
ECHO PYTHON_BUILD_STANDALONE_TAG is not set in pkg\python-version.txt.
|
|
EXIT /B 1
|
|
)
|
|
|
|
REM Split for the runtime build, where e.g. 3.13.15 becomes 313
|
|
FOR /F "tokens=1,2,3 delims=." %%a IN ("%PYTHON_VERSION%") DO (
|
|
SET "PYTHON_MAJOR=%%a"
|
|
SET "PYTHON_MINOR=%%b"
|
|
SET "PYTHON_REVISION=%%c"
|
|
)
|
|
|
|
SET "PGADMIN_PYTHON_DIR=%TMPDIR%\python"
|
|
SET "PYTHON_ARCHIVE=cpython-%PYTHON_VERSION%+%PYTHON_BUILD_STANDALONE_TAG%-x86_64-pc-windows-msvc-install_only.tar.gz"
|
|
|
|
IF NOT EXIST "%TMPDIR%" MKDIR "%TMPDIR%"
|
|
|
|
ECHO Downloading Python %PYTHON_VERSION%...
|
|
curl.exe --fail --location --silent --show-error --output "%TMPDIR%\python.tar.gz" "https://github.com/astral-sh/python-build-standalone/releases/download/%PYTHON_BUILD_STANDALONE_TAG%/%PYTHON_ARCHIVE%" || EXIT /B 1
|
|
|
|
ECHO Unpacking Python...
|
|
REM The archive unpacks to a single python\ directory.
|
|
tar -x -f "%TMPDIR%\python.tar.gz" -C "%TMPDIR%" || EXIT /B 1
|
|
DEL /q "%TMPDIR%\python.tar.gz" > nul 2>&1
|
|
|
|
"%PGADMIN_PYTHON_DIR%\python.exe" --version || EXIT /B 1
|
|
|
|
REM :CREATE_VIRTUAL_ENV needs virtualenv rather than the venv module, as it
|
|
REM relocates the result. pip is bundled with the distribution, but ask
|
|
REM ensurepip for it anyway so this does not quietly depend on that.
|
|
ECHO Installing virtualenv...
|
|
"%PGADMIN_PYTHON_DIR%\python.exe" -m ensurepip --upgrade || EXIT /B 1
|
|
"%PGADMIN_PYTHON_DIR%\python.exe" -m pip install --upgrade pip virtualenv || EXIT /B 1
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
:SET_ENVIRONMENT
|
|
ECHO Configuring the environment...
|
|
IF "%PGADMIN_KRB5_DIR%" == "" SET "PGADMIN_KRB5_DIR=C:\build64\krb5"
|
|
IF "%PGADMIN_POSTGRES_DIR%" == "" SET "PGADMIN_POSTGRES_DIR=C:\build64\postgresql"
|
|
IF "%PGADMIN_INNOTOOL_DIR%" == "" SET "PGADMIN_INNOTOOL_DIR=C:\Program Files (x86)\Inno Setup 6"
|
|
IF "%PGADMIN_VCREDIST_DIR%" == "" SET "PGADMIN_VCREDIST_DIR=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Redist\MSVC\14.40.33807"
|
|
IF "%PGADMIN_VCREDIST_FILE%" == "" SET "PGADMIN_VCREDIST_FILE=vc_redist.x64.exe"
|
|
IF "%PGADMIN_SIGNTOOL_DIR%" == "" SET "PGADMIN_SIGNTOOL_DIR=C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64"
|
|
IF "%PGADMIN_WINDOWS_CSC%" == "" SET "PGADMIN_WINDOWS_CSC="
|
|
|
|
REM Set additional variables we need
|
|
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_RELEASE =" %WD%\web\version.py') DO SET APP_MAJOR=%%a
|
|
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_REVISION =" %WD%\web\version.py') DO SET APP_MINOR=%%a
|
|
FOR /F "tokens=3" %%a IN ('findstr /C:"APP_SUFFIX =" %WD%\web\version.py') DO SET APP_VERSION_SUFFIX=%%a
|
|
REM remove single quote from the string
|
|
SET APP_VERSION_SUFFIX=%APP_VERSION_SUFFIX:'=%
|
|
SET APP_NAME=""
|
|
FOR /F "tokens=2* DELims='" %%a IN ('findstr /C:"APP_NAME =" web\branding.py') DO SET APP_NAME=%%a
|
|
FOR /f "tokens=1 DELims=." %%G IN ('%PGADMIN_PYTHON_DIR%/python.exe -c "print('%APP_NAME%'.lower().replace(' ', ''))"') DO SET APP_SHORTNAME=%%G
|
|
SET APP_VERSION=%APP_MAJOR%.%APP_MINOR%
|
|
SET INSTALLERNAME=%APP_SHORTNAME%-%APP_MAJOR%.%APP_MINOR%-%APP_VERSION_SUFFIX%-x64.exe
|
|
IF "%APP_VERSION_SUFFIX%" == "" SET INSTALLERNAME=%APP_SHORTNAME%-%APP_MAJOR%.%APP_MINOR%-x64.exe
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
:VALIDATE_ENVIRONMENT
|
|
ECHO ****************************************************************
|
|
ECHO * Build summary
|
|
ECHO ****************************************************************
|
|
ECHO Build path: %BUILDROOT%
|
|
ECHO Output directory: %DISTROOT%
|
|
ECHO Installer name: %INSTALLERNAME%
|
|
ECHO.
|
|
ECHO Python version: %PYTHON_MAJOR%.%PYTHON_MINOR%.%PYTHON_REVISION%
|
|
ECHO Python directory: %PGADMIN_PYTHON_DIR%
|
|
ECHO.
|
|
ECHO KRB5 directory: %PGADMIN_KRB5_DIR%
|
|
ECHO PostgreSQL directory: %PGADMIN_POSTGRES_DIR%
|
|
ECHO.
|
|
ECHO VC++ redist directory: %PGADMIN_VCREDIST_DIR%
|
|
ECHO VC++ redist file: %PGADMIN_VCREDIST_FILE%
|
|
ECHO InnoTool directory: %PGADMIN_INNOTOOL_DIR%
|
|
ECHO signtool directory: %PGADMIN_SIGNTOOL_DIR%
|
|
IF "%PGADMIN_WINDOWS_CSC%" == "" (
|
|
ECHO Code signing certificate: [NONE - Signing disabled]
|
|
) ELSE (
|
|
ECHO Code signing certificate: %PGADMIN_WINDOWS_CSC%
|
|
)
|
|
ECHO.
|
|
ECHO App version: %APP_VERSION%
|
|
ECHO App version suffix: %APP_VERSION_SUFFIX%
|
|
ECHO App short name: %APP_SHORTNAME%
|
|
ECHO App name: %APP_NAME%
|
|
ECHO ****************************************************************
|
|
|
|
ECHO Checking the environment...
|
|
REM The checks are made by :REQUIRE_DIR rather than inline, because the
|
|
REM message names the directory that was looked for, and a default such as
|
|
REM "C:\Program Files (x86)\Inno Setup 6" carries a closing parenthesis.
|
|
REM Inside a parenthesised block that parenthesis ends the block, as cmd
|
|
REM expands the variable when it parses the block rather than when it runs it,
|
|
REM so the block failed to parse whether or not the directory was there.
|
|
CALL :REQUIRE_DIR "%PGADMIN_INNOTOOL_DIR%" "Please install InnoTool and set the PGADMIN_INNOTOOL_DIR environment variable." || EXIT /B 1
|
|
CALL :REQUIRE_DIR "%PGADMIN_VCREDIST_DIR%" "Please install Microsoft Visual Studio and set the PGADMIN_VCREDIST_DIR environment variable." || EXIT /B 1
|
|
CALL :REQUIRE_DIR "%PGADMIN_KRB5_DIR%" "Please install MIT Kerberos for Windows, from the winpgbuild project or elsewhere, and set the PGADMIN_KRB5_DIR environment variable." || EXIT /B 1
|
|
CALL :REQUIRE_DIR "%PGADMIN_POSTGRES_DIR%" "Please install PostgreSQL, from the winpgbuild project or elsewhere, and set the PGADMIN_POSTGRES_DIR environment variable." || EXIT /B 1
|
|
|
|
SET "PATH=%PGADMIN_POSTGRES_DIR%\bin;%PATH%"
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
:CREATE_VIRTUAL_ENV
|
|
ECHO Creating virtual environment...
|
|
IF NOT EXIST "%TMPDIR%" MKDIR "%TMPDIR%"
|
|
|
|
CD "%TMPDIR%"
|
|
|
|
REM Note that we must use virtualenv.exe here, as the venv module doesn't allow python.exe to relocate.
|
|
"%PGADMIN_PYTHON_DIR%\Scripts\virtualenv.exe" venv || EXIT /B 1
|
|
|
|
XCOPY /S /I /E /H /Y "%PGADMIN_PYTHON_DIR%\DLLs" "%TMPDIR%\venv\DLLs" > nul || EXIT /B 1
|
|
REM Copy the standard library, but NOT site-packages: the venv already has its
|
|
REM own seeded pip there, and overwriting only the files the system Python also
|
|
REM has leaves a mix of two pip versions behind.
|
|
ROBOCOPY /E /R:3 /W:5 /NFL /NDL /NP "%PGADMIN_PYTHON_DIR%\Lib" "%TMPDIR%\venv\Lib" /XD site-packages
|
|
CALL :CHECK_ROBOCOPY_ERROR || EXIT /B 1
|
|
|
|
ECHO Activating virtual environment - %TMPDIR%\venv...
|
|
CALL "%TMPDIR%\venv\Scripts\activate" || EXIT /B 1
|
|
|
|
ECHO Installing dependencies...
|
|
CALL python -m pip install --upgrade pip || EXIT /B 1
|
|
CALL pip install --only-binary=cryptography -r "%WD%\requirements.txt" || EXIT /B 1
|
|
|
|
CD %WD%
|
|
EXIT /B 0
|
|
|
|
|
|
:CREATE_PYTHON_ENV
|
|
ECHO Staging Python...
|
|
MKDIR "%BUILDROOT%\python\Lib" || EXIT /B 1
|
|
|
|
ECHO Downloading embedded Python %PYTHON_VERSION%...
|
|
REM What ships is the embeddable distribution from python.org, a different
|
|
REM build from the one :FETCH_PYTHON downloaded, and it has to be the same
|
|
REM CPython version: the extension modules in site-packages were compiled
|
|
REM against the build Python and are about to be run under this one. Both
|
|
REM therefore read pkg\python-version.txt, rather than either asking the
|
|
REM other what it is.
|
|
curl.exe --fail --location --silent --show-error --output "%TMPDIR%\python-embedded.zip" "https://www.python.org/ftp/python/%PYTHON_VERSION%/python-%PYTHON_VERSION%-embed-amd64.zip" || EXIT /B 1
|
|
tar -x -f "%TMPDIR%\python-embedded.zip" -C "%BUILDROOT%\python" || EXIT /B 1
|
|
|
|
ECHO Copying site-packages...
|
|
XCOPY /S /I /E /H /Y "%TMPDIR%\venv\Lib\site-packages" "%BUILDROOT%\python\Lib\site-packages" > nul || EXIT /B 1
|
|
|
|
REM This is done to the copy rather than the venv, which still needs pip to
|
|
REM install Sphinx for the docs in :CREATE_RUNTIME_ENV.
|
|
ECHO Removing unused files from the installed packages...
|
|
"%TMPDIR%\venv\Scripts\python.exe" "%WD%\pkg\prune_site_packages.py" "%BUILDROOT%\python\Lib\site-packages" || EXIT /B 1
|
|
|
|
REM NOTE: There is intentionally no space after "site" in the line below, to prevent Python barfing if there's one in the file
|
|
ECHO import site>> "%BUILDROOT%\python\python%PYTHON_MAJOR%%PYTHON_MINOR%._pth"
|
|
|
|
REM NOTE: The Kerberos components are staged into the runtime directory
|
|
REM alongside libpq.dll, not here. See :CREATE_RUNTIME_ENV.
|
|
|
|
ECHO Cleaning up unnecessary .pyc and .pyo files...
|
|
FOR /R "%BUILDROOT%\python" %%f in (*.pyc *.pyo) do DEL /q "%%f" 1> nul 2>&1
|
|
ECHO Removing tests...
|
|
FOR /R "%BUILDROOT%\python\Lib" %%f in (test tests) do RD /Q /S "%%f" 1> nul 2>&1
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
:CREATE_RUNTIME_ENV
|
|
IF NOT EXIST "%BUILDROOT%" MKDIR "%BUILDROOT%"
|
|
MKDIR "%BUILDROOT%\runtime"
|
|
|
|
ECHO Removing webpack caches...
|
|
RD /Q /S "%WD%\web\pgadmin\static\js\generated\.cache" 1> nul 2>&1
|
|
|
|
ECHO Copying web directory...
|
|
ROBOCOPY /S /NFL /NDL /NP "%WD%\web" "%BUILDROOT%\web"
|
|
CALL :CHECK_ROBOCOPY_ERROR || EXIT /B 1
|
|
|
|
ECHO Installing javascript dependencies...
|
|
CD "%BUILDROOT%\web"
|
|
SET "YARN_VERSION="
|
|
FOR /f "delims=" %%v IN ('node -p "require('./package.json').packageManager.split('@')[1]"') DO SET "YARN_VERSION=%%v"
|
|
IF "%YARN_VERSION%"=="" (
|
|
ECHO ERROR: Could not determine Yarn version from package.json packageManager field.
|
|
EXIT /B 1
|
|
)
|
|
CALL yarn set version %YARN_VERSION% || EXIT /B 1
|
|
CALL yarn install --immutable || EXIT /B 1
|
|
CALL npm rebuild || EXIT /B 1
|
|
|
|
ECHO Bundling javascript...
|
|
CALL yarn run bundle || EXIT /B 1
|
|
|
|
ECHO Cleaning up unnecessary .pyc and .pyo files...
|
|
FOR /R "%BUILDROOT%\web" %%f in (*.pyc *.pyo) do DEL /q "%%f" 1> nul 2>&1
|
|
ECHO Removing tests, Python caches and node modules...
|
|
FOR /R "%BUILDROOT%\web" %%f in (tests feature_tests __pycache__ node_modules) do RD /Q /S "%%f" 1> nul 2>&1
|
|
ECHO Removing the test framework...
|
|
RD /Q /S "%BUILDROOT%\web\regression" 1> nul 2>&1
|
|
ECHO Removing tools...
|
|
RD /Q /S "%BUILDROOT%\web\tools" 1> nul 2>&1
|
|
ECHO Removing the JavaScript build configuration...
|
|
RD /Q /S "%BUILDROOT%\web\.yarn" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\yarn.lock" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\.yarnrc.yml" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\package.json" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\jest.config.js" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\babel.cfg" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\babel.config.json" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\webpack.config.js" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\webpack.shim.js" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\.eslintrc.js" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\.editorconfig" 1> nul 2>&1
|
|
ECHO Removing any existing configurations...
|
|
DEL /q "%BUILDROOT%\web\pgadmin4.db" 1> nul 2>&1
|
|
DEL /q "%BUILDROOT%\web\config_local.py" 1> nul 2>&1
|
|
|
|
ECHO Staging license files...
|
|
COPY "%WD%\LICENSE" "%BUILDROOT%\" > nul || EXIT /B 1
|
|
|
|
ECHO Creating config_distro.py
|
|
ECHO SERVER_MODE = False > "%BUILDROOT%\web\config_distro.py"
|
|
ECHO HELP_PATH = '../../../docs/en_US/html/' >> "%BUILDROOT%\web\config_distro.py"
|
|
ECHO DEFAULT_BINARY_PATHS = { >> "%BUILDROOT%\web\config_distro.py"
|
|
ECHO 'pg': '$DIR/../runtime', >> "%BUILDROOT%\web\config_distro.py"
|
|
ECHO 'ppas': '' >> "%BUILDROOT%\web\config_distro.py"
|
|
ECHO } >> "%BUILDROOT%\web\config_distro.py"
|
|
|
|
ECHO Building docs...
|
|
CALL pip install sphinx || EXIT /B 1
|
|
CALL pip install sphinxcontrib-youtube || EXIT /B 1
|
|
MKDIR "%BUILDROOT%\docs\en_US\html"
|
|
CD "%WD%\docs\en_US"
|
|
CALL "%TMPDIR%\venv\Scripts\python.exe" build_code_snippet.py || EXIT /B 1
|
|
CALL "%TMPDIR%\venv\Scripts\sphinx-build.exe" "%WD%\docs\en_US" "%BUILDROOT%\docs\en_US\html" || EXIT /B 1
|
|
|
|
REM Remove unnecessary doc files
|
|
DEL /q "%BUILDROOT%\docs\en_US\html\_static\*.png" 1> nul 2>&1
|
|
RD /Q /S "%BUILDROOT%\docs\en_US\html\_sources" 1> nul 2>&1
|
|
|
|
ECHO Staging runtime components...
|
|
MKDIR "%BUILDROOT%\runtime\resources\app"
|
|
XCOPY /S /I /E /H /Y "%WD%\runtime\assets" "%BUILDROOT%\runtime\resources\app\assets" > nul || EXIT /B 1
|
|
XCOPY /S /I /E /H /Y "%WD%\runtime\src" "%BUILDROOT%\runtime\resources\app\src" > nul || EXIT /B 1
|
|
|
|
COPY "%WD%\runtime\package.json" "%BUILDROOT%\runtime\resources\app\" > nul || EXIT /B 1
|
|
COPY "%WD%\runtime\.yarnrc.yml" "%BUILDROOT%\runtime\resources\app\" > nul || EXIT /B 1
|
|
|
|
CD "%BUILDROOT%\runtime\resources\app\"
|
|
|
|
SET "YARN_VERSION="
|
|
FOR /f "delims=" %%v IN ('node -p "require('./package.json').packageManager.split('@')[1]"') DO SET "YARN_VERSION=%%v"
|
|
IF "%YARN_VERSION%"=="" (
|
|
ECHO ERROR: Could not determine Yarn version from package.json packageManager field.
|
|
EXIT /B 1
|
|
)
|
|
CALL yarn set version %YARN_VERSION% || EXIT /B 1
|
|
CALL yarn workspaces focus --production || EXIT /B 1
|
|
|
|
ECHO Removing yarn cache...
|
|
RD /Q /S "%BUILDROOT%\runtime\resources\app\.yarn" 1> nul 2>&1
|
|
|
|
ECHO Downloading Electron to %TMPDIR%...
|
|
REM Get a fresh copy of electron.
|
|
|
|
REM Resolve the electron version from runtime\yarn.lock, NOT from the npm
|
|
REM registry and NOT from the range in runtime\package.json. The registry's
|
|
REM `latest` dist-tag lands any newly published electron release in shipped
|
|
REM binaries without review, whilst the package.json range is only a lower
|
|
REM bound, so yarn is free to resolve it to a build other than the one we
|
|
REM ship. The lockfile is the single source of truth, and `yarn info`
|
|
REM reports its resolution without touching the network or needing
|
|
REM node_modules.
|
|
REM
|
|
REM It must run inside runtime\ though: outside a yarn project, `yarn info`
|
|
REM silently falls back to querying the registry and still exits 0, so check
|
|
REM the lockfile is present first and validate what comes back.
|
|
IF NOT EXIST "%WD%\runtime\yarn.lock" (
|
|
ECHO ERROR: "%WD%\runtime\yarn.lock" not found; cannot resolve the pinned Electron version.
|
|
EXIT /B 1
|
|
)
|
|
|
|
SET "ELECTRON_VERSION="
|
|
PUSHD "%WD%\runtime" || EXIT /B 1
|
|
FOR /f "delims=" %%i IN ('yarn info electron --json ^| node -e "const lines=require('fs').readFileSync(0,'utf8').split('\n').filter(Boolean);const pkg=lines.map(l=>{try{return JSON.parse(l);}catch(e){return null;}}).find(o=>o&&typeof o.value==='string'&&o.value.startsWith('electron@npm:'));const version=(pkg&&pkg.children&&pkg.children.Version)||'';process.stdout.write(/^[0-9]+[.][0-9]+[.][0-9]+(-[0-9A-Za-z.-]+)?$/.test(version)?version:'');"') DO SET "ELECTRON_VERSION=%%i"
|
|
POPD
|
|
IF "%ELECTRON_VERSION%"=="" (
|
|
ECHO ERROR: Could not resolve the pinned Electron version from "%WD%\runtime\yarn.lock".
|
|
EXIT /B 1
|
|
)
|
|
|
|
REM WGET
|
|
:GET_NW
|
|
wget https://github.com/electron/electron/releases/download/v%ELECTRON_VERSION%/electron-v%ELECTRON_VERSION%-win32-x64.zip -O "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip"
|
|
IF %ERRORLEVEL% NEQ 0 GOTO GET_NW
|
|
|
|
MKDIR "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" || EXIT /B 1
|
|
tar -C "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64" -xvf "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64.zip" || EXIT /B 1
|
|
REM WGET END
|
|
|
|
REM XCOPY
|
|
XCOPY /S /I /E /H /Y "%TMPDIR%\electron-v%ELECTRON_VERSION%-win32-x64\*" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
REM XCOPY END
|
|
|
|
MOVE "%BUILDROOT%\runtime\electron.exe" "%BUILDROOT%\runtime\pgAdmin4.exe"
|
|
|
|
ECHO Downloading rcedit.exe...
|
|
wget https://github.com/electron/rcedit/releases/download/v2.0.0/rcedit-x64.exe -O "%TMPDIR%\rcedit-x64.exe"
|
|
|
|
ECHO Replacing executable icon, description, version...
|
|
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-icon "%WD%\pkg\win32\Resources\pgAdmin4.ico"
|
|
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-version-string "FileDescription" "%APP_NAME%"
|
|
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-version-string "ProductName" "%APP_NAME%"
|
|
%TMPDIR%\rcedit-x64.exe "%BUILDROOT%\runtime\pgAdmin4.exe" --set-product-version "%APP_VERSION%"
|
|
|
|
ECHO Staging PostgreSQL components...
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libpq.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-*-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libssl-*-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libintl-*.dll" (
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libintl-*.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
)
|
|
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libiconv-*.dll" (
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libiconv-*.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
)
|
|
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\liblz4.dll" (
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\liblz4.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
)
|
|
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libzstd.dll" (
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\libzstd.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
)
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\zlib1.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\pg_dump.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\pg_dumpall.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\pg_restore.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_POSTGRES_DIR%\bin\psql.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
|
|
REM The Kerberos runtime belongs here, next to libpq.dll and the client
|
|
REM binaries: PostgreSQL 18 and later are built with GSSAPI support, so
|
|
REM libpq.dll has a load-time dependency on gssapi64.dll, and pg_dump.exe
|
|
REM and friends resolve it from their own directory. The pgAdmin server
|
|
REM process finds it here too, because the runtime prepends this directory
|
|
REM to PATH before spawning Python.
|
|
REM
|
|
REM krb5_64.dll loads krbcc64.dll dynamically rather than importing it, but
|
|
REM it is not optional: on Windows krb5 defaults the credential cache type
|
|
REM to CCAPI, and the entry points for that are left null when the DLL
|
|
REM cannot be loaded, so the first use of the default credential cache
|
|
REM calls address zero. ccapiserver.exe is the process krbcc64.dll drives.
|
|
ECHO Staging Kerberos components...
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\kinit.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\krb5_64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\comerr64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\k5sprt64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\gssapi64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\krbcc64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
COPY "%PGADMIN_KRB5_DIR%\bin\ccapiserver.exe" "%BUILDROOT%\runtime" > nul || EXIT /B 1
|
|
|
|
ECHO Staging VC++ runtime...
|
|
MKDIR "%BUILDROOT%\installer" || EXIT /B 1
|
|
COPY "%PGADMIN_VCREDIST_DIR%\%PGADMIN_VCREDIST_FILE%" "%BUILDROOT%\installer" > nul || EXIT /B 1
|
|
|
|
CD %WD%
|
|
EXIT /B 0
|
|
|
|
|
|
:CREATE_INSTALLER
|
|
ECHO Preparing for creation of windows installer...
|
|
IF NOT EXIST "%DISTROOT%" MKDIR "%DISTROOT%"
|
|
|
|
ECHO Copying icon file...
|
|
COPY "%WD%\pkg\win32\Resources\pgAdmin4.ico" "%BUILDROOT%" > nul || EXIT /B 1
|
|
|
|
CD "%WD%\pkg\win32"
|
|
|
|
ECHO Processing installer configuration script...
|
|
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in" "-o" "%WD%\pkg\win32\installer.iss.in_stage1" "-s" MYAPP_FULLVERSION -r """%APP_VERSION%"""
|
|
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage1" "-o" "%WD%\pkg\win32\installer.iss.in_stage2" "-s" MYAPP_VERSION -r """v%APP_MAJOR%"""
|
|
CALL "%PGADMIN_PYTHON_DIR%\python" "%WD%\pkg\win32\replace.py" "-i" "%WD%\pkg\win32\installer.iss.in_stage2" "-o" "%WD%\pkg\win32\installer.iss" "-s" MYAPP_VCDIST -r """%PGADMIN_VCREDIST_DIRNAME%\%PGADMIN_VCREDIST_FILE%"""
|
|
|
|
ECHO Cleaning up...
|
|
DEL /s "%WD%\pkg\win32\installer.iss.in_stage*" > nul
|
|
|
|
ECHO Creating windows installer using INNO tool...
|
|
REM Inno signs the installer and its uninstaller itself, one file per call,
|
|
REM through the same script as :SIGN_FILES. Inno replaces $q with a quote
|
|
REM and $f with the already-quoted file name. The outer pair of quotes is
|
|
REM for cmd /c, which strips the first and last quote of its command line.
|
|
IF NOT "%PGADMIN_WINDOWS_CSC%" == "" (
|
|
CALL "%PGADMIN_INNOTOOL_DIR%\ISCC.exe" "%WD%\pkg\win32\installer.iss" "/DSIGNED" "/SpgAdminSigntool=%ComSpec% /c $q$q%WD%\pkg\win32\sign-files.bat$q $q%PGADMIN_WINDOWS_CSC%$q $f$q" || EXIT /B 1
|
|
) ELSE (
|
|
CALL "%PGADMIN_INNOTOOL_DIR%\ISCC.exe" "%WD%\pkg\win32\installer.iss" || EXIT /B 1
|
|
)
|
|
|
|
ECHO Renaming installer...
|
|
MOVE "%WD%\pkg\win32\Output\pgadmin4-setup.exe" "%DISTROOT%\%INSTALLERNAME%" > nul || EXIT /B 1
|
|
|
|
ECHO Location - %DISTROOT%\%INSTALLERNAME%
|
|
ECHO Installer generated successfully.
|
|
|
|
CD %WD%
|
|
EXIT /B 0
|
|
|
|
:GENERATE_SBOM
|
|
ECHO Generating SBOM...
|
|
CALL syft "%BUILDROOT%" -o cyclonedx-json > "%BUILDROOT%\sbom.json"
|
|
|
|
EXIT /B 0
|
|
|
|
:VERIFY_SIGNATURE
|
|
IF "%PGADMIN_WINDOWS_CSC%" == "" (
|
|
ECHO Skipping signature verification ^(PGADMIN_WINDOWS_CSC is not set^)...
|
|
EXIT /B 0
|
|
)
|
|
|
|
ECHO Verifying the installer signature...
|
|
|
|
CALL "%PGADMIN_SIGNTOOL_DIR%\signtool.exe" verify /pa /v "%DISTROOT%\%INSTALLERNAME%"
|
|
REM PAUSE was the old handler here, which on the signing host either returns
|
|
REM immediately, leaving the build to carry on with an unverified installer,
|
|
REM or blocks until the job times out. There is nobody at that machine to
|
|
REM press a key.
|
|
IF ERRORLEVEL 1 (
|
|
ECHO.
|
|
ECHO ************************************************************
|
|
ECHO * Failed to verify signature of the installer
|
|
ECHO ************************************************************
|
|
EXIT /B 1
|
|
)
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
REM Sign one or more files, passed as quoted arguments, with a SHA-256 file
|
|
REM digest. pkg\win32\sign-files.bat does the work, in signtool's split
|
|
REM digest, sign and ingest steps, because the one-step form cannot use a
|
|
REM SHA-2 digest with the card's key; the script explains why. All the files
|
|
REM go in one call because each call costs one PIN prompt, and their names
|
|
REM must be unique, which the script checks.
|
|
:SIGN_FILES
|
|
IF "%PGADMIN_WINDOWS_CSC%" == "" EXIT /B 0
|
|
|
|
CALL "%WD%\pkg\win32\sign-files.bat" "%PGADMIN_WINDOWS_CSC%" %*
|
|
REM IF ERRORLEVEL, not IF %ERRORLEVEL%: the latter would be expanded before
|
|
REM the script had run were this ever moved inside a parenthesised block.
|
|
IF ERRORLEVEL 1 (
|
|
ECHO.
|
|
ECHO ************************************************************
|
|
ECHO * Failed to sign one or more files
|
|
ECHO ************************************************************
|
|
EXIT /B 1
|
|
)
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
REM Sign the components that we build ourselves: the runtime executable, and
|
|
REM the PostgreSQL and Kerberos utilities and libraries obtained from the
|
|
REM winpgbuild project, all of which are staged into the runtime directory by
|
|
REM :CREATE_RUNTIME_ENV. The Electron, Python and VC++ runtime components are
|
|
REM deliberately left alone, as they are third party binaries that we do not
|
|
REM build, and signing them would replace any signature of their own. Names
|
|
REM are matched as patterns as some of the libraries include version numbers,
|
|
REM and some of them are optional.
|
|
:SIGN_COMPONENTS
|
|
IF "%PGADMIN_WINDOWS_CSC%" == "" (
|
|
ECHO Skipping code signing ^(PGADMIN_WINDOWS_CSC is not set^)...
|
|
EXIT /B 0
|
|
)
|
|
|
|
ECHO Attempting to sign the pgAdmin, PostgreSQL and Kerberos components...
|
|
|
|
SETLOCAL EnableDelayedExpansion
|
|
SET "COMPONENTS="
|
|
FOR %%p IN (pgAdmin4.exe libpq.dll libcrypto-*-x64.dll libssl-*-x64.dll libintl-*.dll libiconv-*.dll liblz4.dll libzstd.dll zlib1.dll pg_dump.exe pg_dumpall.exe pg_restore.exe psql.exe kinit.exe krb5_64.dll comerr64.dll k5sprt64.dll gssapi64.dll krbcc64.dll ccapiserver.exe) DO (
|
|
FOR /F "delims=" %%f IN ('DIR /B "%BUILDROOT%\runtime\%%p" 2^>nul') DO SET "COMPONENTS=!COMPONENTS! "%BUILDROOT%\runtime\%%f""
|
|
)
|
|
IF "!COMPONENTS!" == "" (
|
|
ECHO.
|
|
ECHO ************************************************************
|
|
ECHO * No components were found to sign
|
|
ECHO ************************************************************
|
|
ENDLOCAL
|
|
EXIT /B 1
|
|
)
|
|
CALL :SIGN_FILES !COMPONENTS! || EXIT /B 1
|
|
ENDLOCAL
|
|
|
|
EXIT /B 0
|
|
|
|
|
|
:USAGE
|
|
ECHO Invalid command line options.
|
|
ECHO Usage: "Make.bat [clean]"
|
|
ECHO.
|
|
|
|
EXIT /B 1
|
|
|
|
:CHECK_ROBOCOPY_ERROR
|
|
IF %ERRORLEVEL% GEQ 8 EXIT /B %ERRORLEVEL%
|
|
EXIT /B 0
|
|
|
|
|
|
REM Check that a prerequisite directory exists, reporting the path that was
|
|
REM looked for and how to correct it if it does not. The first argument is the
|
|
REM directory and the second the hint; both are echoed here, outside any
|
|
REM parenthesised block, so a parenthesis in either is just a character.
|
|
:REQUIRE_DIR
|
|
IF EXIST "%~1" EXIT /B 0
|
|
|
|
ECHO %~1 does not exist.
|
|
ECHO %~2
|
|
|
|
EXIT /B 1
|