RuboCop 규칙 개발 지침

우리의 코드베이스 스타일은 RuboCop에 의해 정의되고 강제됩니다.

bundle exec rubocop --parallel로 로컬에서 임의의 위반사항을 확인할 수 있습니다. CI에서는 이를 static-analysis 작업으로 자동으로 확인합니다.

또한, Solargraph 젬을 이용하여 지원되는 IDE에 RuboCop을 통합할 수 있습니다. RuboCop 규칙에 대해 아직 결정하지 않은 경우에는 Ruby 스타일 가이드를 따라야 합니다.

리뷰어/유지보수자는 스타일에 대해 너무 엄격하게 하지 말아야 합니다.

일부 RuboCop 규칙은 비활성화되어 있으며, 이에 대해 리뷰어/유지보수자는 저자에게 한 가지 스타일을 사용하도록 요청해서는 안 됩니다. 이는 bike-shedding에 대한 공간을 남겨두기 때문에 이상적인 상황은 아닙니다. 이상적으로는 스타일 관련 토론, 트집잡기, 또는 리뷰 과정에서 번복을 피하기 위해 모든 RuboCop 규칙을 활성화해야 합니다. GitLab Ruby 스타일 가이드에는 리뷰에서 종종 나오는 스타일 디렉터리과 강제되지 않는 스타일들에 대한 비충분한 디렉터리이 포함되어 있습니다.

추가로, 우리는 전용 테스트별 스타일 가이드와 모범 사례를 보유하고 있습니다.

규칙 인라인 비활성화

기본적으로 RuboCop 규칙은 소스 코드 내에서 비활성화되어서는 안 되며, 이는 규칙이 코드베이스에 적용하려는 합의된 코드 표준을 무효화시킵니다.

인라인 비활성화를 사용해야 하는 경우에는 비활성화된 규칙이 있는 동일한 줄에 주석으로 이유를 제공해야 합니다.

더 많은 문맥은 이 인라인 비활성화 주석 위에 코드 주석으로 제공할 수 있습니다. 상세한 문맥을 제공하기 위해 자세한 리소스(이슈, 에픽 등)를 링크드 해서 코드 주석을 줄일 수 있습니다.

임시 인라인 비활성화에는 rubocop:todo를 사용하고 후속 이슈를 링크하세요.

예를 들면:

# bad
module Types
  module Domain
    # rubocop:disable Graphql/AuthorizeTypes
    class SomeType < BaseObject
      if condition # rubocop:disable Style/GuardClause
        # more logic...
      end
      
      object.public_send(action) # rubocop:disable GitlabSecurity/PublicSend
    end
    # rubocop:enable Graphql/AuthorizeTypes
  end
end

# good
module Types
  module Domain
    # rubocop:disable Graphql/AuthorizeTypes -- 부모 엔티티에서 이미 승인됨
    class SomeType < BaseObject
      if condition # rubocop:todo Style/GuardClause -- 정리: https://gitlab.com/gitlab-org/gitlab/-/issues/1234567890
        # more logic...
      end
      
      # 이 지점에서 `action`은 `public_send`에서 안전하게 사용될 수 있습니다.
      # 자세한 내용은 https://gitlab.com/gitlab-org/gitlab/-/issues/123457890을 참조하세요.
      object.public_send(action) # rubocop:disable GitlabSecurity/PublicSend -- 사용자 입력 확인됨
    end
    # rubocop:enable Graphql/AuthorizeTypes
  end
end

새로운 RuboCop 규칙 생성

일반적으로 트집잡기를 줄이기 위해 린팅 규칙은 프로그래밍적으로 강제하는 것이 더 나은 경우가 많습니다.

이를 위해, 코드베이스에 새로운 RuboCop 규칙을 생성하는 것을 장려합니다. 특정 스타일을 강제하는 새로운 cop를 추가하기 전에 팀과 토의하는 것이 좋습니다.

