InfoGrab DocsInfoGrab Docs

GitLab 프로젝트 파이프라인

요약

gitlab-org/gitlab(및 dev 인스턴스)의 파이프라인은 일반적인 .gitlab-ci.yml에 구성되어 있으며, 유지 관리를 쉽게 하기 위해 .gitlab/ci/ 아래의 파일들을 포함합니다. 가능한 한 GitLab CI/CD 기능과 모범 사례를 dogfood하기 위해 노력하고 있습니다.

gitlab-org/gitlab(및 dev 인스턴스)의 파이프라인은 일반적인 .gitlab-ci.yml에 구성되어 있으며, 유지 관리를 쉽게 하기 위해 .gitlab/ci/ 아래의 파일들을 포함합니다.

가능한 한 GitLab CI/CD 기능과 모범 사례를 dogfood하기 위해 노력하고 있습니다.

dev.gitlab.com 인스턴스에 미러링되어 있지 않은 CI/CD 컴포넌트는 gitlab-org/gitlab 파이프라인에서 사용하지 않습니다. CI/CD 컴포넌트는 서로 다른 인스턴스 간에는 동작하지 않으며, 해당 인스턴스에 존재하지 않으면 dev.gitlab.com 미러에서 파이프라인 실패를 유발합니다.

파이프라인 티어#

머지 리퀘스트는 일반적으로 여러 CI/CD 파이프라인을 실행합니다. 머지 리퀘스트가 승인 프로세스에서 어느 단계에 있는지에 따라 서로 다른 종류의 파이프라인이 트리거됩니다. 이러한 파이프라인 종류를 파이프라인 티어라고 합니다.

현재 세 가지 티어가 있습니다.

  1. pipeline::tier-1: 머지 리퀘스트에 승인이 없는 경우
  2. pipeline::tier-2: 머지 리퀘스트에 승인이 하나 이상 있지만 아직 더 필요한 경우
  3. pipeline::tier-3: 머지 리퀘스트가 필요한 승인을 모두 받은 경우

일반적으로 파이프라인 티어가 낮을수록 파이프라인은 더 빨라야 합니다. 파이프라인 티어가 높을수록 더 많은 테스트를 실행해 더 큰 확신을 줘야 합니다.

구현에 대한 자세한 내용은 MR 파이프라인에 "티어" 도입 에픽을 참고합니다.

머지 리퀘스트 승인 전 예측 테스트 job#

파이프라인 비용을 줄이고 job 실행 시간을 단축하기 위해, 머지 리퀘스트가 승인되기 전에는 파이프라인이 해당 변경 사항에서 실패할 가능성이 있는 RSpec·Jest 테스트를 예측해서 실행합니다.

머지 리퀘스트가 승인된 후에는 파이프라인에 전체 RSpec·Jest 테스트가 포함됩니다. 이를 통해 머지 리퀘스트가 머지되기 전에 모든 테스트가 실행되도록 보장합니다.

GitLab 프로젝트 테스트 의존성 개요#

예측 테스트 job이 어떻게 실행되는지 이해하려면, GitLab 코드(프론트엔드와 백엔드)와 그에 대응하는 테스트(Jest와 RSpec) 간의 의존성을 이해해야 합니다. 이 의존성은 다음 다이어그램으로 나타낼 수 있습니다.

Mermaid 다이어그램 (10줄)
소스 코드 보기
flowchart LR
    subgraph frontend
    fe["Frontend code"]--tested with-->jest
    end
    subgraph backend
    be["Backend code"]--tested with-->rspec
    end
be--generates-->fixtures["frontend fixtures"]
fixtures--used in--&gt;jest</code></pre></details></div>

요약하면 다음과 같습니다.

  • RSpec 테스트는 백엔드 코드에 의존합니다.
  • Jest 테스트는 프론트엔드 코드와 백엔드 코드 양쪽에 의존하며, 백엔드 코드에는 프론트엔드 픽스처를 통해 의존합니다.

detect-tests CI job#

gitlab-org/gitlab의 대부분의 CI/CD 파이프라인은 prepare 스테이지에서 detect-tests CI job을 실행해, 해당 MR에서 변경된 파일을 기준으로 실행할 백엔드/프론트엔드 테스트를 감지합니다.

detect-tests job은 실행할 백엔드/프론트엔드 테스트를 담은 여러 파일을 생성합니다. 이 파일들은 파이프라인의 이후 job에서 읽히며, 해당 테스트만 실행됩니다.

RSpec 예측 job#

머지 리퀘스트에서 예측 RSpec 테스트 파일 결정#

머지 리퀘스트에서 실패할 가능성이 있는 RSpec 테스트를 식별하기 위해 동적 매핑과 정적 매핑을 사용합니다.

동적 매핑#

먼저 test_file_finder gem을 사용하며, 동적 매핑 전략은 Crystalball gem에서 가져옵니다 (사용되는 위치 참고, Crystalball에서 사용하는 매핑 전략).

