Gems 개발 지침
GitLab은 Gems를 모놀리식 코드베이스에서 코드 재사용성과 모듈성을 개선하는 도구로 사용합니다.
우리는 기능이 고도로 격리되어 있고, 다른 애플리케이션에서 사용하고 싶거나 더 넓은 커뮤니티에 도움이 될 것이라고 생각되는 라이브러리를 코드베이스에서 추출합니다.
코드를 gem으로 추출하면 gem이 애플리케이션 코드에 대한 숨겨진 종속성을 포함하지 않도록 보장됩니다.
기능이 격리될 수 있다고 간주되는 경우에는 항상 Gems를 사용해야 하며, GitLab의 비즈니스 논리와 분리되어 별도로 개발될 수 있습니다.
새로운 gems를 추출할 기회가 있는 Rails 코드베이스에서 가장 좋은 위치는 lib/ 폴더입니다.
우리의 lib/ 폴더는 일반/범용, GitLab 전용 및 나머지 코드베이스와 밀접하게 통합된 코드의 혼합입니다.
코드베이스의 일부를 Gem으로 추출할지 말지를 결정하기 위해 다음 질문을 스스로에게 해보세요:
-
이 코드는 별도의 작은 프로젝트로 수행할 수 있는 일반적이거나 범용적인 코드인가?
-
모놀리식 구조 외부에서 내부적으로 사용될 것이라고 예상하는가?
-
넓은 커뮤니티에 유용하여 별도의 구성 요소로 릴리스해야 한다고 생각하는가?
위 질문들 중 하나라도 예라고 대답하면, 새로운 Gem 생성을 강하게 고려해야 합니다.
같은 레포지토리 에서 새로운 Gem을 생성하여 시작할 수 있으며, 후에 더 넓은 커뮤니티에서 사용될 예정이라면 별도의 레포지토리로 마이그레이션할지 평가할 수 있습니다.
Gems 사용의 장점
Gems를 사용하면 코드 유지 관리에 여러 가지 이점이 있습니다:
-
코드 재사용성 - Gems는 단일 목적을 제공하는 격리된 라이브러리입니다. Gems를 사용할 때, 공통 함수는 잘 문서화되고 테스트되며 서로 다른 애플리케이션에서 재사용할 수 있는 간단한 패키지로 격리될 수 있습니다.
-
모듈성 - Gems는 특정 기능을 자체 포함된 라이브러리 내에 캡슐화하여 격리를 생성하는 데 도움을 줍니다. 이는 코드를 더 잘 조직하고, 주어진 모듈의 소유자를 더 잘 정의하며, 특정 gems를 유지 관리하거나 업데이트하기 쉽게 만듭니다.
-
소형 - 설계상 Gems는 격리된 함수 집합을 구현하므로 작습니다. 작은 프로젝트는 이해하고 확장하며 유지 관리하기 훨씬 더 쉽습니다.
-
테스트 - Gems는 작기 때문에 모든 테스트를 실행하는 속도가 훨씬 더 빠르며, gem의 테스트를 아주 철저하게 할 수 있습니다. gem은 패키징되어 자주 변경되지 않기 때문에, 테스트를 덜 자주 실행하여 CI 테스트 시간을 개선할 수 있습니다.
Gem 이름 짓기
Gems는 세 가지 다른 경우에 해당할 수 있습니다:
-
unique_gem
: gem 이름에 GitLab에 특정한 내용을 포함하지 않는 경우gitlab
을 포함하지 마세요. -
existing_gem-gitlab
: 공개적으로 사용 가능한 gem을 포크하여 수정/확장하는 경우,-gitlab
접미사를 추가합니다. Rubygems의 규칙에 따라. -
gitlab-unique_gem
: GitLab 프로젝트의 맥락에서만 유용한 gem에는gitlab-
접두사를 포함합니다.
기존 gem의 예:
-
y-rb
: yrs를 위한 Ruby 바인딩. Yrs “wires”는 Yjs 프레임워크의 Rust 포트입니다. -
activerecord-gitlab
:activerecord
공개 gem에 GitLab 전용 패치를 추가합니다. -
gitlab-rspec
및gitlab-utils
: 특정 맥락에서 도움을 주거나 코드를 재사용하기 위한 GitLab 전용 클래스 집합입니다.
같은 레포지토리에서
기존 코드베이스에서 Gems를 추출할 때, GitLab 모노레포의 gems/
에 배치하세요.
이렇게 하면 Gems의 장점(모듈화된 코드, 개발에서 테스트를 더 빠르게 실행할 수 있음)을 누릴 수 있으며,
복잡성을 방지합니다(레포지토리 간 변경 사항 조정, 새로운 권한, 여러 프로젝트 등).
같은 레포지토리에 저장된 Gems는 Gemfile
에서 path:
구문으로 참조해야 합니다.
경고: 악의적인 사용자들이 추출한 Gems의 이름을 침해하는 것을 방지하기 위해,
gems 이름 예약하기 지침을 따르세요.
새로운 Gem 생성 및 사용
새로운 gem을 추가하는 예제는 다음과 같습니다: !121676.
-
Gem naming 규칙을 따라 gem에 좋은 이름을 선택하세요.
-
bundle gem gems/<name-of-gem> --no-exe --no-coc --no-ext --no-mit
를 사용하여gems/<name-of-gem>
에 새로운 gem을 생성하세요. -
rm -rf gems/<name-of-gem>/.git
를 사용하여gems/<name-of-gem>
의.git
폴더를 제거하세요. -
gems/<name-of-gem>/README.md
를 편집하여 Gem에 대한 간단한 설명을 제공하세요. -
gems/<name-of-gem>/<name-of-gem>.gemspec
을 편집하고 다음 예제와 같이 Gem에 대한 세부 정보를 작성하세요:# frozen_string_literal: true require_relative "lib/name/of/gem/version" Gem::Specification.new do |spec| spec.name = "<name-of-gem>" spec.version = Name::Of::Gem::Version::VERSION spec.authors = ["group::tenant-scale"] spec.email = ["engineering@gitlab.com"] spec.summary = "Gem 요약" spec.description = "Gem이 수행하는 작업에 대한 보다 설명적인 텍스트입니다." spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/<name-of-gem>" spec.license = "MIT" spec.required_ruby_version = ">= 3.0" spec.metadata["rubygems_mfa_required"] = "true" spec.files = Dir['lib/**/*.rb'] spec.require_paths = ["lib"] end
-
gems/<name-of-gem>/.rubocop.yml
을 다음으로 업데이트하세요:inherit_from: - ../config/rubocop.yml
-
새로 추가된 Gem의 CI를 구성하세요:
-
gems/<name-of-gem>/.gitlab-ci.yml
에 추가하세요:include: - local: gems/gem.gitlab-ci.yml inputs: gem_name: "<name-of-gem>"
-
.gitlab/ci/gitlab-gems.gitlab-ci.yml
에 추가하세요:include: - local: .gitlab/ci/templates/gem.gitlab-ci.yml inputs: gem_name: "<name-of-gem>"
-
-
Gemfile
에 Gem을 다음과 같이 참조하세요:gem '<name-of-gem>', path: 'gems/<name-of-gem>'
Gem에 대한 의존성 지정하기
Gem이 자체 Gemfile
을 가지고 있지만,
실제 애플리케이션에서는 모노리스 GitLab의 최상위 Gemfile
이 사용됩니다.
즉, gem의 Gemfile
은 최상위 Gemfile
과 충돌할 수 있는 의존성의 버전을 사용하지 않아야 하며,
가능하다면 동일한 의존성을 사용하도록 노력해야 합니다.
이의 한 예는 Rack입니다. 만약 모노리스가 Rack 2를 사용하고 있고
우리가 Rack 3으로 업그레이드 중이라면,
우리가 개발하는 모든 Gems는 Rack 2에 대해서도 테스트되어야 하며,
CI에서 별도의 Gemfile
을 사용하는 경우 Rack 3에 대해서도 옵션으로 테스트해야 합니다.
여기에서 예제를 확인하세요.
이것은 Rack에만 국한되지 않고, 모든 의존성에 적용됩니다.
Gem 추출의 예시
gitlab-utils
는 GitLab 개발자가 사용하는 strong_memoize
또는 Gitlab::Utils.to_boolean
과 같은 일반 내장 함수를 구현하는 클래스 집합을 포함하는 Gem입니다.
gitlab-database-schema-migrations
는 데이터베이스 마이그레이션이 저장되는 방식을 개선하는 Rails 프레임워크에 대한 우리의 확장을 포함하는 잠재적인 Gem입니다. 이것은 Rails 위에 구축되었으며 GitLab 애플리케이션에 국한되지 않으며 다른 프로젝트에서도 일반적으로 사용할 수 있거나 잠재적으로 업스트림될 수 있습니다.
gitlab-database-load-balancing
는 이전과 유사하게 Rails 데이터베이스 처리를 위한 GitLab 특정 로드 밸런싱을 구현하는 잠재적인 Gem입니다. 이는 상당히 복잡하고 매우 특정한 코드로, 이 복잡성을 격리된 상태에서 잘 테스트된 Gem으로 유지하는 것이 큰 단일 코드베이스에서 이 복잡성을 제거하는 데 도움이 될 것입니다.
gitlab-flipper
는 코드베이스 내에서 기능 플래그를 지원하기 위한 모든 사용자 정의 확장을 구현하는 또 다른 잠재적인 Gem입니다. 시간이 지남에 따라 단일 코드베이스는 기능 플래그 사용에 대한 체크, 일관성 검사 추가 및 기능 플래그 소유자를 추적하는 다양한 도우미 도구가 포함되면서 성장했습니다. 이것은 GitLab 비즈니스 로직의 일부가 아니며, 우리의 Flipper 구현을 더 잘 추적하고 가능하면 GitLab Feature Flags로 변경하는데 훨씬 더 용이할 수 있습니다.
activerecord-gitlab
은 GitLab 특정 Active Record 패치를 추가하는 gem입니다. 이러한 내용을 별도로 관리하여 복잡성을 격리하는 것이 매우 바람직합니다.
다른 잠재적 사용 사례
gitlab-ci-config
는 .gitlab-ci.yml
을 파싱하는 데 사용되는 모든 CI 코드를 포함하는 잠재적인 Gem입니다. 이 코드는 현재 적절한 추상화 부족으로 GitLab 애플리케이션과 약간 얽혀 있습니다. 그러나 이를 전용 Gem으로 이동하면 GitLab 애플리케이션과의 통합을 처리하는 다양한 어댑터를 구축할 수 있습니다. 인터페이스는 예를 들어 includes:
를 해결하는 어댑터를 정의합니다. gitlab-ci-config
Gem이 생기면 GitLab 내에서 그리고 GitLab Rails 및 GitLab CLI 외부에서도 사용할 수 있습니다.
외부 저장소에서
일반적으로 우리는 이것을 수행하기 전에 신중하게 생각해야 하며, 이는 심각한 단점이 있습니다.
외부 저장소에 저장된 Gem은 Gemfile
에서 version
구문으로 참조해야 합니다.
항상 RubyGems에 게시되어야 합니다.
예시
GitLab에서는 여러 외부 gem을 사용합니다:
잠재적 단점
-
Gem은 - GitLab에서 유지 관리하더라도 - 메인 Rails 애플리케이션과 동일한 코드 검토 프로세스를 반드시 거치지 않습니다. 이는 애플리케이션 보안에 대해 특히 중요합니다.
-
CI/CD를 처음부터 설정해야 하며, 일관된 코드 검토 기준을 지원하는 Danger와 같은 도구들이 필요합니다.
-
코드를 별도의 프로젝트로 추출하는 것은 기능 변경을 위해 최소 두 개의 병합 요청이 필요합니다: 기능 변경을 위한 gem의 하나와 버전을 증가시키기 위한 Rails 앱의 하나입니다.
-
gitlab-rails
와의 통합이 두 번째 MR을 요구하므로 통합 문제가 늦게 발견될 수 있습니다. -
gitlab-rails
에 비해 검토자 및 유지 관리자의 풀이 작기 때문에 코드 검토가 더 오래 걸릴 수 있으며 “버스 팩터”의 영향이 증가합니다. -
새 Gem 버전을 릴리스하는 방식에 대한 일관된 워크플로우가 없습니다. 현재는 라이브러리 유지 관리자의 재량에 따라 결정됩니다.
-
코드에 대한 가시성과 노출이 적어 GitLab에서 지식 고립을 조장합니다.
-
우리는 GitLab 검토자를 유지 관리자로 승진시키기 위한 잘 정의된 프로세스를 가지고 있습니다. 이는 추출된 라이브러리에는 해당되지 않으며, 코드 검토의 기준을 낮출 위험을 증가시키고 변경 사항을 배포할 위험이 증가합니다.
-
Gem의 자체 사용 필요는 더 넓은 커뮤니티의 요구와 일치하지 않을 수 있습니다. 일반적으로 최신 버전의 Gem을 사용하지 않는다면 이는 경고 신호일 수 있습니다.
잠재적인 장점
- 더 작은 리포지토리에서 CI/CD가 실행되므로 더 빠른 피드백 루프.
- 프로젝트를 더 넓은 커뮤니티에 공개하고 외부 기여로부터 이익을 얻을 수 있습니다.
- 리포지토리 소유자는 변경 사항을 검토하기에 가장 적합한 대상일 가능성이 높아
gitlab-rails
에서 적합한 리뷰어를 찾는 필요성을 줄입니다.
루비 젬 생성 및 게시
새로운 젬의 프로젝트는 항상 gitlab-org/ruby/gems
네임스페이스에서 생성되어야 합니다:
-
젬에 적합한 이름을 결정합니다. GitLab 소유의 젬인 경우, 젬 이름 앞에
gitlab-
를 붙입니다. 예:gitlab-sidekiq-fetcher
. -
필요한 경우 로컬에서 젬이나 포크를 생성합니다.
-
빈
0.0.1
버전의 젬을 rubygems.org에 게시하여 젬 이름을 예약합니다.
-
다음 명령을 실행하여 새 젬의 소유자로
gitlab_rubygems
사용자를 추가합니다:gem owner <gem-name> --add gitlab_rubygems
- Rémy Coutable에게 연락하여 Rubygems 위원회 프로젝트에서 소유권을 확인합니다.
-
선택 사항. 다음 사용자 중 일부 또는 모두를 공동 소유자로 추가합니다:
-
선택 사항. 기타 관련 개발자를 공동 소유자로 추가합니다.
-
https://rubygems.org/gems/<gem-name>
를 방문하여 젬이 성공적으로 게시되었는지 확인하고gitlab_rubygems
가 소유자임을 확인합니다. -
gitlab-org/ruby/gems
그룹 (또는 해당 하위 그룹) 에서 프로젝트를 생성합니다:-
새 프로젝트에 대한 지침을 따릅니다.
-
CI/CD 구성 설정에 대한 지침을 따릅니다.
-
gem-release
CI 구성 요소를 사용하여 새로운 젬 버전을 릴리스하고 게시하려면.gitlab-ci.yml
에 다음을 추가합니다:include: - component: $CI_SERVER_FQDN/gitlab-org/components/gem-release/gem-release@~latest
이 작업은 젬을 빌드하고 게시하는 작업을 처리합니다(젬 패키지를 게시하기 위해
gitlab-org/ruby/gems
그룹에서 상속한gitlab_rubygems
Rubygems.org API 토큰을 사용함) 원래의 태그, 릴리스를 만들고 릴리스 메모를 채우기 위해변경 로그 데이터 생성 API 엔드포인트를 사용합니다.
언제 변경 로그 항목 파일을 생성할지와 방법에 대한 지침은 전용 변경 로그 항목 페이지를 참조하세요.
GitLab 프로젝트와 일관성을 유지하기 위해 젬 프로젝트는
.gitlab/changelog_config.yml
에 [gitlab-styles] 젬의 같은 내용으로 변경 로그 YAML 구성 파일을 정의할 수 있습니다. -
릴리스 프로세스를 용이하게 하기 위해, 또한 [gitlab-styles] 젬의 같은 내용으로
.gitlab/merge_request_templates/Release.md
MR 템플릿을 생성할 수 있습니다(실제 젬 이름으로gitlab-styles
를 교체해야 함). -
프로젝트 게시에 대한 지침을 따릅니다.
-
노트: 경우에 따라 우리는 젬을 자체 네임스페이스로 이동할 수 있습니다. 예를 들어, 하나 이상의 프로젝트를 자연스럽게 가질 것으로 예상되는 경우(예: 별도의 라이브러리로서의 플러그인), 또는 GitLab 외부의 사용자가 이 프로젝트의 유지 관리자로 참여할 것으로 기대되는 경우입니다. 후자의 경우(GitLab 외부의 유지관리자)는 현재 GitLab에서 일하는 누군가가 GitLab에서 근무하는 기간 이후에도 젬을 계속 유지 관리하기를 원할 것이라면 해당할 수 있습니다.
vendor/gems/
의 목적
vendor/
의 목적은 GitLab 모노레포에 외부 종속성을 가져오는 것입니다.
외부 저장소가 있지만 단순함을 위해 모노레포에 저장하려고 합니다:
-
vendor/gems/
는 외부 저장소에서 스크립트 또는 수동으로 가져오는 경우에만 사용해야 합니다. -
vendor/gems/
는 내부 Gem을 저장하는 데 사용하지 말아야 합니다. -
vendor/gems/
는 GitLab 모노레포와 함께 빌드 가능하도록 수정 사항을 수용할 수 있습니다. -
gems/
는 GitLab 모노레포의 일부인 모든 내부 Gem을 저장하는 데 사용해야 합니다. -
RubyGems는 GitLab 모노레포의
gems/
에 없는 외부 저장 종속성에 사용해야 합니다.
vendor/gems
의 기존 Gems 처리
-
외부 저장소가 없고 현재
vendor/gems/
에 저장된 내부 Gems에 대하여:-
다른 저장소에서 사용되는 Gems의 경우:
-
해당 Gem을 자체 저장소로 마이그레이션합니다.
-
RubyGems를 통해 게시를 시작하거나 계속합니다.
-
이러한 Gems는
Gemfile
에서 버전으로 참조하고 RubyGems에서 가져옵니다.
-
-
모노레포에서만 사용되는 Gems의 경우:
-
새로운 버전을 RubyGems에 게시하지 않습니다.
-
이미 게시된 버전은 애플리케이션이 의존할 수 있으므로 RubyGems에서 가져오지 않습니다.
-
해당 Gems를
gems/
로 이동합니다. -
이러한 Gems는
Gemfile
에서path:
로 참조됩니다.
-
-
-
모노레포에 벤더 처리된 외부
vendor/gems/
의 경우:-
수정이 필요하고 아직 업스트림되지 않은 경우에는 저장소에서 유지합니다.
-
벤더 처리된 Gems는 제3자에 의해 게시될 수 있습니다.
-
해당 Gems는 우리가 RubyGems에 게시하지 않습니다.
-
이러한 Gems는 RubyGems에 의존할 수 없으므로
Gemfile
에서path:
로 참조됩니다.
-
rubygems.org에 대한 고려사항
Gem 이름 예약
공식 코드를 게시하기 전에 새 Gem을 포함한 공용 코드를 게시하기 전에 이름 도용자가 이름을 차지하는 것을 피하기 위해 precaution으로 Gem 이름을 예약할 수 있습니다.
Gem 이름을 예약하려면 Ruby Gem 생성 및 게시하기에서 다음 변경 사항을 따라 주세요:
-
버전으로
0.0.0
을 사용합니다. -
lib/NAME.rb
에raise "Reserved for GitLab"
의 내용을 포함하는 단일 파일을 포함합니다. -
build
와publish
를 수행하고 https://rubygems.org/gems/에서 성공 여부를 확인합니다.
계정 생성
GitLab에서 작업하기 위해 RubyGems.org에 계정을 생성할 계획이라면, 회사 이메일 계정을 사용해야 합니다.
유지 관리자 및 계정 변경
계정 이메일 또는 비밀번호, Gem 소유자 변경, Gem 삭제와 같은 모든 변화는 직접 책임이 있는 팀에 문제 또는 Slack(팀의 Slack 채널인 #rubygems
, #ruby
, #development
)을 통해 미리 전달해야 합니다.