diff --git a/.github/workflows/helm-test.yml b/.github/workflows/helm-test.yml index 96b099f..0696c34 100644 --- a/.github/workflows/helm-test.yml +++ b/.github/workflows/helm-test.yml @@ -63,3 +63,40 @@ jobs: --set image.tag=test --set serviceMonitor.enabled=true) echo "$rendered" | grep -q 'kind: ServiceMonitor' echo "$rendered" | kubeconform -strict -summary -skip ServiceMonitor -kubernetes-version "$KUBERNETES_VERSION" + + umbrella-test: + name: Umbrella Lint & Validate + runs-on: ubuntu-latest + + permissions: + contents: read + + env: + KUBECONFORM_VERSION: v0.6.7 + KUBERNETES_VERSION: "1.31.0" + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Helm + uses: azure/setup-helm@v5 + with: + version: "v3.16.4" + + - name: Build chart dependencies + run: helm dependency build deploy/helm/fincore-engine + + - name: Helm lint + run: helm lint deploy/helm/fincore-engine + + - name: Install kubeconform + run: | + curl -sSL "https://github.com/yannh/kubeconform/releases/download/${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz" \ + | tar -xz kubeconform + sudo mv kubeconform /usr/local/bin/ + + - name: Helm template and validate manifests + run: | + helm template fincore deploy/helm/fincore-engine \ + | kubeconform -strict -summary -skip ServiceMonitor -kubernetes-version "$KUBERNETES_VERSION" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1187a8f..4296586 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,20 +10,12 @@ concurrency: cancel-in-progress: false permissions: - contents: write - packages: write - id-token: write + contents: read jobs: - release: - name: Build, Push & Release + test: + name: Test gate runs-on: ubuntu-latest - - permissions: - contents: write - packages: write - id-token: write - steps: - name: Checkout uses: actions/checkout@v7 @@ -42,16 +34,46 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Run tests + run: ./gradlew test integrationTest --no-daemon + + images: + name: Build & sign ${{ matrix.image }} + needs: test + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + id-token: write + + strategy: + fail-fast: false + matrix: + include: + - { image: fincore-ledger, context: ".", file: Dockerfile, build_args: "SERVICE=ledger" } + - { image: fincore-payments, context: ".", file: Dockerfile, build_args: "SERVICE=payments" } + - { image: fincore-compliance, context: ".", file: Dockerfile, build_args: "SERVICE=compliance" } + - { image: fincore-decision, context: ".", file: Dockerfile, build_args: "SERVICE=decision" } + - { image: fincore-web, context: "web", file: web/Dockerfile, build_args: "" } + + steps: + - name: Checkout + uses: actions/checkout@v7 + - name: Extract version from tag id: version run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" - - name: Run tests - run: ./gradlew test integrationTest --no-daemon - - name: Install cosign uses: sigstore/cosign-installer@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Log in to GitHub Container Registry uses: docker/login-action@v4 with: @@ -59,67 +81,116 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 - - - name: Build and push ledger-service Docker image - id: docker-build-ledger + - name: Build and push image + id: build uses: docker/build-push-action@v7 with: - context: services/ledger - file: services/ledger/Dockerfile + context: ${{ matrix.context }} + file: ${{ matrix.file }} + build-args: ${{ matrix.build_args }} push: true platforms: linux/amd64,linux/arm64 tags: | - ghcr.io/tiana-code/fincore-ledger:${{ steps.version.outputs.version }} - ghcr.io/tiana-code/fincore-ledger:latest + ghcr.io/tiana-code/${{ matrix.image }}:${{ steps.version.outputs.version }} + ghcr.io/tiana-code/${{ matrix.image }}:latest labels: | org.opencontainers.image.source=https://github.com/tiana-code/fincore-engine org.opencontainers.image.version=${{ steps.version.outputs.version }} org.opencontainers.image.licenses=BUSL-1.1 - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=gha,scope=${{ matrix.image }} + cache-to: type=gha,mode=max,scope=${{ matrix.image }} - - name: Sign Docker image (ledger-service) with cosign + - name: Sign image with cosign (keyless) run: | cosign sign --yes \ - ghcr.io/tiana-code/fincore-ledger@${{ steps.docker-build-ledger.outputs.digest }} + ghcr.io/tiana-code/${{ matrix.image }}@${{ steps.build.outputs.digest }} + + helm: + name: Package & sign Helm umbrella chart + needs: test + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Extract version from tag + id: version + run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" - name: Set up Helm uses: azure/setup-helm@v5 with: version: "v3.16.4" - - name: Package Helm chart + - name: Install cosign + uses: sigstore/cosign-installer@v3 + + - name: Log in to GHCR for Helm OCI + run: | + echo "${{ secrets.GITHUB_TOKEN }}" \ + | helm registry login ghcr.io --username "${{ github.actor }}" --password-stdin + + - name: Build chart dependencies + run: helm dependency build deploy/helm/fincore-engine + + - name: Package Helm umbrella chart run: | helm package deploy/helm/fincore-engine \ - --version ${{ steps.version.outputs.version }} \ - --app-version ${{ steps.version.outputs.version }} \ + --version "${{ steps.version.outputs.version }}" \ + --app-version "${{ steps.version.outputs.version }}" \ --destination /tmp/helm-packages - name: Push Helm chart to GHCR OCI registry run: | - helm push /tmp/helm-packages/fincore-engine-${{ steps.version.outputs.version }}.tgz \ + helm push "/tmp/helm-packages/fincore-engine-${{ steps.version.outputs.version }}.tgz" \ oci://ghcr.io/tiana-code/charts - - name: Sign Helm chart with cosign + - name: Sign Helm chart with cosign (keyless) run: | cosign sign --yes \ ghcr.io/tiana-code/charts/fincore-engine:${{ steps.version.outputs.version }} - - name: Generate SBOM (CycloneDX) - run: ./gradlew cyclonedxBom --no-daemon + release: + name: SBOM & GitHub release + needs: [images, helm] + runs-on: ubuntu-latest + + permissions: + contents: write - - name: Create GitHub Release - uses: googleapis/release-please-action@v5 + steps: + - name: Checkout + uses: actions/checkout@v7 with: - token: ${{ secrets.GITHUB_TOKEN }} - release-type: simple - config-file: .github/release-please-config.json + fetch-depth: 0 + + - name: Set up JDK 21 (Temurin) + uses: actions/setup-java@v5 + with: + java-version: "21" + distribution: temurin + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v6 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Generate SBOM (CycloneDX aggregate) + run: ./gradlew cyclonedxBom --no-daemon - - name: Upload SBOM to release + - name: Create GitHub release uses: softprops/action-gh-release@v3 with: - files: "**/build/reports/bom.json" + generate_release_notes: true + files: | + build/reports/cyclonedx/bom.json + build/reports/cyclonedx/bom.xml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/build.gradle.kts b/build.gradle.kts index d55d320..719cd58 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.detekt) alias(libs.plugins.spotless) + alias(libs.plugins.cyclonedx) } group = "com.fincore" diff --git a/deploy/helm/fincore-engine/.helmignore b/deploy/helm/fincore-engine/.helmignore new file mode 100644 index 0000000..ca646ab --- /dev/null +++ b/deploy/helm/fincore-engine/.helmignore @@ -0,0 +1,9 @@ +# Patterns to ignore when building packages. +.DS_Store +.git/ +.gitignore +*.tmproj +*.bak +*.orig +.idea/ +*.swp diff --git a/deploy/helm/fincore-engine/Chart.yaml b/deploy/helm/fincore-engine/Chart.yaml new file mode 100644 index 0000000..a8327b7 --- /dev/null +++ b/deploy/helm/fincore-engine/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: fincore-engine +description: FinCore Engine umbrella chart, deploys the ledger, payments and sandbox web services as one stack +type: application +version: 0.1.0 +appVersion: "0.1.0" +home: https://github.com/tiana-code/fincore-engine +sources: + - https://github.com/tiana-code/fincore-engine +maintainers: + - name: FinCore Engine Authors +dependencies: + - name: ledger + version: 0.1.0 + repository: file://../ledger + condition: ledger.enabled + - name: payments + version: 0.1.0 + repository: file://../payments + condition: payments.enabled + - name: web + version: 0.1.0 + repository: file://../web + condition: web.enabled diff --git a/deploy/helm/fincore-engine/templates/NOTES.txt b/deploy/helm/fincore-engine/templates/NOTES.txt new file mode 100644 index 0000000..95579eb --- /dev/null +++ b/deploy/helm/fincore-engine/templates/NOTES.txt @@ -0,0 +1,19 @@ +FinCore Engine has been deployed. + +Enabled services: +{{- if .Values.ledger.enabled }} + - ledger +{{- end }} +{{- if .Values.payments.enabled }} + - payments +{{- end }} +{{- if .Values.web.enabled }} + - web (sandbox dashboard) +{{- end }} + +Check rollout status: + kubectl get pods -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Each service is its own sub-chart. Tune a service by overriding under its key, +for example: + helm upgrade {{ .Release.Name }} . --set ledger.replicaCount=3 diff --git a/deploy/helm/fincore-engine/values.yaml b/deploy/helm/fincore-engine/values.yaml new file mode 100644 index 0000000..bd1aeb7 --- /dev/null +++ b/deploy/helm/fincore-engine/values.yaml @@ -0,0 +1,13 @@ +# Umbrella values. Each block below is forwarded to the matching sub-chart. +# Toggle a service off to deploy a subset of the stack. Per-service tuning +# (resources, image tag, secrets) lives in the sub-chart values and can be +# overridden here under the same key. + +ledger: + enabled: true + +payments: + enabled: true + +web: + enabled: true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d6dc696..f168376 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,7 @@ cloudevents = "4.1.1" ksp = "2.0.21-1.0.28" spring-dependency-management = "1.1.7" spotless = "7.0.4" +cyclonedx = "3.2.4" [libraries] # Kotlin @@ -96,3 +97,4 @@ spring-dependency-management = { id = "io.spring.dependency-management", version ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } +cyclonedx = { id = "org.cyclonedx.bom", version.ref = "cyclonedx" }