diff --git a/.github/actions/analysis/security/action.yml b/.github/actions/analysis/security/action.yml index a208aff3..a74dc073 100644 --- a/.github/actions/analysis/security/action.yml +++ b/.github/actions/analysis/security/action.yml @@ -66,5 +66,5 @@ runs: uses: github/codeql-action/upload-sarif@v3.25.6 with: sarif_file: build/reports/detekt/detekt.sarif - checkout_path: ${{ github.workspace }} token: ${{ inputs.token }} + category: static-code-analysis diff --git a/.github/actions/docker/action.yml b/.github/actions/docker/action.yml deleted file mode 100644 index aea20096..00000000 --- a/.github/actions/docker/action.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: Package and Publish ๐Ÿ“ฆ -description: | - This workflow is responsible for packaging and publishing the application - to the container registry. It also performs vulnerability scanning. - -inputs: - deliver: - description: 'Deliver application to production' - required: true - default: 'true' - registry: - description: 'The container registry to push the image to' - required: true - default: 'ghcr.io' - docker_username: - description: 'The username for Docker Hub' - required: true - docker_password: - description: 'The password for Docker Hub' - required: true - image_name: - description: 'The name of the image to be published' - required: true - version: - description: 'The version of the application' - required: true - ci_github_token: - description: 'GITHUB_TOKEN with permissions to push to the container registry' - required: true - gradle-encryption-key: - description: 'The encryption key to use for the gradle cache' - required: true - docker_hub_image: - description: 'Docker hub image name' - required: false - default: 'n4t5u/lyra' - non_prod_tag: - description: 'Non production docker image tag' - required: false - default: 'nightly' - -runs: - using: composite - steps: - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ inputs.gradle-encryption-key }} - - - name: Execute Gradle build - run: | - chmod +x gradlew - ./gradlew assemble - ./gradlew bootBuildImage --imageName base/${{ inputs.image_name }}:latest - shell: bash - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Authenticate to Docker Hub and GHCR - run: | - echo ${{ inputs.ci_github_token }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin - echo ${{ inputs.docker_password }} | docker login docker.io -u ${{ inputs.docker_username }} --password-stdin - shell: bash - - - name: Build and Push Docker images (multi-platform) - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: | - ghcr.io/${{ inputs.image_name }}:${{ inputs.version }}, - ghcr.io/${{ inputs.image_name }}:latest, - docker.io/${{ inputs.docker_hub_image }}:${{ inputs.version }}, - docker.io/${{ inputs.docker_hub_image }}:latest - cache-from: type=registry,ref=ghcr.io/${{ inputs.image_name }}:cache - cache-to: type=inline - - - name: Set up Trivy cache directory - run: | - mkdir -p ~/.cache/trivy - shell: bash - - - name: Cache Trivy DB - uses: actions/cache@v4 - with: - path: ~/.cache/trivy - key: ${{ runner.os }}-trivy-db - restore-keys: | - ${{ runner.os }}-trivy-db - - - name: Publish to GHCR ๐Ÿ“ฆ - uses: ./.github/actions/docker/ghcr - with: - deliver: ${{ inputs.deliver }} - image_name: ${{ inputs.image_name }} - version: ${{ inputs.version }} - ci_github_token: ${{ inputs.ci_github_token }} - non_prod_tag: ${{ inputs.non_prod_tag }} - - - name: Publish to Docker Hub ๐Ÿ“ฆ - uses: ./.github/actions/docker/dockerhub - with: - deliver: ${{ inputs.deliver }} - docker_username: ${{ inputs.docker_username }} - docker_password: ${{ inputs.docker_password }} - image_name: ${{ inputs.image_name }} - version: ${{ inputs.version }} - docker_hub_image: ${{ inputs.docker_hub_image }} - non_prod_tag: ${{ inputs.non_prod_tag }} diff --git a/.github/actions/docker/backend/action.yml b/.github/actions/docker/backend/action.yml new file mode 100644 index 00000000..6191d028 --- /dev/null +++ b/.github/actions/docker/backend/action.yml @@ -0,0 +1,80 @@ +name: Package and Publish Backend ๐Ÿ“ฆ +description: | + This workflow is responsible for packaging and publishing the backend application + to the container registry. It also performs vulnerability scanning. + +inputs: + deliver: + description: 'Deliver backend to production' + required: true + default: 'true' + docker_username: + description: 'The username for Docker Hub' + required: true + docker_password: + description: 'The password for Docker Hub' + required: true + version: + description: 'The version of the backend' + required: true + ci_github_token: + description: 'GITHUB_TOKEN with permissions to push to the container registry' + required: true + gradle-encryption-key: + description: 'The encryption key to use for the gradle cache' + required: true + +runs: + using: composite + steps: + - name: Install Java Tools & Dependencies + uses: ./.github/actions/install/java + with: + java-version: 21 + gradle-encription-key: ${{ inputs.gradle-encryption-key }} + + - name: Cache Gradle Dependencies + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Execute Gradle build + run: | + chmod +x gradlew + ./gradlew assemble + ./gradlew bootBuildImage -x test + shell: bash + + - name: ๐Ÿช„ Scan Docker images for vulnerabilities + uses: aquasecurity/trivy-action@0.20.0 + with: + image-ref: ghcr.io/dallay/lyra:latest + format: sarif + output: trivy-lyra-report.sarif + severity: HIGH,CRITICAL + ignore-unfixed: true + cache-dir: /tmp/trivy-cache-lyra + + - name: โ‡ช Upload Trivy Scan Report + uses: actions/upload-artifact@v3 + with: + name: trivy-lyra-report + path: trivy-lyra-report.sarif + + - name: ๐Ÿณ Authenticate to Docker Hub and GHCR + run: | + echo ${{ inputs.ci_github_token }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin + echo ${{ inputs.docker_password }} | docker login docker.io -u ${{ inputs.docker_username }} --password-stdin + shell: bash + + - name: ๐Ÿณ Push Docker image to GHCR and Docker Hub + if: ${{ inputs.deliver }} + run: | + docker push --all-tags ${{ inputs.docker_username }}/lyra + docker push --all-tags ghcr.io/dallay/lyra + shell: bash diff --git a/.github/actions/docker/dockerhub/action.yml b/.github/actions/docker/dockerhub/action.yml deleted file mode 100644 index bc2ec951..00000000 --- a/.github/actions/docker/dockerhub/action.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Publish to Docker Hub ๐Ÿ“ฆ -description: | - This workflow packages and publishes the application to Docker Hub. - -inputs: - deliver: - description: 'Deliver application to production' - required: true - default: true - docker_username: - description: 'The username for Docker Hub' - required: true - docker_password: - description: 'The password for Docker Hub' - required: true - image_name: - description: 'The name of the image to be published' - required: true - version: - description: 'The version of the application' - required: true - default: ${{ github.sha }} - docker_hub_image: - description: 'Docker hub image name' - required: false - default: 'n4t5u/lyra' - non_prod_tag: - description: 'Non production docker image tag' - required: false - default: 'nightly' - -runs: - using: composite - steps: - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ inputs.docker_username }} - password: ${{ inputs.docker_password }} - - - name: Push container image to Docker Hub ${{ inputs.version }} - if: ${{ inputs.deliver }} - run: | - docker tag base/${{ inputs.image_name }}:latest docker.io/${{ inputs.docker_hub_image }}:${{ inputs.version }} - docker tag base/${{ inputs.image_name }}:latest docker.io/${{ inputs.docker_hub_image }}:latest - docker tag lyra-app:latest docker.io/${{ inputs.docker_hub_image }}-app:${{ inputs.version }} - docker tag lyra-app:latest docker.io/${{ inputs.docker_hub_image }}-app:latest - docker tag lyra-landing-page:latest docker.io/${{ inputs.docker_hub_image }}-landing-page:${{ inputs.version }} - docker tag lyra-landing-page:latest docker.io/${{ inputs.docker_hub_image }}-landing-page:latest - docker push --all-tags docker.io/${{ inputs.docker_hub_image }} - docker push --all-tags docker.io/${{ inputs.docker_hub_image }}-app - docker push --all-tags docker.io/${{ inputs.docker_hub_image }}-landing-page - shell: bash - - - name: Push non-production container image to Docker Hub ${{ inputs.non_prod_tag }} - if: ${{ !inputs.deliver }} - run: | - docker tag base/${{ inputs.image_name }}:latest \ - docker.io/${{ inputs.docker_hub_image }}:${{ inputs.non_prod_tag }} - docker tag lyra-app:latest \ - docker.io/${{ inputs.image_name }}-app:${{ inputs.non_prod_tag }} - docker tag lyra-landing-page:latest \ - docker.io/${{ inputs.image_name }}-landing-page:${{ inputs.non_prod_tag }} - docker push --all-tags docker.io/${{ inputs.docker_hub_image }} - docker push --all-tags docker.io/${{ inputs.image_name }}-app - docker push --all-tags docker.io/${{ inputs.image_name }}-landing-page - shell: bash diff --git a/.github/actions/docker/frontend/action.yml b/.github/actions/docker/frontend/action.yml new file mode 100644 index 00000000..3a4b7432 --- /dev/null +++ b/.github/actions/docker/frontend/action.yml @@ -0,0 +1,77 @@ +name: Package and Publish Frontend ๐Ÿ“ฆ +description: | + This composite action packages and publishes the frontend application + (lyra-app and lyra-landing-page) to the container registry. It also performs vulnerability scanning. + +inputs: + deliver: + description: 'Deliver frontend to production' + required: true + default: 'true' + docker_username: + description: 'The username for Docker Hub' + required: true + docker_password: + description: 'The password for Docker Hub' + required: true + version: + description: 'The version of the frontend' + required: true + ci_github_token: + description: 'GITHUB_TOKEN with permissions to push to the container registry' + required: true + target: + description: 'The target image to build (lyra-app, lyra-landing-page)' + required: true + +runs: + using: composite + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Authenticate to Docker Hub and GHCR + run: | + echo ${{ inputs.ci_github_token }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin + echo ${{ inputs.docker_password }} | docker login docker.io -u ${{ inputs.docker_username }} --password-stdin + shell: bash + + - name: Cache Docker layers + id: cache + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Build and Push Docker images + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + target: ${{ inputs.target }} + push: ${{ inputs.deliver }} + tags: | + ghcr.io/dallay/${{ inputs.target }}:${{ inputs.version }}, + ghcr.io/dallay/${{ inputs.target }}:latest, + docker.io/${{ inputs.target }}:${{ inputs.version }}, + docker.io/${{ inputs.target }}:latest + cache-from: type=registry,ref=ghcr.io/dallay/${{ inputs.target }}:cache + cache-to: type=registry,ref=ghcr.io/dallay/${{ inputs.target }}:cache,mode=max + + - name: ๐Ÿช„ Scan Docker images for vulnerabilities + uses: aquasecurity/trivy-action@0.20.0 + with: + image-ref: ghcr.io/dallay/${{ inputs.target }}:latest + format: sarif + output: trivy-${{ inputs.target }}-report.sarif + severity: HIGH,CRITICAL + ignore-unfixed: true + cache-dir: /tmp/trivy-cache-${{ inputs.target }} + + - name: โ‡ช Upload Trivy Scan Report + uses: actions/upload-artifact@v3 + with: + name: trivy-${{ inputs.target }}-report + path: trivy-${{ inputs.target }}-report.sarif diff --git a/.github/actions/docker/ghcr/action.yml b/.github/actions/docker/ghcr/action.yml deleted file mode 100644 index 86762522..00000000 --- a/.github/actions/docker/ghcr/action.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Publish to GHCR ๐Ÿ“ฆ -description: | - This workflow packages and publishes the application to GHCR and performs vulnerability scanning. - -inputs: - deliver: - description: 'Deliver application to production' - required: true - default: true - image_name: - description: 'The name of the image to be published' - required: true - version: - description: 'The version of the application' - required: true - default: ${{ github.sha }} - ci_github_token: - description: 'GITHUB_TOKEN with permissions to push to the container registry' - required: true - non_prod_tag: - description: 'Non production docker image tag' - required: false - default: 'nightly' - -runs: - using: composite - steps: - # Trivy cache setup - - name: Set up Trivy cache directory - run: | - mkdir -p ~/.cache/trivy - shell: bash - - - name: Cache Trivy DB - uses: actions/cache@v4 - with: - path: ~/.cache/trivy - key: ${{ runner.os }}-trivy-db - restore-keys: | - ${{ runner.os }}-trivy-db - - # Vulnerability scanning - - name: OCI image vulnerability scanning - uses: aquasecurity/trivy-action@0.20.0 - with: - image-ref: base/${{ inputs.image_name }}:latest - format: 'sarif' - output: 'trivy-results-oci-image.sarif' - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' - - - name: Upload vulnerability report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: 'trivy-results-oci-image.sarif' - - - name: Log into GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ inputs.ci_github_token }} - - - name: Push container image to GHCR ${{ inputs.version }} - if: ${{ inputs.deliver }} - run: | - docker tag base/${{ inputs.image_name }}:latest ghcr.io/${{ inputs.image_name }}:${{ inputs.version }} - docker tag base/${{ inputs.image_name }}:latest ghcr.io/${{ inputs.image_name }}:latest - docker tag lyra-app:latest ghcr.io/${{ inputs.image_name }}-app:${{ inputs.version }} - docker tag lyra-app:latest ghcr.io/${{ inputs.image_name }}-app:latest - docker tag lyra-landing-page:latest ghcr.io/${{ inputs.image_name }}-landing-page:${{ inputs.version }} - docker tag lyra-landing-page:latest ghcr.io/${{ inputs.image_name }}-landing-page:latest - docker push --all-tags ghcr.io/${{ inputs.image_name }} - docker push --all-tags ghcr.io/${{ inputs.image_name }}-app - docker push --all-tags ghcr.io/${{ inputs.image_name }}-landing-page - shell: bash - - - name: Push non-production container image to GHCR ${{ inputs.non_prod_tag }} - if: ${{ !inputs.deliver }} - run: | - docker tag base/${{ inputs.image_name }}:latest \ - ghcr.io/${{ inputs.image_name }}:${{ inputs.non_prod_tag }} - docker tag lyra-app:latest ghcr.io/${{ inputs.image_name }}-app:${{ inputs.non_prod_tag }} - docker tag lyra-landing-page:latest ghcr.io/${{ inputs.image_name }}-landing-page:${{ inputs.non_prod_tag }} - docker push --all-tags ghcr.io/${{ inputs.image_name }} - docker push --all-tags ghcr.io/${{ inputs.image_name }}-app - docker push --all-tags ghcr.io/${{ inputs.image_name }}-landing-page - shell: bash diff --git a/.github/workflows/deploy-main-stage.yml b/.github/workflows/deploy-main-stage.yml index 77b22ef2..9609f79a 100644 --- a/.github/workflows/deploy-main-stage.yml +++ b/.github/workflows/deploy-main-stage.yml @@ -61,7 +61,7 @@ jobs: with: java-version: 21 gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - gradle-arguments: build --scan --no-daemon --stacktrace + gradle-arguments: build koverXmlReport --scan --no-daemon --stacktrace github_token: ${{ env.CI_GITHUB_TOKEN }} - name: Source code vulnerability scanning @@ -79,6 +79,14 @@ jobs: sarif_file: 'trivy-results.sarif' category: source-code + - name: ๐Ÿ“Š Upload coverage reports + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: build/reports/kover/report.xml + fail_ci_if_error: true # optional (default = false) + verbose: true # optional (default = false) + static-analysis-security: name: ๐Ÿ”ฎ Static analysis and ๐Ÿ”’Security Checks needs: [ validation ] @@ -118,47 +126,16 @@ jobs: steps: - run: echo "Running security tests" - code-coverage: - name: Code Coverage ๐Ÿ“Š - needs: [ build ] - runs-on: ubuntu-latest - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - token: ${{ env.CI_GITHUB_TOKEN }} - - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - - name: Install Tools & Dependencies - uses: ./.github/actions/install/node - - - name: Run Code Coverage - run: | - ./gradlew koverXmlReport --no-daemon --stacktrace - - - name: Upload coverage reports - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: build/reports/kover/report.xml - fail_ci_if_error: true # optional (default = false) - verbose: true # optional (default = false) - approval: name: Deploy Approval ๐Ÿ’ซ runs-on: ubuntu-latest - needs: [ functional, performance, security, code-coverage ] + needs: [ functional, performance, security ] steps: - name: Checkout source code uses: actions/checkout@v4 - id: get_data run: | - echo "version=$(cat gradle.properties | grep "version =" | cut -d'=' -f2)" >> $GITHUB_OUTPUT + echo "version=$(cat gradle.properties | grep "version =" | cut -d'=' -f2 | sed 's/^ *//;s/ *$//')" >> $GITHUB_OUTPUT - name: ๐ŸŸข Generate token for approval workflow uses: actions/create-github-app-token@v1 id: app-token @@ -202,12 +179,11 @@ jobs: id: semantic_release run: | npx semantic-release - echo "RELEASE_VERSION=$(cat gradle.properties | grep "version =" | cut -d'=' -f2)" >> "$GITHUB_OUTPUT" env: GITHUB_TOKEN: ${{ env.CI_GITHUB_TOKEN }} - package: - name: Package and Publish ๐Ÿ“ฆ + package-backend: + name: Package and Publish ๐Ÿ“ฆ (Backend application) needs: [ semantic-release ] runs-on: ubuntu-latest permissions: write-all @@ -215,14 +191,42 @@ jobs: - name: Checkout source code uses: actions/checkout@v4 - - name: Package, Publish and Deploy ${{ github.repository }} version ${{ steps.semantic_release.outputs.RELEASE_VERSION || env.VERSION }} - uses: ./.github/actions/docker + - id: get_version + run: | + echo "version=$(cat gradle.properties | grep "version =" | cut -d'=' -f2 | sed 's/^ *//;s/ *$//')" >> $GITHUB_OUTPUT + - name: Build and Push Docker images for the backend application + uses: ./.github/actions/docker/backend with: deliver: true - registry: ${{ env.REGISTRY }} - image_name: ${{ env.IMAGE_NAME }} - version: ${{ steps.semantic_release.outputs.RELEASE_VERSION || env.VERSION }} - ci_github_token: ${{ env.CI_GITHUB_TOKEN }} docker_username: ${{ secrets.DOCKER_USERNAME }} docker_password: ${{ secrets.DOCKER_PASSWORD }} + version: ${{ steps.get_version.outputs.version }} + ci_github_token: ${{ env.CI_GITHUB_TOKEN }} gradle-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + + + package-frontend: + name: Package and Publish ๐Ÿ“ฆ (Frontend application) + needs: [ semantic-release ] + runs-on: ubuntu-latest + permissions: write-all + strategy: + matrix: + target: [lyra-app,lyra-landing-page] + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - id: get_version + run: | + echo "version=$(cat gradle.properties | grep "version =" | cut -d'=' -f2 | sed 's/^ *//;s/ *$//')" >> $GITHUB_OUTPUT + + - name: ๐Ÿ—๏ธ Build and Push Docker images for the frontend application ${{ matrix.target }} + uses: ./.github/actions/docker/frontend + with: + deliver: true + docker_username: ${{ secrets.DOCKER_USERNAME }} + docker_password: ${{ secrets.DOCKER_PASSWORD }} + version: ${{ steps.get_version.outputs.version }} + ci_github_token: ${{ env.CI_GITHUB_TOKEN }} + target: ${{ matrix.target }} diff --git a/.github/workflows/dev-commit-stage.yml b/.github/workflows/dev-commit-stage.yml index b8997b96..aae19cf9 100644 --- a/.github/workflows/dev-commit-stage.yml +++ b/.github/workflows/dev-commit-stage.yml @@ -60,7 +60,7 @@ jobs: with: java-version: 21 gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - gradle-arguments: build --scan --no-daemon --stacktrace + gradle-arguments: build koverXmlReport --scan --no-daemon --stacktrace github_token: ${{ env.CI_GITHUB_TOKEN }} - name: Source code vulnerability scanning @@ -78,30 +78,7 @@ jobs: sarif_file: 'trivy-results.sarif' category: source-code - code-coverage: - name: Code Coverage ๐Ÿ“Š - needs: [ build ] - runs-on: ubuntu-latest - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - token: ${{ env.CI_GITHUB_TOKEN }} - - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - - name: Install Tools & Dependencies - uses: ./.github/actions/install/node - - - name: Run Code Coverage - run: | - ./gradlew koverXmlReport --no-daemon --stacktrace - - - name: Upload coverage reports + - name: ๐Ÿ“Š Upload coverage reports uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -129,7 +106,7 @@ jobs: with: gradle-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - package: + package-backend: name: Package and Publish ๐Ÿ“ฆ needs: [ build ] runs-on: ubuntu-latest @@ -138,14 +115,42 @@ jobs: - name: Checkout source code uses: actions/checkout@v4 - - name: Package docker image - uses: ./.github/actions/docker + - id: get_version + run: | + echo "version=$(cat gradle.properties | grep "version =" | cut -d'=' -f2 | sed 's/^ *//;s/ *$//')" >> $GITHUB_OUTPUT + - name: Build and Push Docker images for the backend application + uses: ./.github/actions/docker/backend with: deliver: false - registry: ${{ env.REGISTRY }} - image_name: ${{ env.IMAGE_NAME }} - version: ${{ github.sha }} - ci_github_token: ${{ env.CI_GITHUB_TOKEN }} docker_username: ${{ secrets.DOCKER_USERNAME }} docker_password: ${{ secrets.DOCKER_PASSWORD }} + version: ${{ steps.get_version.outputs.version }} + ci_github_token: ${{ env.CI_GITHUB_TOKEN }} gradle-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + + + package-frontend: + name: Package and Publish ๐Ÿ“ฆ (Frontend application) + needs: [ build ] + runs-on: ubuntu-latest + permissions: write-all + strategy: + matrix: + target: [lyra-app,lyra-landing-page] + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - id: get_version + run: | + echo "version=$(cat gradle.properties | grep "version =" | cut -d'=' -f2 | sed 's/^ *//;s/ *$//')" >> $GITHUB_OUTPUT + + - name: ๐Ÿ—๏ธ Build and Push Docker images for the frontend application ${{ matrix.target }} + uses: ./.github/actions/docker/frontend + with: + deliver: false + docker_username: ${{ secrets.DOCKER_USERNAME }} + docker_password: ${{ secrets.DOCKER_PASSWORD }} + version: ${{ steps.get_version.outputs.version }} + ci_github_token: ${{ env.CI_GITHUB_TOKEN }} + target: ${{ matrix.target }} diff --git a/.github/workflows/pre-release-stage.yml b/.github/workflows/pre-release-stage.yml deleted file mode 100644 index ba8c9a1b..00000000 --- a/.github/workflows/pre-release-stage.yml +++ /dev/null @@ -1,197 +0,0 @@ -name: Pre Release Stage ๐Ÿš€ -on: - push: - branches: [ alpha, beta ] - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - VERSION: ${{ github.sha }} - CI: CI - NATIVE_IMAGE_ENABLED: enabled - CI_GITHUB_TOKEN: ${{ secrets.CI_GITHUB_TOKEN }} - -permissions: - # required for all workflows - security-events: write - packages: write - contents: write - issues: write - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - validation: - name: Validation ๐Ÿ‘€ - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Check Project Integrity - uses: ./.github/actions/check - - build: - name: Build and Test ๐Ÿงช - needs: [ validation ] - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - steps: - - name: ๐Ÿ” CI_GITHUB_TOKEN - if: env.CI_GITHUB_TOKEN == '' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: echo "CI_GITHUB_TOKEN=${GITHUB_TOKEN}" >> $GITHUB_ENV - - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - gradle-arguments: build --scan - - - name: Source code vulnerability scanning - uses: aquasecurity/trivy-action@0.24.0 - with: - scan-type: 'fs' - ignore-unfixed: true - format: 'sarif' - output: 'trivy-results.sarif' - severity: 'CRITICAL' - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: 'trivy-results.sarif' - category: source-code - - static-analysis-security: - name: ๐Ÿ”ฎ Static analysis and ๐Ÿ”’Security Checks - needs: [ validation ] - runs-on: ubuntu-latest - - steps: - - name: ๐Ÿ”„ Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - - name: Install Tools & Dependencies - uses: ./.github/actions/install/node - - - name: Check OWASP ๐Ÿ›ก๏ธ - run: ./gradlew dependencyCheckAnalyze --no-daemon --stacktrace - - - name: Upload owasp-report results ๐Ÿ›ก๏ธโฌ†๏ธ - uses: actions/upload-artifact@v4 - with: - name: owasp-reports - path: build/reports/owasp - - - name: Run detekt - run: ./gradlew detektAll --no-daemon --stacktrace - - - name: Upload static reports artifact - uses: actions/upload-artifact@v4.3.3 - with: - name: static-report - path: | - build/reports/detekt/detekt.xml - **/build/reports/lint-results-debug.xml - retention-days: 1 - - - name: Analyze detekt report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: build/reports/detekt/detekt.sarif - checkout_path: ${{ github.workspace }} - - functional: - name: Functional Acceptance Tests ๐Ÿงช - needs: [ build ] - runs-on: ubuntu-latest - steps: - - run: echo "Running functional acceptance tests" - performance: - name: Performance Tests ๐Ÿš€ - needs: [ functional ] - runs-on: ubuntu-latest - steps: - - run: echo "Running performance tests" - security: - name: Security Tests ๐Ÿค– - needs: [ performance ] - runs-on: ubuntu-latest - steps: - - run: echo "Running security tests" - - code-coverage: - name: Code Coverage ๐Ÿ“Š - needs: [ build ] - runs-on: ubuntu-latest - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - token: ${{ env.CI_GITHUB_TOKEN }} - - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - - name: Install Tools & Dependencies - uses: ./.github/actions/install/node - - - name: Run Code Coverage - run: | - ./gradlew koverXmlReport --no-daemon --stacktrace - - - name: Upload coverage reports - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: build/reports/kover/report.xml - fail_ci_if_error: true # optional (default = false) - verbose: true # optional (default = false) - - semantic-release: - name: Semantic Release ๐Ÿงญ - needs: [ security ] - runs-on: ubuntu-latest - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - token: ${{ env.CI_GITHUB_TOKEN }} - - - name: Install Java Tools & Dependencies - uses: ./.github/actions/install/java - with: - java-version: 21 - gradle-encription-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - - name: Install Tools & Dependencies - uses: ./.github/actions/install/node - - - name: Run Semantic Release - run: | - npx semantic-release - env: - GITHUB_TOKEN: ${{ env.CI_GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile index 60a93e1d..1c79854b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,62 +1,87 @@ +# Base image for Node.js applications with PNPM setup FROM node:22-alpine AS base + +# Metadata for the image +LABEL maintainer="05barkers.airbase@icloud.com" +LABEL org.opencontainers.image.source="https://github.com/dallay/lyra" +LABEL org.opencontainers.image.title="Lyra Project" +LABEL org.opencontainers.image.description="Multi-platform Docker images for all Lyra applications" +LABEL org.opencontainers.image.licenses="MIT" + +# Environment setup for Node.js and PNPM ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable -# Build stage -FROM base AS build +# Build stage for frontend +FROM base AS build-frontend COPY . /usr/src/app WORKDIR /usr/src/app -# Install dependencies and build the project +# Install dependencies and build frontend projects (Nuxt and Astro) RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN pnpm run build -# Deploy output to specific directories +# Deploy output to specific directories for lyra-app and lyra-landing-page RUN pnpm deploy --filter=lyra-app --prod /prod/lyra-app RUN pnpm deploy --filter=lyra-landing-page --prod /prod/lyra-landing-page -# Application image for lyra-app-dev (Nuxt) +# Application image for lyra-app-dev (Nuxt) - Development FROM base AS lyra-app-dev -COPY --from=build /prod/lyra-app /prod/lyra-app-dev +LABEL stage="development" +COPY --from=build-frontend /prod/lyra-app /prod/lyra-app-dev WORKDIR /prod/lyra-app-dev -# Expose port for Nuxt server +# Expose port for Nuxt server in development EXPOSE 3000 -# Start the Nuxt server +# Start the Nuxt server in development mode CMD [ "pnpm", "run", "start" ] -# Application image for lyra-app (Nuxt) +# Application image for lyra-app (Nuxt) - Production FROM gcr.io/distroless/nodejs22-debian12 AS lyra-app -COPY --from=build /usr/src/app/apps/frontend/apps/lyra-app/.output /prod/lyra-app +LABEL stage="production" WORKDIR /prod/lyra-app -# Expose port for Nuxt server + +# Copy only the production build output from the build stage +COPY --from=build-frontend /usr/src/app/apps/frontend/apps/lyra-app/.output /prod/lyra-app + +# Expose port for Nuxt server in production EXPOSE 3000/tcp -# Start node server +# Start the Nuxt server in production mode CMD [ "./server/index.mjs" ] -# Application image for lyra-landing-page-dev (Astro) +# Application image for lyra-landing-page-dev (Astro) - Development FROM base AS lyra-landing-page-dev -COPY --from=build /prod/lyra-landing-page /prod/lyra-landing-page-dev +LABEL stage="development" +COPY --from=build-frontend /prod/lyra-landing-page /prod/lyra-landing-page-dev WORKDIR /prod/lyra-landing-page-dev +# Set environment variables for Astro development ENV HOST=0.0.0.0 ENV PORT=4321 + +# Expose port for Astro server in development EXPOSE 4321 -# Start the static file server +# Start the Astro server in development mode CMD [ "pnpm", "run", "start" ] -# Application image for lyra-landing-page (Astro) +# Application image for lyra-landing-page (Astro) - Production FROM gcr.io/distroless/nodejs22-debian12 AS lyra-landing-page -COPY --from=build /usr/src/app/apps/frontend/apps/lyra-landing-page/dist /prod/lyra-landing-page +LABEL stage="production" WORKDIR /prod/lyra-landing-page +# Copy only the production build output from the build stage +COPY --from=build-frontend /usr/src/app/apps/frontend/apps/lyra-landing-page/dist /prod/lyra-landing-page + +# Set environment variables for Astro production ENV HOST=0.0.0.0 ENV PORT=4321 + +# Expose port for Astro server in production EXPOSE 4321 -# Start node server +# Start the Astro static file server in production mode CMD ["./server/entry.mjs"] diff --git a/apps/backend/backend.gradle.kts b/apps/backend/backend.gradle.kts index d5079f8f..c3e6349c 100644 --- a/apps/backend/backend.gradle.kts +++ b/apps/backend/backend.gradle.kts @@ -99,15 +99,27 @@ tasks.named("assemble") { tasks.named("bootBuildImage") { buildCache { volume { - name.set("cache-${rootProject.name}.build") + name.set("cache-${rootProject.name}-deps") } } + launchCache { volume { - name.set("cache-${rootProject.name}.launch") + name.set("launch-cache-${rootProject.name}") } } + + imageName = "lyra" createdDate = "now" + + tags = listOf( + "lyra:${project.version}", + "ghcr.io/dallay/lyra:latest", + "ghcr.io/dallay/lyra:${project.version}", + "docker.io/n4t5u/lyra:latest", + "docker.io/n4t5u/lyra:${project.version}", + ) + dependsOn("build") } diff --git a/apps/backend/src/main/kotlin/com/lyra/app/newsletter/subscriber/application/search/email/GetAllSubscribersByEmailService.kt b/apps/backend/src/main/kotlin/com/lyra/app/newsletter/subscriber/application/search/email/GetAllSubscribersByEmailService.kt index b0f11f4e..cf4148d5 100644 --- a/apps/backend/src/main/kotlin/com/lyra/app/newsletter/subscriber/application/search/email/GetAllSubscribersByEmailService.kt +++ b/apps/backend/src/main/kotlin/com/lyra/app/newsletter/subscriber/application/search/email/GetAllSubscribersByEmailService.kt @@ -29,9 +29,9 @@ class GetAllSubscribersByEmailService(private val repository: SubscriberSearchRe if (emails.isEmpty()) { return SubscribersResponse(emptyList()) } - val organizationId = OrganizationId(organizationId) + val orgId = OrganizationId(organizationId) val response: List = - repository.searchAllByEmails(organizationId, emails).map { SubscriberResponse.from(it) } + repository.searchAllByEmails(orgId, emails).map { SubscriberResponse.from(it) } return SubscribersResponse(response) } diff --git a/apps/backend/src/test/kotlin/com/lyra/app/authentication/infrastructure/AuthenticationSteps.kt b/apps/backend/src/test/kotlin/com/lyra/app/authentication/infrastructure/AuthenticationSteps.kt index 76f3aed0..ec6b54ce 100644 --- a/apps/backend/src/test/kotlin/com/lyra/app/authentication/infrastructure/AuthenticationSteps.kt +++ b/apps/backend/src/test/kotlin/com/lyra/app/authentication/infrastructure/AuthenticationSteps.kt @@ -70,10 +70,10 @@ class AuthenticationSteps { fun token(): String { return Jwts.builder() - .setSubject("authentication") + .subject("authentication") .signWith(CucumberAuthenticationConfiguration.JWT_KEY) - .setClaims(claims) - .setExpiration(Date.from(Instant.now().plusSeconds(300))) + .claims(claims) + .expiration(Date.from(Instant.now().plusSeconds(300))) .compact() } } diff --git a/gradle.properties b/gradle.properties index c8db6f8c..3ebb3a1d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,3 +10,4 @@ org.gradle.configureondemand = true # Kotlin Settings kotlin.code.style = official kotlin.incremental = true +org.gradle.daemon=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a..df97d72b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME