GitLab 프로젝트의 파이프라인

gitlab-org/gitlab (그리고 dev 인스턴스의)에 대한 파이프라인은 보통의 .gitlab-ci.yml 에 구성되어 있으며, 이 자체에는 유지보수를 쉽게 하기 위해 .gitlab/ci/ 하위 파일이 포함되어 있습니다.

우리는 가능한 한 많이 GitLab CI/CD 기능과 모범 사례를 소비(GitLab에서 사용)하려고 노력하고 있습니다.

gitlab-org/gitlab 파이프라인에는 CI/CD 구성 요소dev.gitlab.com 인스턴스에서 미러링하지 않는 한 사용하지 마십시오. CI/CD 구성 요소는 서로 다른 인스턴스에서 작동하지 않으며, 그 인스턴스에 존재하지 않는 경우 dev.gitlab.com 미러에서 파이프라인 실패를 일으킵니다.

파이프라인 계층

활발히 개발 중: 자세한 정보는 epic 58을 참조하십시오.

병합 요청은 일반적으로 여러 CI/CD 파이프라인을 실행합니다. 병합 요청이 승인 프로세스에서 어디에 있는지에 따라 다양한 종류의 파이프라인을 트리거할 것입니다. 우리는 그러한 종류의 파이프라인을 파이프라인 계층이라고 합니다.

우리는 현재 세 가지 계층이 있습니다.

  1. pipeline::tier-1: 병합 요청에는 승인이 없습니다
  2. pipeline::tier-2: 병합 요청은 적어도 하나의 승인이 있지만 더 많은 승인이 필요합니다
  3. pipeline::tier-3: 병합 요청에 필요한 모든 승인이 있습니다

보통 파이프라인 계층이 낮을수록 파이프라인은 더 빨라야 합니다. 파이프라인 계층이 높을수록 더 많은 테스트를 실행함으로써 파이프라인은 우리에게 더 큰 확신을 줄 것입니다.

구현에 대한 자세한 정보는 Introduce “tiers” in MR pipelines Epic을 참조하십시오.

병합 요청 승인 전 예측 테스트 작업

파이프라인 비용을 줄이고 작업 시간을 단축하기 위해 병합 요청이 승인되기 전에, 파이프라인은 병합 요청 변경 사항에 대해 실패할 가능성이 있는 일련의 RSpec 및 Jest 테스트를 실행할 것입니다.

병합 요청이 승인된 후, 파이프라인에는 전체 RSpec 및 Jest 테스트가 포함될 것입니다. 이는 병합 요청이 병합되기 전에 모든 테스트가 실행되었음을 보장할 것입니다.

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

예측 테스트 작업이 실행되는 방법을 이해하려면 GitLab 코드(프론트엔드 및 백엔드)와 해당 테스트(Jest 및 RSpec)간의 의존성을 이해해야 합니다. 이 의존성은 다음 다이어그램으로 시각화할 수 있습니다:

flowchart LR subgraph frontend fe["프론트엔드 코드"]--테스트됨-->jest end subgraph backend be["백엔드 코드"]--테스트됨-->rspec end be--생성-->fixtures["프론트엔드 fixtures"] fixtures--사용됨-->jest

요약하자면:

  • RSpec 테스트는 백엔드 코드에 의존합니다.
  • Jest 테스트는 프론트엔드와 백엔드 코드 모두, 후자는 프론트엔드 fixtures를 통해 의존합니다.

예측 테스트 대시보드

detect-tests CI 작업

gitlab-org/gitlab의 대부분의 CI/CD 파이프라인은 주어진 MR의 변경된 파일을 기반으로 실행될 백엔드/프론트엔드 테스트를 감지하기 위해 detect-tests CI 작업을 준비 단계에서 실행할 것입니다.

detect-tests 작업은 많은 파일을 생성하며, 해당 파일에는 실행할 백엔드/프론트엔드 테스트가 포함될 것입니다. 이러한 파일은 파이프라인의 후속 작업에서 읽히며, 해당 테스트만이 실행될 것입니다.

RSpec 예측 작업

병합 요청에서 예측적인 RSpec 테스트 파일 결정

병합 요청에서 실패 가능성이 있는 RSpec 테스트를 식별하기 위해 동적 매핑정적 매핑을 사용합니다.

동적 매핑

먼저, 우리는 test_file_finder을 사용하며, 동적 매핑 전략은 Crystalball)에서 제공됩니다 (사용된 곳Crystalball에서 사용하는 매핑 전략).

