속닥속닥 소나큐브 적용기 - 개인레포에서 연습

조현근·2022년 11월 3일
0
post-thumbnail

사전준비

  • 소나큐브를 띄울 ec2(아니면 로컬에서 vm을 올리거나 도커를 해도 될거같다)

소나큐브 설치

  • 준비한 ec2(혹은 vm, 도커)에 접속

1. apt 업데이트 & unzip 설치

$ sudo apt update
$ sudo apt install unzip

2. 자바 11 설치

$ wget -O- https://apt.corretto.aws/corretto.key | sudo apt-key add - 
 sudo add-apt-repository 'deb https://apt.corretto.aws stable main'
$ sudo apt-get update; sudo apt-get install -y java-11-amazon-corretto-jdk

3. sonarQube를 설치할 폴더 생성, 이동

$ sudo mkdir /app
$ cd /app
$ sudo mkdir sonarqube
$ cd /sonarqube

4. 인스턴스 root 위치에서 폴더를 생성한다.

$ sudo wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-9.5.0.56709.zip
$ sudo unzip sonarqube-9.5.0.56709.zip

5. sonarQube 실행 권한 변경

$ sudo chown -R ubuntu /app/sonarqube

6. (상황에 따라) ec2를 사용하는데 인바운드 규칙때문에(우리가 변경 못하는 경우) 열려있는 포트로 sonarQube web port를 바꿔줘야 한다.(기본은 9000)

$ vim /app/sonarqube/sonarqube-9.5.0.56709/conf/sonar.properties

위 폴더에서 아래를 다음과 같이 변경하자

sonar.web.port=<사용가능한 포트>(ex. 8081)

7. 원래대로라면 /app/sonarqube/sonarqube-9.5.0.56709/bin 안에 있는 폴더 중 현재 컴퓨터 아키텍쳐에 맞는 폴더로 들어가 “sonar.sh”를 실행해야 한다.

하지만 우테코에서 제공하는 ec2는 arm 아키텍쳐이다. 따라서 아래와 같은 방법으로 실행해야 한다.

$ cd /app/sonarqube/sonarqube-9.5.0.56709/lib

$ java -jar sonar-application-9.5.0.56709.jar
혹은
$ nohup java -jar sonar-application-9.5.0.56709.jar &

8. 외부에서 브라우저에 <소나큐브 public ip>:<위에서 설정한 포트번호>(속닥속닥은 43.200.116.125:8081)를 검색해 접속하자

위 페이지가 뜨면 성공! 초기 아이디와 비번은 둘다 admin이다.

9. 우측 상단의 프로필을 누르고 My Account를 누르자. 그 후 Security탭의 Generates Token을 하면 된다.

나오는 문자열을 꼭 복사해두자!

이 토큰은 나중에 Scanner를 발동시킬 때 사용된다.

10. 소나큐브 프로젝트 생성

  • 소나큐브 웹페이지에서 Projects를 누르고 Manually를 눌르자

  • 적절한 이름을 넣어주자.

  • 깃헙액션에 연동해 사용할거니 With Github Actions를 클릭하자

  • Generate a token을 눌러 토큰을 생성하자

  • default값을 이용하거나 특정 값을 넣고 토큰을 생성하자
    나오는 문자열은 저장해두자!

  • 필요한 의존성을 추가하자. 우리는 Gradle를 사용하니 Gradle을 누르고 나오는 의존성을 스프링부트 프로젝트 build.gradle에 추가하면 된다.

  • 깃허브 레포의 .github/workflows 하위에 추가할 yaml파일 샘플을 보여준다. 해당 스크립트를 커스텀해 사용하면 된다. 하지만 속닥속닥팀에선 이미 깃헙액션에서 테스트코드 검증을 사용한다. 따라서 아래와 같은 스크립트를 짜서 사용했다.
name: backend

on:
  push:
    branches:
      - master
    paths: 'sokdakInfraPractice/**'
  pull_request:
    branches:
      - master
    paths: 'sokdakInfraPractice/**'

