copy.bara.sky라는 파일에 Starlark(Bazel의 파이썬 같은 언어)로 적는다.
핵심은 stateless — 진행 상태를 별도 DB가 아니라 목적지 커밋 메시지의 라벨에 저장해, 누가 돌려도 같은 결과가 난다.
(저장소: google/copybara · Apache-2.0 · Java 96% · ★2.8k · 주간 스냅샷 릴리스 v20260629 · TrendShift Monthly 13위)
이 저장소가 대체 무엇인가.
구글은 거의 모든 코드를 하나의 거대한 저장소(모노레포, 사내 이름 google3)에서 개발한다. 그런데 Bazel·gVisor·Copybara처럼 GitHub에도 공개하는 프로젝트가 있다. 사내에서 코드를 고칠 때마다, 그걸 그대로 밖에 낼 수는 없다 — 사내 전용 경로·기밀 파일·내부 참조를 걷어내고 "공개용"으로 다듬어야 한다. 이 반복 작업을 사람이 손으로 하면 지옥이다.
Copybara는 이 "변형 후 이사"를 자동화한다. 어디서(Origin) → 무엇을 바꿔서(Transformations) → 어디로(Destination)를 copy.bara.sky 파일에 적어 두면, 명령 한 줄로 매번 같은 규칙이 적용돼 코드가 흘러간다. 비유하면 — 내용물은 그대로 두되 라벨과 포장만 목적지 규격에 맞게 바꿔 부치는 "스마트 택배 시스템"이다.
README의 자기소개는 딱 한 줄이다: "A tool for transforming and moving code between repositories." ("저장소들 사이에서 코드를 변형하고 이동시키는 도구.") 이어서 "Copybara is a tool used internally at Google." — 즉 마케팅용으로 급조된 게 아니라 구글이 실제로 매일 쓰는 사내 도구를 오픈소스로 공개한 것이다.
가장 흔한 시나리오는 README의 표현대로 "maintaining a confidential repository and a public repository in sync" — 기밀 레포와 공개 레포를 계속 동기화하는 것이다. 규칙은 하나다: 둘 중 하나를 반드시 "권위 있는 원본(source of truth)"으로 정한다. 진실은 한 곳에만 있어야 충돌이 안 나기 때문이다. 다만 기여(contribution)는 어느 쪽에서 들어와도 되고, Copybara가 그걸 원본 쪽으로 다시 실어 나른다.
보통 이런 동기화 도구는 "어디까지 옮겼나"를 어딘가(DB·파일)에 기록해야 한다. Copybara는 그러지 않는다. README 왈 "it stores the state in the destination repository (As a label in the commit message)." 즉 목적지 커밋 메시지 안에 GitOrigin-RevId: <해시> 같은 라벨을 심어, "원본의 어느 지점까지 반영했는지"를 거기서 읽는다. 덕분에 여러 사람·서버가 같은 설정으로 돌려도 같은 결과가 나오고, 관리할 외부 상태 저장소가 없다.
"구글이 진짜로 쓰는 도구" · Starlark 설정 · stateless 우아함 · git 표준 도구로는 안 되는 영역.
Copybara는 2016년부터 있던 오래된 프로젝트인데도 월간 트렌딩에 다시 올라왔다. 이유는 몇 가지가 겹친다. 첫째, "빅테크가 사내에서 실제로 굴리는 인프라 도구"라는 후광이다. Bazel(빌드), Copybara(레포 동기화)는 구글 오픈소스 릴리스 파이프라인의 두 축이라, "구글은 어떻게 모노레포를 GitHub에 뿌리나?"의 정답지 역할을 한다. 둘째, AI 코딩 시대에 여러 레포를 자동으로 오가며 코드를 옮기는 수요가 늘면서 다시 조명받았다.
| 도구 | 할 수 있는 것 | 한계 |
|---|---|---|
| git subtree / submodule | 다른 레포를 하위 폴더로 끼워 넣기 | 경로 재배치·파일 필터·기밀 제거 같은 "변형"이 안 됨 |
| git filter-repo | 히스토리 통째로 재작성(경로 이동·삭제) | 일회성 마이그레이션용. 지속적 양방향 동기화엔 부적합 |
| 수작업 cherry-pick | 커밋 하나씩 골라 옮기기 | 반복·대량엔 비현실적, 실수 위험 |
| Copybara | 변형 규칙 + 반복 이동 + 상태 자동 추적 | 설정(Starlark)·JDK/Bazel 학습 곡선 |
git subtree가 "복사-붙여넣기"라면, Copybara는 "번역 + 정기 배송 계약"이다. 단순히 코드를 복제하는 게 아니라, 매번 정해진 규칙으로 손질(경로 치환·파일 제외·저작자 정리)한 다음 목적지로 보낸다. 그리고 "지난번에 어디까지 보냈는지"를 스스로 기억(커밋 라벨)해서, 다음 배송 때 그 다음부터 이어서 보낸다.
주목도를 끌어올린 구체적 요인은 다음과 같다.
① 설정이 곧 코드 — Starlark. copy.bara.sky는 Bazel과 같은 Starlark 언어로 쓴다. Bazel을 쓰는 조직엔 이미 친숙한 문법이라 진입장벽이 낮고, 설정을 함수·변수·반복문으로 프로그래밍할 수 있다(정적 YAML보다 유연).
② 확장 가능한 플러그인 구조. Origin(어디서)·Destination(어디로)이 인터페이스로 추상화돼 있어 Git·GitHub·Gerrit·GitLab·로컬 폴더·Mercurial을 같은 방식으로 다룬다. 새 백엔드도 끼워 넣을 수 있다.
③ 가역 변환(reversible). "밖으로 내보낼 때 경로를 A→B로 바꿨다면, 안으로 받을 땐 자동으로 B→A로 되돌린다." 많은 변환이 역방향을 자동 계산하도록 설계돼, 양방향 동기화가 깔끔하다.
④ 코드가 곧 교과서. "설정 파일 언어를 프로그램에 임베딩하는 법"(Java에 Starlark 인터프리터를 심는 법)을 배우기에 이만한 실전 예제가 드물다. 뒤에서 자세히 본다.
순수 Java(96%) + 임베딩된 Starlark 인터프리터 · Bazel(Bzlmod) 빌드 · JDK 21.
언어 구성은 단순하다 — Java 96.4%, Starlark 2.8%, 나머지가 셸·Docker·YAML이다. 여기서 "Starlark"는 두 얼굴을 가진다: (1) Bazel 빌드 파일(BUILD, .bzl)로서의 Starlark, 그리고 (2) 사용자가 쓰는 설정 파일(copy.bara.sky)을 실행하기 위해 Java 안에 심어 둔 Starlark 인터프리터. 제품 로직 자체는 100% Java다.
| 영역 | 도구 | 역할 |
|---|---|---|
| 언어 | Java (JDK 21) | .bazelrc가 java_language_version=21 고정. 클래스파일 버전 65.0 |
| 빌드 | Bazel (Bzlmod) | MODULE.bazel 기반. WORKSPACE 없이 모던 모듈 방식 |
| 설정 언어 | Starlark (net.starlark.java) | copy.bara.sky를 실행. in-tree로 벤더링 |
| CLI 파싱 | JCommander 2.0 | 서브커맨드·플래그 파싱(@Parameter) |
| 핵심 유틸 | Guava 33.4.6 | Immutable 컬렉션 등 구글 표준 유틸, 전방위 사용 |
| 정규식 | re2j 1.7 | 설정의 core.replace 등에서 RE2 문법 사용(선형시간) |
| AutoValue 1.11 | 불변 값 클래스 자동 생성(보일러플레이트 제거) |
| Flogger 0.7.4 | 구글 표준 로깅 API |
| Gson 2.10 · tomlj · jsoup | JSON · TOML · HTML 파싱(설정용 json/toml/html 모듈) |
| google-http-client | GitHub·Gerrit·GitLab REST API 호출 |
| Error Prone · jsr305 | 정적 분석 · null 어노테이션 |
| 도구 | 역할 |
|---|---|
| JUnit 4.13 | 테스트 러너(@Test) — 약 221개 테스트 파일 |
| Truth 1.3 | 구글 fluent assertion(assertThat(x).contains(...)) |
| TestParameterInjector | 구글 파라미터화 테스트 러너(한 테스트를 여러 입력으로) |
| Mockito 4.5 | 목(mock) 객체 |
| Jimfs 1.2 | 인메모리 파일시스템 — 파일 조작 테스트를 디스크 없이 |
| 주간 스냅샷 | GitHub Actions가 매주 월요일 copybara_deploy.jar(uberjar)를 빌드해 v<YYYYMMDD> 태그로 릴리스 |
| Docker | 멀티스테이지 빌드(golang→bazel→temurin 21-jre). "실험적"이라 명시 |
| Google Cloud Build | cloudbuild.yaml — 이미지를 Artifact Registry로 푸시 |
| Arch Linux | aur/copybara-git로도 설치 가능 |
Copybara는 Starlark 엔진(net.starlark.java)을 외부 라이브러리로 받지 않고 저장소 안(third_party/bazel/main/java/net/starlark/java/)에 통째로 복사(vendoring)해 둔다. 이유는 Bazel과 같은 Starlark 구현을 정확히 같은 버전으로 쓰기 위해서다. 설정 언어의 동작이 Bazel 사용자 기대와 어긋나면 안 되기 때문. "핵심 의존성은 버전 드리프트를 막으려 아예 내재화한다"는, 대규모 인프라 도구다운 선택이다.
Origin → Transformations → Destination 파이프라인 · 4가지 WorkflowMode · Starlark 임베딩.
Copybara의 심장은 core.workflow 하나다. 워크플로는 "어디서(origin) 읽어 → 무엇으로(transformations) 바꿔 → 어디로(destination) 쓸지"를 묶은 단위다. Java에서는 Workflow<O extends Revision, D extends Revision> 클래스가 이 모델을 담는다. 전체 흐름을 그림으로 보면 이렇다.
같은 "이동"이라도 어떻게 커밋을 만드느냐가 상황마다 다르다. Copybara는 이를 WorkflowMode라는 Java enum 4종으로 못박아 뒀다.
| 모드 | 동작 | 언제 |
|---|---|---|
| SQUASH (기본) | 원본의 여러 변경을 목적지에 커밋 1개로 압축 | 내부 히스토리를 감추고 "스냅샷"만 공개할 때 |
| ITERATIVE | 원본 변경을 하나씩 그대로 재생(커밋별) | 커밋 히스토리를 보존해 옮길 때 |
| CHANGE_REQUEST | 공통 부모 기준 diff를 PR/Gerrit 변경 형태로 | 외부 기여를 리뷰 요청(PR/CL)으로 만들 때 |
| CHANGE_REQUEST_ FROM_SOT | 원본(Source of Truth)에서 가져와 리뷰용으로 | 이미 SoT에 있는 변경을 다른 시스템에서 리뷰할 때 |
SQUASH는 "이번 분기 작업을 한 장으로 요약해 제출", ITERATIVE는 "매일 쓴 일기를 날짜별 그대로 옮겨 적기"다. CHANGE_REQUEST는 "여기 고친 부분이니 검토해 주세요"라며 리뷰 요청(PR)으로 포장하는 것. 같은 코드 이동도 목적에 따라 커밋을 뭉칠지·펼칠지·리뷰로 낼지가 달라진다.
copy.bara.sky는 어떻게 실행될까? Copybara는 Bazel의 Starlark 인터프리터(net.starlark.java)를 그대로 내장한다. 파서는 config/SkylarkParser.java다. 흐름의 핵심 한 줄은 이렇다.
// config/SkylarkParser.java — 설정을 실행하는 핵심
Module module = Module.withPredeclared(semantics, environment);
// environment = core, git, folder, metadata... (자바로 만든 모듈들)
Program prog = Program.compileFile(StarlarkFile.parse(input, options), module);
// 이제 copy.bara.sky 안에서 core.workflow(...), git.origin(...) 호출 가능
즉 Java로 만든 모듈들을 "미리 선언된 전역(predeclared globals)"으로 주입해 두면, 설정 파일에서 core.workflow(...)처럼 바로 부를 수 있다. 어떤 Java 메서드를 설정에 노출할지는 어노테이션으로 표시한다.
// folder/FolderModule.java — 자바 메서드를 Starlark 함수로 노출
@StarlarkMethod(
name = "origin",
doc = "A folder origin is a origin that uses a folder as input...",
parameters = { /* @Param(name="inside_symlinks_mode", ...) 등 */ })
public FolderOrigin origin(...) throws EvalException { ... }
@StarlarkBuiltin(name="git")을, 메서드에 @StarlarkMethod(name="origin")을 붙이면, 어노테이션 프로세서가 Java ↔ Starlark 연결 코드를 자동 생성한다. 그 결과 설정 파일에서 git.origin(...)이 실제로는 GitModule.origin(...) 자바 메서드를 부르게 된다. "설정 언어 함수 = 자바 메서드"를 잇는 다리다.
이 모든 모듈이 한 곳 ModuleSupplier.java에서 인스턴스화·등록된다. CoreModule, GitModule, FolderModule, MetadataModule, Authoring.Module, HgModule, FormatModule, PatchModule, HttpModule … 여기가 "설정에서 쓸 수 있는 API 전체 목록"이다.
core.move("a", "b")는 순방향으로 a→b 경로 이동이다. Copybara는 이 변환의 역(reverse)을 자동으로 안다 — b→a. 그래서 core.reverse([...])로 워크플로 방향을 통째로 뒤집을 수 있고, 양방향 동기화가 성립한다. 되돌릴 수 없는 변환(예: 무조건 삭제)은 core.transform(..., reversal=[...])로 역방향을 명시해 줘야 한다. 내부적으로 ExplicitReversal·Sequence가 이 로직을 담당한다.
앞서 말한 트레이드마크다. Copybara는 실행 이력을 목적지 커밋 메시지의 라벨(예: GitOrigin-RevId: abc123…)에 남긴다. 다음 실행 때 목적지의 최신 커밋에서 이 라벨을 읽어 "원본 어디까지 왔는지"를 복원한다. 외부 상태 저장소가 필요 없으니, 여러 사람·CI가 같은 설정으로 아무 때나 돌려도 결정론적으로 같은 결과가 난다.
"Java 소스 한 트리 + 거울처럼 대응되는 테스트 트리 + 벤더링된 Starlark".
세 덩어리로 나눠 읽으면 명료하다.
① 제품 코드 java/com/google/copybara/. 여기가 전부다. 루트에 오케스트레이션 클래스(Main·Workflow·WorkflowMode·CoreModule·각종 *Cmd)가 있고, 그 아래 기능별 패키지가 붙는다. 가장 큰 패키지는 git/(59파일)로, GitHub·Gerrit·GitLab까지 커버하는 실제 세계의 주력이다.
② 거울 테스트 javatests/. 메인 트리와 패키지 구조가 1:1로 대응된다(구글 표준). 어떤 클래스의 동작이 궁금하면 같은 경로의 테스트 파일을 열면 사용 예가 나온다 — 테스트가 곧 문서다.
③ 벤더링된 Starlark third_party/.../net/starlark/java/. 설정 언어 엔진을 저장소 안에 통째로 넣어 뒀다(eval/·syntax/·annot/ 등). 3장에서 본 "핵심 의존성 내재화"의 실물이다.
이 저장소에서 "무엇을 배울 수 있는가" — 개념 → 코드 위치 → 실습 아이디어.
YAML·JSON 대신 진짜 프로그래밍 언어(Starlark)를 설정으로 쓰고, 그 인터프리터를 Java 앱에 심는 법을 실물로 배운다. config/SkylarkParser.java → ModuleSupplier.java → 각 *Module.java로 이어지는 @StarlarkMethod 브리지가 핵심.
실습: FolderModule.java(가장 작고 깔끔) 한 파일을 정독하고, @StarlarkMethod가 붙은 메서드가 copy.bara.sky의 어떤 호출과 대응되는지 짝지어 보기.
Origin·Destination·Transformation이라는 3개 인터페이스만으로 Git·Gerrit·GitLab·폴더·Mercurial을 동일하게 다룬다. "코어는 인터페이스에만 의존하고, 구현은 갈아 끼운다"는 의존성 역전의 교과서적 사례.
실습: Origin.java 인터페이스를 읽고, GitOrigin과 FolderOrigin이 같은 계약을 어떻게 다르게 구현하는지 비교.
"순방향 변환을 정의하면 역방향이 자동으로 따라온다"는 우아한 아이디어. transform/의 ExplicitReversal·Sequence·Replace를 보면, 각 변환이 reverse()를 어떻게 구현하는지 드러난다. 양방향 동기화가 왜 가능한지 이해하는 열쇠.
실습: 간단한 copy.bara.sky에서 core.move 하나를 넣고, core.reverse(transformations)로 감싼 워크플로가 파일을 어떻게 되돌리는지 로컬 폴더로 확인.
외부 DB 없이 목적지 커밋 메시지 라벨에 진행 위치를 기록하는 패턴. 분산 환경에서 결정론(누가 돌려도 같은 결과)을 확보하는 실전 기법이다. transform/metadata/와 라벨 처리 로직을 보라.
실습: 로컬 bare repo로 워크플로를 두 번 실행하고, 목적지 커밋 메시지에 어떤 라벨이 붙는지, 두 번째 실행이 그걸 어떻게 읽는지 관찰.
WORKSPACE 없이 MODULE.bazel만으로 Java·Go·Proto 의존성을 관리하는 모던 Bazel, 그리고 GitHub Actions로 매주 uberjar를 자동 릴리스하는 파이프라인(release.yaml)을 통째로 읽을 수 있다.
실습: bazel build //java/com/google/copybara:copybara_deploy.jar로 실행 가능한 uberjar를 직접 만들어 보기(JDK 21 필요).
migrate·info·validate·regenerate·onboard 같은 서브커맨드를 CopybaraCmd 인터페이스로 통일하고, @Parameter로 플래그를 선언하는 구조. "명령행 도구를 확장 가능하게 짜는 법"의 참고서.
실습: Main.java에서 서브커맨드가 등록·디스패치되는 부분을 따라가고, 새 더미 서브커맨드를 하나 추가해 보기.
무거운 GPU·서버가 필요 없다 — JDK와 git만 있으면 노트북에서 충분.
| 항목 | 요구/권장 |
|---|---|
| 실행(사전 빌드 jar) | Java 런타임 21 이상 (클래스파일 65.0). java -jar copybara_deploy.jar ... |
| 소스 빌드 | JDK 21 + Bazel. bazel build //java/com/google/copybara |
| 필수 외부 도구 | git (기본 대상). 실행 시 시스템의 git 바이너리를 호출 |
| 선택 외부 도구 | mercurial(hg origin)·quilt(patch) — 해당 기능 쓸 때만 |
| Docker(선택) | docker build -t copybara . 후 docker run. "실험적"이라 명시 |
| 메모리/CPU | 일반 개발 노트북으로 충분. 무거운 연산 없음(git 조작 + 텍스트 변환) |
| 네트워크 | 원격 origin/destination(GitHub 등) 접근 시 필요. 로컬 폴더끼리면 오프라인 가능 |
소스 빌드는 Bazel 학습이 필요해 벽이 높지만, 주간 릴리스에 올라온 copybara_deploy.jar를 받아 java -jar로 바로 실행하거나 Docker 이미지를 쓰면 몇 분 만에 첫 워크플로를 돌릴 수 있다. 학습 목적이면 로컬 폴더 두 개 사이 이동부터 시작하는 것이 안전하다(원격 인증 없이).
README가 못박는다 — 주간 릴리스는 "released automatically without any manual testing, version compatibility or correctness guarantees." 즉 수동 테스트·버전 호환·정확성 보장이 없다. 또한 시맨틱 버전이 아니라 v<날짜> 태그뿐이라, 프로덕션에 물릴 땐 특정 스냅샷을 고정(pin)하고 충분히 검증해야 한다. README의 "Install JDK 11" 문구는 오래된 흔적으로, 실제 기준은 .bazelrc·Dockerfile의 JDK 21이다.
난이도별로 손을 움직여 보는 5가지.
빌드 없이 Docker 이미지로 도움말부터 띄운다.
# 이미지 빌드 후 help
docker build --rm -t copybara .
docker run -it -v "$(pwd)":/usr/src/app copybara help
출력되는 서브커맨드(migrate/info/validate…) 목록과 플래그를 훑어보며 "이 도구가 무엇을 받는지" 감을 잡는다.
원격 인증 없이 안전하게 파이프라인을 체험한다. README 예제를 로컬로 축소한 copy.bara.sky를 만든다.
# copy.bara.sky (요지)
core.workflow(
name = "default",
origin = git.github_origin(url="https://github.com/google/copybara.git", ref="master"),
destination = git.destination(url="file:///tmp/foo"),
authoring = authoring.pass_thru("Me <me@example.com>"),
transformations = [ core.move("", "third_party/copybara") ],
)
(mkdir /tmp/foo ; cd /tmp/foo ; git init --bare)
copybara copy.bara.sky --force # 첫 이동은 --force
/tmp/foo에 생긴 커밋 메시지에서 상태 라벨을 찾아본다.
transformations에 core.replace(문자열 치환)와 core.remove(파일 제외)를 추가해 "공개용 손질"을 흉내 낸다. 그런 다음 워크플로를 core.reverse(...)로 감싸, 같은 규칙이 거꾸로 적용되는지 확인한다. validate 서브커맨드로 설정 오류도 잡아 본다.
core.workflow(..., mode="SQUASH")와 mode="ITERATIVE"를 각각 돌려, 목적지 커밋이 1개로 뭉치는지 / 원본 커밋별로 펼쳐지는지를 눈으로 비교한다. info 서브커맨드로 "다음에 무엇이 이동될지" 미리보기도 확인.
JDK 21 + Bazel로 copybara_deploy.jar를 직접 빌드하고, git.github_pr_destination(...)를 써서 실제 PR을 생성하는 워크플로를 CHANGE_REQUEST 모드로 구성한다(본인 소유 테스트 레포에서). 인증 토큰 설정과 metadata.squash_notes()로 커밋 메시지를 다듬는 것까지 해 보면 실무 감각이 완성된다.
4주 코스 — Starlark/Bazel 기초부터 직접 모듈 추가까지.
| 주차 | 주제 | 구체적 목표 |
|---|---|---|
| 1주차 | Starlark & copy.bara.sky 문법 | Starlark 언어 스펙 훑기 → README/examples.md의 설정을 읽고 직접 작성. glob·authoring·transformations 리스트 이해 |
| 2주차 | 3대 추상화 소스 읽기 | Origin·Destination·Transformation 인터페이스와 GitOrigin/GitDestination 구현 정독. Workflow.java의 필드로 전체 모델 파악 |
| 3주차 | WorkflowMode & Stateless | 4개 모드의 run() 로직 비교. 커밋 메시지 라벨로 상태를 복원하는 흐름 추적(metadata/) |
| 4주차 | Java↔Starlark 브리지 직접 확장 | ModuleSupplier에 내 커스텀 모듈 하나 등록 → @StarlarkMethod로 새 함수 노출 → copy.bara.sky에서 호출까지 |
Copybara는 구글 생태계의 일부라, Bazel(빌드), Gerrit(코드 리뷰), 모노레포 트렁크 기반 개발을 함께 알면 "왜 이렇게 설계했나"가 훨씬 잘 보인다. 특히 "Trunk-Based Development에서 외부 미러를 어떻게 유지하나"라는 질문이 이 도구의 존재 이유다.
이 프로젝트를 읽을 때 반복 등장하는 용어 정리.
| 키워드 | 뜻 |
|---|---|
| Workflow | Origin→Transformations→Destination를 묶은 이동 단위. core.workflow(...)로 정의 |
| Origin / Destination | 코드를 읽어오는 출발지 / 써 넣는 도착지. 각각 인터페이스로 추상화 |
| Transformation | 이동 중 코드를 손질하는 규칙(move·replace·remove·scrubber…). 대개 가역 |
| Starlark | Bazel의 파이썬 같은 설정 언어. copy.bara.sky가 이것으로 작성됨 |
| copy.bara.sky | Copybara 설정 파일의 관례적 이름 |
| Source of Truth (SoT) | "진실은 여기 하나"로 정한 권위 있는 원본 레포 |
| WorkflowMode | SQUASH(압축)·ITERATIVE(커밋별)·CHANGE_REQUEST·CHANGE_REQUEST_FROM_SOT |
| Authoring | 커밋 저작자 처리 규칙. pass_thru·overwrite·allowed |
| Stateless | 상태를 외부 DB가 아닌 목적지 커밋 라벨에 저장 → 결정론적 재실행 |
| GitOrigin-RevId | 목적지 커밋에 심는 상태 라벨. "원본 어디까지 반영했나"를 표시 |
| glob | 포함/제외할 파일 패턴(glob(["**"], exclude=[...])) |
| scrubber | 커밋 메시지에서 내부 전용 텍스트를 걸러내는 metadata 변환 |
| Gerrit | 구글식 코드 리뷰 시스템. git.gerrit_origin/destination으로 연동 |
| Bazel / Bzlmod | 구글 빌드 도구 / 그 최신 모듈 의존성 관리 방식(MODULE.bazel) |
| uberjar | 의존성까지 다 담아 단독 실행 가능한 jar(copybara_deploy.jar) |
| Vendoring | 외부 라이브러리 소스를 저장소에 복사해 함께 관리(Starlark 엔진이 그렇게 들어옴) |
원본을 직접 확인하고 싶을 때.