test_file_finder 외에도 더 많은 테스트 실행을 감지하기 위해 몇 가지 고급 매핑이 추가되었습니다:

  • FindChanges (!74003)
    • 백엔드 변경 시 실행할 Jest 테스트를 자동으로 감지(프론트엔드 fixtures를 통해)
  • PartialToViewsMappings (#395016)
    • MR에서 변경되는 Rails 부분뷰가 포함된 경우 view specs를 실행합니다.
  • JsToSystemSpecsMappings (#386754)
    • MR에서 JavaScript 파일이 변경되면 일부 system specs를 실행합니다.
  • GraphqlBaseTypeMappings (#386756)
    • GraphQL 타입 클래스가 변경되면 해당 타입을 포함하는 다른 GraphQL 타입을 식별하고 해당 테스트를 실행하려고 합니다.
  • ViewToSystemSpecsMappings (#395017)
    • 뷰가 변경되면 해당 코드 영역을 테스트할 feature specs를 찾으려고 합니다.
  • ViewToJsMappings (#386719)
    • JS 파일이 변경되면 해당 JS 컴포넌트를 사용하는 system specs를 식별하려고 합니다.
  • FindFilesUsingFeatureFlags (#407366)
    • feature flag이 변경되면 해당 기능 플래그를 포함하는 Ruby 파일을 확인한 후 해당 MR의 변경된 파일을 기반으로 실행해야 하는 백엔드/프론트엔드 테스트를 추가합니다. 작업의 나머지 부분은 해당 변경된 파일을 기반으로 실행될 백엔드/프론트엔드 테스트를 감지할 것입니다.
Static mappings

우리는 test_file_finder gem을 사용하며, 특수한 경우에 대한 정적 매핑은 tests.yml 파일에서 유지됩니다. 이 정적 매핑은 동적 매핑을 통해 매핑할 수 없는 특수한 경우를 위해 유지됩니다 (사용된 위치 참조).

test mappings에는 각 소스 파일을 해당 소스 파일에 종속된 테스트 파일 목록에 매핑하는 맵이 포함되어 있습니다.

특이한 경우

또한, 특정 상황에서는 항상 전체 RSpec 테스트를 실행해야 합니다:

  • 병합 요청에 pipeline:run-all-rspec 라벨이 설정된 경우. 이 라벨은 as-if-foss 작업에서 실행된 테스트를 포함하여 모든 RSpec 테스트를 트리거합니다.
  • 병합 요청에 pipeline:mr-approved 라벨이 설정되어 있고, 코드 변경이 backend-patterns 규칙을 만족하는 경우. 이 라벨은 어떤 리뷰어에 의해 병합 요청이 승인될 때 triage 자동화에 의해 할당됩니다. 이 라벨은 수동으로 적용하는 것이 권장되지 않습니다.
  • 자동화에 의해 병합 요청이 생성된 경우 (예: Gitaly 업데이트 또는 안정적인 브랜치를 대상으로 하는 MR)
  • 보안 미러에서 병합 요청이 생성된 경우
  • CI 구성 파일이 변경된 경우 (예: .gitlab-ci.yml 또는 .gitlab/ci/**/*와 같은 파일)

백엔드 예측 테스트에서 문제가 발생했습니까?

그렇다면, 백엔드 예측 테스트 문제에 대한 조치 방법은 Engineering Productivity RUNBOOK on predictive tests을 참조하십시오. 또한, 테스트 선택 상의 빈틈을 식별했다면 @gl-quality/eng-prod에 알려서 필요한 조치를 취할 수 있도록 해주세요.

Jest 예측 작업

병합 요청에서 예측적인 Jest 테스트 파일 식별

병합 요청에서 예측적으로 실패할 가능성이 있는 jest 테스트를 식별하기 위해 변경된 모든 파일의 목록을 --findRelatedTests 옵션을 사용하여 jest에 전달합니다(https://jestjs.io/docs/cli#–findrelatedtests-spaceseparatedlistofsourcefiles). 이 모드에서 jest는 변경된 파일과 관련된 모든 종속 항목을 해결하며, 이는 이러한 파일이 종속 체인에 있는 테스트 파일을 포함합니다.

특이한 경우

또한, 특정 상황에서는 항상 전체 Jest 테스트를 실행해야 합니다:

  • 병합 요청에 pipeline:run-all-jest 라벨이 설정된 경우
  • 자동화에 의해 병합 요청이 생성된 경우 (예: Gitaly 업데이트 또는 안정적인 브랜치를 대상으로 하는 MR)
  • 보안 미러에서 병합 요청이 생성된 경우
  • 관련 CI 구성 파일이 변경된 경우 (.gitlab/ci/rules.gitlab-ci.yml, .gitlab/ci/frontend.gitlab-ci.yml)
  • Frontend 종속성 파일이 변경된 경우 (예: package.json, yarn.lock, config/webpack.config.js, config/helpers/**/*.js)
  • Vendor JavaScript 파일이 변경된 경우 (예: vendor/assets/javascripts/**/*)

전체 Jest 테스트의 rules 정의는 rules.gitlab-ci.yml.frontend:rules:jest에서 정의됩니다.

프런트엔드 예측 테스트에 문제가 발생했습니까?

그렇다면, 프런트엔드 예측 테스트 문제에 대한 조치 방법은 Engineering Productivity RUNBOOK on predictive tests을 참조하십시오.

분기 파이프라인

분기 파이프라인에서는 pipeline:run-all-rspec 라벨이 MR에 설정되지 않는 한 예측 RSpec 및 Jest 작업만 실행합니다. 목표는 분기 파이프라인에서 사용하는 계산 할당량을 줄이는 것입니다.

실험 이슈를 확인하십시오.

병합 요청 파이프라인에서 fail-fast 작업

기존 테스트를 파열하는 병합 요청에 대한 빠른 피드백을 제공하기 위해 fail-fast 메커니즘을 구현했습니다.

rspec fail-fast 작업은 병합 요청 파이프라인의 다른 모든 rspec 작업과 병렬로 추가됩니다. 이 작업은 병합 요청에서 변경된 내용과 직접 관련된 테스트를 실행합니다.

이러한 테스트 중 하나라도 실패할 경우, rspec fail-fast 작업이 실패하여 fail-pipeline-early 작업을 트리거합니다. fail-pipeline-early 작업은:

  • 현재 실행 중인 파이프라인 및 진행 중인 작업을 모두 취소합니다.
  • 파이프라인을 failed 상태로 설정합니다.

예를 들면:

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"--> G G --"on failure"--> Z

병합 요청과 관련된 테스트 파일이 10개 이상인 경우 rspec fail-fast가 실행되지 않도록 하여 rspec fail-fast의 지속 시간이 평균 rspec 작업의 지속 시간을 초과함을 방지합니다.

이 숫자는 RSPEC_FAIL_FAST_TEST_FILE_COUNT_THRESHOLD라는 CI/CD 변수를 설정함으로써 재정의할 수 있습니다.

병합 요청 파이프라인에서 이전에 실패한 테스트 다시 실행

병합 요청의 실패한 테스트를 해결한 후에 피드백 시간을 줄이기 위해, rspec rspec-pg14-rerun-previous-failed-testsrspec rspec-ee-pg14-rerun-previous-failed-tests 작업은 이전 MR 파이프라인에서 실패한 테스트를 실행합니다.

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

작동 방식

  1. detect-previous-failed-tests 작업(준비 단계)은 이전 MR 파이프라인의 실패한 RSpec 작업과 연관된 테스트 파일을 감지합니다.
  2. rspec rspec-pg14-rerun-previous-failed-testsrspec rspec-ee-pg14-rerun-previous-failed-tests 작업은 detect-previous-failed-tests 작업에서 수집한 테스트 파일을 실행할 것입니다.
graph LR subgraph "준비 단계"; A["detect-previous-failed-tests"] end subgraph "테스트 단계"; B["rspec rspec-pg14-rerun-previous-failed-tests"]; C["rspec rspec-ee-pg14-rerun-previous-failed-tests"]; end A --"artifact: 테스트 파일 목록"--> B & C

병합 트레인

현재 사용법

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

현재, 병합 트레인 파이프라인에서는 어떠한 테스트도 실행되지 않습니다: 이미 존재하는 “병합 요청 병합” 가이드라인을 강제하기만 합니다. 이전에 병합 트레인을 사용하도록 허용되었지만 우리가 쉽게 강제하지 못했던 가이드라인입니다.

병합 트레인 파이프라인은 단일 pre-merge-checks 작업을 실행하여 병합 전 최신 파이프라인이:

  1. 병합된 결과 파이프라인
  2. tier-3 파이프라인 (예: 예측이 아닌 완전한 파이프라인)
  3. 최대 8시간 전에 생성됨 (안정 브랜치의 경우 최대 72시간 전)

우리는 이 해결책에 대한 피드백 이슈를 열었습니다 제한하여 이 해결책을 바탕으로 반복할 수 있도록.

다음 반복

병합 트레인 파이프라인에서 실제로 테스트를 실행하기 시작하려면 병합 트레인 다음 반복을 논의하기 위한 전용 이슈를 열었습니다.

“전체” 테스트 파이프라인 실행을 가능하게 하는 병합 트레인의 활성화에 대한 도전과제

왜 “안정적인” 기본 브랜치가 필요한가요?

기본 브랜치가 불안정한 경우 (즉, 기본 브랜치의 CI/CD 파이프라인이 자주 실패하는 경우), 불량 병합 요청 파이프라인 이후에 추가된 모든 병합 요청 파이프라인은 취소되고 다시 트레인에 추가되어 긴 병합 트레인을 만드는 많은 지연을 유발합니다.

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

특정 숫자는 없지만, 테스트 실패와 인프라 실패에 대한 더 나은 숫자가 필요합니다 (Master Broken Incidents RCA 대시보드 참조(https://10az.online.tableau.com/#/site/gitlab/workbooks/2296993/views)).

일부 병합 요청에 대한 빠른 피드백

‘master’ 브랜치 수정

master 브랜치를 수정해야 할 때, 병합 요청에서 실행되는 파이프라인을 가속화하기 위해 pipeline::expedited 레이블을 추가할 수 있습니다.

병합 요청에 master:broken 또는 master:foss-broken 레이블이 설정되어 있어야 합니다.

되돌린 MR

되돌린 MR의 처리를 더 빠르게 하려면, 병합 요청을 생성하기 전에 되돌린 MR 템플릿을 사용하세요. 이렇게 하면 pipeline::expedited 레이블 및 기타 레이블이 적용되어 병합 요청에서 실행되는 파이프라인을 가속화합니다.

‘pipeline::expedited’ 레이블

이 레이블이 할당되면 다음 CI/CD 파이프라인 단계가 건너뜁니다:

병합 요청에 레이블을 적용하고 MR을 위해 새 파이프라인을 실행하세요.

테스트 작업들

테스트 레벨에 대한 전용 작업들이 있으며, 각 작업은 병합 요청에서 수행된 변경 내용에 따라 실행됩니다. 변경과 관계없이 모든 RSpec 작업을 강제로 실행하려면 병합 요청에 pipeline:run-all-rspec 레이블을 추가할 수 있습니다.

경고: 문서만 관련된 MR에 모든 작업을 강제 실행하는 경우 선행되어야 하는 작업을 갖추지 않고 오류를 유발할 수 있습니다.

end-to-end 작업

e2e:test-on-omnibus 자식 파이프라인은 변경 내용에 따라 자동으로 end-to-end 작업을 실행하거나 다른 경우에는 수동으로 실행됩니다. 특정 규칙 목록에 대한 .qa:rules:test-on-omnibus을 참조하십시오.

병합 요청에서 변경 내용과 관계없이 e2e:test-on-omnibus를 강제로 실행하려면 pipeline:run-all-e2e 레이블을 MR에 추가하세요.

e2e:test-on-gdk 자식 파이프라인은 code patterns changes에 대해 자동으로 :blocking E2E 스펙을 실행합니다. 특정 규칙 목록에 대한 .qa:rules:e2e-blocking-gdk을 참조하십시오.

더 많은 정보를 보려면 End-to-end Testing 전용 페이지를 참조하십시오.

가시성 end-to-end 작업

GitLab 관찰성 백앤드는 GitLab 인스턴스와의 통합이 올바르게 작동하는지 확인하기 위해 전용 end-to-end 테스트로 이루어져 있습니다.

GitLab 파이프라인에는 (GitLab MR에서 실행할 수 있는) 이러한 작업을 트리거할 수 있는 전용 작업들이 있습니다. 이러한 작업은 리뷰 중인 GitLab 변경 사항이 GitLab MR 브랜치에서 빌드된 GitLab 인스턴스에서 E2E 테스트가 실패하지 않도록 하는 데 유용합니다.

가시성 end-to-end 작업에는 두 가지가 있습니다:

  • e2e:observability-backend-main-branch: GitLab Observability Backend의 메인 브랜치에 대한 테스트를 실행합니다.
  • e2e:observability-backend: MR 브랜치와 동일한 이름을 가진 GitLab Observability Backend의 브랜치에 대한 테스트를 실행합니다.

이러한 작업을 수동으로 실행하려면 MR에 pipeline:run-observability-e2e-tests-main-branch 또는 pipeline:run-observability-e2e-tests-current-branch 레이블을 추가하세요.

다음 예제 작업 흐름에서는 개발자가 Observability 코드를 사용하는 MR을 만들고 Observability end-to-end 작업을 사용하는 방법을 보여줍니다:

  1. 개발자가 Observability 코드를 사용하는 GitLab MR을 만듭니다. MR은 자동으로 e2e:observability-backend-main-branch 작업을 실행합니다.
  2. e2e:observability-backend-main-branch이 실패하면, MR이 무언가를 망가뜨린 경우 (수정해야 함) 또는 MR이 업데이트된 e2e 테스트를 요구한 변경 사항을 만든 경우입니다.
  3. 테스트를 업데이트하려면, 개발자는 다음과 같은 작업을 수행해야 합니다:
    1. 저장소에 MR 브랜치와 동일한 이름으로 브랜치를 만듭니다.
    2. e2e 테스트를 수정합니다.
    3. 변경 사항을 가진 MR을 만듭니다.
  4. 개발자는 MR에 pipeline:run-observability-e2e-tests-current-branch 레이블을 추가하고 e2e:observability-backend 작업이 성공할 때까지 기다려야 합니다.
  5. e2e:observability-backend 작업이 성공하면, 개발자는 두 MR을 병합할 수 있습니다.

또한, 개발자는 역학 코드와 관련이 없는 파일 변경 사항의 경우를 위해 수동으로 pipeline:run-observability-e2e-tests-main-branch를 추가할 수 있습니다.

리뷰 앱 작업

start-review-app-pipeline 자식 파이프라인은 변경 사항에 따라 자동으로 리뷰 앱을 배포하고 종단 간 테스트를 실행하며, 다른 경우에는 수동으로 실행합니다. 구체적인 규칙 목록은 rules.gitlab-ci.yml.review:rules:start-review-app-pipeline을 참조하세요.

변경 사항에 관계없이 리뷰 앱을 배포하려면 병합 요청에 pipeline:run-review-app 라벨을 추가할 수 있습니다.

추가 정보는 리뷰 앱 전용 페이지를 참조하세요.

As-if-FOSS 작업 및 상위 프로젝트 하향형 파이프라인

FOSS 프로젝트에서 관련 변경 사항이 제대로 작동하는지 확인하기 위해 일부 조건에서는 다음도 실행합니다.

  • 동일 파이프라인에서 * as-if-foss 작업
  • 상위 FOSS 하향형 프로젝트

* as-if-foss 작업은 GitLab 테스트 스위트를 “FOSS처럼” 실행하므로, 마치 이 작업들이 gitlab-org/gitlab-foss의 컨텍스트에서 실행되는 것처럼입니다. 반면, 상위 FOSS 하향형 프로젝트는 실제로 FOSS 프로젝트 내에서 실행되며, 실제 FOSS 환경에 더 가깝습니다.

다음 경우에 실행됩니다. - 병합 요청에 pipeline:run-as-if-foss 라벨이 설정된 경우 - 병합 요청이 gitlab-org/security/gitlab 프로젝트에서 생성된 경우 - CI 구성 파일이 변경된 경우(예: .gitlab-ci.yml 또는 .gitlab/ci/**/*)

* as-if-foss 작업은 일반 EE-컨텍스트 작업 외에 실행됩니다. 이들은 FOSS_ONLY='1' 변수가 설정되어 있으며 테스트가 시작되기 전에 ee/ 폴더가 제거됩니다.

상위 FOSS 하향형 프로젝트는 해당 FOSS 프로젝트의 기본 브랜치로 병합 요청을 시뮬레이션하여 파일 목록을 제거합니다. 해당 목록은 .gitlab/ci/as-if-foss.gitlab-ci.ymlmerge-train/bin/merge-train에서 찾을 수 있습니다.

이는 gitlab-org/gitlabgitlab-org/gitlab-foss로 동기화된 후 변경 사항이 실패를 일으키지 않도록 하는 의도입니다.

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

  • AS_IF_FOSS_TOKEN: 이것은 GitLab FOSS 프로젝트 토큰으로 developer 역할과 write_repository 권한을 갖고 있어 as-if-foss/* 브랜치를 푸시합니다.
    • 동일한 이름을 보안 프로젝트에 사용해야 하므로, 보안 변경 사항을 공개 프로젝트에 올리지 않도록 다른 토큰을 사용해야 합니다.

As-if-JH 상위 프로젝트 하향형 파이프라인

이것이란

이 파이프라인은 지후 검증 파이프라인으로도 불리며, 현재 실패할 수 있는 것으로 설정되어 있습니다. 이 경우 검증 파이프라인이 실패한 경우 할 일을 따릅니다.

실행 방법

start-as-if-jh 작업은 GitLab JH 내에서 생성된 브랜치를 기반으로 변경 내용을 추가하여 전체 파이프라인을 마치 JiHu처럼 만들기 위해 상위 프로젝트 하향형 파이프라인을 트리거합니다. 이 머지 요청의 경우에만 이러한 작업이 생성됩니다.

as-if-jh/ 접두어가 있는 생성된 브랜치 이름은 머지 요청 브랜치를 기반으로 생성되며, 이후 해당 JH 브랜치에서 다운로드한 변경 사항을 추가하여 전체 파이프라인을 마치 JiHu처럼 만듭니다.

변경 사항이 GitLabGitLab JH로 동기화된 후 변경 사항이 실패를 일으키지 않도록 하는 의도입니다.

pipeline:run-as-if-jh 라벨 적용 시기

Ruby 파일이 이름을 바꾸고 해당 파일에 대응하는 prepend_mod 라인이 있는 경우, GitLab JH가 이에 의존하고 해당 모듈 또는 클래스의 이름을 바꾸는 변경이 필요할 수 있습니다.

해당 JH 브랜치

GitLab JH에 해당 JH 브랜치를 만들려면 브랜치 이름 뒤에 -jh를 추가할 수 있습니다. 생성된 JH 브랜치를 사용하면 해당 JH 브랜치에서 파일을 다운로드합니다. 기본 브랜치 main-jh가 아니라 해당 브랜치에서.

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

참고: GitLab JH validationGitLab JH mirror의 미러이지만, 기본 main-jh 외에 해당하는 JH 브랜치를 포함하지 않습니다. 따라서 해당 JH 브랜치를 가져오려면 유효성 검사 프로젝트가 아니라 미러 프로젝트에서 가져와야 합니다.

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

전체 프로세스는 다음과 같습니다:

참고: 의존성 변경이 있을 때에만 sync-as-if-jh-branch를 실행합니다.

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 --"다운로드 아티팩트"--> 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 --"master 및 main-jh를 사용하여 미러 풀"--> gitlab-org-sandbox/gitlab-jh-validation Mirror --"ADD_JH_FILES_TOKEN으로 JiHu 파일 다운로드"--> Add Prepare --"AS_IF_JH_TOKEN으로 as-if-jh 브랜치 푸시"--> Sync Sync --"AS_IF_JH_TOKEN으로 as-if-jh 브랜치 푸시"--> Start Start --> AsIfJH end JH --"JH 브랜치에 해당하는 미러 풀"--> Mirror
프로젝트 변수에 설정된 토큰
  • ADD_JH_FILES_TOKEN: 이것은 GitLab JH 미러 프로젝트 토큰으로 read_api 권한이 있어 JiHu 파일을 다운로드할 수 있습니다.
  • AS_IF_JH_TOKEN: 이것은 GitLab JH 검증 프로젝트 토큰으로 developer 역할과 write_repository 권한이 있어 생성된 as-if-jh/* 브랜치를 푸시할 수 있습니다.
as-if-JH 브랜치 생성 방법

먼저 add-jh-files 작업은 해당 JH 브랜치에서 필요한 JiHu 파일을 다운로드하여 아티팩트에 저장합니다. 다음으로 prepare-as-if-jh-branch 작업은 MR 브랜치에서 새 브랜치를 생성하고 변경 사항을 커밋한 후 생성된 브랜치를 검증 프로젝트로 푸시합니다.

선택적으로, MR에 의존성 변경이 있는 경우 추가 단계로 sync-as-if-jh-branch 작업을 실행하여 as-if-jh-code-sync 브랜치에서 downstream 파이프라인을 트리거합니다. 이 작업은 JiHu code-sync와 동일한 프로세스를 수행하여 의존성 변경이 as-if-jh 브랜치로 가져올 수 있는지 확인합니다.

의존성 변경 사항이 없는 경우 이 프로세스를 실행하지 않습니다.

as-if-JH 파이프라인을 트리거하고 실행하는 방법

as-if-jh/* 브랜치를 준비하고 선택적으로 동기화한 후, start-as-if-jh 작업은 교차 프로젝트 downstream 파이프라인을 실행하기 위해 검증 프로젝트에서 파이프라인을 트리거합니다.

GitLab JH 미러 프로젝트 설정 방법

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

이것은 GitLab JH에서 풀 미러로, 모든 브랜치를 미러링하며, 발산된 참조를 재정의하고 미러가 업데이트될 때 파이프라인을 트리거하지 않습니다.

풀링 사용자는 프로젝트의 관리자인 @gitlab-jh-validation-bot입니다. 자격 증명은 1password 엔지니어링 보관함에 있습니다.

미러링에서 암호를 사용하지 않습니다. GitLab JH는 공개 프로젝트이기 때문입니다.

GitLab JH 검증 프로젝트 설정 방법

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

이것은 GitLab JH 미러에서 풀 미러로, 특정 브랜치를 미러링하며, 발산된 참조를 재정의하고 미러가 업데이트될 때 파이프라인을 트리거하지 않습니다.

풀링 사용자는 프로젝트와 GitLab JH 미러 모두에서 유지관리자인 @gitlab-jh-validation-bot입니다. 자격 증명은 1password 엔지니어링 보관함에 있습니다.

@gitlab-jh-validation-bot의 개인 액세스 토큰과 write_repository 권한이 사용되어 GitLab JH 미러에서 변경 사항을 풀기 위해 사용됩니다. 사용자 이름은 gitlab-jh-validation-bot으로 설정됩니다.

또한 maintenance로 설정된 SCHEDULE_TYPE 변수로 매일 업데이트 캐시를 업데이트하는 유지보수 파이프라인을 실행하는 파이프라인 스케줄이 있습니다.

기본 CI/CD 구성 파일은 jh/.gitlab-ci.yml로 설정되어 있으므로 GitLab JH와 똑같이 실행됩니다.

또한 특별한 브랜치 as-if-jh-code-sync가 설정되어 있고 보호되어 있습니다. 유지보수자는 푸시하고 개발자는 병합할 수 있도록 설정합니다. 개발자가 이 브랜치의 파이프라인을 트리거할 수 있어야 하므로 이것은 Developer-level 사용자가 보호된 브랜치에서 더 이상 파이프라인을 실행할 수 없음을 해결하기 전에 일시적인 해결책입니다.

이 브랜치에서 sync-as-if-jh-branch를 실행하여 MR이 의존성을 동기화하도록 사용됩니다. 작동 방법에 대해서는 as-if-JH 브랜치 생성 방법을 참조하세요.

임시 GitLab JH 유효성 검사 프로젝트 변수
미러 프로젝트와 유효성 검사 프로젝트를 왜 두 개나 갖고 있나요?

우리는 몇 가지 이유로 별도의 프로젝트를 가지고 있습니다.

  • 보안: 이전에는 미러 프로젝트만 있었습니다. 그러나 보안 문제를 완전히 해소하기 위해 미러 프로젝트를 비공개로 만들어야 했습니다.
  • 격리: 우리는 JH 코드를 완전히 격리되고 독립적인 프로젝트에서 실행하고 싶습니다. 즉, 미러 프로젝트가 있는 gitlab-org 그룹에서 실행하면 안 됩니다. 유효성 검사 프로젝트는 완전히 격리됩니다.
  • 비용: 각 병합 요청에서 JiHuLab.com에 연결하고 싶지 않습니다. JiHuLab.com에서 GitLab.com의 어딘가로 코드를 미러링하고 병합 요청에서 코드를 가져오는 것이 더 비용 효율적입니다. 따라서 유효성 검사 프로젝트는 미러에서 코드를 가져오고 JiHuLab.com에서 가져오는 것이 아니게 됩니다. 미러 프로젝트는 주기적으로 JiHuLab.com에서 가져오게 됩니다.
  • 브랜치 분리/보안/효율성: 우리는 모든 브랜치를 미러링해서 해당 JH 브랜치를 JiHuLab.com에서 가져올 수 있기를 원합니다. 그러나 유효성 검사 프로젝트의 as-if-jh-code-sync 브랜치를 덮어쓰고 싶지 않습니다. 왜냐하면 유효성 검사 파이프라인을 제어하는 데 사용되고 AS_IF_JH_TOKEN에 액세스가 필요하기 때문입니다. 그러나 단일 브랜치를 제외한 모든 브랜치를 미러링하고 싶지 않습니다. 자세한 내용은 이 문제를 참조하십시오.

    이 문제로 인해 유효성 검사 프로젝트는 mastermain-jh만 미러링하도록 설정됩니다. 기술적으로 이러한 브랜치는 심지어 필요하지 않지만, 병합 요청에서 변경 사항을 푸시할 때 기본 브랜치의 변경 사항만 푸시하고 싶은데 더 효율적일 수 있도록 저장소를 최신 상태로 유지하고자 합니다.

  • 관심 분리:
    • 유효성 검사 프로젝트에는 다음과 같은 브랜치만 있습니다:
      • 변경 사항을 최신 상태로 유지하기 위한 mastermain-jh.
      • 종속성 동기화를 위한 as-if-jh-code-sync. 이를 미러링해서는 안 됩니다.
      • 병합 요청의 as-if-jh/* 브랜치들. 이를 미러링해서는 안 됩니다.
    • 미러 프로젝트의 모든 브랜치는 모두 JiHuLab.com에서 가져옵니다. 미러 프로젝트에는 어떤 것도 푸시하지 않으며 파이프라인도 실행되지 않습니다. 미러 프로젝트에서 CI/CD가 비활성화됩니다.

두 프로젝트를 병합하여 설정과 프로세스를 간소화할 수 있지만, 모든 이유가 더 이상 문제가 되지 않도록해야 합니다.

rspec:undercoverage 작업

rspec:undercoverage 작업은 undercover를 실행하여 병합 요청에서 소개된 변경 사항의 코드 커버리지가 0인 경우 실패합니다.

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

만약 rspec:undercoverage 작업이 CE 메서드가 EE에서 재정의된 경우 누락된 커버리지를 감지하면 병합 요청에 pipeline:run-as-if-foss 레이블을 추가하고 새 파이프라인을 시작합니다.

작업 중에 긴급한 상황이나 잘못된 양성 결과가 발생하면 병합 요청에 pipeline:skip-undercoverage 레이블을 추가하여 이 작업을 실패하도록 허용할 수 있습니다.

rspec:undercoverage 실패의 문제 해결

rspec:undercoverage 작업에는 알려진 버그가 있을 수 있으며, 이로 인해 잘못된 양성 실패가 발생할 수 있습니다. 이와 같은 잘못된 양성 실패는 너무 오래된 데이터베이스 마이그레이션을 업데이트하는 경우에도 발생할 수 있습니다. pipeline:skip-undercoverage를 적용할 수 있는지 로컬에서 커버리지를 테스트하여 안전한지 확인할 수 있습니다. 예를 들어, <spec>을 실패한 테스트의 이름으로 사용하여:

  1. RUN_ALL_MIGRATION_TESTS=1 SIMPLECOV=1 bundle exec rspec <spec>을 실행하세요.
  2. scripts/undercoverage을 실행하세요.

이러한 명령이 undercover: ✅ 최신 변경 사항에서 누락된 커버리지가 없습니다를 반환하면 pipeline:skip-undercoverage를 적용하여 파이프라인 실패를 우회할 수 있습니다.

pajamas_adoption 작업

pajamas_adoption 작업은 병합 요청에서 Pajamas Design System의 채택에서 발생하는 회귀를 방지하기 위해 Pajamas Adoption Scanner를 실행합니다.

작업은 병합 요청에서 회귀가 감지되면 실패합니다. 만약 회귀를 병합 요청에서 수정할 수 없는 경우 pipeline:skip-pajamas-adoption 레이블을 추가하고 작업을 다시 시도하세요.

테스트 스위트 병렬화

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

  1. prepare 단계의 retrieve-tests-metadata 작업은 knapsack/report-master.json 파일이 있는지 확인합니다.
    • knapsack/report-master.json 파일은 최신 main 파이프라인에서 update-tests-metadata를 실행하게 됩니다(현재는 2시간마다 실행되는 maintenance 예정된 마스터 파이프라인). 여기에 없으면 {}로 파일을 초기화합니다.
  2. [rspec|rspec-ee] [migration|unit|integration|system|geo] n m 작업은 knapsack rspec를 사용하여 테스트를 균등하게 분배해야 합니다:
    • 작업은 “모든 이전 단계의 artifacts가 기본적으로 전달됩니다”라는 이유로 knapsack/report-master.json에 액세스할 수 있습니다.
    • 작업은 자체 보고서 경로를 "knapsack/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"로 설정해야 합니다.
    • knapsack가 작동하기 때문에 실행된 테스트 파일은 잔여 specs가 아니라 보고서 specs에 나열되어 있어야 합니다.
  3. update-tests-metadata 작업(단일 the canonical project에 대한 예정된 파이프라인에서만 실행됨)은 다음과 같이 해결합니다:
    1. 기본적으로, 모든 knapsack/rspec*.json 파일을 가져와서 모두 단일 knapsack/report-master.json 파일로 병합합니다.
    2. (실험적) AVERAGE_KNAPSACK_REPORT 환경 변수가 true로 설정된 경우, 보고서를 병합하는 대신 작업은 knapsack/report-master.jsonknapsack/rspec*.json 사이의 테스트 기간의 평균을 계산하여 임의적인 팩터(예: spec 순서, 러너 하드웨어 차이, 불안정한 테스트 등)로 인한 성능 영향을 줄이고자 합니다. 이 실험적인 접근 방식은 병렬 작업 간에 부하를 균등하게 분산하여 작업이 비슷한 시간에 완료될 수 있도록 더 나은 예측을 하기 위해 설정되었습니다.

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

Flaky tests

Flaky tests의 자동 스킵

알려진 flaky(불안정한) 테스트를 스킵했었지만, 실제로 master를 망가뜨릴 수 있으므로 더 이상 그렇게 하지 않았습니다. 대신, #master-broken 사건에서 보고된 모든 flaky 테스트를 적극적으로 격리하기 위해 빠른 격리 프로세스를 도입했습니다.

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

실패한 테스트 자동 재시도 및 별도 프로세스에서 실행

$RETRY_FAILED_TESTS_IN_NEW_PROCESS 변수가 false로 설정되지 않는 한 (true가 기본), RSpec 테스트가 실패하면 별도의 RSpec 프로세스에서 한 번 자동으로 다시 시도됩니다. 이를 통해 이전 테스트에서의 대부분의 부작용을 제거하여 다음 테스트 실패를 방지하는 것이 목표입니다.

다시 시도된 테스트는 rspec:flaky-tests-report 작업에 의해 생성된 $RETRIED_TESTS_REPORT_FILE 파일에 저장됩니다.

실험 이슈를 참조하십시오.

호환성 테스트

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

다른 버전(일반적으로 하위 호환 버전 및 상위 호환 버전 중 하나)은 매일 예약된 파이프라인에서 실행되어야 합니다.

이 일반 지침에 대한 예외는 동기화되고 문서화되어야 합니다.

Ruby 버전 테스트

GitLab.com 및 기본 브랜치에서 Ruby 3.1을 실행하고 있습니다. 다음 Ruby 버전을 준비하기 위해 병합 요청을 Ruby 3.2에서 실행합니다. 자세한 내용은 Ruby 3.2 epic을 참조하십시오.

지원되는 모든 Ruby 버전이 작동하는지 확인하기 위해 각 지원 버전에 대해 2시간마다 예약된 파이프라인에서 테스트를 실행합니다.

병합 요청에 대해 특정 Ruby 버전만 실행하려면 다음 레이블을 추가할 수 있습니다:

  • pipeline:run-in-ruby3_1

PostgreSQL 버전 테스트

GitLab.com은 PostgreSQL 14에서 실행되며, Omnibus는 새 설치 및 업그레이드에 대해 기본적으로 PG14를 사용합니다..

우리는 PostgreSQL 14, 15 및 16에 대해 매일 예약된 파이프라인에서 테스트 스위트를 실행합니다.

현재 버전 테스트

위치 PostgreSQL 버전 Ruby 버전
병합 요청 14 (기본 버전) 3.1 (기본 버전)
master 브랜치 커밋 14 (기본 버전) 3.1 (기본 버전)
maintenance 예약된 파이프라인(매 짝수 시간 XX:05에 실행되는 master 브랜치 전용) 14 (기본 버전) 3.1 (기본 버전)
maintenance 예약된 파이프라인(매 홀수 시간 XX:10에 실행되는 ruby3_2 브랜치 전용) 14 (기본 버전) 3.2
nightly 예약된 파이프라인(master 브랜치 전용) 14 (기본 버전), 15, 16 3.1 (기본 버전)

각 지원 중인 Ruby 버전에 대해 예약된 유지관리 파이프라인에서 매 2시간마다 ruby\d_\d 브랜치에서 실행합니다. 이러한 브랜치에는 변경 사항이 없어야 합니다. 이러한 브랜치는 예약된 유지관리 파이프라인에서 해당 Ruby 버전으로 파이프라인을 실행하기 위해 존재합니다.

또한, ruby-sync 브랜치에서도 ruby\d_\d 브랜치를 기본 브랜치 master와 최신 상태로 유지하기 위해 매 2시간마다 예약된 파이프라인이 실행됩니다. 이러한 푸시를 통해 파이프라인은 트리거되지 않습니다.

ruby-sync 브랜치의 gitlab 작업은 2024-12-01에 만료되는 Maintainer 역할과 write_repository 범위가 있는 gitlab-org/gitlab 프로젝트 토큰인 RUBY_SYNC를 사용합니다. 토큰은 예약된 ruby-sync 브랜치의 파이프라인 일정에 있는 RUBY_SYNC_TOKEN 변수에 저장됩니다.

Redis 버전 테스트

GitLab.com은 Redis 6에서 실행되며, Omnibus는 새 설치 및 업그레이드에 대해 기본적으로 Redis 6를 사용합니다..

특정 매일 예약된 파이프라인에서는 Redis 7에서 실행되며, 이는 PostgreSQL 15 작업을 위해 전방 호환된 PostgreSQL 15 작업 실행 시에만 해당됩니다.

현재 버전 테스트

위치 Redis 버전
MRs 6
default branch (예약되지 않은 파이프라인) 6
nightly 예약된 파이프라인 7

단일 데이터베이스 테스트

기본적으로 여러 데이터베이스에서 모든 테스트가 실행됩니다.

또한, 매일 예약된 파이프라인과 데이터베이스 관련 파일을 touch하는 병합 요청에서 단일 데이터베이스에서도 테스트를 실행합니다.

단일 데이터베이스 테스트는 다음과 같은 두 가지 모드로 실행됩니다:

  1. 하나의 연결을 사용하는 단일 데이터베이스. GitLab이 하나의 연결 풀을 사용하여 모든 테이블에 연결하는 모드입니다. 이는 -single-db로 끝나는 모든 작업을 통해 실행됩니다.
  2. 두 개의 연결을 사용하는 단일 데이터베이스. GitLab이 서로 다른 데이터베이스 연결을 사용하여 gitlab_main, gitlab_ci 데이터베이스 테이블에 연결하는 모드입니다. 이는 -single-db-ci-connection로 끝나는 모든 작업을 통해 실행됩니다.

단일 데이터베이스로 테스트를 실행하려면 병합 요청에 pipeline:run-single-db 레이블을 추가할 수 있습니다.

모니터링

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

로깅

  • 기본적으로 CI에서 성능 이유로 Rails 로깅은 log/test.log로 설정되지 않습니다. 이 설정을 오버라이드하려면 RAILS_ENABLE_TEST_LOG 환경 변수를 지정하십시오.

병합 요청용 파이프라인 유형

일반적으로 MR(병합 요청)의 파이프라인은 MR에서 발생한 변경 사항에 따라 다음 중 하나의 유형으로 나뉩니다(짧은 것부터 긴 것까지):

“파이프라인 유형”은 대부분 “중요 경로”(예: 파이프라인의 지속 시간의 합이 개별 작업의 지속 시간과 같아지는 작업 체인)을 설명하는 추상적인 용어입니다. 우리는 이러한 “파이프라인 유형”을 사용하여 메트릭 대시보드에서 최적화가 먼저 이루어져야 하는 유형과 작업을 감지합니다.

여러 영역을 건드리는 MR은 해당하는 가장 긴 유형에 관련됩니다. 예를 들어, 백엔드와 프론트엔드를 건드린 MR은 “프론트엔드” 파이프라인 유형에 해당되며, 이 유형은 “백엔드” 파이프라인 유형보다 완료하는 데 더 오랜 시간이 걸립니다.

우리는 파이프라인에서 실행되어야 하는 작업을 결정하기 위해 rules:needs: 키워드를 광법위하게 사용합니다. 참고로 여러 종류의 변경을 포함하는 MR은 여러 유형의 작업(예: 문서 전용 및 코드 전용 파이프라인의 조합)을 포함하는 파이프라인을 갖습니다.

다음은 각 파이프라인 유형의 중요 경로 그래프입니다. 중요 경로에 속하지 않는 작업은 제외됩니다.

문서 파이프라인

참조 파이프라인.

graph LR classDef criticalPath fill:#f66; 1-3["docs-lint links (5분)"]; class 1-3 criticalPath; click 1-3 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356757&udv=0"

백엔드 파이프라인

참조 파이프라인.

graph RL; classDef criticalPath fill:#f66; 1-1["clone-gitlab-repo (1분)"]; 1-3["compile-test-assets (3분)"]; click 1-3 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914317&udv=0" 1-6["setup-test-env (4분)"]; class 1-6 criticalPath; click 1-6 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914315&udv=0" 1-14["retrieve-tests-metadata (50초)"]; click 1-14 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=8356697&udv=0" 1-15["detect-tests (1분)"]; click 1-15 "https://app.periscopedata.com/app/gitlab/652085/EP---Jobs-Durations?widget=10113603&udv=1005715" 2_5-1["rspec & db jobs (30~50분)"]; class 2_5-1 criticalPath; click 2_5-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations" 2_5-1 --> 1-1 & 1-3 & 1-6 & 1-14 & 1-15; ac-1["rspec:artifact-collector (30초)<br/>('needs' 제한의 해결책)"]; class ac-1 criticalPath; ac-1 --> 2_5-1; 3_2-1["rspec:coverage (3분)"]; class 3_2-1 criticalPath; click 3_2-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=7248745&udv=0" 3_2-1 --> ac-1; 4_3-1["rspec:undercoverage (1.3분)"]; class 4_3-1 criticalPath; click 4_3-1 "https://app.periscopedata.com/app/gitlab/652085/EP---Jobs-Durations?widget=13446492&udv=1005715" 4_3-1 --> 3_2-1;

리뷰 앱 파이프라인

참조 파이프라인.

graph RL; classDef criticalPath fill:#f66; 1-2["build-qa-image (2분)"]; click 1-2 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914325&udv=0" 1-5["compile-production-assets (12분)"]; class 1-5 criticalPath; click 1-5 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914312&udv=0" 2_3-1["build-assets-image (1.1분)"]; class 2_3-1 criticalPath; 2_3-1 --> 1-5 2_6-1["start-review-app-pipeline (52분)"]; class 2_6-1 criticalPath; click 2_6-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations" 2_6-1 --> 2_3-1 & 1-2;

엔드 투 엔드 파이프라인

참조 파이프라인.

graph RL; classDef criticalPath fill:#f66; 1-2["build-qa-image (2분)"]; click 1-2 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914325&udv=0" 1-5["compile-production-assets (12분)"]; class 1-5 criticalPath; click 1-5 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914312&udv=0" 1-15["detect-tests"]; click 1-15 "https://app.periscopedata.com/app/gitlab/652085/EP---Jobs-Durations?widget=10113603&udv=1005715" 2_3-1["build-assets-image (1.1분)"]; class 2_3-1 criticalPath; 2_3-1 --> 1-5 2_4-1["e2e:test-on-omnibus-ee (103분)"]; class 2_4-1 criticalPath; click 2_4-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914305&udv=0" 2_4-1 --> 1-2 & 2_3-1 & 1-15;

CI 구성 내부

전용 CI 구성 내부 페이지를 참조하세요.

성능

전용 CI 구성 성능 페이지를 참조하세요.


Development documentation으로 돌아가기