우리는 여러 Ruby 코드베이스에서 규칙을 유지하고 있는데, 그 중 일부는 GitLab 애플리케이션에만 적합한 것은 아닙니다. 여러 애플리케이션에 적용될 수 있는 새로운 cop을 만들 때는 gitlab-styles 젬에 추가하는 것을 장려합니다. 만약 cop이 주로 GitLab 애플리케이션에만 적용되는 경우에는 GitLab에 추가해야 합니다.

Cop 유예 기간

규칙이 사용되고 있으며 그 TODO YAML 구성에서 Details: grace period가 정의되어 있을 때, 해당 규칙은 _유예 기간_에 있습니다.

기본 브랜치에서는 grace period에 속하는 규칙에 대한 위반사항은 RuboCop CI 작업을 실패시키지 않습니다. 대신 해당 작업은 #f_rubocop Slack 채널에 알립니다. 그러나 다른 브랜치에서는 RuboCop 작업이 실패합니다.

#f_rubocop Slack 채널에서 1주일 동안 경고가 없는 한 유예 기간을 안전하게 해제할 수 있습니다.

새로운 규칙 활성화

  1. .rubocop.yml에서 새로운 규칙을 활성화합니다 (만약 gitlab-styles을 통해 이미 활성화 되지 않았을 경우).
  2. 새로운 규칙에 대한 TODO를 생성합니다.
  3. 새로운 규칙을 유예 기간으로 설정합니다.
  4. TODO를 해결하고 커뮤니티 기여를 장려하는 이슈를 작성합니다 (~"quick win" 및/또는 ~"Seeking community contributions"을 통해). 일부 예시를 참조하세요.
  5. #f_rubocop Slack 채널에서 1주일 동안 조용하다면 유예 기간을 제거하는 이슈를 작성합니다. 예시를 참조하세요.

소리 없는 위반사항

규칙이 유예 기간에 대한 cop에 대해 비활성화되어 있을 때, #f_rubocop Slack 채널은 2시간마다 알림 메시지를 받습니다.

이 문제를 해결하려면:

  1. 연결된 CI 작업에서 비활성화된 규칙을 찾습니다.
  2. 이러한 규칙에 대해 TODO를 생성합니다.

RuboCop 노드 패턴

Ruby의 AST과 일치시키기 위해 노드 패턴을 만들 때, scripts/rubocop-parse를 사용할 수 있습니다. 이를 통해 Ruby 표현식의 AST를 표시하여 matcher를 만드는 데 도움을 받을 수 있습니다. !97024도 참고하세요.

RuboCop 예외 해결

RuboCop 예외의 수가 기본 exclude-limit 값인 15을 초과하는 경우, 예외를 여러 커밋으로 해결하고자 할 수 있습니다. 혼란을 최소화하기 위해, 예외 디렉터리을 통해 진행 상황을 추적해야 합니다.

초기 디렉터리이나 특정 RuboCop 규칙을 위한 디렉터리을 생성하는 우선적인 방법은 Rake 작업 rubocop:todo:generate를 실행하는 것입니다:

# 초기 디렉터리
bundle exec rake rubocop:todo:generate

# 특정 RuboCop 규칙을 위한 디렉터리
bundle exec rake 'rubocop:todo:generate[Gitlab/NamespacedClass,Lint/Syntax]'

이 Rake 작업은 .rubocop_todo/에 예외 디렉터리을 생성하거나 업데이트합니다. 예를 들어, RuboCop 규칙 Gitlab/NamespacedClass의 구성은 .rubocop_todo/gitlab/namespaced_class.yml에 위치합니다.

Rake 작업을 실행한 후 .rubocop_todo/에서 발생한 모든 변경 사항을 커밋해야 합니다.

기존 RuboCop 예외 확인

.rubocop_todo.yml.rubocop_todo/**/*.yml을 통해 제외된 코드에서 기존 RuboCop 예외를 확인하려면 환경 변수 REVEAL_RUBOCOP_TODO1로 설정하세요.

이를 통해 매일의 작업주기 중에 기존 RuboCop 예외를 확인하고 그에 따라 수정할 수 있습니다.

note
.rubocop_todo/**/*.yml 대신 .rubocop.yml에 영구적인 Exclude를 정의하세요.