Jenkinsfile for Oracle deployments

This example CI/CD pipeline uses the installed Flyway, SQL Compare, and Code Analysis for Oracle command lines on a Windows agent. This is implemented using a Pipeline as Code Jenkinsfile.  If you are familiar with Jenkinsfile, we hope you can drop this into your repository and customize the variables at the top to start a proof of concept quickly in your environment.  

This example produces the following pipeline.  More or less environments can be added for your proof of concept.  We are using the open-source Blue Ocean extension from Jenkins to provide this visual view of the pipeline. 

Jenkinsfile Toggle source code

  1. node {
  2. def OutputDirectory = "C:/Program Files (x86)/Jenkins/jobs/Oracle DevOps (migrations)"
  3. def CiDatabaseJdbc = "jdbc:oracle:thin:@//localhost:1521/ci"
  4. def AcceptanceDatabaseJdbc = "jdbc:oracle:thin:@//localhost:1521/acceptance"
  5. def ProductionDatabaseJdbc = "jdbc:oracle:thin:@//localhost:1521/production"
  6. def Schema = "HR"
  7. def User = "HR"
  8. def Password = "Redgate1"
  9. def SQLCOMPARE='"C:\\Program Files\\Red Gate\\Schema Compare for Oracle 5\\sco.exe"'
  10. def SQLDATACOMPARE='"C:\\Program Files\\Red Gate\\Data Compare for Oracle 5\\dco.exe"'
  11. def CODEANALYSIS='"C:\\Program Files\\Red Gate\\Code Analysis for Oracle\\cao.cmd"'
  12. stage ('Prep') {
  13. echo "Ensure the pipeline is configured to clean the Jenkins workspace before checkout"
  14. //bat('set')
  15. deleteDir()
  16. checkout scm
  17. }
  18. stage ('Build') {
  19. echo "DB Build"
  20. def status
  21. // Clean the CI DB to get a fresh build
  22. status = bat returnStatus: true, label: "Clean", script:" FLYWAY clean info -url=${CiDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  23. echo "Status of Running CI build: $status"
  24. if (status != 0) { error('Running CI build failed') }
  25. // Generate the dry run script for later review
  26. status = bat returnStatus: true, label: "DryRun", script:" FLYWAY migrate info -dryRunOutput=\"${OutputDirectory}/workspace/Build.sql\" -url=${CiDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  27. echo "Status of Running CI build: $status"
  28. if (status != 0) { error('Running CI build failed') }
  29. archiveArtifacts allowEmptyArchive: true, artifacts: 'Build.sql'
  30. // Migrate the CI db
  31. status = bat returnStatus: true, label: "Build", script:" FLYWAY migrate info -url=${CiDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  32. echo "Status of Running CI build: $status"
  33. if (status != 0) { error('Running CI build failed') }
  34. // Execute the code analysis to check for issues
  35. status = bat returnStatus: true, label: "Code Analysis", script:"${CODEANALYSIS} /source:migrations /outfile:CodeAnalysisResults.html /IAgreeToTheEula"
  36. archiveArtifacts allowEmptyArchive: true, artifacts:'CodeAnalysisResults.html'
  37. echo "Status of Running CI build: $status"
  38. if (status != 0) {
  39. if (status == 1) { error('Redgate Code Analysis for Oracle failed to complete due to a fatal error') }
  40. else if (status == 2) {
  41. status = 1
  42. echo "Redgate Code Analysis for Oracle only partially completed - often this is because one or more SQL files could not be parsed (such files are currently excluded from the generated reports)"
  43. }
  44. else if (status == 3) { error('Redgate Code Analysis for Oracle ran successfully but reported one or more errors in the analyzed SQL files') }
  45. else if (status == 4) {
  46. status = 1
  47. echo "Redgate Code Analysis for Oracle ran successfully but reported one or more warnings in the analyzed SQL files"
  48. }
  49. else { error('Code Analysis failed') }
  50. }
  51. // Take a snapshot of the CI db after deploying to it to use in change reports for downstream environments
  52. // Comparing the target environment to this snapshot will show an object level report of the expected changes
  53. status = bat returnStatus: true, label: "Snapshot", script:"${SQLCOMPARE} /i:sdwgvac /b:hdrg /forceservicename /source:${User}/${Password}@localhost/CI{${Schema}} /snp:BuildStatePostDeploy.onp"
  54. if (status != 0) { error('Failed to create a snapshot of the CI database after deploying to it') }
  55. archiveArtifacts allowEmptyArchive: true, artifacts:'BuildStatePostDeploy.onp'
  56. }
  57. stage ('Unit Tests') {
  58. echo "Running utPLSQL database Unit Tests"
  59. /*
  60. // status = bat returnStatus: true, label: "Unit Tests", script: "exec ut.Run();"
  61. echo "Status of utPLSQL database Unit Tests: $status"
  62. if (status != 0) { error('ERROR: Running Unit Tests failed') }
  63. status = junit allowEmptyResults: true, testResults: 'testResults.xml'
  64. echo "Failed JUnit tests: $status.failCount"
  65. archiveArtifacts allowEmptyArchive: true, artifacts: 'testResults.xml'
  66. zip zipFile: 'codeCoverage.zip', archive: true, glob: 'codeCoverage.html, codeCoverage.html_assets/*'
  67. archiveArtifacts allowEmptyArchive: true, artifacts: 'codeCoverage.zip', fingerprint: true
  68. */
  69. }
  70. stage ('Deploy to Acceptance') {
  71. echo "Deploying project to Acceptance Database"
  72. status = bat returnStatus: true, label: "Acceptance Deployment report", script:"${SQLCOMPARE} /i:sdwgvac /b:hdrg /forceservicename /source:BuildStatePostDeploy.onp{${Schema}} /target:${User}/${Password}@localhost/Acceptance{${Schema}} /report:Acceptance_schema_changes.html /reportType=interactive"
  73. archiveArtifacts allowEmptyArchive: true, artifacts: 'Acceptance_schema_changes.html'
  74. status = bat returnStatus: true, label: "Acceptance DryRun", script:" FLYWAY migrate -dryRunOutput=\"${OutputDirectory}/workspace/Acceptance.sql\" -url=${AcceptanceDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  75. echo "Generating the dryrun script for Acceptance: $status"
  76. if (status != 0) { error('Generating Dryrun script for Acceptance failed') }
  77. status = bat returnStatus: true, label: "Acceptance Deployment", script:" FLYWAY migrate info -url=${AcceptanceDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  78. echo "deploying to Acceptance: $status"
  79. if (status != 0) { error('Deployment to Acceptance failed') }
  80. archiveArtifacts allowEmptyArchive: true, artifacts: 'Acceptance.sql'
  81. archiveArtifacts allowEmptyArchive: true, artifacts: '**/Acceptance_schema_changes_images/**'
  82. }
  83. stage ('Approval Gate'){
  84. def message = "Approve release to Production?"
  85. // wrapping in a time out so it does not block the agent and simply fails the build if there is no user intervention.
  86. timeout(time: 30, unit: 'MINUTES') {
  87. def userInput = input(
  88. id: 'userInput',
  89. message: "$message",
  90. parameters: [
  91. [$class: 'TextParameterDefinition', defaultValue: 'I Approve The Deployment', description: 'To Proceed, type I Approve The Deployment', name: 'Review deployment artifacts before proceeding']
  92. ]
  93. )
  94. if (userInput.indexOf('I Approve The Deployment') == -1) {
  95. currentBuild.result = 'ABORTED'
  96. error('Deployment aborted')
  97. }
  98. }
  99. }
  100. stage ('Deploy to Production') {
  101. echo "Deploying to Production Database"
  102. // Create a pre-deployment snapshot of Production
  103. status = bat returnStatus: true, label: "Production snapshot", script:"${SQLCOMPARE} /i:sdwgvac /b:hdrg /forceservicename /source:${User}/${Password}@localhost/Production{${Schema}} /snp:ProductionStatePreDeploy.onp"
  104. if (status != 0) { error('Generating Production snapshot failed') }
  105. archiveArtifacts allowEmptyArchive: true, artifacts: 'ProductionStatePreDeploy.onp'
  106. // Checking for Production drift by using the Build snapshot from the previous build
  107. // more logic needed to get latest successful build or possibly skip with warning/input from user if not found
  108. Integer previousBuildNumber = 0
  109. previousBuildNumber = "$BUILD_NUMBER" as Integer
  110. previousBuildNumber = previousBuildNumber - 1
  111. status = bat returnStatus: true, label: "Production drift check", script:"${SQLCOMPARE} /i:sdwgvac /b:hdrg /forceservicename /source:../builds/$previousBuildNumber/archive/BuildStatePostDeploy.onp{${Schema}} /target:ProductionStatePreDeploy.onp{${Schema}} /report=ProductionDriftReport.html /reportType=interactive"
  112. echo "Checking for Production Drift using /builds/$previousBuildNumber/archive/BuildStatePostDeploy.onp: $status"
  113. if (status == 61) {
  114. // Stop the build because Production has drifted or get ok from user to continue
  115. archiveArtifacts allowEmptyArchive: true, artifacts: 'ProductionDriftReport.html'
  116. archiveArtifacts allowEmptyArchive: true, artifacts: '**/ProductionDriftReport_images/**'
  117. error('Production has drifted since the last deployment')
  118. }
  119. // Create a change report for Production
  120. status = bat returnStatus: true, label: "Production Deployment report", script:"${SQLCOMPARE} /i:sdwgvac /b:hdrg /forceservicename /source:BuildStatePostDeploy.onp{${Schema}} /target:ProductionStatePreDeploy.onp{${Schema}} /report:Production_schema_changes.html /reportType=interactive"
  121. echo "Generating the change report for Production: $status"
  122. if (status != 0 && status != 61) { error('ERROR: Generating Production change report failed') } // 0 = no diffs found; 61 = diffs found
  123. archiveArtifacts allowEmptyArchive: true, artifacts: 'Production_schema_changes.html'
  124. archiveArtifacts allowEmptyArchive: true, artifacts: '**/Production_schema_changes_images/**'
  125. // Create the dry run script
  126. status = bat returnStatus: true, label: "Production DryRun", script:" FLYWAY migrate -dryRunOutput=\"${OutputDirectory}/workspace/Production.sql\" -url=${ProductionDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  127. echo "Generating the dryrun script for Production: $status"
  128. if (status != 0) { error('Generating Dryrun script for Production failed') }
  129. archiveArtifacts allowEmptyArchive: true, artifacts: 'Production.sql'
  130. // Deploy to Production
  131. status = bat returnStatus: true, label: "Production Deployment", script:" FLYWAY migrate info -url=${ProductionDatabaseJdbc} -user=${User} -password=${Password} -schemas=${Schema}"
  132. echo "deploying to Production: $status"
  133. if (status != 0) { error('Deployment to Production failed') }
  134. // Generate a rollback script for Production
  135. status = bat returnStatus: true, label: "Production rollback script", script:"${SQLCOMPARE} /i:sdwgvac /b:hdrg /forceservicename /source:ProductionStatePreDeploy.onp{${Schema}} /target:BuildStatePostDeploy.onp{${Schema}} /scriptfile:Production_rollback_script.sql"
  136. echo "Generating the rollback script for Production: $status"
  137. //if (status != 0 && status != 61) { error('ERROR: Generating Production rollback script report failed') }
  138. archiveArtifacts allowEmptyArchive: true, artifacts: 'Production_rollback_script.sql'
  139. }
  140. }

Didn't find what you were looking for?