Rewrite Jenkinsfile for Windows appdev agent (bat, az CLI, EF direct update)

This commit is contained in:
2026-05-04 21:29:18 -04:00
parent 2885bc1228
commit e447cdc803
Vendored
+30 -130
View File
@@ -1,154 +1,54 @@
pipeline {
agent any
agent { label 'appdev' }
// No triggers — start this pipeline manually from the Jenkins UI only.
environment {
DOTNET_CLI_HOME = '/tmp/dotnet_cli_home'
WEB_PROJECT = 'src/PowderCoating.Web/PowderCoating.Web.csproj'
INFRA_PROJECT = 'src/PowderCoating.Infrastructure/PowderCoating.Infrastructure.csproj'
PUBLISH_DIR = "${WORKSPACE}/publish"
DEPLOY_ZIP = "${WORKSPACE}/deploy_${BUILD_NUMBER}.zip"
MIGRATION_SQL = "${WORKSPACE}/migration_${BUILD_NUMBER}.sql"
options {
disableConcurrentBuilds()
timestamps()
}
stages {
stage('Checkout') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'refs/heads/master']],
userRemoteConfigs: scm.userRemoteConfigs
])
echo "Building commit: ${GIT_COMMIT}"
checkout scm
}
}
stage('Build & Test') {
stage('Restore & Build') {
steps {
sh 'dotnet restore'
sh 'dotnet build --no-restore -c Release'
sh '''
dotnet test --no-build -c Release \
--logger "trx;LogFileName=results.trx" \
--results-directory TestResults
'''
bat 'dotnet restore PowderCoatingApp.sln'
bat 'dotnet build PowderCoatingApp.sln -c Release --no-restore'
}
post {
always {
junit testResults: 'TestResults/*.trx', allowEmptyResults: true
}
stage('Run Migrations') {
steps {
bat 'dotnet tool install --global dotnet-ef 2>nul || dotnet tool update --global dotnet-ef 2>nul'
withCredentials([string(credentialsId: 'pcl-prod-sql', variable: 'SQL_CONN')]) {
bat '"%USERPROFILE%\\.dotnet\\tools\\dotnet-ef.exe" database update --project src\\PowderCoating.Infrastructure --startup-project src\\PowderCoating.Web --configuration Release --no-build --context ApplicationDbContext --connection "%SQL_CONN%"'
}
}
}
stage('Publish') {
steps {
sh """
dotnet publish '${WEB_PROJECT}' \
-c Release --no-build \
-o '${PUBLISH_DIR}'
"""
bat 'dotnet publish src\\PowderCoating.Web\\PowderCoating.Web.csproj -c Release --no-build -o publish'
}
}
// Generates an idempotent SQL migration script (no live DB connection required).
// The script checks which migrations have already been applied before running each one.
stage('Generate Migration Script') {
stage('Deploy to Azure') {
steps {
sh """
dotnet ef migrations script \
--idempotent \
--output '${MIGRATION_SQL}' \
--project '${INFRA_PROJECT}' \
--startup-project '${WEB_PROJECT}' \
--context ApplicationDbContext \
--no-build
"""
archiveArtifacts artifacts: "migration_${BUILD_NUMBER}.sql", fingerprint: true
echo "Migration script archived — review it in the Jenkins build artifacts before this pipeline runs next time."
}
}
stage('Apply Migration to Azure SQL') {
steps {
withCredentials([
string(credentialsId: 'PCL_SQL_SERVER', variable: 'SQL_SERVER'),
string(credentialsId: 'PCL_SQL_DATABASE', variable: 'SQL_DATABASE'),
string(credentialsId: 'PCL_SQL_USER', variable: 'SQL_USER'),
string(credentialsId: 'PCL_SQL_PASSWORD', variable: 'SQL_PASSWORD')
]) {
sh '''
echo "Applying migration to ${SQL_SERVER}/${SQL_DATABASE} ..."
/opt/mssql-tools18/bin/sqlcmd \
-S "${SQL_SERVER}" \
-d "${SQL_DATABASE}" \
-U "${SQL_USER}" \
-P "${SQL_PASSWORD}" \
-C \
-b \
-i "${MIGRATION_SQL}"
echo "Migration applied successfully."
'''
}
}
}
stage('Deploy to Azure App Service') {
steps {
withCredentials([
string(credentialsId: 'PCL_AZURE_CLIENT_ID', variable: 'AZ_CLIENT_ID'),
string(credentialsId: 'PCL_AZURE_CLIENT_SECRET', variable: 'AZ_CLIENT_SECRET'),
string(credentialsId: 'PCL_AZURE_TENANT_ID', variable: 'AZ_TENANT_ID'),
string(credentialsId: 'PCL_AZURE_SUBSCRIPTION_ID', variable: 'AZ_SUBSCRIPTION_ID'),
string(credentialsId: 'PCL_AZURE_RESOURCE_GROUP', variable: 'AZ_RG'),
string(credentialsId: 'PCL_AZURE_APP_NAME', variable: 'AZ_APP')
]) {
sh '''
az login --service-principal \
--username "$AZ_CLIENT_ID" \
--password "$AZ_CLIENT_SECRET" \
--tenant "$AZ_TENANT_ID" \
--output none
az account set --subscription "$AZ_SUBSCRIPTION_ID"
echo "Packaging deployment artifact ..."
cd "$PUBLISH_DIR"
zip -r "$DEPLOY_ZIP" .
echo "Pushing ZIP to ${AZ_APP} ..."
az webapp deployment source config-zip \
--resource-group "$AZ_RG" \
--name "$AZ_APP" \
--src "$DEPLOY_ZIP"
az logout
echo "Deploy complete."
'''
}
}
}
stage('Smoke Test') {
steps {
withCredentials([
string(credentialsId: 'PCL_AZURE_APP_NAME', variable: 'AZ_APP')
]) {
sh '''
APP_URL="https://${AZ_APP}.azurewebsites.net"
echo "Smoke-testing ${APP_URL} ..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time 45 --retry 3 --retry-delay 10 \
"${APP_URL}")
echo "HTTP status: ${HTTP_STATUS}"
# 200 = OK, 302 = redirect to login (both are healthy)
if [ "$HTTP_STATUS" != "200" ] && [ "$HTTP_STATUS" != "302" ]; then
echo "SMOKE TEST FAILED — got HTTP ${HTTP_STATUS}"
exit 1
fi
echo "Smoke test passed."
'''
bat 'powershell -Command "Compress-Archive -Path publish\\* -DestinationPath deploy.zip -Force"'
withCredentials([azureServicePrincipal(
credentialsId: 'azure-pcl',
subscriptionIdVariable: 'AZ_SUB_ID',
clientIdVariable: 'AZ_CLIENT_ID',
clientSecretVariable: 'AZ_CLIENT_SECRET',
tenantIdVariable: 'AZ_TENANT_ID'
)]) {
bat 'az login --service-principal -u "%AZ_CLIENT_ID%" -p "%AZ_CLIENT_SECRET%" --tenant "%AZ_TENANT_ID%" --output none'
bat 'az account set --subscription "%AZ_SUB_ID%"'
bat 'az webapp deploy --resource-group rg-powdercoatinglogix-prod --name linuxpcl --src-path deploy.zip --type zip'
bat 'az logout'
}
}
}
@@ -156,7 +56,7 @@ pipeline {
post {
success {
echo "Production deployment #${BUILD_NUMBER} (${GIT_COMMIT}) completed successfully."
echo "Production deployment #${BUILD_NUMBER} completed successfully."
}
failure {
echo "Pipeline #${BUILD_NUMBER} FAILED — review the stage logs above."