test_file_finder 외에도 실행할 테스트를 더 많이 감지하기 위해 여러 고급 매핑을 추가했습니다.

  • FindChanges (!74003)
    • 백엔드 변경 시 실행할 Jest 테스트를 (프론트엔드 픽스처를 통해) 자동으로 감지합니다
  • PartialToViewsMappings (#395016)
    • MR에서 뷰에 포함된 Rails partial이 변경되면 해당 뷰 스펙을 실행합니다
  • JsToSystemSpecsMappings (#386754)
    • MR에서 JavaScript 파일이 변경되면 특정 시스템 스펙을 실행합니다
  • GraphqlBaseTypeMappings (#386756)
    • GraphQL 타입 클래스가 변경되면, 이 타입을 포함할 수 있는 다른 GraphQL 타입을 찾아 해당 스펙을 실행합니다.
  • ViewToSystemSpecsMappings (#395017)
    • 뷰가 변경되면 해당 코드 영역을 테스트하는 feature 스펙을 찾습니다.
  • ViewToJsMappings (#386719)
    • JS 파일이 변경되면 해당 JS 컴포넌트를 다루는 시스템 스펙을 찾습니다.
  • FindFilesUsingFeatureFlags (#407366)
    • 기능 플래그가 변경되면 해당 플래그를 포함하는 Ruby 파일을 확인해 detect-tests CI job의 변경 파일 목록에 추가합니다. 이후 job의 나머지 부분에서는 이 변경 파일을 기준으로 실행할 프론트엔드/백엔드 테스트를 감지합니다.
정적 매핑#

동적 매핑으로 매핑할 수 없는 특수한 경우를 위해, test_file_finder gem과 tests.yml 파일에서 관리되는 정적 매핑을 사용합니다 (사용되는 위치 참고).

테스트 매핑에는 각 소스 파일과 그 소스 파일에 의존하는 테스트 파일 목록 간의 매핑이 담겨 있습니다.

예외적인 경우#

또한 항상 전체 RSpec 테스트를 실행하는 몇 가지 경우가 있습니다.

  • 머지 리퀘스트에 pipeline:run-all-rspec 레이블이 설정된 경우. 이 레이블은 as-if-foss job에서 실행되는 테스트를 포함해 모든 RSpec 테스트를 트리거합니다.
  • 머지 리퀘스트에 pipeline:mr-approved 레이블이 설정되어 있고 코드 변경 사항이 backend-patterns 규칙을 충족하는 경우. 이 레이블은 리뷰어가 머지 리퀘스트를 승인하면 트리아지 자동화가 부여하는 것이며, 수동으로 적용하는 것은 권장하지 않습니다.
  • 머지 리퀘스트가 자동화(예: Gitaly 업데이트나 안정 브랜치를 대상으로 하는 MR)에 의해 생성된 경우
  • 머지 리퀘스트가 보안 미러에서 생성된 경우
  • CI 구성 파일이 변경된 경우(예: .gitlab-ci.yml 또는 .gitlab/ci/**/*)

백엔드 예측 테스트 문제 대응#

예측 테스트 문제에 대응하는 방법은 예측 테스트에 대한 Development Analytics RUNBOOK을 참고합니다. 또한 테스트 선택에서 빠진 부분을 발견했다면 @gl-dx/development-analytics에 알려주면, 테스트 선택을 최적화하기 위한 필요한 조치를 취할 수 있습니다.

GitLab Duo 시스템 테스트 선택#

tier-2 파이프라인에서는 GitLab Duo가 MR 변경 사항과 관련된 시스템 스펙을 예측합니다. 이는 detect-tests job에 더해 실행되며, 시스템 스펙(spec/features/, ee/spec/features/)에 특화되어 있습니다.

동작 방식#

detect-system-tests-duo job은 MR diff를 대상으로 GitLab Duo CLI를 호출합니다. 그런 다음 PreparePredictiveSystemPipeline 스크립트가 그 출력을 처리해 파이프라인 입력을 준비합니다.

  • GitLab Duo가 확신하는 경우: GitLab Duo의 예측이 detect-tests가 선택한 시스템 테스트와 합쳐집니다(합집합). 예측 파이프라인은 이 합쳐진 테스트 집합을 실행합니다. GitLab Duo가 시스템 테스트가 필요 없다고 예측하면 detect-tests가 선택한 테스트만 실행됩니다.
  • GitLab Duo가 확신하지 못하거나 실패한 경우: 예측 파이프라인에서 시스템 테스트가 제외됩니다. 전체 시스템 테스트 스위트는 별도의 하위(child) 파이프라인(rspec-predictive-system-full)에서 실행됩니다.

범위#

GitLab Duo 시스템 테스트 선택은 gitlab-org/gitlab의 tier-2 파이프라인에서만 실행되며, 다음 경우에는 건너뜁니다.

  • pipeline:run-all-rspec 레이블이 설정된 경우(전체 스위트가 이미 실행됩니다).
  • pipeline:spec-only 레이블이 설정된 경우(detect-tests가 이미 스펙 파일을 직접 식별합니다).
  • 변경 사항이 core-backend 또는 workhorse 패턴과 일치하는 경우(이러한 변경 사항에 대해서는 이미 메인 파이프라인에서 모든 시스템 테스트가 실행됩니다).

tier-3 파이프라인에서는 이 job이 지표 수집을 위해 실행되지만 그 출력은 테스트 선택에 영향을 주지 않습니다.

코드를 변경하지 않고 GitLab Duo 시스템 테스트 선택을 비활성화하려면 GLCI_DUO_SYSTEM_TESTS_DISABLED CI/CD 프로젝트 변수를 "true"로 설정합니다. 비활성화하면 detect-system-tests-duo job이 건너뛰어지고, tier-2에서는 대체 동작으로 전체 시스템 테스트 스위트가 실행됩니다.

Jest 예측 job#

머지 리퀘스트에서 예측 Jest 테스트 파일 결정#

머지 리퀘스트에서 실패할 가능성이 있는 jest 테스트를 식별하기 위해, 변경된 모든 파일의 목록을 --findRelatedTests 옵션을 사용해 jest에 전달합니다. 이 모드에서 jest는 변경된 파일과 관련된 모든 의존성을 해석하며, 여기에는 해당 파일을 의존성 체인에 포함하는 테스트 파일도 포함됩니다.

예외적인 경우#

또한 항상 전체 Jest 테스트를 실행하는 몇 가지 경우가 있습니다.

  • 머지 리퀘스트에 pipeline:run-all-jest 레이블이 설정된 경우
  • 머지 리퀘스트가 자동화(예: Gitaly 업데이트나 안정 브랜치를 대상으로 하는 MR)에 의해 생성된 경우
  • 머지 리퀘스트가 보안 미러에서 생성된 경우
  • 관련 CI 구성 파일이 변경된 경우(.gitlab/ci/rules.gitlab-ci.yml, .gitlab/ci/frontend.gitlab-ci.yml)
  • 프론트엔드 의존성 파일이 변경된 경우(예: package.json, yarn.lock, config/webpack.config.js, config/helpers/**/*.js)
  • 벤더링된 JavaScript 파일이 변경된 경우(예: vendor/assets/javascripts/**/*)

전체 Jest 테스트에 대한 rules 정의는 rules.gitlab-ci.yml의 .frontend:rules:jest에 정의되어 있습니다.

프론트엔드 예측 테스트 문제 대응#

예측 테스트 문제에 대응하는 방법은 예측 테스트에 대한 Development analytics RUNBOOK을 참고합니다.

포크 파이프라인#

포크 파이프라인에서는 MR에 pipeline:run-all-rspec 레이블이 설정되지 않은 경우 예측 RSpec·Jest job만 실행합니다. 목적은 포크 파이프라인이 소비하는 컴퓨팅 할당량을 줄이는 것입니다.

자세한 내용은 실험 이슈를 참고합니다.

머지 리퀘스트 파이프라인의 Fail-fast job#

머지 리퀘스트가 기존 테스트를 망가뜨렸을 때 더 빠르게 피드백을 제공하기 위해 fail-fast 메커니즘을 구현했습니다.

머지 리퀘스트 파이프라인에서는 다른 모든 rspec job과 병렬로 rspec fail-fast job이 추가됩니다. 이 job은 머지 리퀘스트의 변경 사항과 직접 관련된 테스트를 실행합니다.

이 테스트 중 하나라도 실패하면 rspec fail-fast job이 실패하고, fail-pipeline-early job이 트리거되어 실행됩니다. fail-pipeline-early job은 다음을 수행합니다.

  • 현재 실행 중인 파이프라인과 진행 중인 모든 job을 취소합니다.
  • 파이프라인 상태를 failed로 설정합니다.

예를 들면 다음과 같습니다.

Mermaid 다이어그램 (20줄)
소스 코드 보기
graph LR
    subgraph "prepare stage";
        A["detect-tests"]
    end
subgraph "test stage";
    B["jest"];
    C["rspec migration"];
    D["rspec unit"];
    E["rspec integration"];
    F["rspec system"];
    G["rspec fail-fast"];
end

subgraph "post-test stage";
    Z["fail-pipeline-early"];
end

A --"artifact: list of test files"--&gt; G
G --"on failure"--&gt; Z</code></pre></details></div>

머지 리퀘스트와 관련된 테스트 파일이 10개를 초과하면 rspec fail-fast는 아무 동작도 하지 않습니다. 이는 rspec fail-fast의 실행 시간이 평균적인 rspec job 실행 시간을 초과해 원래 목적을 무력화하는 것을 막기 위한 것입니다.

이 숫자는 RSPEC_FAIL_FAST_TEST_FILE_COUNT_THRESHOLD라는 이름의 CI/CD 변수를 설정해 재정의할 수 있습니다.

머지 리퀘스트 파이프라인에서 이전에 실패한 테스트 재실행#

머지 리퀘스트의 실패한 테스트를 해결한 후 피드백 시간을 줄이기 위해, rspec rspec-pg17-rerun-previous-failed-tests 와 rspec rspec-ee-pg17-rerun-previous-failed-tests job이 이전 MR 파이프라인에서 실패한 테스트를 실행합니다.

이 기능은 2021년 8월 25일에 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053과 함께 도입되었습니다.

실패한 테스트가 재실행되는 방식#

  1. detect-previous-failed-tests job(prepare 스테이지)이 이전 MR 파이프라인에서 실패한 RSpec job과 관련된 테스트 파일을 감지합니다.
  2. rspec rspec-pg17-rerun-previous-failed-tests와 rspec rspec-ee-pg17-rerun-previous-failed-tests job 이 detect-previous-failed-tests job이 수집한 테스트 파일을 실행합니다.
Mermaid 다이어그램 (11줄)
소스 코드 보기
graph LR
    subgraph "prepare stage";
        A["detect-previous-failed-tests"]
    end
subgraph "test stage";
    B["rspec rspec-pg17-rerun-previous-failed-tests"];
    C["rspec rspec-ee-pg17-rerun-previous-failed-tests"];
end

A --"artifact: list of test files"--&gt; B &amp; C</code></pre></details></div>

머지 트레인#

현재 사용 현황#

2024년 6월부터 머지 트레인을 사용하기 시작했습니다.

현재 머지 트레인 파이프라인은 어떤 테스트도 실행하지 않으며, 머지 트레인 도입 이전부터 있었지만 쉽게 강제할 수 없었던 "머지 리퀘스트 머지" 가이드라인만을 강제합니다.

머지 트레인 파이프라인은 머지 전 최신 파이프라인이 다음 조건을 만족하는지 확인하는 단일 pre-merge-checks job을 실행합니다.

  1. 머지 결과(Merged Results) 파이프라인일 것
  2. (예측 파이프라인이 아닌) 전체 파이프라인인 tier-3 파이프라인일 것
  3. 생성된 지 16시간 이내(안정 브랜치는 72시간 이내)일 것

이 방식을 개선하기 위해 피드백 이슈를 개설했습니다.

다음 반복 개선#

머지 트레인 파이프라인에서 실제로 테스트를 실행하기 시작하기 위해, 머지 트레인의 다음 개선 방향을 논의하는 전담 이슈를 개설했습니다.

머지 트레인에서 "전체" 테스트 파이프라인을 실행하기 위한 과제#

"안정적인" 기본 브랜치가 필요한 이유#

기본 브랜치가 불안정하면(예: 기본 브랜치의 CI/CD 파이프라인이 자주 실패하는 경우), 문제가 있는 머지 리퀘스트 파이프라인 이후에 추가된 모든 머지 리퀘스트 파이프라인을 취소하고 트레인에 다시 추가해야 하며, 머지 트레인이 길면 이로 인해 지연이 크게 발생합니다.

기본 브랜치가 얼마나 안정적이어야 하는가#

구체적인 수치는 없지만, 플레이키 테스트 실패와 인프라 실패에 대한 더 나은 수치가 필요합니다(Master Broken Incidents RCA 대시보드 참고).

일부 머지 리퀘스트의 더 빠른 피드백#

손상된 master 수정#

손상된 master를 수정해야 할 때는, 머지 리퀘스트에서 실행되는 파이프라인을 신속하게 처리하기 위해 pipeline::expedited 레이블을 추가할 수 있습니다.

이때 머지 리퀘스트에는 master:broken 또는 master:foss-broken 레이블도 설정되어 있어야 합니다.

되돌리기(Revert) MR#

되돌리기 MR을 더 빠르게 만들려면 머지 리퀘스트를 생성하기 전에 되돌리기 MR 템플릿을 사용합니다. 이 템플릿은 pipeline::expedited 레이블 등을 적용해 머지 리퀘스트에서 실행되는 파이프라인을 신속하게 처리합니다.

pipeline::expedited 레이블#

이 레이블이 부여되면 CI/CD 파이프라인의 다음 단계를 건너뜁니다.

  • e2e:test-on-omnibus-ee job
  • rspec:undercoverage job

머지 리퀘스트에 레이블을 적용하고 해당 MR에 대해 새 파이프라인을 실행합니다.

테스트 job#

각 테스트 레벨마다 전담 job이 있으며, 각 job은 머지 리퀘스트에서 이루어진 변경 사항에 따라 실행됩니다. 변경 사항과 무관하게 모든 RSpec job을 강제로 실행하려면 머지 리퀘스트에 pipeline:run-all-rspec 레이블을 추가할 수 있습니다.

Warning

문서 전용 MR에서 모든 job을 강제로 실행하면 필요한 선행 job이 없어 오류가 발생합니다

gitaly-mvcc RSpec job은 MVCC 스토리지 백엔드를 사용하는 Gitaly 서버를 대상으로 테스트 스위트를 실행합니다. 이 job은 자동으로 실행되지 않습니다. 모두 실행하려면 머지 리퀘스트에 pipeline:run-gitaly-mvcc 레이블을 추가하고 새 파이프라인을 시작합니다. (gitlab-org/gitaly의 rails-specs 트리거처럼) 프로젝트 간 트리거되는 파이프라인에서는 대신 ENABLE_RSPEC_GITALY_MVCC=true를 설정합니다.

엔드투엔드 job#

자세한 내용은 엔드투엔드 테스트 파이프라인을 참고합니다.

As-if-FOSS job과 프로젝트 간 다운스트림 파이프라인#

관련 변경 사항이 FOSS 프로젝트에서 올바르게 동작하는지 확인하기 위해, 특정 조건에서는 다음도 함께 실행합니다.

  • 같은 파이프라인의 * as-if-foss job
  • 프로젝트 간 다운스트림 FOSS 파이프라인

* as-if-foss job은 마치 gitlab-org/gitlab-foss의 컨텍스트에서 실행되는 것처럼 GitLab 테스트 스위트를 "FOSS인 것처럼" 실행합니다. 반면 프로젝트 간 다운스트림 FOSS 파이프라인은 실제로 FOSS 프로젝트 내부에서 실행되므로, 실제 FOSS 환경에 더 가깝습니다.

다음의 경우에 이를 실행합니다.

  • 머지 리퀘스트에 pipeline:run-as-if-foss 레이블이 설정된 경우
  • 머지 리퀘스트에 pipeline:as-if-foss-run-predictive 레이블이 설정된 경우 (FOSS 파이프라인에서 예측 테스트, RuboCop, eslint, 정적 분석만 실행합니다)
  • 머지 리퀘스트가 gitlab-org/security/gitlab 프로젝트에서 생성된 경우
  • CI 구성 파일이 변경된 경우(예: .gitlab-ci.yml 또는 .gitlab/ci/**/*)

* as-if-foss job은 일반적인 EE 컨텍스트 job에 더해 실행됩니다. 이 job에는 FOSS_ONLY='1' 변수가 설정되며, 테스트가 시작되기 전에 ee/ 폴더가 제거됩니다.

프로젝트 간 다운스트림 FOSS 파이프라인은 대신 머지 리퀘스트를 FOSS 프로젝트의 기본 브랜치에 머지하는 것을 시뮬레이션하며, 이때 특정 파일 목록이 제거됩니다. 그 목록은 .gitlab/ci/as-if-foss.gitlab-ci.yml 과 merge-train/bin/merge-train에서 확인할 수 있습니다.

이는 gitlab-org/gitlab이 gitlab-org/gitlab-foss에 동기화된 후에도 변경 사항이 실패를 유발하지 않도록 하기 위한 것입니다.

프로젝트 변수에 설정된 토큰#

  • AS_IF_FOSS_TOKEN: 생성된 as-if-foss/* 브랜치를 푸시하기 위한, developer 권한과 write_repository 퍼미션을 가진 GitLab FOSS 프로젝트 토큰입니다.
    • 보안 프로젝트에서는 이름은 같지만 보안 FOSS 프로젝트의 다른 토큰을 사용해야 합니다. 이렇게 하면 보안 변경 사항이 공개 프로젝트로 푸시되는 일이 없습니다.

As-if-JH 프로젝트 간 다운스트림 파이프라인#

개요#

이 파이프라인은 JiHu 검증 파이프라인이라고도 하며, 현재는 실패해도 허용됩니다. 실패했을 때는 검증 파이프라인이 실패했을 때 해야 할 일을 따릅니다.

실행 방식#

start-as-if-jh job은 프로젝트 간 다운스트림 파이프라인을 트리거해, 마치 GitLab JH의 컨텍스트에서 실행되는 것처럼 GitLab 테스트 스위트를 "JiHu인 것처럼" 실행합니다. 이 job은 다음의 경우에만 생성됩니다.

  • 기능 플래그에 변경이 있는 경우
  • 머지 리퀘스트에 pipeline:run-as-if-jh 레이블이 설정된 경우

이 파이프라인은 GitLab JH mirror의 미러인 GitLab JH validation 프로젝트에서 생성된 브랜치의 컨텍스트로 실행됩니다.

생성되는 브랜치 이름은 머지 리퀘스트의 브랜치 이름 앞에 as-if-jh/가 붙습니다. 이 브랜치는 머지 리퀘스트 브랜치를 기반으로 하며, 전체 파이프라인을 JiHu인 것처럼 만들기 위해 대응하는 JH 브랜치에서 내려받은 변경 사항을 추가로 반영합니다.

이는 GitLab이 GitLab JH에 동기화된 후에도 변경 사항이 실패를 유발하지 않도록 하기 위한 것입니다.

pipeline:run-as-if-jh 레이블 적용을 고려해야 하는 경우#

Ruby 파일 이름이 변경되고 그에 대응하는 prepend_mod 줄이 있다면, GitLab JH가 이를 의존하고 있을 가능성이 크므로 prepend하는 모듈이나 클래스 이름을 바꾸는 대응 변경이 필요합니다.

대응하는 JH 브랜치#

브랜치 이름에 -jh를 붙여서 GitLab JH에 대응하는 JH 브랜치를 만들 수 있습니다. 대응하는 JH 브랜치가 있으면 as-if-jh 파이프라인은 기본 브랜치 main-jh가 아니라 해당 브랜치에서 파일을 가져옵니다.

현재는 CI가 GitLab JH mirror에서 해당 브랜치를 가져오려고 시도하므로, 새 JH 브랜치가 미러에 전파되기까지 시간이 걸릴 수 있습니다.

Note

GitLab JH validation은 GitLab JH mirror의 미러이지만, 기본 브랜치 main-jh 외에는 대응하는 JH 브랜치를 포함하지 않습니다. 그래서 대응하는 JH 브랜치를 가져와야 할 때는 validation 프로젝트가 아니라 메인 미러에서 가져와야 합니다.

as-if-JH 파이프라인 구성 방식#

전체 과정은 다음과 같습니다.

Note

sync-as-if-jh-branch는 의존성 변경이 있을 때만 실행합니다.

Mermaid 다이어그램 (28줄)
소스 코드 보기
flowchart TD
  subgraph "JiHuLab.com"
    JH["gitlab-cn/gitlab"]
  end

subgraph "GitLab.com" Mirror["gitlab-org/gitlab-jh-mirrors/gitlab"]

subgraph MR["gitlab-org/gitlab merge request"]
  Add["add-jh-files job"]
  Prepare["prepare-as-if-jh-branch job"]
  Add --"download artifacts"--&gt; Prepare
end

subgraph "gitlab-org-sandbox/gitlab-jh-validation"
  Sync["(*optional) sync-as-if-jh-branch job on branch as-if-jh-code-sync"]
  Start["start-as-if-jh job on as-if-jh/* branch"]
  AsIfJH["as-if-jh pipeline"]
end

Mirror --"pull mirror with master and main-jh"--&gt; gitlab-org-sandbox/gitlab-jh-validation
Mirror --"download JiHu files with ADD_JH_FILES_TOKEN"--&gt; Add
Prepare --"push as-if-jh branches with AS_IF_JH_TOKEN"--&gt; Sync
Sync --"push as-if-jh branches with AS_IF_JH_TOKEN"--&gt; Start
Start --&gt; AsIfJH

end

JH --"pull mirror with corresponding JH branches"--> Mirror

프로젝트 변수에 설정된 토큰#
  • ADD_JH_FILES_TOKEN: JiHu 파일을 내려받을 수 있도록 하는, read_api 퍼미션을 가진 GitLab JH mirror 프로젝트 토큰입니다.
  • AS_IF_JH_TOKEN: 생성된 as-if-jh/* 브랜치를 푸시하기 위한, developer 권한과 write_repository 퍼미션을 가진 GitLab JH validation 프로젝트 토큰입니다.
as-if-JH 브랜치 생성 방식#

먼저 add-jh-files job이 대응하는 JH 브랜치에서 필요한 JiHu 파일을 내려받아 아티팩트로 저장합니다. 이어서 prepare-as-if-jh-branch job이 머지 리퀘스트 브랜치에서 새 브랜치를 생성하고 변경 사항을 커밋한 다음, 그 브랜치를 validation 프로젝트에 푸시합니다.

머지 리퀘스트에 의존성 변경 사항이 있는 경우에는 선택적으로, validation 프로젝트의 as-if-jh-code-sync 브랜치 에서 다운스트림 파이프라인을 트리거하는 sync-as-if-jh-branch job을 실행하는 추가 단계가 있습니다. 이 job은 JiHu code-sync와 같은 과정을 수행하여, 검증 파이프라인을 실행하기 전에 의존성 변경 사항이 as-if-jh 브랜치에 반영되도록 합니다.

의존성 변경 사항이 없으면 이 과정을 실행하지 않습니다.

as-if-JH 파이프라인 트리거 및 실행 방식#

as-if-jh/* 브랜치가 준비되고 선택적으로 동기화되면, start-as-if-jh job이 validation 프로젝트에서 파이프라인을 트리거해 프로젝트 간 다운스트림 파이프라인을 실행합니다.

GitLab JH mirror 프로젝트 설정 방식#

GitLab JH mirror 프로젝트는 비공개이며 CI가 비활성화되어 있습니다.

이 프로젝트는 GitLab JH에서 가져오는 풀 미러이며, 모든 브랜치를 미러링하고 분기된 ref를 덮어쓰며, 미러가 업데이트될 때 파이프라인을 트리거하지 않습니다.

가져오기를 수행하는 사용자는 @gitlab-jh-validation-bot이며, 이 프로젝트의 메인테이너입니다. 자격 증명은 1password 엔지니어링 볼트에서 확인할 수 있습니다.

GitLab JH는 공개 프로젝트이므로 미러링에는 비밀번호를 사용하지 않습니다.

GitLab JH validation 프로젝트 설정 방식#

이 GitLab JH validation 프로젝트는 공개이며 CI가 활성화되어 있고 임시 프로젝트 변수가 설정되어 있습니다.

이 프로젝트는 GitLab JH mirror에서 가져오는 풀 미러로, 특정 브랜치((master|main-jh))만 미러링하고 분기된 ref를 덮어쓰며, 미러가 업데이트될 때 파이프라인을 트리거하지 않습니다.

가져오기를 수행하는 사용자는 @gitlab-jh-validation-bot이며, 이 프로젝트의 메인테이너이자 GitLab JH mirror의 메인테이너이기도 합니다. 자격 증명은 1password 엔지니어링 볼트에서 확인할 수 있습니다.

GitLab JH mirror에서 변경 사항을 가져오는 비밀번호로는 write_repository 퍼미션을 가진 @gitlab-jh-validation-bot의 개인 액세스 토큰을 사용합니다. 사용자 이름은 gitlab-jh-validation-bot으로 설정됩니다.

또한 매일 실행되어 캐시를 업데이트하는, 변수 SCHEDULE_TYPE이 maintenance로 설정된 유지 관리 파이프라인을 실행하는 파이프라인 일정도 있습니다.

기본 CI/CD 구성 파일도 jh/.gitlab-ci.yml로 설정되어 있어, GitLab JH와 정확히 동일하게 실행됩니다.

또한 특별한 브랜치 as-if-jh-code-sync 가 설정되어 있고 보호되어 있습니다. 메인테이너는 푸시할 수 있고 개발자는 이 브랜치에 머지할 수 있습니다. 개발자가 이 브랜치에 대한 파이프라인을 트리거할 수 있어야 하기 때문에 개발자가 머지할 수 있도록 설정했습니다. 이는 Developer 권한 사용자가 보호된 브랜치에서 더 이상 파이프라인을 실행할 수 없는 문제를 해결하기 전까지의 타협책입니다.

이 브랜치는 머지 리퀘스트가 의존성을 변경했을 때 의존성을 동기화하는 sync-as-if-jh-branch를 실행하는 데 사용됩니다. 구현 방식은 as-if-JH 브랜치 생성 방식을 참고합니다.

임시 GitLab JH validation 프로젝트 변수
mirror 프로젝트와 validation 프로젝트를 둘 다 두는 이유#

여러 이유로 프로젝트를 분리해 두고 있습니다.

  • 보안: 이전에는 mirror 프로젝트만 있었습니다. 하지만 완전히 완화하기 위해 보안 이슈를, mirror 프로젝트를 비공개로 전환해야 했습니다.

  • 격리: JH 코드는 완전히 격리된 독립 프로젝트에서 실행하고자 합니다. mirror 프로젝트가 속한 gitlab-org 그룹 아래에서 실행하면 안 됩니다. validation 프로젝트는 완전히 격리되어 있습니다.

  • 비용: 머지 리퀘스트마다 JiHuLab.com에 연결하는 것은 원하지 않습니다. JiHuLab.com의 코드를 GitLab.com의 어딘가로 미러링하고, 머지 리퀘스트가 거기서 코드를 가져오게 하는 것이 더 비용 효율적입니다. 즉 validation 프로젝트는 JiHuLab.com이 아니라 mirror에서 코드를 가져올 수 있습니다. mirror 프로젝트는 주기적으로 JiHuLab.com에서 가져옵니다.

  • 브랜치 분리/보안/효율성: JiHuLab.com에서 대응하는 JH 브랜치를 가져올 수 있도록 모든 브랜치를 미러링하고자 합니다. 하지만 validation 프로젝트의 as-if-jh-code-sync 브랜치는 검증 파이프라인을 제어하는 데 사용되고 AS_IF_JH_TOKEN에 접근 권한이 있으므로 덮어쓰고 싶지 않습니다. 다만 특정 브랜치 하나만 제외하고 모든 브랜치를 미러링하는 것은 불가능합니다. 자세한 내용은 이 이슈를 참고합니다.

    이 문제 때문에 validation 프로젝트는 master와 main-jh만 미러링하도록 설정되어 있습니다. 기술적으로는 이 브랜치들이 반드시 필요하지는 않지만, 머지 리퀘스트에서 변경 사항을 푸시할 때 머지 리퀘스트의 변경 사항만 푸시하면 되도록 리포지터리를 모든 기본 브랜치와 최신 상태로 유지하려는 것이며, 이렇게 하면 더 효율적일 수 있습니다.

  • 관심사 분리:

    • validation 프로젝트에는 다음 브랜치만 있습니다.
      • 변경 사항을 최신으로 유지하기 위한 master와 main-jh
      • 의존성 동기화를 위한 as-if-jh-code-sync 이 브랜치는 절대 미러링하지 않습니다.
      • 머지 리퀘스트에서 생성되는 as-if-jh/* 브랜치 이 브랜치들도 절대 미러링하지 않습니다.
    • mirror 프로젝트의 모든 브랜치는 전부 JiHuLab.com에서 옵니다. mirror 프로젝트에는 아무것도 푸시하지 않으며 파이프라인도 실행하지 않습니다. mirror 프로젝트에서는 CI/CD가 비활성화되어 있습니다.

설정과 프로세스를 단순화하기 위해 두 프로젝트를 합치는 것을 고려할 수 있지만, 위에서 언급한 이유들이 더 이상 우려되지 않는지 먼저 확인해야 합니다.

rspec:undercoverage job#

rspec:undercoverage job은 undercover를 실행해, 머지 리퀘스트에서 도입된 변경 사항 중 커버리지가 0인 부분이 있는지 감지하고, 있으면 실패합니다.

rspec:undercoverage job은 rspec:coverage job에서 커버리지 데이터를 가져옵니다.

rspec:undercoverage job이 EE에서 재정의된 CE 메서드로 인해 커버리지 누락을 감지하면, 머지 리퀘스트에 pipeline:run-as-if-foss 레이블을 추가하고 새 파이프라인을 시작합니다.

긴급 상황이거나 이 job에서 오탐이 발생한 경우, 머지 리퀘스트에 pipeline:skip-undercoverage 레이블을 추가해 이 job이 실패해도 되도록 허용합니다.

rspec:undercoverage 실패 문제 해결#

먼저 rspec:coverage job의 gitlab.lcov 아티팩트에서 커버리지 데이터를 확인합니다. rspec:coverage job이 다양한 이유로 커버리지 데이터 수집에 실패했을 수 있습니다.

rspec:undercoverage job이 메서드 호출에 대한 커버리지 부족을 감지하고도 경고를 표시하지 않을 수 있습니다.

loc: app/controllers/projects/attestations_controller.rb:84:102, coverage: 87.5%
  def parsed_attestation_file hits: n/a
    @parsed_attestation_file ||= begin hits: 52
      if attestation && attestation_file hits: 12 branches: 1/1
        Gitlab::Json.parse(attestation_file.read) hits: 10
      else hits: n/a
        {} hits: 2
      end hits: n/a
    rescue JSON::ParserError => e hits: n/a
      Gitlab::AppJsonLogger.error( hits: 2
        message: 'Failed to parse attestation file', hits: n/a
        error_class: e.class.name, hits: n/a
        error_message: e.message, hits: n/a
        attestation_id: attestation&.id, hits: n/a
        project_id: project.id, hits: n/a
        feature_category: 'artifact_security' hits: n/a
      ) hits: n/a
      {} hits: 2
    end hits: n/a
  end hits: n/a

rspec:undercoverage job에는 오탐 실패를 유발할 수 있는 알려진 버그가 있습니다. 너무 오래된 데이터베이스 마이그레이션을 업데이트하는 경우에도 이런 오탐 실패가 발생할 수 있습니다. 로컬에서 커버리지를 테스트해 pipeline:skip-undercoverage를 적용해도 안전한지 확인할 수 있습니다. 예를 들어 실패를 유발하는 테스트 이름을 <spec>이라고 하면 다음과 같습니다.

  1. RUN_ALL_MIGRATION_TESTS=1 SIMPLECOV=1 bundle exec rspec <spec>를 실행합니다.
  2. scripts/undercoverage를 실행합니다.

이 명령이 undercover: ✅ No coverage is missing in latest changes를 반환하면 pipeline:skip-undercoverage를 적용해 파이프라인 실패를 우회할 수 있습니다.

관련 없는 파이프라인 실패를 우회하기 위해 pipeline:skip-undercoverage를 사용해야 했다면, 필요한 테스트 커버리지를 추가하는 후속 MR을 열어주십시오. 이렇게 하면 다른 사람들을 위해 시스템이 더 나은 상태로 유지됩니다.

pajamas_adoption job#

pajamas_adoption job은 머지 리퀘스트에서 Pajamas Adoption Scanner를 실행해 Pajamas Design System 채택의 리그레션을 방지합니다.

이 job은 스캐너가 머지 리퀘스트로 인한 리그레션을 감지하면 실패합니다. 머지 리퀘스트에서 리그레션을 수정할 수 없는 경우, 머지 리퀘스트에 pipeline:skip-pajamas-adoption 레이블을 추가한 다음 job을 재시도합니다.

테스트 스위트 병렬화#

현재 RSpec 테스트 병렬화 설정은 다음과 같습니다.

  1. prepare 스테이지의 retrieve-tests-metadata job이 knapsack/report-master.json 파일이 있는지 확인합니다.
    • knapsack/report-master.json 파일은 update-tests-metadata를 실행하는 최신 main 파이프라인 (현재는 2시간마다 실행되는 maintenance 예약된 master 파이프라인)에서 가져옵니다. 파일이 없으면 {}로 초기화합니다.
  2. 각 [rspec|rspec-ee] [migration|unit|integration|system|geo] n m job은 knapsack rspec으로 실행되며 테스트를 균등하게 나눠 가져야 합니다.
    • "이전 모든 스테이지의 아티팩트는 기본적으로 전달되므로" 각 job이 knapsack/report-master.json에 접근할 수 있어 이 방식이 동작합니다.
    • 각 job은 자신의 리포트 경로를 "knapsack/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"으로 설정합니다.
    • knapsack이 제대로 동작하고 있다면, 실행된 테스트 파일은 Leftover specs가 아니라 Report specs 아래에 나열되어야 합니다.
  3. update-tests-metadata job(canonical 프로젝트의 예약된 파이프라인에서만 실행됩니다)은 다음 두 가지 방식으로 knapsack/report-master.json을 업데이트합니다.
    1. 기본적으로는 모든 knapsack/rspec*.json 파일을 가져와 하나의 knapsack/report-master.json 파일로 합쳐 아티팩트로 저장합니다.
    2. (실험적) AVERAGE_KNAPSACK_REPORT 환경 변수가 true로 설정된 경우, 리포트를 합치는 대신 스펙 순서, 러너 하드웨어 차이, 플레이키 테스트 등 무작위 요인으로 인한 성능 영향을 줄이기 위해 knapsack/report-master.json과 knapsack/rspec*.json 간의 테스트 실행 시간 평균을 계산합니다. 이 실험적 방식은 각 스펙 파일의 실행 시간을 더 잘 예측해 병렬 job 간에 부하를 더 균등하게 분배함으로써 job들이 비슷한 시점에 끝나도록 하는 것을 목표로 합니다.

이후 다음 파이프라인은 최신 knapsack/report-master.json 파일을 사용합니다.

코드 커버리지#

테스트 선택, 커버리지 분석, 플레이키 테스트 분석에 활용하기 위해 테스트 스위트에서 코드 커버리지 데이터를 수집합니다.

커버리지 유형#

유형 수집 방법 테스트
백엔드 SimpleCov → LCOV RSpec
백엔드 E2E Coverband E2E 스펙
프론트엔드 Istanbul Jest
프론트엔드 E2E Istanbul E2E 스펙
Workhorse Go coverage Go 테스트

CI job#

커버리지 데이터는 여러 CI job을 거쳐 흐릅니다.

  1. 수집: 커버리지 계측과 함께 테스트를 실행합니다

    • rspec job은 SimpleCov를 통해 백엔드 커버리지를 수집합니다
    • jest job은 Istanbul을 통해 프론트엔드 커버리지를 수집합니다
    • e2e:test-on-cng는 Coverband(백엔드)와 Istanbul(프론트엔드)을 통해 E2E 커버리지를 수집합니다
    • workhorse job은 Go 커버리지를 수집합니다
  2. 병합: 병렬 job과 E2E의 커버리지를 병합합니다

    • rspec:coverage가 RSpec 커버리지를 coverage/lcov/gitlab.lcov로 병합합니다
    • coverage-frontend가 Jest 커버리지를 병합합니다
    • 병합 스크립트가 E2E 커버리지를 유닛/통합 커버리지와 결합합니다
  3. 내보내기: 병합된 커버리지를 ClickHouse로 내보냅니다

    • test-coverage:export-rspec-and-e2e가 백엔드 커버리지를 내보냅니다
    • test-coverage:export-jest-and-e2e가 프론트엔드 커버리지를 내보냅니다
    • test-coverage:export-workhorse가 Workhorse 커버리지를 내보냅니다

커버리지 수집, 데이터 흐름, ClickHouse 저장에 대한 자세한 문서는 코드 커버리지를 참고합니다.

플레이키 테스트 재시도와 격리#

실패한 테스트 자동 재시도#

실패한 백엔드 테스트는 별도의 RSpec 프로세스에서 한 번 자동으로 재시도됩니다. 이는 같은 커밋 SHA에서 실패했다가 통과하는 테스트로 정의되는 플레이키 테스트를 감지하는 데 도움이 됩니다.

"새 RSpec 프로세스에서 실패한 테스트 재시도"는 $RETRY_FAILED_TESTS_IN_NEW_PROCESS 변수를 false로 설정해 비활성화할 수 있습니다.

격리된 테스트#

GitLab CI 파이프라인은 조사 중인 동안 파이프라인을 막고 있는 테스트를 건너뛰기 위해 빠른 격리(fast quarantine)를 사용합니다.

빠른 격리 프로세스는 $FAST_QUARANTINE 변수를 false로 설정해 비활성화할 수 있습니다.

격리 절차와 문법은 테스트 격리와 격리 프로세스 핸드북을 참고합니다.

호환성 테스트#

기본적으로 모든 테스트는 GitLab.com에서 사용하는 버전으로 실행합니다.

다른 버전(일반적으로 이전 호환 버전 하나와 이후 호환 버전 하나)은 nightly 예약된 파이프라인에서 실행해야 합니다.

이 일반 가이드라인에 대한 예외는 근거를 밝히고 문서화해야 합니다.

Ruby 버전 테스트#

GitLab.com과 기본 브랜치 모두에서 Ruby 3.3을 실행하고 있습니다. 머지 리퀘스트 파이프라인도 다른 버전을 선택하는 레이블을 추가하지 않는 한 기본 버전으로 실행됩니다.

다음 Ruby 버전을 준비하기 위해 ruby-next 브랜치에서 2시간마다(홀수 시각마다) 예약된 파이프라인을 실행합니다. 자세한 로드맵은 Ruby 3.4 에픽을 참고합니다.

머지 리퀘스트에서는 다음 레이블 중 하나를 추가해 파이프라인이 실행할 Ruby 버전을 바꿀 수 있습니다.

  • pipeline:run-in-ruby3_3: 머지 리퀘스트 파이프라인을 기본 Ruby 버전으로 유지합니다. 이후 기본 Ruby 버전이 바뀌면 이 레이블도 새 기본 버전을 따라갑니다. 두 레이블이 함께 있으면 pipeline:run-with-ruby-next보다 우선합니다.
  • pipeline:run-with-ruby-next: RUBY_VERSION_NEXT CI 변수에 지정된 다음 Ruby 버전으로 머지 리퀘스트 파이프라인을 실행합니다. Rails에 대해 pipeline:run-with-rails-next가 동작하는 방식과 같습니다. RUBY_VERSION_NEXT를 올리는 머지 리퀘스트에는 이 레이블을 추가해, 머지되기 전에 새 버전이 검증되도록 합니다. 머지 트레인 파이프라인은 이 레이블과 무관하게 항상 기본 Ruby 버전으로 실행됩니다. 이 레이블은 커뮤니티 기여, 봇이 작성한 머지 리퀘스트, 안정 브랜치를 대상으로 하는 머지 리퀘스트처럼 더 먼저 평가되는 워크플로 규칙에 매칭되는 머지 리퀘스트에는 영향을 주지 않습니다. pipeline:run-with-rails-next의 규칙도 먼저 평가되므로, 두 레이블을 모두 가진 머지 리퀘스트는 기본 Ruby 버전을 사용하게 됩니다. 반면 database 레이블 규칙은 이 규칙 이후에 평가되므로, 두 레이블을 모두 가진 머지 리퀘스트는 다음 Ruby 버전으로 실행되며 QUERY_LOG_LINE을 받지 않습니다.

PostgreSQL 버전 테스트#

테스트 스위트는 GitLab.com이 PostgreSQL 16에서 실행되고 Omnibus는 신규 설치와 업그레이드에서 기본적으로 PG14를 사용하기 때문에 PostgreSQL 16을 대상으로 실행됩니다.

nightly 예약된 파이프라인에서는 PostgreSQL 16, 17, 18을 대상으로 테스트 스위트를 실행합니다.

Note

PG17이 추가되면서 nightly job의 한도에 가까워지고 있으며, 파이프라인당 2000개 job 중 1946개를 사용하고 있습니다. 새 job 계열을 추가하면 nightly 파이프라인이 실패할 수 있습니다.

현재 버전 테스트 현황#

위치? PostgreSQL 버전 Ruby 버전
머지 리퀘스트 17(기본 버전) 3.3(기본 버전)
master 브랜치 커밋 17(기본 버전) 3.3(기본 버전)
master 브랜치의 maintenance 예약된 파이프라인(짝수 시각 XX:05마다) 17(기본 버전) 3.3(기본 버전)
ruby-next 브랜치의 maintenance 예약된 파이프라인(홀수 시각 XX:10마다) 17(기본 버전) 3.4
master 브랜치의 nightly 예약된 파이프라인 17(기본 버전), 16, 18 3.3(기본 버전)
master 브랜치의 weekly 예약된 파이프라인 17(기본 버전) 3.3(기본 버전)

테스트 대상인 다음 Ruby 버전을 위해, ruby-next 브랜치에서 2시간마다 maintenance 예약된 파이프라인을 실행합니다. ruby-next에는 어떤 변경 사항도 있으면 안 됩니다. 이 브랜치는 예약된 maintenance 파이프라인에서 다른 Ruby 버전으로 파이프라인을 실행하기 위한 용도로만 존재합니다.

ruby-sync 브랜치#

ruby-sync 브랜치는 ruby-next와 rails-next 브랜치를 master와 최신 상태로 유지합니다. 이 브랜치는 (master에서 파생되지 않은) **고아 브랜치(orphan branch)**로, 자체 .gitlab-ci.yml, scripts/slack 헬퍼, README.md만 포함합니다.

예약된 파이프라인이 ruby-sync에서 2시간마다 실행됩니다. gitlab job은 다음을 수행합니다.

  1. 전체 gitlab-org/gitlab 리포지터리를 클론합니다.
  2. ruby-next와 rails-next 각각에 대해 해당 브랜치를 체크아웃하고 origin/master를 머지한 다음 결과를 푸시합니다.

이 푸시로는 어떤 다운스트림 파이프라인도 트리거되지 않습니다. ruby-next와 rails-next 브랜치는 각자 독립적으로 예약된 maintenance 파이프라인을 실행합니다.

인증#

gitlab job은 write_repository 스코프와 Maintainer 권한을 가진 프로젝트 토큰(RUBY_SYNC_TOKEN)으로 인증합니다. 이 토큰은 ruby-sync 브랜치의 파이프라인 일정 변수에 저장되어 있습니다.

재시도 및 실패 알림#

gitlab job은 일시적인 오류를 처리하기 위해 스크립트가 실패하면 한 번 재시도합니다. 그래도 실패하면 notify job이 CI_SLACK_WEBHOOK_URL을 통해 #backend Slack 채널(사용자 이름 ruby-sync)로 메시지를 보냅니다.

☠️ ruby-sync failed to merge master into the ruby-next/rails-next branches
in gitlab-org/gitlab. Pipeline: <pipeline_url> — Docs: <docs_url>
ruby-sync 실패 문제 해결#

흔한 실패 원인은 다음과 같습니다.

  • 일시적인 GitLab 부하 오류: 이 job은 전체 리포지터리를 클론하므로 부하가 높은 시간대에는 실패할 수 있습니다. 실패한 파이프라인을 재시도합니다.
  • 머지 충돌: ruby-next 또는 rails-next가 master와 충돌을 일으키는 방식으로 벌어져 있으면 git merge 단계가 실패합니다. 영향을 받은 브랜치에서 충돌을 수동으로 해결한 다음 재시도합니다.
  • 인증 오류: RUBY_SYNC_TOKEN 프로젝트 토큰이 만료되지 않았는지 확인합니다. 파이프라인 일정 구성을 확인합니다.

재시도하려면 파이프라인 일정 페이지로 이동해 ruby-sync 일정을 실행하거나, 파이프라인 페이지에서 실패한 job을 재시도합니다.

Redis 버전 테스트#

테스트 스위트는 기본적으로 GitLab 설치의 권장 버전인 Redis 7.2를 대상으로 실행됩니다. Weekly 예약된 파이프라인에서는 지원 범위 전체의 호환성을 검증하기 위해 Redis 7.0(최소 버전)과 Valkey 7.2도 함께 테스트합니다.

현재 버전 테스트 현황#

위치? Redis 버전
MR 7.2
default branch(예약되지 않은 파이프라인) 7.2
nightly 예약된 파이프라인 7.2
weekly 예약된 파이프라인 7.0(최소), Valkey 7.2

단일 데이터베이스 테스트#

기본적으로 모든 테스트는 다중 데이터베이스로 실행됩니다.

nightly 예약된 파이프라인과 데이터베이스 관련 파일을 다루는 머지 리퀘스트에서는 단일 데이터베이스로도 테스트를 실행합니다.

단일 데이터베이스 테스트는 두 가지 모드로 실행됩니다.

  1. 단일 연결의 단일 데이터베이스. GitLab이 하나의 커넥션 풀로 모든 테이블에 연결합니다. -single-db로 끝나는 모든 job에서 실행됩니다
  2. 두 연결의 단일 데이터베이스. GitLab이 gitlab_main, gitlab_ci 데이터베이스 테이블에 서로 다른 데이터베이스 연결로 접속합니다. -single-db-ci-connection으로 끝나는 모든 job에서 실행됩니다.

테스트를 강제로 단일 데이터베이스로 실행하려면 머지 리퀘스트에 pipeline:run-single-db 레이블을 추가할 수 있습니다.

Elasticsearch·OpenSearch 버전 테스트#

특정 조건을 만족하면 GitLab.com이 Elasticsearch 9에서 실행되므로, 테스트 스위트도 Elasticsearch 9를 대상으로 실행됩니다.

nightly 예약된 파이프라인에서는 Elasticsearch 8, 9와 OpenSearch 1, 2를 대상으로 테스트 스위트를 실행합니다. 모든 테스트 스위트는 데이터베이스와 검색 백엔드 간에 의존성이 없으므로 PostgreSQL 17을 사용합니다.

위치? Elasticsearch 버전 OpenSearch 버전 PostgreSQL 버전
~group::global search 또는 ~pipeline:run-search-tests 레이블이 있는 머지 리퀘스트 9.X(production) 17(기본 버전)
master 브랜치의 nightly 예약된 파이프라인 7.X, 9.X(production) 1.X, 2.X 17(기본 버전)
master 브랜치의 weekly 예약된 파이프라인 8.X latest 17(기본 버전)

모니터링#

GitLab 테스트 스위트는 main 브랜치와 이름에 rspec-profile이 포함된 모든 브랜치에 대해 모니터링됩니다.

로깅#

  • CI에서는 성능상의 이유로 log/test.log로의 Rails 로깅이 기본적으로 비활성화되어 있습니다. 이 설정을 재정의하려면 RAILS_ENABLE_TEST_LOG 환경 변수를 지정합니다.

CI 구성 내부 구조#

자세한 내용은 전담 CI 구성 내부 구조 페이지를 참고합니다.

성능#

자세한 내용은 전담 CI 구성 성능 페이지를 참고합니다.


개발 문서로 돌아가기

GitLab 프로젝트 파이프라인

GitLab v19.4
원문 보기

요약

gitlab-org/gitlab(및 dev 인스턴스)의 파이프라인은 일반적인 .gitlab-ci.yml에 구성되어 있으며, 유지 관리를 쉽게 하기 위해 .gitlab/ci/ 아래의 파일들을 포함합니다. 가능한 한 GitLab CI/CD 기능과 모범 사례를 dogfood하기 위해 노력하고 있습니다.

gitlab-org/gitlab(및 dev 인스턴스)의 파이프라인은 일반적인 .gitlab-ci.yml에 구성되어 있으며, 유지 관리를 쉽게 하기 위해 .gitlab/ci/ 아래의 파일들을 포함합니다.

가능한 한 GitLab CI/CD 기능과 모범 사례를 dogfood하기 위해 노력하고 있습니다.

dev.gitlab.com 인스턴스에 미러링되어 있지 않은 CI/CD 컴포넌트는 gitlab-org/gitlab 파이프라인에서 사용하지 않습니다. CI/CD 컴포넌트는 서로 다른 인스턴스 간에는 동작하지 않으며, 해당 인스턴스에 존재하지 않으면 dev.gitlab.com 미러에서 파이프라인 실패를 유발합니다.

파이프라인 티어#

머지 리퀘스트는 일반적으로 여러 CI/CD 파이프라인을 실행합니다. 머지 리퀘스트가 승인 프로세스에서 어느 단계에 있는지에 따라 서로 다른 종류의 파이프라인이 트리거됩니다. 이러한 파이프라인 종류를 파이프라인 티어라고 합니다.

현재 세 가지 티어가 있습니다.

  1. pipeline::tier-1: 머지 리퀘스트에 승인이 없는 경우
  2. pipeline::tier-2: 머지 리퀘스트에 승인이 하나 이상 있지만 아직 더 필요한 경우
  3. pipeline::tier-3: 머지 리퀘스트가 필요한 승인을 모두 받은 경우

일반적으로 파이프라인 티어가 낮을수록 파이프라인은 더 빨라야 합니다. 파이프라인 티어가 높을수록 더 많은 테스트를 실행해 더 큰 확신을 줘야 합니다.

구현에 대한 자세한 내용은 MR 파이프라인에 "티어" 도입 에픽을 참고합니다.

머지 리퀘스트 승인 전 예측 테스트 job#

파이프라인 비용을 줄이고 job 실행 시간을 단축하기 위해, 머지 리퀘스트가 승인되기 전에는 파이프라인이 해당 변경 사항에서 실패할 가능성이 있는 RSpec·Jest 테스트를 예측해서 실행합니다.

머지 리퀘스트가 승인된 후에는 파이프라인에 전체 RSpec·Jest 테스트가 포함됩니다. 이를 통해 머지 리퀘스트가 머지되기 전에 모든 테스트가 실행되도록 보장합니다.

GitLab 프로젝트 테스트 의존성 개요#

예측 테스트 job이 어떻게 실행되는지 이해하려면, GitLab 코드(프론트엔드와 백엔드)와 그에 대응하는 테스트(Jest와 RSpec) 간의 의존성을 이해해야 합니다. 이 의존성은 다음 다이어그램으로 나타낼 수 있습니다.

Mermaid 다이어그램 (10줄)
소스 코드 보기
flowchart LR
    subgraph frontend
    fe["Frontend code"]--tested with-->jest
    end
    subgraph backend
    be["Backend code"]--tested with-->rspec
    end
be--generates--&gt;fixtures["frontend fixtures"]
fixtures--used in--&gt;jest</code></pre></details></div>

요약하면 다음과 같습니다.

  • RSpec 테스트는 백엔드 코드에 의존합니다.
  • Jest 테스트는 프론트엔드 코드와 백엔드 코드 양쪽에 의존하며, 백엔드 코드에는 프론트엔드 픽스처를 통해 의존합니다.

detect-tests CI job#

gitlab-org/gitlab의 대부분의 CI/CD 파이프라인은 prepare 스테이지에서 detect-tests CI job을 실행해, 해당 MR에서 변경된 파일을 기준으로 실행할 백엔드/프론트엔드 테스트를 감지합니다.

detect-tests job은 실행할 백엔드/프론트엔드 테스트를 담은 여러 파일을 생성합니다. 이 파일들은 파이프라인의 이후 job에서 읽히며, 해당 테스트만 실행됩니다.

RSpec 예측 job#

머지 리퀘스트에서 예측 RSpec 테스트 파일 결정#

머지 리퀘스트에서 실패할 가능성이 있는 RSpec 테스트를 식별하기 위해 동적 매핑과 정적 매핑을 사용합니다.

동적 매핑#

먼저 test_file_finder gem을 사용하며, 동적 매핑 전략은 Crystalball gem에서 가져옵니다 (사용되는 위치 참고, Crystalball에서 사용하는 매핑 전략).

test_file_finder 외에도 실행할 테스트를 더 많이 감지하기 위해 여러 고급 매핑을 추가했습니다.

  • FindChanges (!74003)
    • 백엔드 변경 시 실행할 Jest 테스트를 (프론트엔드 픽스처를 통해) 자동으로 감지합니다
  • PartialToViewsMappings (#395016)
    • MR에서 뷰에 포함된 Rails partial이 변경되면 해당 뷰 스펙을 실행합니다
  • JsToSystemSpecsMappings (#386754)
    • MR에서 JavaScript 파일이 변경되면 특정 시스템 스펙을 실행합니다
  • GraphqlBaseTypeMappings (#386756)
    • GraphQL 타입 클래스가 변경되면, 이 타입을 포함할 수 있는 다른 GraphQL 타입을 찾아 해당 스펙을 실행합니다.
  • ViewToSystemSpecsMappings (#395017)
    • 뷰가 변경되면 해당 코드 영역을 테스트하는 feature 스펙을 찾습니다.
  • ViewToJsMappings (#386719)
    • JS 파일이 변경되면 해당 JS 컴포넌트를 다루는 시스템 스펙을 찾습니다.
  • FindFilesUsingFeatureFlags (#407366)
    • 기능 플래그가 변경되면 해당 플래그를 포함하는 Ruby 파일을 확인해 detect-tests CI job의 변경 파일 목록에 추가합니다. 이후 job의 나머지 부분에서는 이 변경 파일을 기준으로 실행할 프론트엔드/백엔드 테스트를 감지합니다.
정적 매핑#

동적 매핑으로 매핑할 수 없는 특수한 경우를 위해, test_file_finder gem과 tests.yml 파일에서 관리되는 정적 매핑을 사용합니다 (사용되는 위치 참고).

테스트 매핑에는 각 소스 파일과 그 소스 파일에 의존하는 테스트 파일 목록 간의 매핑이 담겨 있습니다.

예외적인 경우#

또한 항상 전체 RSpec 테스트를 실행하는 몇 가지 경우가 있습니다.

  • 머지 리퀘스트에 pipeline:run-all-rspec 레이블이 설정된 경우. 이 레이블은 as-if-foss job에서 실행되는 테스트를 포함해 모든 RSpec 테스트를 트리거합니다.
  • 머지 리퀘스트에 pipeline:mr-approved 레이블이 설정되어 있고 코드 변경 사항이 backend-patterns 규칙을 충족하는 경우. 이 레이블은 리뷰어가 머지 리퀘스트를 승인하면 트리아지 자동화가 부여하는 것이며, 수동으로 적용하는 것은 권장하지 않습니다.
  • 머지 리퀘스트가 자동화(예: Gitaly 업데이트나 안정 브랜치를 대상으로 하는 MR)에 의해 생성된 경우
  • 머지 리퀘스트가 보안 미러에서 생성된 경우
  • CI 구성 파일이 변경된 경우(예: .gitlab-ci.yml 또는 .gitlab/ci/**/*)

백엔드 예측 테스트 문제 대응#

예측 테스트 문제에 대응하는 방법은 예측 테스트에 대한 Development Analytics RUNBOOK을 참고합니다. 또한 테스트 선택에서 빠진 부분을 발견했다면 @gl-dx/development-analytics에 알려주면, 테스트 선택을 최적화하기 위한 필요한 조치를 취할 수 있습니다.

GitLab Duo 시스템 테스트 선택#

tier-2 파이프라인에서는 GitLab Duo가 MR 변경 사항과 관련된 시스템 스펙을 예측합니다. 이는 detect-tests job에 더해 실행되며, 시스템 스펙(spec/features/, ee/spec/features/)에 특화되어 있습니다.

동작 방식#

detect-system-tests-duo job은 MR diff를 대상으로 GitLab Duo CLI를 호출합니다. 그런 다음 PreparePredictiveSystemPipeline 스크립트가 그 출력을 처리해 파이프라인 입력을 준비합니다.

  • GitLab Duo가 확신하는 경우: GitLab Duo의 예측이 detect-tests가 선택한 시스템 테스트와 합쳐집니다(합집합). 예측 파이프라인은 이 합쳐진 테스트 집합을 실행합니다. GitLab Duo가 시스템 테스트가 필요 없다고 예측하면 detect-tests가 선택한 테스트만 실행됩니다.
  • GitLab Duo가 확신하지 못하거나 실패한 경우: 예측 파이프라인에서 시스템 테스트가 제외됩니다. 전체 시스템 테스트 스위트는 별도의 하위(child) 파이프라인(rspec-predictive-system-full)에서 실행됩니다.

범위#

GitLab Duo 시스템 테스트 선택은 gitlab-org/gitlab의 tier-2 파이프라인에서만 실행되며, 다음 경우에는 건너뜁니다.

  • pipeline:run-all-rspec 레이블이 설정된 경우(전체 스위트가 이미 실행됩니다).
  • pipeline:spec-only 레이블이 설정된 경우(detect-tests가 이미 스펙 파일을 직접 식별합니다).
  • 변경 사항이 core-backend 또는 workhorse 패턴과 일치하는 경우(이러한 변경 사항에 대해서는 이미 메인 파이프라인에서 모든 시스템 테스트가 실행됩니다).

tier-3 파이프라인에서는 이 job이 지표 수집을 위해 실행되지만 그 출력은 테스트 선택에 영향을 주지 않습니다.

코드를 변경하지 않고 GitLab Duo 시스템 테스트 선택을 비활성화하려면 GLCI_DUO_SYSTEM_TESTS_DISABLED CI/CD 프로젝트 변수를 "true"로 설정합니다. 비활성화하면 detect-system-tests-duo job이 건너뛰어지고, tier-2에서는 대체 동작으로 전체 시스템 테스트 스위트가 실행됩니다.

Jest 예측 job#

머지 리퀘스트에서 예측 Jest 테스트 파일 결정#

머지 리퀘스트에서 실패할 가능성이 있는 jest 테스트를 식별하기 위해, 변경된 모든 파일의 목록을 --findRelatedTests 옵션을 사용해 jest에 전달합니다. 이 모드에서 jest는 변경된 파일과 관련된 모든 의존성을 해석하며, 여기에는 해당 파일을 의존성 체인에 포함하는 테스트 파일도 포함됩니다.

예외적인 경우#

또한 항상 전체 Jest 테스트를 실행하는 몇 가지 경우가 있습니다.

  • 머지 리퀘스트에 pipeline:run-all-jest 레이블이 설정된 경우
  • 머지 리퀘스트가 자동화(예: Gitaly 업데이트나 안정 브랜치를 대상으로 하는 MR)에 의해 생성된 경우
  • 머지 리퀘스트가 보안 미러에서 생성된 경우
  • 관련 CI 구성 파일이 변경된 경우(.gitlab/ci/rules.gitlab-ci.yml, .gitlab/ci/frontend.gitlab-ci.yml)
  • 프론트엔드 의존성 파일이 변경된 경우(예: package.json, yarn.lock, config/webpack.config.js, config/helpers/**/*.js)
  • 벤더링된 JavaScript 파일이 변경된 경우(예: vendor/assets/javascripts/**/*)

전체 Jest 테스트에 대한 rules 정의는 rules.gitlab-ci.yml의 .frontend:rules:jest에 정의되어 있습니다.

프론트엔드 예측 테스트 문제 대응#

예측 테스트 문제에 대응하는 방법은 예측 테스트에 대한 Development analytics RUNBOOK을 참고합니다.

포크 파이프라인#

포크 파이프라인에서는 MR에 pipeline:run-all-rspec 레이블이 설정되지 않은 경우 예측 RSpec·Jest job만 실행합니다. 목적은 포크 파이프라인이 소비하는 컴퓨팅 할당량을 줄이는 것입니다.

자세한 내용은 실험 이슈를 참고합니다.

머지 리퀘스트 파이프라인의 Fail-fast job#

머지 리퀘스트가 기존 테스트를 망가뜨렸을 때 더 빠르게 피드백을 제공하기 위해 fail-fast 메커니즘을 구현했습니다.

머지 리퀘스트 파이프라인에서는 다른 모든 rspec job과 병렬로 rspec fail-fast job이 추가됩니다. 이 job은 머지 리퀘스트의 변경 사항과 직접 관련된 테스트를 실행합니다.

이 테스트 중 하나라도 실패하면 rspec fail-fast job이 실패하고, fail-pipeline-early job이 트리거되어 실행됩니다. fail-pipeline-early job은 다음을 수행합니다.

  • 현재 실행 중인 파이프라인과 진행 중인 모든 job을 취소합니다.
  • 파이프라인 상태를 failed로 설정합니다.

예를 들면 다음과 같습니다.

Mermaid 다이어그램 (20줄)
소스 코드 보기
graph LR
    subgraph "prepare stage";
        A["detect-tests"]
    end
subgraph "test stage";
    B["jest"];
    C["rspec migration"];
    D["rspec unit"];
    E["rspec integration"];
    F["rspec system"];
    G["rspec fail-fast"];
end

subgraph "post-test stage";
    Z["fail-pipeline-early"];
end

A --"artifact: list of test files"--&gt; G
G --"on failure"--&gt; Z</code></pre></details></div>

머지 리퀘스트와 관련된 테스트 파일이 10개를 초과하면 rspec fail-fast는 아무 동작도 하지 않습니다. 이는 rspec fail-fast의 실행 시간이 평균적인 rspec job 실행 시간을 초과해 원래 목적을 무력화하는 것을 막기 위한 것입니다.

이 숫자는 RSPEC_FAIL_FAST_TEST_FILE_COUNT_THRESHOLD라는 이름의 CI/CD 변수를 설정해 재정의할 수 있습니다.

머지 리퀘스트 파이프라인에서 이전에 실패한 테스트 재실행#

머지 리퀘스트의 실패한 테스트를 해결한 후 피드백 시간을 줄이기 위해, rspec rspec-pg17-rerun-previous-failed-tests 와 rspec rspec-ee-pg17-rerun-previous-failed-tests job이 이전 MR 파이프라인에서 실패한 테스트를 실행합니다.

이 기능은 2021년 8월 25일에 https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69053과 함께 도입되었습니다.

실패한 테스트가 재실행되는 방식#

  1. detect-previous-failed-tests job(prepare 스테이지)이 이전 MR 파이프라인에서 실패한 RSpec job과 관련된 테스트 파일을 감지합니다.
  2. rspec rspec-pg17-rerun-previous-failed-tests와 rspec rspec-ee-pg17-rerun-previous-failed-tests job 이 detect-previous-failed-tests job이 수집한 테스트 파일을 실행합니다.
Mermaid 다이어그램 (11줄)
소스 코드 보기
graph LR
    subgraph "prepare stage";
        A["detect-previous-failed-tests"]
    end
subgraph "test stage";
    B["rspec rspec-pg17-rerun-previous-failed-tests"];
    C["rspec rspec-ee-pg17-rerun-previous-failed-tests"];
end

A --"artifact: list of test files"--&gt; B &amp; C</code></pre></details></div>

머지 트레인#

현재 사용 현황#

2024년 6월부터 머지 트레인을 사용하기 시작했습니다.

현재 머지 트레인 파이프라인은 어떤 테스트도 실행하지 않으며, 머지 트레인 도입 이전부터 있었지만 쉽게 강제할 수 없었던 "머지 리퀘스트 머지" 가이드라인만을 강제합니다.

머지 트레인 파이프라인은 머지 전 최신 파이프라인이 다음 조건을 만족하는지 확인하는 단일 pre-merge-checks job을 실행합니다.

  1. 머지 결과(Merged Results) 파이프라인일 것
  2. (예측 파이프라인이 아닌) 전체 파이프라인인 tier-3 파이프라인일 것
  3. 생성된 지 16시간 이내(안정 브랜치는 72시간 이내)일 것

이 방식을 개선하기 위해 피드백 이슈를 개설했습니다.

다음 반복 개선#

머지 트레인 파이프라인에서 실제로 테스트를 실행하기 시작하기 위해, 머지 트레인의 다음 개선 방향을 논의하는 전담 이슈를 개설했습니다.

머지 트레인에서 "전체" 테스트 파이프라인을 실행하기 위한 과제#

"안정적인" 기본 브랜치가 필요한 이유#

기본 브랜치가 불안정하면(예: 기본 브랜치의 CI/CD 파이프라인이 자주 실패하는 경우), 문제가 있는 머지 리퀘스트 파이프라인 이후에 추가된 모든 머지 리퀘스트 파이프라인을 취소하고 트레인에 다시 추가해야 하며, 머지 트레인이 길면 이로 인해 지연이 크게 발생합니다.

기본 브랜치가 얼마나 안정적이어야 하는가#

구체적인 수치는 없지만, 플레이키 테스트 실패와 인프라 실패에 대한 더 나은 수치가 필요합니다(Master Broken Incidents RCA 대시보드 참고).

일부 머지 리퀘스트의 더 빠른 피드백#

손상된 master 수정#

손상된 master를 수정해야 할 때는, 머지 리퀘스트에서 실행되는 파이프라인을 신속하게 처리하기 위해 pipeline::expedited 레이블을 추가할 수 있습니다.

이때 머지 리퀘스트에는 master:broken 또는 master:foss-broken 레이블도 설정되어 있어야 합니다.

되돌리기(Revert) MR#

되돌리기 MR을 더 빠르게 만들려면 머지 리퀘스트를 생성하기 전에 되돌리기 MR 템플릿을 사용합니다. 이 템플릿은 pipeline::expedited 레이블 등을 적용해 머지 리퀘스트에서 실행되는 파이프라인을 신속하게 처리합니다.

pipeline::expedited 레이블#

이 레이블이 부여되면 CI/CD 파이프라인의 다음 단계를 건너뜁니다.

  • e2e:test-on-omnibus-ee job
  • rspec:undercoverage job

머지 리퀘스트에 레이블을 적용하고 해당 MR에 대해 새 파이프라인을 실행합니다.

테스트 job#

각 테스트 레벨마다 전담 job이 있으며, 각 job은 머지 리퀘스트에서 이루어진 변경 사항에 따라 실행됩니다. 변경 사항과 무관하게 모든 RSpec job을 강제로 실행하려면 머지 리퀘스트에 pipeline:run-all-rspec 레이블을 추가할 수 있습니다.

Warning

문서 전용 MR에서 모든 job을 강제로 실행하면 필요한 선행 job이 없어 오류가 발생합니다

gitaly-mvcc RSpec job은 MVCC 스토리지 백엔드를 사용하는 Gitaly 서버를 대상으로 테스트 스위트를 실행합니다. 이 job은 자동으로 실행되지 않습니다. 모두 실행하려면 머지 리퀘스트에 pipeline:run-gitaly-mvcc 레이블을 추가하고 새 파이프라인을 시작합니다. (gitlab-org/gitaly의 rails-specs 트리거처럼) 프로젝트 간 트리거되는 파이프라인에서는 대신 ENABLE_RSPEC_GITALY_MVCC=true를 설정합니다.

엔드투엔드 job#

자세한 내용은 엔드투엔드 테스트 파이프라인을 참고합니다.

As-if-FOSS job과 프로젝트 간 다운스트림 파이프라인#

관련 변경 사항이 FOSS 프로젝트에서 올바르게 동작하는지 확인하기 위해, 특정 조건에서는 다음도 함께 실행합니다.

  • 같은 파이프라인의 * as-if-foss job
  • 프로젝트 간 다운스트림 FOSS 파이프라인

* as-if-foss job은 마치 gitlab-org/gitlab-foss의 컨텍스트에서 실행되는 것처럼 GitLab 테스트 스위트를 "FOSS인 것처럼" 실행합니다. 반면 프로젝트 간 다운스트림 FOSS 파이프라인은 실제로 FOSS 프로젝트 내부에서 실행되므로, 실제 FOSS 환경에 더 가깝습니다.

다음의 경우에 이를 실행합니다.

  • 머지 리퀘스트에 pipeline:run-as-if-foss 레이블이 설정된 경우
  • 머지 리퀘스트에 pipeline:as-if-foss-run-predictive 레이블이 설정된 경우 (FOSS 파이프라인에서 예측 테스트, RuboCop, eslint, 정적 분석만 실행합니다)
  • 머지 리퀘스트가 gitlab-org/security/gitlab 프로젝트에서 생성된 경우
  • CI 구성 파일이 변경된 경우(예: .gitlab-ci.yml 또는 .gitlab/ci/**/*)

* as-if-foss job은 일반적인 EE 컨텍스트 job에 더해 실행됩니다. 이 job에는 FOSS_ONLY='1' 변수가 설정되며, 테스트가 시작되기 전에 ee/ 폴더가 제거됩니다.

프로젝트 간 다운스트림 FOSS 파이프라인은 대신 머지 리퀘스트를 FOSS 프로젝트의 기본 브랜치에 머지하는 것을 시뮬레이션하며, 이때 특정 파일 목록이 제거됩니다. 그 목록은 .gitlab/ci/as-if-foss.gitlab-ci.yml 과 merge-train/bin/merge-train에서 확인할 수 있습니다.

이는 gitlab-org/gitlab이 gitlab-org/gitlab-foss에 동기화된 후에도 변경 사항이 실패를 유발하지 않도록 하기 위한 것입니다.

프로젝트 변수에 설정된 토큰#

  • AS_IF_FOSS_TOKEN: 생성된 as-if-foss/* 브랜치를 푸시하기 위한, developer 권한과 write_repository 퍼미션을 가진 GitLab FOSS 프로젝트 토큰입니다.
    • 보안 프로젝트에서는 이름은 같지만 보안 FOSS 프로젝트의 다른 토큰을 사용해야 합니다. 이렇게 하면 보안 변경 사항이 공개 프로젝트로 푸시되는 일이 없습니다.

As-if-JH 프로젝트 간 다운스트림 파이프라인#

개요#

이 파이프라인은 JiHu 검증 파이프라인이라고도 하며, 현재는 실패해도 허용됩니다. 실패했을 때는 검증 파이프라인이 실패했을 때 해야 할 일을 따릅니다.

실행 방식#

start-as-if-jh job은 프로젝트 간 다운스트림 파이프라인을 트리거해, 마치 GitLab JH의 컨텍스트에서 실행되는 것처럼 GitLab 테스트 스위트를 "JiHu인 것처럼" 실행합니다. 이 job은 다음의 경우에만 생성됩니다.

  • 기능 플래그에 변경이 있는 경우
  • 머지 리퀘스트에 pipeline:run-as-if-jh 레이블이 설정된 경우

이 파이프라인은 GitLab JH mirror의 미러인 GitLab JH validation 프로젝트에서 생성된 브랜치의 컨텍스트로 실행됩니다.

생성되는 브랜치 이름은 머지 리퀘스트의 브랜치 이름 앞에 as-if-jh/가 붙습니다. 이 브랜치는 머지 리퀘스트 브랜치를 기반으로 하며, 전체 파이프라인을 JiHu인 것처럼 만들기 위해 대응하는 JH 브랜치에서 내려받은 변경 사항을 추가로 반영합니다.

이는 GitLab이 GitLab JH에 동기화된 후에도 변경 사항이 실패를 유발하지 않도록 하기 위한 것입니다.

pipeline:run-as-if-jh 레이블 적용을 고려해야 하는 경우#

Ruby 파일 이름이 변경되고 그에 대응하는 prepend_mod 줄이 있다면, GitLab JH가 이를 의존하고 있을 가능성이 크므로 prepend하는 모듈이나 클래스 이름을 바꾸는 대응 변경이 필요합니다.

대응하는 JH 브랜치#

브랜치 이름에 -jh를 붙여서 GitLab JH에 대응하는 JH 브랜치를 만들 수 있습니다. 대응하는 JH 브랜치가 있으면 as-if-jh 파이프라인은 기본 브랜치 main-jh가 아니라 해당 브랜치에서 파일을 가져옵니다.

현재는 CI가 GitLab JH mirror에서 해당 브랜치를 가져오려고 시도하므로, 새 JH 브랜치가 미러에 전파되기까지 시간이 걸릴 수 있습니다.

Note

GitLab JH validation은 GitLab JH mirror의 미러이지만, 기본 브랜치 main-jh 외에는 대응하는 JH 브랜치를 포함하지 않습니다. 그래서 대응하는 JH 브랜치를 가져와야 할 때는 validation 프로젝트가 아니라 메인 미러에서 가져와야 합니다.

as-if-JH 파이프라인 구성 방식#

전체 과정은 다음과 같습니다.

Note

sync-as-if-jh-branch는 의존성 변경이 있을 때만 실행합니다.

Mermaid 다이어그램 (28줄)
소스 코드 보기
flowchart TD
  subgraph "JiHuLab.com"
    JH["gitlab-cn/gitlab"]
  end

subgraph "GitLab.com" Mirror["gitlab-org/gitlab-jh-mirrors/gitlab"]

subgraph MR["gitlab-org/gitlab merge request"]
  Add["add-jh-files job"]
  Prepare["prepare-as-if-jh-branch job"]
  Add --"download artifacts"--&gt; Prepare
end

subgraph "gitlab-org-sandbox/gitlab-jh-validation"
  Sync["(*optional) sync-as-if-jh-branch job on branch as-if-jh-code-sync"]
  Start["start-as-if-jh job on as-if-jh/* branch"]
  AsIfJH["as-if-jh pipeline"]
end

Mirror --"pull mirror with master and main-jh"--&gt; gitlab-org-sandbox/gitlab-jh-validation
Mirror --"download JiHu files with ADD_JH_FILES_TOKEN"--&gt; Add
Prepare --"push as-if-jh branches with AS_IF_JH_TOKEN"--&gt; Sync
Sync --"push as-if-jh branches with AS_IF_JH_TOKEN"--&gt; Start
Start --&gt; AsIfJH

end

JH --"pull mirror with corresponding JH branches"--> Mirror

프로젝트 변수에 설정된 토큰#
  • ADD_JH_FILES_TOKEN: JiHu 파일을 내려받을 수 있도록 하는, read_api 퍼미션을 가진 GitLab JH mirror 프로젝트 토큰입니다.
  • AS_IF_JH_TOKEN: 생성된 as-if-jh/* 브랜치를 푸시하기 위한, developer 권한과 write_repository 퍼미션을 가진 GitLab JH validation 프로젝트 토큰입니다.
as-if-JH 브랜치 생성 방식#

먼저 add-jh-files job이 대응하는 JH 브랜치에서 필요한 JiHu 파일을 내려받아 아티팩트로 저장합니다. 이어서 prepare-as-if-jh-branch job이 머지 리퀘스트 브랜치에서 새 브랜치를 생성하고 변경 사항을 커밋한 다음, 그 브랜치를 validation 프로젝트에 푸시합니다.

머지 리퀘스트에 의존성 변경 사항이 있는 경우에는 선택적으로, validation 프로젝트의 as-if-jh-code-sync 브랜치 에서 다운스트림 파이프라인을 트리거하는 sync-as-if-jh-branch job을 실행하는 추가 단계가 있습니다. 이 job은 JiHu code-sync와 같은 과정을 수행하여, 검증 파이프라인을 실행하기 전에 의존성 변경 사항이 as-if-jh 브랜치에 반영되도록 합니다.

의존성 변경 사항이 없으면 이 과정을 실행하지 않습니다.

as-if-JH 파이프라인 트리거 및 실행 방식#

as-if-jh/* 브랜치가 준비되고 선택적으로 동기화되면, start-as-if-jh job이 validation 프로젝트에서 파이프라인을 트리거해 프로젝트 간 다운스트림 파이프라인을 실행합니다.

GitLab JH mirror 프로젝트 설정 방식#

GitLab JH mirror 프로젝트는 비공개이며 CI가 비활성화되어 있습니다.

이 프로젝트는 GitLab JH에서 가져오는 풀 미러이며, 모든 브랜치를 미러링하고 분기된 ref를 덮어쓰며, 미러가 업데이트될 때 파이프라인을 트리거하지 않습니다.

가져오기를 수행하는 사용자는 @gitlab-jh-validation-bot이며, 이 프로젝트의 메인테이너입니다. 자격 증명은 1password 엔지니어링 볼트에서 확인할 수 있습니다.

GitLab JH는 공개 프로젝트이므로 미러링에는 비밀번호를 사용하지 않습니다.

GitLab JH validation 프로젝트 설정 방식#

이 GitLab JH validation 프로젝트는 공개이며 CI가 활성화되어 있고 임시 프로젝트 변수가 설정되어 있습니다.

이 프로젝트는 GitLab JH mirror에서 가져오는 풀 미러로, 특정 브랜치((master|main-jh))만 미러링하고 분기된 ref를 덮어쓰며, 미러가 업데이트될 때 파이프라인을 트리거하지 않습니다.

가져오기를 수행하는 사용자는 @gitlab-jh-validation-bot이며, 이 프로젝트의 메인테이너이자 GitLab JH mirror의 메인테이너이기도 합니다. 자격 증명은 1password 엔지니어링 볼트에서 확인할 수 있습니다.

GitLab JH mirror에서 변경 사항을 가져오는 비밀번호로는 write_repository 퍼미션을 가진 @gitlab-jh-validation-bot의 개인 액세스 토큰을 사용합니다. 사용자 이름은 gitlab-jh-validation-bot으로 설정됩니다.

또한 매일 실행되어 캐시를 업데이트하는, 변수 SCHEDULE_TYPE이 maintenance로 설정된 유지 관리 파이프라인을 실행하는 파이프라인 일정도 있습니다.

기본 CI/CD 구성 파일도 jh/.gitlab-ci.yml로 설정되어 있어, GitLab JH와 정확히 동일하게 실행됩니다.

또한 특별한 브랜치 as-if-jh-code-sync 가 설정되어 있고 보호되어 있습니다. 메인테이너는 푸시할 수 있고 개발자는 이 브랜치에 머지할 수 있습니다. 개발자가 이 브랜치에 대한 파이프라인을 트리거할 수 있어야 하기 때문에 개발자가 머지할 수 있도록 설정했습니다. 이는 Developer 권한 사용자가 보호된 브랜치에서 더 이상 파이프라인을 실행할 수 없는 문제를 해결하기 전까지의 타협책입니다.

이 브랜치는 머지 리퀘스트가 의존성을 변경했을 때 의존성을 동기화하는 sync-as-if-jh-branch를 실행하는 데 사용됩니다. 구현 방식은 as-if-JH 브랜치 생성 방식을 참고합니다.

임시 GitLab JH validation 프로젝트 변수
mirror 프로젝트와 validation 프로젝트를 둘 다 두는 이유#

여러 이유로 프로젝트를 분리해 두고 있습니다.

  • 보안: 이전에는 mirror 프로젝트만 있었습니다. 하지만 완전히 완화하기 위해 보안 이슈를, mirror 프로젝트를 비공개로 전환해야 했습니다.

  • 격리: JH 코드는 완전히 격리된 독립 프로젝트에서 실행하고자 합니다. mirror 프로젝트가 속한 gitlab-org 그룹 아래에서 실행하면 안 됩니다. validation 프로젝트는 완전히 격리되어 있습니다.

  • 비용: 머지 리퀘스트마다 JiHuLab.com에 연결하는 것은 원하지 않습니다. JiHuLab.com의 코드를 GitLab.com의 어딘가로 미러링하고, 머지 리퀘스트가 거기서 코드를 가져오게 하는 것이 더 비용 효율적입니다. 즉 validation 프로젝트는 JiHuLab.com이 아니라 mirror에서 코드를 가져올 수 있습니다. mirror 프로젝트는 주기적으로 JiHuLab.com에서 가져옵니다.

  • 브랜치 분리/보안/효율성: JiHuLab.com에서 대응하는 JH 브랜치를 가져올 수 있도록 모든 브랜치를 미러링하고자 합니다. 하지만 validation 프로젝트의 as-if-jh-code-sync 브랜치는 검증 파이프라인을 제어하는 데 사용되고 AS_IF_JH_TOKEN에 접근 권한이 있으므로 덮어쓰고 싶지 않습니다. 다만 특정 브랜치 하나만 제외하고 모든 브랜치를 미러링하는 것은 불가능합니다. 자세한 내용은 이 이슈를 참고합니다.

    이 문제 때문에 validation 프로젝트는 master와 main-jh만 미러링하도록 설정되어 있습니다. 기술적으로는 이 브랜치들이 반드시 필요하지는 않지만, 머지 리퀘스트에서 변경 사항을 푸시할 때 머지 리퀘스트의 변경 사항만 푸시하면 되도록 리포지터리를 모든 기본 브랜치와 최신 상태로 유지하려는 것이며, 이렇게 하면 더 효율적일 수 있습니다.

  • 관심사 분리:

    • validation 프로젝트에는 다음 브랜치만 있습니다.
      • 변경 사항을 최신으로 유지하기 위한 master와 main-jh
      • 의존성 동기화를 위한 as-if-jh-code-sync 이 브랜치는 절대 미러링하지 않습니다.
      • 머지 리퀘스트에서 생성되는 as-if-jh/* 브랜치 이 브랜치들도 절대 미러링하지 않습니다.
    • mirror 프로젝트의 모든 브랜치는 전부 JiHuLab.com에서 옵니다. mirror 프로젝트에는 아무것도 푸시하지 않으며 파이프라인도 실행하지 않습니다. mirror 프로젝트에서는 CI/CD가 비활성화되어 있습니다.

설정과 프로세스를 단순화하기 위해 두 프로젝트를 합치는 것을 고려할 수 있지만, 위에서 언급한 이유들이 더 이상 우려되지 않는지 먼저 확인해야 합니다.

rspec:undercoverage job#

rspec:undercoverage job은 undercover를 실행해, 머지 리퀘스트에서 도입된 변경 사항 중 커버리지가 0인 부분이 있는지 감지하고, 있으면 실패합니다.

rspec:undercoverage job은 rspec:coverage job에서 커버리지 데이터를 가져옵니다.

rspec:undercoverage job이 EE에서 재정의된 CE 메서드로 인해 커버리지 누락을 감지하면, 머지 리퀘스트에 pipeline:run-as-if-foss 레이블을 추가하고 새 파이프라인을 시작합니다.

긴급 상황이거나 이 job에서 오탐이 발생한 경우, 머지 리퀘스트에 pipeline:skip-undercoverage 레이블을 추가해 이 job이 실패해도 되도록 허용합니다.

rspec:undercoverage 실패 문제 해결#

먼저 rspec:coverage job의 gitlab.lcov 아티팩트에서 커버리지 데이터를 확인합니다. rspec:coverage job이 다양한 이유로 커버리지 데이터 수집에 실패했을 수 있습니다.

rspec:undercoverage job이 메서드 호출에 대한 커버리지 부족을 감지하고도 경고를 표시하지 않을 수 있습니다.

loc: app/controllers/projects/attestations_controller.rb:84:102, coverage: 87.5%
  def parsed_attestation_file hits: n/a
    @parsed_attestation_file ||= begin hits: 52
      if attestation && attestation_file hits: 12 branches: 1/1
        Gitlab::Json.parse(attestation_file.read) hits: 10
      else hits: n/a
        {} hits: 2
      end hits: n/a
    rescue JSON::ParserError => e hits: n/a
      Gitlab::AppJsonLogger.error( hits: 2
        message: 'Failed to parse attestation file', hits: n/a
        error_class: e.class.name, hits: n/a
        error_message: e.message, hits: n/a
        attestation_id: attestation&.id, hits: n/a
        project_id: project.id, hits: n/a
        feature_category: 'artifact_security' hits: n/a
      ) hits: n/a
      {} hits: 2
    end hits: n/a
  end hits: n/a

rspec:undercoverage job에는 오탐 실패를 유발할 수 있는 알려진 버그가 있습니다. 너무 오래된 데이터베이스 마이그레이션을 업데이트하는 경우에도 이런 오탐 실패가 발생할 수 있습니다. 로컬에서 커버리지를 테스트해 pipeline:skip-undercoverage를 적용해도 안전한지 확인할 수 있습니다. 예를 들어 실패를 유발하는 테스트 이름을 <spec>이라고 하면 다음과 같습니다.

  1. RUN_ALL_MIGRATION_TESTS=1 SIMPLECOV=1 bundle exec rspec <spec>를 실행합니다.
  2. scripts/undercoverage를 실행합니다.

이 명령이 undercover: ✅ No coverage is missing in latest changes를 반환하면 pipeline:skip-undercoverage를 적용해 파이프라인 실패를 우회할 수 있습니다.

관련 없는 파이프라인 실패를 우회하기 위해 pipeline:skip-undercoverage를 사용해야 했다면, 필요한 테스트 커버리지를 추가하는 후속 MR을 열어주십시오. 이렇게 하면 다른 사람들을 위해 시스템이 더 나은 상태로 유지됩니다.

pajamas_adoption job#

pajamas_adoption job은 머지 리퀘스트에서 Pajamas Adoption Scanner를 실행해 Pajamas Design System 채택의 리그레션을 방지합니다.

이 job은 스캐너가 머지 리퀘스트로 인한 리그레션을 감지하면 실패합니다. 머지 리퀘스트에서 리그레션을 수정할 수 없는 경우, 머지 리퀘스트에 pipeline:skip-pajamas-adoption 레이블을 추가한 다음 job을 재시도합니다.

테스트 스위트 병렬화#

현재 RSpec 테스트 병렬화 설정은 다음과 같습니다.

  1. prepare 스테이지의 retrieve-tests-metadata job이 knapsack/report-master.json 파일이 있는지 확인합니다.
    • knapsack/report-master.json 파일은 update-tests-metadata를 실행하는 최신 main 파이프라인 (현재는 2시간마다 실행되는 maintenance 예약된 master 파이프라인)에서 가져옵니다. 파일이 없으면 {}로 초기화합니다.
  2. 각 [rspec|rspec-ee] [migration|unit|integration|system|geo] n m job은 knapsack rspec으로 실행되며 테스트를 균등하게 나눠 가져야 합니다.
    • "이전 모든 스테이지의 아티팩트는 기본적으로 전달되므로" 각 job이 knapsack/report-master.json에 접근할 수 있어 이 방식이 동작합니다.
    • 각 job은 자신의 리포트 경로를 "knapsack/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"으로 설정합니다.
    • knapsack이 제대로 동작하고 있다면, 실행된 테스트 파일은 Leftover specs가 아니라 Report specs 아래에 나열되어야 합니다.
  3. update-tests-metadata job(canonical 프로젝트의 예약된 파이프라인에서만 실행됩니다)은 다음 두 가지 방식으로 knapsack/report-master.json을 업데이트합니다.
    1. 기본적으로는 모든 knapsack/rspec*.json 파일을 가져와 하나의 knapsack/report-master.json 파일로 합쳐 아티팩트로 저장합니다.
    2. (실험적) AVERAGE_KNAPSACK_REPORT 환경 변수가 true로 설정된 경우, 리포트를 합치는 대신 스펙 순서, 러너 하드웨어 차이, 플레이키 테스트 등 무작위 요인으로 인한 성능 영향을 줄이기 위해 knapsack/report-master.json과 knapsack/rspec*.json 간의 테스트 실행 시간 평균을 계산합니다. 이 실험적 방식은 각 스펙 파일의 실행 시간을 더 잘 예측해 병렬 job 간에 부하를 더 균등하게 분배함으로써 job들이 비슷한 시점에 끝나도록 하는 것을 목표로 합니다.

이후 다음 파이프라인은 최신 knapsack/report-master.json 파일을 사용합니다.

코드 커버리지#

테스트 선택, 커버리지 분석, 플레이키 테스트 분석에 활용하기 위해 테스트 스위트에서 코드 커버리지 데이터를 수집합니다.

커버리지 유형#

유형 수집 방법 테스트
백엔드 SimpleCov → LCOV RSpec
백엔드 E2E Coverband E2E 스펙
프론트엔드 Istanbul Jest
프론트엔드 E2E Istanbul E2E 스펙
Workhorse Go coverage Go 테스트

CI job#

커버리지 데이터는 여러 CI job을 거쳐 흐릅니다.

  1. 수집: 커버리지 계측과 함께 테스트를 실행합니다

    • rspec job은 SimpleCov를 통해 백엔드 커버리지를 수집합니다
    • jest job은 Istanbul을 통해 프론트엔드 커버리지를 수집합니다
    • e2e:test-on-cng는 Coverband(백엔드)와 Istanbul(프론트엔드)을 통해 E2E 커버리지를 수집합니다
    • workhorse job은 Go 커버리지를 수집합니다
  2. 병합: 병렬 job과 E2E의 커버리지를 병합합니다

    • rspec:coverage가 RSpec 커버리지를 coverage/lcov/gitlab.lcov로 병합합니다
    • coverage-frontend가 Jest 커버리지를 병합합니다
    • 병합 스크립트가 E2E 커버리지를 유닛/통합 커버리지와 결합합니다
  3. 내보내기: 병합된 커버리지를 ClickHouse로 내보냅니다

    • test-coverage:export-rspec-and-e2e가 백엔드 커버리지를 내보냅니다
    • test-coverage:export-jest-and-e2e가 프론트엔드 커버리지를 내보냅니다
    • test-coverage:export-workhorse가 Workhorse 커버리지를 내보냅니다

커버리지 수집, 데이터 흐름, ClickHouse 저장에 대한 자세한 문서는 코드 커버리지를 참고합니다.

플레이키 테스트 재시도와 격리#

실패한 테스트 자동 재시도#

실패한 백엔드 테스트는 별도의 RSpec 프로세스에서 한 번 자동으로 재시도됩니다. 이는 같은 커밋 SHA에서 실패했다가 통과하는 테스트로 정의되는 플레이키 테스트를 감지하는 데 도움이 됩니다.

"새 RSpec 프로세스에서 실패한 테스트 재시도"는 $RETRY_FAILED_TESTS_IN_NEW_PROCESS 변수를 false로 설정해 비활성화할 수 있습니다.

격리된 테스트#

GitLab CI 파이프라인은 조사 중인 동안 파이프라인을 막고 있는 테스트를 건너뛰기 위해 빠른 격리(fast quarantine)를 사용합니다.

빠른 격리 프로세스는 $FAST_QUARANTINE 변수를 false로 설정해 비활성화할 수 있습니다.

격리 절차와 문법은 테스트 격리와 격리 프로세스 핸드북을 참고합니다.

호환성 테스트#

기본적으로 모든 테스트는 GitLab.com에서 사용하는 버전으로 실행합니다.

다른 버전(일반적으로 이전 호환 버전 하나와 이후 호환 버전 하나)은 nightly 예약된 파이프라인에서 실행해야 합니다.

이 일반 가이드라인에 대한 예외는 근거를 밝히고 문서화해야 합니다.

Ruby 버전 테스트#

GitLab.com과 기본 브랜치 모두에서 Ruby 3.3을 실행하고 있습니다. 머지 리퀘스트 파이프라인도 다른 버전을 선택하는 레이블을 추가하지 않는 한 기본 버전으로 실행됩니다.

다음 Ruby 버전을 준비하기 위해 ruby-next 브랜치에서 2시간마다(홀수 시각마다) 예약된 파이프라인을 실행합니다. 자세한 로드맵은 Ruby 3.4 에픽을 참고합니다.

머지 리퀘스트에서는 다음 레이블 중 하나를 추가해 파이프라인이 실행할 Ruby 버전을 바꿀 수 있습니다.

  • pipeline:run-in-ruby3_3: 머지 리퀘스트 파이프라인을 기본 Ruby 버전으로 유지합니다. 이후 기본 Ruby 버전이 바뀌면 이 레이블도 새 기본 버전을 따라갑니다. 두 레이블이 함께 있으면 pipeline:run-with-ruby-next보다 우선합니다.
  • pipeline:run-with-ruby-next: RUBY_VERSION_NEXT CI 변수에 지정된 다음 Ruby 버전으로 머지 리퀘스트 파이프라인을 실행합니다. Rails에 대해 pipeline:run-with-rails-next가 동작하는 방식과 같습니다. RUBY_VERSION_NEXT를 올리는 머지 리퀘스트에는 이 레이블을 추가해, 머지되기 전에 새 버전이 검증되도록 합니다. 머지 트레인 파이프라인은 이 레이블과 무관하게 항상 기본 Ruby 버전으로 실행됩니다. 이 레이블은 커뮤니티 기여, 봇이 작성한 머지 리퀘스트, 안정 브랜치를 대상으로 하는 머지 리퀘스트처럼 더 먼저 평가되는 워크플로 규칙에 매칭되는 머지 리퀘스트에는 영향을 주지 않습니다. pipeline:run-with-rails-next의 규칙도 먼저 평가되므로, 두 레이블을 모두 가진 머지 리퀘스트는 기본 Ruby 버전을 사용하게 됩니다. 반면 database 레이블 규칙은 이 규칙 이후에 평가되므로, 두 레이블을 모두 가진 머지 리퀘스트는 다음 Ruby 버전으로 실행되며 QUERY_LOG_LINE을 받지 않습니다.

PostgreSQL 버전 테스트#

테스트 스위트는 GitLab.com이 PostgreSQL 16에서 실행되고 Omnibus는 신규 설치와 업그레이드에서 기본적으로 PG14를 사용하기 때문에 PostgreSQL 16을 대상으로 실행됩니다.

nightly 예약된 파이프라인에서는 PostgreSQL 16, 17, 18을 대상으로 테스트 스위트를 실행합니다.

Note

PG17이 추가되면서 nightly job의 한도에 가까워지고 있으며, 파이프라인당 2000개 job 중 1946개를 사용하고 있습니다. 새 job 계열을 추가하면 nightly 파이프라인이 실패할 수 있습니다.

현재 버전 테스트 현황#

위치? PostgreSQL 버전 Ruby 버전
머지 리퀘스트 17(기본 버전) 3.3(기본 버전)
master 브랜치 커밋 17(기본 버전) 3.3(기본 버전)
master 브랜치의 maintenance 예약된 파이프라인(짝수 시각 XX:05마다) 17(기본 버전) 3.3(기본 버전)
ruby-next 브랜치의 maintenance 예약된 파이프라인(홀수 시각 XX:10마다) 17(기본 버전) 3.4
master 브랜치의 nightly 예약된 파이프라인 17(기본 버전), 16, 18 3.3(기본 버전)
master 브랜치의 weekly 예약된 파이프라인 17(기본 버전) 3.3(기본 버전)

테스트 대상인 다음 Ruby 버전을 위해, ruby-next 브랜치에서 2시간마다 maintenance 예약된 파이프라인을 실행합니다. ruby-next에는 어떤 변경 사항도 있으면 안 됩니다. 이 브랜치는 예약된 maintenance 파이프라인에서 다른 Ruby 버전으로 파이프라인을 실행하기 위한 용도로만 존재합니다.

ruby-sync 브랜치#

ruby-sync 브랜치는 ruby-next와 rails-next 브랜치를 master와 최신 상태로 유지합니다. 이 브랜치는 (master에서 파생되지 않은) **고아 브랜치(orphan branch)**로, 자체 .gitlab-ci.yml, scripts/slack 헬퍼, README.md만 포함합니다.

예약된 파이프라인이 ruby-sync에서 2시간마다 실행됩니다. gitlab job은 다음을 수행합니다.

  1. 전체 gitlab-org/gitlab 리포지터리를 클론합니다.
  2. ruby-next와 rails-next 각각에 대해 해당 브랜치를 체크아웃하고 origin/master를 머지한 다음 결과를 푸시합니다.

이 푸시로는 어떤 다운스트림 파이프라인도 트리거되지 않습니다. ruby-next와 rails-next 브랜치는 각자 독립적으로 예약된 maintenance 파이프라인을 실행합니다.

인증#

gitlab job은 write_repository 스코프와 Maintainer 권한을 가진 프로젝트 토큰(RUBY_SYNC_TOKEN)으로 인증합니다. 이 토큰은 ruby-sync 브랜치의 파이프라인 일정 변수에 저장되어 있습니다.

재시도 및 실패 알림#

gitlab job은 일시적인 오류를 처리하기 위해 스크립트가 실패하면 한 번 재시도합니다. 그래도 실패하면 notify job이 CI_SLACK_WEBHOOK_URL을 통해 #backend Slack 채널(사용자 이름 ruby-sync)로 메시지를 보냅니다.

☠️ ruby-sync failed to merge master into the ruby-next/rails-next branches
in gitlab-org/gitlab. Pipeline: <pipeline_url> — Docs: <docs_url>
ruby-sync 실패 문제 해결#

흔한 실패 원인은 다음과 같습니다.

  • 일시적인 GitLab 부하 오류: 이 job은 전체 리포지터리를 클론하므로 부하가 높은 시간대에는 실패할 수 있습니다. 실패한 파이프라인을 재시도합니다.
  • 머지 충돌: ruby-next 또는 rails-next가 master와 충돌을 일으키는 방식으로 벌어져 있으면 git merge 단계가 실패합니다. 영향을 받은 브랜치에서 충돌을 수동으로 해결한 다음 재시도합니다.
  • 인증 오류: RUBY_SYNC_TOKEN 프로젝트 토큰이 만료되지 않았는지 확인합니다. 파이프라인 일정 구성을 확인합니다.

재시도하려면 파이프라인 일정 페이지로 이동해 ruby-sync 일정을 실행하거나, 파이프라인 페이지에서 실패한 job을 재시도합니다.

Redis 버전 테스트#

테스트 스위트는 기본적으로 GitLab 설치의 권장 버전인 Redis 7.2를 대상으로 실행됩니다. Weekly 예약된 파이프라인에서는 지원 범위 전체의 호환성을 검증하기 위해 Redis 7.0(최소 버전)과 Valkey 7.2도 함께 테스트합니다.

현재 버전 테스트 현황#

위치? Redis 버전
MR 7.2
default branch(예약되지 않은 파이프라인) 7.2
nightly 예약된 파이프라인 7.2
weekly 예약된 파이프라인 7.0(최소), Valkey 7.2

단일 데이터베이스 테스트#

기본적으로 모든 테스트는 다중 데이터베이스로 실행됩니다.

nightly 예약된 파이프라인과 데이터베이스 관련 파일을 다루는 머지 리퀘스트에서는 단일 데이터베이스로도 테스트를 실행합니다.

단일 데이터베이스 테스트는 두 가지 모드로 실행됩니다.

  1. 단일 연결의 단일 데이터베이스. GitLab이 하나의 커넥션 풀로 모든 테이블에 연결합니다. -single-db로 끝나는 모든 job에서 실행됩니다
  2. 두 연결의 단일 데이터베이스. GitLab이 gitlab_main, gitlab_ci 데이터베이스 테이블에 서로 다른 데이터베이스 연결로 접속합니다. -single-db-ci-connection으로 끝나는 모든 job에서 실행됩니다.

테스트를 강제로 단일 데이터베이스로 실행하려면 머지 리퀘스트에 pipeline:run-single-db 레이블을 추가할 수 있습니다.

Elasticsearch·OpenSearch 버전 테스트#

특정 조건을 만족하면 GitLab.com이 Elasticsearch 9에서 실행되므로, 테스트 스위트도 Elasticsearch 9를 대상으로 실행됩니다.

nightly 예약된 파이프라인에서는 Elasticsearch 8, 9와 OpenSearch 1, 2를 대상으로 테스트 스위트를 실행합니다. 모든 테스트 스위트는 데이터베이스와 검색 백엔드 간에 의존성이 없으므로 PostgreSQL 17을 사용합니다.

위치? Elasticsearch 버전 OpenSearch 버전 PostgreSQL 버전
~group::global search 또는 ~pipeline:run-search-tests 레이블이 있는 머지 리퀘스트 9.X(production) 17(기본 버전)
master 브랜치의 nightly 예약된 파이프라인 7.X, 9.X(production) 1.X, 2.X 17(기본 버전)
master 브랜치의 weekly 예약된 파이프라인 8.X latest 17(기본 버전)

모니터링#

GitLab 테스트 스위트는 main 브랜치와 이름에 rspec-profile이 포함된 모든 브랜치에 대해 모니터링됩니다.

로깅#

  • CI에서는 성능상의 이유로 log/test.log로의 Rails 로깅이 기본적으로 비활성화되어 있습니다. 이 설정을 재정의하려면 RAILS_ENABLE_TEST_LOG 환경 변수를 지정합니다.

CI 구성 내부 구조#

자세한 내용은 전담 CI 구성 내부 구조 페이지를 참고합니다.

성능#

자세한 내용은 전담 CI 구성 성능 페이지를 참고합니다.


개발 문서로 돌아가기