name: Build agent
concurrency:
group: ci-build-agent-${{ github.sha }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
sign_output:
description: 'Sign the output'
required: true
type: boolean
default: false
permissions:
contents: read
jobs:
build_agent_win64:
runs-on: windows-latest
timeout-minutes: 45
env:
NODE_VERSION: '24'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_CACHE: 'remote:rw'
permissions:
actions: read
contents: write
id-token: write # Required for OIDC authentication with Azure Trusted Signing
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Install NSIS
run: choco install nsis
- name: Add NSIS to PATH
run: echo "C:\Program Files (x86)\NSIS" >> $GITHUB_PATH
shell: bash
- name: Install Wget
run: choco install wget
- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
# See: https://github.com/actions/cache/blob/5a3ec84eff668545956fd18022155c47e93e2684/examples.md#node---npm
- name: Get npm cache directory
id: npm-cache-dir
shell: pwsh
run: echo "dir=$(npm config get cache)" >> ${env:GITHUB_OUTPUT}
- name: Cache node modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
env:
cache-name: cache-node-modules
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-build-agent-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-agent-${{ env.cache-name }}-
${{ runner.os }}-build-agent
${{ runner.os }}-
- name: Install dependencies
run: npm ci
- name: Set repo hash
shell: bash
# This forces git shorthash to match between @medplum/agent and @medplum/core
run: |
set -e
echo "MEDPLUM_GIT_SHORTHASH=$(git rev-parse --short=7 HEAD)" >> $GITHUB_ENV
- name: Build
run: npm run build -- --filter=@medplum/agent
- name: Find signtool
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
id: find-signtool
with:
result-encoding: string
script: |
const fs = require('node:fs/promises');
/**
* Searches the installed Windows SDKs for the most recent signtool.exe version
* Taken from https://github.com/dlemstra/code-sign-action
* @returns Path to most recent signtool.exe (x86 version)
*/
async function getSigntoolLocation() {
const windowsKitsFolder = 'C:/Program Files (x86)/Windows Kits/10/bin/';
const folders = await fs.readdir(windowsKitsFolder);
let fileName = '';
let maxVersion = 0;
for (const folder of folders) {
if (!folder.endsWith('.0')) {
continue;
}
const folderVersion = Number.parseInt(folder.replaceAll('.',''));
if (folderVersion > maxVersion) {
const signtoolFilename = `${windowsKitsFolder}${folder}/x64/signtool.exe`;
try {
const stat = await fs.stat(signtoolFilename);
if (stat.isFile()) {
fileName = signtoolFilename;
maxVersion = folderVersion;
}
} catch {
console.warn('Skipping %s due to error.', signtoolFilename);
}
}
}
if(fileName == '') {
throw new Error('Unable to find signtool.exe in ' + windowsKitsFolder);
}
console.log(fileName);
return fileName;
}
const path = await getSigntoolLocation();
return path.replace(' ', '\ ');
- name: Build Agent executable and download dependencies
shell: bash
run: ./scripts/build-agent-win64.sh
env:
SHAWL_VERSION: 'v1.7.0'
SIGNTOOL_PATH: ${{steps.find-signtool.outputs.result}}
- name: Login to Azure
if: ${{ inputs.sign_output }}
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
enable-AzPSSession: true
- name: Sign executables with Azure Trusted Signing
if: ${{ inputs.sign_output }}
uses: azure/trusted-signing-action@fc390cf8ed0f14e248a542af1d838388a47c7a7c # v0.5.10
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
exclude-azure-cli-credential: true
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-powershell-credential: false
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: GitHubActionSigner
certificate-profile-name: MedplumCI
files-folder: ${{ github.workspace }}\packages\agent\dist
files-folder-filter: medplum-agent-*-win64.exe,shawl-*.exe
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
append-signature: true
- name: Set Azure CodeSigning DLL path
if: ${{ inputs.sign_output }}
shell: pwsh
run: |
$dll = Get-ChildItem -Path "$env:LOCALAPPDATA" -Filter "Azure.CodeSigning.Dlib.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1
if ($dll) {
$folder = Split-Path $dll.FullName -Parent
echo "AZURE_CODESIGNING_PATH=$folder" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
Write-Host "Set AZURE_CODESIGNING_PATH=$folder"
} else {
Write-Error "Azure.CodeSigning.Dlib.dll not found"
exit 1
}
- name: Import GPG key
if: ${{ inputs.sign_output }}
run: echo "${{ secrets.MEDPLUM_RELEASE_GPG_KEY }}" | gpg --batch --no-tty --import
- name: Build Agent installer
shell: bash
run: ./scripts/build-agent-installer-win64.sh
env:
SHAWL_VERSION: 'v1.7.0'
SIGNTOOL_PATH: ${{steps.find-signtool.outputs.result}}
SKIP_SIGNING: ${{ !inputs.sign_output && '1' || '' }}
GPG_KEY_ID: ${{ secrets.MEDPLUM_RELEASE_GPG_KEY_ID }}
GPG_PASSPHRASE: ${{ secrets.MEDPLUM_RELEASE_GPG_PASSPHRASE }}
- name: Set Medplum version
shell: bash
run: |
set -e
echo "MEDPLUM_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV
- name: Upload agent installer
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: medplum-agent-installer-${{ env.MEDPLUM_VERSION }}-windows
path: packages/agent/medplum-agent-installer-*
build_agent_linux-x64:
runs-on: ubuntu-latest
timeout-minutes: 45
env:
NODE_VERSION: '24'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_CACHE: 'remote:rw'
permissions:
actions: read
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Cache node modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-agent-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-agent-${{ env.cache-name }}-
${{ runner.os }}-build-agent
${{ runner.os }}-
- name: Install dependencies
run: npm ci
- name: Set repo hash
shell: bash
# This forces git shorthash to match between @medplum/agent and @medplum/core
run: |
set -e
echo "MEDPLUM_GIT_SHORTHASH=$(git rev-parse --short=7 HEAD)" >> $GITHUB_ENV
- name: Build
run: npm run build -- --filter=@medplum/agent
- name: Import GPG key
if: ${{ inputs.sign_output }}
run: echo "${{ secrets.MEDPLUM_RELEASE_GPG_KEY }}" | gpg --batch --no-tty --import
- name: Build Agent
shell: bash
run: ./scripts/build-agent-installer-linux.sh
env:
SKIP_SIGNING: ${{ !inputs.sign_output && '1' || '' }}
GPG_KEY_ID: ${{ secrets.MEDPLUM_RELEASE_GPG_KEY_ID }}
GPG_PASSPHRASE: ${{ secrets.MEDPLUM_RELEASE_GPG_PASSPHRASE }}
- name: Set Medplum version
shell: bash
run: |
set -e
echo "MEDPLUM_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV
- name: Make tarball
shell: bash
run: |
set -e
mkdir medplum-agent-$MEDPLUM_VERSION-linux-x64
cp packages/agent/medplum-agent-$MEDPLUM_VERSION-linux ./medplum-agent-$MEDPLUM_VERSION-linux-x64/medplum-agent-$MEDPLUM_VERSION-linux-x64
cp packages/agent/medplum-agent-$MEDPLUM_VERSION-linux.sha256 ./medplum-agent-$MEDPLUM_VERSION-linux-x64/medplum-agent-$MEDPLUM_VERSION-linux-x64.sha256
tar -czvf ./medplum-agent-$MEDPLUM_VERSION-linux-x64.tar.gz ./medplum-agent-$MEDPLUM_VERSION-linux-x64
- name: Upload agent installer
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: medplum-agent-${{ env.MEDPLUM_VERSION }}-linux-x64
path: medplum-agent-${{ env.MEDPLUM_VERSION }}-linux-x64.tar.gz
build_agent_linux-arm64:
runs-on: ubuntu-24.04-arm
timeout-minutes: 45
env:
NODE_VERSION: '24'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_CACHE: 'remote:rw'
permissions:
actions: read
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Cache node modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-agent-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-agent-${{ env.cache-name }}-
${{ runner.os }}-build-agent
${{ runner.os }}-
- name: Install dependencies
run: npm ci
- name: Set repo hash
shell: bash
# This forces git shorthash to match between @medplum/agent and @medplum/core
run: |
set -e
echo "MEDPLUM_GIT_SHORTHASH=$(git rev-parse --short=7 HEAD)" >> $GITHUB_ENV
- name: Build
run: npm run build -- --filter=@medplum/agent
- name: Import GPG key
if: ${{ inputs.sign_output }}
run: echo "${{ secrets.MEDPLUM_RELEASE_GPG_KEY }}" | gpg --batch --no-tty --import
- name: Build Agent
shell: bash
run: ./scripts/build-agent-installer-linux.sh
env:
SKIP_SIGNING: ${{ !inputs.sign_output && '1' || '' }}
GPG_KEY_ID: ${{ secrets.MEDPLUM_RELEASE_GPG_KEY_ID }}
GPG_PASSPHRASE: ${{ secrets.MEDPLUM_RELEASE_GPG_PASSPHRASE }}
- name: Set Medplum version
shell: bash
run: |
set -e
echo "MEDPLUM_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV
- name: Make tarball
shell: bash
run: |
set -e
mkdir medplum-agent-$MEDPLUM_VERSION-linux-arm64
cp packages/agent/medplum-agent-$MEDPLUM_VERSION-linux ./medplum-agent-$MEDPLUM_VERSION-linux-arm64/medplum-agent-$MEDPLUM_VERSION-linux-arm64
cp packages/agent/medplum-agent-$MEDPLUM_VERSION-linux.sha256 ./medplum-agent-$MEDPLUM_VERSION-linux-arm64/medplum-agent-$MEDPLUM_VERSION-linux-arm64.sha256
tar -czvf ./medplum-agent-$MEDPLUM_VERSION-linux-arm64.tar.gz ./medplum-agent-$MEDPLUM_VERSION-linux-arm64
- name: Upload agent installer
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: medplum-agent-${{ env.MEDPLUM_VERSION }}-linux-arm64
path: medplum-agent-${{ env.MEDPLUM_VERSION }}-linux-arm64.tar.gz
build_agent_macos-arm64:
runs-on: macos-latest
timeout-minutes: 45
env:
NODE_VERSION: '24'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
TURBO_CACHE: 'remote:rw'
permissions:
actions: read
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Setup Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Cache node modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-agent-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-agent-${{ env.cache-name }}-
${{ runner.os }}-build-agent
${{ runner.os }}-
- name: Install dependencies
run: npm ci
- name: Set repo hash
shell: bash
# This forces git shorthash to match between @medplum/agent and @medplum/core
run: |
set -e
echo "MEDPLUM_GIT_SHORTHASH=$(git rev-parse --short=7 HEAD)" >> $GITHUB_ENV
- name: Build
run: npm run build -- --filter=@medplum/agent
- name: Import GPG key
if: ${{ inputs.sign_output }}
run: echo "${{ secrets.MEDPLUM_RELEASE_GPG_KEY }}" | gpg --batch --no-tty --import
- name: Build Agent
shell: bash
run: ./scripts/build-agent-macos.sh
env:
SKIP_SIGNING: ${{ !inputs.sign_output && '1' || '' }}
GPG_KEY_ID: ${{ secrets.MEDPLUM_RELEASE_GPG_KEY_ID }}
GPG_PASSPHRASE: ${{ secrets.MEDPLUM_RELEASE_GPG_PASSPHRASE }}
- name: Set Medplum version
shell: bash
run: |
set -e
echo "MEDPLUM_VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_ENV
- name: Make tarball
shell: bash
run: |
set -e
mkdir medplum-agent-$MEDPLUM_VERSION-macos-arm64
cp packages/agent/medplum-agent-$MEDPLUM_VERSION-macos ./medplum-agent-$MEDPLUM_VERSION-macos-arm64/medplum-agent-$MEDPLUM_VERSION-macos-arm64
cp packages/agent/medplum-agent-$MEDPLUM_VERSION-macos.sha256 ./medplum-agent-$MEDPLUM_VERSION-macos-arm64/medplum-agent-$MEDPLUM_VERSION-macos-arm64.sha256
tar -czvf ./medplum-agent-$MEDPLUM_VERSION-macos-arm64.tar.gz ./medplum-agent-$MEDPLUM_VERSION-macos-arm64
- name: Upload agent installer
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: medplum-agent-${{ env.MEDPLUM_VERSION }}-macos-arm64
path: medplum-agent-${{ env.MEDPLUM_VERSION }}-macos-arm64.tar.gz