From 5139e2ff9514d8d9031514e5379182a26d4c7629 Mon Sep 17 00:00:00 2001 From: Marcus Aspin Date: Wed, 10 Jan 2024 17:13:38 +0000 Subject: [PATCH] PI-1748 Add build info from environment variables (#2986) --- .../actions/cloud-platform-deploy/action.yml | 18 ++++-- .github/actions/get-build-info/action.yml | 35 ++++++++++++ .github/dependabot.yml | 55 +++++++++++++++++++ build.gradle.kts | 17 +++++- .../digital/hmpps/config/BuildInfoConfig.kt | 29 ++++++++++ .../hmpps/config/BuildInfoConfigTest.kt | 45 +++++++++++++++ 6 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 .github/actions/get-build-info/action.yml create mode 100644 libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfig.kt create mode 100644 libs/commons/src/test/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfigTest.kt diff --git a/.github/actions/cloud-platform-deploy/action.yml b/.github/actions/cloud-platform-deploy/action.yml index 4f50995087..23a8bf8d4d 100644 --- a/.github/actions/cloud-platform-deploy/action.yml +++ b/.github/actions/cloud-platform-deploy/action.yml @@ -33,7 +33,7 @@ inputs: runs: using: composite steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get environment details uses: ./.github/actions/get-env-details @@ -41,6 +41,12 @@ runs: with: environment: ${{ inputs.environment }} + - name: Get build info + uses: ./.github/actions/get-build-info + with: + project: ${{ inputs.project }} + version: ${{ inputs.version }} + - name: Authenticate uses: ./.github/actions/cloud-platform-auth with: @@ -63,9 +69,11 @@ runs: --force \ --install \ --reset-values \ - --set 'generic-service.env.VERSION=${{ inputs.version }}' \ - --set 'generic-service.image.tag=${{ inputs.version }}' \ --set 'version=${{ inputs.version }}' \ + --set 'generic-service.image.tag=${{ inputs.version }}' \ + --set 'generic-service.env.VERSION=${{ inputs.version }}' \ + --set "generic-service.env.BUILD_INFO=$([ -r "$BUILD_INFO" ] && cat "$BUILD_INFO" | base64 -w0 || echo)" \ + --set "generic-service.env.GIT_INFO=$([ -r "$GIT_INFO" ] && cat "$GIT_INFO" | base64 -w0 || echo)" \ --timeout 10m \ --values <(curl "$(gh api '/repos/ministryofjustice/hmpps-ip-allowlists/contents/ip-allowlist-groups.yaml' | jq -r '.download_url')") \ --values templates/helm-defaults.yml \ @@ -73,4 +81,6 @@ runs: --values 'projects/${{ inputs.project }}/deploy/${{ steps.env.outputs.values-file }}' \ --wait env: - GITHUB_TOKEN: ${{ inputs.github_token }} \ No newline at end of file + GITHUB_TOKEN: ${{ inputs.github_token }} + BUILD_INFO: projects/${{ inputs.project }}/build-info.properties + GIT_INFO: projects/${{ inputs.project }}/git.properties \ No newline at end of file diff --git a/.github/actions/get-build-info/action.yml b/.github/actions/get-build-info/action.yml new file mode 100644 index 0000000000..7e56f68b99 --- /dev/null +++ b/.github/actions/get-build-info/action.yml @@ -0,0 +1,35 @@ +name: Get build info +description: Generate build info files for Gradle projects + +inputs: + project: + description: The name of the project + required: true + version: + description: The version of the service to deploy + required: true + +runs: + using: "composite" + steps: + - uses: actions/checkout@v4 + + - name: Check if Gradle project + id: gradle_file + uses: andstor/file-existence-action@20b4d2e596410855db8f9ca21e96fbe18e12930b # v2 + with: + files: projects/${{ inputs.project }}/build.gradle.kts + + - uses: actions/setup-java@v3 + if: ${{ steps.gradle_file.outputs.files_exists == 'true' }} + with: + java-version: '21' + distribution: 'temurin' + + - name: Get build info + uses: gradle/gradle-build-action@v2 + if: ${{ steps.gradle_file.outputs.files_exists == 'true' }} + with: + arguments: '${{ inputs.project }}:buildInfo ${{ inputs.project }}:gitInfo' + env: + ORG_GRADLE_PROJECT_version: ${{ inputs.version }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5eba8d6645..713aea7183 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,61 @@ updates: schedule: interval: "daily" + - package-ecosystem: "github-actions" + directory: "/.github/actions/analyse" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/check-changes" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/cloud-platform-auth" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/cloud-platform-deploy" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/create-signed-pull-request" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/docker-build" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/format-code" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/get-build-info" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/get-env-details" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/merge-changes" + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directory: "/.github/actions/render-project-template" + schedule: + interval: "daily" + - package-ecosystem: "terraform" directory: "/templates" schedule: diff --git a/build.gradle.kts b/build.gradle.kts index cc9ba442f5..bcc38685b3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,7 @@ +import com.gorylenko.GenerateGitPropertiesTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.noarg.gradle.NoArgExtension +import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo import org.springframework.boot.gradle.tasks.bundling.BootJar import org.springframework.boot.gradle.tasks.run.BootRun import uk.gov.justice.digital.hmpps.plugins.ClassPathPlugin @@ -12,6 +14,7 @@ plugins { kotlin("kapt") version "1.9.22" apply false id("org.springframework.boot") version "3.2.1" apply false id("io.spring.dependency-management") version "1.1.4" apply false + id("com.gorylenko.gradle-git-properties") version "2.4.1" apply false id("com.google.cloud.tools.jib") apply false id("base") id("org.sonarqube") @@ -59,15 +62,16 @@ subprojects { apply { plugin("org.springframework.boot") } apply { plugin("io.spring.dependency-management") } apply { plugin("org.jetbrains.kotlin.jvm") } - apply { plugin("org.jetbrains.kotlin.plugin.spring") } + apply { plugin("org.jetbrains.kotlin.kapt") } apply { plugin("org.jetbrains.kotlin.plugin.jpa") } + apply { plugin("org.jetbrains.kotlin.plugin.spring") } apply { plugin("jacoco") } apply { plugin("test-report-aggregation") } apply { plugin("jacoco-report-aggregation") } + apply { plugin("org.sonarqube") } + apply { plugin("com.gorylenko.gradle-git-properties") } apply { plugin(JibConfigPlugin::class.java) } apply { plugin(ClassPathPlugin::class.java) } - apply { plugin("org.sonarqube") } - apply { plugin("org.jetbrains.kotlin.kapt") } tasks { withType { @@ -81,6 +85,13 @@ subprojects { isReproducibleFileOrder = true archiveFileName.set("${archiveBaseName.get()}-${archiveClassifier.get()}.${archiveExtension.get()}") } + named("generateGitProperties") { enabled = false } + register("gitInfo") { + gitProperties.gitPropertiesResourceDir = projectDir + } + register("buildInfo") { + destinationDir = projectDir + } } extensions.configure { diff --git a/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfig.kt b/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfig.kt new file mode 100644 index 0000000000..9ef8415c32 --- /dev/null +++ b/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfig.kt @@ -0,0 +1,29 @@ +package uk.gov.justice.digital.hmpps.config + +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.info.BuildProperties +import org.springframework.boot.info.GitProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import java.util.* + +/** + * Read build and git info from environment variables into the /info endpoint. + * + * This enables us to generate new build info without invalidating caches. + */ +@Configuration +class BuildInfoConfig { + @Bean + fun buildProperties(@Value("\${build.info:#{null}}") info: String?) = + info?.let { BuildProperties(loadFrom(it, "build.")) } + + @Bean + fun gitProperties(@Value("\${git.info:#{null}}") info: String?) = + info?.let { GitProperties(loadFrom(it, "git.")) } + + private fun loadFrom(base64Properties: String, prefix: String) = Properties() + .apply { load(String(Base64.getDecoder().decode(base64Properties)).reader()) } + .mapKeys { it.key.toString().removePrefix(prefix) } + .let { Properties().apply { putAll(it) } } +} \ No newline at end of file diff --git a/libs/commons/src/test/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfigTest.kt b/libs/commons/src/test/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfigTest.kt new file mode 100644 index 0000000000..a4ab2077ed --- /dev/null +++ b/libs/commons/src/test/kotlin/uk/gov/justice/digital/hmpps/config/BuildInfoConfigTest.kt @@ -0,0 +1,45 @@ +package uk.gov.justice.digital.hmpps.config + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.nullValue +import org.junit.jupiter.api.Test +import java.util.* + +class BuildInfoConfigTest { + @Test + fun `converts base64-encoded properties into BuildProperties`() { + val properties = """ + build.artifact=value1 + build.group = value2 + build.name=value3\nvalue3 + #build.version=value4 + + non-build.other=value5 + """.trimIndent() + val encodedProperties = Base64.getEncoder().encodeToString(properties.toByteArray()) + val buildProperties = BuildInfoConfig().buildProperties(encodedProperties) + + checkNotNull(buildProperties) + assertThat(buildProperties.artifact, equalTo("value1")) + assertThat(buildProperties.group, equalTo("value2")) + assertThat(buildProperties.name, equalTo("value3\nvalue3")) + assertThat(buildProperties.version, nullValue()) + } + + @Test + fun `converts base64-encoded properties into GitProperties`() { + val properties = "git.branch=value1" + val encodedProperties = Base64.getEncoder().encodeToString(properties.toByteArray()) + val gitProperties = BuildInfoConfig().gitProperties(encodedProperties) + + checkNotNull(gitProperties) + assertThat(gitProperties.branch, equalTo("value1")) + } + + @Test + fun `handles null`() { + val gitProperties = BuildInfoConfig().gitProperties(null) + assertThat(gitProperties, nullValue()) + } +}