Skip to content

Admin Sample CD

Admin Sample CD #1061

name: Admin Sample CD
# Project templates come equipped with CI/CD for both Azure DevOps and GitHub, providing you with a hassle-free way to get started with your new project. It is important to note that you should not depend on the contents of this file. More info at https://bitplatform.dev/templates/dev-ops
env:
SERVER_WEB_ADDRESS: 'https://adminpanel.bitplatform.dev'
SERVER_API_ADDRESS: 'https://adminpanel-api.bitplatform.dev'
APP_SERVICE_NAME: 'bit-adminpanel'
R2_BUCKET: 'adminpanel'
APP_BUNDLE_ID: 'com.bitplatform.AdminPanel.Template'
OpenAI__ApiKey: ${{ secrets.OpenAI__ApiKey }}
OpenAI__Endpoint: ${{ secrets.OpenAI__Endpoint }}
OPENAI__MODEL: ${{ secrets.OPENAI__MODEL }}
on:
workflow_dispatch:
inputs:
server:
description: Server web + api
type: boolean
default: true
wasm:
description: Blazor WASM standalone
type: boolean
default: true
windows:
description: Windows app
type: boolean
default: true
android:
description: Android app
type: boolean
default: true
ios:
description: iOS app
type: boolean
default: true
workflow_call:
inputs:
server:
type: boolean
default: true
wasm:
type: boolean
default: true
windows:
type: boolean
default: true
android:
type: boolean
default: true
ios:
type: boolean
default: true
permissions:
contents: read
jobs:
build_api_blazor:
name: build api + blazor web
if: inputs.server
runs-on: windows-2025
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: src/global.json
- name: Create project from Boilerplate
run: |
cd src/Templates/Boilerplate && dotnet build -c Release
dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0
dotnet new install Bit.Boilerplate.0.0.0.nupkg
cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --module Admin --appInsights --apiServerUrl ${{ env.SERVER_API_ADDRESS }} --webAppUrl ${{ env.SERVER_WEB_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR --ads --api Standalone --brouter true
- name: Use Bit.ResxTranslator
run: |
cd AdminPanel
dotnet tool install --global Bit.ResxTranslator --prerelease
bit-resx-translate
- name: Update core appsettings.json
uses: devops-actions/variable-substitution@ae7b1f3676ae374dc70282e6b9650bc50b611222 # v1.2
with:
files: 'AdminPanel/**/appsettings*json'
env:
ServerAddress: ${{ env.SERVER_API_ADDRESS }}
WebAppRender.BlazorMode: 'BlazorWebAssembly'
GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }}
AdsPushVapid.PublicKey: ${{ secrets.ADMINPANEL_PUBLIC_VAPIDKEY }}
ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }}
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install wasm
run: cd src && dotnet workload install wasm-tools
- name: Configure text embedding
run: |
sed -i 's/IsEmbeddingEnabled = false/IsEmbeddingEnabled = true/' AdminPanel/src/Server/AdminPanel.Server.Api/Infrastructure/Data/AppDbContext.cs
- name: Generate CSS/JS files
run: |
dotnet build AdminPanel/src/Client/AdminPanel.Client.Core/AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
dotnet build AdminPanel/src/Client/AdminPanel.Client.Web/AdminPanel.Client.Web.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
- name: Publish Server Web
run: dotnet publish AdminPanel/src/Server/AdminPanel.Server.Web/AdminPanel.Server.Web.csproj -c Release -o server-web -p:Version="${{ vars.APP_VERSION}}" -p:AspNetCoreHostingModel=OutOfProcess
- name: Publish Server API
run: dotnet publish AdminPanel/src/Server/AdminPanel.Server.Api/AdminPanel.Server.Api.csproj -c Release -o server-api -p:Version="${{ vars.APP_VERSION}}" -p:AspNetCoreHostingModel=OutOfProcess
# Server-owned; C:\inetpub keeps its own copies. Also keeps them out of the artifact.
- name: Strip server-owned files from bundles
shell: pwsh
run: |
foreach ($bundle in 'server-web', 'server-api') {
Get-ChildItem $bundle -File -Include 'appsettings*.json', 'AppCertificate.*', 'AppleAuthKey.p8', '*.pfx' -Recurse |
ForEach-Object { Write-Host "Removing $($_.FullName)"; Remove-Item $_.FullName -Force }
}
- name: Upload server web artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: AdminPanel
path: server-web
include-hidden-files: true # Required for wwwroot/.well-known folder
- name: Upload server api artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: AdminPanelApi
path: server-api
include-hidden-files: true # Required for wwwroot/.well-known folder
deploy_server_web:
name: deploy server web
needs: build_api_blazor
runs-on: [self-hosted, Windows, X64, bitplatform-iis]
environment:
name: production
url: 'https://adminpanel.bitplatform.dev'
steps:
- name: Checkout deploy action
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 1
sparse-checkout: .github/actions
- name: Deploy
uses: ./.github/actions/deploy-to-server
with:
artifact-name: AdminPanel
app-pool: AdminPanel
site-path: C:\inetpub\AdminPanel
sentinel-file: AdminPanel.Server.Web.dll
health-check-url: 'https://adminpanel.bitplatform.dev/health'
cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
cloudflare-token: ${{ secrets.CLOUDFLARE_TOKEN }}
deploy_server_api:
name: deploy server api
needs: build_api_blazor
runs-on: [self-hosted, Windows, X64, bitplatform-iis]
environment:
name: production
url: 'https://adminpanel-api.bitplatform.dev'
steps:
- name: Checkout deploy action
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 1
sparse-checkout: .github/actions
- name: Deploy
uses: ./.github/actions/deploy-to-server
with:
artifact-name: AdminPanelApi
app-pool: AdminPanelApi
site-path: C:\inetpub\AdminPanelApi
sentinel-file: AdminPanel.Server.Api.dll
health-check-url: 'https://adminpanel-api.bitplatform.dev/health'
cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
cloudflare-token: ${{ secrets.CLOUDFLARE_TOKEN }}
deploy_blazor_wasm_standalone:
name: build blazor wasm standalone
if: inputs.wasm
runs-on: ubuntu-24.04
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: src/global.json
- name: Create project from Boilerplate
run: |
cd src/Templates/Boilerplate && dotnet build -c Release
dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0
dotnet new install Bit.Boilerplate.0.0.0.nupkg
cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --module Admin --sentry --apiServerUrl ${{ env.SERVER_API_ADDRESS }} --webAppUrl ${{ env.SERVER_WEB_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR --ads --brouter false --theme Material
- name: Update core appsettings.json
uses: devops-actions/variable-substitution@ae7b1f3676ae374dc70282e6b9650bc50b611222 # v1.2
with:
files: 'AdminPanel/**/appsettings*json'
env:
ServerAddress: ${{ env.SERVER_API_ADDRESS }}
Logging.Sentry.Dsn: ${{ secrets.ADMINPANEL_SENTRY_DSN }}
GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }}
AdsPushVapid.PublicKey: ${{ secrets.ADMINPANEL_PUBLIC_VAPIDKEY }}
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Install wasm
run: cd src && dotnet workload install wasm-tools
- name: Generate CSS/JS files
run: dotnet build AdminPanel/src/Client/AdminPanel.Client.Core/AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" -p:WasmEnableSIMD=true --no-restore -c Release
- name: Publish
run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Web/AdminPanel.Client.Web.csproj -c Release -o client -p:WasmEnableSIMD=true -p:Version="${{ vars.APP_VERSION}}"
- name: Upload to asw
run: |
npm install -g @azure/static-web-apps-cli
swa deploy --deployment-token ${{ secrets.ADMINPANEL_ASW_TOKEN }} --env production --app-location client/wwwroot
build_blazor_hybrid_windows:
name: build blazor hybrid (windows)
if: inputs.windows
runs-on: windows-2025
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: src\global.json
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Create project from Boilerplate
run: |
cd src\Templates\Boilerplate && dotnet build -c Release
dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0
dotnet new install Bit.Boilerplate.0.0.0.nupkg
cd ..\..\..\ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --module Admin --sentry --apiServerUrl ${{ env.SERVER_API_ADDRESS }} --webAppUrl ${{ env.SERVER_WEB_ADDRESS }} --filesStorage AzureBlobStorage --captcha reCaptcha --signalR --ads --brouter true
- name: Use Bit.ResxTranslator
run: |
cd AdminPanel
dotnet tool install --global Bit.ResxTranslator --prerelease
bit-resx-translate
- name: Update core appsettings.json
uses: devops-actions/variable-substitution@ae7b1f3676ae374dc70282e6b9650bc50b611222 # v1.2
with:
files: 'AdminPanel\**\appsettings*json'
env:
WebAppUrl: ${{ env.SERVER_WEB_ADDRESS }}
ServerAddress: ${{ env.SERVER_API_ADDRESS }}
Logging.Sentry.Dsn: ${{ secrets.ADMINPANEL_SENTRY_DSN }}
GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }}
WindowsUpdate.FilesUrl: https://windows-adminpanel.bitplatform.dev
- name: Generate CSS/JS files
run: dotnet build AdminPanel\src\Client\AdminPanel.Client.Core\AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
# Needed for delta packages. Fails on the first run, when the bucket is empty.
- name: Download previous releases from R2
continue-on-error: true
run: |
cd AdminPanel\src\Client\AdminPanel.Client.Windows\
dotnet tool restore
dotnet vpk download s3 --endpoint ${{ vars.R2_ENDPOINT }} --bucket ${{ env.R2_BUCKET }} --keyId ${{ secrets.R2_ACCESS_KEY_ID }} --secret ${{ secrets.R2_SECRET_ACCESS_KEY }} -o .\Releases
- name: Publish
run: |
cd AdminPanel\src\Client\AdminPanel.Client.Windows\
dotnet publish AdminPanel.Client.Windows.csproj -c Release -o .\publish-result -r win-x64 -p:Version="${{ vars.APP_VERSION}}" -p:PublishReadyToRun=true -p:PublishReadyToRunComposite=true --self-contained
dotnet tool restore
dotnet vpk pack -u AdminPanel.Client.Windows -v "${{ vars.APP_VERSION }}" -p .\publish-result -e AdminPanel.Client.Windows.exe -r win-x64 --framework webview2 --icon .\wwwroot\favicon.ico --packTitle 'AdminPanel'
# The update feed behind WindowsUpdate.FilesUrl.
- name: Upload releases to R2
run: |
cd AdminPanel\src\Client\AdminPanel.Client.Windows\
dotnet vpk upload s3 --endpoint ${{ vars.R2_ENDPOINT }} --bucket ${{ env.R2_BUCKET }} --keyId ${{ secrets.R2_ACCESS_KEY_ID }} --secret ${{ secrets.R2_SECRET_ACCESS_KEY }} -o .\Releases --keepMaxReleases 10
build_blazor_hybrid_android:
name: build blazor hybrid (android)
if: inputs.android
runs-on: ubuntu-24.04
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: src/global.json
- name: Create project from Boilerplate
run: |
cd src/Templates/Boilerplate && dotnet build -c Release
dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0
dotnet new install Bit.Boilerplate.0.0.0.nupkg
cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --module Admin --sentry --apiServerUrl ${{ env.SERVER_API_ADDRESS }} --webAppUrl ${{ env.SERVER_WEB_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR --ads --brouter true
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Extract Android signing key from env
uses: timheuer/base64-to-file@dfa5a5dd38803cb4f5c3a081eeb28e8362d43e9c # v2.0.0
with:
fileDir: './AdminPanel/src/Client/AdminPanel.Client.Maui/'
fileName: 'AdminPanel.keystore'
encodedString: ${{ secrets.ANDROID_RELEASE_KEYSTORE_FILE_BASE64 }}
- name: Extract Android signing key from env
uses: timheuer/base64-to-file@dfa5a5dd38803cb4f5c3a081eeb28e8362d43e9c # v2.0.0
with:
fileDir: './AdminPanel/src/Client/AdminPanel.Client.Maui/Platforms/Android'
fileName: 'google-services.json'
encodedString: ${{ secrets.ADMINPANEL_GOOGLE_SERVICES_JSON_BASE64 }}
- name: Use Bit.ResxTranslator
run: |
cd AdminPanel
dotnet tool install --global Bit.ResxTranslator --prerelease
bit-resx-translate
- name: Update core appsettings.json
uses: devops-actions/variable-substitution@ae7b1f3676ae374dc70282e6b9650bc50b611222 # v1.2
with:
files: 'AdminPanel/**/appsettings*json'
env:
WebAppUrl: ${{ env.SERVER_WEB_ADDRESS }}
ServerAddress: ${{ env.SERVER_API_ADDRESS }}
Logging.Sentry.Dsn: ${{ secrets.ADMINPANEL_SENTRY_DSN }}
GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }}
- name: Install maui
run: cd src && dotnet workload install maui-android
- name: Install Android Sdk platform tools
run: ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "platform-tools"
- name: Generate CSS/JS files
run: |
dotnet build AdminPanel/src/Client/AdminPanel.Client.Core/AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
dotnet build AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
- name: Publish aab
run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -c Release -p:EnableLLVM=true -p:AndroidEnableProfiledAot=false -p:AndroidPackageFormats=aab%3Bapk -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="AdminPanel.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:Version="${{ vars.APP_VERSION }}" -p:ApplicationTitle="AdminPanel" -p:ApplicationId="${{ env.APP_BUNDLE_ID }}" -f net10.0-android
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: android-bundle
path: AdminPanel/src/Client/AdminPanel.Client.Maui/bin/Release/net10.0-android/*-Signed.apk
# versionCode derives from APP_VERSION; bump it or Play rejects the bundle.
- name: Upload to Google Play (internal track)
uses: r0adkll/upload-google-play@e738b9dd8f2476ea806d921b64aacd24f34515a5 # v1.1.5
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: ${{ env.APP_BUNDLE_ID }}
releaseFiles: AdminPanel/src/Client/AdminPanel.Client.Maui/bin/Release/net10.0-android/*-Signed.aab
releaseName: ${{ vars.APP_VERSION }}
track: internal
status: completed
build_blazor_hybrid_ios:
name: build blazor hybrid (iOS-macOS)
if: inputs.ios
runs-on: macOS-26
steps:
- name: Checkout source code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: src/global.json
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
with:
xcode-version: '26.6'
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- name: Create project from Boilerplate
run: |
cd src/Templates/Boilerplate && dotnet build -c Release
dotnet pack -c Release -o . -p:ReleaseVersion=0.0.0 -p:PackageVersion=0.0.0
dotnet new install Bit.Boilerplate.0.0.0.nupkg
cd ../../../ && dotnet new bit-bp --name AdminPanel --database PostgreSQL --module Admin --sentry --apiServerUrl ${{ env.SERVER_API_ADDRESS }} --webAppUrl ${{ env.SERVER_WEB_ADDRESS }} --filesStorage AzureBlobStorage --notification --captcha reCaptcha --signalR --ads --brouter true
- name: Use Bit.ResxTranslator
run: |
cd AdminPanel
dotnet tool install --global Bit.ResxTranslator --prerelease
bit-resx-translate
- name: Update core appsettings.json
uses: devops-actions/variable-substitution@ae7b1f3676ae374dc70282e6b9650bc50b611222 # v1.2
with:
files: 'AdminPanel/**/appsettings*json'
env:
WebAppUrl: ${{ env.SERVER_WEB_ADDRESS }}
ServerAddress: ${{ env.SERVER_API_ADDRESS }}
Logging.Sentry.Dsn: ${{ secrets.ADMINPANEL_SENTRY_DSN }}
GoogleRecaptchaSiteKey: ${{ secrets.GOOGLE_RECAPTCHA_SITE_KEY }}
- name: Install maui
run: cd src && dotnet workload install maui
- name: Import Code-Signing Certificates
uses: apple-actions/import-codesign-certs@5142e029c445c10ffc7149d172e540235a065466 # v7.0.0
with:
p12-file-base64: ${{ secrets.APPSTORE_CODE_SIGNING_CERTIFICATE_FILE_BASE64 }}
p12-password: ${{ secrets.APPSTORE_CODE_SIGNING_CERTIFICATE_FILE_PASSWORD }}
- name: Download Apple Provisioning Profiles
uses: Apple-Actions/download-provisioning-profiles@2126b5f86a0e2863cbf4d2d312112b22b943a594 # v6.1.0
with:
bundle-id: ${{ env.APP_BUNDLE_ID }}
issuer-id: ${{ secrets.APPSTORE_API_KEY_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_API_KEY_PRIVATE_KEY }}
- name: Generate CSS/JS files
run: |
dotnet build AdminPanel/src/Client/AdminPanel.Client.Core/AdminPanel.Client.Core.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
dotnet build AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APP_VERSION}}" --no-restore -c Release
- name: Build ipa
run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="AdminPanel" -p:Version="${{ vars.APP_VERSION }}" -p:ApplicationTitle="AdminPanel" -p:ApplicationId="${{ env.APP_BUNDLE_ID }}" -f net10.0-ios
- name: Locate ipa
id: ipa
run: echo "path=$(ls AdminPanel/src/Client/AdminPanel.Client.Maui/bin/release/net10.0-ios/ios-arm64/publish/*.ipa | head -n 1)" >> $GITHUB_OUTPUT
# App Store submission stays manual.
- name: Upload to TestFlight
uses: apple-actions/upload-testflight-build@1fc69ca369a60b813eaaa2d936eda22794b52e17 # v5.4.0
with:
app-path: ${{ steps.ipa.outputs.path }}
app-type: ios
issuer-id: ${{ secrets.APPSTORE_API_KEY_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_API_KEY_PRIVATE_KEY }}
uses-non-exempt-encryption: 'false'