defaults:
  run:
    working-directory: sokdakInfraPractice

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: 리포지토리를 가져옵니다
        uses: actions/checkout@v3

      - name: JDK 11을 설치합니다
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'
          
      - name: Setup timezone
        uses: zcong1993/setup-timezone@master
        with:
          timezone: Asia/Seoul

      - name: Gradle 명령 실행을 위한 권한을 부여합니다.
        run: chmod +x gradlew

      - name: Gradle build를 수행합니다
        run: ./gradlew build

      - name: 테스트 결과를 PR에 코멘트로 등록합니다
        uses: EnricoMi/publish-unit-test-result-action@v1
        if: always()
        with:
          files: '**/build/test-results/test/TEST-*.xml'

      - name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록합니다
        uses: mikepenz/action-junit-report@v3
        if: always()
        with:
          report_paths: '**/build/test-results/test/TEST-*.xml'
          token: ${{ github.token }}

  analysis:
    runs-on: ubuntu-latest
    env:
      SONARQUBE_PROJECT_KEY: ${{ secrets.SONARQUBE_PROJECT_KEY }}
      SONARQUBE_URL: ${{ secrets.SONARQUBE_URL }}
      SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }}
      PR_NUMBER: ${{ github.event.pull_request.number }}
      PR_BRANCH: ${{ github.event.pull_request.branch }}
    steps:
      - name: 리포지토리를 가져옵니다
        uses: actions/checkout@v3
        
      - name: Gradle 명령 실행을 위한 권한을 부여합니다.
        run: chmod +x gradlew
        
      - name: Setup timezone
        uses: zcong1993/setup-timezone@master
        with:
          timezone: Asia/Seoul

      # Gralde 의 Scanner 발동, 위의 env 에서 선언한 환경변수와 함께 발동
      - name: Sonaqube Analysis
        run: ./gradlew test sonarqube
          -Dsonar.host.url=${{ env.SONARQUBE_URL }}
          -Dsonar.projectKey=${{ env.SONARQUBE_PROJECT_KEY }}
          -Dsonar.projectName=${{ env.SONARQUBE_PROJECT_KEY }}-${{ env.PR_NUMBER }}
          -Dsonar.login=${{ env.SONARQUBE_TOKEN }}
          
      - name: get analysis results
        id: analysis_results
        run: |
          RESPONSE=$(curl -X GET -G '${{ env.SONARQUBE_AUTH_URL }}/api/measures/search' \
          -d projectKeys=sokdak \
          -d metricKeys=bugs,vulnerabilities,security_hotspots,code_smells,coverage,tests,test_success_density,test_failures,test_errors,skipped_tests,duplicated_lines_density,duplicated_files,duplicated_blocks \
          | jq '.measures')
          echo "$RESPONSE" | jq -c '.[] | .' | while read -r data;
          do
            METRIC=$(echo $data | jq -r '.metric')
            VALUE=$(echo $data | jq -r '.value')
            BEST_VALUE=$(echo $data | jq -r '.bestValue')
            echo "::set-output name=${METRIC}_value::${VALUE}"
            if [ $BEST_VALUE == true ]; then
              echo "::set-output name=${METRIC}_best_value::✅"
            else
              echo "::set-output name=${METRIC}_best_value::❌"                 
            fi
          done
      
      # PR 에 Comment 를 달아주는 스크립트 실행
      - name: Comment Sonarqube URL
        uses: actions/github-script@v4
        with:
          script: |
            const { SONARQUBE_PROJECT_KEY, SONARQUBE_URL, PR_NUMBER } = process.env
            github.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## 🚧 Analysis Results
            ${ SONARQUBE_PROJECT_KEY }-${ PR_NUMBER }
            
            Bugs :    ${{ steps.analysis_results.outputs.bugs_value }}    ${{ steps.analysis_results.outputs.bugs_best_value }}
            Vulnerabilities :    ${{ steps.analysis_results.outputs.vulnerabilities_value }}    ${{ steps.analysis_results.outputs.vulnerabilities_best_value }} 
            Security Hotspots :    ${{ steps.analysis_results.outputs.security_hotspots_value }}    ${{ steps.analysis_results.outputs.security_hotspots_best_value }} 
            Code Smells :    ${{ steps.analysis_results.outputs.code_smells_value }}    ${{ steps.analysis_results.outputs.code_smells_best_value }} 
            Coverage :    ${{ steps.analysis_results.outputs.coverage_value }}    ${{ steps.analysis_results.outputs.coverage_best_value }} 
            Tests :    ${{ steps.analysis_results.outputs.tests_value }}    ${{ steps.analysis_results.outputs.tests_best_value }} 
            Test Success Density :    ${{ steps.analysis_results.outputs.test_success_density_value }}    ${{ steps.analysis_results.outputs.test_success_density_best_value }} 
            Test Failures :    ${{ steps.analysis_results.outputs.test_failures_value }}    ${{ steps.analysis_results.outputs.test_failures_best_value }}             
            Duplicated Lines Density :    ${{ steps.analysis_results.outputs.duplicated_lines_density_value }}    ${{ steps.analysis_results.outputs.duplicated_lines_density_best_value }}
            
            [분석 결과 확인하기](${SONARQUBE_URL})`
            })

스크립트 중간의 analysis 이후가 소나큐브를 위한 스크립트이다.

해당 스크립트엔 여러 환경변수들이 있는데, 이를 이제 깃허브에 추가해주자!

💡 위의 스크립트를 잘 보면 analysis 하위에 PR_NUMBER를 이용해 요청을 보내는 것을 알 수 있다. 따라서 위 스크립트를 사용하면 소나큐브는 pr을 날렸을때만 정상작동 한다.

11. 깃허브에 환경변수 추가

환경변수를 추가하기 위해 깃허브 레포로 돌아가자.
레포에서 Settings의 Security 하위의 Actions를 누르자.
우측 상단에 New Repository secret 버튼을 누르면 환경변수 추가 창이 뜬다.

이전 깃헙액션의 analysis 하위 스크립트에 secrets.~~~ 로 시작하는 얘들이 깃허브에 저장해야 하는 환경변수이다.

  1. secrets.SONARQUBE_URL: <소나큐브를 띄운 ec2 public ip>:<소나큐브 web 포트번호> (ex. http://:)
  2. env.SONARQUBE_PROJECT_KEY: 소나큐브에서 설정한 key를 넣어주면 된다. 우리는 위에서 sokdak으로 했었다.
  3. env.SONARQUBE_TOKEN: 소나큐브에서 프로젝트를 만들 때 생성한 토큰을 넣어주면 된다.

12. 소나큐브는 push일땐 정상작동하지 않고 pr을 날렸을때만 정상작동한다.

스크립트를 보면 아래와 같은 코드가 있다.
PR_NUMBER를 사용하는데, 만약 깃허브에 push를 날렸을땐 해당 값이 안들어가 소나큐브가 정상작동하지 않는다.

push를 날렸을때 실패한 모습

Not found가 뜨고, url에 issues//comments로 pr_number가 들어가지 않아 오류가 뜬 것을 알 수 있다.

pr을 날렸을때 정상작동한 모습

profile
안녕하세요!

1개의 댓글

comment-user-thumbnail
2023년 4월 16일

안녕하세요 ! 혹시 ec2 프리티어환경에서 구현 하셨나요?

답글 달기