diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 90a99570e2..f23a455210 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,10 +27,6 @@ permissions: security-events: write jobs: - lint: - name: Lint - uses: ./.github/workflows/lint.yml - openapi: name: Lead Provider OpenAPI Check uses: ./.github/workflows/lead_provider_openapi_check.yml @@ -38,10 +34,11 @@ jobs: rspec: name: Run the RSpec tests uses: ./.github/workflows/rspec.yml + secrets: inherit permit-merge: name: Permit merge - needs: [lint, rspec] + needs: [rspec] runs-on: ubuntu-latest steps: - run: "echo 'Linting and tests passed, this branch is ready to be merged'" @@ -117,7 +114,7 @@ jobs: deploy_staging: name: Deploy staging - needs: [docker, rspec, lint, brakeman] + needs: [docker, rspec, brakeman] runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' environment: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 930812b2b8..0000000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,104 +0,0 @@ -name: "Lint" -on: - workflow_call: - inputs: - ruby-version: - description: Ruby version - type: string - required: false - default: "3.2.4" - node-version: - description: Node version - type: string - required: false - default: "18.18.x" - -jobs: - ruby_linting: - name: "Lint ruby" - env: - GOVUK_NOTIFY_API_KEY: Test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - name: Checkout Code - - - name: Set up Ruby - uses: ruby/setup-ruby@v1.215.0 - with: - ruby-version: ${{ inputs.ruby-version }} - - - name: Install dependencies - run: bundle install - - - name: Lint Ruby - run: bundle exec rubocop - - js_linting: - name: "Lint JS" - env: - GOVUK_NOTIFY_API_KEY: Test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - name: Checkout Code - - - name: Set up Node - uses: actions/setup-node@v4.2.0 - with: - node-version: ${{ inputs.node-version }} - cache: "yarn" - - - name: Yarn install - run: npm i -g yarn && yarn - - - name: Lint JS - run: |- - yarn lint - - scss_linting: - name: "Lint SCSS" - env: - GOVUK_NOTIFY_API_KEY: Test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - name: Checkout Code - - - name: Set up Ruby - uses: ruby/setup-ruby@v1.215.0 - with: - ruby-version: ${{ inputs.ruby-version }} - - - name: Install dependencies - run: bundle install - - - name: Lint SCSS - run: |- - bundle exec rake lint:scss - - erb_linting: - name: "Lint ERB" - env: - GOVUK_NOTIFY_API_KEY: Test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - name: Checkout Code - - - name: Set up Ruby - uses: ruby/setup-ruby@v1.215.0 - with: - ruby-version: ${{ inputs.ruby-version }} - - - name: Install dependencies - run: bundle install - - - name: Lint ERB Templates - if: false - run: |- - bundle exec erblint --lint-all diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 0fb626879c..e9d9e8d7df 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -7,8 +7,110 @@ on: type: boolean required: false default: true - + +env: + code-coverage-artifact-name: code_coverage_${{github.run_number}} + unit-tests-artifact-name: unit_tests_${{github.run_number}} + rubocop-artifact-name: rubocop_results_${{github.run_number}} + jobs: + ruby-linting: + name: "Lint ruby" + env: + GOVUK_NOTIFY_API_KEY: Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + name: Checkout Code + + - name: Set up Ruby + uses: ruby/setup-ruby@v1.215.0 + with: + ruby-version: ${{ inputs.ruby-version }} + + - name: Install dependencies + run: bundle install + + - name: Lint Ruby + run: bundle exec rubocop --format json --out=out/rubocop-result.json + + - name: Keep Rubocop output + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ env.rubocop-artifact-name }} + path: ${{ github.workspace }}/out/rubocop-result.json + include-hidden-files: true + + js-linting: + name: "Lint JS" + env: + GOVUK_NOTIFY_API_KEY: Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + name: Checkout Code + + - name: Set up Node + uses: actions/setup-node@v4.2.0 + with: + node-version: ${{ inputs.node-version }} + cache: "yarn" + + - name: Yarn install + run: npm i -g yarn && yarn + + - name: Lint JS + run: |- + yarn lint + + scss-linting: + name: "Lint SCSS" + env: + GOVUK_NOTIFY_API_KEY: Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + name: Checkout Code + + - name: Set up Ruby + uses: ruby/setup-ruby@v1.215.0 + with: + ruby-version: ${{ inputs.ruby-version }} + + - name: Install dependencies + run: bundle install + + - name: Lint SCSS + run: |- + bundle exec rake lint:scss + + erb_linting: + name: "Lint ERB" + env: + GOVUK_NOTIFY_API_KEY: Test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + name: Checkout Code + + - name: Set up Ruby + uses: ruby/setup-ruby@v1.215.0 + with: + ruby-version: ${{ inputs.ruby-version }} + + - name: Install dependencies + run: bundle install + + - name: Lint ERB Templates + if: false + run: |- + bundle exec erblint --lint-all + tests: name: Run rspec runs-on: ubuntu-20.04 @@ -59,7 +161,23 @@ jobs: run: |- bundle exec rake "knapsack:rspec[--tag ~type:feature]" - feature-tests: + - name: Keep Code Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ env.code-coverage-artifact-name }}_${{ matrix.ci_node_index }}_tests + path: ./coverage + include-hidden-files: true + + - name: Keep Unit Tests Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ env.unit-tests-artifact-name }}_${{ matrix.ci_node_index }}_tests + path: ./test-report/* + include-hidden-files: true + + featuretests: name: Run rspec (features) runs-on: ubuntu-20.04 strategy: @@ -109,6 +227,22 @@ jobs: run: |- bundle exec rake "knapsack:rspec[--tag type:feature --fail-fast]" + - name: Keep Code Coverage Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ env.code-coverage-artifact-name }}_${{ matrix.ci_node_index }}_feature_tests + path: ./coverage + include-hidden-files: true + + - name: Keep Unit Tests Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ env.unit-tests-artifact-name }}_${{ matrix.ci_node_index }}_feature_tests + path: ./test-report/* + include-hidden-files: true + e2e-scenarios: if: ${{ inputs.run-end-to-end-tests }} name: Run end to end scenarios @@ -158,3 +292,75 @@ jobs: CI_NODE_TOTAL: ${{ matrix.ci_node_total }} CI_NODE_INDEX: ${{ matrix.ci_node_index }} run: bundle exec bin/scenarios_ci + + sonar-scanner: + name: Sonar Scanner + runs-on: ubuntu-24.04 + needs: [ tests, feature-tests, ruby-linting ] + if: github.ref != 'refs/heads/main' && github.actor != 'dependabot[bot]' + environment: + name: staging + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ inputs.ruby-version }} + + - name: Install gems + run: | + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + + - name: Setup sonarqube + uses: warchant/setup-sonar-scanner@v8 + + - name: Download Artifacts + uses: actions/download-artifact@v4 + + - name: Combine Coverage Reports + run: |- + # Copy files from separate artifacts into one directory + mkdir ${{github.workspace}}/code_coverage + cp -r ${{github.workspace}}/${{ env.code-coverage-artifact-name }}_*/ ${{github.workspace}}/code_coverage + bundle exec rake coverage:collate + env: + COVERAGE_DIR: ${{github.workspace}}/code_coverage + + - name: Login Azure + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Fetch secrets from key vault + uses: azure/CLI@v2 + id: keyvault-yaml-secret + with: + inlineScript: | + SONAR_TOKEN=$(az keyvault secret show --name "SONAR-TOKEN" --vault-name "s189t01-cpdnpq-te-app-kv" --query "value" -o tsv) + echo "::add-mask::$SONAR_TOKEN" + echo "SONAR_TOKEN=$SONAR_TOKEN" >> $GITHUB_OUTPUT + + - name: Run sonarqube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: sonar-scanner + -Dsonar.token=${{ steps.keyvault-yaml-secret.outputs.SONAR_TOKEN }} + -Dsonar.organization=dfe-digital + -Dsonar.host.url=https://sonarcloud.io/ + -Dsonar.projectKey=DFE-Digital_npq-registration + -Dsonar.testExecutionReportPaths=${{github.workspace}}/${{env.unit-tests-artifact-name}}_0_tests/test-report-0.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_1_tests/test-report-1.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_2_tests/test-report-2.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_3_tests/test-report-3.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_4_tests/test-report-4.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_5_tests/test-report-5.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_6_tests/test-report-5.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_0_feature_tests/test-report-1.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_1_feature_tests/test-report-2.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_2_feature_tests/test-report-3.xml,\ + ${{github.workspace}}/${{env.unit-tests-artifact-name}}_3_feature_tests/test-report-4.xml + -Dsonar.ruby.coverage.reportPaths=${{github.workspace}}/coverage/coverage.json + -Dsonar.ruby.rubocop.reportPaths=${{github.workspace}}/${{env.rubocop-artifact-name}}/rubocop-result.json diff --git a/Gemfile b/Gemfile index d5a87b1c36..177b917d54 100644 --- a/Gemfile +++ b/Gemfile @@ -178,6 +178,7 @@ group :test do gem "pundit-matchers", "~> 1.9.0" gem "rails-controller-testing", "~> 1.0.5" gem "rspec-default_http_header", "~> 0.0.6" + gem "rspec-sonarqube-formatter", require: false gem "selenium-webdriver" gem "shoulda-matchers", "~> 5.3" gem "simplecov" diff --git a/Gemfile.lock b/Gemfile.lock index 44044b89bb..01e85e4745 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -286,6 +286,7 @@ GEM hashie (5.0.0) html-attributes-utils (1.0.2) activesupport (>= 6.1.4.4) + htmlentities (4.3.4) httparty (0.22.0) csv mini_mime (>= 1.0.0) @@ -503,6 +504,10 @@ GEM nokogiri rexml (3.4.0) rouge (4.5.1) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) rspec-core (3.13.2) rspec-support (~> 3.13.0) rspec-default_http_header (0.0.6) @@ -521,6 +526,9 @@ GEM rspec-expectations (~> 3.13) rspec-mocks (~> 3.13) rspec-support (~> 3.13) + rspec-sonarqube-formatter (1.6.3) + htmlentities (~> 4.3) + rspec (~> 3.0) rspec-support (3.13.1) rswag-specs (2.16.0) activesupport (>= 5.2, < 8.1) @@ -790,6 +798,7 @@ DEPENDENCIES rouge rspec-default_http_header (~> 0.0.6) rspec-rails (~> 6.1.5) + rspec-sonarqube-formatter rswag-specs (~> 2.16) rubocop-govuk (>= 4.8) rubyzip (~> 2.4) diff --git a/lib/tasks/coverage.rake b/lib/tasks/coverage.rake new file mode 100644 index 0000000000..7da1d36e51 --- /dev/null +++ b/lib/tasks/coverage.rake @@ -0,0 +1,15 @@ +desc "Code Coverage tasks" +namespace :coverage do + desc "Collates all result sets generated by the different test runners" + task collate: :environment do + require "simplecov" + require "simplecov_json_formatter" + + SimpleCov.collate Dir["#{ENV['COVERAGE_DIR']}/**/.resultset.json"], "rails" do + formatter SimpleCov::Formatter::MultiFormatter.new([ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::JSONFormatter, + ]) + end + end +end diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000000..e583fdda37 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,8 @@ +sonar.projectKey=DFE-Digital_npq-registration +sonar.organization=dfe-digital +sonar.exclusions= +sonar.sources=app,lib +sonar.tests=./spec +sonar.ruby.coverage.framework=RSpec +sonar.ruby.rubocop.reportPath=rubocop-result.json +sonar.ruby.coverage.reportPaths=code_coverage/.resultset.json diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index fd99dd9eee..9a86845c12 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "simplecov" +require "simplecov_json_formatter" SimpleCov.start # This file is copied to spec/ when you run 'rails generate rspec:install'