Rust Forge
Rust Forge에 오신 것을 환영합니다! Rust Forge는 Rust 프로그래밍 언어 구성원에게 유용한 보충 문서를 모아 둔 저장소입니다. 오류나 오타를 발견하셨거나 Rust Forge에 내용을 추가하고 싶으시다면 언제든지 GitHub에 이슈나 PR을 올려 주십시오.
도움 요청
Rust에 기여하고 싶지만 어디서부터 시작해야 할지 모르시겠습니까? 이 가이드를 확인하십시오.
현재 릴리스 버전
| 채널 | 버전 | stable 전환일 | main 분기일 |
|---|---|---|---|
| Stable | |||
| Beta | |||
| Nightly | |||
| Nightly +1 |
릴리스 전 며칠 동안 어떤 일이 진행되는지 자세히 알아보려면 릴리스 절차 문서를 참고하십시오.
도구 손상 금지 주간
beta 릴리스에 모든 도구가 포함되도록 beta 분기 직전 한 주 동안은 도구 손상이 허용되지 않습니다(nightly 전용 도구는 예외입니다).
| Beta 분기 | 호환성 파괴 없는 주간 |
|---|---|
외부 링크
- Rust에 영향을 준 연구 논문과 기타 프로젝트의 참고 문헌입니다.
- Rust Pontoon은 Rust 웹사이트를 현지화하는 데 사용하는 번역 관리 시스템입니다.
기여를 시작하는 방법
Rust에 기여하는 데 관심을 가져 주셔서 감사합니다! 기여하는 방법은 다양하며, 저희는 그 모든 기여에 감사드립니다. 이 문서는 다른 Rust 기여자와 연락하고 Rust 프로젝트에 기여를 시작하는 방법을 설명합니다.
다시 한번 말씀드리지만, 모든 기여자는 저희 행동 강령을 따라야 합니다.
질문하기
먼저, 하려는 기여에 관해 궁금한 점이 있다면 다음 장소에서 다른 기여자에게 질문할 수 있습니다.
- Rust Zulip 서버는 대부분의 Rust 팀과 기여자가 사용하는 주요 소통 공간입니다. 또한 무슨 일이 진행되고 있는지 지켜보기에도 좋은 곳입니다.
- 예를 들어 컴파일러 팀(
t-compiler)의 Zulip “채널”이나t-compiler/help를 살펴볼 수 있습니다.
- 예를 들어 컴파일러 팀(
- internals.rust-lang.org(IRLO)는 Rust 개발을 논의하는 포럼입니다.
더 많은 자료는 공식 웹사이트의 팀 및 워킹 그룹 목록과 커뮤니티 페이지도 참고하십시오.
부디 질문해 주십시오! 많은 분이 “전문가의 시간을 낭비하고 있다“고 느낀다고 말씀하시지만, 저희는 그렇게 생각하지 않습니다. 기여자는 저희에게 중요합니다.
또한 부담스럽지 않다면 공개 토픽을 이용하십시오. 그러면 다른 사람들도 질문과 답변을 볼 수 있고, 어쩌면 그 내용을 이 가이드에 다시 반영할 수도 있습니다 :)
팁: 영어가 모국어가 아니어서 글쓰기에 자신이 없다면 번역기의 도움을 받아 보십시오. 다만 길고 복잡한 단어를 만들어 내는 LLM 도구는 사용하지 마십시오. 일상적인 팀 작업에서는 이해하기 쉽도록 간단하고 명확한 단어를 쓰는 것이 가장 좋습니다. 사소한 오타나 문법 실수가 오히려 더 인간적으로 보이게 할 수 있으며, 사람들은 사람과 더 잘 소통합니다.
저희 LLM 정책도 참고하십시오.
상당한 규모의 변경 사항을 기여하고 싶다면, 먼저 해당 변경 사항과 관련된 팀에 연락하시기를 권합니다. 팀마다 선호하는 작업 방식이 있으므로, 사전 논의를 거치고 팀의 동의를 얻을 수 있도록 권장 절차를 따르십시오.
- 컴파일러 팀: 주요 변경 제안(MCP) 또는 의견 요청(RFC)(자세한 내용은 여기를 참고하십시오)
- Rustdoc 팀: 팀의 Zulip 채널에서 팀에 연락하십시오
- 라이브러리 팀: GitHub에 API 변경 제안(ACP)을 올리거나 Zulip에서 팀에 연락하십시오(자세한 내용은 여기를 참고하십시오)
- 부트스트랩 팀: 팀의 Zulip 채널에서 질문하십시오
확실하지 않다면 언제든지 Zulip에서 질문하십시오.
에티켓
질문할 때 유용한 정보를 가능한 한 많이 포함해 주시기를 부탁드립니다. 다만 Rust 기여에 익숙하지 않다면 이것이 어려울 수 있다는 점도 잘 알고 있습니다.
아무런 맥락 없이 누군가를 핑하기만 하면 다소 성가실 수 있고 불필요한 소음만 만들 수 있습니다. 그러니 팀 구성원들이 하루에도 많은 핑을 받는다는 점을 유념해 주십시오.
기여를 시작하려면 어떻게 해야 합니까?
Rust 프로젝트는 규모가 상당히 커서 어느 부분에 도움이 필요한지, 또는 어느 부분이 입문자가 시작하기에 좋은지 알기 어려울 수 있습니다. 다음은 기여를 시작하기에 좋은 rust-lang 프로젝트 목록입니다(모든 프로젝트를 담고 있지는 않습니다). 이 중 일부 프로젝트에는 기여자 가이드가 있고, 도움이 필요한 이슈나 처음 시작하기 좋은 이슈로 표시된 이슈도 있습니다.
| 프로젝트 | 기여 가이드 | 처음 시작하기 좋은 이슈 | 설명 |
|---|---|---|---|
| 컴파일러 | rustc-dev-guide | 도움 요청 | Rust 컴파일러 및 관련 도구 |
| 표준 라이브러리 | std-dev-guide | Rust 표준 라이브러리 | |
| Rustdoc | Rustdoc 개요 | Rust 문서 생성기 | |
| Cargo | Cargo 기여자 가이드 | 처음 시작하기 좋은 이슈 | Rust 패키지 관리자 및 빌드 시스템 |
| Clippy | Clippy 기여자 가이드 | 처음 시작하기 좋은 이슈 | Rust 린터 |
| Rustfmt | Rustfmt 기여 가이드 | 처음 시작하기 좋은 이슈 | Rust 포매터 |
| Rust analyzer | 기여 빠른 시작 | 처음 시작하기 좋은 이슈 | IDE용 Rust 컴파일러 프런트엔드 및 LSP |
| Miri | Miri 기여 가이드 | 처음 시작하기 좋은 이슈 | Rust 인터프리터 및 UB 탐지기 |
| Rustup | Rustup 개발자 가이드 | 도움 요청 | Rust 툴체인 설치 프로그램 |
| crates.io | crates.io 기여 가이드 | 이슈 트래커 | Rust 패키지 레지스트리 |
| Bors | bors 개발 가이드 | 도움 요청 | Rust CI 병합 봇 |
| rustc-perf | 도움 요청 | Rust 컴파일러 벤치마크 모음 | |
| Triagebot | 처음 시작하기 좋은 이슈 | Rust 자동화 봇 | |
| Rust 플레이그라운드 | 도움 요청 | Rust 온라인 플레이그라운드 | |
| Rustlings | Rustlings 기여 가이드 | 이슈 트래커 | Rust 연습 문제 |
| MdBook | MdBook 기여 가이드 | 도움 요청 | Rust로 작성된 책 생성기 |
영감을 얻고 싶으시다면 rust-lang의 모든 저장소를 살펴보십시오!
에티켓
FOSS1 프로젝트에 처음 기여하는 일이 때로는 혼란스러울 수 있다는 것을 저희도 알고 있으며, 저희 프로젝트에서 협업할 때 기여자와 리뷰어 모두가 최상의 경험을 하기를 바랍니다.
이 목표를 이루기 위해 서로 신뢰를 쌓고 서로의 시간과 노력을 존중하고자 합니다. 다음의 간단한 지침을 따르기를 권장합니다:
- 작게 시작하십시오. 첫 기여로 거대한 코드 덩어리를 제출하는 것은 신뢰를 쌓는 데 도움이 되지 않습니다
- 제출하는 작업은 본인의 것이어야 합니다. 즉, 그 모든 부분을 완전히 이해하고 있어야 합니다
- 제출하기 전에 작업을 꼼꼼히 확인하십시오. 확실하지 않은 부분은 질문하거나 (인라인 주석이나
todo!()로) 저희에게 알려 주십시오 - 이슈를 수정하고 싶지만 설계에 확신이 없다면 저희 Zulip 서버에 참여하여 조언을 구하셔도 좋습니다
- 리뷰어의 시간을 존중해 주십시오. 리뷰 사이에 며칠의 여유를 두고, 코드가 컴파일되고 테스트를 통과할 때만 리뷰를 요청하거나, 그 단계에서 리뷰를 요청하는 이유를 설명해 주십시오(리뷰할 준비가 될 때까지 초안 상태로 두어도 됩니다)
- 코멘트는 간결하게 작성하도록 노력하시고, 완벽한 글쓰기에 대해서는 걱정하지 마십시오. 명확하게, 요점만 전달하도록 노력하십시오
- 프로젝트 내 소통의 일관성을 위해 (커밋 메시지 등을 포함하여) 소통할 때는 영어를 사용해 주십시오(영어는 저희 프로젝트에서 공용어로 인정되는 언어입니다). 문법이나 문장이 완벽할 필요는 전혀 없습니다!
저희의 LLM 사용 정책도 참고하십시오.
다양한 종류의 기여
Rust 프로젝트에 기여할 수 있는 방법은 다양합니다:
-
코드 작성이 가장 대표적인 방법입니다. 하지만 꼭 Rust로만 작성해야 하는 것은 아닙니다! 물론 저희 프로젝트 대부분은 Rust로 작성되어 있지만, 다른 기술도 사용합니다. 예를 들어 GitHub CI 워크플로나 자동화용 Python 스크립트를 개선하는 데 도움을 주시거나, HTML/CSS/JS로 웹 프런트엔드(예: Rustdoc이나 벤치마크 스위트 웹사이트)에 기여하실 수 있습니다. 자신의 강점을 살리십시오!
-
문서 개선은 아마도 가장 쉽게 기여를 시작할 수 있는 방법 중 하나일 것입니다. 이 페이지나 Forge의 다른 곳, 또는 다른 Rust (사용자 대상) 문서에서 오타나 불분명한 부분을 발견하셨거나, 유용한 정보가 빠져 있다고 느끼셨습니까? 좋습니다. 그렇다면 풀 리퀘스트를 보내 주십시오. 그러면 기여자가 되실 것입니다! :)
현재 내부 문서에 대한 오타/맞춤법 수정(대개 변경에 따르는 번거로움이나 리뷰 시간만큼의 가치가 없습니다)이나 테스트 스위트에 대한 수정(의도치 않게 코드 회귀를 일으킬 수 있습니다)은 받지 않는다는 점에 유의하십시오
-
테스트 개선도 매우 가치 있는 일입니다. 테스트는 아무리 많아도 부족하기 때문입니다. 여기에는 기존 테스트를 문서화하거나, 이미 수정되었지만 적절한 테스트가 없는 이슈에 대한 테스트를 작성하는 일도 포함될 수 있습니다.
-
또한 이슈 트리아지를 돕거나 오래된 이슈를 재현하여 여전히 유효한지 확인하는 방식으로 저장소의 이슈 트래커 개선에 도움을 주실 수도 있습니다.
-
또는 프로그래밍 언어에 관한 논의를 좋아하신다면 RFC 절차에 참여하실 수도 있습니다.
-
users.rust-lang.org(URLO)나 StackOverflow에서 질문에 답변하여 다른 Rust 사용자를 도우실 수도 있습니다.
-
자유 오픈 소스 프로젝트입니다. 참고: https://en.wikipedia.org/wiki/Free_and_open-source_software ↩
프로젝트 온보딩
이 문서는 새 팀 구성원이 프로젝트 구성원으로서 알아 두면 유용한 사항, 특히 우려 사항을 어디에 제기하고 어디에서 도움을 받을 수 있는지를 알아보기 위한 출발점입니다(기존 팀 구성원에게는 복습 자료가 됩니다).
팀 합류하기
팀마다 새 팀 구성원을 받아들이는 정책이 다르며, 합류한 뒤 맡게 되는 책임도 다릅니다. 현재로서는 팀 리드와 이야기하여 해당 팀의 정책을 알아보십시오(그리고 팀 리드에게 그 정책을 이곳에 문서화하도록 권해 주십시오!). 일반적으로 대부분의 팀은 최소한 다음을 기대합니다.
- 합류하기 전에 적어도 두어 달 동안 팀에 기여하십시오.
- RFC와 PR의 최종 의견 수렴 기간에 합리적인 시간 안에 응답하십시오.
트리아지 합류하기
위 내용의 한 가지 예외는 트리아지 팀으로, 프로젝트 작업을 시작하는 입문 과정으로 적극 추천합니다. 이 팀은 rust-lang/rust 저장소의 이슈와 PR을 트리아지하는 일을 하며, 프로그래밍이나 컴파일러에 대한 사전 경험을 요구하지 않습니다. Rust 프로젝트와 교류한 적이 있다면 부담 없이 트리아지에 합류하십시오(저희가 알아볼 수 있을 만큼 자주 교류하는 것을 권장하지만 필수는 아닙니다).
이 팀에 합류하려면 Dylan-DPC와 이야기한 뒤 rust-lang/team에 PR을 열기만 하면 됩니다. 팀에 합류할 때 어떤 변경을 해야 하는지는 team#2447의 예시를 참고하십시오.
wg-triage에 대한 자세한 내용은 트리아지 절차를 참고하십시오.
팀에 속하지 않고 구성 요소 유지 관리하기
특정 모듈이나 파일에 큰 기여를 했지만 팀에 추가될 만큼 오래 기여하지는 않았다면, 주요 작업을 한 파일/디렉터리에 대해 triagebot 멘션을 설정하는 것이 도움이 될 수 있습니다. 이렇게 하면 해당 파일을 변경하는 모든 PR에 대해 알림을 받고 피드백이나 맥락을 제공할 수 있으므로, 작성자는 더 빨리 피드백을 받을 수 있고 리뷰어가 해야 할 작업량도 줄어듭니다.
위원회와의 관계
리더십 위원회는 필요할 때 프로젝트를 대표하여 공식적으로 입장을 표명합니다. 모든 Rust 프로젝트 구성원은 공식적으로 대략 한 명의 위원회 구성원을 통해 대표됩니다(일부 팀은 두 명 이상의 구성원의 상위 팀입니다).
일반적으로 더 넓은 범위에 해당하는 우려 사항(즉, 여러분의 팀에만 국한되지 않는 사항)이나 팀 내에서 충분히 처리되지 않는다고 느끼는 우려 사항은 위원회에 제기할 수 있습니다. 대인 관계 문제나 행동 강령 위반은 항상 모더레이션 팀에 전달해야 한다는 점에 유의하십시오.
위원회에 제기하는 방법은 다음과 같습니다.
- GitHub에서 새 이슈를 여는 방법입니다. 이 이슈는 정기 위원회 회의에서 논의됩니다(모든 프로젝트 구성원이 이 회의에 초대됩니다).
- Zulip의 #council에서 제기하는 방법입니다.
- Zulip DM으로 여러분의 위원회 대표에게 연락하는 방법입니다(각 대표가 어느 팀을 대표하는지는 council을 참고하십시오).
어느 방법으로 시작해도 괜찮으니 더 편하게 느끼는 방법을 선택하십시오. 다만 가급적 공개적인 방법을 택하십시오.
리더십 위원회 직위는 순환 일정에 따라 프로젝트 팀들이 선출합니다. 자세한 내용은 위원회 임기 제한을 참고하십시오.
재단과의 관계
재단은 프로젝트를 지원하기 위해 활동하며, 프로젝트는 재단 이사회에 직접 대표자(프로젝트 이사 5명)를 두고 있습니다. 이 이사들은 정관에 따라 리더십 위원회가 선임합니다.
그 이사들은 프로젝트 이사가 아닌 이사의 수와 관계없이 재단의 모든 이사회 표결에서 50%의 의결권을 가집니다.
현재 프로젝트 이사 명단은 웹사이트에서 확인할 수 있습니다.
단순히 관심이 있거나, 질문이 있거나, 우려 사항이 있다면 다음 경로로 언제든지 연락해 주십시오.
- Zulip의 #foundation 채널로 연락하십시오.
- 위원회에 연락하십시오(위 내용 참조).
- 프로젝트 이사 중 한 명에게 연락하십시오(위 구성원 목록 참조). 처음 연락할 때는 Zulip DM을 권장합니다.
플랫폼
Rust는 팀 간 업무 조직과 내부 소통을 위해 여러 가지 플랫폼을 사용합니다. 이 문서는 현재 모든 플랫폼을 망라하려는 것이 아니라, 팀들이 사용하는 일부 플랫폼에 대한 정책을 문서화하는 것을 목적으로 합니다.
트위터
Rust 프로젝트에는 여러 공식 트위터 계정이 있으며, 이 계정들의 자격 증명은 현재 인프라 팀이 관리합니다.
트위터 가이드라인
프로젝트는 트위터 계정 @rustlang을 운영합니다. 이 계정은 소수의 자원봉사자 팀이 관리합니다.
이 계정은 주로 Rust 블로그와 Rust Insiders 블로그의 링크를 트윗합니다. 또한 다음 내용을 리트윗합니다:
- Rust에 관한 블로그 게시물 링크(가능하면 원작성자의 트윗을 리트윗)
- 모든 팔로워가 도울 수 있도록 Rust에 관한 질문
- 밋업 또는 콘퍼런스 공지
- 새로운 Rust 프로젝트 공지
- 그 밖에 관련 있는 모든 내용
다음 내용은 리트윗하지 않습니다:
- 다른 프로그래밍 언어/프로젝트를 비방하거나, 언어/기술 선택에 관한 논의에서 그 밖의 방식으로 건설적이지 않은 콘텐츠
- 개인적인 소식(“오늘부터 $COMPANY에서 Rust를 작성하는 일을 시작합니다”)
- Rust 학습 근황(“오늘 Rust를 배우기 시작했습니다”)
다이렉트 메시지는 누구에게나 열려 있습니다. 리트윗을 원하는 내용이 있다면 DM으로 해당 트윗을 보내야 합니다. 이러한 요청은 위의 규칙에 맞는 한 대부분 리트윗해야 합니다. 작성자가 DM이나 트윗으로 특정 내용을 리트윗하지 말아 달라고 요청하면 이를 존중합니다.
또한 계정 관리자는 주목할 만한 콘텐츠를 찾기 위해 #rustlang 해시태그를 살펴볼 수 있습니다.
이 계정은 프로젝트가 소유하거나 프로젝트와 관련된 소수의 트위터 계정만 팔로우합니다. 이 글을 작성한 시점(2022년 2월)에는 @cratesiostatus와 @rust_foundation뿐입니다.
접근 권한
현재 네 계정 모두에 대한 접근 권한은 하나의 1password 보관함을 통해 함께 부여되며, 이를 더 세분화된 접근 권한으로 나누지 않습니다. 일부 자동화는 상태 계정의 API 키를 사용하여 crates.io의 예정된 이벤트에 관해 자동으로 트윗합니다.
접근 권한은 social-media 마커 팀에 속한 소수의 사람으로 제한되며, 이는 자동화되어 있지 않습니다(변경 시 접근 권한 부여를 위해 인프라 관리자에게 핑해야 합니다).
1password에 접근 권한이 있는 사람은 다음을 수행해야 합니다:
- 비밀번호를 변경하거나 그 밖의 관리 작업을 절대 수행하지 마십시오(이러한 작업은 인프라 관리자만 수행합니다).
- 비밀번호 사본은 프로젝트에서 호스팅하는 인스턴스에만 보관하십시오(브라우저를 포함한 다른 어떤 비밀번호 데이터베이스에도 저장하지 마십시오).
- 비밀번호를 다른 사람과 절대 공유하지 마십시오(그 사람이 목록에 있더라도 마찬가지입니다).
- 비밀번호를 안전하지 않은 채널(예: 이메일)로 전달하다가 실수로 유출하는 일이 없도록, 모든 접근은 항상 정규 채널을 거쳐야 합니다.
- 비밀번호는 정기적으로 변경될 수 있으며, 그때마다 다시 권한을 부여받아야 한다는 점에 유의하십시오.
접근 권한이 있어야 한다고 생각되면 팀 저장소에 권한을 요청하는 PR을 제출하고, 설명란에 이 정책을 읽었다고 적어 주십시오.
Discord
Rust는 예전에 Discord에서 공식 서버를 운영했습니다. 이 서버는 Zulip으로 옮기면서 운영이 종료되었고, 지금은 읽기 전용으로 남아 있습니다.
읽기 전용 화면은 https://discord.gg/rust-lang에서 볼 수 있습니다.
이메일
Rust의 논의는 대부분 다른 플랫폼에서 이루어지지만, 이메일은 영원히 남는 수단이며 때로는 개인이나 그룹에 비공개로 연락할 방법이 필요합니다. 저희 이메일은 Mailgun(Mozilla 제공)을 통해 호스팅됩니다. 팀용 메일링 리스트는 rust-lang/team 저장소를 통해 생성하고 편집합니다. 저희 이메일 도메인은 rust-lang.org이며, 예를 들면 ferris@rust-lang.org와 같습니다.
전체 공지 보내기
여러분의 팀이 Rust 조직의 모든 사람에게 연락해야 하는 경우 all@로 이메일을 보낼 수 있습니다. 이 메일링 리스트는 All Hands와 같은 구성원 행사를 준비하거나 보안 경고를 알리는 경우처럼 모든 구성원에게 연락해야 한다는 것이 확실할 때만 사용할 것을 권장합니다.
답장을 비공개로 유지하기
all@로 메시지를 보낼 때는 To에 all@를 넣지 마십시오. 그렇게 하면 여러분의 공지에 대한 모든 답장도 모든 사람에게 전송됩니다. 대신 To 필드에는 여러분 팀의 이메일 주소를 넣고, Bcc 필드에 all@를 넣으십시오. 그러면 모든 답장이 여러분의 팀에게만 전송됩니다.
GitHub
GitHub는 Rust 프로젝트의 모든 코드를 호스팅하는 곳이며, 논의의 상당 부분도 이곳에서 이루어집니다.
조직
rust-lang— Rust 프로젝트 조직입니다.rust-embedded— 임베디드 워킹 그룹 조직입니다.rustwasm— WebAssembly 워킹 그룹 조직입니다.rust-cli— 명령줄 애플리케이션 워킹 그룹 조직입니다.rust-secure-code— 보안 코드 워킹 그룹 조직입니다.rust-gamedev— 게임 개발 워킹 그룹 조직입니다.
rust-lang 조직 정책
다음은 rust-lang 조직 관리에 관한 정책입니다.
접근 권한
rust-lang GitHub 조직에 대한 모든 접근 권한은 team 저장소1를 통해 관리됩니다. 접근 수준을 부여하거나 새 저장소를 만들려는 팀은 해당 저장소에 풀 리퀘스트를 열어 변경을 요청해야 합니다.
인프라 팀은 rust-lang GitHub 조직의 전반적인 관리를 책임집니다. 인프라 팀에서 선정된 구성원은 업무상 필요한 경우 조직 소유자가 될 수 있습니다.
Rust-Lang GitHub 조직과 상호 작용하는 데 사용하는 모든 GitHub 계정(소유자 여부와 관계없이)은 2FA를 활성화해야 합니다. 이는 GitHub에서 강제합니다.
인프라 팀이 관리하는 봇 계정(예: triagebot)에는 인프라 팀의 재량에 따라 작동에 필요한 어떤 수준의 접근 권한이든 부여할 수 있습니다.
Zulip
Rust의 Zulip은 Rust 팀 구성원들이 Rust 개발을 논의하는 데 사용하는 채팅 플랫폼입니다.
Zulip은 처음 시작할 때 직관적이지 않게 느껴질 수 있는 플랫폼입니다. 시작하려면 시작 가이드를 살펴보십시오. 더 자세한 내용은 Zulip 사용자 문서를 살펴보십시오!
모든 프로젝트 구성원에게 연락할 수 있는 수단을 확보하기 위해, 모든 Rust 프로젝트 구성원은 Zulip 계정을 보유해야 합니다1.
Zulip 사용에 관한 도움을 받을 수 있는 곳
기능을 테스트하거나 도움을 받고 싶다면 #zulip 스트림을 이용하십시오. 다른 곳과 마찬가지로, 질문마다 새 토픽을 만드는 것이 가장 좋습니다.
시작하기
먼저 공식 시작 가이드를 살펴보는 것을 권장합니다. Rust 자체와 마찬가지로 Zulip도 다소 독특하므로, 본격적으로 사용하기 전에 문서를 읽어 두면 큰 도움이 될 수 있습니다.
처음 시작할 때는 구독할 스트림을 반드시 설정하는 것이 좋습니다. 기본 구독 목록은 상당히 제한적이며, 그 밖에도 많은 그룹이 있습니다. 스트림 구독에는 부담이 거의 없습니다. IRC 채널에 “들어가 있는” 것과 비슷하지만, 구독 여부와 관계없이 모든 스트림의 기록을 볼 수 있다는 점이 다릅니다.
자기소개를 할 필요는 없지만, #new members 스트림에서 자유롭게 인사하셔도 됩니다.
사용자 그룹
다른 사용자를 핑하는 것과 마찬가지로, 누구나 @<group> 표기법으로 사용자 그룹을 핑할 수 있습니다. 그룹은 누구나 만들 수 있으며, 누구나 그룹에 참여할 수 있습니다.
사용자는 자유롭게 스스로 그룹에 참여하거나 그룹에서 탈퇴할 수 있습니다. 또한 사용자는 필요에 따라 자유롭게 그룹을 만들 수 있지만, 현재로서는 그런 경우가 다소 드물 것으로 예상합니다. 그룹 이름은 같은 목적의 스트림 이름을 짓는 방식과 비슷하게 지어야 합니다. 다만 그룹은 스트림보다 더 세분화될 수도 있고 덜 세분화될 수도 있습니다. 예를 들어, @T-compiler/meeting에는 현재 전용 스트림이 없습니다.
적절한 대화
대부분의 스트림에서는 팀 업무와 관련된 대화를 유지하도록 노력해야 합니다. #general 스트림은 범위가 조금 더 넓지만, 그곳에서도 논의는 Rust와 밀접하게 관련되어야 합니다(특정 팀의 프로젝트와는 관련이 없어도 됩니다). 다만 모든 채널은 Rust 프로젝트와 관련된 논의에 사용되어야 합니다. 예를 들어 야생 동물이나 관광에 관한 논의는 적절하지 않습니다.
스트림
스트림은 다른 플랫폼의 “채널“과 비슷합니다(즉, 너무 많아서는 안 됩니다). 반면 구독할 스트림을 직접 선택할 수 있으므로, 다른 플랫폼의 채널보다 많아도 괜찮습니다. 자세한 내용은 Zulip 문서를 읽어 보십시오.
스트림은 모든 Rust 공식 그룹에 적합합니다. 예를 들어 워킹 그룹, 프로젝트 그룹, 팀은 모두 공식 그룹에 해당합니다. 이러한 그룹은 이상적으로는 팀 저장소에도 등록되어 있어야 합니다.
기본 스트림
이 절은 아직 논의 중이며, 어느 방향으로 나아갈지 아직 명확하지 않습니다. 이 절은 규범적이지 않으며, 아직 Zulip 인스턴스를 수정하는 근거로 사용해서는 안 됩니다.
기본 스트림 목록은 새로 들어오는 사람들이 최소한 한 곳에는 찾아갈 수 있도록 선정하며, 필요하다면 그곳에서 더 구체적인 위치로 안내받을 수 있습니다.
현재 이는 Zulip에 있는 모든 최상위 그룹이 기본적으로 표시된다는 의미입니다. 구체적으로 말하면, /가 포함된 스트림은 기본으로 활성화되지 않습니다.
현재 이 목록은 다음과 같습니다.
- general
- t-lang
- t-compiler
- t-libs
- project-ffi-unwind (프로젝트)
- project-inline-asm (프로젝트)
- project-safe-transmute (프로젝트)
- rust-survey-2019
- wg-async-foundations
- wg-database
- wg-formal-methods
- wg-secure-code
- wg-traits
- zulip
이와 다른 최소주의적 접근 방식으로는 다음을
- general
- zulip
- announce
- new members
기본 목록으로 사용하는 방법이 있으며, 이렇게 하면 사람들이 처음 시작할 때 자신의 기본 목록을 직접 조정하도록 유도하게 됩니다.
스트림 이름 짓기
스트림 이름은 #t-{team}/{group name}와 같은 형식으로 지어야 합니다. 예를 들어 #t-compiler/parallel-rustc와 같습니다. 더 깊이 중첩해도 괜찮습니다. 예를 들어 워킹 그룹에 “하위 그룹“이 필요할 수도 있습니다. 다만 이 경우에는 팀 이름을 생략하는 편이 나을 수 있습니다. 스트림 이름을 짧게 유지하면 사용하기 편하고, 같은 접두사를 공유하는 여러 스트림을 서로 혼동하지 않을 수 있습니다.
최상위 팀이 없거나 그룹이 여러 팀에 걸쳐 있는 경우(예: project-ffi-unwind 프로젝트)에는 최상위 팀을 생략해야 합니다.
스트림은 특정 목적을 위한 것임을 명확히 알려야 합니다. 그 목적은 넓어도 되지만, 어떤 형태로든 그룹을 포함하는 것이 좋습니다. 그 그룹은 일시적이어도 됩니다. 예를 들어 Rust 빌드 시스템 때문에 어려움을 겪는 사람들이나 컴파일러 작업을 하는 사람들이 그러한 그룹입니다. 또한 현재로서는 이 Zulip을 Rust 조직과 관련이 없는 커뮤니티 프로젝트를 위한 일반적인 공간으로 삼을 의도가 없습니다. 그러한 프로젝트가 Zulip을 사용하려 한다면 Zulip은 오픈 소스에 무료로 제공됩니다.
새 스트림을 만들면 #announce에 공지해야 합니다. 이 공지는 일반적으로 Zulip이 자동으로 올립니다.
토픽
스트림 안의 모든 메시지에는 토픽이 붙습니다(토픽은 스트림 안의 하위 구분입니다). 토픽은 일반적으로 일시적이며, 해당 토픽에 대한 논의가 활발하게 이루어지는 동안에만 유지됩니다. 토픽을 이메일 제목처럼 생각하면 도움이 됩니다.
스트림에서 새로운 대화를 시작할 때는 거의 항상 기존 토픽이 아닌 새 토픽을 사용해야 합니다. (예를 들어) GitHub 이슈와 달리, 같은 주제를 다룬 과거 토픽을 찾으려고 해서는 안 됩니다. 또한 토픽 이름은 짧게만 지으면 되며, 그 이상으로 시간을 들이지 마십시오. 토픽 이름이 사용자에게 잘 보이도록 일반적으로 20자(대략 두세 단어)를 넘지 않아야 합니다.
새로운 논의는 적극적으로 새 토픽으로 분리해야 합니다. 실수로 다른 논의 영역으로 벗어난 경우에는 다른 토픽의 뒷부분만 분리할 수도 있다는 점에 유의하십시오.
기존 토픽에서 분리하는 방법은 여기에 있는 Zulip 문서를 참고하십시오.
메시지
Zulip은 동기식 소통과 비동기식 소통을 한곳에 결합한 독특한 플랫폼입니다. 일반적으로 메시지에 빠른 응답을 기대해서는 안 됩니다. 또한 (예를 들어) Discord와 달리, 메시지가 특정 토픽 안에 따로 있어 지난 기록 속에 묻힐 가능성이 낮으므로 특정 이슈에 대해 몇 시간마다 “다시 핑“을 보낼 이유는 별로 없습니다.
링크 변환기
우리 Zulip은 유용한 링크 변환기를 많이 지원하며, 요청하시면 일반적으로 기꺼이 더 추가합니다. 형식은 문서를 참고하십시오. #zulip에서 제안해 주십시오!
일반적으로 이슈나 PR에 링크할 때는 github-org/repo#123를 사용할 수 있습니다. 아래 목록은 “특별히 처리되는” 저장소 몇 개를 추가로 보여 줍니다.
표준 Markdown 링크 문법도 사용할 수 있다는 점을 잊지 마십시오.
rust-lang GitHub 조직 안에 있는 저장소의 이슈는 rust-lang/ 접두사 없이도 링크할 수 있습니다. 예를 들면 다음과 같습니다.
- rust-lang/rfcs:
RFC#3434또는rfc#3434 - rust-lang/async-book:
async-book#2334 - rust-lang/cargo:
cargo#2334
rust-lang/rust 이슈는 접두사 없이 링크할 수 있습니다:
- rust-lang/rust:
#4545또는rust#4545
현재 다음 저장소의 커밋에 대한 링크를 지원합니다:
- rust-lang/rust: 40자 길이의 SHA(예:
25434f898b499876203a3b95c1b38bad5ed2cc5d)
읽기 전용 보기
저희 Zulip 인스턴스에는 웹 공개 스트림 beta 기능이 활성화되어 있으며, 모든 공개 스트림에 이 기능을 사용합니다. 이와 관련하여 문제가 있으면 저희나 Zulip 개발자에게 알려 주십시오. 웹 공개 보기를 제공하던 이전 방식은 Zulip 아카이브였으며, 이 아카이브는 이제 웹 공개 보기로 리디렉션됩니다.
t-all/private 채널
모든 Rust 프로젝트 팀 구성원은 비공개 채널인 t-all/private에 자동으로 구독됩니다. 이 채널은 모든 프로젝트 구성원에게 연락하는 수단으로 사용할 수 있으며, 이메일이 스팸으로 분류되는 경우가 잦은 all@rust-lang.org 메일링 리스트보다 더 신뢰할 수 있는 대안이 되어야 합니다.
이 채널은 주로 다음 두 종류의 주제에 사용해야 합니다:
- 오직 Rust 프로젝트에만 관련되고 그 외에는 누구와도 관련이 없는 정보
- 예:
team데이터베이스나 Rust 웹사이트의 변경 사항 중 사람들이 관심을 가질 만한 내용을 알리는 경우
- 예:
- 프로젝트 내부에서만 공유하고 외부에는 공유하지 않아야 하는 정보
- 예: 프로젝트 외부 사람이 응답해서는 안 되는 프로젝트 전체 대상 설문 조사를 공유하는 경우
위에서 언급한 두 경우 모두, 이 채널은 메시지가 (예를 들어 단일 팀에만 관련된 것이 아니라) 프로젝트 전체와 관련이 있을 때에만 사용해야 합니다. 그 밖의 소통은 가급적 공개되어 있고 범위가 더 좁은 채널을 통해 하십시오.
Zulip 모더레이션
Zulip은 다른 모든 공식 Rust 공간과 마찬가지로 행동 강령의 적용을 받습니다. 우려되는 사항이 있다면 언제든지 모더레이션 팀에 문제를 제기하십시오.
다만 모더레이션 팀이 이 영역의 최상위 기구이기는 하지만, Zulip 내 모더레이션과 관련하여 도움을 구할 수 있는 곳이 모더레이션 팀만 있는 것은 아닙니다.
Zulip 관리자에게 비공개로 연락하는 한 가지 방법은 zulip-admin.239bd484c0347d2d43214d8581f3e125.show-sender@streams.zulipchat.com으로 이메일을 보내는 것입니다. 이 방식이 어떻게 작동하는지에 대한 자세한 내용은 이 페이지를 참고하십시오.
Zulip에서 @mods 그룹을 핑할 수도 있습니다. 다만 이 방법은 공개적으로 이루어진다는 점에 유의하십시오.
현재 일반 사용자는 스스로 관리 작업(예: 다른 사용자 뮤트)을 수행할 수 없습니다. 다만 비공개 스트림을 포함한 각 스트림은 개별적으로 뮤트할 수 있습니다.
관리자/모더레이터용 안내
모더레이터가 자주 수행하는 작업 몇 가지가 이 페이지에 정리되어 있습니다.
특히,
- “Organization permissions”에서 사용자가 가입하기 전에 반드시 초대를 받도록 제한할 수 있습니다(“no new users” 버튼이 이 기능입니다).
새 관리자/모더레이터는 Zulip의 mods 그룹에 직접 자신을 추가해야 합니다. (이 작업은 어떤 사용자든 할 수 있다는 점에 유의하십시오!)
Rust 블로그 가이드라인
배경
Rust 프로젝트는 두 개의 블로그를 운영합니다. “메인 블로그”(blog.rust-lang.org)와 “inside Rust 블로그”(blog.rust-lang.org/inside-rust)입니다. 이 문서는 각 블로그에 글을 쓰려면 무엇이 필요한지에 대한 가이드라인과 함께, 글을 제안하는 방법 및 가장 적합한 블로그를 고르는 방법을 안내합니다.
알맞은 블로그를 선택하는 방법: 독자
Rust 블로그 게시물을 작성하려는데, 어느 블로그에 게시해야 할지 알고 싶으실 것입니다. 결국 선택지는 세 가지입니다.
- 메인 Rust 블로그
- 독자가 “모든 Rust 사용자 또는 잠재적 사용자”일 때 적합합니다
- 개인이 서명하더라도 “공식 입장”에서 작성됩니다
- inside Rust 블로그
- 독자가 “모든 Rust 기여자 또는 잠재적 기여자”일 때 적합합니다
- 개인이 서명하더라도 “공식 입장”에서 작성됩니다
- 여러분의 개인 블로그
- 그 밖의 모든 경우
이 중 어느 것이 적합한지 결정하려면 두 가지 핵심 질문에 답해야 합니다.
- “공식적인 자격”으로 말하고 있습니까, 아니면 “개인”으로서 말하고 있습니까?
- 글의 독자는 누구입니까?
일반적으로 “개인”으로서 말하는 경우라면 여러분의 개인 블로그에 쓰는 것이 가장 좋을 것입니다.
반면 공식적인 자격으로 글을 쓴다면 Rust 블로그 중 하나가 적합할 것입니다. 그렇다고 해서 개인으로서 글을 쓸 수 없다는 뜻은 아니라는 점에 유의하십시오. Rust 블로그의 많은 글은 개인 명의로 서명되어 있으며, 실제로 그 방식이 선호됩니다. 다만 그런 글은 대개 팀의 공식 입장을 기록하는 글입니다. 좋은 예로 Rust의 언어 인체공학 이니셔티브에 관해 Aaron Turon이 쓴 고전적인 글이 있습니다. 때로는 흥미로운 프로젝트를 소개하는 글도 있지만, 이 역시 프로젝트 전체를 대표하는 방식으로 작성됩니다(예: Firefox Quantum의 두려움 없는 동시성에 관한 Manish Goregaokar의 보고).
메인 블로그와 inside Rust 블로그 중 하나를 고르려면 글의 독자가 누구인지 스스로 물어보십시오. 메인 블로그의 글은 모든 Rust 사용자 또는 잠재적 사용자를 대상으로 해야 합니다. 이런 글은 기술적 세부 사항을 비교적 가볍게 다루고, 많은 배경지식 없이도 읽을 수 있게 작성되는 경향이 있습니다. inside Rust 블로그의 글은 훨씬 더 많은 배경지식과 Rust에 대한 친숙함을 전제할 수 있습니다.
메인 Rust 블로그에 글쓰기
메인 Rust 블로그에 무엇을 게시할지는 최종적으로 리더십 위원회가 결정합니다.
Rust 조직 내부의 흥미로운 진전을 소개하는 글 제안과 Rust의 흥미로운 활용 사례를 소개하는 글 모두 환영합니다. 다만 일반적으로 다른 프로젝트와의 “홍보성 교차 게시”는 하지 않습니다.
릴리스 노트 블로그 게시물
특별한 경우로, 모든 Rust 릴리스와 함께 나오는 정기 릴리스 노트 게시물이 있습니다. 이 게시물은 릴리스 팀이 관리하며 메인 블로그에 게시됩니다.
블로그 게시물은 릴리스 당일에 해당 릴리스를 진행하는 릴리스 팀 구성원이 직접 게시합니다. 릴리스는 항상 목요일에 이루어집니다.
릴리스 게시물은 게시되기 전에 다음과 같은 초안 작성 과정을 거칩니다.
- 해당 릴리스의 마일스톤(예: 1.39.0)을 참고합니다.
- 충분히 중요하다고 판단되는 PR을 포함하며, 일부 항목은 주요 항목으로 부각합니다. 블로그 게시물은 보통 hackmd 문서에서 작성합니다.
- 헤드라인 항목은 때때로 서로 다른 사람이 작성하며, 저희는 각 하위 섹션을 동료 리뷰하려고 노력합니다.
- 블로그 게시물 초안은 최종 리뷰를 위해 릴리스 며칠 전에 블로그 저장소에 PR로 제출됩니다.
Inside Rust 블로그
Inside Rust 블로그에 무엇을 쓸지는 일반적으로 각 팀이 스스로 정할 수 있습니다.
Inside Rust 블로그 게시물의 대표적인 주제는 다음과 같습니다.
- 새로운 이니셔티브 및 참여 요청
- 진행 중인 작업의 업데이트 및 현황 보고
- 설계 노트
승인 절차
Inside Rust 블로그와 메인 블로그 모두 최소 한 명의 승인이 필요합니다. 승인자는 다음 그룹 중 하나에 속해야 합니다.
- 모든 팀 리드(최상위 팀 여부와 무관)
- 모든 리더십 위원회 구성원
- Rust 재단 프로젝트 이사
이들은 주로 inside-rust-reviewers 마커 팀1의 구성원입니다. 이는 메인 블로그와 Inside Rust 블로그 모두에 적용된다는 점에 유의하십시오(이름은 추후에 변경될 예정입니다).
승인 시에는 다음 사항을 평가해야 합니다.
- 게시물의 어조와 내용이 공식 채널에 적합합니까?
- 예를 들어, 다른 생태계나 언어에 대한 부정적인 언급은 피해야 합니다.
- 게시물이 누구를 대표하여 작성되었는지 분명합니까?
- 이는 작성자 표기뿐 아니라 사용된 표현에도 해당될 수 있습니다. 게시물이 Rust 프로젝트 전체를 대표하여 공식 입장을 밝히는 경우, 리더십 위원회 구성원 중 최소 한 명의 승인을 반드시 받으십시오. 게시물이 특정 팀을 대표하여 입장을 밝히는 경우, 해당 팀이 그 내용에 동의해야 합니다.
일반적으로 누군가(반드시 위의 승인자일 필요는 없습니다)가 게시물을 교정했는지 확인하는 것이 좋지만, 블로그에서는 대체로 완벽한 내용보다 게시가 막히지 않도록 하는 것을 우선시합니다. 위 사항은 일반적으로 이 사람이 여러분이 대표하여 게시하는 팀의 구성원일 것을 요구하지 않는다는 점에 유의하십시오. 다만 이 사람은 해당 게시물에 대해 알고 있어야 합니다.
리뷰 받기
Triagebot은 새로운 블로그 PR마다 리더십 위원회 대표를 자동으로 배정합니다. 리뷰를 제때 받지 못하고 있다면 이 대표에게 핑을 보내면 됩니다. 다만 팀 리드에게도 리뷰를 요청하면 더 빨리 리뷰를 받을 수도 있습니다. 다시 말해, 먼저 팀 리드에게 요청하고, 그다음 팀의 리더십 위원회 대표에게 핑을 보낸 뒤, 마지막으로 배정된 리뷰어에게 요청하는 순서로 에스컬레이션할 것을 권장합니다.
리뷰가 이루어지도록 하는 최종 책임은 배정된 리뷰어에게 있습니다.
일반적으로 첫 리뷰까지 최소 1주일 정도가 걸릴 것으로 예상하십시오. 최종 수정 사항에 대한 재리뷰나 최종 병합 버튼 누르기는 대개 신속하게 진행할 수 있습니다. 게시물을 병합해야 할 때 위 그룹의 구성원 중 시간이 되는 사람을 찾으십시오.
-
릴리스 블로그 게시를 위해 릴리스 팀 구성원도 이 팀에 포함되어 있지만, 현재로서는 임의의 게시물을 승인할 수 있는 승인자로 간주되지 않습니다. ↩
캘린더
많은 Rust 팀과 워킹 그룹이 정기 회의를 열고 있어, 모든 캘린더 일정을 관리하는 일이 금세 까다로워질 수 있습니다.
그래서 일회성 일정과 반복 일정을 모두 생성할 수 있는 자동화 도구를 마련해 두었습니다. 이 도구는 calendar 저장소에서 찾을 수 있으며, 이 저장소에는 사용 가이드도 함께 들어 있습니다.
이 도구를 사용하면 TOML 파일로 캘린더 초대를 선언적으로 생성하고 갱신할 수 있으며, 도구는 이를 바탕으로 다양한 캘린더 도구로 가져올 수 있는 .ics 파일을 생성합니다.
Triagebot
Triagebot(rustbot이라고도 함)은 rust-lang 조직에서 다양한 작업에 쓰이는 범용 봇으로, 주로 GitHub나 Zulip 댓글로 명령을 보내는 방식으로 사용됩니다. 다음 페이지에서 사용 가능한 기능을 설명합니다.
명령은 보통 @rustbot이라는 텍스트로 시작하는 댓글을 작성하여 내립니다. 사용 가능한 명령은 사용 중인 저장소에 따라 다릅니다. 각 저장소에는 triagebot.toml이 있으며, 여기에서 어떤 기능이 활성화되어 있는지 확인할 수 있습니다.
예를 들어 다음 댓글은:
@rustbot label A-diagnostics A-macros
GitHub UI에서 직접 그렇게 할 권한이 없는 사람이라도 GitHub 이슈나 풀 리퀘스트에 지정한 레이블을 설정합니다.
GitHub 명령
GitHub 이슈나 풀 리퀘스트에서 명령은 보통 댓글의 아무 곳에나 @rustbot을 쓰고 그 뒤에 명령을 작성하여 내립니다. @rustbot은 마크다운 코드 블록, 인라인 코드 스팬, HTML 블록, 인용 블록 안에 있는 명령은 무시합니다. 댓글 하나에 여러 개의 rustbot 명령을 입력할 수 있습니다.
Triagebot은 댓글 수정도 지원합니다. 명령의 텍스트를 수정하지 않으면 triagebot은 해당 수정을 무시합니다. 그러나 기존 명령을 수정하거나 새 명령을 추가하면 해당 명령이 처리됩니다.
Zulip 명령
Zulip에서는 triagebot 계정에 다이렉트 메시지를 보내거나, Zulip 스트림에서 @triagebot을 핑하고 명령을 지정하는 방식으로 명령을 내리며, 후자의 경우 명령은 해당 스트림 안에서 실행됩니다.
triagebot Zulip 명령에 대한 문서는 여기에서 찾을 수 있습니다.
설정
개별 GitHub 저장소는 기본 브랜치의 루트에 있는 triagebot.toml이라는 파일을 통해 triagebot 기능을 설정할 수 있습니다. 다음 페이지에서 각 기능에 필요한 문법을 설명합니다.
예를 들어 rust-lang/rust의 설정 파일은 https://github.com/rust-lang/rust/blob/HEAD/triagebot.toml에 있습니다.
새 저장소에 triagebot.toml을 처음 추가할 때는 봇이 동작할 수 있도록 권한을 활성화해야 합니다. 이는 rust-lang/team 데이터베이스에 PR을 올려 repos/rust-lang 디렉터리의 해당 저장소에 bots = ["rustbot"]를 추가하면 됩니다.
전역 설정
GitHub 조직은 조직 내 모든 저장소에 적용되는 전역 설정을 가질 수 있습니다. rust-lang 전역 설정은 https://github.com/rust-lang/triagebot/blob/master/rust-lang.triagebot.toml에 있습니다.
전역 설정의 설정 옵션은 일반 triagebot.toml 파일과 같지만, 각 설정 항목에 지정한 저장소에서 해당 기능을 비활성화하는 excluded-repos = ["rust-lang/foo"] 옵션을 포함할 수 있다는 점이 다릅니다.
자주 쓰는 명령 요약
다음은 rust-lang/rust에서 볼 수 있는 자주 쓰는 명령입니다.
| 명령 | 설명 | 문서 |
|---|---|---|
@rustbot claim | 이슈를 자신에게 배정합니다. | 이슈 배정 |
@rustbot release-assignment | 이슈에 대한 자신의 배정을 해제합니다. | 이슈 배정 |
@rustbot assign @octocat | 이슈를 특정 사용자에게 배정합니다. | 이슈 배정 |
@rustbot ready | PR이 리뷰받을 준비가 되었음을 나타냅니다. | 단축 명령 |
@rustbot author | PR이 작성자의 조치를 기다리고 있음을 나타냅니다. | 단축 명령 |
@rustbot blocked | PR이 무언가에 의해 막혀 있음을 나타냅니다. | 단축 명령 |
@rustbot label A-diagnostics A-macros | 이슈나 PR에 레이블 두 개를 추가합니다. | 레이블 지정 |
@rustbot label -P-high | 이슈나 PR에서 레이블을 제거합니다. | 레이블 지정 |
@rustbot ping windows | Windows 핑 그룹을 핑하는 댓글을 게시합니다. | 핑 보내기 |
@rustbot prioritize | 우선순위 지정 워킹 그룹에 우선순위 지정을 요청합니다. | 우선순위 지정 |
r? @octocat | PR을 사용자에게 배정합니다. | PR 할당 |
r? libs | 라이브러리 리뷰 그룹에서 무작위로 한 사람에게 할당합니다. | PR 할당 |
r? rust-lang/cargo | cargo 팀에 속한 사람 중 한 명에게 무작위로 배정합니다. | PR 할당 |
다음은 Zulip에서 자주 볼 수 있는 명령어입니다.
| 명령어 | 설명 | 문서 |
|---|---|---|
@triagebot read | 회의에서 참석자들이 문서를 다 읽을 때까지 기다립니다. | Zulip 회의 관리 |
@triagebot end-topic | 회의에서 모든 참석자가 해당 주제에 대한 논의를 마쳤는지 확인합니다. | Zulip 회의 관리 |
@triagebot end-meeting | 모든 참석자가 회의를 마칠 준비가 되었는지 확인합니다. | Zulip 회의 관리 |
더 많은 Zulip 명령어는 여기에서 확인할 수 있습니다.
구현
triagebot의 소스 코드는 https://github.com/rust-lang/triagebot에서 확인할 수 있습니다. triagebot 확장에 관심이 있다면 해당 위치의 문서에서 시작 방법에 대한 안내를 얻을 수 있습니다.
안건 생성기
언어 팀은 회의 안건 작성을 돕기 위해 안건 생성기를 사용합니다.
사용법
안건 생성기는 https://triage.rust-lang.org/agenda에서 확인할 수 있습니다.
설정
이 기능에는 구성 항목이 없습니다.
구현
src/agenda.rs를 참고하십시오.
이슈 배정
이슈 배정 명령을 사용하면 모든 사용자가 자기 자신을 GitHub 이슈에 배정할 수 있습니다.
사용법
이슈 배정은 GitHub 댓글에 다음 명령 중 하나를 입력하여 수행합니다.
@rustbot claim— 이슈를 자기 자신에게 할당합니다.@rustbot release-assignment또는@rustbot unclaim— 현재 담당자를 제거합니다. 현재 담당자 또는 팀 구성원만 배정을 릴리스(해제)할 수 있습니다.@rustbot assign @user— 특정 사용자를 할당합니다. 팀 구성원만 다른 사용자를 할당할 수 있습니다.
GitHub의 제약으로 인해 모든 사용자를 이슈에 직접 할당할 수 있는 것은 아닙니다. 저장소에 대한 쓰기 권한이 있는 사용자나 rust-lang 조직 구성원만 직접 할당될 수 있습니다. triagebot이 사용자를 직접 할당할 수 없는 경우, 대신 @rustbot을 할당하고 최상위 댓글을 수정하여 누군가 이슈를 맡았다는 메시지를 남깁니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
이슈 배정은 triagebot.toml에 [assign] 테이블이 있으면 저장소에서 활성화됩니다.
[assign]
구현
parser/src/command/assign.rs와 src/handlers/assign.rs를 참고하십시오.
PR 할당
Triagebot은 GitHub PR의 자동 할당과 수동 할당을 처리합니다. 또한 새 사용자가 PR을 올리면 환영 인사를 보내는 일도 처리합니다.
rust-lang/rust 저장소의 기여자는 Zulip 연동을 통해 자신의 작업 큐를 추적하고 관리할 수 있습니다. 리뷰 큐 추적을 참고하십시오.
rust-lang 조직에서 자신에게 할당된 풀 리퀘스트는 이 GitHub URL에서 확인할 수 있습니다.
사용법
새 PR의 자동 할당은 아래에서 설명하는 triagebot.toml의 설정에 따라 처리됩니다.
수동 할당은 PR에 다음 텍스트로 댓글을 달면 됩니다:
r? @octocat— 특정 사용자를 할당합니다.r? octocat—@는 생략할 수 있습니다.r? libs—triagebot.toml에 정의된 libs(라이브러리) 임시 그룹에서 무작위로 한 명을 선택합니다. 예를 들어 rust-lang/rust 저장소의 임시 그룹 이름 목록은triagebot.toml을 참고하십시오.r? rust-lang/libs—rust-lang/조직 이름 접두사는 생략할 수 있습니다.r? rustdoc— rustdoc 팀에서 무작위로 한 명을 선택합니다. 팀 이름 목록은 팀 데이터베이스를 참고하십시오.r? rust-lang/rustdoc— 조직 이름 접두사는 생략할 수 있습니다.@를 사용하면 팀 전체가 해당 PR을 구독하고 알림을 받게 되므로,@는 사용하지 않을 것을 강력히 권장합니다.
팀에서 사용자를 선택할 때 triagebot은 팀에 직접 소속된 팀 구성원만 고려합니다(하위 팀은 무시합니다).
이름을 조회할 때 triagebot은 먼저 임시 그룹을 확인하고, 그다음 rust-lang 팀을 확인하며, 둘 중 어디에도 일치하지 않으면 GitHub 사용자로 간주합니다.
PR은 저장소에 쓰기 권한이 있는 사용자, 읽기 권한이 있는 rust-lang 조직 구성원, 또는 해당 PR에 댓글을 단 사람에게만 할당할 수 있습니다.
사용자가 r? name 댓글을 달아 특정 사용자에게 할당할 수 있게 하려면 triagebot.toml에 [assign] 테이블을 추가하십시오.
Ghost
PR을 열 때 첫 PR 최상위 댓글에 r? ghost를 사용하면 triagebot의 자동 할당이 비활성화됩니다. ghost는 삭제된 계정을 대신하는 GitHub의 자리 표시자 계정입니다. 여기서는 편의상 이 계정을 사용합니다. 이 방법은 보통 할당이나 불필요한 알림을 원하지 않는 롤업이나 실험에 사용됩니다.
다시 할당하기
풀 리퀘스트의 파일 diff를 기준으로 다른 리뷰어에게 할당하려면(즉, 명시적인 r? 없이 PR을 열었을 때 일어나는 동작을 재현하려면) @rustbot reroll 명령을 사용할 수 있습니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
저장소의 triagebot.toml에 [assign] 테이블이 있으면 해당 저장소에서 r?를 통한 PR 할당이 활성화됩니다.
[assign.owners] 테이블이 있으면 triagebot은 PR에서 수정된 파일을 기준으로 리뷰어를 자동으로 선택합니다.
# These are ad-hoc groups that can be referenced in `r?` and the `owners` table below.
# The values may contain GitHub usernames, other groups, or rust-lang teams.
# The `@` is optional.
# Group names should be lowercase.
[assign.adhoc_groups]
libs = ["@joshtriplett", "@Mark-Simulacrum", "@kenntytm", "@m-ou-se", "@thomcc"]
# Can reference other groups.
compiler = ["compiler-team", "compiler-team-contributors"]
compiler-team = ["cjgillot", "estebank"]
compiler-team-contributors = ["compiler-errors", "jackh726"]
# Can reference rust-lang teams.
libs = ["rust-lang/libs"]
# This is a special group that will be used if none of the `owners` entries matches.
fallback = ["@Mark-Simulacrum"]
# This specifies users, groups, or teams to assign for different paths.
# Triagebot will pick one person to assign.
# Paths are gitignore-style matches.
[assign.owners]
# Examples of assigning individuals.
"Cargo.lock" = ["@Mark-Simulacrum"]
"/library/std/src/sys/windows" = ["@ChrisDenton"]
# Example of assigning to a group.
"/library/std" = ["libs"]
# Supports gitignore patterns.
"*.js" = ["@octocat"]
# If you want to match all files, `*` should be sufficient.
"*" = ["@octocat"]
# Can use teams from the rust-lang teams database.
"/src/tools/cargo" = ["@rust-lang/cargo"]
휴가
리뷰어가 (자동 또는 수동으로) 할당되지 않도록 일시적으로 막고 싶다면 특별 그룹인 assign.users_on_vacation에 자신을 추가할 수 있습니다.
[assign]
users_on_vacation = ["jyn514", "ChrisDenton"]
rust-lang/rust에서는 Zulip 연동을 사용하여 휴가를 설정할 수도 있습니다.
커뮤니티 리뷰
저장소는 “커뮤니티 리뷰” 우선 방식을 설정할 수 있습니다. 이 방식에서는 (누구에게서든) PR 리뷰 승인이 최소 개수에 도달할 때까지 리뷰어 자동 할당이 미뤄집니다.
[assign.community_reviews]
# This is the minimun number of review approvals on the PR to be able to automatically assign a reviewer.
# Manual assignments (r?, reroll) are exempted from this minimum.
minimum_approvals = 2
# Label on the PR that denote the need for a community reviews (manually removing it, triggers auto assignment).
label = "S-waiting-on-community-reviews"
새 PR에 대한 추가 트리거 옵션
triagebot은 사용자에게 환영 메시지도 게시합니다. 이 동작은 몇 가지 요인에 따라 달라집니다.
- 이전에 커밋을 한 적이 없는 PR 작성자는 더 자세한 환영 메시지를 받습니다.
- 커밋을 한 적이 있는 PR 작성자는 축약된 메시지를 받습니다.
- 첫 PR 코멘트에
r?명령이 있으면 환영 메시지가 게시되지 않습니다.
triagebot.toml에는 새 PR에 대한 동작을 제어하는 여러 옵션이 있습니다.
[assign]
# If set, posts a warning message if the PR is opened against a non-default
# branch (usually main or master).
warn_non_default_branch = true
# If set, the welcome message to new contributors will include this link to
# a contributing guide.
contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html"
# If set, the welcome message to new contributors will include this link to
# a LLM policy.
llm_policy_url = "https://<llm policy link>"
또한 PR이 서브모듈을 수정하면 triagebot이 경고가 담긴 코멘트를 게시합니다.
기본 브랜치 경고의 예외
일부 PR은 나머지 PR과 기본 브랜치가 다를 수 있습니다. 이런 경우 PR 제목을 기준으로 예외를 추가할 수 있으며, 그러면 PR이 지정된 브랜치가 아닌 다른 브랜치를 대상으로 할 때 경고합니다.
[assign]
warn_non_default_branch.enable = true
[[assign.warn_non_default_branch.exceptions]]
title = "[beta" # title contains "[beta" in it
branch = "beta"
사용자 지정 메시지
일부 저장소는 미리 설정된 메시지 대신 사용자 지정 메시지를 사용하고 싶을 수 있습니다. 리뷰어를 자동으로 할당할 때(또는 할당하지 않을 때) 표시되는 메시지를 사용자 지정할 수 있습니다. 사용자 지정 메시지를 제공하더라도 contributing_url은 계속 사용됩니다.
[assign.custom_messages]
auto-assign-someone = "Thanks for the contribution, assigning {assignee}!" # only required if auto-assign (`[assign.owners]`) is configured
auto-assign-no-one = """
Thanks for the contribution!
Unfortunately, no reviewer could be found at the moment.
"""
메시지 내용은 ({assignee}를 제외하고) 그대로 GitHub에 전달되므로 GitHub가 지원하는 모든 형식을 내용에 사용할 수 있습니다.
구현
parser/src/command/assign.rs와 src/handlers/assign.rs를 참고하십시오.
리뷰 큐 추적
Triagebot은 rust-lang/rust 저장소에서 리뷰어의 작업량을 더 정교하게 추적하는 기능을 지원합니다. 각 리뷰어에게 “관련된” 풀 리퀘스트가 몇 개 할당되어 있는지 추적하며, 리뷰어가 이러한 PR의 최대 수용량과 자동 할당을 받을지 여부를 설정할 수 있게 합니다.
이 페이지에서는 리뷰 큐가 작동하는 방식과 Zulip에서 triagebot과 상호작용하여 리뷰 큐를 설정하고 확인하는 방법을 설명합니다.
설정
저장소에서 리뷰 큐 추적을 활성화하려면 해당 저장소의 triagebot.toml에 [pr-tracking] 테이블을 포함하십시오.
PR에 리뷰어를 할당할 때 리뷰 큐를 고려하도록 하려면 triagebot.toml에 [assign.review_prefs] 테이블을 추가하십시오.
이 기능은 현재
rust-lang/rust저장소에서만 작동한다는 점에 유의하십시오(triagebot에 하드코딩되어 있습니다). 더 많은 저장소에서 이 기능을 활성화하려면 추가적인 설계 및 구현 작업이 필요합니다.
리뷰 큐 설계
리뷰 큐는 특정 시점에 각 리뷰어에게 “관련된” rust-lang/rust PR이 몇 개 할당되어 있는지 기억합니다. 현재 PR을 “관련된” 것으로 판단하는 휴리스틱은 다음과 같습니다.
- PR이 차단된 상태가 아니어야 합니다(
S-blocked또는S-inactive레이블이 없어야 합니다). - PR이 롤업이 아니어야 합니다.
- PR이 리뷰어를 기다리는 중이어야 합니다(
S-waiting-on-review레이블이 있어야 합니다). - PR이 PR 작성자가 아닌 다른 사람에게 할당되어 있어야 합니다.
- PR이 열려 있어야 하며 초안이 아니어야 합니다.
PR이 이 모든 검사를 통과하고 리뷰어 R에게 할당되어 있다면, 해당 PR은 R의 리뷰 큐에 있는 것으로 간주됩니다.
자세한 내용은 triagebot의 waits_for_a_review 함수 구현을 참조하십시오.
리뷰 선호 설정
리뷰어는 PR에 누구를 할당할지 결정할 때 고려되는 리뷰 선호 설정을 구성할 수 있습니다.
- 리뷰 큐 수용량(
C) — 여러분의 리뷰 큐에 있는 PR 수가C에 도달하거나 이를 초과하면triagebot은 여러분에게 새 풀 리퀘스트를 할당하지 않습니다. - 로테이션 모드(
on또는off로테이션) — 로테이션 모드를off로 설정하면triagebot은 여러분에게 새 풀 리퀘스트를 할당하지 않습니다.- 이는 자신을 “휴가 중”으로 설정하는 방법의 대안으로,
triagebot.toml파일을 수정하는 풀 리퀘스트를 보낼 필요가 없습니다.triagebot은triagebot.toml의users_on_vacation과 로테이션 모드를 모두 고려하며, 둘 중 어느 하나에서라도 휴가 중으로 표시되어 있으면 여러분에게 PR을 할당하지 않습니다.
- 이는 자신을 “휴가 중”으로 설정하는 방법의 대안으로,
- 팀 로테이션 모드(
on또는off로테이션) —foo팀에 대한 팀 로테이션 모드를off로 설정하면 누군가r? foo를 사용할 때triagebot은 여러분을 고려하지 않습니다.- 이를 통해 어떤 팀의 리뷰를 맡을 수 있을지 선택적으로 정할 수 있습니다. 이 설정은
assign.owners와도 상호작용합니다.assign.owners는 특정 PR이 수정하는 파일에 따라 triagebot이 팀 구성원에게 PR을 자동으로 할당하도록 설정할 수 있습니다.
- 이를 통해 어떤 팀의 리뷰를 맡을 수 있을지 선택적으로 정할 수 있습니다. 이 설정은
리뷰 선호 설정은 애드혹 그룹이나 팀을 기반으로 한 할당에만 영향을 준다는 점에 유의하십시오. 누군가 여러분에게 직접 리뷰를 요청하는 경우(r? <user>), 현재 triagebot은 항상 여러분을 할당합니다. 여러분이 로테이션에서 빠져 있거나 최대 리뷰 수용량에 도달한 경우, triagebot은 여러분이 직접 할당된 PR에 댓글을 남겨 여러분이 제때 리뷰하지 못할 수도 있음을 PR 작성자에게 알립니다.
사용법
triagebot에 다이렉트 메시지 명령을 보내 자신의 리뷰 큐를 확인하고 리뷰 선호 설정을 구성할 수 있습니다.
work show:rust-lang/rust저장소에 있는 리뷰 큐의 내용과 리뷰 설정을 표시합니다.work set-pr-limit <number>|unlimited: 리뷰 용량을<number>로 설정하거나 용량 제한을 해제합니다(unlimited).work set-rotation-mode off|on: 로테이션 모드를on또는off로 설정합니다.work set-team-rotation-mode <team> off|on: 지정한<team>의 로테이션 모드를on또는off로 설정합니다.
구현
src/handlers/pr_tracking.rs를 참고하십시오.
백포트를 위한 풀 리퀘스트 자동 상정
컴파일러나 표준 라이브러리(또는 기타 구성 요소)의 회귀가 수정되면, 해당 패치를 회귀가 발생한 릴리스 채널(beta 또는 stable, 백포트 참고)로 백포트할지 평가합니다. 관련 팀이 수정 사항을 검토하고 백포트를 수락하거나 거절합니다.
중요한 회귀를 수정하는 패치의 가시성을 높이고 팀의 의사 결정을 돕기 위해, Zulip 채팅에 논의 주제를 자동으로 열 수 있습니다(예시).
백포트를 위한 풀 리퀘스트 자동 상정 기능은 git 저장소의 모든 새 패치를 검사하여 첫 코멘트에서 텍스트 표식(예: “Fixes #123”, GitHub 문서 참고)을 찾도록 구성할 수 있습니다. 그러한 텍스트가 발견되고, 수정되는 이슈가 P-high 또는 P-critical로 분류되어 있으며 required-issue-label 레이블(예: regression-from-*) 중 하나를 가지고 있으면, 핸들러가 구성된 add-labels 레이블을 적용합니다.
설정
triagebot.toml에 [backport.<foo>] 테이블이 하나 이상 구성되어 있으면 해당 저장소에서 자동 백포트 레이블 지정이 활성화됩니다:
[backport.foo]
required-pr-labels = ["T-compiler"]
required-issue-label = "regression-from-stable-to-beta"
add-labels = ["beta-nominated"]
[backport.bar]
required-pr-labels = ["T-compiler"]
required-issue-label = "regression-from-stable-to-stable"
add-labels = ["beta-nominated", "stable-nominated"]
[backport.baz]
required-pr-labels = ["T-libs"]
required-issue-label = "regression-from-stable-to-stable"
add-labels = ["stable-nominated"]
foo, bar, baz는 테이블을 서로 구분하기 위한 고유 식별자의 예시이며, 어떤 값이든 사용할 수 있습니다.
필드 설명:
required-pr-labels: 풀 리퀘스트가 열릴 때 반드시 가지고 있어야 하는 레이블 목록입니다(팀 레이블T-*사용을 권장합니다).required-issue-label: 회귀로 식별되기 위해 해당 회귀가 반드시 가지고 있어야 하는 레이블입니다.regression-from-stable-to-nightly,regression-from-stable-to-beta,regression-from-stable-to-stable중 하나를 사용할 수 있습니다.add-labels: 모든 조건이 충족될 경우 풀 리퀘스트에 지정될 레이블 목록입니다.
구현
src/handlers/backport.rs를 참고하십시오.
자동 레이블
자동 레이블은 triagebot.toml의 [autolabel] 설정에 따라 GitHub 이슈와 PR에 레이블을 자동으로 적용합니다.
사용법
자동 레이블은 수동으로 제어할 수 없습니다. 레이블을 수동으로 변경하는 방법은 레이블 지정을 참조하십시오.
설정
레이블에 의한 트리거
다른 레이블이 추가될 때 레이블을 추가할 수 있습니다. trigger_labels 설정 옵션은 어떤 레이블이 이를 트리거할지 지정합니다.
# Automatically applies the `I-prioritize` label whenever one of the labels
# listed below is added to an issue (unless the issue already has one of the
# labels listed in `exclude_labels`).
[autolabel."I-prioritize"]
trigger_labels = [
"regression-untriaged",
"regression-from-stable-to-stable",
"regression-from-stable-to-beta",
"regression-from-stable-to-nightly",
"I-unsound",
]
exclude_labels = [
"P-*",
"T-infra",
"T-release",
"requires-nightly",
]
제외 레이블은 셸과 유사한 * glob 패턴을 지원합니다.
파일에 의한 트리거
PR에서 어떤 파일이 수정되었는지에 따라 레이블을 추가할 수 있습니다. trigger_files 설정 옵션은 어떤 파일이 레이블을 추가하게 할지 지정합니다. 경로는 starts_with로 매칭합니다.
# Adds the `T-compiler` label to any PR that touches `compiler` or
# `src/test/ui` unless it already has a `T-*` label.
[autolabel."T-compiler"]
trigger_files = [
"compiler",
"tests/ui",
]
exclude_labels = [
"T-*",
]
새 PR에 의한 트리거
초안이 아닌 상태의 모든 PR에 레이블을 추가할 수 있으며, PR이 열릴 때 또는 이후 상태가 전환될 때 추가됩니다. PR이 더 이상 해당 조건을 충족하지 않으면 레이블이 제거됩니다.
이 기능을 활성화하려면 new_pr = true 설정 옵션을 지정하십시오. 예를 들면 다음과 같습니다.
[autolabel."S-waiting-on-review"]
new_pr = true
새 초안 PR에 의한 트리거
초안 상태의 모든 PR에 레이블을 추가할 수 있으며, PR이 열릴 때 또는 이후 상태가 전환될 때 추가됩니다. PR이 더 이상 해당 조건을 충족하지 않으면 레이블이 제거됩니다.
이 기능을 활성화하려면 new_draft = true 설정 옵션을 지정하십시오. 예를 들면 다음과 같습니다.
[autolabel."S-waiting-on-author"]
new_draft = true
새 이슈에 의한 트리거
모든 이슈에 대해 이슈가 열릴 때 레이블을 추가할 수 있습니다. 이 기능을 활성화하려면 new_issue = true 설정 옵션을 지정하십시오. 예를 들면 다음과 같습니다.
[autolabel."new-issue"]
new_issue = true
병합된 PR에 의한 트리거
모든 PR에 대해 PR이 병합될 때 레이블을 추가할 수 있습니다. 이 기능을 활성화하려면 pr_merged = true 설정 옵션을 지정하십시오. 예를 들면 다음과 같습니다.
[autolabel."needs-relnotes-triage"]
pr_merged = true
구현
src/handlers/autolabel.rs를 참조하십시오.
업스트림 뒤처짐
이 핸들러는 PR이 X일 지난 커밋을 기반으로 하는지 확인합니다.
배경
PR의 코드가 업스트림 브랜치의 아주 오래된 커밋을 기반으로 하면, 로컬에서 테스트할 때는 통과하지만 CI를 통해 테스트하도록 PR을 제출하면 실패합니다.
이는 CI가 커밋 패치를 현재 업스트림 브랜치에 적용하는데, 그 브랜치에 새로운 테스트 케이스가 있을 수 있어 통과하지 못하기 때문입니다. PR을 가장 최신 업스트림 브랜치로 리베이스해야 합니다.
설정
기본 임계값은 현재 7일로 설정되어 있습니다.
저장소의 triagebot.toml에 [behind-upstream] 테이블을 두면 이 기능이 활성화됩니다.
[behind-upstream]
또는 일수 임계값을 직접 설정할 수도 있습니다.
[behind-upstream]
days-threshold = 7
구현
이슈 링크 정규화
이 핸들러의 이름이 [issue-links]로 변경되었습니다. 이슈 링크를 참고하십시오.
닫기
close 명령을 사용하여 GitHub 이슈나 풀 리퀘스트를 닫을 수 있습니다.
사용법
이슈나 풀 리퀘스트를 닫으려면 rust-lang 팀 구성원 누구나 다음 명령을 입력할 수 있습니다:## Translator’s note I kept “rust-lang 팀 구성원” and did not use “언어 팀”. The flagged term is a false match. “rust-lang team member” means a member of any team in the rust-lang organization, not the language team (“lang team”). Writing “rust-lang 언어 팀 구성원” (as in Draft 2) would wrongly limit the command to language team members.
@rustbot close
이 명령을 입력하면 해당 이슈나 PR이 즉시 닫힙니다.
설정
저장소의 triagebot.toml에 [close] 테이블을 추가하면 해당 저장소에서 이 기능이 활성화됩니다.
[close]
구현
우려 사항
concern 명령은 GitHub 이슈/PR의 최상단 댓글에 우려 사항을 공식적으로 등록하는 데 사용됩니다.
사용법
다음 명령이 담긴 댓글을 작성하여 우려 사항을 등록할 수 있습니다:
@rustbot concern my concern title
그러면 해당 우려 사항이 GitHub 이슈 최상단 댓글의 특별 섹션인 Concerns에 추가됩니다(이 섹션은 손으로 직접 편집해서는 안 됩니다).
우려 사항은 조직 구성원만 등록할 수 있으며, 진행 중인 rfcbot FCP에는 등록할 수 없습니다.
우려 사항 해결하기
다음 명령이 담긴 댓글을 작성하여 우려 사항을 해결할 수 있습니다:
@rustbot resolve my concern title
그러면 해당 우려 사항에 취소선이 그어지고, 그 옆에 우려 사항을 해결한 댓글로 연결되는 링크가 추가됩니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
이 기능은 triagebot.toml에 [concern] 테이블을 두면 활성화됩니다:
[concern]
labels = ["has-concerns"] # optional, list of labels to be added to the issue/PR when there are un-resolved concerns
구현
parser/src/command/concern.rs와 src/handlers/concern.rs를 참고하십시오.
문서 업데이트
triagebot은 2주마다 모든 책 서브모듈을 업데이트하는 PR을 rust-lang/rust에 자동으로 생성합니다. 이 PR은 수동으로 승인해야 합니다. 이 업데이트는 현재 @ehuss가 관리합니다.
사용법
이 기능에는 설정이나 수동 제어 수단이 없습니다.
구현
src/handlers/docs_update.rs를 참조하십시오.
GitHub 릴리스
triagebot을 사용하면 태그가 푸시될 때 변경 로그의 해당 섹션을 릴리스 본문으로 사용하여 GitHub에 릴리스를 자동으로 생성할 수 있습니다. 이때 아티팩트는 업로드되지 않습니다.
사용법
git 태그를 푸시하거나 변경 로그의 내용을 수정할 때마다 triagebot은 모든 태그를 릴리스와 동기화합니다. 즉, 릴리스가 없는 태그에 대해서는 새 릴리스가 생성됩니다. 또한 모든 릴리스의 텍스트가 변경 로그의 텍스트와 동기화됩니다.
변경 로그에 항목이 없는 태그에 대해서는 릴리스가 생성되지 않습니다.
설정
GitHub 릴리스 자동 생성을 활성화하려면 저장소 루트의 triagebot.toml에 다음을 추가하십시오.
[github-releases]
format = "rustc"
project-name = "Rust"
changelog-path = "RELEASES.md"
changelog-branch = "main"
format은 변경 로그 파일이 따르는 형식을 정의하며, 변경 로그에서 해당 섹션을 올바르게 추출하는 데 사용됩니다. triagebot의 src/changelogs/를 수정하여 다른 형식을 추가할 수 있습니다. 현재 지원되는 형식은 다음과 같습니다.
rustc: rustc의 RELEASES.md에서 쓰는 자체 스타일을 따릅니다.
project-name은 릴리스의 제목을 정의합니다. 최종 제목은 {project-name} {tag}가 됩니다.
changelog-path와 changelog-branch 키는 triagebot이 변경 로그를 찾을 위치를 정의합니다.
구현
Git rebase (range) diff
강제 푸시로 PR의 베이스 커밋이 바뀌면 GitHub 자체 비교 기능에는 관련 없는 변경 사항이 대량으로 표시됩니다. 이 핸들러는 그러한 상황이 발생한 뒤 triagebot의 range-diff 기능으로 연결되는 댓글을 게시합니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
이 기능은 triagebot.toml에 빈 [range-diff] 테이블을 두면 해당 저장소에서 활성화됩니다:
[range-diff]
포함할 수 있는 선택적 키는 다음과 같습니다:
consider-push-from-bots(기본값:false) — 봇의 푸시도 range-diff 댓글 대상으로 고려할지 여부입니다.
구현
이슈 링크
이슈 링크 정규화
GitHub에서는 Fixes #123과 같은 자동 동작을 사용할 수 있으며, 이 동작은 123번 이슈를 닫습니다. 이 핸들러는 풀 리퀘스트 설명을 정규화된 형태인 Fixes org/repo#123으로 갱신합니다.
이는 서브트리를 업스트림 저장소로 갱신할 때 유용합니다. 서브트리 쪽 이슈 대신 업스트림 저장소의 이슈를 참조하고 닫아 버리는 일을 막아 주기 때문입니다.
커밋 내 이슈 링크
GitHub에서는 커밋에도 이슈 링크를 넣을 수 있으며, 이렇게 넣은 링크는 해당 이슈에 참조로 표시됩니다. 유용하기는 하지만, 참조된 이슈를 스팸으로 도배하는 결과를 낳는 경우가 많습니다. 이 핸들러는 이러한 링크에 대해 경고를 표시합니다.
설정
이 기능은 triagebot.toml에 [issue-links] 테이블을 추가하면 해당 저장소에서 활성화됩니다:
[issue-links]
커밋 검사 없이 사용하기
일부 저장소에서는 커밋 메시지 내 이슈 링크에 대해 경고할 필요가 없거나, 경고하지 않는 편을 선호할 수 있습니다. 이 동작은 check-commits 설정 옵션으로 비활성화할 수 있습니다:
[issue-links]
check-commits = false # defaults to true
정규화되지 않은 이슈 링크만 검사하기
서브트리(또는 내장된 저장소)의 경우, 정규화되지 않은 이슈 링크(예: rust-lang/cargo#123 대신 #132)는 계속 검사할 것을 권장합니다. 이렇게 하면 커밋 내 이슈 링크는 계속 허용하면서도, 서브트리가 업스트림에 병합될 때 링크가 잘못된 저장소를 가리키는 일을 막을 수 있습니다.
[issue-links]
check-commits = "uncanonicalized" # for subtrees
구현
src/handlers/issue_links.rs 및 src/handlers/check_commits/issue_links.rs를 참조하십시오.
이슈 이전
transfer 명령을 사용하면 GitHub 이슈를 한 저장소에서 다른 저장소로 이전할 수 있습니다.
사용법
이슈를 다른 저장소로 이전하려면 다음 형식으로 댓글을 입력하십시오.
@rustbot transfer <repository-name>
이전하는 이유를 설명하는 댓글도 함께 남기는 것을 권장합니다. 예를 들면 다음과 같습니다.
Transferring to rust-lang/cargo since this is an issue with how cargo
implements diagnostic reports.
@rustbot transfer cargo
중요: 이슈가 이전되고 있다는 시각적 표시는 나타나지 않습니다. GitHub API의 제약으로 인해 아무런 활동도 보이지 않습니다. 새 위치에서 이슈를 보려면 페이지를 새로 고쳐야 합니다. GitHub가 모든 데이터를 이전하는 데 잠시 시간이 걸릴 수 있습니다.
경고: 이전은 부분적으로 파괴적인 명령입니다. 예를 들어, 대상 저장소에 존재하지 않는 레이블과 마일스톤은 이슈에서 제거됩니다.
이전 명령은 rust-lang 조직의 팀 구성원과 wg-triage 및 wg-async의 구성원만 사용할 수 있습니다. 이전은 rust-lang 조직의 저장소로만 할 수 있습니다. 또한 대상 저장소에 triagebot이 활성화되어 있어야 합니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
해당 저장소로부터의 이전을 허용하려면 원본 저장소에 빈 transfer 테이블이 있어야 합니다. 이슈는 rust-lang 조직 내의 (triagebot이 활성화된) 어떤 저장소로든 이전할 수 있습니다.
[transfer]
구현
parser/src/command/transfer.rs 및 src/handlers/transfer.rs를 참조하십시오.
레이블 지정
댓글을 작성하여 이슈나 PR에 GitHub 레이블을 적용할 수 있습니다. 이슈에 레이블을 지정하면 검색하거나, 이슈들을 서로 연결하거나, 상태와 같은 정보를 공식적인 방식으로 나타내는 데 매우 유용합니다.
트리아지 WG는 이슈에 레이블을 지정하는 일을 돕습니다. 이슈 트리아지를 돕는 데 관심이 있다면 트리아지 WG 절차를 참고하십시오.
사용법
댓글의 일반적인 형식은 @rustbot label 뒤에 추가하거나 제거할 레이블 목록을 공백으로 구분하여 붙이는 것입니다. 레이블 앞에 - 문자를 붙이면 해당 레이블을 제거할 수 있습니다. 몇 가지 예시는 다음과 같습니다:
@rustbot label A-diagnostics A-macros@rustbot label +T-lang -T-compiler—T-compiler를 제거하고T-lang을 추가합니다.
레이블은 왼쪽에서 오른쪽 순서로 파싱되어 적용됩니다(충돌하는 레이블은 서로 상쇄됩니다). 예시:
# this command ...
@rustbot label +Alpaca -Bench -Carlo +Esteban +Dwight +Bench
# ... will be executed as:
@rustbot label +Alpaca +Esteban +Dwight -Carlo
이 명령의 구문은 어느 정도 유연하여, 원하는 대로 사용할 수 있도록 몇 가지 다른 형식을 지원합니다. 사용할 수 있는 변형의 몇 가지 예시는 다음과 같습니다:
@rustbot label: +T-lang, -T-compiler@rustbot label: +T-lang and -T-compiler@rustbot modify labels to +T-lang and -T-compiler@rustbot modify labels: +T-lang and -T-compiler@rustbot modify labels to +T-lang -T-compiler@rustbot labels "+good first issue"
이 명령은 ., ; 또는 줄 끝으로 종료할 수 있습니다.
형식 문법은 다음과 같습니다:
명령 →
@rustbotmodify? 레이블-단어to?:? 레이블-목록 (;|.)?레이블-단어 →
label
|labels레이블-목록 →
레이블-변경+
| 레이블-변경and레이블-목록
| 레이블-변경,레이블-목록
| 레이블-변경,and레이블-목록레이블-변경 →
+레이블
|-레이블
| 레이블
|"+인용된-레이블"
|"-인용된-레이블"
|"인용된-레이블"인용된-레이블 → [^“]*
레이블 → [^.,:!?;\n() ]+
권한
모든 레이블은 rust-lang 조직의 팀 구성원(그리고 트리아지 워킹 그룹인 wg-triage와 wg-async)이 지정할 수 있습니다. 팀에 속하지 않은 사용자는 triagebot.toml에서 명시적으로 허용된 레이블만 지정할 수 있습니다. 메인테이너는 대부분의 레이블을 누구나 지정할 수 있도록 허용하는 것이 좋습니다. 제한해야 할 레이블의 예로는 beta-accepted가 있습니다. beta로의 백포트를 승인하는 일은 보통 팀 구성원만 하기 때문입니다.
설정
저장소의 triagebot.toml에 [relabel] 테이블이 있으면 해당 저장소에서 레이블 지정 기능이 활성화됩니다:
[relabel]
인증되지 않은 사용자의 레이블 지정을 허용하려면 allow-unauthenticated 목록에 해당 레이블을 나열하면 됩니다:
[relabel]
# any label is allowed to be set by team members (anyone on a team in rust-lang/team)
# but these can be set by anyone in the world
allow-unauthenticated = [
"C-*", # any C- prefixed label will be allowed for anyone, independent of authorization with rust-lang/team
"!C-bug", # but not C-bug (order does not matter)
]
별칭
설정에서는 별칭도 지원합니다. 별칭은 여러 레이블의 집합으로 확장되는 단일 단어로, 명령 하나로 여러 레이블을 설정할 수 있게 해 주므로 같은 레이블 집합을 반복해서 추가하거나 제거할 때 유용합니다. 별칭을 설정하려면 triagebot 설정에 다음 항목을 추가하십시오:
[relabel.alias-name]
add-labels = ["Foo", "Bar"]
rem-labels = ["Baz"]
add-labels와 rem-labels는 별칭이 확장될 레이블의 배열입니다. 예를 들어, 위의 설정이 주어졌을 때:
# the command
@rustbot label alias-name
# translates to
@rustbot label +Foo +Bar -Baz
별칭은 부정형으로도 사용할 수 있으며, 이 경우 효과가 반대가 됩니다:
# this command
@rustbot label -alias-name
# translates to
@rustbot label +Baz -Foo -Bar
레이블과 별칭을 섞어서 사용할 수도 있습니다. 서로 상쇄되는 레이블은 생략됩니다:
# this command
@rustbot label alias-name +Baz
# translates to:
@rustbot label +Foo +Bar
구현
src/handlers/relabel.rs를 참조하십시오.
잠금
lock/unlock 명령으로 GitHub 이슈나 풀 리퀘스트를 잠그거나 잠금 해제할 수 있습니다.
사용법
잠금
이슈나 풀 리퀘스트를 잠그려면 rust-lang 팀 구성원 누구나 다음 명령을 입력할 수 있습니다:
@rustbot lock
이 명령을 입력하면 이슈나 PR이 즉시 잠깁니다.
잠금 해제
이슈나 풀 리퀘스트의 잠금을 해제하려면 rust-lang 팀 구성원 누구나 다음 명령을 입력할 수 있습니다:
@rustbot unlock
Note
팀 구성원이 명령을 게시할 수 없는 경우에는 triagebot Zulip
unlock명령을 사용할 수 있습니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
저장소의 triagebot.toml에 [lock] 테이블이 있으면 해당 저장소에서 이 기능이 활성화됩니다:
[lock]
구현
주요 변경
Triagebot은 주요 변경 제안의 자동 처리를 돕습니다.
사용법
이 절차는 이슈에 적절한 레이블이 설정되면 시작됩니다. 예를 들어 rust-lang/compiler-team 저장소에는 major-change 레이블을 자동으로 설정하는 주요 변경 템플릿이 있습니다. Triagebot은 이를 감지하여 논의를 위한 새 Zulip 토픽을 만들고, Zulip 스트림 링크가 담긴 댓글을 이슈에 게시합니다.
팀 구성원이 GitHub 이슈에 @rustbot second(또는 @rustbot seconded)라는 댓글을 작성하면, triagebot이 적절한 레이블을 설정하고 Zulip에 댓글을 게시합니다.
팀 구성원이 major-change-accepted 레이블을 추가하면, triagebot이 Zulip에 댓글을 게시하여 해당 제안이 수락되었음을 사람들에게 알립니다.
설정
이 기능은 triagebot.toml의 [major-change] 테이블로 활성화됩니다:
[major-change]
# Issues that have this label will start the MCP process.
# Defaults to "major-change".
enabling_label = "major-change"
# Label to apply once an MCP is seconded.
second_label = "final-comment-period"
# Label to apply when an MCP is created.
# Typically this is used to track what needs to be discussed at a meeting.
meeting_label = "to-announce"
# Label that indicates there are active concerns on the MCP
# Typically tracked by `@rustbot concern`
concerns_label = "has-concerns"
# When this label is added to an issue, that triggers acceptance of the proposal
# which sends an update to Zulip.
# Defaults to "major-change-accepted".
accept_label = "major-change-accepted"
# Waiting period (in days) before the MCP is considered accepted.
# Defaults to 10 days.
waiting_period = 10
# Enables automatic closing of the major change when the waiting period is completed.
# Defaults to false.
auto_closing = true
# Optional extra text that is included in the GitHub comment when the issue is opened.
open_extra_text = "cc @rust-lang/compiler @rust-lang/compiler-contributors"
# The Zulip stream to automatically create topics about MCPs in
# Can be found by looking for the first number in URLs, e.g.
# https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler
zulip_stream = 233931
# An Zulip group or username to tag in the Zulip message when a
# proposal has been seconded.
zulip_ping = "T-compiler"
# An optional tracking issue template that is automatically created when the major
# is accepted.
#
# There are currently three replacement variables:
# - ${mcp_number}: Issue number of the major change
# - ${mcp_title}: Title of the major change
# - ${mcp_author}: GitHub handle of the author of the major change
[major-change.tracking-issue-template]
# Name of the repository where the tracking issue should be created
repository = "rust"
# Title of tracking issue to create
title = "Tracking issue for MCP#${mcp_number}"
# Body of the tracking issue to create
body = """
Multi line body for MCP#${mcp_number}: ${mcp_title}
Created by @${mcp_author}
"""
# Labels to add to the tracking issue
labels = ["C-tracking-issue", "T-compiler"]
구현
src/handlers/major_change.rs를 참고하십시오.
멘션
Triagebot은 특정 파일을 변경하는 PR에 댓글을 남길 수 있습니다. 이 기능은 해당 파일의 변경 사항을 리뷰하려는 사람들에게 알리거나, 작성자에게 참고용 메시지를 전달하는 데 유용할 수 있습니다.
사용법
멘션은 저장소의 triagebot.toml 설정에 따라 PR이 열릴 때(또는 새 변경 사항이 푸시될 때) 자동으로 트리거됩니다.
설정
멘션을 활성화하려면 triagebot.toml의 [mentions] 테이블에 항목을 추가하십시오.
테이블의 각 키는 저장소 내의 경로이거나 문자열(type="content"인 경우)이어야 합니다. 자세한 내용은 아래의 해당 절을 참고하십시오.
테이블에는 다음 네 가지 선택적 값을 지정할 수 있습니다.
type— 충족해야 하는 매칭 유형을 지정합니다.filename(기본값) 또는content중 하나입니다.trigger_files—type = "content"멘션에서 내용 매칭 대상을 지정된 파일 glob 배열로만 제한합니다.cc— 핑을 보낼 사용자의 문자열 목록입니다.@ehuss나@rust-lang/clippy처럼@로 시작해야 합니다. 이 값을 지정하지 않으면 아무에게도 핑을 보내지 않습니다.message— 댓글에 포함될 메시지입니다. 이 값을 지정하지 않으면 댓글에는Some changes occurred in {path}라고 표시됩니다.
경로 기반 멘션
기본적으로 triagebot은 주어진 UNIX 스타일 경로로 시작하는1 모든 파일을 확인합니다. type="filename"으로 명시적으로 지정할 수도 있습니다.
glob 매칭은 다음 문법을 지원합니다.
?는 임의의 한 문자와 일치합니다.*는 0개 이상의 문자와 일치합니다.**는 디렉터리를 재귀적으로 일치시킵니다.{a,b}는a또는b와 일치하며, 여기서a와b는 임의의 glob 패턴입니다.[ab]는a또는b와 일치하며, 여기서a와b는 문자입니다.[!ab]는a와b를 제외한 모든 문자와 일치합니다.
예를 들어 library/std(또는 library/std*)는 library/std/src/process.rs처럼 library/std 디렉터리 아래의 모든 항목과 일치합니다.
내용 기반 멘션
선택적으로 triagebot은 type="content"를 사용하여 PR에서 추가된 모든 줄을 확인할 수 있습니다.
이 경우 키는 찾을 내용입니다.
예시
[mentions."src/tools/cargo"]
cc = ["@ehuss"]
[mentions."src/rustdoc-json-types"]
message = """
rustdoc-json-types is a **public** (although nightly-only) API.
If possible, consider changing `src/librustdoc/json/conversions.rs`;
otherwise, make sure you bump the `FORMAT_VERSION` constant.
"""
[mentions."#[rustc_attr]"]
type = "content"
cc = ["@someone"]
[mentions."miri"]
type = "content"
trigger_files = ["library"]
message = """
Any special-casing of Miri in the standard library requires review.
"""
cc = ["@rust-lang/miri"]
구현
src/handlers/mentions.rs를 참고하십시오.
-
시작하는 조건을 구현하기 위해 경로 끝에 암묵적인 glob
*가 추가됩니다. ↩
병합
merge 기능을 사용하면 병합 큐를 사용하는 저장소에서 PR을 병합하거나 PR 병합 권한을 위임할 수 있습니다.
참고: 현재 이 기능은 bors를 사용하는 저장소나 병합 큐 없이 PR을 직접 병합하는 저장소에서는 동작하지 않습니다.
사용법
병합
풀 리퀘스트를 병합하려면 권한이 있는 사용자(아래 참고)는 누구나 다음 명령을 입력할 수 있습니다:
@rustbot merge
이 명령을 입력하면 PR이 병합 큐에 추가됩니다.
이 명령은 다음 사용자만 사용할 수 있습니다:
- 저장소에 대해 최소한 쓰기 권한을 가진 구성원
- 병합 권한을 위임받은 사용자
위임
작성자에게 위임하기
풀 리퀘스트 병합 권한을 PR 작성자에게 위임하려면 저장소에 대해 최소한 쓰기 권한을 가진 구성원은 누구나 다음 명령을 입력할 수 있습니다:
@rustbot delegate+
또는
@rustbot delegate
그러면 대략 다음과 같은 메시지가 게시됩니다:
:v: @User 님, 이제 이 풀 리퀘스트를 병합할 수 있습니다!
@reviewer 님이 추가로 변경한 뒤 병합하라고 했다면, 해당 변경을 적용한 후
@rustbot merge를 게시하십시오.
특정 사용자에게 위임하기
PR 작성자가 아닌 다른 사용자에게 위임하려면 특정 사용자를 지정할 수 있습니다:
@rustbot delegate=@other_user
설정
이 기능은 triagebot.toml에 [merge] 테이블을 추가하면 해당 저장소에서 활성화됩니다:
[merge]
type = "merge-queue"
다음과 같은 선택적 키를 포함할 수 있습니다:
type— 사용할 병합 방식이며, 지원되는 값은merge-queue입니다.
구현
src/handlers/merge.rs를 참고하십시오.
병합 충돌
merge-conflicts 기능은 풀 리퀘스트에 병합 충돌이 있는지 감지하고, 작성자에게 충돌을 해결해 달라고 요청하는 댓글을 게시합니다.
사용법
이 기능은 브랜치에 커밋이 이루어져 기존에 열려 있는 PR에 병합 충돌이 생기면 자동으로 실행됩니다. 봇은 PR에 대략 다음과 같은 댓글을 게시합니다:
☔ 최신 업스트림 변경 사항(아마도 #152)으로 인해 이 풀 리퀘스트를 병합할 수 없게 되었습니다. 병합 충돌을 해결해 주십시오.
댓글이 게시되기까지 1분 정도 걸릴 수 있다는 점에 유의하십시오.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있으나, 자체 알림 기능이 있는 bors를 사용하는 경우는 예외입니다.
저장소의 triagebot.toml에 [merge-conflicts] 테이블을 두면 해당 저장소에서 이 기능이 활성화됩니다:
[merge-conflicts]
다음과 같은 선택적 키를 포함할 수 있습니다:
consider-prs-from-bots— 봇이 연 PR에 대해서도 병합 충돌을 보고할지 여부입니다.remove— 충돌이 감지되었을 때 PR에서 제거할 레이블 목록입니다.add— 충돌이 감지되었을 때 PR에 추가할 레이블 목록입니다.unless— PR에 이미 붙어 있으면 triagebot이 레이블을 추가하거나 제거하지 않도록 하는 레이블 목록입니다.
예시
[merge-conflicts]
consider-prs-from-bots = true
remove = ['S-waiting-on-bors']
add = ['S-waiting-on-author']
unless = ['S-blocked', 'S-waiting-on-crater', 'S-waiting-on-team', 'S-waiting-on-review']
구현
src/handlers/merge_conflicts.rs를 참고하십시오.
병합 금지 정책
병합 금지 정책은 풀 리퀘스트에 병합 커밋이 있는 경우 이를 사용자에게 알립니다. 일부 저장소는 리베이스 중심의 워크플로만 사용하는 것을 선호합니다.
사용법
이 기능은 PR에 병합 커밋이 있으면 자동으로 실행됩니다. Triagebot은 병합 커밋을 감지하면 해당 PR에 댓글을 남깁니다. 이 댓글은 병합 금지 정책과 사용자가 병합 커밋을 피하는 방법을 설명합니다.
설정
이 기능은 triagebot.toml에 [no-merges] 테이블을 두면 해당 저장소에서 활성화됩니다:
[no-merges]
이 테이블에는 다음 세 가지 선택적 값을 지정할 수 있습니다:
-
exclude_titles— 제외할 제목 일부를 나타내는 문자열 목록입니다. 제목에 이 부분 문자열이 포함된 PR은 병합 커밋 검사를 받지 않습니다. 대소문자를 구분합니다. -
labels— 추가할 레이블 이름의 문자열 목록입니다. 병합 커밋이 감지되면 이 레이블들이 PR에 지정됩니다. -
message— 병합 커밋에 대해 게시되는 기본 메시지를 재정의합니다. 이 메시지 뒤에는 항상 “The following commits are merge commits:“라는 문구와 병합 커밋 목록이 이어집니다.
기본 메시지
변경 사항에 병합 커밋(부모가 여러 개인 커밋)이 있습니다. 저희는 병합 금지 정책을 따르고 있으므로 이 풀 리퀘스트가 병합되려면 이 커밋들을 제거해야 합니다.
다음 명령으로 리베이스를 시작할 수 있습니다:
$ # rebase $ git pull --rebase https://github.com/rust-lang/rust.git HEAD $ git push --force-with-lease
예시
[no-merges]
# PRs with the following labels will be skipped
exclude_labels = ["rollup", "sync"]
# Add the following labels to PRs with merge commits
labels = ["has-merge-commits", "S-waiting-on-author"]
# Post the following warning message as a comment on PRs with merge commits
message = """
This repository does not allow merge commits.
Your PR cannot be merged until it is rebased.
"""
구현
상정
nominate 명령은 백포트할 이슈를 상정하는 데 사용됩니다.
사용법
GitHub 댓글에서 상정을 처리하는 데 사용할 수 있는 명령은 여러 가지가 있습니다.
@rustbot beta-nominate <team>—beta-nominated레이블과 지정한 팀의 레이블을 추가합니다. 이는 해당 이슈가 beta 백포트 대상으로 상정되었으며, 팀이 이를 수락할지 거부할지 결정해야 함을 나타냅니다.@rustbot nominate <team>—I-nominated레이블과 지정한 팀의 레이블을 추가합니다. 이는 팀에서 논의할 이슈를 상정하는 데 사용됩니다.@rustbot beta-accept—beta-accepted레이블을 추가합니다. 이는 해당 이슈가 beta 백포트 대상으로 승인되었으며, 누군가(보통 릴리스 팀)가 백포트 적용을 맡을 것임을 나타냅니다.@rustbot beta-approve—beta-accept의 별칭입니다.
rust-lang 팀 구성원만 상정 명령어를 사용할 수 있습니다.
설정에 나열된 팀만 상정 대상으로 지정할 수 있습니다.
여러 팀에 상정해야 하는 경우, 각 팀을 별도의 명령으로 추가하십시오. 이는 전반적인 요약 대신 각 팀이 무엇을 해야 하는지 팀별로 설명하도록 권장하기 위함입니다.
설정
이 기능은 triagebot.toml에 [nominate] 테이블을 두면 해당 저장소에서 활성화됩니다. nominate.teams 테이블에는 팀 이름과 각 팀에 사용할 레이블이 나열됩니다.
[nominate.teams]
compiler = "T-compiler"
release = "T-release"
core = "T-core"
infra = "T-infra"
구현
src/handlers/nominate.rs 및 parser/src/command/nominate.rs를 참조하십시오.
노트
note 명령을 사용하면 GitHub 이슈의 최상단 댓글에 요약을 추가하고 갱신할 수 있습니다.
사용법
다음 명령이 포함된 댓글을 작성하면 GitHub 이슈에 요약 노트를 추가할 수 있습니다:
@rustbot note summary-title
그러면 note 뒤의 단어가 GitHub 이슈의 최상단 댓글에 링크로 추가됩니다:
<!-- TRIAGEBOT_SUMMARY_START -->
### Summary Notes
- ["summary-title" by @username](link-to-comment)
Generated by triagebot, see [help](https://github.com/rust-lang/triagebot/wiki/Note) for how to add more
<!-- TRIAGEBOT_SUMMARY_END -->
이 링크는 note 명령을 게시한 댓글을 가리킵니다.
제목 단어는 정규 표현식 [^.,:!?;\n() ]+에 일치하는 문자열일 수 있습니다. 또는 "this is a title"처럼 따옴표로 묶은 문자열일 수도 있습니다.
노트를 더 추가하면 목록 끝에 덧붙여집니다:
<!-- TRIAGEBOT_SUMMARY_START -->
### Summary Notes
- ["first-note" by @username](link-to-comment)
- ["second-note" by @username](link-to-comment)
- ["summary-title" by @username](link-to-comment)
<!-- TRIAGEBOT_SUMMARY_END -->
이 요약 섹션은 손으로 직접 편집해서는 안 됩니다.
기존 요약 제거하기
@rustbot note remove summary-title 명령이 포함된 댓글을 작성하면 노트를 제거할 수 있습니다. 여기서 summary-title은 노트를 만들 때 사용한 단어입니다. 그러면 Triagebot이 요약 목록에서 해당 항목을 제거합니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
triagebot.toml에 [note] 테이블을 추가하면 이 기능이 활성화됩니다:
[note]
구현
핑 보내기
triagebot을 사용하면 대응하는 GitHub 팀이 없는 사람들의 팀에 “핑“을 보낼 수 있습니다. 이 기능이 유용한 이유는, 알림을 보낼 수 있는 사람들의 그룹을 유지하고 싶지만 그 그룹의 모든 구성원을 GitHub 조직에 추가하고 싶지는 않은 경우가 있기 때문입니다. 그렇게 하면 그들이 Rust 팀의 구성원이라는 의미가 되기 때문입니다(예를 들어, GitHub가 그들의 이름에 “member” 등의 표시를 붙입니다). 컴파일러 팀은 이 기능을 사용하여 알림 그룹에 연락합니다.
팀에 핑을 보내면 이슈에 메시지를 게시하고 레이블도 추가합니다. 메시지에는 팀의 모든 구성원을 @로 멘션하는 cc 줄이 포함됩니다.
사용법
핑 그룹이 구성된 저장소에서는 모든 Rust 팀 구성원(그리고 트리아지 워킹 그룹인 wg-triage와 wg-async)이 다음과 같은 GitHub 댓글을 작성할 수 있습니다:
@rustbot ping windows
이렇게 하면 triagebot이 windows 핑 그룹의 구성원에게 알리는 댓글을 게시합니다.
핑을 보낼 수 있는 팀
핑을 받으려면 팀이 Rust 팀 저장소에 생성되어 있어야 합니다. 이러한 팀은 흔히 marker-team으로 표시되는데, 이는 웹사이트에 나타나지 않는다는 뜻입니다. WASM 팀이 그 예입니다.
또한 해당 팀은 저장소의 triagebot.toml 파일에도 설정되어 있어야 합니다.
설정
팀(예: TeamName)에 핑을 보낼 수 있게 하려면, 다음과 같이 저장소 루트에 있는 triagebot.toml 파일에 섹션을 추가해야 합니다:
[ping.TeamName]
message = """\
Put your message here. It will be added as a Github comment,
so it can include Markdown and other markup.
"""
label = "help wanted"
이 설정은 지정된 메시지를 게시하고 이슈에 help wanted 레이블도 추가합니다.
별칭을 정의하여 동일한 대상 팀을 가리키는 추가 레이블을 더할 수도 있습니다. 별칭은 기억하기 쉬운 레이블을 추가하거나 사소한 철자 오류(“llvm” 대신 “llvms” 등)를 수용하는 데 유용할 수 있습니다. 다음 예시를 참고하십시오:
[ping.cleanup-crew]
alias = ["cleanup", "cleanups", "shrink", "reduce", "bisect"]
message = """\
message content...
"""
이렇게 하면 @rustbot ping cleanup-crew 명령을 모든 별칭 변형으로도 인식할 수 있습니다. 예:
@rustbot ping cleanup
@rustbot ping shrink
...
최신 예시는 rust-lang/rust 설정을 확인하십시오.
구현
렌더링된 링크
렌더링된 링크는 triagebot이 PR 설명에 자동으로 추가하고 갱신하는 간단한 하이퍼링크입니다.
설정
저장소의 triagebot.toml에 [rendered-link] 테이블을 두면 이 기능이 활성화됩니다:
[rendered-link]
trigger-files = ["posts/"]
exclude-files = ["posts/SUMMARY.md"]
trigger-files 키는 수정 여부를 감시할 디렉터리를 설정하며, exclude-files 키로 파일이나 디렉터리를 제외할 수 있습니다.
“렌더링된 링크“는 일치하는 파일 중 수정 사항이 가장 많은 파일을 가리킵니다.
구현
src/handlers/rendered_link.rs를 참조하십시오.
우선순위 지정 요청
사용자는 이슈의 우선순위 지정을 요청할 수 있습니다. 우선순위 지정이란 관련 팀(T-compiler, T-rustdoc, T-libs)이 해당 이슈를 살펴보고 우선순위를 평가하는 것을 의미합니다.
이 절차는 보통 회귀에만 사용됩니다. Rust 프로젝트는 회귀를 중요하게 다루며, 회귀는 대개 다른 모든 것보다 우선합니다. 전용 GitHub 이슈 템플릿으로 회귀를 등록하면 해당 이슈의 우선순위 지정이 자동으로 요청됩니다.
이 절차에 대해 더 알아보려면 우선순위 지정 문서를 참고하십시오.
사용법
우선순위 지정이 설정된 저장소에서는 누구나 다음과 같은 댓글을 남길 수 있습니다.
@rustbot prioritize
그러면 이슈에 I-prioritize 레이블이 추가되고 Zulip에 알림이 전송됩니다.
설정
이 기능은 triagebot.toml의 [prioritize] 테이블로 저장소에서 활성화됩니다.
[prioritize]
# Name of the label used for requesting prioritization on issues
label = "I-prioritize"
구현
parser/src/command/prioritize.rs와 src/handlers/prioritize.rs를 참고하십시오.
리뷰 이후 변경 사항
이 기능은 GitHub 리뷰 본문을 자동으로 수정하여 리뷰 이후 발생한 변경 사항을 확인할 수 있는 링크를 추가합니다.
사용법
풀 리퀘스트 리뷰를 작성하면 봇이 리뷰 본문 끝에 리뷰 이후 발생한 변경 사항을 확인할 수 있는 링크를 자동으로 덧붙입니다.
설정
Note
이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
저장소의 triagebot.toml에 [review-changes-since] 테이블이 있으면 해당 저장소에서 이 기능이 활성화됩니다:
[review-changes-since]
구현
src/handlers/review_changes_since.rs를 참고하십시오.
리뷰 변경 요청
이 기능은 리뷰어1가 변경을 요청하는 리뷰를 보내면 풀 리퀘스트의 레이블을 자동으로 조정합니다.
사용법
풀 리퀘스트 리뷰를 작성할 때, 리뷰를 마치면서 “Request Changes” 옵션을 클릭하십시오.
그러면 리뷰 레이블이 자동으로 제거되고, PR이 작성자의 조치를 기다리고 있음을 나타내는 새 레이블이 추가됩니다.
설정
이 기능은 triagebot.toml에 [review-submitted] 테이블을 두면 해당 저장소에서 활성화됩니다:
[review-submitted]
# These labels are removed when a review is submitted.
review_labels = ["S-waiting-on-review"]
# This label is added when a review is submitted.
reviewed_label = "S-waiting-on-author"
구현
src/handlers/review_submitted.rs를 참고하십시오.
-
이 기능에서 리뷰어는 담당자 중 한 명이거나, 저장소에 “write” 권한이 있는 사람이면 누구든 될 수 있습니다. ↩
리뷰 요청됨
이 기능은 PR 작성자가 담당자에게 리뷰를 요청하면 풀 리퀘스트의 레이블을 자동으로 조정합니다.
사용법
리뷰어 목록에서 담당자 이름 옆에 있는 “Re-request review” 버튼을 클릭하십시오. 그러면 “waiting on the author” 레이블이 자동으로 제거되고, PR이 리뷰를 기다리고 있음을 나타내는 새 레이블이 추가됩니다.
설정
저장소의 triagebot.toml에 [review-requested] 테이블을 두면 이 기능이 활성화됩니다:
[review-requested]
# Those labels are removed when PR author requests a review from an assignee
remove_labels = ["S-waiting-on-author"]
# Those labels are added when PR author requests a review from an assignee
add_labels = ["S-waiting-on-review"]
구현
src/handlers/review_requested.rs를 참고하십시오.
Rustc 커밋 추적
triagebot은 rust-lang/rust 저장소의 커밋 데이터베이스를 유지합니다. 이 정보를 가져오는 GitHub API는 느릴 수 있으므로 이 데이터베이스가 유용합니다. 예를 들어, rustc-perf 시스템에서 이를 사용합니다.
사용법
최상위 bors 병합 커밋은 https://triage.rust-lang.org/bors-commit-list에서 가져올 수 있습니다.
설정
이 기능에는 설정이 없으며, 자동으로 처리됩니다.
구현
src/db/rustc_commits.rs 및 src/handlers/rustc_commits.rs를 참고하십시오.
단축 명령
단축 명령은 자주 수행하는 작업을 위한 간단한 명령입니다.
사용법
단축 명령은 아래와 같이 GitHub 댓글을 작성하여 실행할 수 있습니다.
ready
@rustbot ready
이 명령은 PR이 리뷰할 준비가 되었음을 나타냅니다. 이 명령은 풀 리퀘스트에 S-waiting-on-review 레이블을 지정하고, S-waiting-on-author와 S-blocked 레이블이 있으면 모두 제거합니다.
@rustbot review와 @rustbot reviewer는 ready의 별칭입니다.
author
@rustbot author
이 명령은 PR이 작성자의 조치를 기다리고 있음을 나타냅니다. 이 명령은 풀 리퀘스트에 S-waiting-on-author 레이블을 지정하고, S-waiting-on-review와 S-blocked 레이블이 있으면 모두 제거합니다.
blocked
@rustbot blocked
이 명령은 PR이 무언가에 막혀 있음을 나타냅니다. 이 명령은 풀 리퀘스트에 S-blocked 레이블을 지정하고, S-waiting-on-author와 S-waiting-on-review 레이블이 있으면 모두 제거합니다.
설정
이 기능은 저장소의 triagebot.toml에 [shortcut] 테이블을 두면 활성화됩니다:
[shortcut]
구현
parser/src/command/shortcut.rs와 src/handlers/shortcut.rs를 참고하십시오.
Triagebot 대시보드
트리아지 대시보드는 열려 있는 풀 리퀘스트의 트리아지를 돕는 데 사용됩니다.
사용법
저장소별 트리아지 대시보드는 https://triage.rust-lang.org/triage에서 확인할 수 있습니다.
모든 rust-lang 저장소는 https://triage.rust-lang.org/triage/<owner>/<repo> 형식으로 볼 수 있습니다.
설정
이 기능에는 설정이 없습니다.
구현
src/triage.rs를 참고하십시오.
모든 댓글 보기 링크
“모든 댓글 보기” 링크는 triagebot이 PR 또는 이슈 설명에 자동으로 추가하는 단순한 하이퍼링크입니다. 이 링크는 GitHub 이슈와 PR용 triagebot 댓글 뷰어로 연결됩니다.
이름에서 알 수 있듯이, 이 링크는 GitHub에서 모든 댓글(및 리뷰 댓글)을 불러와 한 번에 모두 표시합니다. 이는 전체 대화를 보려면 “Load More“를 여러 번 클릭해야 하는 GitHub UI와 대조됩니다.
설정
저장소의 triagebot.toml에 [view-all-comments-link] 테이블이 있으면 이 기능이 활성화됩니다:
[view-all-comments-link]
threshold = 25 # 20 comments by default
exclude-prs = true # false by default
exclude-issues = false # false by default
모든 옵션은 선택 사항입니다.
“모든 댓글 보기” 링크는 자체 GitHub 댓글 뷰어인 triagebot의 /gh-comments 엔드포인트를 가리킵니다.
구현
Triagebot Zulip 명령
다음 두 가지 방법으로 Rust Zulip 서버에서 triagebot에 명령을 보낼 수 있습니다.
- triagebot 계정에 다이렉트 메시지(DM)를 보내는 방법입니다.
- 스트림에서
@**triagebot**을 태그하고 그 뒤에 명령을 붙여(예:@**triagebot** end-meeting) 메시지를 보내는 방법입니다.
Triagebot 명령은 팀 데이터베이스에 등록된 사용자만 보낼 수 있습니다.
다이렉트 메시지 명령
다음 명령은 triagebot 계정에 다이렉트 메시지로 보낼 수 있습니다.
whoami: 사용자가 속한 Rust 팀을 보여 줍니다.lookup github <zulip-name>: 사용자의 Zulip 이름으로 GitHub 사용자 이름을 조회합니다.lookup zulip <github-username>: 사용자의 GitHub 사용자 이름으로 Zulip 이름을 조회합니다.unlock [--org <org>] <repo> <issue-id>: 지정한 이슈나 풀 리퀘스트의 잠금을 해제합니다(기본 조직: rust-lang).user-info <user-name> [--org <org>]: 지정한 GitHub 계정의 기본 정보를 보여 줍니다. 여기에는 지정한 GitHub 조직(org)에서 해당 계정이 최근에 작성한 댓글과 PR이 포함됩니다.team-stats <team-name>: 지정한 팀의 모든 구성원에 대한 리뷰 작업 큐 통계를 보여 줍니다.team-stats <team-name> <repo>: 지정한 저장소를 기준으로(즉, 해당 저장소의triagebot.toml을 고려하여) 지정한 팀의 모든 구성원에 대한 리뷰 작업 큐 통계를 보여 줍니다.- 리뷰어 작업 큐 명령은 여기에 설명되어 있습니다.
이 명령들의 매개변수를 더 자세히 알아보려면 각 명령에 --help 플래그나 help 하위 명령을 사용하십시오.
다른 사용자로 실행하기
다음 메시지를 사용하면 위 명령을 다른 GitHub 사용자를 대신하여 실행할 수도 있습니다.
as <github-username> <command>
# e.g.
as MyFavouriteGitHubUser work show
이 방식으로 “민감한” 명령(즉, 무언가를 변경하는 명령)을 실행하면, triagebot은 다른 사용자가 해당 사용자를 대신하여 명령을 실행했다는 사실을 다이렉트 메시지로 그 사용자에게 알립니다.
다른 사용자로 실행하는 기능은 다른 사용자의 상태(예: 리뷰어 작업 큐나 Rust 팀)를 확인하거나 가끔 디버깅하는 용도로 마련된 것입니다. 악의적인 목적으로 사용하지 마십시오.
스트림 명령
- 회의 명령은 Zulip 회의 진행을 제어하는 데 사용됩니다. 이 명령들은 여기에 설명되어 있습니다.
- Rust 프로젝트 목표 명령은 Rust 프로젝트 목표 추적을 제어하는 데 사용됩니다.
@**triagebot** ping-goals <threshold> <next-update>: 목표 팀이 Zulip에서 목표 담당자에게 핑을 보내 목표의 진행 상황을 공유하도록 요청할 때 사용합니다. 최근<threshold>일 이내에 댓글이 있었다면 핑을 보내지 않습니다.<next-update>는 다음 블로그 업데이트가 언제 시작되는지 알려 주는 문자열입니다.
@**triagebot** docs-update: 문서 서브모듈을 업데이트하는 풀 리퀘스트(예시)를 생성합니다. 문서 업데이트를 참조하십시오.@**triagebot** backport [approve | decline ] [stable | beta ] <PR>(예: “@triagebot backport approve beta 123456”): GitHub에 PR 백포트를 승인하거나 거절하는 댓글을 게시합니다(백포트 참조).@**triagebot** assign-prio <issue #> [ critical | high | medium | low | none ]: 이슈에 우선순위 레이블을 지정합니다(우선순위 지정 참조). “none“을 지정하면I-prioritize레이블만 제거합니다.@**triagebot** unlock [--org <org>] <repo> <issue-id>: 지정한 이슈나 풀 리퀘스트의 잠금을 해제합니다(기본 조직: rust-lang).
구현
src/zulip.rs를 참고하십시오.
Zulip 회의 관리
Triagebot은 회의 진행을 돕기 위해 Zulip에서 몇 가지 명령에 응답할 수 있습니다.
사용법
Zulip에서 @triagebot을 대상으로 아래에 나열된 명령을 담은 메시지를 입력하십시오.
문서 읽기
@triagebot read
이 명령을 사용하면 triagebot이 모든 사람이 어떤 문서를 다 읽고 논의를 시작할 준비가 되었는지 확인하는 투표 댓글을 게시합니다. 메시지는 대략 다음과 같습니다.
Click on the :book: when you start reading (and leave it clicked).
Click on the :checkered_flag: when you finish reading.
그러면 사용자는 이모지 반응 버튼을 눌러 현재 읽는 중임을 표시하고, 다 읽은 후에 다시 눌러 완료했음을 표시할 수 있습니다.
주제 종료
@triagebot end-topic
이 명령을 사용하면 triagebot이 회의 참석자 모두가 다음 주제로 넘어갈 준비가 되었는지 확인하는 투표 댓글을 게시합니다. 메시지는 대략 다음과 같습니다.
Does anyone have something to add on the current topic?
React with :working_on_it: if you have something to say.
React with :all_good: if not.
그러면 사용자는 이모지 반응 버튼을 눌러 준비가 되었는지 여부를 표시할 수 있습니다.
@triagebot await는 end-topic의 별칭입니다.
회의 종료
@triagebot end-meeting
이 명령을 사용하면 triagebot이 모든 사람이 회의를 끝낼 준비가 되었는지 확인하는 투표 댓글을 게시합니다. 메시지는 대략 다음과 같습니다.
Does anyone have something to bring up?
React with :working_on_it: if you have something to say.
React with :all_good: if you're ready to end the meeting.
그러면 사용자는 이모지 반응 버튼을 눌러 회의를 끝낼 준비가 되었는지 여부를 표시할 수 있습니다.
설정
이 기능에는 별도의 설정이 없으며 모든 팀 구성원이 사용할 수 있습니다. 단, 본인의 Zulip ID가 팀 데이터베이스에 설정되어 있어야 합니다.
구현
src/zulip.rs를 참고하십시오.
Zulip 알림
triagebot은 이슈 레이블 등 다양한 트리거를 기반으로 Zulip에 메시지를 보낼 수 있습니다.
사용법
Zulip 알림은 아래에 설명된 설정에 따라 자동으로 전송됩니다. 레이블이 추가되거나 제거될 때, 또는 이슈가 닫히거나 다시 열릴 때 알림이 전송되도록 할 수 있습니다.
예를 들어 rust-lang/rust 저장소는 이슈에 A-edition-2021 레이블이 붙을 때마다 “Edition 2021”(2021 에디션) 스트림에 메시지를 자동으로 게시하도록 설정되어 있으며, 그 메시지는 대략 다음과 같습니다.
triagebot
이슈 #109298 “ICE
Subslice unexpected because it isn't captured–edition=2021“이 추가되었습니다.
설정
저장소의 triagebot.toml에 [notify-zulip] 테이블이 있으면 해당 저장소에서 이 기능이 활성화됩니다.
일부 메시지는 치환을 지원합니다:
{number}는 이슈/PR 번호로 치환됩니다.{title}은 이슈/PR 제목으로 치환됩니다.{recipients}는 이슈/PR에 관련된 사람들, 즉 작성자와 담당자를 멘션하는 목록으로 치환됩니다.
# Triggers a Zulip notification based on the given label name.
[notify-zulip."label-name"]
# The Zulip stream to post to.
# Can be found by looking for the first number in URLs, e.g. https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler
zulip_stream = 245100 # #t-compiler/prioritization/alerts
# The Zulip topic to post to.
# Supports {number} and {title} substitution.
topic = "#{number} {title}"
# Optional message to be posted on GitHub when opening the Zulip topic.
# Useful to add a backlink from GitHub to the Zulip topic.
# Supports {zulip_topic_url} substitution.
github_comment = "[Zulip topic]({zulip_topic_url}) was opened to discuss this issue."
# The message to post when the label is added.
# Supports {number}, {title}, and {recipients} substitution.
message_on_add = "Issue #{number} \"{title}\" has been added."
# The message to post when the label is removed.
# Supports {number}, {title}, and {recipients} substitution.
message_on_remove = "Issue #{number}'s nomination has been removed. Thanks all for participating!"
# The message to post when the issue/PR is closed and it has the label.
# Supports {number}, {title}, and {recipients} substitution.
message_on_close = "Issue #{number} has been closed. Thanks for participating!"
# The message to post when the issue/PR is reopened and it has the label.
# Supports {number}, {title}, and {recipients} substitution.
message_on_reopen = "Issue #{number} has been reopened. Pinging @*T-types*."
# The Zulip notification will not be posted unless the issue/PR has all of these labels.
# Please replace the `{team}` placeholder with the appropriate team to be notified for the nomination
# (ex. `I-compiler-nominated`, `I-lang-nominated`, ...)
required_labels = ["I-{team}-nominated"]
구현
src/handlers/notify_zulip.rs를 참조하십시오.
GitHub Actions가 생성한 PR 열기/닫기 기능
이 자동화는 github-actions 사용자, 즉 GitHub Actions 작업이 연 PR을 자동으로 닫았다가 다시 엽니다. 이를 통해 사람이 직접 손대지 않아도 해당 PR에서 CI가 실행될 수 있습니다.
설정
이 기능은 저장소의 triagebot.toml에 [bot-pull-requests] 테이블을 두면 활성화됩니다.
[bot-pull-requests]
구현
src/handlers/bot_pull_requests.rs를 참조하십시오.
컴파일러
Rust 컴파일러 팀은 Rust 컴파일러의 유지 관리, 성능 개선, 컴파일러 기능의 안정화 검토를 맡고 있습니다.
저희는 Forge에 팀의 프로세스, 정책, 업무 관행을 문서화합니다. 컴파일러의 동작 방식이나 개발 환경 설정 방법을 알고 싶으시다면 rustc-dev-guide를 참고하십시오.
- 백포트
- 컴파일러 변경 사항의 beta 및/또는 stable 백포트는 어떻게 요청합니까? 컴파일러 백포트 상정은 어떻게 처리됩니까?
- 캘린더
- 컴파일러 팀의 캘린더는 어떻게 구독합니까?
- 팀 간 협업
- 컴파일러 팀에 도움은 어떻게 요청합니까?
- 회의
- 컴파일러 팀은 어떤 회의를 운영하며, 어떻게 참석할 수 있습니까?
- 구성원 자격
- 컴파일러 팀 구성원에게는 무엇이 기대되며, 어떻게 합류할 수 있습니까?
- 제안, 승인 및 안정화
- 컴파일러 팀에 변경 사항은 어떻게 제안합니까? 제 변경 사항에는 어떤 승인이 필요합니까?
- 저희가 유지 관리하는 저장소
- 팀이 유지 관리하고 기여하는 여러 코드 저장소
- 자료
- 기여자와 팀 구성원이 활용할 수 있는 유용한 자료에는 무엇이 있습니까?
- 리뷰 정책
- 리뷰하기 쉬운 기여는 어떻게 만듭니까? 팀 구성원으로서 리뷰는 어떻게 시작합니까?
- 보조 도구
rustc에서strip같은 외부 도구는 언제 호출할 수 있습니까? 외부 도구와 관련된 이슈는 어떻게 트리아지해야 합니까?
- 서드파티 및 트리 외부 크레이트 정책
- 컴파일러에 서드파티 크레이트는 언제 추가할 수 있습니까? 컴파일러용 트리 외부 크레이트는 언제 만들 수 있습니까?
- 트리아지 및 우선순위 지정
- 컴파일러 이슈는 어떻게 트리아지되고 우선순위가 정해집니까?
- 운영
- 컴파일러 팀 지원
- 작업 영역
- 컴파일러 관련 세부 작업 영역
백포트
심각한 회귀를 수정하거나, 처음 병합할 때 발견하지 못한 의도치 않은 변경을 되돌리기 위해 컴파일러 수정 사항을 stable 채널이나 beta 채널, 또는 두 채널 모두에 백포트해야 하는 경우가 있습니다. 이슈와 회귀의 우선순위를 정하는 방법은 우선순위 지정도 참고하십시오.
stable 채널에 백포트가 적용되면 Rust 프로젝트는 패치 릴리스를 출시합니다(예를 들어 최초 stable 릴리스 1.87.0 다음에 1.87.1 포인트 릴리스가 나올 수 있습니다).
백포트 리뷰에 상정하기
GitHub 풀 리퀘스트에 레이블을 붙여 변경 사항의 백포트를 제안할 수 있습니다. 패치를 beta 채널에 백포트해야 한다면 beta-nominated를 추가하고, stable 채널에도 백포트해야 한다면 stable-nominated도 추가하십시오. 풀 리퀘스트에 T-compiler 레이블도 있는지 확인하십시오.
어떤 경우든 풀 리퀘스트를 백포트에 상정할 때는 코멘트를 추가하여 컴파일러 팀 백포트 리뷰어에게 이 변경을 백포트해야 하는 이유를 설명해야 합니다.
백포트 상정이 반드시 승인되는 것은 아닙니다. 백포트 상정의 승인 또는 거부 기준은 아래 백포트를 승인해야 합니까 절을 참고하십시오.
stable 채널까지 넘어간 beta 회귀는 stable 백포트 상정이 필요하며, 승인되면 이후 패치 릴리스도 필요합니다.
컴파일러 팀은 치명적인 이슈(P-critical 레이블이 붙은 이슈)가 stable 릴리스까지 넘어가지 않도록 노력합니다.
컴파일러 백포트 상정 리뷰하기
beta-nominated 또는 stable-nominated 레이블 중 하나가 붙으면 Zulip의 #t-compiler/backports 채널에 새 스레드가 자동으로 열립니다. 컴파일러 팀 구성원은 이 Zulip 스레드에서 비동기적으로 백포트 찬성 투표를 하거나 우려를 제기할 수 있습니다. 컴파일러 팀 구성원으로서 이 스레드의 알림을 받고 싶다면 해당 Zulip 채널을 구독하십시오.
주간 트리아지 회의(#t-compiler/meetings에서 진행되며, 여기를 참고하십시오) 중에 컴파일러 팀이 결정을 확정하고 해당하는 {beta,stable}-accepted 레이블을 적용합니다.
백포트를 승인해야 합니까?
다음은 컴파일러 백포트 리뷰어가 백포트 여부를 결정할 때 고려할 수 있는 사항이며, 전부를 망라한 목록은 아닙니다.
- 상정된 변경 사항이 (예를 들어 구현상의 문제로) stable 또는 beta 릴리스 채널에 맞춰 제때 병합되지 않았습니다.
- 백포트를 테스트할 시간이 충분합니까?
- (beta 백포트 상정의 경우) 상정된 변경 사항이 다음 stable 컴파일러 릴리스에 너무 임박해서 적용됩니다. 이 시점에 백포트를 병합하면 테스트할 시간이 매우 제한적이거나 아예 없습니다.
- stable 포인트 릴리스는 검증 기간 없이 모든 사용자에게 즉시 배포됩니다!
- 상정된 컴파일러 변경 사항은 얼마나 복잡하거나 위험합니까? 백포트가 새 회귀를 일으킬 위험이 해결하려는 회귀나 이슈보다 더 심각할 가능성이 있습니까?
- 수정하려는 회귀나 이슈는 얼마나 심각합니까?
- 예를 들어 stable 회귀라고 해서 모두 같지는 않습니다. 어떤 회귀는 그다지 심각하지 않고, 어떤 회귀는 이미 여러 릴리스 전부터 stable 채널에 있었습니다.
기본적으로 stable 백포트가 승인되면 릴리스 팀이 새 포인트 릴리스를 출시합니다.
다만 컴파일러 팀은 stable 백포트를 승인하면서도, 이 상정이 그 자체만으로는 stable 포인트 릴리스를 낼 근거가 되지 않는다고 릴리스 팀에 추가로 알릴 수 있습니다. 이 경우 릴리스 팀은 승인된 다른 stable 백포트 후보들을 이 후보와 함께 검토하고 각각이 얼마나 심각한지 고려하여 stable 포인트 릴리스를 진행할지 최종 결정합니다.
승인된 백포트는 어떻게 처리됩니까?
릴리스 팀(T-release)이 현재 개발 주기가 끝날 때 백포트를 처리합니다(릴리스 백포트 참조). beta 백포트 상정이 너무 늦게 승인되면 릴리스 팀이 해당 변경 사항을 백포트하지 못할 수도 있습니다.
대부분의 경우 승인된 백포트는 기본 브랜치를 대상으로 합니다. 드물게는 beta 백포트가 beta 브랜치를 직접 대상으로 해야 할 수도 있습니다. 이 경우 병합하기 전에 Zulip #t-release 채널)에 새 스레드를 열어 릴리스 팀과 조율하십시오.
복잡한 백포트의 경우 릴리스 팀이 패치 작성자에게 도움을 요청할 수 있습니다.
캘린더
컴파일러 팀의 모든 캘린더는 Rust 프로젝트의 rust-lang/calendar 저장소에서 확인할 수 있습니다.
새 일정 추가하기
프로젝트 구성원이라면 누구나 새 일정이나 하위 캘린더를 추가하는 풀 리퀘스트를 제출할 수 있습니다. 풀 리퀘스트의 리뷰어로 컴파일러 팀 리드를 지정하기만 하면 됩니다.
캘린더 구독하기
팀 캘린더는 원하는 캘린더 애플리케이션으로 가져올 수 있는 ics 파일로 배포됩니다.
아래에서 캘린더 링크를 복사할 수 있습니다.
https://rust-lang.github.io/calendar/compiler.ics워킹 그룹이 포함되지 않은 대체 링크는 캘린더 저장소에서 확인할 수 있습니다.
- Fastmail
- 설정 페이지를 여십시오. 왼쪽 사이드바에서 “Calendars“로 이동하십시오. “Subscriptions” 섹션까지 스크롤한 다음, 입력란에 컴파일러 팀 캘린더 링크를 붙여 넣고 “Subscribe to calendar“를 누르십시오.
- Google Calendar
- 왼쪽 사이드바에서 “Other Calendars” 옆의 “+” 아이콘을 누르십시오. “From URL“을 선택하고 입력란에 컴파일러 팀 캘린더 링크를 붙여 넣은 다음 “Add calendar“를 누르십시오.
- Outlook Web
- 왼쪽 사이드바의 아이콘을 사용해 캘린더로 이동하십시오. 왼쪽에서 “Add Calendar“를 선택한 다음 “Subscribe from the web“을 선택하고, 입력란에 컴파일러 팀 캘린더 링크를 붙여 넣은 뒤 “Import“를 누르십시오.
사용하는 캘린더 애플리케이션이 위 목록에 없다면, 이 문서에 안내를 추가하는 풀 리퀘스트를 언제든지 제출하십시오.
팀 간 협업
다른 팀의 구성원으로서 컴파일러 팀에 이슈를 제기하고자 하는 경우..
..논의를 위해
GitHub 이슈에 상정 사유(즉, 어떤 결정이 필요한지/어떤 의견을 구하는지, 컴파일러 팀과 관련된 부분이 무엇인지 등)를 설명하는 댓글을 작성하고 이슈에 I-compiler-nominated 레이블을 추가하십시오(댓글에 @rustbot label +I-compiler-nominated를 포함하면 레이블을 추가할 수 있습니다).
상정된 이슈는 다가오는 트리아지 회의에서 논의됩니다. 컴파일러 팀이 매주 상정된 이슈를 모두 다루지 못할 때도 있으므로, 이슈가 논의되기까지 회의가 두 번 이상 걸릴 수 있습니다.
논의가 끝나면 팀 구성원이 논의 결론과 관련 Zulip 대화 링크를 이슈에 댓글로 남깁니다.
..수정을 위해
요청하는 팀의 구성원과 컴파일러 기여자 사이에 이미 협업 관계가 있다면, 팀이 작업 완료를 요청할 때 가장 먼저 택할 수 있는 방법은 해당 기여자를 핑하여 작업을 맡아 줄 수 있는지 묻는 것입니다. 핑은 공개 Zulip 채널에서 하는 것을 권장합니다. 그래야..
- ..여유 시간이 있는 다른 기여자들이 도움을 제안할 기회를 얻을 수 있습니다.
- ..다른 컴파일러 팀 구성원/리더십이 요청이 합리적인지 확인할 수 있습니다(컴파일러 팀이 다른 팀을 위해 우선적으로 처리하기로 약속한 이슈 유형은 이 절의 나머지 부분을 참고하십시오).
요청을 받는 기여자가 쓸 수 있는 여력이 얼마나 되는지, 그리고 그 기여자의 컴파일러 전문 분야가 요청과 관련이 있는지 고려하는 것이 좋습니다.
컴파일러 팀에 직접 연락할 적절한 담당자가 없다면, GitHub 이슈에 완료해야 할 작업을 설명하는 댓글을 작성하십시오(또는 이슈를 생성하십시오). 팀은 다음과 같은 경우에 컴파일러 팀에 이슈를 상정해야 합니다. 이슈가..
- ..기존 이니셔티브나 워킹 그룹에서 이미 추적하고 있지 않거나 그 일부가 아니고..
- ..다른 팀의 작업을 막거나 방해하고 있지만(예: 그 외에는 완성된 무언가의 안정화를 가로막는 기능이나 버그)..
- ..절대적으로 치명적이지는 않은 경우입니다. 건전성 버그나 그 밖의 치명적인 이슈는 우선순위 지정 워킹 그룹이 우선순위를 정하고, 이러한 버그를 위한 컴파일러 팀의 다른 절차를 통해 처리됩니다. 이슈에 우선순위 레이블이 없다면
I-prioritize레이블을 추가할 수 있으며, 그러면 해당 이슈가 우선순위 지정 대기열에 추가됩니다.
가능하다면 요청하는 기능이나 수정할 버그를 자세히 설명하는 것이 도움이 됩니다(그래야 컴파일러 기여자가 요청하는 팀의 문제를 해결할 방법을 추측하지 않아도 됩니다). 요청하는 팀의 구성원이 이슈 담당 연락처로 명시되어 있지 않으면 댓글 작성자가 담당 연락처로 간주됩니다.
이슈에 I-compiler-nominated 레이블을 추가하십시오(@rustbot label +I-compiler-nominated를 사용하여 추가할 수 있습니다).
상정된 이슈는 다가오는 트리아지 회의에서 논의됩니다. 컴파일러 팀이 매주 상정된 이슈를 모두 다루지 못할 때도 있으므로, 이슈가 논의되기까지 회의가 두 번 이상 걸릴 수 있습니다. 컴파일러 팀의 논의 결과, 이슈는..
- ..수락될 수 있습니다. 이 경우 이슈가 기여자에게 배정되고 상정 레이블이 제거됩니다. 배정되면 팀 구성원이 해당 이슈를 작업합니다. 적절한 시간이 지나도 작업이 완료되지 않으면 이슈를 다시 상정하십시오. 그러면 컴파일러 팀이 작업을 완료할 다른 사람을 찾습니다.
- ..또는 수락되지 않을 수 있습니다(예: 여력 부족, 치명적이거나 우선순위가 높은 다른 버그, 적절한 기여자를 찾지 못함, 이슈의 실현 가능성 부족 등). 이 경우 컴파일러 팀은 상정에 대해 사유를 설명하는 답변을 남기고 상정 레이블을 제거합니다.
회의
컴파일러 팀은 팀 운영과 고품질 컴파일러 툴체인 제공에 필요한 정기 업무를 원활히 처리하기 위해 다양한 정기 회의를 개최합니다.
모든 T-compiler 회의는 Zulip 채팅 #t-compiler/meetings에서 텍스트로만 진행됩니다.
트리아지 회의
주간 트리아지 회의에서 팀은 백포트를 검토하고, 성능 트리아지 보고서를 리뷰하며, 상정된 이슈를 논의합니다. 최신 회의 시간은 팀 캘린더에서 확인할 수 있습니다. 누구나 참석할 수 있으며, 컴파일러 팀 구성원은 참석할 것을 권장합니다.
트리아지 회의 안건은 HackMD에 저장됩니다.
트리아지 회의 안건 생성
트리아지 회의 안건 생성에 관한 문서는 우선순위 지정을 참조하십시오.
스티어링/기획 회의
팀은 상위 수준의 주제를 논의하기 위해 정기적으로 회의도 엽니다. 스티어링/기획 회의는 다음과 같은 반복 일정으로 진행됩니다:
- 1주 차: 기획 회의
- 팀에 제안된 회의 중에서 다음 세 번의 회의 주제를 선정합니다.
- 2~4주 차: 스티어링 회의
- 계획된 주제를 논의합니다.
기획 회의에서는 회의를 진행하는 팀 리드가 논의할 만한 주제를 찾아봅니다. 일부 주제는 논의 전에 추가 조사가 필요하거나, 이미 시기가 지났거나, 다른 팀과 더 관련이 있을 수 있습니다. 그런 다음 회의 제안자와 관련 팀 구성원의 일정에 따라 이후 몇 주간의 스티어링 회의 시간에 회의가 배정됩니다. 모든 회의 시간을 채울 필요는 없습니다.
회의는 “meeting proposal” 템플릿을 사용하여 컴파일러 팀 저장소에 이슈를 열어 제안합니다. 스티어링 회의는 결정이나 분위기 확인이 필요하면서, 트리아지 회의에서 상정된 이슈를 논의할 때 보통 주어지는 시간보다 오래 걸릴 이슈를 더 넓은 팀과 논의하기에 좋은 기회입니다.
스티어링 회의 제안자는 주제를 설명하고 논의에 필요한 모든 맥락을 담은 짧은 비공식 문서를 준비해야 합니다. 다만 이 문서는 회의 당일까지만 준비하면 되며, 처음 회의를 제안할 때는 필요하지 않습니다.
모든 기여자는 회의 주제를 제안할 수 있습니다. 스티어링 회의 주제로 적합한 예시는 다음과 같습니다:
- 팀의 피드백이 필요한 제안
- 예: 컴파일러 하위 시스템 리팩터링 또는 새로운 트리 외부 의존성 추가
- 대규모 기여에 대한 심층 리뷰
- 예: 작성자가 자신의 작업을 설명하고 질문에 답변하는 경우
- 팀과 프로젝트의 나머지 부분 간의 조율
- 예: 제안된 프로젝트 목표 리뷰
- 팀 정책 결정
- 즉, 외부 의존성으로 인한 차단 요소를 처리하는 방법
예정된 계획 회의와 스티어링 회의는 컴파일러 팀 캘린더에서 확인할 수 있습니다.
스티어링 회의의 회의록은 HackMD에 저장됩니다.
구성원 자격
현재 구성원 자격에는 두 단계가 있습니다.
- 구성원: r+ 권한, 봇 권한, 인프라 접근 권한을 가진 정기적인 기여자
- 메인테이너: 컴파일러의 품질과 컴파일러 팀의 건전한 운영에 힘쓰기로 약속한 구성원
구성원이 되는 과정
컴파일러에 기여하려는 사람은 보통 두 가지 방법 중 하나로 시작합니다. “일회성” 이슈를 해결하거나, 기존 워킹 그룹 중 하나에 참여할 수 있습니다. 이들은 아직 컴파일러를 잘 알지 못하며 특별한 권한도 없습니다. 이들은 triagebot을 통해 이슈를 배정받고, (보통) 멘토나 멘토링 안내에 따라 작업합니다.
컴파일러 팀 구성원
한동안 꾸준히 기여한 사람은 컴파일러 팀 구성원으로 승격될 수 있습니다(아래 의사 결정 방식 절을 참고하십시오). 이 직함은 해당 인물이 꾸준히 기여하는 사람임을 나타냅니다.
이러한 승격이 적절한 시점의 정확한 조건을 정의하기는 어렵습니다. 구성원으로 승격되는 것은 단순히 여러 항목을 충족했는지 확인하는 문제가 아닙니다. 다만 일반적으로는 다음 세 가지를 보여 주었을 때 준비가 되었다고 봅니다.
- “지속성” – 어떤 방식으로든 꾸준히 기여하고 있어야 합니다. 예를 들어 몇 가지 프로젝트를 완료했다는 뜻일 수 있습니다.
- “독립성과 숙련도” – 적어도 워킹 그룹의 범위 안에서는 작업을 맡을 때 어느 정도 독립적으로 움직여야 합니다. 간단한 PR에 대해서는 다른 사람을 멘토링할 수 있을 정도여야 합니다.
- “친절함” – 컴파일러 팀 구성원은 Rust 조직의 일원이 되며, 행동 강령에 관해 더 높은 기준을 적용받습니다. 행동 강령의 문구뿐 아니라 그 정신도 지켜야 합니다.
구성원으로 승격되면 다음과 같은 여러 권한을 얻습니다.
-
구성원은
r+(풀 리퀘스트 승인) 권한을 가지며 리뷰를 할 수 있습니다(앞서 설명한 것처럼 이 권한을 적절히 사용해야 합니다). 또한 perf/rustc-timer 및 이와 비슷한 봇을 제어할 수 있습니다.bors와r+에 관한 문서는 https://bors.rust-lang.org에서 확인하십시오.팁: bors 권한에 관한 기본 규칙은 다음과 같습니다. 악성 코드가 있는지 먼저 확인하지 않았다면
try빌드를 하지 말고, 해당 코드를 효과적으로 리뷰할 수 있다는 충분한 확신이 없다면r+를 하지 마십시오. -
컴파일러 팀 구성원은 Rust 조직의 일원이므로 레이블을 수정하고 이슈를 배정받을 수 있습니다.
-
구성원은 GitHub의
rust-lang/compiler팀에 소속되므로, 누군가 팀 전체를 호출할 때 핑을 받습니다. -
구성원은 rust-lang.org 웹 페이지에 이름이 게시됩니다.
또한 몇 가지 의무도 따릅니다(일부는 선택 사항입니다).
- 구성원은 리뷰어 순번에 추가되기를 원하는지 질문을 받게 됩니다.
- 구성원은 팀을 돕기 위해 그 밖의 다양한 메인테이너 활동에 참여할 수 있습니다.
- 구성원에게는 행동 강령에 관해 일반인보다 더 높은 기준이 적용됩니다.
컴파일러 팀 구성원이 된다는 것의 의미
컴파일러 팀의 구성원이 되면 다음과 같은 여러 가지 일이 일어납니다.
-
내부 논의가 이루어지거나 아주 초기 단계의 아이디어가 공유되는 비공개 Zulip 스트림에 접근할 수 있게 됩니다. 새로운 팀 구성원들에게 인사를 건네 보십시오!
-
여러 GitHub 저장소를 구독하게 되며 쓰기 권한도 얻게 됩니다. 이제 어떤 저장소에 접근할 수 있는지 이 GitHub 페이지에서 확인하십시오. 그중 일부는 활동이 거의 없거나 더 이상 쓰이지 않으므로 전부 신경 쓰실 필요는 없습니다.
팁: GitHub는 쓰기 권한을 얻은 모든 저장소에 여러분을 자동으로 구독자로 추가합니다. 이 동작은 설정(여기)에서 끌 수 있습니다.
-
또한
all@rust-lang.org메일링 리스트에도 구독됩니다. 메일링 리스트 구독이 어떻게 이루어지는지는 이 파일을 참고하십시오. 이 메일링 리스트는 메일이 매우 드물게 오며(아마 1년에 몇 통 정도), 모든 기여자에게 소식을 전하는 수단입니다. 이 주소로 스팸을 보내는 일은 없을 것입니다.
메인테이너
컴파일러 팀 구성원으로 1년을 활동한 뒤에는 컴파일러 팀 메인테이너가 되기를 요청하거나 요청받을 수 있습니다. 이는 단순히 꾸준한 기여자일 뿐만 아니라 팀이나 컴파일러의 일부(또는 여러 부분)의 방향을 적극적으로 이끌어 가는 데 도움을 주고 있다는 뜻입니다.
- 컴파일러 팀 메인테이너는 적어도 하나 이상의 유지보수 활동에 참여할 것으로 기대됩니다.
- 컴파일러 팀 메인테이너는 rust-lang 웹사이트에서 “Maintainer” 역할로 표시됩니다.
승격 결정 방식
어떤 사람이 한동안 컴파일러에 기여해 왔다면, 기존 컴파일러 팀 구성원이 그 사람을 추천할 수도 있고, 본인이 컴파일러 팀 리드에게 자신의 기여 이력이 팀 구성원이 되기에 충분한지 물어볼 수도 있습니다.
컴파일러 팀 리드는 그 사람에게 구성원 초대를 보내는 데 우려 사항이 있는지 나머지 컴파일러 팀 구성원들에게 확인합니다. 반대 의견이 없으면 초대를 보냅니다. 일주일 이내에 답변하는 것을 목표로 하지만, 더 오래 걸릴 수도 있습니다(대개 결정 자체와는 무관한 이유 때문입니다).
당사자가 초대를 수락하면 컴파일러 팀 리드는 새로운 역할을 반영하도록 team 저장소를 업데이트합니다.
코드만이 전부가 아닙니다
컴파일러 팀의 구성원이 된다고 해서 반드시 PR을 작성해야 하는 것은 아니라는 점을 강조해 둘 만합니다. 컴파일러를 지원하기 위해 해야 하는 일은 매우 다양하며, 이러한 일도 구성원 자격의 근거가 되어야 합니다. 그러한 일에는 회의 준비, 회의 참여, 이슈 bisect 및 트리아지, 문서 작성, rustc-dev-guide 작업 등이 포함됩니다.
특히 컴파일러 팀 구성원이 되기 위한 가장 중요한 기준은 규칙적이고 꾸준한 참여입니다. 컴파일러 팀 메인테이너의 경우 가장 중요한 기준은 팀이나 컴파일러의 방향을 적극적으로 이끌어 가는 것입니다.
전임 구성원 상태
컴파일러 팀 구성원이나 메인테이너가 언제든 참여를 잠시 쉬고 싶다면, 스스로 전임 구성원 상태로 전환하는 것을 선택할 수 있습니다. 전임 구성원 상태가 되면 GitHub 별칭 등에서 제외되므로, 핑이나 메시지에 신경 쓰지 않아도 됩니다. 또한 r+ 권한도 갖지 않습니다. 다만 전임 구성원도 GitHub 조직 전체의 구성원으로는 계속 남습니다.
전임 구성원 상태인 사람은 언제든지 “활동” 상태로 복귀를 요청할 수 있습니다. 이 요청은 특별한 사정이 없는 한 보통 자동으로 승인됩니다.
전임 구성원 상태인 사람도 여전히 이전에 도달했던 수준의 팀 구성원이며 이를 공개적으로 밝힐 수 있지만, 활동했던 기간도 함께 밝혀야 합니다.
메인테이너 역할 맡기 및 내려놓기
컴파일러 팀 구성원이 메인테이너가 되어 컴파일러를 적극적으로 유지 관리하기로 약속한 후에도, 이러한 지속적인 책임에서 일시적으로 또는 무기한으로 물러나고 싶을 수 있습니다. 어느 경우든 메인테이너는 컴파일러 팀 리드에게 알리거나 team 저장소에 직접 PR을 열어, 메인테이너 마커 팀에서 자신을 제외하고 전임 구성원 목록에 자신을 추가할 수 있습니다.
이후 이전 메인테이너가 유지 관리 업무를 재개하고 싶다면, 컴파일러 팀 리드에게 복귀를 요청할 수 있습니다. 이 요청은 특별한 사정이 없는 한 보통 자동으로 승인됩니다.
컴파일러 팀 전임 구성원
마찬가지로 컴파일러 팀의 구성원 누구든 기여와 팀과의 교류를 장기간 쉬고 싶다면, 컴파일러 팀 리드에게 알리거나 team 저장소에 직접 PR을 열어 자신을 전임 구성원 상태로 옮길 수 있습니다.
전임 구성원이 이후 컴파일러 팀 구성원 자격을 재개하고 싶다면, 컴파일러 팀 리드에게 복귀를 요청할 수 있으며 이는 보통 승인됩니다.
6개월 동안 활동하지 않으면 자동으로 전임 구성원 상태로 전환
구성원이나 메인테이너가 컴파일러에서 6개월간 활동하지 않았다면, 전임 구성원 상태로 전환할 의향이 있는지 본인에게 묻습니다. 그렇다고 답하거나 응답이 없으면 전임 구성원 상태로 전환될 수 있습니다. 활동 상태를 유지하고 싶다면 그래도 괜찮지만, 계속 활동하지 않는다면 주기적으로 다시 질문을 받게 됩니다.
절차: 새 팀 구성원 추가하기
기존 구성원들이 팀 구성원 후보를 상정하면, 팀 리드가 새 팀 구성원을 추가하기 위해 따를 수 있는 표준 절차가 있습니다:
-
팀 리드가 모더레이션 팀에 연락하여 알고 있는 반대 사유가 있는지 묻습니다. 반대 사유가 없고 팀 구성원들이 동의하면, 새 기여자에게 합류를 요청합니다.
-
후보자에게 연락하여 팀 합류에 관심이 있는지 묻습니다:
Hey $name, you've been nominated for compiler team membership by a few people on the compiler
team! The [compiler team re-org RFC][rfc] has the full details as to what this means. This would
grant you permission to resources like bors and such.
This would not require you to take on additional work or responsibilities (though joining the
review queue is encouraged), and is just public recognition of the great work you've already been
doing around the compiler!
If you would like to accept, please let me know and I can update the teams repo accordingly.
[rfc]: https://rust-lang.github.io/rfcs/3599-compiler-team-reorganisation.html#team-members
알림 그룹
컴파일러 팀에는 사람들에게 핑을 보내 이슈에 주의를 기울이게 하는 데 사용하는 알림 그룹이 여러 개 있습니다. 알림 그룹은 원하는 사람이면 누구나 참여할 수 있도록 설정되어 있습니다.
Rust 프로젝트 GitHub 팀의 구성원만 이 알림 그룹을 사용할 수 있다는 점에 유의하십시오. 팀 구성원이 아닌 사람이 사용하면 자동화 봇에서 오류가 발생합니다.
알림 그룹 만들기
알림 그룹을 만들고 싶다면 다음 단계를 따르십시오. 먼저 컴파일러 팀의 승인을 받아야 합니다:
- 진행 상황을 모아 둘 추적 이슈를 rust-lang/compiler-team 저장소에 등록하십시오.
- 팀 저장소 rust-lang/team에 알림 그룹을 추가하는 PR을 생성하십시오(예시 PR)
- rust-lang/rust 저장소가 이 그룹에 대한 triagebot 명령을 받아들이도록 설정하십시오. (예시 PR)
- rustc-dev-guide의 알림 그룹 섹션에 여러분의 그룹을 언급하도록 수정하는 PR을 생성하십시오.
- 팀 저장소 rust-lang/team에 자기 자신을 추가하는 방법을 보여 주는 샘플 PR을 생성하십시오. 이 PR은 사람들에게 참여 방법을 보여 주기 위해 블로그 게시물에서 참조됩니다. (예시 PR)
- Inside Rust에 올릴 공지 블로그 게시물을 작성하고 blog.rust-lang.org에 PR을 여십시오(예시 PR)
운영
“운영“은 컴파일러 팀의 일부로, 조직 및 유지 관리 업무를 맡으며 전반적으로 일이 원활하게 진행되도록 돕습니다. T-compiler 운영 팀은 Zulip의 #t-compiler/ops에서 활동합니다.
다음은 정기적으로 수행하는 작업 목록입니다. 가능하면 매주 이 목록을 점검하십시오. 진행을 가로막는 문제나 의문점이 있다면 적절한 맥락을 파악한 뒤 주저하지 말고 관련된 사람들에게 핑을 보내십시오. 기여자는 프로젝트의 가장 소중한 자원이며(그러므로 우리는 그들의 시간을 배려하고자 합니다) 언제나 기꺼이 도와줍니다.
특정 주제, 이슈 또는 풀 리퀘스트에 대한 논의는 Zulip의 #t-compiler에 새 토픽을 열어 시작할 수 있으며, 합의가 필요하고 팀이 더 집중해서 다뤄야 하는 경우에는 I-compiler-nominated 레이블을 붙여 회의에서 논의할 수 있습니다(회의 절 참조).
이슈 관리
- 우선순위를 지정해야 할 이슈: 우선순위 지정을 참조하십시오.
- 담당자가 없는 P-high 이슈: 이 범주의 이슈에는 가능하면 담당자가 있어야 합니다(PR이 없는 이슈만 걸러 내십시오). 드물게는 담당자가 없어도 괜찮습니다.
- FCP 상태인 MCP: 재청된 지 10일이 넘은 MCP를 종료하되, 해결되지 않은 우려 사항이 없는지 확인하십시오
- 열린 MCP 확인: MCP는 제안을 컴파일러 팀에 알리기 위한 절차입니다. MCP가 재청되거나 재청되지 않아 종료되는 두 결과 중 하나를 향해 진행되고 있는지 확인하십시오. MCP가 재청되지 않을 것이 분명하거나 방치된 경우, 약 두세 달이 지난 뒤에는 상태를 문의하고 종료 여부를 검토해도 됩니다. 그렇지 않은 경우에는 막힌 MCP가 다시 진행되도록 도우십시오.
- MCVE가 필요한 이슈
- FCP가 진행 중인 이슈와 PR: 팀이 체크박스를 표시해야 하는지 확인하십시오. 이 이슈들은 주간 트리아지 회의 안건에 포함됩니다.
T-compiler의 우선순위 지정
우선순위 지정이 어떻게 이루어지는지는 여기를 참조하십시오.
회귀를 살펴볼 때 유용한 필터는 다음과 같습니다.
PR 관리
- 모든 PR에는 팀이 지정되어 있어야 합니다
릴리스 일주일 전에 할 일:
- 우선순위가 없는 회귀가 없어야 합니다: 회귀가 수정되었는지 확인하고, 수정되지 않았다면 팀의 관심을 끌도록 노력하십시오.
- 담당자가 없는 beta 회귀나 stable 회귀가 없어야 합니다. PR이 없는 회귀는 걸러 내십시오.
- 작업이 진행 중인 beta 회귀나 stable 회귀가 없어야 합니다. 이상적으로는 모두 병합되어 있어야 합니다.
- 호환성을 깨는 변경(즉, 수용할 수 있다고 합의된 회귀)마다
relnotes-tracking-issue태그가 붙은 이슈가 있는지 확인하십시오. 릴리스 노트 목록을 참고하십시오. 그러면 T-release가 이를 가져와 릴리스 노트에 추가합니다.
릴리스 이후
- 어떤 회귀를 “수용됨“으로 닫을 수 있는지 신중하게 확인하십시오. 회귀를 일으킨 PR이 호환성을 깨는 변경으로 수용되었음을 밝히는 댓글을 추가하십시오. 예: “PR #123456이 릴리스 노트에 언급될 예정이므로 닫습니다”. 이 관행에 대한 논의와 의견은 Zulip에서 나눌 수 있습니다.
회의
T-compiler에는 트리아지 회의와 설계 회의, 두 종류의 회의가 있습니다. 트리아지 회의는 매주 목요일에 열리며(이 저장소에서 컴파일러 팀 캘린더를 구독할 수 있습니다), 회의 안건 대부분을 생성하는 도구가 있습니다(자세한 내용은 트리아지 회의를 참고하십시오). 설계 회의 제안은 T-compiler 저장소에서 진행됩니다. 일정은 정기적으로 열리는 운영(steering) 회의에서 잡힙니다. 다음 설계 회의 일정도 이 회의에서 정합니다. 설계 회의에도 안건이 필요합니다. 또한 주제를 요약하고, 문서를 취합하고, 관련자를 초대하는 등의 작업도 어느 정도 필요합니다.
주간 트리아지 회의
먼저 관련 이슈에 T-compiler 레이블이 붙어 있는지 확인하십시오:
I-prioritize레이블이 붙은 이슈- stable 릴리스 채널 백포트에 상정된 풀 리퀘스트
- beta 릴리스 채널 백포트에 상정된 풀 리퀘스트
I-compiler-nominated레이블이 붙은 이슈(즉, T-compiler의 논의가 필요한 이슈)- 팀의 피드백을 기다리는 풀 리퀘스트
- 우선순위가
P-high로 분류된 이슈 - 우선순위가
P-critical로 분류된 이슈
..그리고 우선순위 지정이 완료되었는지 확인하십시오. I-prioritize 레이블이 붙은 회귀는 우선순위 평가를 기다리고 있다는 뜻입니다. 이 레이블이 이슈에 추가되면 triagebot이 #t-compiler/prioritization/alerts Zulip 채널로 알림을 보냅니다.
이상적으로는 I-prioritize 레이블이 붙은 모든 T-compiler 이슈에 우선순위가 지정되어 있어야 합니다. 그렇지 않다면 이 목표에 도달하도록 노력하십시오. 때로는 여러 요인 때문에 이슈에 우선순위 레이블을 지정하지 못하기도 합니다. 보고 내용이나 맥락이 불분명한 경우도 있고, 재현할 수 없어 MCVE가 있으면 도움이 되는 경우도 있습니다. 이슈 보고자나 다른 기여자에게 주저하지 말고 설명을 요청하십시오.
stable, beta, nightly를 리뷰하고 가능하면 담당자가 배정되도록 하십시오.
안건을 생성하기 전 마지막 단계는 주요 변경 제안(MCP)을 수락하는 것이며, 이 작업은 보통 자동화되어 있습니다. 최종 의견 수렴 기간(FCP) 단계(final-comment-period 레이블이 붙어 있는 것으로 식별됨)에 10일 넘게 머물러 있는 MCP는 수락할 수 있습니다. MCP에 해결되지 않은 우려 사항이 없다면(has-concerns 레이블을 확인하십시오) final-comment-period 레이블을 제거하고 major-change-accepted 레이블을 추가한 뒤 이슈를 닫으면 됩니다.
마지막으로 회의 안건을 생성할 수 있습니다. triagebot을 클론하여 빌드한 뒤 다음을 실행하십시오:
$ cargo run --bin prioritization-agenda
내용을 “Rust Lang Compiler Team”(Rust 언어 컴파일러 팀) 공간의 새 HackMD 문서에 복사하십시오. 이 도구는 최신 주간 컴파일러 트리아지 로그도 내려받습니다. 이 작업이 제대로 되지 않았다면 가장 최근의 성능 트리아지 로그를 직접 복사하십시오(약간 정리하여 Zulip에서 잘 표시되지 않을 내용은 제거하십시오).
안건에 다음 세부 사항을 직접 추가하십시오:
- stable/beta 상정 건의 요약을 추가하십시오(예: 누가 백포트를 상정했고 그 이유는 무엇인지).
- 팀의 응답을 기다리는 PR의 요약을 추가하십시오(즉, 왜 대기 중인지).
P-critical/P-high버그에 대한 초기 소견을 추가하십시오.- 상정된 이슈의 요약을 추가하십시오(예: 담당자가 누구인지, 왜 상정되었는지 등).
- 리뷰를 기다리는 가장 오래된 PR 목록을 채우십시오.
- 핑을 보내는 것이 적절한지 판단하십시오(예: 풀 리퀘스트가 실험용이라면 리뷰가 필요하지 않을 수도 있습니다. 마지막 리뷰 활동 이후 얼마나 지났는지, 최근 댓글 내용은 어떠한지도 살펴보십시오).
회의 약 두 시간 전에 다가오는 회의의 Zulip 스레드에 완성된 안건을 공지하고 공유하십시오(스레드가 아직 없다면 새로 만드십시오):
Hello @*T-compiler/meeting*, triage meeting in about 2h.
Pre-triage done in #**t-compiler/prioritization/alerts**.
Meeting agenda [on HackMD](https://hackmd.io/aaabbbccc123456)
GitHub에서 이슈 상태가 바뀌었을 수 있으므로, 생성기를 다시 실행하여 새로운 세부 사항을 안건에 옮겨 적는 것을 항상 권장합니다.
회의가 끝난 후에는 몇 가지 마무리 작업이 있습니다:
- HackMD에서 쓰기 권한을
Owners에게 부여하여 안건을 잠그십시오. - MCP에서
to-announce레이블을 제거하십시오. 단, 이 레이블이 바로 회의 도중에 추가된 경우(따라서 다음 회의에서 다뤄질 경우)는 예외입니다. rust-lang/rust,compiler-team, forge에서to-announceFCP를 제거하십시오. 회의 도중의 변경 사항에 관해서는 앞과 동일한 단서가 적용됩니다.- 회의에서 수락된
beta nominated및stable nominated백포트를 수락하거나 거절하십시오. 자세한 내용은t-release백포트 문서를 확인하십시오.- 백포트를 수락하려면
{beta,stable}-accepted레이블을 추가하고{beta,stable}-nominated레이블은 그대로 두십시오. 다른 자동화 절차가 이 풀 리퀘스트들을 처리하므로 두 레이블을 모두 남겨 두는 것이 중요합니다. GitHub에 Zulip 논의 링크를 담은 댓글을 추가하십시오. - 백포트를 거절하려면
{beta,stable}-nominated레이블을 제거하기만 하면 됩니다. GitHub에 백포트가 거절된 이유를 설명하는 댓글을 추가하고 Zulip 논의 링크를 첨부하십시오.
- 백포트를 수락하려면
- 논의된 이슈에서
I-compiler-nominated레이블을 제거하십시오. (시간 제약 때문에) 상정된 이슈가 모두 논의되지 못하고 다음 회의로 넘어가는 경우도 있습니다.
P-high 이슈 트리아지 회의
약 6개월마다 P-high 레이블이 붙은 열린 이슈의 큐를 리뷰합니다(지난 회의 목록은 여기에 있습니다). 이 회의의 목표는 이러한 P-high 이슈의 우선순위를 재평가하는 것입니다. 이러한 이슈는 여러 이유로 우선순위가 낮아질 수 있습니다(너무 많은 P-high 이슈가 너무 오래 남아 있으면 배경 소음이 되어 실제로 우선순위가 높은 이슈를 가리게 됩니다).
안건을 생성하는 데에는 이 도구를 사용합니다.
트리아지 회의록은 이후 HackMD에 업로드됩니다.
다른 팀 현황
이 필터들은 다른 팀에서 어떤 일이 진행되고 있는지 확인하기 위한 것입니다
- 팀이 제안을 논의하거나 검토하기를 기다리고 있는 열린 RFC 목록(모든 팀)입니다. 이 RFC들이 진행되도록 도울 수 있는 일이 있습니까?
유용한 팁
GitHub 이슈 대시보드
GitHub 이슈 대시보드를 활용하여 사용자 지정 필터를 만들 수 있습니다. 이 필터를 사용하면 여러 저장소의 이슈와 PR을 한데 모아 볼 수 있으며, 고급 필터도 적용할 수 있습니다. https://github.blog/changelog/2025-04-02-github-issues-dashboard-updates/를 참고하십시오.
사용자 지정 필터 예시
주로 rust-lang/rust를 대상으로 합니다
| 필터 | 설명 |
|---|---|
repo:rust-lang/rust label:P-critical is:open | 열려 있는 P-critical 치명적인 이슈 |
repo:rust-lang/rust label:T-compiler label:P-high is:open | 열린 P-high T-compiler 이슈 |
repo:rust-lang/rust label:needs-triage -label:relnotes | 트리아지되지 않은 이슈 |
repo:rust-lang/rust label:regression-untriaged | 트리아지되지 않은 회귀 |
repo:rust-lang/rust label:proposed-final-comment-period | FCP가 진행 중인 이슈/PR |
repo:rust-lang/rust label:proposed-final-comment-period label:T-compiler | T-compiler FCP가 진행 중인 이슈/PR |
repo:rust-lang/rust label:I-prioritize | 우선순위가 지정되지 않은 이슈 |
repo:rust-lang/rust label:needs-triage label:relnotes-tracking-issue | 트리아지되지 않은/편집되지 않은 relnotes 이슈 |
repo:rust-lang/rfcs label:T-compiler is:pr is:open | T-compiler 관련 RFC |
repo:rust-lang/rfcs label:T-compiler label:proposed-final-comment-period | FCP가 진행 중인 T-compiler 관련 RFC |
제안, 승인 및 안정화
컴파일러에 기여할 때는 실험이나 리팩터링을 진행하기 위한 허가를 받거나 기능을 안정화하는 경우처럼, 피드백과 승인을 받아야 하는 일이 매우 흔합니다. 상당한 규모의 변경 사항이 있다면, 기여자는 리뷰를 위해 제출하기 전에 Zulip에서 팀에 연락하여 해당 변경 사항을 논의할 것을 권장합니다. 이 문서는 컴파일러 팀이 승인 결정을 내리는 데 사용하는 다양한 절차와 각 절차를 언제 사용해야 하는지를 요약하는 것을 목표로 합니다.
승인
팀이 제안을 승인하는 데 사용할 수 있는 메커니즘은 세 가지입니다(모든 승인 메커니즘이 모든 제안 방식에 적합한 것은 아닙니다. 아래를 참조하십시오).
- r+
- 제안이 병합 승인을 받으면 r+되었다고 합니다.
- r+는 PR을 승인하는 데에만 사용할 수 있습니다.
- 재청(주요 변경 제안에만 해당, 아래 참조)
- 팀 구성원이 제안을 공식적으로 지지하면 해당 제안은 재청된 것입니다. 재청은 다른 팀 구성원이 우려 사항을 제기할 수 있도록 10일간의 대기 기간을 두는 것을 조건으로 제안을 잠정적으로 수락합니다.
- MCP에서
final-comment-period레이블을 제거하여 “재청을 취소“할 수 있습니다.
- FCP
- 최종 의견 수렴 기간은 T-compiler 구성원이 시작합니다. 이는 팀으로부터 구체적인 합의를 얻기 위한 도구입니다. 제안을 승인하려면 컴파일러 FCP 리뷰어(
compiler-fcp하위 팀)의 승인을 받은 후 10일간의 대기 기간을 거쳐야 합니다. - FCP는 모든 형태의 제안을 승인하는 데 사용할 수 있습니다.
- 최종 의견 수렴 기간은 T-compiler 구성원이 시작합니다. 이는 팀으로부터 구체적인 합의를 얻기 위한 도구입니다. 제안을 승인하려면 컴파일러 FCP 리뷰어(
제안
컴파일러 팀에 변경을 제안하는 방법은 세 가지입니다. 어떤 방법이 적절한지는 아래에 설명하는 제안의 성격에 따라 달라집니다.
- 의견 요청(RFC)
- RFC는
rust-lang/rfcs저장소에 대한 풀 리퀘스트이며, 중대한 변경 사항에 한해 사용하는 무거운 제안 메커니즘입니다. - RFC 제안은 FCP로만 승인할 수 있습니다.
- RFC는
- 주요 변경 제안(MCP)(RFC 2904에서 도입)
- MCP는
rust-lang/compiler-team저장소의 이슈이며, 대부분의 제안에 적합한 중간 무게의 제안 메커니즘입니다. MCP는 최종 사용자에게 직접 영향을 주지 않는 서면 제안에 권장됩니다. - MCP 제안은 FCP 또는 재청(팀 구성원 1인의 지지)으로 승인할 수 있습니다.
- MCP는
- 풀 리퀘스트(PR)
- PR은
rust-lang/rust저장소에 대한 풀 리퀘스트이며, 대부분의 제안에 적합한 가벼운 제안 메커니즘입니다. 제안에 작은 패치 세트가 수반되는 경우(예: 컴파일러 플래그의 안정화 또는 새 타깃 추가)에는 PR이 선호됩니다. - PR 제안은 FCP 또는 r+로 승인할 수 있습니다.
- PR은
MCP는 어떻게 제출합니까?
- 주요 변경 템플릿을 사용하여 rust-lang/compiler-team 저장소에 추적 이슈를 여십시오.
- 봇이
#t-compiler/major changes스트림에 Zulip 토픽을 자동으로 생성합니다. - 우려 사항이 제기되면 해당 우려 사항을 해소하도록 제안을 수정하는 것이 좋습니다.
- 또는 설계 회의 제안을 제출하여 더 길고 집중적인 논의를 진행할 수도 있습니다.
- 봇이
- 주요 변경 제안이 수락되려면 다음 세 가지가 필요합니다.
- 재청: 아이디어에 찬성하되 제안을 처음 제기한 사람은 아닌 컴파일러 팀 구성원입니다.
- 최종 의견 수렴 기간(사람들이 의견을 남길 시간을 주기 위해 10일간 기다리는 기간)입니다.
- 변경 사항을 쉽게 되돌릴 수 있거나 추가 반대 의견이 나올 가능성이 낮다고 판단되는 경우 FCP를 생략할 수 있습니다. 예를 들어, 사전에 많은 논의가 있었던 경우 이런 일이 자주 있습니다.
- 해결되지 않은 우려 사항이 있으면 최종 의견 수렴 기간이 완료되지 않습니다.
- 해결되지 않은 우려 사항이 모두 해결되면 최종 의견 수렴 기간 카운트다운이 다시 시작됩니다.
- FCP가 완료된 후 해결되지 않은 우려 사항이 없으면 기여를 시작할 수 있습니다.
- 이전에 수락된 MCP가 이후에 필요한 승인을 대신하지는 않습니다.
compiler-team 저장소의 MCP에는 어떤 종류의 댓글을 남겨야 합니까?
기술적인 대화는 Zulip 스트림에서 진행하십시오. compiler-team 저장소의 이슈는 트래픽이 적게 유지되고 절차적인 목적으로만 사용되도록 의도되었습니다.
제안을 “재청“하려는 팀 구성원은 관련 코드를 잘 알고 있는 것이 좋습니다. 간과해서는 안 될 우려 사항은 누구나 지적할 수 있습니다.
우려 사항을 제기한 팀 구성원은 이후 제안이 변경되면 후속 조치를 하고 해당 우려 사항을 해소해야 합니다.
반대 의견은 어떻게 제기합니까?
이러한 절차적인 댓글은 이슈에 남길 수 있습니다(Zulip에도 메시지를 남기는 것이 좋습니다). 이전 절을 참고하십시오. 우려 사항은 진행을 막는다는 점을 기억하는 것이 중요합니다. 따라서 우려 사항은 가능한 한 빨리, 그리고 가장 명확한 방식으로 등록해야 합니다(등록되지 않은 “가벼운 우려 사항“은 진행을 막는 것으로 간주하지 않습니다).
기계가 우려 사항을 파싱하여 확인할 수 있도록 다음 문법을 사용하여 우려 사항을 공식적으로 등록하십시오.
@rustbot concern reason-for-concern
<long description of the concern>
그리고 우려 사항이 해결되면 다음 문법을 사용하여 해제하십시오.
@rustbot resolve reason-for-concern
MCP는 어떻게 재청합니까?
일반적으로 MCP를 승인(재청)하기 전에 약간의 시간(예를 들어 며칠)을 기다려 서로 다른 시간대와 근무 일정을 가진 사람들이 살펴볼 수 있도록 하는 것이 좋습니다. 단, 제안이 매우 간단하고 명확하여 고민할 필요 없이 승인할 수 있는 경우는 예외입니다.
MCP는 다음을 사용하여 재청할 수 있습니다.
@rustbot second
우려 사항이 해결되지 않았는지는 누가 결정합니까?
보통은 해당 분야의 전문가들이 합의에 이르지만, 가부 동수를 해소하기 위한 투표나 판단이 필요한 경우에는 컴파일러 팀 리드가 최종 결정을 내립니다.
MCP는 언제 닫아야 합니까?
MCP는 다음과 같은 경우에 닫힐 수 있습니다.
- 작성자가 더 이상 제안을 추진할 의사가 없는 경우 작성자가 닫을 수 있습니다. 이런 경우 MCP 작성자가 직접 제안을 닫아 주시면 정리 상태를 유지하는 데 도움이 되므로 감사하겠습니다
- 팀의 핵심 구성원들이 강하게 반대하고 그 반대가 해소될 가능성이 낮아 보이는 경우 팀 리드나 전문가가 닫을 수 있습니다.
- 약 3개월 동안 활동이 없는 경우 트리아지를 담당하는 사람이 닫을 수 있습니다. 이 경우 이슈를 “되살리고” 싶다면 누구든 부담 없이 다시 열어도 됩니다.
승인이 필요한 기여를 승인 없이 제출하면 어떻게 됩니까?
해당 기여에 필요한 승인이 MCP나 RFC를 요구한다면, 먼저 MCP나 RFC를 작성해 달라고 요청하면서 기여를 닫거나 차단됨으로 표시해야 합니다. 해당 기여에 PR 승인만으로 충분하다면(아래 참조) 승인 절차를 시작할 수 있습니다.
승인을 받기 전에 실험적으로 코드 작업을 해도 됩니까?
물론입니다! 자유롭게 PR 작업을 하거나 코드를 작성해도 됩니다. 다만 그러한 PR은 실험적인 것으로 표시해야 하며, 병합되어서는 안 되고, (원하는 사람이 있는 경우가 아니라면) 누구에게도 리뷰를 기대해서는 안 됩니다. 또한 팀이 제안을 어떻게 받아들이는지 분위기를 파악해 보십시오. 제안을 보여 주는 코드는 환영하지만, 동시에 승인되지 않을 수도 있는 작업에 시간을 들이지 않기를 바랍니다. 따라서 개념 증명이나 더 정교한 패치가 승인에 도움이 될 수는 있어도 승인을 보장하지는 않는다는 점을 유념하십시오.
좋은 제안의 조건은 무엇입니까?
좋은 제안은 다음 내용을 다룹니다.
- 동기: 이 제안이 왜 필요합니까? 어떤 문제를 해결합니까? 그 문제가 왜 중요합니까?
- 설계: 무엇을 제안합니까?
- 구현 참고 사항: 보통은 구현에 대해 이야기할 필요가 없지만, 특별히 짚어야 할 사항이 있다면(예: 구현이 매우 침습적이었다면) 여기에 적을 수 있습니다.
- 선례, 링크 및 관련 자료:
clang이나lld같은 다른 컴파일러/링커/도구에 비슷한 제안이 있었습니까? - 대안, 우려 사항 및 주요 결정: 검토한 대안이 있었습니까? 있었다면 왜 이 설계를 선택했습니까?
어떤 제안/승인이 필요합니까?
이 절에서는 각 상황에 어떤 제안과 승인이 필요한지 빠짐없이 자세히 설명합니다.
내부
- 알림 그룹 만들기
- 제안 방법: PR
- 승인 방법: r+
- 팀 구성원이 새 그룹이 타당하다고 판단하면 그룹을 추가하는 변경 사항을 병합할 수 있습니다.
- 중요한 내부 리팩터링/변경
- 제안 방법: MCP
- 승인 방법: 재청
- 제안하는 리팩터링을 MCP에 자세히 설명하십시오. 더 집중적인 논의가 필요하다면 스티어링 회의를 잡을 수도 있습니다. 논의가 마무리되면 팀 구성원이 제안을 재청할 수 있습니다.
- 소규모 팀 정책 정의/변경
- 제안 방법: MCP
- 승인 방법: 재청
- MCP로 충분한 소규모 정책 변경의 예로는 대소문자를 구분하지 않는 파일 시스템에 대한 지원 수준이나, 팀이 추적 이슈를 논의의 장으로 삼을지 여부 등이 있습니다.
- 대규모 팀 정책 정의/변경
- 제안 방법: RFC
- 승인 방법: FCP
- FCP가 필요한 대규모 정책 변경에는 팀 구조와 구성원 자격 기준 등에 대한 제안이 포함됩니다.
컴파일러 플래그
- 내부 전용 컴파일러 옵션 추가(예:
-Ztreat-bug-as-err)- 제안 방법: PR
- 승인 방법: r+
- 팀 구성원이 새 옵션이 타당하다고 판단하면 해당 옵션을 추가하는 변경을 병합할 수 있습니다.
- 추후 안정화를 목표로 하는 단순한 컴파일러 옵션 추가
- 제안 방법: MCP
- 승인 방법: 재청
- LLVM의 논란의 여지가 없는 옵션을 노출하는 것과 같은 단순한 옵션은 재청된 MCP와 리뷰어의 r+ 승인으로 구현하고 병합할 수 있습니다. 추후 안정화할 때는 정식 FCP가 필요합니다.
- 추후 안정화를 목표로 하는 복잡한 컴파일러 옵션 추가
- 제안 방법: RFC
- 승인 방법: FCP
- 옵션이 복잡하고 설계상의 고려가 필요하다면
t-compilerRFC를 작성하여 제출하십시오.
- 내부 전용 플래그 제거
- 제안 방법: MCP
- 승인 방법: 재청
- 불안정 구현을 제거하는 근거를 설명하십시오. 논의가 마무리되면 팀 구성원이 제안을 재청할 수 있습니다.
- 최종적으로 안정화할 예정이었던 플래그 제거
- 제안 방법: MCP
- 승인 방법: 재청
- 불안정 구현을 제거하는 근거를 설명하십시오. 논의가 마무리되면 팀 구성원이 제안을 재청할 수 있습니다
- 컴파일러 옵션 안정화
- 제안 방법: PR
- 승인 방법: FCP
- PR을 열고 안정화 가이드를 따르십시오. 배정된 리뷰어가 안정화 가이드를 따랐는지 확인하고 코드를 리뷰한 뒤 FCP를 시작합니다
- 컴파일러 옵션 안정화 되돌리기
- 제안 방법: PR
- 승인 방법: FCP
- PR을 열고 안정화 가이드를 따르십시오. 배정된 리뷰어가 안정화 가이드를 따랐는지 확인하고 코드를 리뷰한 뒤 FCP를 시작합니다
- stable 플래그의 동작 확장
- 제안 방법: MCP
- 승인 방법: 재청
- 플래그의 동작을 확장하는 근거를 설명하십시오. 논의가 마무리되면 팀 구성원이 제안을 재청할 수 있습니다
속성
- 내부 전용 속성 추가(예:
rustc_attrs)- 제안 방법: PR
- 승인 방법: r+
- 팀 구성원이 새 속성이 타당하다고 판단하면 해당 속성을 추가하는 변경을 병합할 수 있습니다
- 향후 안정화를 목표로 하는 속성 추가
- 언어 팀의 절차를 따르고 구현 PR은 컴파일러 팀 구성원의 리뷰를 받으십시오
- 내부 전용 속성 제거
- 제안 방법: MCP
- 승인 방법: 재청
- 불안정 구현을 제거하는 근거를 설명하십시오. 논의가 마무리되면 팀 구성원이 제안을 재청할 수 있습니다
- 최종적으로 안정화하는 것을 목표로 했던 속성 제거
- 언어 팀의 절차를 따르고, 제거 PR은 컴파일러 팀 구성원의 리뷰를 받으십시오
- 속성 안정화
- 언어 팀의 절차를 따르고, 안정화 PR은 컴파일러 팀 구성원의 리뷰를 받으십시오
- 속성 안정화 되돌리기
- 언어 팀의 절차를 따르고, 되돌리기 PR은 컴파일러 팀 구성원의 리뷰를 받으십시오
기능
- 아직 제안되지 않은 언어 기능의 실험적 구현 추가
- 제안 방법: MCP
- 승인 방법: 재청
- 언어 팀의 승인(해당 기능이 실험해 볼 가치가 있다는 판단)을 받은 뒤 RFC를 제출하십시오. 논의가 끝난 후 컴파일러 팀 구성원이 구현이 실현 가능하고 컴파일러 메인테이너에게 과도한 부담을 주지 않는다는 데 동의하면, 해당 구성원이 MCP를 재청할 수 있으며 구현을 진행할 수 있습니다.
- 구현 담당자가 컴파일러 팀 구성원인 경우에는 이 절차가 필요하지 않습니다
- 언어 기능 안정화
- 언어 팀의 절차를 따르고, 안정화 PR은 컴파일러 팀 구성원의 리뷰를 받으십시오
- 언어 기능 안정화 되돌리기
- 언어 팀의 절차를 따르고, 되돌리기 PR은 컴파일러 팀 구성원의 리뷰를 받으십시오
타깃
타깃 티어별 세부 요구 사항은 타깃 티어 정책을 참고하십시오.
타깃 승격
타깃 승격 개요:
| 현재 타깃 티어 | 목표 타깃 티어 | 제안 | 필요한 승인 |
|---|---|---|---|
| 해당 없음(새 타깃 제안) | 3 | PR | r+(컴파일러 리드) |
| 3 | 2 | MCP | FCP |
| 2 | 1 | RFC | FCP |
- 새 타깃 제안하기
- 제안 방법: PR
- 승인 방법: r+ (컴파일러 리드)
- PR에
r? compiler_leads를 남기면 컴파일러 리드 중 한 명을 무작위로 리뷰어로 지정할 수 있습니다. - 새 타깃을 추가하는 PR을 (관련 문서 업데이트와 함께) 여십시오. PR 설명에는 이 타깃이 타깃 티어 정책을 준수한다는 점을 문서화하십시오.
- 새 타깃은 반드시 Tier 3에서 시작해야 합니다.
- 새 타깃은 컴파일러 팀 공동 리드에게 배정해야 합니다. 공동 리드는 라이선스 관련 우려 사항이 있는지 확인합니다. 또한 해당 타깃이 프로젝트 인프라에 요구하는 사항이 있다면 이를 검토하고 관련 팀과 함께 확인합니다.
- 타깃을 Tier 3에서 Tier 2로 승격하기
- 제안 방법: MCP
- 승인 방법: FCP
- 해당 타깃에 대한 MCP를 여십시오. MCP 설명에는 이 타깃이 Tier 2 타깃 정책을 준수한다는 점을 문서화하십시오.
- 타깃을 Tier 2에서 Tier 1로 승격하기
- 제안 방법: RFC
- 승인 방법: FCP
- 해당 타깃에 대한 RFC를 여십시오. RFC 본문에는 이 타깃이 Tier 1 타깃 정책을 준수한다는 점을 문서화하십시오.
타깃 강등 또는 제거
타깃 강등 및 제거 요약:
| 현재 타깃 티어 | 목표 타깃 티어 | 제안 | 필요한 승인 |
|---|---|---|---|
| 1 | 2 (또는 제거) | RFC | FCP |
| 2 | 3 (또는 제거) | MCP | FCP |
| 3 | 해당 없음(티어 3 타깃 제거) | PR | r+(컴파일러 리드) |
- 타깃을 티어 1에서 티어 2로 강등하거나 티어 1 타깃을 제거하는 경우
- 제안 방법: RFC
- 승인 방법: FCP
- 대상 타깃과 근거를 담은 RFC를 여십시오.
- 예시: RFC: i686-pc-windows-gnu를 티어 2로 강등 #3771
- 타깃을 티어 2에서 티어 3으로 강등하거나 티어 2 타깃을 제거하는 경우
- 제안 방법: MCP
- 승인 방법: FCP
- 대상 타깃과 근거를 담은 MCP를 여십시오.
- 티어 3 타깃 제거
- 제안 방법: PR
- 승인 방법: r+(컴파일러 리드)
- 제거할 티어 3 타깃과 제거 근거를 담은 PR을 여십시오.
기타 타깃 변경
- 티어 3 타깃의 이름을 바꾸거나 호환성을 깨는 변경을 하는 경우
- 제안 방법: PR
- 승인 방법: r+
- 제안하는 새 이름을 담은 PR을 열어 변경 동기를 설명하고 리뷰어에게서 r+를 받으십시오.
- 티어 2 타깃의 이름을 바꾸거나 호환성을 깨는 변경을 하는 경우
- 제안 방법: MCP
- 승인 방법: FCP
- 변경 동기를 설명하는 MCP를 열고, 승인을 위해 FCP를 시작하십시오.
- 승인되면 변경 사항을 알리는 블로그 게시물을 함께 발행해야 하며, 변경이 적용되기 전에 최소 한 번의 릴리스에 해당하는 사전 공지 기간을 두어야 합니다.
- 티어 2 타깃의 호스트 도구 상태 변경(호스트 도구 없음 => 호스트 도구 있음, 호스트 도구 있음 => 호스트 도구 없음)
- 제안 방법: MCP
- 승인 방법: FCP
- 변경 동기를 설명하는 MCP를 열고, 승인을 위해 FCP를 시작하십시오.
- 티어 1 타깃의 이름을 바꾸거나 호환성을 깨는 변경을 하는 경우
- 제안 방법: RFC
- 승인 방법: FCP
- 변경 동기를 설명하는 RFC를 열고, 승인을 위해 FCP를 시작하십시오.
- 승인되면 변경을 알리는 블로그 게시물을 함께 게시해야 하며, 변경이 적용되기 전에 최소 한 릴리스의 예고 기간을 두어야 합니다.
- 티어 1 타깃의 호스트 도구 지원 여부 변경(호스트 도구 없음 => 호스트 도구 있음, 호스트 도구 있음 => 호스트 도구 없음)
- 제안 방법: RFC
- 승인 방법: FCP
- 변경 동기를 설명하는 RFC를 열고, 승인을 위해 FCP를 시작하십시오.
- 승인되면 변경을 알리는 블로그 게시물을 함께 게시해야 하며, 변경이 적용되기 전에 최소 한 릴리스의 예고 기간을 두어야 합니다.
- 호스트 도구 지원을 중단하는 경우, 부득이하게 한 릴리스의 유예 기간을 생략해야 할 때도 있습니다.
- 타깃 기준선 변경(예: 최소 Darwin 또는 Windows 버전 상향)
- 제안 방법: MCP
- 승인 방법: FCP
- 타깃의 기준선을 변경해야 하는 이유를 설명하는 MCP를 작성하십시오. 논의가 마무리되면 기준선 변경을 승인하기 위한 FCP를 시작할 수 있습니다.
- 타깃 메인테이너 추가/제거
- 제안 방법: PR
- 승인 방법: r+
- 타깃 문서 변경 사항을 담은 PR을 열고 리뷰어로부터 r+를 받으십시오.
- 타깃 기능 추가
- 제안 방법: PR
- 승인 방법: r+
- 타깃 기능을 추가하는 PR을 열고 리뷰어로부터 r+를 받으십시오.
- 타깃 기능 안정화
- 제안 방법: PR
- 승인 방법: FCP
- 타깃 기능을 안정화하는 PR을 여십시오. 리뷰어가 변경 사항에 만족하면 FCP를 시작할 수 있습니다.
린트, 오류 및 경고
- 새 경고/오류 추가
- 제안 방법: PR
- 승인 방법: r+
- 구현을 담은 PR을 열고 리뷰어로부터 r+를 받으십시오.
- 새 린트 그룹 추가
- 언어 팀의 절차를 따르고, 구현 PR은 컴파일러 팀 구성원에게 리뷰를 받으십시오
- 컴파일러 기능과 관련된 새 린트 추가
- 제안 방법: MCP
- 승인 방법: FCP
- 컴파일러 플래그처럼 원래 컴파일러 팀의 소관인 세부 사항에 관한 린트는 언어 팀이 아니라 컴파일러 팀이 승인합니다.
- 린트와 그 근거를 설명하는 MCP를 작성하십시오. 논의가 마무리되면 새 린트를 승인하기 위한 FCP를 시작할 수 있습니다
- 컴파일러 기능과 관련된 새 향후 호환성 경고(FCW) 추가
- 제안 방법: MCP
- 승인 방법: FCP
- 컴파일러 플래그처럼 원래 컴파일러 팀의 소관인 세부 사항에 관한 FCW는 언어 팀이 아니라 컴파일러 팀이 승인합니다.
- FCW와 그 근거를 설명하는 MCP를 작성하십시오. 논의가 마무리되면 새 FCW를 승인하기 위한 FCP를 시작할 수 있습니다
- 컴파일러 기능과 관련된 린트의 기본 린트 수준 변경
- 제안 방법: MCP
- 승인 방법: FCP
- 컴파일러 플래그처럼 원래 컴파일러 팀의 소관인 세부 사항에 관한 린트는 언어 팀이 아니라 컴파일러 팀이 승인합니다.
- 기본 린트 수준을 변경하려는 근거를 설명하는 MCP를 작성하십시오. 논의가 마무리되면 새 린트를 승인하기 위한 FCP를 시작할 수 있습니다
- 언어 기능과 관련된 새 린트 추가
- 언어 팀의 절차를 따르고, 구현 PR은 컴파일러 팀 구성원에게 리뷰를 받으십시오
- 언어 기능과 관련된 새 향후 호환성 경고(FCW) 추가
- 언어 팀의 절차를 따르고, 구현 PR은 컴파일러 팀 구성원에게 리뷰를 받으십시오
- 언어 기능과 관련된 린트의 기본 린트 수준 변경
- 언어 팀의 절차를 따르고, 구현 PR은 컴파일러 팀 구성원에게 리뷰를 받으십시오
라이선스
- 새 의존성 도입/라이선스 변경/의존성 버전 올리기
- 제안 방법: PR
- 승인 방법: r+ (컴파일러 리드)
- 라이선스에 영향을 주는 변경 사항으로 PR을 열고, 팀 리드에게 리뷰를 배정하십시오
컴파일러 변경 사항의 stable/beta 채널 백포트 상정
백포트를 참조하십시오.
rust-lang/rust CI에 생태계/통합 테스트 작업/구성 요소 추가
rust-lang/rust CI에 생태계/통합 테스트 작업/구성 요소 추가하기
적용 범위
이 정책은 추가 산출물을 빌드하고 테스트하는 새로운 생태계 및 통합 테스트 작업/구성 요소를 추가하자는 제안에 적용됩니다. 이러한 작업은 rust-lang/rust PR CI 또는 Full CI(이하 “CI”)를 실패시키거나 실패 메시지를 발생시킬 수 있으며, 이는 rust-lang/rust CI를 함께 사용하는 다른 rust-lang/rust 기여자에게 영향을 줄 수 있습니다.
예를 들면 다음과 같습니다(전체 목록이 아닙니다).
- 생태계 테스트 작업: Rust for Linux 또는 Fuchsia.
- 통합 테스트 구성 요소: GCC 코드 생성 백엔드 또는 Cranelift 코드 생성 백엔드.
배경
rust-lang/rust는 PR CI에서 소규모 빌드/테스트 작업을 실행하고(더 빠르며 비용이 적게 들고 덜 포괄적임), Full CI에서는 훨씬 더 많은 빌드/테스트 작업을 실행합니다. PR CI는 보통 1시간 정도 걸리고, Full CI는 보통 3시간 정도 걸립니다. 생태계 및 통합 테스트 작업/구성 요소가 다음과 같다면,
- 간헐적으로 실패할 수 있거나,
- 실제로 실패할 수 있지만 그 실패에 대해 누구와 상의해야 하는지, 또는 누가 실패를 수정할 책임이 있는지 명확하지 않거나,
- 그 밖에 테스트 작업 메인테이너와 실패 대응 절차가 제대로 문서화되어 있지 않다면,
다른 기여자들에게 큰 마찰과 불만을 초래할 수 있습니다. 이들은 그 실패에 대처하거나 애초에 테스트 작업이 왜 실패했는지 파악해야 하며, 그 실패가 자신의 PR을 가로막고 있을 수도 있습니다. rust-lang/rust CI를 사용하는 모든 사람은 이를 책임감 있게 사용해야 합니다.
이를 돕기 위해, rust-lang/rust CI에 생태계/통합 테스트 작업/구성 요소를 추가하자고 제안하려면 아래에 설명된 절차를 따르십시오.
rust-lang/rust CI에 생태계/통합 테스트 작업/구성 요소를 추가하는 절차
- 제안하는 테스트 작업/구성 요소를 수용할 여력이 있는지 Zulip(#t-infra)에서 인프라 팀에 확인을 요청하십시오.
- 주요 변경 제안(MCP)으로 제안하십시오.
- 작성을 마친 생태계 및 통합 테스트 작업/구성 요소 정책(아래 참조)을 포함하십시오.
- MCP가 지지를 받아 승인되면 rust-lang/rust에 제안에 관한 새 이슈를 만들어 MCP를 링크한 다음,
@rustbot label +I-libs-nominated로 라이브러리 팀 리뷰에 제안을 상정하십시오.- MCP를 링크하십시오.
- 라이브러리 팀이 제안을 리뷰한 뒤 차단할 만한 우려 사항이 없으면 구현 PR을 rust-lang/rust에 제출하십시오.
- PR에는 트리 내 rustc-dev-guide에 추가할 해당 생태계/통합 테스트 작업/구성 요소 지원 페이지를 함께 포함해야 하며, 실패 대응 절차가 잘 문서화되어 있는지 확인해야 합니다(아래 참조). MCP와 이슈를 링크하십시오.
- 인프라 팀이 구현 PR을 리뷰합니다. 승인되면 해당 생태계/통합 테스트 작업/구성 요소가 rust-lang/rust PR CI 또는 Full Merge CI에서 실행됩니다.
생태계 및 통합 테스트 작업/구성 요소 정책
MCP의 일부로 이 템플릿(구분선 아래)을 복사하여 작성하십시오. 정책 질문/설명 자체는 인용 블록으로 표시됩니다. MCP 작성자가 제공해야 하는 정보는 기울임꼴 문장으로 표시되어 있으며, 그 내용을 실제 정보로 바꿔 작성해야 합니다.
참고: 이 정책은 생태계 테스트 작업/구성 요소를 추가하는 일을 번거롭고 성가시게 만들려는 것이 아닙니다. 저희는 필요한 배경 정보를 미리 수집하고(특히 실현 가능한 실패 대응 절차를 함께 마련하고) 싶을 뿐입니다. 이는 테스트 작업/구성 요소가 실제로 실패하여 전혀 관련 없는 PR의 PR / Full Merge CI까지 막게 될 경우, 다른 rust-lang/rust 기여자들이 겪을 수 있는 불편을 최소화하기 위함입니다.
## Ecosystem and Integration Test Job/Component Policy
The ecosystem/integration test job/component ("test job/component") proposed for the
[rust-lang/rust] CI must:
- Be approved by the compiler team through a proposed MCP, where the MCP is seconded by a compiler
team member, and the MCP is accepted with no blocking concerns.
- Have no blocking concerns from the library team.
- Have the implementation PR be reviewed and approved by the infrastructure team.
- Be properly documented on [rustc-dev-guide] (preferably as part of the implementation PR).
Please complete the sections below so [rust-lang/rust] teams can have sufficient context about the
proposed test job/component.
### Test job/component rationale
> What does this test job/component do?
>
> - If an ecosystem test job/component is being proposed, can you briefly describe the intended
> ecosystem users?
*Please provide responses here, replacing this sentence.*
> What [rust-lang/rust] changes can potentially break the test job/component?
>
> E.g. changes to rustc, standard library, bootstrap or tools (like clippy/rustfmt/cargo).
*Please provide responses here, replacing this sentence.*
> Why does this test job/component need to be part of the [rust-lang/rust] PR and/or Full Merge CI?
*Please provide responses here, replacing this sentence.*
> If the test job/component will block on failure, why does it need to block?
*Please provide responses here, replacing this sentence.*
> If the test job/component will not block on failure initially but is intended to eventually become
> blocking:
>
> - Why will it become blocking?
> - When will it become blocking?
*Please provide responses here, replacing this sentence.*
### Test job/component maintainers
> The proposed test job/component for [rust-lang/rust] CI must have at least one dedicated test
> job/component maintainer. The test job/component maintainers understand that they will be pinged
> or otherwise contacted about the ecosystem/integration test job/component, particularly for (but
> not limited to) its failures.
>
> **Please list who will be maintaining this ecosystem/integration test job/component here. Please
> format the github handles in the style:**
>
> ```
> [@github_handle_1](https://github.com/github_handle_1)
> [@github_handle_2](https://github.com/github_handle_2)
> ```
>
> NOTE: For future readers, you can paste the usernames without formatting them as links via
> **ctrl-shift-v**.
*Please list test job/component maintainers here with the formatting advice above, replacing this
sentence.*
> **NOTE: If an ecosystem/integration test job/component no longer has an active dedicated
> maintainer (or maintainers), and if [rust-lang/rust] teams find the ecosystem/integration test
> job/component causes significant burden or becomes irrelevant, then the ecosystem/integration test
> job/component may be removed.**
### CI infrastructure considerations
> You should ask the Infrastructure Team on the
> [`#t-infra`](https://rust-lang.zulipchat.com/#narrow/channel/242791-t-infra) zulip channel when
> proposing a new ecosystem/integration test job/component to check if there's capacity for the test
> job/component.
>
> - Does the ecosystem/integration test job/component require substantial CI resources (storage and/
> or CI time)? In particular, will it require large runners?
*Please provide responses here, replacing this sentence. If there is a zulip topic discussing it
with the Infrastructure Team, please include the zulip topic link here.*
### Features and implementation details
> Does the proposed test job/component intend to use any unstable features?
>
> - If so, are the unstable features ready for exposure (e.g. must an unstable feature be completely
> reworked)?
> - For ecosystem test jobs/components, are the unstable features ready for such exposure to the
> ecosystem, and are the feature stakeholders ready for such usage?
*Please provide responses here, replacing this sentence.*
> Does the proposed test job/component intend to intentionally depend on any implementation details?
> This may include but is not limited to: unstable/internal compiler/tool flags and behaviors,
> `RUSTC_BOOTSTRAP` usages, standard library implementation details, etc.
>
> - If so, are there plans to shrink or expand such dependencies in the future?
*Please provide responses here, replacing this sentence.*
### Failure protocol: what to do if the job/component breaks/fails?
> **NOTE: If the artifacts of an ecosystem/integration test job/component are not shipped as part of
> a distribution component/toolchain, the test job/component may be temporarily disabled to unblock
> [rust-lang/rust] PR CI or Full Merge CI without receiving prior approval from the test
> job/component maintainers. The test job/component maintainers will be pinged or otherwise notified
> about the test job/component being disabled.**
> How can the test job/component maintainers be contacted in case of failure? By default, it is
> assumed that the test job/component maintainer can be pinged via their GitHub handles.
*Please provide responses here, replacing this sentence.*
> (If applicable) If the addition of an ecosystem/integration test job is being proposed:
>
> - How can the test job be run in CI? If so, is there a try job (`try-job: ...`) invocation? What's
> the job name?
> - Can the test job be run locally? If so, how?
*If applicable, please provide responses here, replacing this sentence. Otherwise, you can ignore
this question.*
> (If applicable) If the addition of an ecosystem/integration test component is being proposed:
>
> - Which existing CI jobs will be building and testing this test component?
> - Can they be built and ran as part of a try job? If so, what are the job names and the try job
> (`try-job: ...`) invocations?
> - Can the test component be built and run locally? If so, how?
*If applicable, please provide responses here, replacing this sentence. Otherwise, you can ignore
this question.*
> How can the test job/component be disabled in the event of spurious failures that are blocking PR
> and/or Full Merge CI?
*Please provide responses here, replacing this sentence.*
> If a PR breaks the test job/component:
>
> - If the breakage seems **spurious** and retrying does not resolve the spurious breakage, the test
> job may be **temporarily disabled** (see below).
> - If the breakage is **intentional**, how will this be resolved?
> - If the breakage is **unintentional**, is the PR author expected to fix the breakage?
*Please provide responses here, replacing this sentence.*
### Dependencies, build/test environments and reliability
> Does the test job/component involve any custom build systems that are not used in the regular
> [rust-lang/rust] CI jobs?
*Please provide responses here, replacing this sentence.*
> Does the test job/component depend on external resources (e.g. external servers) that may be
> subject to network connectivity?
>
> - If so, does the infrastructure team need to help maintain a mirror of the required assets?
*Please provide responses here, replacing this sentence.*
> Are there any potential sources of spurious failures due to the test job/component?
*Please provide responses here, replacing this sentence.*
> Are there any other unusual requirements (build environment, dependencies, etc.)?
*Please provide responses here, replacing this sentence.*
[rust-lang/rust]: https://github.com/rust-lang/rust
[rustc-dev-guide]: https://github.com/rust-lang/rustc-dev-guide
컴파일러 팀이 유지 관리하는 저장소
컴파일러 코드의 대부분은 rust-lang/rust 저장소에 있지만, 컴파일러 팀이 책임지는 다른 크레이트/의존성을 담은 추가 저장소도 몇 개 있습니다.
이들 저장소 중 어디에서 발생한 긴급한 이슈에도 팀이 대응할 수 있도록, 컴파일러 팀의 모든 구성원은 풀 리퀘스트를 생성하고 병합할 수 있는 권한을 가지고 있습니다. 하지만 각 저장소에는 보통 해당 저장소를 가장 잘 알고 주요 메인테이너 역할을 하는 팀 구성원이 있습니다.
- 컴파일러 개발에 관심 있는 사람들을 위한 입문 문서인
rustc개발 가이드입니다. - 컴파일러나 인접한 도구 및 구성 요소의 변경 제안을 등록하는 데 사용하는 팀 자체의 저장소입니다.
- Cranelift에 기반한 실험적인 rustc 백엔드인
cranelift입니다. 디버그 모드에서 컴파일 시간을 개선할 가능성이 있습니다. 현재 bjorn3이 유지 관리하고 있습니다. - 컴파일러 팀이 유지 관리하는
LLVM::APFloat라이브러리의 Rust 포트입니다. LLVM 라이브러리의 포트이기 때문에 이 저장소는 다른 저장소와 라이선스 조건이 미묘하게 다릅니다. rustc_apfloat#licensing을 참고하십시오. - LLVM 및 MLIR을 위한 고성능 자동 미분기인 Enzyme의 포크입니다(자세한 정보는 이 링크를 참고하십시오). 이 포크는 ZuseZ4가 유지 관리합니다.
thorin은 GNU 확장 및 DWARF 5 패키지 형식을 지원하는 DWARF 패키징 유틸리티입니다. 주로 davidtwco가 유지 관리합니다.- 지금 읽고 계신 문서 웹사이트인 Rust Forge입니다. Rust 프로젝트 전체가 공동으로 유지 관리합니다.
ar_archive_writer: 앞서 언급한LLVM::APFloat라이브러리와 마찬가지로 다른 저장소와 라이선스가 약간 다릅니다. ar_archive_writer#licensing을 참고하십시오.datafrog는 다른 Rust 프로그램에 내장하여 사용하도록 만든 경량 Datalog 엔진입니다(TODO: 상태?).ena는 Rust로 작성된 union-find / 합동 클로저(congruence-closure) 구현체로, 추론 변수 테이블의 기반 구현을 담고 있습니다. 추론 변수의 인스턴스화와 병합을 추적하는 역할을 합니다.literal-escaper는 문자열 리터럴의 이스케이프를 해제하는 라이브러리입니다.rustc_lexer와proc_macro에서 사용합니다.miri는 Rust의 중간 수준 중간 표현(MIR)을 위한 인터프리터입니다. 안전성 요구 사항을 지키지 않는 unsafe 코드를 탐지합니다.measureme는rustc이벤트를 기록하고 바이너리 형식으로 직렬화하는 라이브러리입니다. 현재는rustc내부에서만 사용합니다.odht는 사전 디코딩 없이 디스크에서 메모리로 매핑할 수 있는 해시 테이블을 위한 크레이트입니다. 현재는rustc내부에서만 사용합니다.rustc-demangle:rustc심볼의 디맹글링을 제공합니다(문서).rustc-hash는rustc에서 사용하는 비암호학적 해싱 알고리즘입니다.rustc-rayon은 Rust용 데이터 병렬 처리 라이브러리인 Rayon의 포크입니다. 이는rustc컴파일을 병렬화하려는 지속적인 노력의 일환입니다. 작업 영역을 참고하십시오.rustc-stable-hash는rustc가 사용하는 크로스 플랫폼 해싱 알고리즘으로, 결정적이며 보안성이 없습니다.stacker는 스택 공간이 부족할 때 스택을 늘릴 수 있도록 돕는 라이브러리입니다. 문서를 참고하십시오.
내부용 도구를 담은 그 밖의 저장소는 다음과 같습니다.
cargo-bisect: Rust 컴파일러의 회귀를 이분 탐색하는 도구로, 버그가 어디에서 도입되었는지 찾는 데 매우 유용합니다.- 모든 팀이 회의를 등록하는 캘린더가 있습니다. 캘린더 클라이언트는
.ics파일을 가져와 업데이트를 받을 수 있습니다. jobserver-rs: Rust용 GNU Make jobserver 구현체입니다. 문서를 참고하십시오.josh-sync: rust-lang/rust 저장소에 있는 Josh 서브트리의Just One Single History동기화(pull 및 push)를 수행하는 라이브러리입니다.
Rust 프로젝트에 기여를 시작하고 싶거나 이미 기여하고 계시고, 이 저장소들 중 어느 것에든 전문 지식이나 관심이 있으시다면 언제든 연락해 주십시오!
자료
기여자와 팀 구성원에게 유용한 여러 자료가 있습니다.
- rustc 개발자 가이드
- 컴파일러 내부 구조와 개발 환경 설정에 관한 문서입니다.
- rustc 생성 문서
- 컴파일러 소스에 대한 rustdoc 출력물입니다.
- FIXMEH
- 컴파일러에 있는 모든
// FIXME주석의 최신 목록입니다.
- 컴파일러에 있는 모든
기여자나 컴파일러 팀 구성원에게 유용할 만한 자료가 더 있다면 언제든지 PR을 제출하여 이곳에 추가하십시오.
리뷰 정책
이 문서는 Rust 컴파일러에 대한 기여의 리뷰 정책을 설명합니다. 이 문서는 기여자와 리뷰어 모두를 대상으로 합니다.
이 정책의 목적은 Rust 프로젝트가 기대하는 바를 명확히 하여 기여자가 리뷰하기 쉬운 풀 리퀘스트를 작성하도록 돕고, 리뷰어에게는 공통적으로 확인해야 할 사항을 간편한 목록으로 제공하여 돕는 것입니다. 양측이 어떻게 협력해야 하는지 명확히 알고 있다면 프로젝트에 도움이 될 것입니다.
코드 리뷰의 목적은 다음과 같습니다.
- 버그와 사용성 및 성능 회귀가 발생할 위험을 줄입니다.
- 코드를 유지보수하기 쉬운 상태로, 즉 읽기 쉽고, 문서화되어 있으며, 충분히 테스트된 상태로 유지합니다.
- 변경 사항이 전체적인 그림과 적절한 맥락을 염두에 두고 이루어지도록 보장합니다. 이는 단독으로 보면 무해해 보이지만 더 넓은 맥락에서는 문제가 되거나 바람직하지 않은 변경 사항에 특히 중요합니다.
리뷰는 제안된 변경 사항을 다른 관점에서 살펴볼 또 다른 시선을 더함으로써 이를 달성하며, 이를 통해 실수를 조기에 발견하고 변경의 근거가 되는 추론에 숨어 있을 수 있는 맹점을 드러낼 가능성이 높아집니다.
기본 리뷰 요건
리뷰가 효과적으로 이루어지려면 충족해야 할 여러 요건이 있습니다.
- 리뷰어는 리뷰 대상 코드를 충분히 이해하고 있어야 합니다.
- 이는 주어진 변경 사항이 가져오는 명확하지 않고 의도하지 않은 부작용을 발견하는 데 중요합니다.
- 풀 리퀘스트 작성자는 다음을 제공해야 합니다.
- 오타 수정처럼 극히 사소한 변경이 아니라면, 간결하고 상위 수준의 변경 사항 설명과 (2) 그 근거를 제공해야 합니다.
- 근거를 더욱 유용하게 만들기 위해, 작성자는 잠재적인 쟁점, 감수해야 했던 절충, 검토했던 대안적 접근 방식, 관련 문서, 논의 및 맥락 등을 나열할 것을 권장합니다.
- 코드 리뷰는 어려운 일이며, 리뷰어에게는 리뷰에 쓸 수 있는 시간이 제한되어 있습니다. 리뷰어가 풀 리퀘스트의 의도와 맥락을 직접 짜맞추지 않아도 되도록 하여 리뷰 과정을 빠르게 시작할 수 있게 하면, 속도가 빨라질 뿐만 아니라 리뷰의 품질도 향상됩니다.
- 리뷰어는 자신이 해당 변경 사항을 승인하기에 적합한 사람인지 잘 판단할 수 있어야 합니다.
- 리뷰 대상 코드에 대한 지식은 이 질문에 답하기 위한 당연한 기준이지만 유일한 기준은 아닙니다.
- 절차 측면에서 리뷰어는 다음 사항도 판단해야 합니다.
- 리뷰어가 단독으로 결정할 수 있습니까?
- 해당 PR이 승인 절차를 거쳐야 합니까?
- 해당 PR에 다른 팀, 특히
t-lang의 리뷰 및/또는 승인이 필요합니까? - 변경 사항이 stable 코드를 깨뜨리거나 의도하지 않은 새로운 코드를 허용하기 시작할 수 있습니까? PR에 위험 요소가 있다면, 충분히 정당화됩니까? 변경 사항에 대해 Crater 실행을 통한 생태계 영향 평가가 필요합니까?
- 해당 PR이 상당한 성능 변화를 가져옵니까? 성능 회귀가 발생할 수 있다면, 그것이 정당화됩니까? 해당 PR에 성능 측정 실행이 필요합니까?
- 리뷰어가 충분히 철저하게, 그리고 적시에 리뷰를 수행할 수 있습니까?
- 리뷰어가 충분히 편향되지 않은 관점을 제공할 수 있을 만큼 공정합니까? 예를 들어 공동 저작(리뷰어가 PR에 상당히 중요한 변경을 가한 경우)이나 기타 이해 상충으로 인한 문제는 없습니까?
리뷰 체크리스트
다음 질문 목록은 리뷰어와 PR 작성자 모두가 PR을 좋은 상태로 만들고 위의 기준을 충족하는 데 도움이 됩니다:
PR 작성자와 리뷰어를 위한 체크리스트
- PR 메시지에 다음이 포함되어 있습니까..
- ..변경 사항에 대한 간결한 상위 수준 설명이 있습니까? (무엇이 변경되는지)
- ..변경하는 이유에 대한 명확한 근거가 있습니까? (왜 변경되는지)
- ..사소하지 않고 적절한 경우, 버그가 어떻게 수정되는지 또는 변경 사항이 어떻게 구현되는지 설명되어 있습니까?
- ..잠재적인 쟁점 목록이 있습니까? 대안은 있습니까? 트레이드오프는 있습니까? 위험은 있습니까?
- ..관련 이슈, RFC, MCP 등에 대한 링크가 있습니까?
- PR에 회귀 테스트가 필요합니까? PR의 테스트 커버리지가 충분합니까?
- 이 변경이 주요 변경 제안(MCP)의 대상이어야 합니까? 이미 다뤄지고 있습니까? 이미 열려 있는 MCP가 있다면, 그것이 이미 수락되었습니까, 아니면 PR이 그것 때문에 보류되어 있습니까?
- PR에 성능 측정 실행이 필요합니까?
- PR에 다른 팀의 리뷰 및/또는 승인이 필요합니까?
- 예: 생태계에 미치는 영향이 큰 린트 확장이나 언어 변경의 경우
t-lang- PR이 다른 팀에 사소하지 않은 방식으로 영향을 미칩니까? 영향을 받는 팀에 미리 알려야 합니까?
- 예: rustfmt, rust-analyzer 또는 서브트리에 대한 변경.
- 1년 후에 이 PR을 이해하려는 사람이 무슨 일이 일어나고 있는지 빠르게 파악할 수 있겠습니까?
- 새 코드가 적절히 문서화되어 있습니까? 기존 문서가 여전히 최신 상태입니까?
- PR의 변경 사항에 따라 레퍼런스나 에디션 가이드를 업데이트해야 합니까?
- PR이 다음 중 어느 항목에 회귀를 일으킵니까:
- 오류 메시지 품질
- 유지 보수성(예: 복잡한 코드, 문서 부재, unsafe)
- 특정 대상 플랫폼
- 다운스트림 도구(예: 링커, 디버거)
- 컴파일 시간
- 메모리 사용량
- 타깃(예: 베이스라인, 타깃 기능, 호출 규약 등)
리뷰어를 위한 체크리스트
- 제가 이 PR을 리뷰하기에 적합한 사람입니까:
- 이 PR의 변경 사항이
t-compiler의 관할에 속합니까?- 제가 코드를 충분히 잘 이해하고 있습니까?
- 명확하지 않은 부작용을 발견할 수 있겠습니까?
- 이 PR로 인해 생긴 버그를 제가 고칠 수 있겠습니까?
- 적시에 리뷰할 수 있습니까?
- 어떤 이유로든 PR을 빨리 승인해야 한다는 압박을 느끼고 있습니까?
- 제가 충분히 공정합니까?
- 병합하기 전:
- PR 제목과 설명이 여전히 정확합니까?
- 커밋 이력이 충분히 깔끔합니까? PR 이력에 “fix typo” 커밋이 16개나 있을 필요는 없습니다.
- PR이 관련 이슈를 올바르게 닫습니까, 아니면 잘못 닫습니까?
- 다른 관련 팀의 리뷰어를 배정해야 합니까?
흔한 상황에 대처하기 위한 지침
대부분의 경우 이 정책을 어떻게 적용할지는 상식으로 충분히 판단할 수 있습니다. 그러나 때로는 어떻게 진행해야 할지 바로 명확하지 않은 모호한 영역이 있습니다. 이 절에서는 몇 가지 흔한 사례와 함께 각 사례에 대처하는 방법에 대한 지침을 제시합니다.
제가 리뷰하기에 적합하지 않은 것 같습니다. 이제 어떻게 해야 합니까?
(rustbot 등을 통해) PR이 (무작위로) 배정되었지만 리뷰하기가 부담스럽게 느껴지는 것은 지극히 정상적인 일입니다. 구체적인 경우에 따라 다음과 같이 할 수 있습니다.
- 변경 사항이 매우 크거나 논란의 여지가 있어 보인다면, 작성자에게 적절한 승인 절차를 거치도록 권하는 것을 고려하십시오.
- 리뷰에 딱 맞는 사람을 알고 있다면,
r? @<github-name>로 그 사람을 배정하십시오. 그 사람에게 리뷰를 맡아 줄 수 있는지 묻는 댓글을 남기는 것이 예의입니다. 다만 그 사람이 실제로 맡을 수 있는지 미리 확인할 필요는 없습니다. - 변경 사항이 그리 복잡하지 않고, 무작위로 다시 배정될 다른 컴파일러 리뷰어도 이 PR을 어려워할 것으로 예상되지 않는다면,
r? compiler로 컴파일러 리뷰어를 무작위로 다시 배정할 수 있습니다. - 변경 사항이 복잡하거나, 다른 컴파일러 리뷰어를 무작위로 배정해도 재배정만 여러 번 반복될 것으로 예상된다면,
#t-compiler/private에 스레드를 열어 팀의 다른 구성원에게 이 PR을 리뷰할 수 있는 사람이 있는지, 더 나아가 팀이 이 변경 사항을 받아들이는 것 자체에 동의하는지 물어보아야 합니다. - 변경 사항이 다른 팀을 대상으로 한다면, 해당 팀의 리뷰어를 배정하십시오(예:
r? compiler).
리뷰어를 찾는 데 도움이 필요하면 언제든지 #t-compiler Zulip 스트림에서 도움을 요청할 수도 있습니다. 부담스럽지 않은 범위에서, PR을 최종 리뷰어에게 넘기기 전에 초기 리뷰를 해 두는 것을 권장합니다. 이렇게 하면 PR 작성자는 유용한 피드백을 더 빨리 받을 수 있고, 이후 리뷰어의 작업은 줄어들며, 여러분도 컴파일러의 다양한 영역에 대한 이해를 넓힐 수 있습니다.
기여에 승인 결정이 필요한지 불분명한 경우
기여에 더 넓은 범위의 팀 승인이 필요할 수 있다고 생각된다면, 제안, 승인 및 안정화 문서를 확인하십시오. 기여가 해당 문서의 어떤 예시에도 해당하지 않으면 #t-compiler/private에 스레드를 열어 문의하십시오.
논의 과정이나 근거가 지나치게 불투명한 경우
때로는 사전 논의의 결과물로 보이지만 설명이나 근거가 전혀 없는 PR이 있습니다. 이런 PR은 대개 “Change X” 같은 제목을 달고 있으며, PR 메시지의 내용은 “r? @xyz“가 전부입니다. 변경 사항이 타당하고 심지어 컴파일러 팀 구성원이 제안한 것일 수도 있지만, 이는 바람직한 형식이 아닙니다.
기여자가 몇 년 뒤 이분 탐색 중에 해당 PR을 우연히 발견하더라도, 오프라인이나 다른 곳에서 논의된 맥락이 전혀 남아 있지 않은 PR만 보게 될 수 있으며, 그 정보는 향후 기여자가 확인할 수 없습니다. 이는 유지 보수성에 좋지 않습니다. 관련 맥락을 포함해 두면 나중에 PR 작성자 자신에게도 도움이 되는 경우가 매우 많습니다!
PR 메시지에는 무엇을 변경하는지, 왜 변경하는지, 그리고 그 밖에 관심을 가질 만한 사항을 그 자체로 완결된 형태로 설명해야 합니다.
몇 년 뒤 이 PR이 수정한 코드와 관련된 버그를 고쳐야 하고, 현재 방식이 왜 이렇게 되었는지 그 근거를 재구성해야 하는 사람의 입장이 되어 보십시오.
리뷰어와 PR 작성자가 같은 조직에 소속되어 있거나 같은 고용주를 위해 일하는 경우
같은 회사의 직원 두 명이 서로의 PR을 리뷰하는 것을 금지하는 규칙은 없습니다. 우리는 컴파일러 팀 리뷰어가 선의로 행동한다고 가정하며, 팀 구성원이 그렇게 하리라 신뢰합니다.
이러한 경우의 우려 사항은 다른 어떤 두 리뷰어의 경우와도 다르지 않습니다. 우리는 고용주가 누구든 모든 리뷰어가 위에서 설명한 메커니즘과 원칙을 존중하기를 기대합니다. PR이 이루어지는 변경 사항을 간결하게 설명하고 있습니까? 이후의 기여자가 추론을 따라가며 무슨 일이 일어나고 있는지 재구성할 수 있도록, 변경 사항이 왜 타당한지에 대한 명확하고 투명한 근거를 제시하고 있습니까? 쟁점이 된 사항은 논의를 거쳐 해소되었습니까? 그렇다면 문제가 없습니다.
어떤 사항이 논쟁의 여지가 있는지 확신이 서지 않으면 @rust-lang/compiler에 미리 알리고 다른 의견을 요청하십시오. 기여에 더 넓은 범위의 팀 승인이 필요할 수 있다고 생각된다면, 제안, 승인 및 안정화 문서를 확인하십시오.
리뷰와 멘토링
PR을 통해 누군가를 멘토링하는 과정에서 리뷰어가 사실상 변경 사항을 공동으로 작성하게 되는 경우가 흔히 있습니다. 이 경우 리뷰어가 사실상 자신의 변경 사항을 승인하는 셈이므로 까다로운 상황이 될 수 있습니다. 어떻게 진행할지 결정할 때 고려해야 할 사항은 다음과 같습니다.
- 변경 사항의 전반적인 방향이 이미 승인 결정의 일환으로 승인되었고, 멘토링 중에 제공한 구체적인 조언이 사소한 기술적 문제를 해결하는 것에 국한되었다면, 추가 리뷰는 필요하지 않습니다.
- 마찬가지로, 논쟁의 여지가 있는 결정이 다른 컴파일러 팀 구성원과 함께 PR에서 드러나게 논의되고 해결되었으며, 나머지 변경 사항이 합의된 전반적인 방향에서 벗어나지 않는다면 이 경우에도 추가 리뷰는 필요하지 않습니다.
- PR이 리뷰어의 구체적인 제안에 따라 열린 것이고 변경 사항이 아주 사소하지만은 않다면, 최종 리뷰는 다른 사람이 하는 것이 좋습니다. 다만 처음 리뷰한 리뷰어나 멘토도 PR을 넘기기 전에 PR이 좋은 상태가 되도록 돕는 것이 권장됩니다.
일반적으로 이런 경우에는 해당 분야를 잘 아는 사람에게 두 번째 의견을 구하는 것이 좋습니다. 멘토가 놓쳤을 수 있는 부분이나 사각지대를 발견할 가능성을 높이기 위해서입니다.
변경되는 코드를 아무도 이해하지 못하는 경우
때로는 더 이상 아무도 이해하지 못하는 코드에서 버그가 발견되기도 합니다. 원래 작성자와는 연락이 닿지 않고, 제안된 수정이 어떤 영향을 미칠지 가늠하기도 어렵습니다. 이런 경우 리뷰어는 PR에 I-compiler-nominated를 지정하거나(계속 주 리뷰어를 맡으려는 경우), 컴파일러 팀 리드를 이슈에 배정하고 S-waiting-on-team 레이블을 추가하는 것이 좋습니다.
어느 경우든 PR은 주간 트리아지 회의에서 다뤄집니다. 또한 수정하려는 문제에 대한 설명, 불분명한 점, 잠재적 위험, 검토한 대안 등 이슈에 대한 정보를 최대한 많이 모아 문서로 남기는 것이 특히 중요합니다. 추적 이슈를 열어 해당 영역을 제대로 이해하지 못하고 있다는 사실과 구체적인 질문, 우려 사항, 버그를 기록해 두는 것도 좋습니다. 이 추적 이슈는 컴파일러 팀 구성원들이 해당 영역을 다시 잘 이해하게 되면 해결할 수 있습니다.
리뷰어는 PR 작성자에게 이러한 정보를 코드 주석이나 PR 메시지(git 커밋 기록의 일부가 됩니다) 또는 둘 모두에 추가해 달라고 요청해야 합니다.
외부 프로젝트가 rustc 내부 요소를 사용할 수 있도록 PR이 변경을 가하는 경우
이는 사안별로 판단해야 합니다.
일반적으로 해당 컴파일러 영역의 담당자가 동의한다면 무언가를 공개하거나, 정리하거나, 더 범용적으로 만드는 변경은 허용해야 합니다(그러니 담당자에게 배정하기만 하면 됩니다).
구체적인 예를 들면, 누군가 MIR 인터프리터를 사용하면서 무언가를 공개하고 싶어 한다면 대개 문제가 되지 않습니다. 하지만 일부 함수는 MIR 인터프리터 내부의 불변 조건을 유지하기 위해 일부러 모듈 비공개 또는 크레이트 비공개로 만들어져 있습니다. 따라서 기본적으로 이러한 PR은 관련된 사람에게 배정하기만 하면 됩니다(보통 이들은 이 부분이 변경되면 핑을 받고 싶다고 rustbot에 등록해 두었기 때문에 어차피 핑을 받습니다).
이러한 API에는 해당 API가 어떤 외부 사용자와 관련이 있고 어떤 목적으로 쓰이는지 밝히는 문서 주석을 달도록 요구하십시오.
기여에 더 넓은 범위의 팀 승인이 필요할 수 있다고 생각된다면 제안, 승인 및 안정화 문서를 확인하십시오.
이로 인해 내부용이어야 할 컴파일러 API가 눈에 띄지 않게 외부 사용자에게 묶일 수 있다는 점에 유의하십시오. (rust-lang/ 프로젝트가 아닌) 외부 사용자에게는 리팩터링을 방해하는 등 컴파일러에 상당한 유지 보수 부담을 주지 않는 한에서만 이러한 편의를 제공할 수 있으며, 엄격한 안정성은 보장하지 않는다는 점을 전달하십시오.
PR이 매우 크고 복잡한 경우
리뷰어가 매우 크고 복잡한 PR까지 감당해야 하는 것은 아닙니다. 기여자는 리뷰가 가능하도록 작업을 나누어야 합니다. 예를 들어 각각 논리적으로 더 독립적이고 이해하기 쉬운 여러 개의 PR로 나눌 수 있습니다. 일반적으로 영향이 큰 변경을 제출하기 전에 기여자는 관련 팀과 설계를 미리 논의해야 합니다. 따라서 그러한 논의를 참조로 제시하는 것은 기여자의 책임입니다.
확신이 서지 않을 때는 Zulip 스레드를 통하거나 컴파일러 트리아지 회의에 상정하거나 둘 다 해서 PR을 팀에 알리십시오. 그러면 팀이 다음 중 어떻게 할지 결정할 수 있습니다.
- 팀은 PR 작성자가 큰 변경을 더 작은 논리적 PR로 나누도록 도와줄 적합한 리뷰어를 찾을 수 있습니다. 나눈 PR은 각각 따로 리뷰할 수 있으면서도 더 큰 변경의 맥락 안에서도 리뷰할 수 있어야 합니다.
- 팀에 여력이 없거나, 팀 구성원들이 큰 변경을 있는 그대로 받아들일 준비가 되어 있지 않거나, 그럴 의향이나 능력이 없을 수도 있습니다. 이런 경우 팀은 PR을 보류할지 닫을지 결정해야 하며, 그 결정과 이유를 PR 작성자에게 명확히 전달해야 합니다. PR이 몇 달 동안 정체되었다가 결국 거절되는 것은 매우 실망스러운 일입니다.
리뷰의 기술적 측면
컴파일러와 관련 크레이트에 병합되는 모든 PR은 해당 코드를 잘 아는 사람이 적어도 한 명 이상 리뷰해야 합니다.
PR을 열 때 PR 설명에 r? @username을 포함하여 리뷰어를 요청할 수 있습니다. 그렇게 하지 않으면 rustbot이 변경된 파일을 기준으로 정해지는 리뷰어 후보 풀에서 자동으로 누군가를 배정합니다.
나중에 다른 사람에게 리뷰를 요청하려고 r? @username 댓글을 남기는 경우가 흔합니다. 이렇게 하면 PR도 재배정됩니다.
여러 리뷰어에게 리뷰를 요청할 수도 있습니다. 예를 들면 다음과 같습니다.
Rolling both a T-compiler and T-bootstrap reviewer as this PR contains both
compiler and bootstrap changes.
r? compiler
r? bootstrap
bors
저희는 PR을 절대 직접 병합하지 않습니다. 대신 bors를 사용합니다. bors 권한이 있는 자격을 갖춘 리뷰어(예: 컴파일러 팀 구성원)가 @bors r+와 같은 댓글을 남깁니다. 이는 해당 리뷰어가 PR을 승인한다는 뜻입니다.
bors 권한이 있는 사람은 @bors r=username 명령을 남길 수도 있습니다. 이는 PR이 이미 @username에 의해 승인되었다는 뜻입니다. 이 명령은 보통 리베이스 후에 사용합니다.
마지막으로, 경우에 따라 @bors delegate+를 작성하여 PR을 “위임“할 수 있습니다. 이렇게 하면 PR 작성자나 위임받은 사용자가 위와 같은 @bors 명령을 내려 PR을 승인할 수 있습니다(단, 이 권한은 해당 PR 하나로 제한됩니다).
되돌리기
병합된 PR이 예상치 못한 유의미한 회귀를 일으킨 것으로 밝혀지면, 가장 좋은 방침은 이를 신속히 되돌리고 나중에 수정 사항과 회귀 테스트가 추가되었을 때 다시 반영하는 것입니다.
이 경우 무엇이 “유의미한 회귀“인지는 되돌리기를 승인하는 사람의 판단에 달려 있습니다.
되돌리기가 정당한지 판단할 때 고려할 기준은 다음과 같습니다.
- stable 기능이나 그 밖의 중요한 기능의 버그로 인해 실제 코드가 컴파일되지 않게 되거나, 런타임 동작이 바뀌거나, (기본 경고 수준 이상의) 린트가 잘못 발생하는 경우입니다. 특히 불안정 기능 게이트 없이도 해당 버그에 도달할 수 있는 경우입니다.
- 버그나 변경 사항(ICE 포함)이 ICE 포함)가 특히 쉽게 발생하는 경우입니다.
- 버그나 변경 사항이 기여자 경험을 크게 저하하는 경우입니다.
- 테스트가 불안정하여 신뢰할 수 없는 경우입니다.
이러한 기준에 해당하는지 확실하지 않을 때, 특히 실제 코드가 영향을 받는다면 PR을 되돌리십시오. 이렇게 하면 세 가지 이점이 있습니다.
- 최신 개발 버전을 쓰는 사용자(특히 nightly 또는 beta 사용자)가 새 버그가 어디서 유입되었는지 더 확실히 알 수 있는 상태에서 HEAD를 계속 사용하고 버그를 보고할 수 있습니다.
- 원래 PR 작성자와 팀의 부담을 덜어 주어, 누구도 이를 즉시 고쳐야 한다는 압박을 느끼지 않게 됩니다.
- 심각한 버그나 회귀가 다음 nightly/beta/stable 빌드에 포함되는 것을 막을 수 있습니다.
PR을 되돌리기 전에 그 PR이 회귀를 일으켰다는 사실을 상당히 높은 확실성으로 보여야 합니다(예: 커밋 단위 이분 탐색, 하나 이상의 컴파일러 팀 구성원이 이 PR을 원인으로 지목한 nightly 단위 이분 탐색, 또는 관련된 모든 사람에게 명백한 경우). 이슈가 특히 심각하거나 긴급히 수정해야 하는 경우에만 더 낮은 확실성으로 되돌리십시오.
되돌리기 만들기
되돌리기 커밋은 git CLI로 만든 뒤 풀 리퀘스트로 올릴 수 있습니다.
$ git revert -m 1 $COMMIT_HASH
여기서 $COMMIT_HASH는 병합 상태 메시지 옆에서 찾을 수 있습니다.
git이 만들어 주는 기본 커밋 제목과 메시지에만 의존하지 마십시오. 대신 되돌리기 커밋에 의미 있는 제목을 붙이고, 회귀를 일으킨 관련 PR을 링크하십시오. 전체 또는 일부를 되돌리는 대상 PR을 구체적으로 링크하십시오. 관련 이슈와 논의를 링크하십시오. 되돌리는 커밋의 해시를 남겨 두십시오.
되돌리기 커밋 제목과 메시지 예시
Revert #131669 due to ICEs Revert <https://github.com/rust-lang/rust/pull/131669> due to ICE reports: - <https://github.com/rust-lang/rust/issues/134059> (real-world) - <https://github.com/rust-lang/rust/issues/134060> (fuzzing) The changes can be re-landed with those cases addressed. This reverts commit 703bb982303ecab02fec593899639b4c3faecddd, reversing changes made to f415c07494b98e4559e4b13a9c5f867b0e6b2444.
원래 PR의 작성자와 리뷰어를 태그해 상황을 알려 주는 것이 예의입니다. 되돌리기 PR 설명에는 다음 메시지 템플릿을 사용할 수 있습니다.
Reverts rust-lang/rust#123456
cc @author @reviewer
This revert is based on the following report of a regression caused by this PR:
<link to issue or comment(s)>
In accordance with the compiler team [revert policy], PRs that cause meaningful
regressions should be reverted and re-landed once the regression has been fixed
(and a regression test has been added, where appropriate).
[revert policy]: https://forge.rust-lang.org/compiler/reviews.html#reverts
Fear not! Regressions happen. Please rest assured that this does not
represent a negative judgment of your contribution or ability to contribute
positively to Rust in the future. We simply want to prioritize keeping existing
use cases working, and keep the compiler more stable for everyone.
r? compiler
되돌리기 커밋으로 회귀가 실제로 해결되는지 확인할 수 있도록 임시 회귀 테스트를 별도 커밋으로 포함하십시오. 다시 반영할 때에는 테스트 커버리지를 개선한 뒤 이 임시 회귀 테스트를 상황에 맞게 수정하거나 제거할 수 있습니다.
r+ 권한이 있다면 되돌리기가 깔끔하고 그 자체로 새로운 회귀를 일으킬 가능성이 낮은 경우에 직접 승인할 수 있습니다. 다만 “약이 병보다 더 해로운” 경우가 아닌지 반드시 확인하십시오. 사소하지 않은 되돌리기라면 원래 리뷰어나 r? compiler를 통해 지정된 다른 컴파일러 리뷰어의 리뷰를 기다리십시오. 사안이 더 급하다면 #t-compiler에서 문의할 수 있습니다.
일반적으로 되돌리기는 우선순위를 높여야 하며, 되돌리는 대상 PR과 같은 rollup 상태를 따라야 합니다. 롤업 대상이 아닌 PR이 성능에 영향을 주지 않는 것으로 확인되면 rollup=always로 표시할 수 있습니다. 되돌리기 작성자는 롤업을 작성하는 기여자와 조율하여 롤업 일정을 조정하거나, 적절한 경우 롤업 사이에 되돌리기 PR을 끼워 넣을 수 있습니다.
후속 수정
회귀를 일으킨 PR을 되돌리는 대신, 전체 변경 사항은 되돌리지 않고 원래 PR을 조금씩 보완하는 후속 PR을 올려 회귀를 해결하고 싶어질 때가 많습니다. 그러나 실제 사용자가 영향을 받았다고 보고했다면, 다음 중 하나에 해당하지 않는 한 이렇게 하지 않기를 강력히 권합니다.
- 신뢰도 높은 수정이 이미 bors 큐에 들어가 있는 경우.
- 회귀가 릴리스 브랜치(beta 또는 stable)에 들어가 백포트가 필요한 경우. 백포트에는 수정이 새로운 회귀를 일으키지 않도록 “가능한 한 가장 작은 변경“이 바람직한 경우가 많습니다. 문제의 PR을 메인 브랜치에서 여전히 되돌릴지 여부는 해당 PR을
r+할 수 있는 사람의 재량에 맡깁니다.
PR이 되돌려지면 크게 후퇴한 것처럼 느껴질 수 있지만, 대부분의 경우 수정이 확인된 뒤 PR을 다시 반영하는 편이 훨씬 쉽습니다. 되돌리기가 반영되도록 두면 여러분과 리뷰어가 서둘러 대응해야 한다는 부담을 덜 수 있고, 이슈를 충분히 해결할 시간을 확보할 수 있습니다. 또한 한발 물러서서 테스트 커버리지를 다시 점검하는 기회이기도 합니다.
롤업
모든 리뷰어에게 PR을 롤업에 포함해야 하는지 여부를 명시적으로 표시할 것을 강력히 권장합니다. 이 표시는 보통 PR을 승인할 때 @bors r+ $ROLLUP_STATUS를 사용하거나 @bors $ROLLUP_STATUS를 사용하여 하며, 이때 $ROLLUP_STATUS는 다음 중 하나로 대체합니다.
rollup=always: 테스트를 깨뜨리거나 성능에 영향을 줄 가능성이 매우 낮은 PR입니다. 예시 상황:- 빌드를 실패시킬 가능성이 매우 낮은 문서, 주석 등으로 변경이 한정된 경우입니다.
- 변경이 성능에 영향을 줄 수 없는 경우입니다.
- PR이 무언가를 깨뜨릴 수 있거나 동작을 바꾸는 변경을 반영하지 않는 경우입니다.
- 다만 다른 변경 없이 기능 안정화만 하는 경우에는 롤업해도 대체로 괜찮습니다.
- 확실하지 않다면 이 옵션을 사용하지 마십시오!
rollup=maybe:@bors r+에 롤업 상태를 전혀 지정하지 않았을 때의 기본값입니다. 변경이 테스트를 깨뜨리지 않을지 다소 의심스러울 때 사용하십시오. 다른 범주 중 하나에 해당하는지 확실하지 않을 때도 사용할 수 있습니다. 기본값이므로 이전 명령으로 지정한 롤업 수준을 해제하려는 경우가 아니라면 보통 명시적으로 지정할 필요가 없습니다.rollup=iffy: 약간 위험한(“maybe“보다 위험한) PR에 사용하십시오. 예시 상황:- PR이 크고 단순 추가가 아닌 경우입니다(참고: 완전히 새로운 테스트 2000줄을 추가하는 것은 롤업해도 괜찮습니다).
- 일반 PR 검사에서 확인하지 않는 플랫폼별 변경이 있는 경우입니다.
- MIR 마이그레이트 모드의 영향을 받을 수 있는 경우입니다.
rollup=never: 롤업에 절대 포함해서는 안 되는 PR입니다. 예시 상황:- 성능에 영향을 줄 수 있는 경우입니다.
- 원인이 불분명한 회귀를 일으킬 수 있는 경우입니다(롤업에서는 이 PR을 원인으로 찾아내기 어려우므로 이 PR을 특정하여 이분 탐색하고 싶어질 가능성이 높습니다).
- 실패할 가능성이 높은 경우입니다.
- 그 밖의 이유로 롤업하기에 위험한 경우입니다.
- 다음 항목을 지나치게 많이 건드리는 경우입니다.
- LLVM 또는 코드 생성
- bootstrap 또는 빌드 시스템
- build-manifest
rollup:rollup=always와 동일합니다.rollup-:rollup=maybe와 동일합니다.
rollup=iffy 또는 rollup=never를 설정할 때 그 이유가 분명하지 않다면 왜 이를 선택했는지 설명하는 짧은 이유를 승인 댓글에 포함하십시오.
우선순위
리뷰어는 우선순위를 설정하는 대신 위에 나열된 롤업 상태 중 하나를 설정하는 것이 좋습니다. Bors는 롤업 상태(never가 가장 높은 우선순위이고 always가 가장 낮은 우선순위입니다)와 PR의 경과 기간을 기준으로 자동으로 정렬합니다. 우선순위를 변경하는 경우에는 최선의 판단으로 다른 PR과의 공정성과 긴급성 사이에서 균형을 맞추십시오.
다음은 우선순위 설정에 관한 몇 가지 지침입니다.
- 1-5
- P-high 이슈 수정
- Toolstate 수정
- 위 항목을 포함하는 되돌리기(revert)
- beta로 상정된 PR
- 서브모듈/서브트리 업데이트
- 5+
- P-critical 치명적인 이슈 수정
- 10+
- 비트로트(bitrot)가 발생하기 쉬운 PR(특히 많은 파일을 수정하는 매우 큰 PR)
- 긴급한 PR(예: 긴급한 되돌리기)
- beta 백포트
- 20+
- 모든 롤업보다 먼저 처리되어야 하는 높은 우선순위
- 큐에 있는 다른 PR 때문에 다시 망가질 위험이 높은 부분을 수정하거나 변경하는 경우
- 1000
- 절대적으로 중요한 수정
- 릴리스 승격
우선순위를 설정할 때 그 이유가 명확하지 않다면, 이 우선순위를 선택한 이유를 설명하는 짧은 사유를 승인 코멘트에 포함하십시오.
r+에 대한 기대 사항
bors 권한은 이분법적입니다. 봇은 여러분이 어떤 코드에 익숙하고 어떤 코드에 익숙하지 않은지 알지 못합니다. 따라서 이 권한은 신중하게 사용해야 합니다. 잘 알지 못하는 코드에 r+를 하지 마십시오. 그러한 코드를 리뷰하는 것은 얼마든지 가능하지만, 최종 r+는 다른 사람에게 넘기도록 하십시오.
마찬가지로, 그 사람이 리뷰를 마쳤고, 리뷰 이후 코드가 크게 바뀌지 않았으며, 다른 기여자가 자신을 대신해 r= 명령을 사용해도 된다고 그 사람이 명시적으로 밝힌 경우가 아니라면 r=username 명령을 절대 발행(이슈)하지 마십시오.
리베이스는 괜찮으며 종종 필요하지만, 기능상의 변경은 일반적으로 다시 리뷰를 받아야 합니다. PR 작성자가 개별 리뷰 코멘트에 응답하는 것과 더불어 마지막 리뷰 이후 변경된 내용을 간단히 요약해 준다면 리뷰어에게 매우 도움이 됩니다.
봇 사용법은 bors 문서를 참고하십시오.
리뷰의 사회적 측면
무엇보다도 PR 작성자와 컴파일러 리뷰어 모두 행동 강령을 준수해야 합니다. 간단히 말해, 리뷰어는 변경 사항에 동의하지 않더라도 PR 작성자를 존중해야 합니다.
리뷰어는 PR 작성자의 관점에서도 사안을 고려하는 것이 권장됩니다. 변경 사항이 절차상의 이유나 리뷰어의 여력 부족으로 몇 달 동안 아무런 결론 없이(현재로서는 컴파일러가 그러한 변경을 받아들일 준비가 되어 있지 않을 수 있지만 그래도 PR 작성자의 기여에 감사를 표한다는 결론도 포함합니다) 멈춰 있고 병합 충돌이 계속 쌓인다면, 매우 좌절스러울 수 있습니다.
논의가 과열되고 있다면 모더레이션 팀에 개입을 요청하십시오.
보조 도구 정책
T-compiler의 임무는 제대로 작동하는 Rust 컴파일러(rustc)를 배포하는 데 필요한 일을 하는 것이지만, rustc는 한 번도 단독으로 동작한 적이 없습니다. 이 도구는 컴파일러 툴체인과 함께 제공되는 것이든 환경에 있을 것으로 기대되는 것이든, 사용하려면 언제나 추가 도구가 필요했습니다. 이 문서는 rustc 그리고 rustc와 함께 사용되거나 rustc가 필요로 하는 도구에 관한 몇 가지 기대 사항을 명확히 하고, 이미 암묵적으로 존재하는 일부 기대 사항을 명시하려는 시도입니다.
정의
- 컴파일 환경 - 제공 도구가 호출되는 호스트 시스템으로, 파일 시스템이나 환경 변수와 같은 일시적인 상태를 포함합니다.
- 호출 - 프로세스 생성, 라이브러리 로딩 또는 컴파일 환경이 지원하는 그 밖의 수단을 통해 임의의 산출물의 코드를 실행하는 것입니다.
- 중간 산출물 - 컴파일러 도구를 호출하여 출력되며, 다시 컴파일러 도구의 입력으로 제공될 산출물입니다.
- 컴파일러 도구 - 코드를 컴파일하고 링크하여 바이너리 코드 객체로 만드는 과정의 일부로 호출되는 산출물이며, 특히 Rust 툴체인의 일부로 배포되는 것을 가리킵니다.
- 바이너리 도구 - 바이너리 코드 객체를 생성하거나 편집하는 데 사용되는 컴파일러 도구입니다.
- 제공 도구 - T-compiler와 T-release의 지원을 받아 Rust 툴체인의 일부로 배포되는 바이너리 도구 또는 컴파일러 도구입니다. 여기에는 특히
rustc,rust-lld,rust-objcopy와 같은 바이너리 도구가 포함됩니다. - 보조 도구 - T-compiler와 T-release가 배포하지는 않지만 제공 도구와 함께 사용될 수 있는 컴파일러 도구입니다. 여기에는 특히
ld.bfd,link.exe,llvm-bolt, 그리고 “링커 스크립트”(Link Editor Command Language로 작성된 프로그램)와 같은 바이너리 도구가 포함됩니다.
보조 도구 사용 방식
rustc를 포함한 모든 제공 도구는 명시적인 사용자 입력을 요구하지 않고 보조 도구를 암묵적으로 호출할 수 있습니다. 제공 도구는 어떤 도구를 호출할지 결정하기 위해 컴파일 환경을 검사할 수 있습니다.1 보조 도구가 호출될 때 컴파일 환경이 관례적인 방식 및/또는 특유의 방식으로 구성되어 있다고 기대할 수 있습니다.
관례적 기대의 예는 다음과 같습니다.
link.exe라는 이름의 실행 파일이 인자로 주어진 경로에 있는 바이너리 코드 객체를 링크하는 작업을 수행하리라고 기대하는 것, 또는- Unix 도구가 POSIX에 명시된 방식으로 인자를 따르리라고 기대하는 것, 또는
$CC와 같은 환경 변수에 C 컴파일러의 경로가 들어 있으리라고 기대하는 것입니다.
특유의 기대는 관례적 기대와 비슷해 보이지만 이를 부당하게 적용한 것일 수 있습니다. 예를 들어 Unix 시스템이라면 macOS조차도 GNU 유저랜드를 갖춘 Linux 시스템과 비슷하다고 가정하는 경우입니다. 또는 순전히 새로 만들어 낸 기대일 수도 있습니다. 예를 들어 컴파일 환경의 데이터가 Rust 툴체인의 다른 제공 도구에 의해 생성되거나 수정되었다고 가정하는 경우입니다.
이러한 기대는 컴파일 환경의 내용이 사용하기에 안전한지, 올바르게 작동하는지, 이름이 올바르게 붙어 있는지 판단할 필요가 없다는 데까지 확장됩니다. 파일 시스템이 특정한 방식으로 구성되어 있다고 기대할 수 있으며, 파일에 붙은 이름은 무엇이든 올바른 레이블로 암묵적으로 신뢰할 수 있습니다. 보조 도구를 예상 경로에서 찾고, 우리가 어떤 도구에 어떤 방식으로 의존할 수 있는지 문서화하려는 합리적인 노력을 기울여야 하지만, 이것이 필수는 아닙니다. 환경에 있는 보조 도구를 계속 호출해야 할 의무조차 없습니다. 제공 도구는 T-release와 T-compiler의 재량에 따라 확장될 수 있으며, 환경에서 발견되는 보조 도구를 대체하려는 명시적인 목적으로 사용될 수 있기 때문입니다.
제공 도구가 보조 도구를 호출해야 하고 그 출력을 컴파일 과정의 다른 부분에서 중간 산출물로 사용해야 한다고 판단하면, 그 결과 출력은 해당 도구가 출력하는 형식을 엄격하게 따를 것으로 기대할 수 있습니다. 또한 이러한 출력이 다른 요구 사항도 충족하리라고 기대할 수 있습니다. 예를 들어 링크하거나 로드할 수 있는 바이너리 코드 객체, 나아가 실행할 수 있는 바이너리 코드 객체여야 하거나, 단순히 Rust 툴체인이 기대하는 추가 메타데이터 섹션을 보존해야 할 수 있습니다.
제공 도구에 대한 기대 사항이 하나도 위반되지 않았다고 가정하면, 제공 도구는 실행 가능하거나 동적으로 로드할 수 있는 바이너리 코드 객체를 생성할 때 적어도 해당 객체의 바이너리 형식 명세를 최소한으로 따르는 객체를 생성해야 합니다. 중간 산출물은 대개 바이너리 형식을 따르더라도 올바른 형식을 갖추지 않을 수 있습니다. 문서화가 부족한 바이너리 형식의 경우, 형식을 따른다는 것이 무엇을 뜻하는지는 T-compiler가 원하는 대로 해석할 수 있습니다.
컴파일러 팀에게 이는 대체로 환경에 있는 것을 원하는 대로 호출해도 된다는 뜻입니다. 예를 들어 소스에 정의되지 않은 동작이 없다고 가정하고, 출력을 올바르게 만드는 데 집중하십시오. 이상적으로는 디버거 등을 포함한 “네이티브” 툴체인이 우리 코드와 잘 작동할 만큼 관례를 충분히 따르는 것이 좋지만, 사람들이 사용하고 싶어 할 수 있는 도구의 목록은 끝이 없습니다.
보조 도구 이슈 트리아지
보조 도구와 관련된 이슈를 해결하기 전에, 우리 툴체인이 의존하는 전제가 “자명“하지 않다면 이를 문서화해야 합니다. 예를 들어 최소 도구 버전에 대한 의존성, 특히 그 버전이 운영 체제에 기본으로 포함된 버전보다 높은 경우가 이에 해당합니다. 자명하지 않은 전제만 문서화하는 이유는 이 문서 앞부분에 있는 장황한 정의의 정의 때문입니다. 아무것도 가정하지 않으면 컴파일러의 동작 방식을 설명하는 데 필요한 어휘를 정의하는 것만으로도 오랜 시간이 걸릴 수 있습니다.
누군가 컴파일러 플래그나 설정을 통해 보조 도구를 의도적으로 포함한 뒤 버그를 제보했다면, 보조 도구 없이도 오브젝트가 올바른 형식인지 확인해 보십시오. 그렇다면 대개 고치려 하지 말고 이슈를 닫아야 합니다
때로는 rustc가 암묵적으로 호출했기 때문에 보조 도구가 관여하기도 합니다. 툴체인이 기능적으로 이를 대체할 수 있는 도구를 제공한다면, 이슈를 해결하기 전에 그렇게 대체하는 것이 바람직합니다.
특정 운영 체제의 고유한 관례를 따르는 문제라면, 그 운영 체제를 위한 별도의 타깃이 있는 한 어느 정도 대응하려고 노력할 수 있습니다. 이를 위한 자원이 공개되어 있고 이용할 수 있다면, 대개 참여하려고 노력해야 합니다.
-
이는 운영 체제가 제공하는 방식으로 어떤 도구든 이름만으로 실행할 수 있거나 그렇게 하는 것이 바람직하다는 점에서 비롯되는 기능적 결과입니다. 이는 암묵적으로든 명시적으로든
$PATH를 뒤진다는 뜻이기 때문입니다. ↩
서드파티 및 트리 외부 크레이트 정책
이 정책은 컴파일러에서 사용할 트리 외부 크레이트를 만드는 방법과 컴파일러에서 서드파티 크레이트를 사용하는 방법에 관한 지침을 설명합니다.
트리 외부 크레이트
이 정책의 주요 목표 중 하나는 컴파일러에서 사용하는 트리 외부 크레이트(적어도 컴파일러 팀이 관리하며 rust-lang에 있는 크레이트)가 일관된 방식으로 구성되도록 하고, rust-lang/rust와 이들 크레이트 전반에서 동일한 경험을 제공하는 것입니다.
컴파일러의 일부를 언제 트리 외부 크레이트로 분리해야 합니까?
이는 컴파일러 팀 구성원의 재량에 맡기지만, 주간 트리아지 회의에서 안건으로 제기하거나 승인 결정을 활용해 비동기적으로 진행하는 방식으로 팀의 다른 구성원과 논의해야 합니다. 크레이트가 워킹 그룹의 산출물이라면, 트리 외부 크레이트가 적합하다는 데 워킹 그룹 내에서 이미 합의가 이루어져 있어야 합니다.
트리 외부 크레이트를 만들려고 할 때는 크레이트를 얼마나 범용적으로 만들지와, 크레이트가 널리 사용될 경우 늘어날 수 있는 유지 관리 부담 사이에서 균형을 잡는 것이 좋습니다.
컴파일러 크레이트는 어디에 두어야 합니까?
트리 외부 컴파일러 크레이트는 rust-lang 조직에서 호스팅해야 합니다. 이렇게 하면 외부 인프라 도구와의 통합이 간단해지고, GitHub의 기존 팀 권한도 그대로 이어받습니다. 모든 문서에서 컴파일러 팀과 관련 워킹 그룹이 해당 크레이트를 책임진다는 점을 분명히 밝혀야 합니다. 다른 조직이나 개인 저장소에서 프로토타입으로 시작하는 것은 권장하지 않습니다.
개인 계정이나 다른 조직에 있는 기존 트리 외부 크레이트를 이전할 수 있습니까?
네, 이전을 권장합니다. 이전하려면 팀과 논의하고 저장소 이전에 관한 GitHub 문서를 숙지한 뒤 이전을 진행하십시오. 이전이 완료되면 원래 계정이나 조직에 리디렉션이 남게 되며, 이 리디렉션은 이전된 저장소를 새로 포크할 때 그 이름과 충돌합니다. 이 문제를 해결하려면 GitHub에 이메일을 보내야 합니다.
이 크레이트들은 누가 소유합니까?
새 버전이 게시되고 풀 리퀘스트가 리뷰되도록 책임지는 소유자가 분명히 있도록, 컴파일러 팀 구성원이나 워킹 그룹이 크레이트에 대해 느슨한 소유권을 갖는 것이 바람직합니다.
때로는 프로젝트 구성원이 아닌 사람이 크레이트의 주 소유자이고, 주 메인테이너와 더 이상 연락이 닿지 않을 경우에 대비해 컴파일러 팀이 “예비” 메인테이너로 추가되는 경우가 있습니다. 이러한 방식은 권장하지 않지만 사안에 따라 정당화될 수 있습니다.
이 크레이트들의 이름은 어떻게 지어야 합니까?
크레이트 이름은 새 트리 외부 크레이트를 컴파일러 팀에 제안할 때 논의합니다.
크레이트 이름은 사안에 따라 다르게 정합니다. 본질적으로 컴파일러와 밀접하게 연결된 크레이트는 rustc_ 접두사가 붙은 이름을 쓰는 것이 좋습니다. 이는 잠재적 사용자에게 해당 크레이트가 얼마나 stable한지를 나타내는 지표입니다. 더 범용적인 그 밖의 크레이트에는 컴파일러와 무관한 이름을 붙입니다.
트리 외부 크레이트의 리뷰 정책에 제한 사항이 있습니까?
일반적으로 크레이트를 주로 맡는 워킹 그룹과 팀 구성원은 자신들의 그룹에 가장 적합한 방식으로 크레이트를 자유롭게 유지 관리할 수 있지만, 컴파일러와 트리 외부 크레이트 전반에 어느 정도 일관성이 있도록 몇 가지 제한 사항이 있습니다:
rust-lang/rust에 r+ 권한이 있는 사람은 누구나 PR을 리뷰하고 승인할 수 있어야 합니다.- 가능하다면 크레이트(또는 관련 워킹 그룹)에 적극적으로 참여하는 사람만 해당 크레이트의 리뷰 로테이션에 포함하면 됩니다.
- 워킹 그룹이나 크레이트 유지 관리에 적극적으로 참여하고 있다면, Rust 전체에 대한 r+ 권한이 없는 사람을 크레이트의 추가 리뷰어로 두어도 괜찮습니다.
- 중요한 풀 리퀘스트는 여러 리뷰어가 리뷰해야 합니다.
트리 외부 크레이트에는 어떤 요건이 있습니까?
트리 외부 크레이트는 반드시 다음 요건을 충족해야 합니다.
- 컴파일러 팀이 유지 관리하는 경우, 달리 해야 할 타당한 이유가 없다면 (컴파일러와 마찬가지로) Apache 2.0과 MIT 이중 라이선스를 적용해야 합니다.
- 다른 라이선스를 원한다면 새 크레이트를 제안할 때 이 점을 제기하여 컴파일러 팀 구성원의 동의를 받아야 합니다. 달리 요구되는 경우(다른 프로젝트의 코드를 이식하는 경우 등)가 아니라면 tidy가 허용하는 라이선스를 우선적으로 사용하십시오.
- Rust의 행동 강령을 준수해야 합니다.
- 해당 크레이트를 Rust 컴파일러 팀과 관련 워킹 그룹이 유지 관리한다는 사실을 명시해야 합니다.
- 특히 잠재적 사용자를 위해 예상되는 유지 관리 수준과 안정성을 자세히 기술해야 합니다.
- 또한 이 저장소에 있는 워킹 그룹 세부 정보로 연결되는 링크도 포함해야 합니다.
- 이 페이지 하단의 목록에 추가되어야 합니다.
- 유의적 버전(semantic versioning)을 따라야 합니다.
- GitHub 병합 큐와
@triagebot을 사용해야 합니다. - 기존 트리아지 절차와 호환되는 레이블을 사용해야 합니다. 이렇게 하면 트리 외부 크레이트에서 상정된 이슈를 트리아지 회의에서 논의할 수 있습니다.
- 예:
T-compiler,I-compiler-nominated(전체 목록은 추후 결정됩니다)
- 예:
- 가능하다면 신뢰할 수 있는 게시(trusted publishing)를 사용해야 합니다.
- 컴파일러 팀을 crates.io에서 해당 크레이트의 소유자로 추가해야 합니다.
트리 외부 크레이트를 위한 커뮤니티 인프라가 필요합니까?
트리 외부 크레이트를 위해 커뮤니티 인프라(Zulip 서버/스트림 등)를 만들어야 한다는 요건은 없습니다. 트리 외부 크레이트가 기여자와 사용자로 이루어진 큰 커뮤니티를 얻게 되면 이러한 인프라가 바람직할 수 있지만, 그렇지 않은 경우에는 처음에는 워킹 그룹이나 컴파일러 팀 스트림을 사용해야 합니다.
요청하면 주 Rust Zulip 서버에 이슈와 PR로 자동 링크하는 링크 변환기를 추가할 수 있습니다.
트리 외부 크레이트 작업에 관한 안내가 있습니까?
rustc-dev-guide의 외부 저장소 사용하기를 참고하십시오.
트리 외부 크레이트에서 안정화나 의미 변경은 어떻게 처리해야 합니까?
트리 외부 크레이트의 변경 사항이 언어의 안정화나 의미 변경으로 이어진다면 언어 팀을 참여시키는 것이 중요합니다. rust-lang/rust에 대한 PR에 포함된 서브모듈 변경 사항에는 해당 변경이 rust-lang/rust에 직접 구현된 것과 마찬가지로 적절한 레이블(예: relnotes, t-compiler, t-lang)을 붙여야 하며, 컴파일러나 트리 외부 크레이트에 익숙하지 않은 사람에게 변경 내용이 명확하지 않은 경우 이에 대한 설명을 포함해야 합니다.
트리 외부 크레이트의 릴리스는 언제 해야 합니까?
트리 외부 크레이트에 패치를 병합할 권한이 있는 팀 구성원이라면 누구나 적절하다고 판단할 때 새 릴리스를 만드는 풀 리퀘스트를 생성할 수 있으며, (다른 모든 풀 리퀘스트와 마찬가지로) 다른 팀 구성원의 리뷰를 받기만 하면 됩니다. 새 릴리스는 테스트를 통과하고 새 릴리스를 낼 만한 이유(예: 의존성 업그레이드, 새 기능, 버그 수정 등)가 있는 크레이트 버전이어야 합니다.
저장소에 신뢰할 수 있는 게시가 설정되어 있다면 crates.io에 자동으로 게시되어야 하며, 그렇지 않으면 팀 구성원이 crates.io에 수동으로 게시할 수 있습니다(이를 위한 권한이 있어야 합니다).
요약하면, 트리 외부 크레이트를 만드는 절차는 다음과 같습니다.
- 필요한 경우 워킹 그룹 내에서 트리 외부 크레이트의 필요성을 논의하고 확인하십시오.
- 이 문서를 수정하여 아래 목록에 해당 크레이트를 추가하는 PR을 생성하십시오.
@rfcbot merge를 사용하여 컴파일러 팀 구성원들의 동의를 얻으십시오. - teams 저장소를 사용하여
rust-lang조직에 새 저장소를 생성하십시오.-
팀이 적절한 권한을 갖도록
access.teams.compiler키가'maintain'으로 설정되어 있는지 확인하십시오. -
크레이트의 의도된 목적, 담당 팀과 워킹 그룹(이 저장소에 있는 해당 페이지로 링크), 그리고 의도된 유지 관리 및 안정성 수준을 설명하는 README를 추가하십시오.
이 크레이트는
rustc내부에서 사용하기 위해 Rust 컴파일러 팀이 개발하고 유지 관리하며, 특히.template작업 영역이 이를 담당합니다. 이 크레이트는 호환성을 깨는 변경이 정기적으로 발생하며 안정성을 보장하지 않습니다 | stable 상태를 유지하고 호환성을 깨는 변경을 최소화하는 것을 목표로 합니다. -
rust-lang/rust에 있는 LICENSE-APACHE 및 LICENSE-MIT 라이선스 파일을 포함하십시오. -
rust-lang/rust의 CODE_OF_CONDUCT 파일을 포함하거나 링크하십시오. -
적절한
.gitignore를 생성하십시오(합리적인 기본값은 여기에 있습니다). -
P-high,P-medium,P-low,I-compiler-nominated및T-compiler레이블을 생성하십시오.
-
- rustc와 통합하기 전에 필요한 초기 개발을 수행하십시오.
- 시맨틱 버저닝에 따라 초기 버전을 게시하십시오.
- 적절한 트리 내부 크레이트에 해당 크레이트를 의존성으로 추가하고 사용을 시작하십시오.
서드파티 크레이트
컴파일러에서 기존 서드파티 크레이트의 기능을 사용하는 것이 바람직할 때가 있습니다.
서드파티 크레이트는 언제 컴파일러 의존성으로 추가할 수 있습니까?
컴파일러에 포함되는 서드파티 크레이트는 잘 유지 관리되고 있어야 하며, 가능하다면 컴파일러 팀 구성원이 메인테이너로 추가되는 것이 바람직합니다. 이 결정을 내리기 전에 컴파일러 팀의 다른 구성원들과 상의해야 합니다.
트리 외부 크레이트의 서드파티 의존성은 어떻습니까?
컴파일러에서 사용되며 compiler-team이 유지 관리하는 모든 크레이트에도 동일한 정책이 적용됩니다.
트리 외부 크레이트 목록
이 섹션에는 컴파일러 팀이 유지 관리하는 기존 트리 외부 크레이트 목록이 있습니다:
우선순위 지정
컴파일러 팀이 우선순위가 높은 이슈를 빠르게 파악할 수 있어야 합니다. 그래서 아래에서 설명하는 우선순위 지정 절차를 마련했습니다.
일반 절차
- 이슈의 현재 상태를 확인하십시오
- 가능하다면 이슈를 진전시켜 보십시오(예: 이슈 작성자나 리뷰어에게 진행 상황을 요청하십시오)
- 이슈에 대한 MCVE가 이미 있습니까?
- 회귀인지 확인하고 알맞은 레이블(
regression-*레이블)을 붙이십시오 - 이슈가 속한 영역을 파악하고 알맞은 레이블(
A-*레이블)을 붙이십시오 - 알림 그룹에 핑을 보내거나 관련 팀을 호출하십시오
- 가능하다면 담당자를 지정하십시오
이슈의 우선순위 지정 요청하기
일반적으로 회귀 이슈와 건전성 문제(unsound) 및 잘못된 컴파일(miscompile) 이슈에 우선순위를 지정합니다.
누구든지 GitHub 댓글에 다음 명령을 남겨 이슈의 우선순위 지정을 요청할 수 있습니다:
@rustbot prioritize
또는 팀 구성원이라면 I-prioritize 레이블을 붙이기만 하면 됩니다.
저장소에서 이슈 우선순위 지정을 활성화하는 방법은 여기에서 자세히 확인하십시오.
이슈에 우선순위 지정하기
우선순위를 지정하려면 I-prioritize 레이블을 P-critical, P-high, P-medium, P-low 중 하나로 교체하고, 그 근거에 대한 간결한 댓글을 추가하거나 이슈 우선순위 지정이 이루어진 Zulip 논의를 링크하십시오. 예시는 다음과 같습니다:
우선순위를 지정합니다(Zulip에서 논의).
@rustbot label -I-prioritize +P-XXX
팁: Github Saved Replies를 사용하면 템플릿 댓글을 만들어 둘 수 있습니다.
Zulip에서도 우선순위를 지정할 수 있습니다:
@**triagebot** assign-prio <issue #> [ critical | high | medium | low | <empty>]
예시:
- 이슈 123456에 높은 우선순위 지정:
@**triagebot** assign-prio 123456 high - 이슈 123456의 우선순위 제거:
@**triagebot** assign-prio 123456
우선순위 수준
컴파일러 팀의 자원은 한정되어 있으므로, 우선순위 지정의 주된 목표는 작업할 가치가 가장 큰 이슈를 가려내어 컴파일러 팀이 가장 중요한 일에 집중할 수 있도록 하는 것입니다.
레이블
이슈에 I-prioritize 레이블을 붙이면 우선순위 지정 절차가 시작되며, 이 절차는 I-prioritize 레이블을 제거하고 아래에서 설명할 레이블 4개 중 하나를 붙이는 것으로 끝납니다:
P-criticalP-highP-mediumP-low
각 레이블은 다음 사항에 대해 팀이 취할 전략을 정의합니다.
- 해당 이슈가 받을 관심의 정도
- 커뮤니티 구성원이 참여할 수 있는 방법
P-critical
P-critical은 컴파일러 릴리스를 막을 가능성이 있는 이슈입니다(즉, 새 컴파일러 릴리스 전에 해결할 것을 강력히 권장하는 이슈입니다). 이러한 이슈는 매주 열리는 컴파일러 팀의 트리아지 회의에서 다루어집니다.
일반적으로 “critical” 버그로 판단하는 사례는 다음과 같습니다.
- 이전에 컴파일되던 코드가 더 이상 컴파일되지 않는 회귀
- 우선순위를 낮출 수 있는 완화 조건:
- 해당 코드가 애초에 컴파일되지 않았어야 하는 경우(다만 회귀가 많은 크레이트에 영향을 준다면 경고 기간이 필요하다는 신호일 수 있습니다)
- 해당 코드가 이론적인 것에 불과해 실제로 존재할 가능성이 낮다고 판단되는 경우, 또는 널리 쓰이지 않고 관리되지 않는 소규모 패키지에만 존재하는 경우
- 회귀가 한두 릴리스 동안 stable에 남아 있었다면(아직 수정을 기다리고 있기 때문이든, 버그가 잠복해 있어 발견되지 않았기 때문이든) 우리는 보통 우선순위도 낮춥니다. 그때까지 사용자들이 그 회귀에 대해 크게 문제를 제기하지 않았다면 그것은 본질적으로 치명적인 이슈가 아니라는 신호이기 때문입니다.
- 우선순위를 낮출 수 있는 완화 조건:
- 코드가 여전히 컴파일되지만 이전과 다르게 동작하는 회귀(동적 의미론이 변경된 경우)
- 우선순위를 낮출 수 있는 완화 조건:
- 코드가 명시적으로 규정되지 않은 기능을 사용하는 경우(예:
std::vec::Vec문서에는 요소를 drop하는 순서가 변경될 수 있다고 명시되어 있습니다)
- 코드가 명시적으로 규정되지 않은 기능을 사용하는 경우(예:
- 우선순위를 낮출 수 있는 완화 조건:
- 기능 게이트 없이 접근할 수 있는, 기능 게이트로 보호된 기능
- 우선순위를 낮출 수 있는 완화 조건:
- 해당 패턴이 나타날 가능성이 매우 낮은 경우
- 우선순위를 낮출 수 있는 완화 조건:
- 실제 영향이 있는 건전성 결함
- 우선순위를 낮출 수 있는 완화 조건:
- 유발하기 어려운 건전성 결함
- stable에 영향을 주지 않는 건전성 결함(예: 해당 결함이 게이트로 보호된 불안정 기능을 사용하는 경우).
- 우선순위를 낮출 수 있는 완화 조건:
- 진단이 매우 흔하게 발생하고 상황이 매우 혼란스러운 진단 회귀
- 흔한 시나리오나 코드 패턴에서 발생하는 ICE
- 우선순위를 낮출 수 있는 완화 조건:
- ICE를 유발하는 코드가 컴파일 오류도 유발하고, 그 오류가 ICE보다 먼저 출력되는 경우
- 해당 코드가 불안정 기능을 사용하는 경우, 특히 ICE가 기능 게이트를 필요로 하는 경우
- 우선순위를 낮출 수 있는 완화 조건:
P-critical 이슈는 가장 많은 관심을 받습니다. 이 이슈는 가능한 한 빨리 한 명 이상의 담당자에게 배정되어야 하며, 나머지 팀원은 필요한 경우 최선을 다해 이들을 도와야 합니다.
P-high
P-high 이슈는 컴파일러 팀의 관심이 필요하지만, 매 회의에서 논의할 정도는 아닌 이슈입니다. 이러한 이슈는 위에서 정의한 완화 조건이 있는 P-critical 이슈일 수도 있고, 릴리스를 막는 이슈로 간주되지는 않지만 중요한 이슈일 수도 있습니다.
모든 컴파일러 회의에서 다루기에는 P-high 이슈가 너무 많으므로, 이러한 이슈가 진척될 수 있도록 팀의 우선순위 지정 작업을 통해 비동기적으로 처리하는 것이 좋습니다. 필요하다고 판단되는 경우에는 여전히 가끔 회의에서 다룰 수 있습니다.
팀의 우선순위 지정이 얼마나 효과적인지는 P-critical 이슈와 P-high 이슈 사이에 선을 긋는 능력에 직접적으로 달려 있습니다. 컴파일러 회의를 감당할 수 없을 만큼 P-critical 이슈가 많아서는 안 되지만, 그렇다고 치명적인 이슈가 P-high 이슈 목록 속에 묻혀서도 안 됩니다.
P-high 이슈는 팀이 주로 작업하게 될 이슈입니다. 이러한 이슈에는 반드시 담당자가 배정되도록 하고, 계속 주시하고자 합니다. 컴파일러 팀은 이러한 이슈를 정기적으로 일괄 검토하며, 이 과정에서 우선순위를 낮출지 결정합니다.
P-medium 및 P-low
P-medium은 팀의 우선순위는 아니지만 장기적으로 해결될 이슈를 가리킵니다. 예를 들어, 특정 기능이 반영된 후에 수정될 이슈가 이에 해당합니다. 이러한 이슈는 수정에 관심이 있는 사람을 팀이 멘토링할 수 있는 이슈입니다. 이러한 이슈는 누군가 문제를 제기하거나, 커뮤니티 구성원이 수정하거나, 우연히 수정될 때까지 이 상태로 남아 있습니다.
P-low는 컴파일러 팀이 해결할 계획은 없지만 그래도 수정할 가치가 있는 이슈를 가리킵니다. 이슈가 불분명하여 논의가 필요한 경우에는 해당 이슈를 상정하십시오.
컴파일러 트리아지
컴파일러 팀은 매주 목요일 Zulip에서 모여 트리아지를 진행하고 다른 주제에 대해서도 이야기를 나눕니다. 누구에게나 열려 있으니 자유롭게 참여하십시오.
트리아지 회의 안건은 우선순위 지정 작업의 결과를 입력으로 사용하여 생성되며, 그 방법은 여기에서 확인하십시오.
작업 영역
컴파일러 팀에서 진행 중인 작업과 이니셔티브의 상당 부분은 특정 작업 영역에 관심 있는 사람들로 구성된 그룹이 수행합니다. 이러한 그룹은 한 영역에 집중된 작업을 꾸준히 제공하고 도움과 조언을 구할 수 있는 전용 채널을 갖추고 있으므로, 새로운 기여자가 참여하기에 좋은 방법입니다. 현재 작업이 진행 중인 영역의 목록은 다음과 같습니다.
| 이름 | 간단한 설명 | 코드 | Zulip 스트림 |
|---|---|---|---|
| Async-await 구현 | async-await 구현 | 링크 | #wg-async |
| 진단 | 진단 렌더링에 crates.io 크레이트를 사용하고 진단 출력을 더 보기 좋게 개선합니다. | rustc_errors, rustc_lint, annotate-snippets | #t-compiler/diagnostics |
| LLVM | LLVM 업스트림과 협력하여 LLVM 개발 과정에서 Rust의 입장을 대변합니다 | rustc, LLVM | #t-compiler/llvm |
| MIR 최적화 | MIR 최적화를 작성하고 MIR을 최적화하기 쉬운 형태로 리팩터링합니다. | MIR 변환 | #t-compiler/mir-opts |
| Parallel-rustc | 병렬 컴파일을 rustc의 기본값으로 만듭니다 | rustc | #t-compiler/parallel-rustc |
| Polonius | “NLL 2.0“과 유사한 “Polonius 분석”을 rustc에 통합하는 방안을 탐구합니다 | 대여 검사, rust-lang/polonius, rust-lang/datafrog | #t-types/polonius |
| RLS 2.0 | IDE에 맞춘 새로운 컴파일러 아키텍처를 실험합니다 | rust-analyzer | #t-compiler/rust-analyzer |
| Rust 컴파일러 개발 가이드 | rustc-dev-guide를 “완전“하게 만들어 컴파일러를 더 쉽게 배울 수 있도록 합니다 | rustc, rustc-dev-guide | #t-compiler/rustc-dev-guide |
| Enzyme | GPU 오프로딩을 위한 실험적 LLVM 기능을 제공합니다 | 프로젝트 목표 | #wg-autodiff |
| 링커 | 링커 관련 이슈를 처리하고 링커를 컴파일러에 통합하는 작업을 지원합니다 | - | #wg-linker |
기록 보존을 위해, 목표를 달성했거나 작업이 중단되어 더 이상 활동하지 않는 워킹 그룹 목록을 아래에 남깁니다:
| 이름 | 간단한 설명 | Zulip 스트림 |
|---|---|---|
| Meta | 컴파일러 팀의 조직 운영 방식을 다룹니다 | #z-archived-t-compiler/wg-meta |
| Non-Lexical Lifetimes (NLL) | 비어휘적 수명(non-lexical lifetimes)을 구현합니다 | #z-archived-t-compiler/wg-nll |
| Polymorphization | 코드 생성 중에 함수가 다형적인 상태로 남을 수 있는 경우를 감지하는 분석을 구현합니다. | #z-archived-t-compiler/wg-polymorphization |
| Prioritization | 회귀를 트리아지하며, 주로 릴리스를 막을 가능성이 있는 문제인지 판단합니다 | #t-compiler/prioritization |
| 프로파일 기반 최적화 | rustc를 위한 프로파일 기반 최적화 구현 | #z-archived-t-compiler/wg-profile-guided-optimization |
| RFC 2229 | 클로저가 복합 변수 전체가 아니라 변수의 개별 필드를 캡처하도록 합니다 | #z-archived-t-compiler/wg-rfc-2229 |
| Rustc 파이프라이닝 | Cargo가 rustc를 파이프라인 방식으로 호출할 수 있도록 하여 크레이트 그래프 컴파일 속도를 높입니다. | #z-archived-t-compiler/wg-pipelining |
| 셀프 프로파일 | -Z self-profile 기능 개선 | #z-archived-t-compiler/wg-self-profile |
| 트레이트 | 트레이트 시스템 설계 및 구현 개선 | #z-archived-t-compiler/wg-traits |
rustc-dev-guide
rustc-dev-guide 팀은 컴파일러 팀의 하위 팀으로, rustc-dev-guide(rust-lang/rustc-dev-guide에 있음)의 유지 관리를 담당합니다.
팀의 책임
- 가이드의 상태를 트리아지하여 오래되었거나 누락된 정보를 찾습니다
- 가이드 페이지의 기타 편집 작업을 하거나 시간이 지나 깨진 링크를 수정합니다
- 도메인별 전문 지식이 필요하지 않은 가이드의 간단한 PR을 리뷰합니다
- 도메인별 문서 변경을 해당 도메인 전문가 리뷰어와 연결합니다
- 메인 rust 저장소와 rustc-dev-guide 저장소 간의 서브트리 동기화를 수행합니다
팀 구성원 자격
-
꾸준히 기여한 사람은 팀 리드의 초대를 받아 팀에 합류할 수 있습니다.
이 요건은 이미 컴파일러 팀의 구성원인 사람에게는 적용되지 않는다는 점에 유의하십시오. 이들은 자신의 변경 사항을 직접 병합할 수 있으므로 암묵적으로 가이드의 내용을 소유하기 때문입니다. 이들에게 구성원 자격은 가이드에 특별한 애정을 더 쏟겠다는 의사를 나타내는 신호에 가깝습니다. 또한 컴파일러 팀 구성원에 나와 있듯이, 컴파일러 팀 구성원이라는 것은 이 요건을 이미 충족했음을 의미한다는 점에도 유의하십시오.
-
6개월 동안 활동하지 않으면 구성원은 전임 구성원으로 전환됩니다
- 전환하기 전에 먼저 이를 알리는 것이 예의입니다
팀 리더십
- 리드는 새 구성원을 승인합니다
- 어떤 구성원도 이의를 제기하지 않아야 합니다
- 리드는 메인 rust 저장소에서 서브트리 동기화를 승인할 수 있습니다
- 팀 리드는 정해진 절차 없이 초대를 통해 임명됩니다
리뷰 정책
개발 가이드는 컴파일러 자체에 비해 변경 사항이 병합되기 위한 기준이 훨씬 낮습니다. 문서가 없는 것보다는 불완전하거나 작성 중인(WIP) 문서라도 있는 것이 낫습니다. 장의 하위 부분에서 빠진 내용을 추적하는 이슈와 함께 TODO를 뼈대로 남겨 두면, 자칫 독자를 혼란스럽게 할 수 있는 문서의 알려진 공백을 표시하는 데 유용합니다.
문서화하는 영역에 자신이 있는 기여자는 리뷰 없이 자신의 문서를 직접 병합해도 됩니다. 이러한 이유로 개발 가이드는 각 PR에 리뷰어를 자동으로 배정하지 않으며, 대신 PR 작성자가 필요하다고 판단하면 직접 PR 리뷰어를 요청할 수 있습니다.
리뷰어는 r? rustc-dev-guide 또는 r? @username이 포함된 코멘트를 작성하여 배정할 수 있습니다.
자신의 rustc-dev-guide PR을 직접 병합해도 괜찮을지 판단하는 좋은 경험 법칙은, 컴파일러의 해당 영역을 건드리는 복잡한 PR을 승인하는 것이 편하게 느껴지는지를 따져 보는 것입니다. 컴파일러 리뷰 정책을 참고하십시오.
rustc-dev-guide 변경 사항을 기여할 곳
변경 사항이 rustc-dev-guide의 문서 내용만 다루고 rust-lang/rust 코드 변경을 수반하지 않는다면, 변경 사항과 PR을 rust-lang/rustc-dev-guide 저장소에 직접 제출하십시오.
이 규칙을 따르면 다음과 같은 이점이 있습니다.
rustc-dev-guide저장소의 변경 사항은 게시된 rustc-dev-guide에 즉시 반영될 수 있습니다.rustc-dev-guide저장소의 변경 사항은rust-lang/rust의 bors CI를 거치지 않아도 됩니다.rust-lang/rust의 bors 큐 부담이 줄어듭니다.
서브트리 동기화
개발 가이드는 메인 rust-lang/rust 저장소의 josh 서브트리입니다. 이를 통해 컴파일러 기여자는 컴파일러 자체의 변경과 함께 개발 가이드의 문서를 더 쉽게 업데이트할 수 있습니다.
이 때문에 개발 가이드에 대한 모든 변경 사항을 개발 가이드 자체 저장소와 rust-lang/rust 저장소에서 유지 관리되는 사본 사이에서 수동으로 동기화하는 절차가 필요합니다.
- 마지막 서브트리 동기화 이후 최소 일주일을 기다리십시오.
- 자동으로 열린 rustc-pull PR이 있거나, 병합 충돌이 있는 경우에는 수동 rustc-pull이 필요합니다. 열려 있는 rustc-pull PR이 있으면 이를 병합하고1, 그렇지 않으면 수동 rustc-pull을 수행해야 합니다. rustc-pull의 예시는 rustc-dev-guide#2451을 참조하십시오.
- 동기화를 진행 중이라는 사실을 서브트리 동기화 조율용 Zulip 채널에 게시하십시오.
- 수동으로 rustc-push를 수행하고 PR을 여십시오. PR은 rustc-dev-guide 리드(현재 @BoxyUwU 또는 @jieyouxu)에게 배정해야 합니다. 저희는 다른 일부 팀처럼 직접 승인하는 대신 rustc-push PR을 다른 사람에게 배정하는 편입니다. 서브트리를 처음 설정할 때 몇 차례 사고가 있었기 때문에, 누군가 확인하지 않고 진행하는 것이 불안하게 느껴졌기 때문입니다.
- 서브트리 동기화가 수행되었음을 모두가 알 수 있도록 rustc-push PR을 서브트리 동기화 조율용 Zulip 채널에 게시하십시오. rustc-push의 예시는 rust-lang/rust#141962를 참조하십시오.
rustc-pull 또는 rustc-push를 수행하는 방법에 대한 문서는 개발 가이드의 README에서 찾을 수 있습니다.
-
자동으로 열린 rustc-pull에 개발 가이드에 대한 실제 변경 사항이 없다면, 동기화할 실제 변경 사항이 생길 때까지 서브트리 동기화를 미룰 수 있습니다. ↩
crates.io
crates.io는 Rust 생태계의 크레이트를 호스팅합니다.
외부 링크
- 소스 코드:
rust-lang/crates.io - 호스팅 주소: https://crates.io/
- 메인테이너: crates.io 팀
- 운영 가이드(팀 구성원만 열람 가능)
docs.rs
docs.rs는 crates.io에 게시된 크레이트의 문서를 호스팅하는 웹사이트입니다.
외부 링크
- 소스 코드: rust-lang/docs.rs
- 호스팅 위치:
docsrs.infra.rust-lang.org(bastion 뒤에 있음 – 접속 방법) - 메인테이너: docs.rs 팀
- 개발 가이드
Rustdoc
Rust의 rustdoc 팀 구성원은 Rustdoc 도구를 유지 관리하고, 성능을 개선하며, rustdoc 기능의 안정화를 검토하는 일을 맡습니다.
저희는 Forge를 사용해 팀의 프로세스, 정책, 업무 방식을 문서화합니다. 컴파일러와 rustdoc이 어떻게 동작하는지, 그리고 개발 환경을 설정하는 방법을 알아보려면 rustc-dev-guide를 참고하십시오.
캘린더
Rustdoc 팀의 캘린더는 Rust 프로젝트의 rust-lang/calendar 저장소에서 확인할 수 있습니다.
회의
rustdoc 팀 회의 일정은 여기에서 확인하실 수 있습니다.
참석하려는 분들에게 시간과 안건을 알리기 위해 한 달 전에 새 스레드가 열립니다.
누구나 회의에 참여할 수 있습니다.
안건에 항목을 추가하고 싶지만 rustdoc 팀 구성원이 아니라면, 다음 회의를 위한 Zulip 스레드에 논의하고 싶은 항목에 대해 댓글을 남겨 주십시오. rustdoc 팀 구성원이 이를 검토하여 해당 항목을 안건에 추가할 수 있는지와 그 우선순위를 결정합니다. 해당 항목이 이미 논의된 적이 있다면, 팀 구성원이 설명을 제공하거나 이전 논의가 이루어진 곳의 링크를 제공합니다.
구성원 자격
이 섹션에서는 rustdoc 팀의 구성원 자격을 다룹니다.
구성원이 되는 길
rustdoc 도구에 기여하려는 사람은 일반적으로 버그 수정이나 새 기능 구현부터 시작합니다. 안내나 도움이 필요하시면 주저하지 말고 Zulip의 t-rustdoc 채널에 질문하십시오!
Rustdoc 구성원
한동안 꾸준히 기여해 온 사람은 rustdoc 팀 구성원으로 승격될 수 있습니다(아래의 결정 방식 섹션을 참고하십시오).
이러한 승격이 적절한 시점을 판단하는 정확한 조건을 정의하기는 어렵습니다. 구성원으로 승격되는 것은 단순히 여러 항목을 충족하는지 확인하는 문제가 아닙니다. 다만 일반적으로는 다음 세 가지를 보여 주었을 때 준비가 되었다고 봅니다.
- “지속력” – 어떤 방식으로든 꾸준히 기여하고 있어야 합니다. 예를 들어 몇 개의 프로젝트를 완료했다는 의미일 수 있습니다.
- “독립성과 숙련도” – 적어도 자신의 “rustdoc 영역” 범위 안에서는 작업을 맡을 때 어느 정도 독립적으로 행동해야 합니다. 간단한 PR에 대해서는 다른 사람을 멘토링할 수 있을 만한 역량이 있어야 합니다.
- “친화성” – rustdoc 팀 구성원은 Rust 조직의 일원이 되며, 행동 강령과 관련하여 더 높은 기준을 요구받습니다. 행동 강령의 문구뿐만 아니라 그 정신도 준수해야 합니다.
구성원으로 승격되면 여러 권한이 주어집니다.
-
구성원은
r+(풀 리퀘스트 승인) 권한을 가지며 리뷰를 할 수 있습니다(앞서 설명한 대로 이 권한을 적절히 사용해야 합니다). 또한 perf/rustc-timer 및 기타 유사한 봇을 제어할 수 있는 권한도 가집니다.bors와r+에 대한 문서는 여기를 참고하십시오.팁: bors 권한에 관한 몇 가지 기본 규칙은 다음과 같습니다. 먼저 악성 코드 여부를 확인하지 않았다면
try빌드를 하지 말고, 해당 코드를 효과적으로 리뷰할 수 있다는 합리적인 확신이 없다면r+를 하지 마십시오. -
rustdoc 팀 구성원은 Rust 조직의 구성원이므로 레이블을 수정하고 이슈에 할당될 수 있습니다.
-
구성원은 GitHub의
rust-lang/rustdoc팀에 소속되므로, 누군가 팀 전체를 호출할 때 핑을 받습니다. -
구성원은 rust-lang.org 웹 페이지에 등재됩니다.
또한 몇 가지 의무도 따릅니다(경우에 따라서는 선택적인 의무입니다).
- 구성원은 FCP에 최대 4주(28일) 이내에 응답해야 합니다.
- 구성원은 팀을 돕기 위해 그 밖의 다양한 메인테이너 활동에 참여할 수 있습니다.
- 구성원은 행동 강령과 관련하여 일반인보다 더 높은 기준을 요구받습니다.
rustdoc 구성원이 된다는 것의 의미
rustdoc 팀의 구성원이 되면 여러 가지 일이 일어납니다.
- 내부 논의가 이루어지는 비공개 Zulip 스트림에 접근할 수 있게 됩니다.
- 또한
all@rust-lang.org및rustdoc@rust-lang.org메일링 리스트에도 구독됩니다. 메일링 리스트 구독이 어떻게 이루어지는지는 이 파일을 참고하십시오. 두 메일링 리스트 모두 발송량이 매우 적습니다(1년에 몇 통 정도입니다).all@rust-lang.org에 관하여: 이 주소는 모든 기여자에게 소식을 전하기 위한 수단입니다. 이 주소로 스팸을 보내지 않습니다.
rustdoc 팀의 역할
rustdoc에는 여러 영역이 있으며, 팀 구성원이 모든 영역에서 작업하는 것은 아닙니다. 현재 주요 영역은 다음 세 가지입니다:
- 프런트엔드: 검색, 프런트엔드 기능, 프런트엔드 설정을 포함하여 HTML/CSS/JS 및 (HTML) UI/UX 변경과 관련된 모든 것입니다.
- JSON 백엔드:
rustdoc-json-types크레이트를 포함하여--output-format=json백엔드에 대한 작업입니다. - 내부 구조: rustdoc의 내부 구조로, 컴파일러와의 상호 작용, doctest, rustdoc 내부 코드 표현 생성, 명령줄 인수 파싱, 린트, intra-doc 링크 등입니다.
이 그룹들은 완전한 팀이 아니므로, 이 그룹 중 어느 곳에 속하려면 rustdoc 팀의 구성원이어야 합니다.
현재로서는 프런트엔드 그룹만 팀 저장소에서 공식적인 지위를 가지며, rustdoc-frontend라고 불립니다. 이 그룹에 참여하고 싶은 rustdoc 팀 구성원은 그룹에 추가해 달라고 요청할 수 있습니다.
rustdoc 역할은 자신의 영역에만 영향을 미치는 경우 FCP와 RFC를 자체적으로 처리하며, 그렇지 않은 경우에는 필요에 따라 다른 역할이나 다른 팀까지 절차에 참여하게 됩니다.
프런트엔드 그룹을 예로 들어 봅시다. 이 그룹은 rustdoc 팀의 일부이므로, 이 그룹에 참여하려면 rustdoc 팀의 구성원이어야 합니다. 프런트엔드 그룹에 속한다는 것은 프런트엔드 풀 리퀘스트의 리뷰 순번에 참여하도록 권장되며, 프런트엔드 FCP와 RFC에 응답해야 한다는 것을 의미합니다.
승격 결정 방식
어떤 사람이 한동안 rustdoc에 기여해 왔다면, 기존 팀 구성원이 비공개 Zulip rustdoc 팀 채널에서 그 사람을 상정할 수 있습니다. 모든 상정은 비공개 Zulip rustdoc 팀 채널에서 반드시 이루어져야 합니다.
rustdoc 팀 구성원들은 해당 인물에게 구성원 초대를 보내는 데 우려 사항이 있는지 확인하며, 10일이 지난 후(반대가 없는 경우) 초대를 보냅니다.
해당 인물이 초대를 수락하면, rustdoc 팀 리드가 새로운 역할을 반영하도록 team 저장소를 업데이트합니다.
전임 구성원 상태
rustdoc 팀 구성원이 언제든 활동을 잠시 쉬고 싶다면, 스스로 전임 구성원 상태로 전환할 수 있습니다. 전임 구성원 상태가 되면 GitHub 별칭 등에서 제외되므로 핑이나 메시지에 신경 쓰지 않아도 됩니다. 또한 r+ 권한도 갖지 않습니다. 다만 전임 구성원은 여전히 GitHub 조직 전체의 구성원으로 남습니다.
전임 구성원 상태인 사람은 언제든지 “활동” 상태로 복귀를 요청할 수 있습니다. 이 요청은 특별한 사정이 없는 한 일반적으로 자동으로 승인됩니다.
전임 구성원 상태인 사람은 이전에 도달한 수준의 팀 구성원으로 여전히 남아 있으며 이를 공개적으로 밝힐 수 있지만, 활동했던 기간도 함께 밝혀야 합니다.
6개월간 활동이 없으면 자동으로 전임 구성원 상태로 전환
구성원 또는 메인테이너가 6개월 동안 rustdoc에서 활동하지 않으면 전임 구성원 상태로 전환됩니다.
자료
기여자와 팀 구성원에게 유용한 다양한 자료가 있습니다.
- rustdoc 내부 구조
- rustc 개발자 가이드
- 컴파일러 내부 구조와 개발 환경 설정에 관한 문서입니다.
- rustc 생성 문서
- rustdoc 생성 문서
- 컴파일러 소스에 대한 rustdoc 출력물입니다.
기여자나 컴파일러 팀 구성원에게 유용할 만한 자료가 더 있다면, 언제든지 여기에 추가하는 PR을 제출하십시오.
리뷰 정책
rustdoc 팀은 컴파일러 팀과 동일한 리뷰 정책을 따릅니다. 이에 관해서는 컴파일러 팀의 해당 장을 참고하십시오.
이에 더해 다음 사항에 유의하십시오.
- rustdoc 팀 구성원이 아니더라도 누구나 코드 리뷰로 의견을 제시할 수 있습니다.
- 풀 리퀘스트의 병합은 팀 구성원만 승인할 수 있습니다.
- 새로운 기능은 보통 더 심도 있는 리뷰를 거치며, RFC나 FCP를 거쳐야 할 수도 있습니다.
제안, 승인 및 안정화
rustdoc에 기여할 때는 실험이나 리팩터링을 진행할 허가를 얻기 위해서든, 기능을 안정화하기 위해서든 피드백과 승인을 받아야 하는 경우가 매우 흔합니다. 이 문서는 rustdoc 팀이 승인 결정을 내리는 데 사용하는 다양한 절차와 각 절차를 언제 사용해야 하는지를 요약하는 것을 목표로 합니다.
승인
팀이 제안을 승인하는 데 사용할 수 있는 메커니즘은 두 가지입니다(모든 승인 메커니즘이 모든 제안 방식에 적합한 것은 아니므로 아래를 참고하십시오):
- r+
- 제안(RFC 또는 FCP)은 병합이 승인되면 r+를 받습니다.
- r+는 PR을 승인하는 데에만 사용할 수 있습니다.
- FCP
- 최종 의견 수렴 기간을 통해 제안을 승인하려면 rustdoc 팀 과반수(전체 구성원 중 2명을 제외한 인원)의 서명이 필요하며, 그 후 10일의 대기 기간을 거칩니다.
- FCP는 모든 형태의 제안을 승인하는 데 사용할 수 있습니다.
제안
rustdoc 팀에 변경을 제안하는 방법은 세 가지입니다. 적절한 방법은 아래에 설명된 제안의 성격에 따라 달라집니다.
- rustdoc Zulip 스레드에서 논의를 시작하십시오.
- 이것이 권장되는 방법입니다. 이렇게 하면 결국 팀이 주요 변경을 요청하거나 아예 거절하는 경우에, 사용자가 무언가를 구현하느라 너무 많은 시간을 낭비하지 않도록 막을 수 있습니다. 논의 후 제안이 수락되면, 변경 사항에 따라 RFC 또는 PR이 다음 단계가 됩니다.
- 의견 요청(Request For Comments, RFC)
- RFC는
rust-lang/rfcs저장소에 올리는 풀 리퀘스트로, 중요한 변경을 위해 마련된 무거운 제안 메커니즘입니다. - RFC 제안은 FCP로만 승인할 수 있습니다.
- RFC는
- 풀 리퀘스트(PR)
rust-lang/rust저장소에 풀 리퀘스트를 여는 것은 대부분의 제안에 적합한 가벼운 메커니즘입니다. 이 방법은 rustdoc 플래그의 안정화나 새 타깃 추가와 같은 경우에 권장됩니다.- PR 제안은 FCP 또는 r+로 승인할 수 있습니다. r+만으로는 충분하지 않은 경우에 대해서는 아래의 FCP/RFC는 언제 필요합니까? 절을 참고하십시오.
- 이슈
- 앞의 방법 중 어느 것이 가장 적합한지 모르겠다면
rust-lang/rust저장소에 이슈를 여는 것도 좋은 출발점입니다.
- 앞의 방법 중 어느 것이 가장 적합한지 모르겠다면
FCP/RFC는 언제 필요합니까?
GUI 웹 인터페이스의 UI/UX 변경, 새 명령줄 인수, 새 속성 등 사용자에게 드러나는 작은 변경을 안정화할 때는 FCP가 필요합니다. 그러나 변경이 너무 크거나 중요하다고 판단되면, 변경이 수락되기 전에 RFC를 작성하고 승인받아야 합니다.
FCP를 시작할 때는 관심 없는 변경 사항으로 사람들에게 핑을 보내지 않도록 이슈/PR에 관련 하위 팀만 레이블로 지정되어 있는지 확인하십시오.
승인이 필요한 기여를 승인 없이 하면 어떻게 됩니까?
기여에 필요한 승인이 RFC를 요구하는 경우, 해당 기여는 먼저 RFC를 작성해 달라는 요청과 함께 닫히거나 차단됨으로 표시되어야 합니다. 특정 기여에 대해 PR 승인만으로 충분하다면(아래 참조), 승인 절차를 시작할 수 있습니다.
승인을 받기 전에 실험적으로 코드 작업을 해도 됩니까?
물론입니다! 자유롭게 PR 작업을 하거나 코드를 작성하셔도 됩니다. 다만 그러한 PR은 실험적인 것으로 표시해야 하며, 병합되어서는 안 되고, 누군가 리뷰해 주리라 기대해서도 안 됩니다(원하는 사람이 있다면 예외입니다).
좋은 제안의 조건은 무엇입니까?
좋은 제안은 다음 사항을 다룹니다.
거버넌스
리더십 위원회
리더십 위원회는 Rust 프로젝트 내 여러 팀을 대표하는 그룹으로, 팀 간 조율을 담당하고 Rust 프로젝트가 원활하게 운영되도록 보장하는 임무를 맡고 있습니다.
리더십 위원회에 적용되는 정책은 리더십 위원회 장에 명시되어 있습니다.
모더레이션
모더레이션 팀은 Rust 행동 강령 위반 사항에 대응하는 일을 맡고 있습니다.
모더레이션 팀에 적용되는 정책은 모더레이션 장에 명시되어 있습니다.
리더십 위원회
이 문서는 Rust 프로젝트의 성공적인 운영을 보장하기 위해 Rust 리더십 위원회(“위원회”)의 권한1과 정책을 정의합니다.
이 문서는 위원회를 규율하는, 현재 승인된 정책 전체를 정의하는 살아 있는 문서입니다. 이 문서는 위원회를 설립한 RFC 3392의 본문을 토대로 작성되었으며, RFC 절차를 통해 갱신될 수 있습니다.
위원회는 이 권한의 상당 부분을 팀(하위 팀, 워킹 그룹 등2을 포함합니다)에 위임하며, 각 팀은 자신의 관할에 관한 결정을 자율적으로 내립니다. 다만 위원회는 이 문서에서 개괄하고 그 범위를 한정한 일부 의사 결정 권한을 보유합니다.
위원회는 https://github.com/rust-lang/leadership-council에 별도의 홈 사이트를 운영하며, 그곳에서 내부 절차를 문서화하고 업무를 조율합니다.
위원회는 각 최상위 팀에서 위원회로 파견한 대표들로 구성됩니다.
위원회는 Rust 프로젝트 전체의 성공에 대한 책임을 맡습니다. 위원회는 수행해야 하지만 아직 명확한 담당자가 없는 일을 찾아내고, 그 일을 수행할 새 팀을 만들며, 기존 팀이 자신의 관할 업무에 책임을 지도록 하고, 프로젝트 팀의 조직 구조를 조율하고 조정합니다.
개요
동기
Rust 프로젝트는 전 세계에 분포한 수백 명의 사람들로 이루어져 있으며, 이들은 다양한 관할을 가진 팀으로 조직되어 있습니다. 그러나 상당히 많은 작업이 기존 어느 팀의 관할에도 속하지 않으며, 그럼에도 여전히 수행되어야 합니다.
위원회는 팀 관할 밖에 있는 작업을 파악하고 우선순위를 정하는 데 집중합니다. 위원회는 그러한 작업을 직접 수행하기보다는 주로 위임합니다. 위원회는 또한 팀 간 협업, 로드맵, 프로젝트의 장기적 성공 등과 관련하여 팀 사이의 조율, 조직, 책임성을 담당하는 기구 역할을 할 수 있습니다.
위원회의 의무, 기대 사항, 제약
개략적으로 말해, 위원회는 다음 의무만을 담당합니다:
- 명확한 소유자가 없어서(소유자가 명시적으로 우선순위를 낮추었거나 백로그에 넣어 둔 경우 등이 아니라) 수행되지 않고 있는 작업을 파악하고, 우선순위를 정하고, 추적하는 일.
- 이러한 작업을 위임하는 일로, 필요하다면 이 작업을 맡을 새로운(경우에 따라서는 임시) 팀을 설립하는 것도 포함합니다.
- 명확한 소유자가 없는 긴급한 사안에 대해 결정을 내리는 일.
- 이는 결정을 기존 팀이나 새로 만든 팀 어디에도 위임할 수 없는 예외적인 상황에서만 이루어져야 합니다.
- 팀, 구조, 프로세스에 대한 프로젝트 전반의 변경을 조율하는 일.
- 최상위 팀이 자신의 관할, 다른 팀, 그리고 프로젝트에 대해 책임을 지도록 보장하는 일.
- 가능한 한 팀이 작업을 수행하는 데 필요한 인력과 자원을 갖추도록 보장하는 일.
- Rust 프로젝트 전체의 공식 입장, 의견 또는 의지를 정립하는 일.
- 이는 프로젝트 전반의 조율 필요성을 줄이는 데 도움이 되며, 특히 긴 공개 의견 수렴과 합의 형성 과정이 현실적이지 않은 경우 - 예를 들어 Rust 프로젝트 전체가 무엇을 “원하는지” 어느 정도 이해해야 하는 제3자와 소통할 때 - 유용합니다.
이러한 의무 외에도, 위원회가 제대로 기능하고 있는지 판단하는 데 도움이 되도록 위원회에는 추가적인 기대 사항과 제약이 있습니다:
- 작업 위임: 위원회는 이 문서가 명시적으로 부여한 것 이상의 작업을 맡아서는 안 되며, 위원회와 구별되는 기존 팀이나 새 팀에 작업을 위임해야 합니다. 그러한 팀에 위원회 대표가 포함될 수는 있지만, 그러한 팀의 구성원이 되는 것은 위원회 대표의 의무에 포함되지 않습니다.
- 프로젝트가 장기적으로 원활하게 운영되도록 보장: 위원회는 긴급하지 않은 프로젝트 관리 작업의 우선순위가 정해지고, 프로젝트에 조직적 부채가 쌓이지 않을 만큼 충분히 규칙적으로 완료되도록 보장해야 합니다.
- 책임을 지십시오: 위원회는 폭넓은 권한을 행사하므로 위원회와 위원회 대표는 자신의 행동에 책임을 져야 합니다. 이들은 다른 사람의 피드백에 귀 기울여야 하며, 자신이 맡은 직위의 의무와 기대를 계속 충족하고 있는지 적극적으로 성찰해야 합니다.
- 대표성을 갖추십시오: 위원회 대표는 프로젝트의 폭넓은 관심사뿐만 아니라 가능한 한 많은 측면(인구 통계, 기술적 배경 등)에서 Rust 커뮤니티의 다양성도 대표해야 합니다.
- 부담을 나누십시오: 모든 위원회 대표는 위원회 업무의 부담을 함께 나누어야 합니다.
- 다른 팀의 관할을 존중하십시오: 위원회는 각 팀에 위임된 관할을 존중해야 합니다. 위원회는 이슈의 해결책을 두고 팀과 협의하고 함께 일해야 하며, 특정 팀의 뜻에 반하는 결정은 거의 내리지 않아야 합니다.
- 선의로 행동하십시오: 위원회 대표는 설령 그 결정이 자신이 속한 개별 팀, 고용주 또는 기타 외부 이해관계와 충돌하더라도 Rust 프로젝트 전체의 최선의 이익을 위해 결정을 내려야 합니다.
- 투명하게 행동하십시오: 모든 결정(또는 결정의 모든 측면)을 공개할 수는 없지만, 위원회는 자신의 의사 결정에 대해 가능한 한 개방적이고 투명해야 합니다. 또한 위원회는 프로젝트의 조직 구조가 명확하고 투명하도록 보장해야 합니다.
- 프라이버시를 존중하십시오: 위원회는 투명성을 이유로 개인 정보나 기밀 정보를 결코 침해해서는 안 되며, 여기에는 특권 정보를 의도치 않게 드러낼 수 있는 인접 정보도 포함됩니다.
- 건강한 업무 환경을 조성하십시오: 위원회 대표는 모두 자신이 기여하는 양과 성격에 만족감을 느껴야 합니다. 이들은 자신이 위원회에 있는 이유가 단지 의무감 때문이 아니라 의미 있는 방식으로 적극적으로 참여하고 있기 때문이라고 느껴야 합니다.
- 발전하십시오: 위원회는 팀, 프로젝트, 커뮤니티의 변화하는 요구를 충족하기 위해 시간이 지남에 따라 발전해 나갈 것으로 기대됩니다.
위원회 대표, 모더레이션 팀 구성원 및 기타 프로젝트 구성원은 주변 사람들과 더 넓은 커뮤니티에 본보기가 됩니다. 이러한 역할은 모두 책임과 리더십을 지닌 직위입니다. 이들의 행동은 무게를 지니며 커뮤니티 내에서 큰 영향력을 발휘할 수 있으므로 마땅히 신중하게 행사되어야 합니다. 따라서 이러한 역할을 맡기로 한 사람은 주변 사람들이 자신에게 그에 상응하는 높은 기준을 요구할 것임을 인식해야 합니다.
위원회의 구조
위원회는 팀 대표들로 구성되며, 각 대표는 하나의 최상위 팀과 그 하위 팀을 대표합니다.
각 최상위 팀은 스스로 선택한 절차에 따라 정확히 한 명의 대표를 지명합니다.
최상위 팀의 구성원이나 그 하위 팀의 구성원이라면 누구나 대표가 될 자격이 있습니다. 팀은 하위 팀 구성원에게 잠재적 후보에 대한 의견과 피드백을 제시할 기회를 제공해야 합니다.
각 대표는 다른 팀의 구성원이기도 하더라도 최대 하나의 최상위 팀만 대표합니다. 어떤 Rust 팀을 대표하는 주된 책임은 그 팀이 속한 최상위 팀의 대표에게 있습니다.3
Rust 프로젝트의 모든 팀은 궁극적으로 적어도 하나의 최상위 팀에 속해야 합니다. Launching Pad 팀은 현재 상위 팀이 없는 팀들의 임시 소속처 역할을 합니다. 이를 통해 모든 팀이 위원회에서 대표될 수 있습니다.
최상위 팀
위원회는 공개 정책 결정을 통해 최상위 팀을 설립합니다. 일반적으로 최상위 팀은 다음 기준을 충족해야 합니다.
- Rust 프로젝트의 근간이 되는 관할을 가집니다
- 해당 관할의 모든 측면에 대해 최종 의사 결정권자입니다
- 다른 팀 관할의 일부가 아닌 관할을 가집니다(즉, 하위 팀이나 이와 유사한 거버넌스 구조여서는 안 됩니다)
- 무기한 지속될 것으로 예상되는, 범위가 한정되지 않은 관할을 가져야 합니다
- 현재 Rust 프로젝트에서 활발히 활동하는 일부여야 합니다
최상위 팀은 4개 이상 9개 이하여야 하며, 5개 이상 8개 이하인 것이 바람직합니다. 이 숫자는 다양하고 비교적 얕은 구조를 바라는 요구와, 대표들 간의 생산적인 대화와 동의를 위한 실용성 사이의 균형을 맞춥니다.4
위원회가 새로운 최상위 팀을 만들면, 해당 팀은 위원회 대표를 지명합니다.5 새로운 최상위 팀을 만들 때 위원회는 그 팀이 하위 팀이나 다른 거버넌스 구조가 되어서는 안 되는 이유에 대한 근거를 제시해야 합니다.
최상위 팀은 다음과 같습니다:
- 컴파일러
- 개발 도구
- 인프라
- 언어
- Launching Pad
- 라이브러리
- 모더레이션
Launching Pad 최상위 팀
Launching Pad 팀은 달리 소속될 최상위 팀이 없는 하위 팀을 임시로 받아들입니다. 이를 통해 더 영구적인 상위 팀을 찾거나 만드는 동안에도 모든 팀이 위원회에 대표를 둘 수 있습니다.
Launching Pad 팀은 우산형 팀입니다. 직접 소속된 구성원은 없고 하위 팀 대표만 있습니다.
위원회는 Launching Pad의 각 하위 팀에 더 적합한 상위 팀을 찾거나 만들고, 이후 해당 하위 팀을 새 상위 팀으로 옮기도록 노력해야 합니다.
적합한 상위 팀이 존재하더라도 아직 하위 팀을 받아들일 준비가 되지 않은 경우가 있을 수 있으며, 이런 경우 Launching Pad가 임시 소속처 역할을 할 수 있습니다.
또한 어떤 팀이 폐지되거나 재편으로 없어질 때, 그 폐지나 재편에서 해당 팀의 하위 팀을 조직 내 다른 곳에 명시적으로 배치하지 않으면 Launching Pad가 그 하위 팀의 기본 소속처 역할을 합니다.
위원회는 6개월마다 Launching Pad에 속한 하위 팀 구성을 리뷰하여 모든 하위 팀의 새로운 상위 팀을 찾는 일이 적절히 진행되고 있는지 확인해야 합니다. 다른 최상위 팀과 마찬가지로, 위원회가 Launching Pad 팀이 더 이상 필요하지 않다고 판단하면 이 팀을 폐지할 수 있습니다(이 경우 위원회 내 대표도 없어집니다). Launching Pad 팀을 폐지하는 절차는 다른 최상위 팀과 같습니다. 아니면 위원회가 Launching Pad 팀에 고유한 관할을 부여할 수도 있습니다.
최상위 팀 제거
어떤 팀의 최상위 지위를 없애는(또는 다른 방식으로 위원회 참여 자격에 영향을 주는) 결정을 하려면, 제거 대상인 최상위 팀의 대표를 제외한 모든 위원회 대표의 동의가 필요합니다. 이러한 예외가 있더라도 해당 팀의 대표는 팀 제거에 관한 위원회 심의에 반드시 초대되어야 하며, 위원회는 극단적인 경우에만 그 팀의 반대를 무릅쓰고 팀을 제거해야 합니다.
위원회는 모더레이션 팀을 제거할 수 없습니다. 위원회는 모더레이션 팀의 동의 없이 모더레이션 팀의 관할을 변경할 수 없습니다.
대리인 및 대표권 포기
대표자는 참여 가능한 시간이나 사정이 바뀌는 등 필요한 경우 임기를 조기에 종료할 수 있습니다. 이 경우 해당 최상위 팀은 새 대표자 선출을 시작해야 합니다. 대표자 역할은 자원봉사직입니다. 누구도 이 역할을 맡을 의무가 없으며, 어떤 팀도 대표자로 활동하는 것을 팀 구성원 자격의 필수 의무로 삼을 수 없습니다. 다만 대표자는 대표자 직위의 직무를 수행하거나, 그렇지 않으면 그 직위에서 물러날 의무가 있습니다.
최상위 팀은 일시적으로 인원이 부족하여 대표자를 맡으려는 사람이 없는 경우 등에 대표권을 일시적으로 포기하기로 결정할 수 있습니다. 그러나 팀이 위원회 대표자를 지정하지 않으면, 그 팀은 프로젝트 전체 수준의 의사 결정에 적극적으로 참여할 권리를 포기하게 됩니다. 이러한 공백으로 인해 의사 결정을 포함한 위원회의 모든 절차가 가로막혀서는 안 됩니다. 위원회는 여전히 모든 프로젝트 구성원이 제시하는 새로운 정보와 이의를 검토할 의무가 있습니다. 그러나 위원회는 대표자가 없는 팀의 피드백을 특별히 검토하거나 취합하기 위해 결정을 보류할 의무는 없습니다.
위원회에 대표자를 파견하는 것은 최상위 팀의 의무로 간주되며, 이를 정기적으로 하지 못한다는 것은 해당 팀이 의무를 다하지 못하고 있다는 뜻입니다. 다만 위원회 대표자는 일시적인 질병, 휴가 등으로 인한 짧은 부재의 경우에는 그 역할을 포기하는 것이 아닙니다.
최상위 팀은 주 대표자가 참여할 수 없는 경우에 활동할 대리 대표자를 지정할 수 있습니다. 이 대리 대표자는 주 대표자가 복귀할 때까지 위원회 대표자의 역할을 온전히 맡습니다. 대리 대표자는 주 대표자가 참석하는 때에는 (참석자 수가 두 배로 늘어나는 것을 피하기 위해) 회의에 정기적으로 참석하지 않습니다.
팀의 대표자와 모든 대리 대표자가 3주 연속으로 위원회의 어떠한 절차에도 참여하지 않으면, 해당 팀이 참여 가능한 대표자를 세울 때까지 그 팀의 대표자는 위원회의 의사 결정 정족수 요건에 산입되지 않습니다. 위원회는 이 조치가 효력을 발생하기 전에 해당 팀에 이를 통지해야 합니다. 팀의 의견 없이, 또는 팀을 대신하여 이의를 제기할 수 없는 상태에서 위원회가 결정을 내리는 일이 없도록 하려면, 팀은 대리 대표자를 확보해 두어야 합니다.
최상위 팀은 필요한 경우 임기가 끝나기 전에 대표자를 교체할 수 있습니다. 그러나 연속성을 유지하는 데에는 부담이 따르므로, 팀은 필요 이상으로 대표자를 교체하는 일을 피해야 합니다. 팀이 지속적으로 다루고자 하는 팀 고유의 이슈나 입장에 대해 대표자와 대리 대표자에게 설명할 일차적 책임은 팀에 있습니다. 위원회 내에서 진행 중인 이슈의 연속성을 유지하고, 대리 대표자와 그 밖의 신임 대표자에게 맥락을 제공할 책임은 위원회와 팀이 함께 집니다.
비공개 사안의 경우, 위원회는 비공개 정보가 불필요하게 퍼지지 않도록 대리 대표자에게 알릴지를 신중히 판단해야 합니다. 대리 대표자가 대신 나서야 하는 경우에는 위원회가 그들에게 상황을 설명할 수 있습니다.
임기 제한
위원회 대표자의 임기는 1년입니다. 각 대표자는 특정 대표단(특정 최상위 팀에서 파견하는 대표단)에 대해 연속 세 번의 온전한 임기라는 비강제 한도를 적용받습니다. 대표자는 해당 팀이 다른 팀 구성원을 대표자로 세울 수 없다는 명시적 확인을 위원회가 받은 경우에 한하여 이 비강제 한도를 넘을 수 있습니다(예를 들어 맡으려는 다른 후보가 없거나, 팀 구성원들이 다른 모든 후보에 대해 차단 이의를 제기한 경우).
이 외에는 대표자가 다른 최상위 팀에서 맡을 수 있는 임기 수나, 한 최상위 팀에서 연속되지 않게 맡을 수 있는 임기 수에 대한 강제 한도는 없습니다. 팀은 경험의 연속성과, 여러 사람이 그러한 경험을 쌓을 수 있도록 대표자를 교대하는 것 사이에서 균형을 이루도록 노력해야 합니다.6
대표 임명의 절반은 3월 말에, 나머지 절반은 9월 말에 이루어집니다. 이렇게 하면 모든 위원회 대표가 한꺼번에 바뀌는 일을 피할 수 있습니다. 초기 위원회를 구성할 때와 최상위 팀 구성이 바뀔 때마다 위원회와 최상위 팀은 협력하여 임기 종료일이 3월과 9월에 대략 균등하게 나뉘도록 해야 합니다. 다만 각 임기는 최소 6개월 이상이어야 합니다(지나치게 짧은 임기를 피하기 위한 일시적인 불균형은 허용됩니다).
위원회와 최상위 팀이 적절한 임기 종료일 변경에 합의하지 못하면, 균형을 유지하기 위해 대표들을 두 종료일(최소 6개월 이후) 중 하나에 무작위로 배정합니다.
단일 회사/단체 출신 대표 수 제한
부적절한 일이나 부적절해 보이는 일을 피하기 위해 위원회 대표가 특정 회사, 법인 또는 밀접하게 관련된 법인들 출신으로 과도하게 치우쳐서는 안 됩니다. 위원회 대표가 5명 이하이면 같은 소속의 대표는 1명을 넘을 수 없고, 위원회 대표가 6명 이상이면 같은 소속의 대표는 2명을 넘을 수 없습니다.
밀접하게 관련된 법인에는 같은 법인의 지사/부서/자회사, 상당한 지분 관계로 연결된 법인 및 이와 유사한 경우가 포함됩니다. 위원회는 특이한 경우에 판단을 내릴 수 있으며, 그 결정에서 이해 상충이 생기지 않도록 주의해야 합니다.
위원회 대표가 어떤 회사나 기타 법인에서 소득의 상당 부분을 얻는다면(고용주, 고객, 주요 후원자 등) 그 법인에 소속된 것으로 봅니다. 대표는 소속이 바뀌면 즉시 이를 공개해야 합니다.
대표의 소속 변경, 최상위 팀의 신임 대표 임명, 위원회 규모 변경 등 어떤 이유로든 이 제약이 지켜지지 않게 되면 다음과 같이 제약을 복원합니다.
- 동일한 소속을 가진 대표들은 먼저 자신들끼리 이 이슈를 해결하려고 시도할 수 있으며, 이 경우 한 대표가 자발적으로 물러나고 그 대표의 팀이 다른 사람을 임명합니다.
- 이 결정은 소속 단체가 아니라 대표 본인이 내려야 합니다. 소속 단체가 이 결정에 영향을 미치는 것은 부적절한 일로 간주됩니다.
- 이러한 논의에서 대표들은 동등한 지위를 가집니다. 프로젝트나 위원회에서의 연공서열 같은 요소를 이용해 다른 사람에게 압력을 가해서는 안 됩니다.
- 해당 소속을 가진 대표들이 합의하지 못하면 그중 한 대표를 무작위로 해임합니다. (그래도 제약이 충족되지 않으면, 남은 대표들은 이 절차를 반복하기 전에 다시 자신들끼리 이 이슈를 해결하려고 시도할 수 있습니다.) 이 방식은 최선이 아닌 결과를 낳을 가능성이 높으므로, 일반적으로는 자발적인 해결책이 더 바람직합니다.
- 팀은 즉시 후임자 선정 절차를 시작해야 하지만, 팀의 기존 대표는 남은 임기 중 최대 3개월까지 계속 직무를 수행할 수 있습니다.
- 기존 대표는 신임 대표와 함께 인수인계를 조율해야 하지만, 최대 3개월의 기간 동안 둘 중 누가 실제 대표를 맡을지는 팀이 결정합니다. 최상위 팀의 대표는 언제나 한 명뿐입니다.
후보 기준
다음은 이상적인 후보를 정하기 위한 기준입니다. 이 기준은 유능한 팀 리드나 공동 리드의 기준과 비슷하지만 같지는 않습니다. 팀 리드가 좋은 위원회 대표가 될 수도 있지만, 팀 리드 역할과 위원회 대표 역할은 모두 상당한 시간을 들여야 하므로 두 역할을 서로 다른 사람이 나누어 맡는 편이 나을 가능성이 높습니다. 이 기준은 반드시 충족해야 하는 요건은 아니지만, 누가 팀의 대표를 맡기에 가장 적합한지 판단하는 데 활용할 수 있습니다. 요컨대 대표는 다음을 갖추어야 합니다.
- 위원회의 필요에 쏟을 충분한 시간과 에너지.
- 프로젝트 운영 및 프로젝트 거버넌스 관련 주제를 돕는 일에 대한 관심.
- 자기 팀이나 활발히 기여하는 영역 밖에서도 프로젝트가 필요로 하는 것에 대한 폭넓은 인식.
- 자기 팀이 필요로 하는 것에 대한 예리한 감각.
- 개인적인 의도보다 다른 사람들의 필요를 우선하여 대변하고 중심에 두는 성품과 능력.
- 일부 견해나 자신이 동의하는 견해만이 아니라 팀의 모든 견해를 대변할 능력과 의지.
일부 팀에는 현재 이 기준에 맞는 후보가 많지 않을 수 있지만, 위원회는 더 넓은 프로젝트 안에서 이러한 역량을 적극적으로 키워야 합니다. 이러한 역량은 위원회 구성원 활동뿐 아니라 프로젝트 전반에 걸쳐 도움이 되기 때문입니다.
자격 증명
위원회는 프로젝트의 관리용 자격 증명에 대한 특권적 접근 권한을 갖지 않습니다. 이 접근 권한은 오직 인프라 팀7에만 있습니다. 인프라 팀의 책임에는 인프라의 보안 및 유지 보수성과 균형을 맞추면서, 각 팀이 업무를 효과적으로 수행하는 데 필요한 도구와 접근 권한을 갖추도록 보장하는 일이 포함됩니다. 위원회는 정책을 통해 어떤 팀이 접근 권한을 가져야 하는지 조율하는 일을 도울 수 있습니다.
Rust 재단과의 관계
위원회는 프로젝트 이사(Project director)를 선출하는 절차를 수립할 책임이 있습니다. 프로젝트 이사는 Rust 프로젝트의 이익이 Rust 재단 이사회에 반영되도록 하는 수단입니다.
위원회는 재단 이사회에서 프로젝트의 이익을 대변하고 재단 관련 사안에 대해 특정 결정을 내릴 수 있는 관할을 프로젝트 이사에게 위임합니다. 해당 관할의 정확한 범위는 아직 정해지지 않았습니다.
위원회의 의사 결정 절차
위원회는 운영 결정과 정책 결정이라는 두 가지 유형의 결정을 내립니다. 결정의 분류에 따라 해당 결정에 특정한 고려 사항이 적용될 수 있습니다. 그러나 기본적으로 위원회는 분류와 관계없이 모든 결정에 동의 기반 의사 결정 절차를 사용합니다.
운영 결정과 정책 결정
운영 결정은 위원회가 목표를 수행하기 위해 일상적으로 내리는 결정이며, 여기에는 (수립된 정책에 따라) 회의 밖에서 이루어지는 정기적인 조치도 포함됩니다. 정책 결정은 운영의 틀을 잡고, 방향을 제시하고, 운영을 지원하기 위한 일반적이고 재사용 가능한 패턴이나 프레임워크를 제공합니다. 특히 정책 결정은 운영 결정이나 운영의 다른 측면을 부분적으로 자동화할 수 있습니다. 별도로 명시되지 않는 한, 위원회는 모든 결정에 기본적으로 동의 기반 의사 결정 절차를 따릅니다.
어떤 결정이 운영 결정이고 어떤 결정이 정책 결정인지는 정확하게 정의되어 있지 않으며, 오히려 결정들은 하나의 연속선상 어딘가에 놓입니다. 이 구분의 목적은 위원회의 의사 결정 절차를 지시하거나 제약하는 데 있지 않습니다. 오히려 이 구분은 위원회에 지침을 제공하고, 위원회가 시간이 지남에 따라 자신의 결정을 어떻게 기록하고 리뷰하고 개선할 것인지를 명확히 합니다. 운영/정책 분류와 관련된 요구 사항이나 지침을 적용할 때, 이 정책이나 향후 정책에서 운영 또는 정책 중 어느 것으로도 명시되지 않은 사항은 기본적으로 정책으로 간주합니다.
반복과 예외
정책 결정은 그렇지 않았다면 운영 결정을 반복해서 내려야 했을 사안을 체계적으로 다루는 경우가 많습니다. 위원회는 반복되는 운영 결정이 정책 결정이나 정책 변경의 필요성을 나타내는 시점을 알아차리도록 노력해야 합니다. 특히 위원회는 반복되는 운영 결정이 사실상의 정책으로 굳어지도록 방치하지 않아야 합니다.
기존 정책에 대한 예외는 해당 정책에서 그러한 예외를 명시적으로 허용하지 않는 한 운영 결정으로 만들 수 없습니다. 임시방편적인 예외를 피하면 “일탈의 정상화”를 막는 데 도움이 됩니다.
동의 기반 의사 결정 절차
동의란 어떤 대표자의 요구 사항(따라서 그 대표자가 대표하는 최상위 팀과 하위 팀의 요구 사항)도 무시될 수 없음을 의미합니다. 위원회는 관련된 모든 의견을 듣고, 모든 목소리에 동등한 무게를 두어 공평하게 함께 일할 수 있는 좋은 기반을 마련합니다.
위원회는 동의 기반 의사 결정 방식을 사용합니다. 이 방식에서는 대표자에게 “동의하십니까?“라고 묻는 대신 “반대하십니까?“라고 묻습니다. 이를 통해 제안을 충분히 검토하고도 그 이유에 대한 명확한 피드백 없이 승인하지 않기로 하는 “묵시적 거부권(pocket veto)“을 없앨 수 있습니다. 우려, 피드백, 선호 및 그 밖에 덜 중대한 형태의 의견은 결정을 막지 않지만, 초안 작성과 논의의 이른 단계에서 반영할지 여전히 검토해야 합니다. 충족되지 않은 요구 사항이나 필요를 나타내는 반대 의견은 결정을 진행하려면 반드시 검토되고 해소되어야 합니다.
승인 기준
동의 기반 의사 결정 절차의 승인 기준은 다음과 같습니다.
- 위원회가 지정한 소통 공간(회의 또는 특정 채널) 중 한 곳에 제안을 게시합니다.
- 최소 N-2명의 위원회 대표자(N은 위원회 대표자의 총수입니다)가 최종 제안을 충분히 검토하고 동의했음을 확인합니다.
- 어떤 위원회 대표자로부터도 해소되지 않은 명시적 반대 의견이 없어야 합니다.
- 피드백을 위해 최소 10일의 기간을 둡니다.
이 승인 기준은 정족수 메커니즘을 제공하는 동시에, 대표자들이 제안을 확인할 수 있는 충분한 시간을 보장합니다. 두 명까지 승인 표시를 하지 않아도 되도록 허용하는 것은 프로젝트가 자원봉사로 운영된다는 특성을 인정한 것입니다. 이는 결정의 속도와 동의 및 무반대에 필요한 확인의 양 사이에서 균형을 맞춰 온 경험에 기반하며, 해당 대표자들이 원했다면 반대 의견을 낼 시간이 있었다고 가정합니다. (이는 현재 RFC 승인에 사용되는 절차를 본뜬 것입니다.)
제안한 대표자가 제안을 철회하기로 결정하면 의사 결정 절차는 언제든지 종료될 수 있습니다. 다른 대표자는 언제든지 그 제안을 이어받아 유지할 수 있습니다.
이해 상충으로 인해 위원회가 어떤 결정에 대한 N-2 정족수를 충족할 수 없게 되면, 위원회는 이해 상충을 문서화한 상태로 결정을 진행하는 방법에 관한 “이해 상충” 섹션에 문서화된 절차를 따르지 않는 한 그 결정을 내릴 수 없습니다. 이러한 경우 위원회는 유사한 충돌이 앞으로 재발하지 않도록 적절한 절차와 정책을 검토해야 합니다.
의사 결정 절차의 수정과 조정
위원회는 공개 정책 절차를 통해 결정의 유형별로 서로 다른 의사 결정 절차를 수립할 수 있습니다.
특정 유형의 결정에 어떤 의사 결정 절차를 채택할지 정할 때, 위원회는 신속한 결정의 필요성과 완전한 합의에 대한 확신의 중요성 사이에서 균형을 맞춥니다. 동의 기반 의사 결정 절차는 다음과 같은 스펙트럼 위에 놓입니다.
- 합의 기반 의사 결정(신속한 의사 결정을 희생하더라도 완전한 의견 일치에 대한 확신을 우선시함): 팀 구성원은 제안을 리뷰하고 다른 모든 제안보다 해당 제안을 선호해야 하며, 어떤 팀 구성원이든 차단 이의를 제기할 수 있습니다
- 동의 기반 의사 결정(위원회의 기본 방식으로, 신속한 결정과 의견 일치에 대한 확신 사이의 균형을 맞춤): 팀 구성원은 제안을 리뷰해야 하며 차단 이의를 제기할 수 있습니다
- 1인 재청 및 이의 없음(의견 일치에 대한 확신을 희생하더라도 신속한 의사 결정을 우선시함): 한 명의 팀 구성원이 리뷰하고 지지해야 하며, 어떤 팀 구성원이든 차단 이의를 제기할 수 있습니다
의사 결정 절차를 정의하는 모든 정책은 최소한 제안을 게시할 수 있는 곳, 정족수 요건, 필요한 리뷰 수, 피드백을 위한 최소 대기 시간을 다루어야 합니다. 반대 의견이 없어야 한다는 것은 모든 의사 결정 절차의 승인 기준에 포함됩니다.
이해 상충으로 인해 위원회의 3분의 1을 초과하는 인원이 어떤 결정에 참여할 수 없게 되면, 위원회는 이해 상충을 문서화한 상태로 결정을 진행하는 방법에 관한 “이해 상충” 섹션에 문서화된 절차를 따르지 않는 한 그 결정을 내릴 수 없습니다. (이는 사용 중인 의사 결정 절차의 다른 정족수 요건과 관계없이 적용됩니다.) 이러한 경우 위원회는 비슷한 상충이 앞으로 다시 발생하지 않도록 적절한 절차와 정책을 검토해야 합니다.
위원회는 공개 정책 결정을 통해 자신의 의사 결정 관할 중 일부를 팀, 다른 거버넌스 조직, 또는 운영 책임자, 회의 진행자, 서기/간사처럼 위원회가 만들고 임명하는 역할에 위임할 수도 있습니다.
위원회는 제안의 초안 작성을 위임하더라도 그 제안을 승인하는 결정까지 반드시 위임해야 하는 것은 아니라는 점에 유의하십시오. 이는 여러 팀의 관할에 걸치거나 어느 팀의 관할에도 속하지 않는 프로젝트 전반의 정책에서 필요할 수 있습니다. 이는 새 팀을 점진적으로 꾸려 나갈 때에도 도움이 될 수 있습니다.
안건 및 백로그
위원회의 안건과 백로그는 프로젝트 전반에서 프로젝트 구성원이 제기한 사안을 위원회가 추적하고 그 진행 상황을 알리는 주요 창구입니다.
안건과 백로그의 공정성과 효과성을 높이기 위해 위원회는 다음을 이행해야 합니다.
- 프로젝트 구성원이 위원회에 요청을 제출하고 해당 요청의 진행 상황을 전달받을 수 있는 도구를 사용하십시오.
- 다가오는 기간의 우선순위와 목표를 정할 때 투명하고 포용적인 절차를 사용하십시오. 이 절차에는 모든 대표자의 정기적인 점검과 피드백이 포함되어야 합니다.
- 백로그와 안건에서 장기적인 전략 목표와 단기적인 필요 사이의 균형을 유지하도록 노력하십시오.
- 유연하고 적응력 있게 대처하며, 상황이나 우선순위가 바뀌면 필요에 따라 백로그와 안건을 기꺼이 조정하십시오.
- 백로그가 위원회의 현재 우선순위와 목표를 정확히 반영하도록 정기적으로 백로그를 리뷰하고 갱신하십시오.
- 역할(예: 회의 진행자와 기록 담당자)에 책임을 위임하고 회의 시작 시 안건에 동의하는 등, 백로그의 항목을 안건으로 옮길 때 명확하고 일관된 절차를 따르십시오. 동의 절차에서 거부된 안건 항목은 모두 그 반대 의견을 위원회가 공개하는 회의록에 기록해야 합니다.
교착 상태 해소
경우에 따라 위원회는 긴급하게 결정을 내려야 하지만, 그 시간 안에 모두가 동의할 제안을 마련할 수 없다고 판단할 수 있습니다. 이러한 경우, 적시에 결정을 내리지 못하는 것보다 자신이 반대하는 결정이라도 적시에 내리는 편이 더 나은 결과라는 데 모두가 동의한다면, 위원회는 교착 상태를 해소하기 위해 대안적인 의사 결정 방법을 사용할 수 있습니다. 이 대안적 절차는 비공식적이며, 위원회 구성원은 여전히 기존 의사 결정 절차를 통해 그 결과에 대한 동의를 재확인해야 합니다. 위원회 구성원은 여전히 언제든지 반대 의견을 제기할 수 있습니다.
예를 들어 위원회가 투표에 동의하면, 투표가 끝난 뒤 모든 위원회 구성원은 투표로 도출된 결정이 무엇이든 그에 동의하게 됩니다. 위원회는 특정한 대안적 의사 결정 모델을 선택할 때 예상되는 장점과 단점을 문서화하도록 노력해야 합니다.
교착 상태 해소를 위한 의무적인 메커니즘은 의도적으로 두지 않았습니다. 대표자들이 결정의 결과를 선호하지 않더라도 결정을 내리는 데 모두 동의하지는 않거나, 어느 대표자라도 위원회의 동의를 얻을 수 있는 제안을 아직 만들 수 있다고 생각한다면, 대표자는 언제든지 반대 의견을 유지할 수 있습니다.
대표자가 반대 의견을 철회하거나 (대안적 의사 결정 절차의 결과이든 아니든) 완전히 동의하지는 않는 결정에 동의한 경우, 위원회는 평가 일정을 잡거나 이미 예정된 평가까지의 기간을 단축하는 것을 고려해야 하며, 제기된 우려를 측정하고 평가할 수단을 마련해야 합니다. 이 리뷰의 결과는 위원회가 이전 결정의 변경을 고려해야 하는지 판단하기 위한 것입니다.
피드백 및 평가
모든 정책 결정에는 정책의 일부로 평가 날짜가 포함되어야 합니다. 최초 평가 기간은 이후의 평가 기간보다 짧아야 합니다. 평가 기간의 길이는 상황의 필요에 따라 조정되어야 합니다. 잘 작동하는 것으로 보이고 변경이 거의 필요하지 않은 정책은 불필요한 리뷰에 드는 시간을 줄이도록 기간을 연장해야 합니다. 최근에 조정되었거나 문제가 제기된 정책은 평가 기간을 단축하여 더 빠르게 반복 개선하며 안정에 이르도록 해야 합니다. 위원회는 새 정책의 기간을 정할 때 기본값으로 사용할 수 있도록 정책 유형별 표준 기간을 마련해야 합니다. 예를 들어 역할은 처음에 3개월, 그 이후에는 1년의 평가 날짜를 둘 수 있으며, 일반 정책은 처음에 6개월, 그 이후에는 2년을 기본값으로 둘 수 있습니다.
- 새로운 정책 결정은 언제든지 기존 정책을 수정하거나 대체할 수 있습니다.
- 정책 결정은 버전 이력과 함께 한곳에 모아 공개해야 합니다.
- 현행 정책 문서를 수정할 때는 사람들이 나중에 관련 맥락을 직접 찾아보리라 기대하지 말고, 해당 정책 결정과 관련된 맥락을 포함하거나 링크해야 합니다.
의사 결정의 투명성과 감독
위원회가 내리는 결정은 결정의 종류에 따라 필연적으로 서로 다른 수준의 투명성과 감독을 요구합니다. 이 절에서는 위원회가 자신의 결정에 대해 어떻게 감독을 받을지, 그리고 어떤 결정을 비공개로 또는 공개적으로 내릴 수 있는지에 관한 지침을 제공합니다.
이 RFC는 특정 결정들을 각 범주에 배정합니다. 구체적으로 열거되지 않은 모든 결정은 공개 정책 절차를 따라야 합니다. 위원회는 공개 정책 절차를 통해 이 분류를 발전시킬 수 있습니다.
위원회가 내리는 결정은 가능하고 필요한 감독의 수준에 따라 다음 세 가지 범주 중 하나에 속합니다.
- 위원회가 내부적으로 내릴 수 있는 결정
- 위원회가 필연적으로 비공개로 내려야 하는 결정
- 위원회가 공개 제안을 통해 내려야 하는 결정
위원회가 내부적으로 내릴 수 있는 결정
일부 유형의 운영 결정은 위원회가 내부적으로 내릴 수 있으며, 다만 위원회는 결정이 내려진 후 그 결정에 대한 커뮤니티 피드백을 받을 수 있는 체계를 갖추고 있어야 합니다.
위원회가 내부적으로 내릴 수 있는 결정 목록에 새로운 결정을 추가하려면 공개 정책 결정이 필요합니다. 위원회 자체의 구조, 의사 결정권자 또는 감독에 영향을 미치는 결정은 이 목록에 추가해서는 안 됩니다.
또한 위원회는 공개 제안을 피하려는 목적으로 내부 결정을 반복하여 사실상의 불문 정책을 만드는 일이 없도록 노력해야 합니다. 자세한 내용은 “반복과 예외”를 참조하십시오.
다음 목록은 위원회가 내부적으로 내릴 수 있는 결정을 빠짐없이 열거한 것입니다.
- 그 자체가 공개적으로 진행될 절차를 시작하기로 하는 결정(예: “설문 조사를 만들어 게시하기 시작합시다”, “향후 내릴 이 공개 결정을 위한 RFC 초안을 작성합시다”).
- Rust 프로젝트의 공식 입장문을 표명하고 전달하는 일.
- Rust 재단과 같은 다른 주체에 Rust 프로젝트의 입장을 직접 표명하고 전달하는 일.
- Rust 프로젝트의 소통 수단(블로그 또는 all@)을 통해 소통하는 일.
- 위원회의 조율 방식, 소통에 사용하는 플랫폼, 회의 장소와 시간, 결정을 내리고 기록하는 데 사용하는 템플릿 등 위원회 자체의 내부 절차에 관한 대부분의 운영 결정을 내리는 것(이 문서의 다른 부분에 있는 요건을 따릅니다).
- 회의 주재·진행, 회의록 작성 및 게시, 여러 당사자로부터의 의견 수집 및 정리 등의 목적으로 위원회 내에 임원이나 임시 역할을 임명하는 일.8 이러한 역할(직함, 직무, 현재 담당자)은 반드시 공개적으로 알리고 문서화해야 한다는 점에 유의하십시오.
- 특정 위원회 회의나 논의에 위원회 대표 이외의 특정 참석자를 초대하거나, 더 넓은 커뮤니티에 공개된 회의를 개최하는 일. (특히 위원회는 특정 결정을 논의할 회의나 논의 자리에 해당 결정의 이해관계자를 초대하는 것이 권장됩니다.)
- 하나 이상의 팀이 요청한 결정으로서, 해당 팀들이 공개 제안 없이 내릴 수 있는 통상적인 관할에 속하는 결정을 내리는 일. (팀은 위원회의 결정을 요청하지 않고도 위원회의 의견을 구할 수 있다는 점에 유의하십시오.)
- 팀들의 관할이 겹치거나 모호한 영역에서 일회성 판단을 내리는 일(단, 해당 팀들의 관할을 변경하는 것은 공개 정책 결정이어야 합니다).
- 이 문서나 향후 위원회 정책에서 운영 결정으로 명시하는 모든 결정.
위원회 결정에 대한 의견 제시 메커니즘에 관한 자세한 내용은 책임성 절을 참조하십시오.
위원회가 필연적으로 비공개로 내려야 하는 결정
일부 결정은 필연적으로 개인이나 다른 주체의 사적인 세부 사항을 수반하며, 이러한 세부 사항을 공개하면 해당 개인이나 주체(예: 안전)와 프로젝트(신뢰 약화) 모두에 부정적인 영향을 미치게 됩니다.
이 추가 제약은 예외적인 경우로 간주해야 합니다. 이는 다음 절에 따라 공개 제안이 필요한 결정을 내리는 것까지 허용하지는 않습니다. 다만 위원회가 내부적으로 내리는 결정을 공개 감독에 필요한 정보를 모두 제공하지 않은 채 비공개로 유지하는 것은 허용합니다.
위원회는 해당 사안이 자신의 관할 밖이라고 판단하는 경우(그래서 다른 팀에 맡기기로 하는 경우)나 해당 사안을 공개적으로 처리해야 한다고 판단하는 경우 등에는 비공개로 결정을 내리는 것을 거절할 수도 있습니다. 그러나 그러한 경우에도 위원회는 기밀로 공유받은 정보를 공개적으로 밝힐 수 없습니다(그렇지 않으면 위원회가 그러한 정보를 받을 만큼 신뢰받지 못할 것이기 때문입니다). 안전에 대한 위협이 임박한 경우에는 당연히 예외가 적용됩니다.
비공개 결정으로 정책을 수립해서는 안 됩니다. 또한 위원회는 공개 제안을 피하려는 목적으로 비공개 결정을 반복하여 사실상의 불문 정책을 수립하는 일이 없도록 노력해야 합니다. 자세한 내용은 “반복과 예외”를 참조하십시오.
다음 목록은 위원회가 부분적으로 또는 전적으로 비공개로 내릴 수 있는 결정을 빠짐없이 열거한 것입니다.
- 출범 전에 기밀 유지가 필요한 새로운 업계 / 오픈 소스 이니셔티브와의 관계를 결정하는 일.
- 대인 관계의 역학/갈등이 얽힌 팀 간 분쟁의 개인적 측면을 논의하는 일.
- 프로젝트를 대표하여 제3자와의 계약 협상에 참여하는 일(예: 프로젝트에 제공되는 자원을 수락하는 일).
- 정치, 개인 안전, 또는 사람들이 공개적으로 자유롭게 발언하기에 안전하지 않을 수 있는 기타 주제 중 프로젝트와 관련된 논쟁적 측면을 다루는 결정.
- 팀이나 개인에게 도움과 지원이 필요한지, 그리고 그 이유가 무엇인지를 논의하는 일로, 이는 개인적인 사안과 관련될 수 있습니다.
- 이 문서 또는 향후 위원회 정책이 비공개 결정으로 지정하는 모든 결정.
위원회는 비공개 또는 공개 결정으로 이어지는 비공개 논의에 다른 팀의 구성원을 참여시킬 수 있습니다. 단, 그렇게 함으로써 위원회에 공개된 비공개 정보가 허가 없이 더 넓게 노출되는 경우는 예외입니다. 가능한 경우 위원회는 결정의 영향을 받는 사람이나 팀을 참여시키도록 노력해야 합니다. 이는 추가적인 감독 역할도 합니다.
일부 사안은 완전히 공개하기에는 적합하지 않더라도 더 작고 더 신뢰할 수 있는 범위(예: 모든 프로젝트 구성원, 팀 리드, 또는 관련/영향을 받는 당사자)에서는 공유해도 괜찮을 수 있습니다. 위원회는 해당 정보에 적절한 범위 내에서 가장 넓은 대상과 정보를 공유하도록 노력해야 합니다.
위원회는 정보가 민감한지 불분명한 경우 새로운 결정이나 결정의 일부 측면을 공개하지 않기로 할 수 있습니다. 그러나 시간이 지나 적절한 공유 대상이 누구인지 더 분명해지거나 적절한 공유 대상이 확대되었음이 분명해지면, 위원회는 정보 공유에 관한 결정을 재검토해야 합니다.
위원회는 대인 갈등/분쟁과 관련된 사안에 대해서는 항상 모더레이션 팀을 참여시켜야 합니다. 그러한 사안이 모더레이션 팀의 관할이기 때문이며, 또한 추가적인 감독을 제공하기 위함입니다.
위원회는 한 부분이 비공개여야 한다는 이유만으로 사안 전체를 비공개로 유지하기보다는, 결정이나 관련 논의 중 어느 부분이 반드시 비공개여야 하는지 평가하고, 민감하지 않은 부분을 공개하는 것이 현실적으로 가능한지 검토해야 합니다. 여기에는 논의가 있었다는 사실이나 대략적인 주제가 포함될 수 있으며, 이는 해당 세부 사항 자체가 민감하지 않은 경우에 한합니다.
비공개 사안은 더 이상 민감하지 않게 되면 나중에 공개되거나 부분적으로 공개될 수도 있습니다. 그러나 일부 사안은 결코 공개되지 못할 수도 있으며, 이는 해당 사안이 더 폭넓은 리뷰와 감독의 대상이 결코 되지 않는다는 것을 의미합니다. 따라서 위원회는 비공개 결정을 내리기 전에 신중하고 사려 깊게 판단해야 합니다.
위원회는 비공개 결정을 내리지 않도록 최선을 다해야 합니다. 위원회는 대표들이 그러한 결정을 함께 리뷰하고 그 필요성을 검토하도록 장려하는 적절한 추가 절차를 마련해야 합니다.
위원회가 공개 제안을 통해 내려야 하는 결정
이 범주의 결정을 내릴 때 위원회는 결정을 내리기 전에 더 넓은 Rust 프로젝트로부터 공개적으로 피드백을 구해야 합니다. 이러한 결정은 적절한 공개 결정 절차를 통해 제안되고 결정되며, 현재는 RFC 절차가 이에 해당합니다(다만 위원회는 향후 다른 공개 제안 절차를 채택할 수 있습니다). 공개 결정 절차는 대표자들의 동의(적극적 찬성 또는 이의 없음을 통한 동의)를 요구해야 하고, 위원회 대표자들이 차단 이의를 제기할 수 있도록 허용해야 하며, 공개적인 평가와 논의를 위한 합리적인 시간을 제공해야 하고, 위원회에 공개 피드백을 전달할 수 있는 명확한 경로를 제공해야 합니다.
기존 RFC 절차에 따라, 공개 제안은 결정이 효력을 발휘하기 전에 피드백을 위한 최소 유예 기간을 두어야 합니다. 모든 대표자는 특정 결정에 대한 피드백 기간을 총 최대 20일까지 연장하도록 요청할 수 있습니다. 위원회는 내부 운영 결정을 통해 피드백 기간을 20일을 넘겨 연장할 수 있습니다. 피드백 유예 기간은 제기된 이의가 없어야 한다는 조건을 포함하여 승인에 필요한 나머지 요건이 모두 충족된 때에만 시작됩니다. 유예 기간 중에 이의가 제기되었다가 해소되면, 대기 기간은 처음부터 다시 시작됩니다.
위원회는 팀, Rust 프로젝트, 커뮤니티의 변화하는 요구를 충족하기 위해 시간이 지남에 따라 발전해 나갈 것으로 예상됩니다. 이러한 발전적 변화는 범위가 작을 수도 있고 클 수도 있으며, 그에 상응하는 수준의 감독이 필요합니다. 위원회의 형태에 실질적인 영향을 미치는 변경은 공개 결정 절차를 거쳐야 합니다.
위의 내용에 대한 예외로, 단일 최상위 팀(모더레이션 팀 제외)의 변경 또는 폐지는 해당 최상위 팀이 위임한 대표자를 제외한 위원회의 만장일치 합의로 이루어질 수 있습니다.
위원회는 최종적으로 공개 제안이 되거나 공개적으로 알려지는 내부 결정이 되는 사안에 대해서도 비공개 논의를 할 수 있습니다. 논의가 민감한 경우, 위원회는 결정 참여자들이 더 솔직하고 자유롭게 발언할 수 있도록 이렇게 하기를 원할 수 있습니다. 또한 경우에 따라서는 공개할 수 없는 비공개 정보가, 그렇지 않았다면 공개 결정이나 공개 제안이 되었을 사안에 영향을 미칠 수 있습니다. 위원회는 최대한 투명하고 오해의 소지가 없도록 노력해야 하며, 모든 근거가 비공개인 불투명한 결정은 피해야 합니다.
(이 문서나 향후의 공개 제안을 통해) 다른 범주에 속한다고 명시적으로 지정되지 않은 한 모든 결정은 이 범주에 속한다는 점에 유의하십시오. 따라서 이 목록은 (다른 두 범주의 목록과 달리) 의도적으로 모호하고 광범위하게 작성되었습니다. 이 목록은 반드시 무언가를 규정하려는 것이 아니라, 어떤 결정이 이 범주에 속할 가능성이 높은지에 대한 지침을 제공하기 위한 것입니다.
- 위원회의 의사 결정자 목록이나 위원회의 의사 결정 절차를 변경하는 효과를 갖는 모든 결정입니다. 예를 들면 다음과 같습니다.
- 이 목록(또는 이 문서 전반)을 변경하는 것입니다.
- 위원회의 공개 제안에 사용되는 공표 및 승인 절차를 변경하는 것입니다. 이러한 제안은 제안된 새 절차가 아니라 기존에 확립된 절차를 따라야 합니다.
- 위원회 대표자의 자격 요건에 영향을 미치는 정책을 추가, 변경 또는 폐지하는 것입니다.
- 하나 이상의 최상위 팀을 추가, 변경 또는 폐지하는 것입니다. 여기에는 다음이 포함됩니다.
- 최상위 팀의 관할을 사실상 다른 팀이 될 정도로 변경하는 것입니다.
- 최상위 팀이 다른 팀 아래로 들어가도록 프로젝트를 재편하는 것입니다.
- 최상위 팀이 위임한 대표자 외에 다른 유형의 위원회 대표자를 추가하는 것입니다.
- 위원회 정족수 또는 구속력 있는 결정을 내릴 수 있는 장소에 관한 정책을 추가, 변경 또는 폐지하는 것입니다.
- 일회성 운영 결정이 아닌 모든 정책 결정입니다. (정책 결정과 운영 결정의 차이에 대한 자세한 내용은 의사 결정 섹션을 참고하십시오.) 여기에는 프로젝트의 다른 부분(예: 다른 팀이나 개인)의 결정을 구속하여 사실상 모든 팀의 통상적인 관할에 대한 예외로 작용하는 모든 결정이 포함됩니다. 정책 결정의 몇 가지 예는 다음과 같습니다.
- 이전에 RFC를 통해 만들어진 정책을 포함하여 기존 정책을 변경하거나 확장하는 것입니다.
- Rust 프로젝트 소프트웨어나 Rust 프로젝트의 기타 작업에 영향을 미치는 법률 및 라이선스 정책입니다.
- 행동 강령의 변경.
- Rust 프로젝트 또는 그 산하 팀의 구성원 자격에 영향을 미치는 정책.
- 모더레이션 팀이 위원회 대표 또는 위원회 전체를 모더레이션하는 방식의 변경. 이러한 결정은 모더레이션 팀과 공동으로 내려야 합니다.
- Rust 프로젝트를 대신하여 지속적인 의무를 지는, 다른 프로젝트나 조직과의 합의. (해당 의무에 동의한 팀이 관련된 일회성 의무는 괜찮습니다.)
- 법적 구조의 신설 또는 상당한 수정(예: 추가 재단 설립, Rust 재단과의 관계 변경, 다른 법인과의 제휴).
- 하나 이상의 팀이 요청한 정책 결정으로서, 원래 해당 팀의 통상적인 관할에 속하는 결정을 내리는 것. (팀은 위원회의 결정을 요청하지 않고도 위원회의 의견을 구할 수 있다는 점에 유의하십시오.)
- 특정 유형의 향후 결정을 다른 팀에 위임하지 않고 항상 위원회의 권한에 속하도록 정하는 것.
- 이 문서 또는 향후 위원회 정책이 공개 정책 결정으로 명시하는 모든 결정.
이해 상충
위원회 대표는 자신에게 이해 상충이 있는 결정에 참여하거나 영향력을 행사해서는 안 됩니다.
이해 상충의 잠재적 원인에는 다음이 포함되며, 이에 국한되지 않습니다:
- 개인적 관계: 자기 자신에 관한 결정
- 재정적 관계: 대표에게 실질적인 재정적 영향을 미치는 결정
- 고용 또는 이에 준하는 관계: 같은 회사의 다른 사람이 관련된 결정, 또는 해당 회사에 다른 곳보다 불균형하게 더 큰 이익이나 손해를 주는 결정
- 직업적 관계 또는 기타 소속: 산업·직업·표준·정부 기관 등 대표가 관계를 맺고 있는 조직이 관련된 결정
- 가족/친분 관계: 대표가 공정할 것이라고 기대하기 어려운 사람에 관한 결정(해당 인물을 통해 생기는 다른 유형의 이해 상충 포함, 예: 가족 구성원의 사업)
위원회 대표는 이해 상충을 즉시 공개하고 영향을 받는 결정에서 스스로 물러나야 합니다. 또한 위원회 대표는 잠재적 이해 상충이 생길 만한 원인을 매년 다른 대표와 모더레이션 팀에 미리 공개해야 합니다.
제안에 특정 주체가 명시되지 않더라도 이해 상충이 발생할 수 있다는 점에 유의하십시오. 예를 들어, 위원회 대표는 자신의 지위를 이용하여 제안의 요구 사항을 자신의 고용주에게 불균형하게 유리하도록 조정할 수 없습니다.
Rust 커뮤니티 전반에서 널리 지지받는 제안은, 그 제안이 특정 주체에 유리하지 않은 한, 대표의 고용주나 이에 준하는 곳도 그 제안의 전반적인 분야를 지지한다는 이유만으로 자동으로 그 대표의 이해 상충이 되지는 않습니다. 예를 들어, 특정 Rust 구성 요소의 보안을 개선하자는 제안은 대표의 고용주가 일반적으로 Rust 보안에 관심을 둔다는 이유만으로 그 대표에게 이해 상충이 되지는 않습니다. 그러나 특정 개발자나 보안 전문가를 참여시키자는 제안, 또는 자신의 보수가 그러한 제안에 좌우되는 경우에는 여전히 이해 상충이 생길 수 있습니다.
위원회는 이해 상충이 해당되는 경우, 이를 경미하다고 판단하더라도 면제할 수 없습니다. 다만 위원회는 애초에 이해 상충이 존재하는지 여부를 평가할 수 있습니다. 위원회 대표는 위원회가 그러한 판단을 내릴 수 있도록 잠재적 이해 상충을 제기해야 합니다.
위원회는 결정에서 물러난 대표에게 특정 정보를 요청할 수 있으며, 그 대표는 요청을 받으면 해당 정보를 제공할 수 있습니다.
가능하고 실용적인 경우, 위원회는 이해 상충의 범위를 줄이기 위해 결정을 분리해야 합니다. 예를 들어, 그렇게 함으로써 이해 상충이 후자의 결정에만 해당되게 된다면, 위원회는 특정 유형의 하드웨어에 접근할 수 있도록 마련하는 결정(구체적인 요구 사항을 정하거나 공급업체를 선정하지 않는 결정)을 정확히 어떤 하드웨어를 어디에서 구매할지에 대한 결정과 분리할 수 있습니다.
대표가 Rust 프로젝트의 이익과 어떤 프로젝트 팀의 이익을 동시에 고려하는 것이 반드시 이해 상충인 것은 아닙니다. 특히 대표는 소속 팀의 대리인으로서 해당 팀과 관련된 결정에 정기적으로 참여할 것으로 기대됩니다.
드물기는 하지만 제안된 결정이 충분히 많은 대표에게 이해 상충을 일으켜 나머지 인원으로는 이전에 정한 정족수 요건을 충족할 수 없고, 그럼에도 결정을 반드시 내려야 하는 경우, 최상위 팀들이 해당 결정을 위한 대리 대표를 지정하거나, (공개 결정에 한해) 위원회가 모든 이해 상충을 공개적으로 문서화하면서 결정을 진행하기로 선택할 수 있습니다. (이해 상충을 문서화하더라도 공개 결정을 진행한다고 해서 실제로 이해 상충이 없어지거나 이해 상충이 결정에 영향을 미치는 것을 막을 수 있는 것은 아니며, 단지 이해 상충이 결정에 영향을 미쳤을 수 있는지를 대중이 판단할 수 있게 할 뿐이라는 점에 유의하십시오. 충돌을 완전히 없애는 편이 언제나 더 바람직합니다.) 이런 경우 위원회는 비슷한 충돌이 다시 생기지 않도록 적절한 절차와 정책을 검토해야 합니다.
팀 관할의 결정 및 변경
위원회는 어떤 영역이나 활동을 이미 있는 최상위 팀이나 새로 만든 최상위 팀(모더레이션 팀 제외)의 관할 사이에서 옮길 수 있습니다. 각 최상위 팀은 자기 관할을 더 세분할 수 있지만, 위원회는 최상위 관할만 옮기거나 조정합니다. 세분된 관할을 옮기는 경우, 위원회는 관련 팀들과 협력하여 적절한 후속 조치를 조율합니다. 이 메커니즘은 기존 팀의 관할이 너무 넓어서 현재 구조로는 그 팀이 관할 전체를 감당하리라고 기대하기 어렵다고 위원회가 판단할 때 사용해야 합니다. 그러나 팀이 일부 업무를 수행할 자원이 당장 부족할 뿐인 경우에는 이 메커니즘을 사용해서는 안 됩니다.
또한 최상위 팀의 관할을 확대하려면 위원회의 승인을 받아야 하며, 최상위 팀의 관할을 축소할 때는 위원회에 알려야 합니다. 이런 일은 대개 팀이 스스로 관할을 확대하거나 축소하기로 정할 때 일어납니다. 최상위 팀들이 서로 관할을 조정하기로 합의하는 과정에서 일어날 수도 있습니다. 위원회가 관할 변경을 알고 있어야 하는 이유 중 하나는 위원회가 그 관할을 다른 곳에 다시 배정하거나 의도적으로 배정하지 않은 채로 둘 수 있도록 하기 위해서입니다.
다만 팀은 (단독으로든 공동으로든) 위원회의 승인 없이 자기 관할을 하위 팀에 다시 위임할 수 있습니다. 최상위 팀은 위임을 하더라도 자신에게 배정된 관할 전체에 대해 계속 책임을 집니다(다시 말해, 위임이 성공적으로 이루어지도록 하는 것도 팀의 책임입니다).
팀 사이에서 관할을 옮기는 것은 비교적 무거운 조치이므로, 위원회는 그에 앞서 팀과 함께 대안 전략을 모색하는 쪽을 우선해야 합니다. 또한 이 메커니즘의 사용 사례 중 하나는, 이전에 어떤 팀에 위임했지만 그 팀이 사실상 더 이상 존재하지 않게 된 경우(예를 들어 팀에 시간을 낼 수 있는 사람이 아무도 없는 경우) 그 관할을 옮기는 것이며, 이는 시간과 역량을 갖춘 사람들이 나타나 그 팀을 다시 꾸릴 때까지 비교적 일시적으로 이루어질 수도 있다는 점도 짚어 둘 만합니다. 이 절은 이러한 협의를 정확히 어떻게 할지(또는 할지 말지)에 대해 위원회에 의도적으로 제약을 두지 않습니다.
감독 및 책임 메커니즘
다음은 위원회가 자신과 다른 이들의 책임을 확보하기 위해 사용하는 여러 메커니즘입니다.
위원회의 책임 확보
위원회는 더 넓은 프로젝트와 커뮤니티가 위원회에 거는 기대가 꾸준히 충족되도록 공개적으로 보장해야 합니다. 이는 위원회의 정책, 절차, 결과를 조정하는 방식과 함께, 프로젝트와 커뮤니티의 기대가 현실과 맞지 않을 때 이를 알리고 설명하는 방식으로 이루어져야 합니다.
이를 위해 위원회는 대표를 교대하고 “기본적으로 공개“라는 원칙을 따르는 것에 더해, 정기적으로(적어도 분기마다) 누구나 쉽게 볼 수 있는 방식으로 활동 내용을 공개해야 하며, 직무, 기대, 제약 목록을 평가 기준으로 삼아 위원회가 얼마나 잘 기능하고 있는지에 대한 평가도 함께 제공해야 합니다.
위원회는 해마다 참여할 뜻과 여건이 있는 모든 프로젝트 구성원에게 위원회가 그 목적을 효과적으로 수행하고 있는지에 대한 의견을 구해야 하며, 모든 프로젝트 구성원의 적극적인 참여를 허용하고 장려하는 자리에서 이 의견을 공개적으로 논의해야 합니다. 이를 위해 위원회와 다른 프로젝트 구성원은 이 문서와 이후의 모든 개정본에 나열된 상위 수준의 직무, 기대, 제약을 참고하여 위원회가 직무와 의무를 다하고 있는지 판단합니다.
또한 이 문서, 다른 위원회 정책 및 절차, 또는 위원회의 책임성에 관한 다른 모든 측면을 따르지 않는 일을 주시하고, 지적하며, 그에 동조하기를 거부하는 것은 모든 대표 개개인의 책임입니다. 대표는 “책임 분산”을 적극적으로 피하도록 힘써야 합니다. 책임 분산이란 구성원 각자가 (의식적으로든 무의식적으로든) 다른 누군가가 그 일을 하리라고 믿는 바람에 집단 전체가 그 일을 하지 못하게 되는 현상입니다. 위원회는 절차 문제를 처리하고 점검하며 특히 절차상 이의를 제기하는 책임을 맡을 역할을 따로 지정할 수도 있습니다. 그렇더라도 다른 사람들 역시 여전히 그렇게 할 수 있고, 그렇게 해야 합니다.
위 과정의 어느 부분에서든 위원회가 의무를 다하고 있지 않다는 결론이 나오면, 위원회가 의무를 더 잘 다할 수 있도록 어떻게 바뀔지에 대한 계획을 되도록 빨리 제시해야 합니다. 이를 위해 헌장 등을 변경하는 RFC, 대표 교대, 또는 그 밖의 실질적인 변경이 필요할 수 있습니다. 모든 계획에는 지난 한 해의 경험에 비추어 위원회나 Rust 거버넌스 전체, 또는 둘 다가 어떻게 발전할지에 대한 구체적인 방안이 담겨야 합니다.
위원회 대표의 책임 확보
위원회 대표자는 대표자로서의 의무를 얼마나 잘 수행하고 있는지 돌아보기 위해 서로 간에, 그리고 각자의 최상위 팀과 정기적으로 피드백을 주고받아야 합니다(그 구체적인 방식은 이 문서의 범위를 벗어납니다). 피드백 세션의 목표는 대표자가 프로젝트에 더 잘 기여할 수 있는 방법을 더 잘 이해하도록 돕는 것입니다. 이 피드백은 모든 대표자, 해당 대표자가 속한 최상위 팀의 모든 구성원, 그리고 모더레이션 팀과 공유해야 합니다. 이 피드백에서는 대표자가 잘한 점과 더 잘할 수 있었던 점을 모두 물어야 합니다.
이와 별도로, 대표자는 언제든지 자신의 팀과 동료 대표자로부터 비공개 피드백을 받을 수 있도록 열려 있어야 하며, 위원회에서 자신이 맡은 역할과 그 효과성에 대해 정기적으로 스스로 돌아보아야 합니다.
안전하고 열린 절차를 보장하기 위해, 이러한 피드백 절차에서 나온 결과물은 절대 공개해서는 안 됩니다. 또한 위원회는 결과가 긍정적인 변화로 이어지지 않는 경우 피드백 절차를 되돌아보고 조정해야 합니다.
위원회의 다른 구성원이 어떤 위원회 대표자가 나머지 위원회 구성원과 잘 협력하지 못한다고 느낀다면, 해당 대표자와 대화해야 하며 필요한 경우 그 대표자의 팀과도 대화해야 합니다. 위원회 대표자는 이러한 대화를 원활하게 진행하기 위해 필요에 따라 모더레이션/중재 자원을 활용해야 합니다. 모더레이션은 해당 이슈를 해결하는 데 도움을 줄 수 있으며, 해당 이슈가 조치 가능한 것인지, 어느 정도의 에스컬레이션이 필요한지 판단하는 데도 도움을 줄 수 있습니다.
개별 팀이 자신의 대표자에게 어떻게 책임을 지우는지 규정하는 것은 이 문서의 범위를 벗어나지만, 각 팀이 위의 메커니즘을 참고하여 자체 정책과 절차를 마련할 것을 권장합니다.
팀의 책임성 보장
팀들은 정기적으로 서로 조율하고 협력하며 각자의 필요에 대해 대화합니다. 일반적인 상황에서 위원회는 개별 팀의 자율성을 존중해야 합니다.
그러나 위원회는 팀들이 서로에게, 그리고 프로젝트 전체에 대해 공동으로 서로의 책임을 묻는 수단으로 기능합니다. 위원회는 다음을 할 수 있습니다.
- 다른 팀이나 프로젝트 전체의 고려 사항을 반영하지 못한 결정에 대해 팀에 재고를 요청합니다.
- 팀들이 다른 팀을 보다 정기적으로 고려하는 절차를 수립하도록 장려합니다.
- 팀들의 관할에 대한 공통된 이해를 보장합니다.
- 팀들이 그 관할을 수행할 의지와 능력을 갖추도록 보장합니다.
- 한 팀의 관할을 보다 관리하기 쉬운 단위로 나누는 새로운 팀을 설립합니다.
책임성 절차는 징벌적이어서는 안 되며, 해당 팀들과의 적극적인 협력 속에서 진행되어야 합니다.
팀이 더 넓은 프로젝트에 대해 고의로 선의에 따라 행동하지 않기를 선택하는 극단적인 상황에서, 위원회는 팀의 관할을 변경하거나, 팀 관할의 일부를 다른 팀으로 옮기거나, 팀을 완전히 없앨 권한을 가집니다. 이는 위원회의 일반적인 의사 결정 절차를 통해 이루어집니다. (이는 모더레이션 팀에는 적용되지 않습니다. 위원회와 모더레이션 팀 간의 책임성에 대해서는 다음 절을 참조하십시오.)
각주
-
이 문서 전체에서 “팀“은 하위 팀, 워킹 그룹, 프로젝트 그룹, 이니셔티브를 비롯하여 프로젝트 내의 모든 형태의 공식 협업 구조를 포함합니다. “하위 팀“은 어떤 팀을 거쳐 상위로 보고하는 모든 형태의 협업 구조를 포함합니다. ↩
-
여러 최상위 팀에 속하는 하위 팀이나 개인은 위원회에서 자신을 대변하는 대표자를 여러 명 둠으로써 불균형한 대표성을 가져서는 안 됩니다. 조직 내 어디에든 이와 같은 “다이아몬드” 구조가 존재하는 경우, 그 구조에 관련된 팀들은 모호함이나 책임 분산을 피하기 위해 노력해야 하며, 사람들과 팀들이 문제를 제기하고 피드백을 제공할 때 어떤 경로를 이용해야 하는지 알 수 있도록 해야 합니다. ↩
-
이는 사실상 위원회 대표의 수도 같은 범위로 제한합니다. 이 제한은 그 자체로도 중요하다는 점에 유의하십시오. ↩
-
위원회는 최상위 팀들이 파견한 대표로만 구성되며, 새로운 구성원을 임시로 직접 임명할 수 없습니다. 다만 위원회가 프로젝트에서 공백을 발견한 경우에는 새로운 최상위 팀을 만들 수 있습니다. 특히 위원회는 프로젝트가 현재 조율되고 체계화된 전문성을 갖추지 못했으며 위원회도 그 문제를 해결할 팀에 헌장을 부여할 적절한 해결 구조를 알지 못하는 문제에 대응하기 위해, 팀 창설의 발판을 마련할 수 있습니다. 그러한 경우 위원회는 해당 문제의 해결 공간을 탐색하고, 올바른 해결책을 결정하고, 제안과 헌장을 가지고 위원회에 돌아오는 것을 관할로 하는 팀을 구성할 수 있습니다. 그러면 그 팀은 위원회에 대표를 파견하며, 이 대표는 해당 문제와 해결책의 여러 측면에 관해 위원회와 협력할 수 있습니다. ↩
-
위원회 대표가 된다는 것은 궁극적으로 소속 팀과 프로젝트 전체에 봉사하는 자리입니다. 그 자리를 맡는 사람이 누구든 보람과 흥미를 느끼기를 바라지만, 동시에 그 자리가 차지하려고 다투는 지위로 여겨지지 않기를 바랍니다. ↩
-
실제로 인프라 팀 전체가 모든 자격 증명에 접근할 수 있는 것은 아니며, 팀 내부적으로 최소 권한 원칙을 지키려고 노력합니다. ↩
-
위원회는 이러한 역할을 위원회 대표에게만 맡겨야 하는 것은 아니며, 의향이 있는 프로젝트 구성원이라면 누구든 임명할 수 있습니다. 이러한 역할을 맡는다고 해서 의사 결정 등의 목적상 위원회 구성원이 되는 것은 아닙니다. ↩
모더레이션, 의견 불일치, 갈등
이 절에서는 의견 불일치와 갈등의 해결을 돕는 리더십 위원회와 모더레이션 팀의 역할, 그리고 두 팀 사이의 상호작용을 설명합니다.
의견 불일치와 갈등은 대인 상호작용이라는 하나의 스펙트럼 위에 놓여 있습니다. 의견 불일치는 주로 사실이나 기술 면에서 생기는 어긋남이고, 갈등은 주로 협업을 가로막는 사회적 또는 관계적 장애물입니다. 많은 상호작용에는 의견 불일치와 갈등의 측면이 함께 나타날 수 있습니다. 위원회는 의견 불일치의 측면에서 도움을 줄 수 있으며, 갈등의 측면은 모더레이션 팀의 관할입니다.
이 문서는 모더레이션 정책 전반을 규정하지 않으며, 위원회와의 상호작용 및 위원회와 모더레이션 팀 사이의 견제와 균형을 규정하는 데 필요한 부분만 다룹니다. 일반적인 모더레이션 정책은 이 문서의 범위에 포함되지 않습니다.
Rust 프로젝트의 작업 대부분은 다른 사람들과의 협업을 수반하며, 그들 모두 자신의 작업을 깊이 아낍니다. 사람들의 의견이 서로 다르고, 그 차이에 대해 강하게 느끼는 것은 자연스러운 일입니다. 의견 불일치는 문제를 드러내고 해결하는 강력한 도구가 될 수도 있으며, 이상적으로는 의견이 다른 사람들이 대인 갈등으로 번지지 않고 협력적이며 (대체로) 우호적인 방식으로 그 차이를 탐구할 수 있습니다.
의견 불일치와 갈등이 발생하는 상황은 복잡할 수 있습니다. 의견 불일치는 갈등으로 격화될 수 있고, 갈등은 의견 불일치로 완화될 수 있습니다. 상황에서 의견 불일치와 갈등의 구분이 명확하지 않거나 참여자들의 판단이 서로 다르다면, 그 상황을 갈등으로 간주하십시오.
갈등이 발생하면 관련 당사자들은 갈등 해결에 도움을 받을 수 있도록 가능한 한 빨리 모더레이션 팀에 연락해야 합니다. 갈등이 악화되거나 더 큰 피해를 일으키기 전에 해결하려면 시간이 매우 중요한 자원입니다.
팀 간의 의견 불일치
가능한 경우 팀들은 필요에 따라 위원회의 도움을 받아 의견 불일치를 스스로 해결하도록 노력해야 합니다. 위원회는 의견 불일치를 해결하기 위해 판단을 내릴 수 있지만, 의견 불일치가 지속되거나 갈등으로 격화되지 않도록 팀들은 서로 좋은 협업 관계를 유지해야 합니다.
팀 간 의견 불일치를 해결하는 방법으로는 이전에 논의된 선택지를 고르는 것, 새로운 선택지를 고안하는 것, 해당 결정이 누구의 관할에 속하는지 정하는 것, 또는 해당 결정이 두 팀 모두의 관할 밖에 있다고 판단하고 그 작업을 맡을 새로운 곳을 찾는 일을 위원회에 맡기는 것 등이 있을 수 있습니다.
팀 또는 프로젝트 구성원이 관련된 갈등
팀 또는 프로젝트 구성원이 관련된 갈등은 가능한 한 빨리 모더레이션 팀에 알려야 합니다. 위원회는 그러한 갈등이 보류 중이거나 긴급한 결정에 미치는 영향을 줄이도록 도울 수 있지만, 팀 사이의 문제이든 아니든 갈등과 대인 문제를 돕는 책임은 모더레이션 팀에 있습니다.
개인이나 팀은 구속력 없는 외부 중재처럼 갈등이나 대인 문제를 해결하기 위한 다른 절차에 자발적으로 참여할 수도 있습니다. 그렇게 하는 경우 개인이나 팀은 모더레이션 팀에 진행 상황을 계속 공유해야 하며, 적절한 자원이나 접근 방식에 대해 모더레이션 팀의 안내를 구해야 합니다. 개인이나 팀은 이해 상충을 일으킬 수 있는 자원을 사용해서는 안 됩니다.
예비 모더레이터
모더레이션 팀은 항상 “예비 모더레이터” 목록을 공개 문서로 유지해야 하며, 예비 모더레이터는 내부 동의 결정을 통해 모더레이션 팀과 위원회 모두의 승인을 받아야 합니다. 모더레이션 팀과 예비 모더레이션 팀은 각각 최소 세 명의 구성원으로 이루어져야 합니다. 예비 모더레이터는 다음 조건을 충족해야 합니다.
- 현재 모더레이션 팀 또는 리더십 위원회에 속하지 않아야 합니다.
- 위원회와 모더레이션 팀이 함께 판단하기에 Rust 프로젝트 구성원들로부터 폭넓은 신뢰를 받아야 합니다. 이는 대개 이미 어떤 형태로든 프로젝트에 참여하고 있다는 뜻입니다.
- 위원회와 모더레이션 팀이 공동으로 판단하기에 모더레이션 업무와 감사를 수행할 자격을 갖추고 있어야 합니다. 보다 상세한 기준과 지침은 모더레이션 정책에서 정하며, 이는 이 문서의 범위를 벗어납니다.
- 예비 모더레이터로 봉사할 의사가 있을 것: 감사를 수행할 의사가 있고, 모더레이션 팀이 해산되거나 활동할 수 없게 되는 경우 새로운 정식 모더레이터를 임명할 수 있을 때까지 임시 모더레이션 업무를 수행할 의사가 있어야 합니다. (예비 모더레이터가 장기적으로 모더레이션 업무를 수행할 의사까지 있을 것으로 기대하지는 않습니다.)
- 모더레이션 팀 구성원에게 기대되는 수준으로 모더레이션 정책과 절차를 계속 숙지할 의사가 있어야 합니다(관련 교육 포함). 예비 모더레이터는 가능한 한 모더레이션 팀과 동일한 교육 기회를 받아야 합니다.
예비 모더레이터가 필요해지는 때는 긴장이 고조된 상황이며, 프로젝트와 위원회는 그러한 상황에 나서는 예비 모더레이터를 신뢰할 준비가 되어 있어야 합니다. 프로젝트의 다른 구성원들에게 잘 알려져 있고 신뢰받는 사람을 선정하면 그러한 상황에서 긴장을 낮추는 데 도움이 됩니다.
모더레이션은 번아웃이 심한 활동이며, 개별 모더레이터나 모더레이션 팀이 그 업무에서 물러나기를 원하게 될 수도 있습니다. 한 명 이상의 개별 모더레이터는 언제든지 물러나기로 선택할 수 있으며, 이 경우 모더레이션 팀은 공백이나 부족분을 메울 새로운 모더레이터를 찾아 영입해야 합니다. 모더레이션 팀이 예비 모더레이터에게 정식 모더레이터가 되어 달라고 요청하는 경우, 팀은 새로운 예비 모더레이터를 임명해야 합니다. 물러난 개별 모더레이터도 예비 모더레이터로 선정될 수 있습니다. 모더레이션 팀 전체가 동시에 활동할 수 없게 되거나(위원회와 예비 모더레이터가 내부 동의 결정을 통해 공동으로 판단합니다) 동시에 물러나기로 선택하는 경우, 예비 모더레이터가 임시 모더레이션 팀이 되며, 신속하게 새로운 예비 모더레이터를 임명하고 새로운 정식 모더레이터를 찾기 시작해야 합니다.
예비 모더레이터 역할에는 예외적인 상황 외에 정기적으로 요구되는 활동이 없으므로, 이 역할에 임명된 사람은 여전히 그 역할을 맡을 의사가 있는지 재확인하고, 예비 모더레이터가 갑자기 필요해졌는데 활동할 수 없는 것으로 드러나는 상황을 피하기 위해 모더레이션 팀과 정기적으로 점검해야 합니다.
모더레이션 팀 정책 및 절차
모더레이션 팀은 견고한 정책과 절차를 마련해 둘 의무가 있습니다. 위원회는 모더레이션 팀이 그러한 정책과 절차를 갖추고 있고 그것이 충분히 견고하도록 감독하고 지원합니다.
위원회는 모더레이션 팀에 피드백을 제공할 수 있으며, 모더레이션 팀은 받은 모든 피드백을 검토해야 합니다. 위원회는 모더레이션 팀이 모더레이션 정책과 절차를 따르지 않았다고 판단하는 경우, 예비 모더레이터에게 감사를 요구할 수 있습니다. 그러나 위원회는 모더레이션 결정이나 정책을 번복할 수 없습니다.
감사
위원회 구성원 중 누구라도 모더레이션 결정(또는 일련의 결정)이 모더레이션 팀의 정책과 절차를 따르지 않았다고 판단하는 경우, 신속하게 모더레이션 팀에 알려야 합니다. 그런 다음 위원회와 모더레이션 팀은 서로 소통하며 이러한 우려를 논의하고 이해하고, 이를 해결하기 위해 노력해야 합니다.
이 문서가 모더레이션 팀의 조치를 프라이버시를 보호하는 방식으로 점검하기 위해 제공하는 메커니즘 중 하나가 감사 메커니즘입니다. 위원회 구성원 중 누구라도 모더레이션 팀의 조치가 문서화된 정책이나 절차를 따르지 않았다고 판단하는 모든 경우에, 해당 위원회 구성원은 감사 절차를 개시하기로 결정할 수 있습니다. (특히 모더레이션 상황에 관련된 커뮤니티 구성원의 신고에 대응하여 그렇게 할 수 있습니다.) 이는 위에서 설명한 소통 및 대화에 더하여 이루어지는 것이며, 위원회와 모더레이션 팀 간의 직접적인 소통을 대체하는 것이 아닙니다.
감사에서 예비 모더레이션 팀은 모더레이션 팀과 협력하여 모더레이션 팀이 문서화된 정책과 절차를 따랐는지 확인합니다. 이 메커니즘에서는 필연적으로 예비 모더레이션 팀이 자체 판단에 따라 모더레이션 정책, 구체적인 증거나 커뮤니케이션, 그리고 그에 따른 모더레이션 조치나 제안된 조치를 평가하게 됩니다. 그러나 이 메커니즘은 조치 자체를 사후에 문제 삼으려는 것이 아닙니다. 감사 메커니즘은 모더레이션 팀이 수립된 정책과 절차에 따라 행동하고 있는지 확인하는 데 중점을 두며, 아울러 정책과 절차 자체가 초래하는 의도치 않은 부정적 결과를 드러내는 데에도 중점을 둡니다.
예비 모더레이터는 필요할 수 있는 추가 맥락을 파악하기 위해 위원회에도 연락합니다.
모더레이션 절차와 감사는 모두 시간이 걸리며, 성실하게 수행되어야 합니다. 그러나 위원회, 예비 모더레이터, 모더레이션 팀은 모두 서로에게 우려 사항과 기대 사항을 합리적으로 시의적절하게 전달하고 열린 소통 창구를 유지하는 것을 목표로 해야 합니다.
예비 모더레이터는 이해 상충이 있는 결정이나 감사에 참여해서는 안 됩니다. 예비 모더레이터는 해당 예비 모더레이터가 예비 모더레이션 팀의 일원으로 공개적으로 명시되기 전에 모더레이션에 제공된 비공개 정보에 접근해서는 안 됩니다. 이를 통해 모더레이션 팀과 이야기하는 사람들이 잠재적인 우려 사항이나 이해 상충을 평가할 기회를 얻습니다.
위원회 및 예비 모더레이션 팀과 논의하는 과정에서, 정책에 예상하지 못한 조건이 있었거나 정책에 반영할 수 없는 맥락 정보가 있었기 때문에 모더레이션 팀이 특정 사안에 대해 정책상 예외를 두어야 했다는 사실이 드러날 수 있습니다. 이는 예상되는 시나리오로서, 예외를 둔 근거와 예외의 필요성을 판단한 절차에 대해 예비 모더레이션 팀이 추가로 면밀히 검토할 만한 사안이지만, 그 자체로 모더레이션 팀의 책무를 위반한 것은 아닙니다.
감사 절차와 위원회·모더레이션 팀 간 논의가 진행됨에 따라, 모더레이션 팀은 모더레이션 정책을 변경하거나, 특정 모더레이션 결정 또는 결정안의 결과를 변경하거나, 둘 다 하기로 결정할 수 있습니다. 이는 전적으로 모더레이션 팀이 내릴 결정입니다.
예비 모더레이션 팀은 감사 결과를 모더레이션 팀과 위원회에 보고하여 리뷰를 받아야 합니다. 이 보고에는 직접적으로든 간접적으로든 개인 정보를 드러낼 수 있는 세부 사항이 포함되어서는 안 됩니다. 이 보고는 모더레이션 팀과의 논의와 더불어 위원회의 우려를 해소하는 것을 목표로 해야 합니다.
최후의 수단으로서의 책임 추궁
리더십 위원회와 모더레이션 팀은 각각 Rust 프로젝트 내에서 상당한 권한을 가지고 있습니다. 이 문서는 두 조직이 갈등을 해결할 수 있는 여러 수단을 제공합니다. 이 절에서는 두 팀이 서로에게 책임을 물을 수 있는 최후의 수단을 설명합니다. 이 절은 결코 필요한 일이 없기를, 그리고 각 팀이 이 지경에 이르지 않고 갈등을 해결하기 위해 가능한 모든 노력을 기울이기를 바라며 작성되었습니다.
위원회가 (예비 모더레이션 팀의 감사 보고서에 근거하든 다른 근거에 따르든) 모더레이션 팀에 구조적인 문제가 있다고 판단하고, 위원회와 모더레이션 팀이 이 상황에 대처할 방법에 대해 자발적으로 합의하지 못한다면, 위원회는 최후의 수단으로서 (만장일치 결정으로) 위원회 자신과 모더레이션 팀을 동시에 해산할 수 있습니다. 이 경우 최상위 팀들은 위원회에 새 대표를 임명해야 하며, 예비 모더레이션 팀이 새로운 임시 모더레이션 팀이 됩니다.
반대로, 모더레이션 팀이 위원회에 구조적인 문제가 있다고 판단하고, 위원회와 모더레이션 팀이 이 상황에 대처할 방법에 대해 자발적으로 합의하지 못한다면, 모더레이션 팀은 최후의 수단으로서 (만장일치 결정으로) 모더레이션 팀 자신과 위원회를 동시에 해산할 수 있습니다. 이 절차는 모더레이션 팀 구성원이 세 명 이상인 경우에만 시행할 수 있습니다. 이 경우 최상위 팀들은 위원회에 새 대표를 임명해야 하며, 예비 모더레이션 팀이 새로운 임시 모더레이션 팀이 됩니다.
모더레이션 팀의 대표는 이해 상충을 피하기 위해 위원회와 모더레이션 팀을 해산하는 결정에서 제척되지만, 해당 대표도 마찬가지로 물러나야 합니다.
해임된 대표와 모더레이터는 최소 1년 동안 위원회나 모더레이션 팀 어느 쪽에서도 활동할 수 없습니다.
기본적으로, 새 위원회와 임시 모더레이션 팀이 이 전환 과정을 명확하게 알릴 책임을 맡습니다.
이 메커니즘은 그야말로 최후의 수단입니다. 좋게 말해도 이 메커니즘은 거의 확실히 최선이 아닌 결과를 낳을 것입니다. 상황이 이러한 결과로까지 치닫는다면 이미 많은 것이 끔찍하게 잘못된 것이며, 사태를 수습하는 사람들은 이러한 일이 다시는 일어나지 않도록 노력해야 합니다. (모더레이션 팀이든 위원회든) 상황이 이 지경까지 치달을 수도 있다는 신호를 보낸다면, 이는 협상 테이블에 앉아 이러한 상황을 피하기 위해 “그것이 아닌 다른 무언가“를 할 방법을 찾으라는 강력한 신호로 받아들여야 합니다.
프로젝트 구성원이 관련된 모더레이션 조치
모더레이션 팀은 모더레이션 업무를 수행하는 과정에서 Rust 커뮤니티 구성원뿐만 아니라 Rust 프로젝트 구성원에 대해서도 조치를 취할 수 있는 권한이 반드시 필요합니다. 이러한 조치는 대화에서부터 프로젝트에서의 제명에 이르기까지 단계적 대응의 전 범위에 걸칠 수 있습니다. 이로 인해 모더레이션 팀은 권한과 신뢰를 가진 위치에 놓이게 됩니다. 이 문서는 위원회뿐만 아니라 모더레이션 팀에 대해서도 적절한 책임성과 상호 견제 장치를 마련하고자 합니다.
모더레이션 팀이 Rust 프로젝트 구성원에게 외부에서 보이는 제재(역할에서의 해임이나 1주일을 초과하는 프로젝트 공간 참여 배제처럼 눈에 띄는 부재를 초래하는 모든 조치)를 시행하려는 경우, 어느 당사자든 위원회나 예비 모더레이터에게 연락하여 감사를 요청할 수 있으며, 그 감사 요청은 자동으로 승인됩니다.
2024년 6월까지는 절차가 제대로 작동하는지 확인하기 위해 요청이 없더라도 감사가 자동으로 수행됩니다. 그 이후에는 위원회와 모더레이션 팀이 공동으로 리뷰하여 이 조항의 갱신 여부를 결정합니다.
모더레이션 팀이 프로젝트 구성원에게 경고를 보내거나 프로젝트 구성원에 관한 모더레이션 조치 통지를 보낼 때, 해당 메시지에는 감사를 요청할 수 있다는 점이 함께 안내됩니다.
프로젝트 구성원에 관한 갈등은 가능한 한 빨리 모더레이션 팀에 알려야 합니다.
위원회 대표가 관련된 갈등
위원회 대표 또는 대리인이 관련된 갈등은 프로젝트 구성원이 관련된 갈등과 같은 절차를 따릅니다. 모더레이션 팀은 프로젝트의 다른 구성원과 마찬가지로 대표나 대리인에 대해서도 같은 모더레이션 권한을 가지며, 외부에 드러나는 모든 제재에 대해 예비 모더레이터가 반드시 수행해야 하는 감사도 똑같이 적용됩니다. 이는 모더레이션 팀의 다른 결정과 마찬가지로 같은 책임성 메커니즘의 적용을 받습니다.
모더레이션 팀은 이미 사용할 수 있는 여러 모더레이션 조치에 더하여, 구성원을 프로젝트에서 완전히 제외하는 것보다 한 단계 낮은 조치로서, 거의 최후의 수단으로 대표나 대리인에 대해 다음과 같은 추가 조치를 취할 수 있습니다. 이러한 조치는 일반적으로 위원회에만 국한되지 않으며, 다른 Rust 팀에도 적용됩니다.
- 모더레이션 팀은 대표를 위원회에서 해임하기로 결정할 수 있습니다. 해당 대표가 대표하던 최상위 팀은 남은 임기 동안 활동할 새 대표를 즉시 파견해야 합니다.
- 모더레이션 팀은 프로젝트 구성원이 위원회 대표가 되지 못하도록 결정할 수 있습니다.
- 모더레이션 팀과 위원회(당사자 제외)는 (비공개 운영상 동의 결정으로서) 해당 대표의 위원회 참여를 제한하는 다른 제재를 적용하기로 공동으로 결정할 수 있습니다. (이 경우에는 제재가 실효성을 가지려면 위원회 전체가 협력해야 하므로, 이해 상충이 있는 대표라도 제외되지 않습니다. 이러한 이해 상충 때문에 부분적 제재를 적용할 수 없는 경우, 모더레이션 팀은 언제든지 해임과 같은 전면적 제재를 선택할 수 있습니다.)
이 모든 조치에도 필수 감사가 뒤따릅니다. 대표나 대리인이 관련된 모든 모더레이션 조치나 누군가가 대표가 되는 것을 직접 막는 조치는 위원회에도 반드시 통지해야 합니다.
모더레이션 팀 구성원이 관련된 갈등
모더레이션 팀 구성원이 관련된 갈등은 모더레이션 팀의 나머지 구성원(이해 상충이 있는 사람 제외)이 추가적인 감독을 위해 예비 모더레이션 팀과 함께 처리합니다. 모더레이션 팀 내에 더 구조적인 이슈가 있는 경우, 모더레이션 팀 또는 예비 모더레이션 팀의 구성원은 누구든지 위원회와 협의해야 합니다. 예비 모더레이터는 이 결정을 반드시 감사하고, 위원회와 모더레이션 팀에 감사 보고서를 제출해야 합니다.
프로젝트 그룹
소개
프로젝트 그룹은 특정 프로젝트를 완수하는 것을 목표로 해당 프로젝트를 수행하는 Rust 팀의 한 형태입니다. 프로젝트 그룹은 RFC 2856에서 처음 정의되었습니다. 요약하면 다음과 같습니다.
- 프로젝트 그룹은 팀의 합의(예: RFC)를 통해 만들어지며 하나 이상의 “상위 팀“을 가집니다.
- 이후 그룹은 후속 RFC 작성, 설계 작업 등을 통해 프로젝트를 완료까지 이끕니다.
- 작업이 마무리되면 그룹은 보관 처리됩니다.
- 각 프로젝트 그룹은 일반적으로 다음을 갖춥니다.
- 그룹의 범위와 목표를 정리한 헌장.
- 임명된 셰퍼드와 팀 연락 담당자.
- 관련 저장소.
- Zulip 등의 전용 스트림.
프로젝트 그룹 정의
프로젝트 그룹은 공식 Rust 팀의 요청에 따라 특정 프로젝트나 업무를 수행하는 사람들의 그룹입니다. 일부 프로젝트 그룹은 한시적이어서, 프로젝트가 완료되면 보관 처리됩니다. 그러나 지속적인 작업과 유지 관리를 맡는 프로젝트 그룹도 있습니다.
특정 기능을 중심으로 한 프로젝트 그룹의 예로는 FFI Unwind, Inline ASM, Safe Transmute가 있습니다.
프로젝트의 목표는 조직 내 특정 기능이나 프로젝트를 중심으로 커뮤니티를 만들거나 기존 커뮤니티를 공식화하고, 이 공간을 활용해 해당 기능을 논의하고 반복적으로 개선하는 것입니다.
커뮤니티를 만드는 일에는 설계 과정에서 생겨나는 조직 내 암묵적 기억을 일부 걷어내고, 기능에 관한 정보와 논의를 한곳에 모아 특정 결정과 절충안이 다른 대안보다 왜 선택되었는지를 더 잘 드러내는 일이 포함됩니다.
이전에는 대규모 기능에 관한 논의와 반복 작업의 상당 부분이 최초 RFC 스레드에서 이루어졌습니다. 이로 인해 스레드 앞부분에 많은 논의가 쌓이게 되며, 그 논의는 현재 반복 단계와 전혀 무관해지는 경우가 많습니다.
또한 이 절차는 개발에 여러 해가 걸리고 설계 과정에서 여러 RFC로 나뉘게 되는 기능을 설명하는 데에도 적합하지 않았습니다. 그 예로는 “impl Trait” 기능과 “macros 2.0” 기능이 있으며, 이들은 최초 RFC에서 목표가 크게 바뀌어 현재 상태를 파악하기 어려울 수 있습니다.
프로젝트 그룹 생성
프로젝트 그룹은 다음을 갖추어야 합니다.
- 리드 — 그룹의 리더 역할을 하는 한 명 이상의 사람으로, 일반적으로 최초 헌장 작성과 행정 및 소통 업무 처리를 맡으며, 이러한 책임을 그룹의 다른 구성원에게 위임하는 일도 맡습니다.
- 연락 담당자 — 작업을 후원하는 공식 Rust 팀의 구성원으로, 팀과 그룹 사이의 연락 창구 역할을 합니다. 연락 담당자는 작업에 직접 깊이 관여할 수도 있고 그렇지 않을 수도 있지만, 주기적으로 진행 상황을 확인하고 팀 회의에서 해당 작업을 대변할 수 있어야 합니다. 또한 이 작업이 워킹 그룹 자체의 범위를 넘어 팀에서 진행 중인 다른 작업과 겹칠 수 있는 때를 주의 깊게 살펴야 합니다.
- 연락 담당자는 리드 중 한 명을 겸할 수도 있지만 반드시 그래야 하는 것은 아닙니다.
- 구성원 — 프로젝트 그룹에 정기적으로 참여하거나 기여하는 사람들입니다.
- 그룹의 구성원 자격 요건은 셰퍼드가 결정하며 헌장에 명시해야 합니다.
- 초기 구성원은 해당 분야에서 이미 꾸준하고 생산적으로 참여해 온 사람들로 구성하도록 노력해야 합니다.
- 다만 프로젝트 그룹에 구성원이 많아야 하는 것은 아니며, 일부 프로젝트 그룹은 리드와 연락 담당자를 포함하여 구성원이 한두 명뿐일 수도 있습니다.
- 그룹의 범위와 취지를 정의하는 헌장.
- 헌장과 함께 커뮤니티 구성원이 그룹을 지켜보거나 그룹에 참여하는 방법에 대한 안내를 담은,
rust-lang조직 아래에 호스팅되는 GitHub 저장소. - 공식 rust-lang.org 웹사이트에서의 그룹 소개.
- “공식적인 의사 결정 권한” 없음: 즉,
rust-lang/rfcs에서 RFC를 승인할 수 없습니다.- 물론 그룹이 RFC를 작성하는 것도, 자신들의 우려 사항과 원하는 변경 사항을 Rust 팀과 커뮤니티에 제기하는 것도 권장됩니다.
- Rust가 공식적으로 관리하는 토론 플랫폼 내의 전용 공간.
- 이 문서를 작성하는 시점에서는 Zulip입니다.
- 원활한 소통을 위해 그룹은 상위 팀과 같은 플랫폼을 사용하는 것이 이상적이지만, 그룹이 다른 플랫폼을 시험 삼아 사용하는 데 팀이 동의하는 경우도 있을 수 있습니다.
헌장 작성하기
프로젝트 그룹은 관련 상위 팀의 승인을 받으므로, 구체적인 요건은 각 팀이 결정합니다. 다만 필자는 그룹이 다음 질문에 답하는 헌장을 작성하도록 노력할 것을 권장합니다.
- 여러분의 그룹이 조직에 어떤 가치를 가져다줄 것이라고 생각하십니까?
- Rust 조직으로부터 필요한 지원은 무엇이며, 그와 별개로 원하는 지원은 무엇입니까?
- 이 활동이 커뮤니티 차원의 활동이 아니라 프로젝트 그룹이어야 하는 이유는 무엇입니까?
- 여러분 그룹의 목표는 무엇입니까?
- 단기적인 목표와, 해당되는 경우 장기적인 목표를 모두 포함합니다.
- 여러분 그룹이 명시적으로 목표로 삼지 않는 것은 무엇입니까?
- 팀과의 관계가 어떠할 것으로 예상하십니까?
- 그룹 외부 사람들도 여러분의 작업에 접근할 수 있도록 어떻게 할 계획입니까?
- 초기 셰퍼드/리더는 누구입니까? (2–3명이 바람직하지만 필수는 아닙니다.)
- 여러분의 그룹은 장기적으로 운영됩니까, 아니면 한시적으로 운영됩니까?
- 한시적이라면 얼마 동안 운영될 것으로 보십니까?
- 여러분 그룹의 장기적인 비전은 무엇입니까?
- 해당되는 경우, 긴밀히 교류할 것으로 예상되는 다른 그룹이나 팀은 어디입니까?
- 여러분의 그룹은 어떤 부분에서 도움이 필요하다고 보십니까?
초기 설정
그룹이 승인되면 초기 구성원 목록을 담은 풀 리퀘스트를 rust-lang/team에 올려야 합니다. 그룹을 만드는 방법은 팀 문서를 참고하십시오.
그런 다음 프로젝트 그룹은 프로젝트 그룹 템플릿을 사용하여 rust-lang 조직 아래에 저장소를 만들고, 필요한 변경과 맞춤 설정을 하는 것이 권장됩니다.
평가
상위 팀은 정기 트리아지의 일환으로 소속 프로젝트 그룹의 상황을 확인하는 절차를 추가해야 합니다. 또한 프로젝트 그룹은 진행 상황 업데이트와 회의록을 “Inside Rust” 블로그에 블로그 게시물로 올리는 것이 권장됩니다.
보관
언젠가는 그룹의 작업이 마무리될 것입니다. 작업이 완료되었기 때문일 수도 있고, 구성원들이 작업을 끝낼 수 없기 때문일 수도 있으며, 그룹이 프로젝트를 더 이상 추진할 가치가 없다고 느끼기 때문일 수도 있습니다. 필자는 이 과정을 “보관“이라고 부릅니다.
공지
보관을 고려하는 그룹은 먼저 자신이 시작한 크레이트, 저장소 또는 프로젝트를 어떻게 처리할지 결정해야 합니다. 일반적으로 이러한 프로젝트는 다른 그룹이나 개인에게 이관해야 하며, 프로젝트를 유지 관리할 적절한 후보가 없다면 보관해야 합니다.
이 문제가 해결되면 그룹은 프로젝트의 이관 및/또는 보관에 관한 세부 사항과 함께 그룹의 보관 공지를 작성해야 합니다.
회고
이 RFC는 조직 내의 현재 조직적 문제 중 일부를 해결하려 하지만, 필자는 이것이 그러한 문제에 대한 만병통치약이 되리라고 생각하지 않으며, 앞으로 새로운 문제를 겪지 않으리라고도 생각하지 않습니다. 그 일환으로, 이 RFC는 상당한 시간이 지났거나 그룹이 프로젝트를 마쳤을 때 그룹과 함께 회고를 진행하는 방안을 도입합니다.
회고에는 그룹 구성원들 간의 논의가 포함되며, 이상적으로는 상위 팀과 거버넌스 워킹 그룹도 함께 참여합니다. 회고의 결과로 Inside Rust 블로그에 공개 블로그 게시물을 작성해야 합니다. 다만 구성원이 비공개로 남기고 싶어 하는 피드백은 제외합니다.
블로그 게시물에서는 RFC나 프로젝트 같은 그룹의 결과물과 함께, 그룹이 효과적이었다고 생각한 점, 그리고 중요하게는 효과적이지 않았던 점을 다루도록 해야 합니다. 이는 이 초기 RFC를 반복적으로 개선하고, 그 과정에서 발생하는 문제를 찾아 해결하는 데 도움이 될 것입니다.
회고와 보관 공지는 하나의 게시물로 작성할 수 있으며, 그렇게 하는 것이 바람직할 것입니다. 그러나 회고를 적시에 진행하기 어려운 경우도 있을 것이며, 그런 경우에는 별도의 짧은 공지 게시물을 작성하는 것이 적절합니다.
프로젝트 그룹의 생애 주기
다음은 프로젝트 그룹의 전체 과정에 대한 개략적인 설명입니다.
그림 1. 프로젝트 그룹 생애 주기
단계
-
탐색 기간.
- 문제 영역에 대한 초기 논의.
- 팀은 초기 논의를 살펴보거나 이에 응답할 의무가 없습니다. 물론 관심 있는 구성원은 자유롭게 참여할 수 있습니다.
- 프로젝트의 동기를 간략하게 작성하십시오.
- 관련 팀에서 연락 담당자 역할을 맡을 의향이 있는 사람을 찾으십시오.
- 연락 담당자를 찾는 방법은 팀마다 다르므로, 프로젝트 그룹을 제안하는 방법에 관해서는 해당 팀의 문서를 참고하십시오.
- 연락 담당자 역할을 맡으려는 사람을 항상 찾을 수 있는 것은 아닙니다. 새로운 활동을 얼마나 감당할 여력이 있는지는 각 팀이 결정할 몫이며, 때로는 그 여력이 전혀 없을 수도 있습니다.
- 문제 영역에 대한 초기 논의.
-
그룹을 만드는 데 대해 팀의 합의를 얻으십시오.
- 연락 담당자와 셰퍼드를 지정하십시오. (프로젝트 그룹 생성 참조)
- 간단한 동기와 가능한 해결책에 관한 몇 가지 메모를 작성하십시오.
- 합의에 이르는 방식은 팀마다 다르며, 어떤 팀은 RFC를 요구하고 다른 팀은 회의에서 결정할 수도 있습니다.
-
그룹을 위한 인프라를 만드십시오.
- RFC 초안 등 작업과 논의를 호스팅하기 위한
rust-lang아래의 GitHub 저장소. - 소통을 위한 Zulip 스트림.
- 권한 관리를 위한
rust-lang/team의 프로젝트 그룹과 GitHub의 팀.
- RFC 초안 등 작업과 논의를 호스팅하기 위한
-
Inside Rust 블로그에 그룹 생성을 알리는 글을 게시하십시오. 다음 정보를 반드시 포함하십시오.
- 소개
- 헌장(링크 또는 본문 포함) [헌장 작성하기 참조]
- 그룹의 GitHub 저장소 링크
- 그룹이 참여자에게 열려 있다면 기여 방법에 관한 정보를 제공하십시오.
- 정기 회의도 진행할 계획이라면 그룹의 회의 일정과 함께 회의 캘린더 일정 링크를 포함하십시오.
-
그룹은 헌장에 명시된 목표를 향해 활동합니다.
-
활발한 작업이 중단되면 그룹은 “보관” 상태가 됩니다.
- 보관은 프로젝트 그룹 셰퍼드나 연락 담당자가 시작할 수 있으며, 필요한 경우 상위 팀의 리드가 시작할 수도 있습니다.
- 보관이 반드시 영구적인 상태인 것은 아니며, 그룹의 현재 상태를 반영할 뿐입니다.
- 마찬가지로 그룹이 보관되었다고 해서 해당 분야의 작업이 모두 끝났다는 뜻은 아닙니다.
- 보관 사유(일부 예시):
- 그룹의 누구에게도 더 이상 시간이 없거나 우선순위가 더 높은 일이 생겼습니다.
- 해결할 수 없는 차단 이슈가 있습니다.
- 가까운 미래에 이 분야에서 추가로 할 작업이 보이지 않습니다.
- 작업이 만족스러운 수준까지 완료되었습니다.
- 그룹이 결국 그 아이디어가 그다지 좋지 않다고 판단했습니다.
-
그룹의 보관을 알리는 블로그 게시물을 작성하십시오.
- 이 글의 범위는 그룹의 범위에 따라 달라지지만, 다음 중 일부를 포함하는 것이 이상적입니다.
- 그룹이 만들어 낸 결정, RFC 및 기타 산출물의 개요.
- 진행 과정에 대한 소감, 즉 과정이 어떻게 진행되었는지(또는 경우에 따라 잘 진행되지 않았는지), 겪은 어려움, 그리고 개선되었으면 하는 점.
- 이 글의 범위는 그룹의 범위에 따라 달라지지만, 다음 중 일부를 포함하는 것이 이상적입니다.
-
인프라를 보관 처리합니다.
- GitHub 저장소를 읽기 전용으로 보관 처리합니다.
- 모든 플랫폼의 채팅 채널을 보관 처리합니다.
프로젝트 목표
소개
프로젝트 목표는 중대형 변경을 제안하는 프로젝트 내외부의 사람들에게 자원과 도움을 제공하기 위해 저희가 운영하는 프로그램입니다.
목표를 세우면 자금 지원을 받는 데 도움이 될 수 있습니다. 프로젝트는 자금 지원을 찾고 있는 목표 목록을 제공하며, 자금 지원 팀과 RCN도 잠재적 자금 지원 파트너가 관심 있는 목표를 찾아볼 수 있는 곳입니다.
이 페이지는 간단한 개요만 제공합니다. 전체 문서는 이 링크에서 확인할 수 있습니다.
구조 및 조율
프로젝트 목표 제안은 GitHub에서 추적됩니다.
자금 지원
프로젝트 목표 제안자는 자금 지원 신청을 요청할 수 있습니다. 자세한 내용은 문서를 참조하십시오.
정책
이 장에서는 Rust 프로젝트와 그 구성원에게 적용되는 정책을 다룹니다.
Rust 크레이트 소유권 정책
소개
이 문서는 Rust 프로젝트가 게시하는 크레이트에 관한 정책을 다룹니다. 이 정책은 처음에 RFC 3119를 통해 채택되었습니다.
분류
Rust 프로젝트가 게시하는 Rust 크레이트는 다음 분류 중 하나에 속합니다.
- 의도된 산출물: 어떤 팀(대개 라이브러리 팀)이 의도적으로 릴리스하고, 활발히 유지보수되며, 외부 사용자가 사용하도록 의도되었고, 의도적으로 공식적인 성격을 띠는 크레이트입니다. 예: libc
- 내부용: rustc, crates.io, docs.rs 등 어떤 “내부 클라이언트“가 사용하는 크레이트입니다. 주된 목적은 외부 사용자가 사용하는 것이 아니지만, 이를 유지보수하는 팀(대개 해당 내부 클라이언트의 팀)은 크레이트가 더 널리 채택되기를 바랄 수도 있습니다. 이 분류와 “의도된 산출물” 사이의 경계는 모호할 수 있으며, 결국 팀의 목표에 따라 달라집니다. 예: conduit, measureme. 전이 의존성으로 나타나도록 의도되었는지에 따라 두 가지 하위 분류가 있습니다.
- 전이적으로 의도됨: 의도된 산출물 라이브러리의 의존성으로, 직접 사용하도록 의도되지 않았더라도 사용자의 의존성 트리에 나타납니다. Rust 프로젝트는 여전히 이러한 크레이트의 보안 이슈를 “의도된 산출물“인 것처럼 처리해야 합니다.
- 전이적으로 의도되지 않음: 배포되는 바이너리, CI 도구, 표준 라이브러리의 의존성이거나, 그 밖에 사용자의 의존성 트리에 나타날 것으로 예상되지 않는 크레이트입니다. Rust 프로젝트는 이러한 크레이트의 보안 이슈를 내부적으로 처리해야 할 수 있지만, 이러한 크레이트의 보안 이슈를 반드시 일반 대중에게 알릴 필요는 없습니다. 이러한 크레이트 중 하나의 보안 이슈가 게시된 바이너리(또는 crates.io 등)에 영향을 미친다면, 이는 여전히 해당 바이너리나 웹사이트의 버그로 처리해야 합니다.
- 실험: 장기적인 유지보수 약속 없이, 사용자가 가져다 써 봄으로써 API 설계(또는 그 밖의 무엇이든)에 더 나은 정보를 제공하도록 의도된 팀의 실험이었던 크레이트입니다. 예: failure
- 지원 중단: 한때 “의도된 산출물”(또는 실험/내부용)이었지만 더 이상 그렇지 않은 크레이트입니다. 예: rustc-serialize
- 자리 표시자: 실제로 기능하는 크레이트가 아니며, 공식 도구의 이름 등을 확보해 두기 위해 사용됩니다. 예: rustup
- 이관됨: Rust 프로젝트가 소유했을 수 있고 여전히 외부 사용자가 사용하도록 의도되었지만, 더 이상 공식적인 것으로 의도되지 않은 크레이트입니다. 이러한 경우 해당 크레이트는 더 이상 Rust 프로젝트가 소유하거나 관리하지 않습니다. 예: rand
정책
조직 내의 모든 크레이트는 crates.io에서 최소 한 개 이상의 팀이 소유해야 합니다. 팀은 이를 위해 rust-lang/foo 팀을 사용해야 합니다. 이관되지 않은 크레이트는 개인 계정을 소유자로 둘 수 없습니다. 크레이트에 팀에 속하지 않은 추가 소유자가 필요하다면, 팀은 프로젝트 그룹을 만들어야 합니다. 이는 팀(또는 프로젝트 그룹)에 속하지 않은 사용자가 저장소에 대한 메인테이너 권한을 갖는 것을 금지하지 않으며, 단지 그들이 게시하는 것만 금지한다는 점에 유의하십시오.
현재는 크레이트를 팀만 소유하도록 할 수 없습니다. 이런 경우에는 rust-lang-owner 계정(또는 인프라 팀이 정하는 비슷한 계정)을 임시방편으로 사용할 수 있습니다. 각 크레이트의 책임자가 누구인지 분명히 하려면 이 계정은 가능한 한 단계적으로 없애 나가야 합니다. 자동으로 게시되는 크레이트에는 GitHub Actions의 신뢰할 수 있는 게시(trusted publishing)를 사용하십시오.
조직의 모든 크레이트와 앞으로 조직에 추가될 모든 크레이트는 위 분류에 따라 자신이 어느 범주에 속하는지 정해야 합니다. 크레이트를 등록할 때 어느 범주로 해야 할지 확실하지 않거나 아직 결정하고 싶지 않다면 “실험(Experimental)“을 선택하십시오.
게시되는 모든 크레이트에는 README가 있어야 합니다. 이 README에는 최소한 주 소유 팀이 명시되어야 합니다. 크레이트는 범주에 따라 README와 문서 루트에 다음 정보도 포함해야 합니다.
의도된 산출물
“의도된 산출물” 크레이트는 무엇을 약속할지 스스로 정할 수 있지만, 그 약속이 무엇인지 안내 문구에서 명확히 밝혀야 합니다. 팀에 헌장이 있거나 생기게 되면, 해당 크레이트는 헌장에도 의도된 산출물로 명시되어야 합니다. 의도된 산출물의 지원 중단은 가볍게 여겨서는 안 되며 RFC가 필요합니다.
이러한 안내 문구의 예시는 다음과 같습니다.
이 크레이트는 더 넓은 생태계에서 사용할 수 있도록 Rust [team] 팀이 유지 관리합니다. 이 크레이트는 1.0 이후 버전이며, API에 대해 semver 호환성을 따릅니다.
이러한 크레이트의 보안 이슈는 보안 대응 워킹 그룹(Security Response WG)이 사안의 무게에 맞게 신중한 안내와 함께 처리해야 하며, 프로젝트의 보안 정책에 따라 보고되어야 합니다.
내부 사용
“내부 사용” 크레이트는 readme/문서의 윗부분에 다음 문구를 포함해야 합니다.
이 크레이트는 [team] 팀이 유지 관리하며, 주로 [rust project(s)] Rust 프로젝트에서 사용하기 위한 것으로 외부 사용(전이 의존성으로 사용하는 경우는 제외)을 의도하지 않습니다. 이 크레이트는 사전 경고 없이 API에 주요 변경을 가하거나 지원 중단될 수 있습니다.
크레이트가 의도적 산출물 라이브러리의 의존성인 경우(“전이적 의도”) “전이 의존성으로 사용하는 경우는 제외” 문구를 포함해야 합니다.
전이적으로 의도된 라이브러리의 보안 이슈는 의도된 산출물과 동일하게 처리해야 합니다.
실험
“실험” 크레이트는 실험임을 명시해야 합니다. 실험 크레이트는 제한된 범위에서 사용되도록 의도될 수 있으므로, 사용을 의도한 경우라면 무엇을 보장하는지 분명히 밝혀야 합니다.
이러한 안내 문구의 예시는 다음과 같습니다.
이 크레이트는 [thingy]에 관한 실험의 일환으로 [team]이 유지 관리합니다. 여러분의 프로젝트에서 이 크레이트를 사용해 보고 [method]를 통해 피드백을 주시기 바랍니다. 다만 장기적인 유지 관리는 보장하지 않습니다.
또는 사용을 전혀 의도하지 않은 실험의 경우는 다음과 같습니다.
이 크레이트는 [team]이 유지 관리하는 내부 실험입니다. 안정성이나 장기적인 유지 관리는 보장하지 않으므로, 사용에 따른 위험은 사용자가 부담합니다.
피드백을 받기 위해 게시하는 실험 크레이트에는 실험의 목적, 대략적인 기간, 진행 절차를 정리한 문서가 있어 이를 링크할 수 있는 것이 가장 좋습니다.
폐기됨
“폐기됨” 크레이트는 readme/문서 상단 가까이에 다음 문구를 포함해야 합니다.
이 크레이트는 폐기되었으며 사용하도록 의도된 것이 아닙니다.
자리 표시자
“자리 표시자” 크레이트는 게시된 readme/문서에 다음 문구를 포함해야 합니다.
이 크레이트는 [tool]의 크레이트 이름을 확보해 두기 위해 존재하는, 기능상 비어 있는 크레이트입니다. 사용해서는 안 됩니다.
일반적으로 yank로 크레이트 이름을 확보해 두는 것보다 비어 있는 자리 표시자 크레이트를 게시하는 편이 낫습니다. 그래야 해당 크레이트를 사용할 수 없는 이유를 사람들이 이해하는 데 도움이 되는 readme가 남기 때문입니다.
이관됨
공식적인 느낌을 주는 요소(rust-lang/foo 팀 소유자 포함)를 제거하는 것 외에 이러한 크레이트에 어떤 조치를 취해야 하는지는 분명하지 않습니다. 현재 이러한 크레이트는 하나(rand)뿐입니다.
이러한 크레이트는 대체로 “팀 관리” 크레이트로 간주되어서는 안 됩니다. 이 범주는 이관을 최종 상태로서 논의할 수 있도록 완결성을 위해 이 RFC에 포함되어 있습니다.
전환과 새 크레이트
팀은 이러한 범주 중 어디에든 자유롭게 새 크레이트를 만들 수 있습니다. 다만 “의도된 산출물” 크레이트에는 반드시 RFC가 함께 있어야 합니다. 팀 헌장을 마련하는 방향으로 나아감에 따라, 이는 헌장 변경(RFC가 필요하거나 자체 절차를 따를 수 있음)으로 전환될 수 있습니다. 리더십 위원회가 이러한 크레이트를 추적하고 이 정책이 적용되도록 보장할 수 있도록, 팀은 이러한 크레이트를 만들었을 때 council@rust-lang.org에 알려야 합니다.
크레이트에 대한 팀의 계획은 때때로 바뀔 수 있습니다. 실험이 종료될 수도 있고, 크레이트의 지원을 중단해야 할 수도 있으며, 팀이 더 넓은 용도로 무언가를 릴리스하기로 결정할 수도 있습니다.
일반적으로 팀은 이러한 전환을 진행할 때 council@rust-lang.org에 알려야 합니다.
“의도된 산출물”에서 다른 범주로 전환하려면 RFC가 필요합니다.
“의도된 산출물“로 전환할 때에는 RFC와 함께, 팀 헌장이 있다면 헌장 갱신도 함께 이루어지는 것이 바람직합니다.
이관은 기본적으로 앞으로 절대 일어나지 않아야 하지만, 정말로 필요한 경우에도 RFC와 리더십 위원회의 승인이 필요합니다. 팀이 크레이트 작업을 중단하고자 한다면 해당 크레이트를 폐기하고, 커뮤니티가 이를 포크하거나 직접 대안을 만들도록 권장해야 합니다. 저장소는 외부로 이전될 수 있지만, crates.io 이름은 Rust 프로젝트가 유지하며 새로운 메인테이너 그룹은 새 크레이트 이름을 정해야 합니다.
“전이적으로 의도된” 크레이트를 폐기하는 경우, 보안 이슈가 계속 처리되도록 주의를 기울여야 합니다.
그 밖의 유형 간 전환은 강력한 안정성/유지 관리 보장이 없음을 명시적이고 분명하게 밝히고 있으므로 자유롭게 할 수 있습니다.
기존 크레이트에 적용하기
잠재적으로 “공식“일 수 있는 기존 크레이트 전체를 감사하여 목록으로 모으고, 각 크레이트의 담당 팀과 범주를 대략적으로 정해야 합니다.
이 목록이 마련되면 팀에 크레이트 목록을 전달하여 분류가 정확한지 확인해 달라고 요청할 수 있습니다. 일부 크레이트의 경우, 팀이 특정 크레이트에 대한 의도를 정리해야 할 수도 있으므로 시간이 다소 걸릴 수 있습니다.
그런 다음 팀과 협력하여 해당 문서에 이러한 변경 사항을 반영합니다. 또한 모든 크레이트에 적절한 rust-lang/teamname GitHub 소유자가 지정되어 있는지 확인하고, 소유자 목록에서 개인 계정을 제거합니다.
더 넓은 커뮤니티에서 직접 많이 사용하는 크레이트를 결국 “의도된 산출물” 외의 범주로 분류하게 된다면, 이 “변경“을 커뮤니티에 알리려는 노력이 있어야 합니다. 이러한 크레이트에 대해 공식적인 약속이 있었던 것은 아니지만, 막연히 공식적이라는 인상 때문에 사람들이 그런 약속이 있었다고 믿었을 수 있으므로, 사람들이 계속 오해하지 않도록 적어도 이를 바로잡으려는 시도는 해야 합니다. 이 작업이 필요한지 여부와 그 방법은 각 팀이 판단할 수 있습니다.
이 작업의 상당 부분은 병렬로 진행할 수 있으며, 한꺼번에 이루어질 필요는 없습니다.
LLM 사용 정책
이 문서는 rust-lang/rust에서 LLM을 사용하는 방식에 관한 모더레이션 정책입니다. 정책 자체에 대한 추가 정보는 부록을 참고하십시오.
개요
rust-lang/rust 작업에서 LLM을 사용하는 것은 신중하게 사용한다는 조건하에 허용됩니다. LLM은 사고를 대신할 수 없습니다. 따라서 프로젝트에 대해 저희가 함께 쌓아 온 사회적·기술적 이해를 잃을 위험이 있는 방식이나, 강한 커뮤니티를 만든다는 저희의 목표를 해치는 방식으로 LLM을 사용하는 것은 허용하지 않습니다.
저희는 이 정책의 많은 조항이 강제하기 어렵다는 것을 알고 있습니다. 저희의 목표는 모든 위반을 적발하는 것이 아닙니다(아래의 탐정 노릇은 여러분의 일이 아닙니다를 참고하십시오). 그 대신 저희의 목표는 그럴듯한 부인 가능성을 없애는 것, 즉 정책을 따를 것인지 의도적으로 위반할 것인지 선택하도록 만드는 것입니다. 동기에 대한 더 자세한 배경은 자금 세탁과 AML 규정 준수를 참고하십시오.
이 정책의 지침은 대략 다음과 같습니다.
질문에 답하고, 분석하고, 요약하고, 다듬고, 점검하고, 제안하고, 리뷰하는 데 LLM을 사용하는 것은 괜찮습니다. 하지만 창작하는 데 사용해서는 안 됩니다.
LLM은 더 빨리 쓰기 위한 도구가 아니라 더 잘 쓰기 위한 도구로 사용할 때 가장 효과적입니다.
향후 이 정책을 개정할 때 참고할 수 있도록 “실험“을 위한 여지를 따로 마련해 둡니다.
모더레이션과 가이드
이 문서에는 저희의 모더레이션 정책만 담겨 있습니다. 기술적인 가이드와 제안은 rustc-dev-guide를 참고하십시오.
규칙
범례
- ✅ 허용됩니다.
- ❌ 금지됩니다.
- ⚠️ 단서를 붙여 허용됩니다. LLM을 사용했다는 사실을 반드시 밝혀야 합니다.
- ℹ️ 정책에 세부 사항을 추가합니다. 이 항목들은 규범적 효력을 가집니다.
- 💡 이 항목에 대한 제안이 dev-guide에 있음을 나타냅니다.
- 🔨 이 조항을 위반하면 행동 강령 위반으로 간주됩니다.
요약
- ✅ 허용: 개인적 사용.
- ❌ 금지: LLM이 작성한 댓글, 문서 또는 진단 메시지. 사람의 판단을 LLM의 판단으로 대체하는 것. 기여하려면 LLM을 사용하도록 요구하는 것.
- ⚠️ 조건부 허용: 사소한 변경, 기계 번역, LLM 리뷰 및 리뷰 봇, 실험 규칙에 따라 LLM이 작성한 코드.
- 🔨 모더레이션 제재 대상: 거짓말.
모든 경우를 망라하지 않는 정책
이 정책은 모든 경우를 망라하는 것을 목표로 하지 않습니다. 이 목록에 없는 LLM 사용 방식을 염두에 두고 있다면 다른 사람들과 그에 관해 이야기하고, 다음 개요의 취지에 따라 판단하십시오.
- 본인의 개인적인 용도로 LLM을 사용하는 것은 허용될 가능성이 높습니다 ✅
- 요청받지 않았는데 LLM 출력을 다른 사람에게 보여 주는 것은 금지될 가능성이 높습니다 ❌
- LLM 출력을 바탕으로 다른 사람에게 영향을 미치는 결정을 내리려면 이를 공개해야 합니다 ⚠️
✅ 허용
다음은 허용됩니다.
- 출력을 보는 사람이 본인뿐인 모든 LLM 사용. 예를 들면 다음과 같습니다.
- 기존 코드베이스에 관해 LLM에 질문하는 것.
- 이슈나 PR의 댓글을 요약해 달라고 LLM에 요청하는 것.
- ℹ️ 그렇다고 요약을 공개적으로 다시 게시해도 되는 것은 아닙니다. 본인의 개인적인 용도에 한합니다.
- 본인의 코드나 글을 비공개로 리뷰해 달라고 LLM에 요청하는 것.
- ℹ️ LLM이 공개적으로 작성하는 댓글에는 해당하지 않습니다. 아래 ⚠️ 항목의 “리뷰 봇“을 참고하십시오.
- LLM을 사용하여 본인의 개인적인 용도로 쓸 개발 도구를 작성하는 것.
- LLM으로 이슈의 해결 방안 후보를 생성하고, 이를 통해 배운 뒤, 본인만의 스타일로 처음부터 새로 작성하는 것.
- 리뷰를 받기 위한 것은 아니지만 crater나 perf 실행 같은 도구상의 이유로
rust-lang/rust에 PR로 올려야 하는, 명백히 실험적인 코드 변경을 만드는 데 LLM을 사용하는 것.- “명백히 실험적“이라는 표시에는
S-experimental레이블,[PERF]제목,r? ghost댓글 등이 포함됩니다. - 실험적인 PR에서 LLM 사용 사실을 공개하는 것을 필수로 요구하지는 않지만 강력히 권장합니다. 다른 사람이 LLM으로 생성된 것임을 모른 채 초안 작업을 이어받는 일을 막기 위해서입니다.
- ℹ️ PR에 더 이상 명백히 실험적이라는 표시가 없다면 그 시점부터는 공개가 필수입니다.
- “명백히 실험적“이라는 표시에는
❌ 금지
다음은 금지됩니다.
- 원래 LLM이 작성한 댓글을 개인 사용자 계정으로 게시하는 것.
- ℹ️ 이 내용은 이슈 본문과 PR 설명에도 적용됩니다.
- ℹ️ 이 내용은 LLM이 처음 만들었거나 대본을 작성한 음성/영상 콘텐츠에도 적용됩니다.
- ℹ️ LLM 콘텐츠를 명확하게 인용하고 표시한 경우에는 적용되지 않으며, 그러한 콘텐츠는 게시할 수 있습니다. 다만 댓글의 내용은 LLM 콘텐츠가 없어도 그 자체로 성립해야 합니다. LLM 콘텐츠가 여러분 자신의 말을 대신할 수는 없습니다.
- ℹ️ 아래 ⚠️의 “기계 번역” 항목도 참고하십시오.
- ℹ️ 아래 부록의 “범위” 항목도 참고하십시오.
- LLM이 처음 작성한 문서
- ℹ️ 여기에는 문서 주석, 안전성 주석, 여러 문단으로 된 일반 주석 등 사소하지 않은 소스 주석이 포함됩니다.
- ℹ️ 여기에는 컴파일러 진단 메시지가 포함됩니다. LLM은 진단을 둘러싼 로직 작업을 조건부로 도울 수 있지만(아래 “실험: LLM이 작성한 코드 변경” 참고), 메시지 자체를 작성하는 데 사용해서는 안 됩니다.
- ℹ️ 여기에는 “사소한” 변경이 포함되지 않습니다(아래 ⚠️ 참고).
- LLM이 있어야만 실행할 수 있도록 작성된 정책이나 절차
- 예를 들어 테스트 위치를
AGENTS.md에만 문서화해서는 안 됩니다. 문서는 기본적으로 사람을 위해 작성해야 하며, LLM용 문서는 그 내용을 요약만 할 수 있고 새로운 세부 사항을 추가해서는 안 됩니다.
- 예를 들어 테스트 위치를
- LLM 리뷰를 변경 사항의 병합이나 거부를 결정하는 충분조건으로 취급하는 것 LLM 리뷰를 활성화하더라도 LLM 리뷰는 반드시 참고용이어야 합니다. 팀은 리뷰 없이 코드를 병합할 수 있다는 정책을 둘 수도 있고 최소 한 명 이상이 코드를 리뷰해야 한다는 정책을 둘 수도 있지만, LLM 리뷰가 사람의 리뷰를 대신한다는 정책은 둘 수 없습니다.
- ℹ️ 아래 ⚠️의 “리뷰 봇” 항목을 참고하십시오.
- ℹ️ LLM 리뷰가 셀프 리뷰를 대신하지는 않습니다. 작성자는 게시하기 전과 변경할 때마다 자신의 코드를 직접 리뷰해야 합니다.
⚠️ 조건부 허용
이러한 사용은 아래 규칙에 따라 사안별로 허용됩니다. 새 기여자라면 아직 리뷰어와 신뢰를 쌓지 못했으므로 기존 기여자보다 더 엄격한 검토를 받게 될 것임을 예상하셔야 합니다.
“⚠️ 조건부 허용“에 해당하는 모든 사용은 LLM을 사용했다는 사실을 반드시 밝혀야 합니다.
- 원래 메시지를 게시하지 않고 모국어로 쓴 글을 기계 번역(예: Google 번역)하여 게시하는 것은 허용되지만 권장하지 않습니다. 이렇게 하면 원래 없던 오해가 새로 생길 수 있고, 해당 언어를 구사하는 사람이 더 나은 번역을 제공할 수도 없게 됩니다.
- ℹ️ 원래 메시지와 번역본을 함께 게시하는 것은 언제나 괜찮지만, 이 경우에도 기계 번역을 사용했다는 사실은 밝혀야 합니다.
- “사소한” 코드 또는 글 변경
- ℹ️ 달리 작성할 방법이 없거나 다른 작성 방법이 있더라도 거의 동일하다면 사소한 변경입니다. 예를 들어 다음은 모두 사소한 변경입니다.
- 오타 수정
- Markdown 링크
- 단어를 동의어로 바꾸는 것
- 트레이트 구현의 타입 시그니처
- ℹ️ 사소한 변경만으로 이루어진 PR에는 주의하십시오. 컴파일러 팀의 오타 수정 정책도 참고하십시오.
- 💡 추가 제안은 dev-guide를 참고하십시오.
- 이 정책의 바탕이 된 개념에 대한 자세한 배경은 독창성의 기준(threshold of originality)과 API 선언을 복제하는 것이 공정 이용에 해당한다고 본 Google 대 Oracle 판결을 참고하십시오.
- ℹ️ 달리 작성할 방법이 없거나 다른 작성 방법이 있더라도 거의 동일하다면 사소한 변경입니다. 예를 들어 다음은 모두 사소한 변경입니다.
- 버그를 직접 검증한다는 조건으로 LLM을 사용해 버그를 찾는 것 퍼저에 관한 가이드라인을 참고하십시오.
- ℹ️ 여기에는 LLM을 사용해 병합되지 않은 코드의 결함을 찾는 리뷰어도 포함됩니다.
- ℹ️ 위의 ❌ 항목에 있는 “개인 사용자 계정으로 게시된 […] 댓글“도 참고하십시오.
- LLM을 PR용 “리뷰 봇“으로 사용하는 것
- ℹ️ 메인테이너의 승인 없이 댓글을 게시하는 리뷰 봇은 차단됩니다. 승인은 한 번만 받으면 되며, 메인테이너가 개별 리뷰를 일일이 승인할 필요는 없습니다.
- ℹ️ 리뷰 봇은 LLM임을 분명히 표시하는 별도의 GitHub 계정을 반드시 사용해야 합니다. 봇의 분석을 명확히 인용하고 그에 대한 본인의 해석을 덧붙이는 경우가 아니라면, LLM 리뷰를 그대로 개인 계정에 게시해서는(또는 도구가 게시하도록 허용해서는) 안 됩니다.
- ℹ️ 리뷰 봇 계정은 개별 사용자가 GitHub의 표준 사용자 차단 기능으로 차단할 수 있어야 합니다. (일부 GitHub “앱” 계정은 사용자처럼 보이는 댓글을 게시하지만 차단할 수 없다는 점에 유의하십시오.)
- ℹ️ LLM 댓글이 병합을 가로막아서는 안 됩니다. 리뷰어는 어떤 댓글을 반영하기를 원하는지 명시해야 합니다.
- 다시 말해, 리뷰어는 PR의 병합을 가로막기 전에 해당 LLM 댓글에 동의한다는 뜻을 명시적으로 밝혀야 합니다. 리뷰어는 LLM 댓글에 대한 본인의 분석에 책임을 지며, 이를 CI 실패처럼 취급해서는 안 됩니다.
- ℹ️ 리뷰를 위해 LLM을 개인적으로 사용하는 경우에는 이 규칙이 적용되지 않습니다. 위의 ✅ 항목을 참고하십시오.
- 💡 추가 제안은 dev-guide를 참고하십시오.
실험: 리뷰를 받기 위해 LLM으로 만든 코드 변경
향후 정책 수립에 참고하기 위해 LLM을 실험해 볼 여지를 열어 둡니다. 이 실험은 향후 실험 단계가 아닌 정식 정책을 수립하는 데 참고하기 위한 것이며, 영구적인 LLM 사용 정책이 되려는 것이 아닙니다.
규칙
처음에 LLM으로 만든 코드 변경이라도 사전에 합의되었고, 핵심적이지 않으며, 품질이 높고, 충분히 테스트되고 리뷰되었다면 그 사실을 밝히는 조건으로 허용됩니다.
- “사전에 합의되었다“는 것은 리뷰어가 LLM으로 만든 PR을 리뷰할 의향이 있다고 미리 알렸다는 뜻입니다.
- ℹ️ 새로운 기여자는 먼저 리뷰어와 이야기하지 않고서는 LLM을 사용해 PR을 만들 수 없습니다. 이때 이야기하는 리뷰어는 해당 PR에 배정될 리뷰어와 같은 사람이어야 합니다.
- 물론 작성자는 리뷰어를 찾기 전에 변경 작업을 시작할 수 있습니다. 그러나 PR을 열기 전에는 반드시 리뷰어를 찾아야 합니다. 리뷰어를 찾기 전에
r? @ghost로 LLM이 작성한 PR을 여는 것은 허용되지만 권장하지 않습니다. 대신 자신의 포크에 푸시한 뒤 리뷰어가 될 사람을 위해 Zulip에 링크를 게시하는 방법을 권장합니다. 이러한 PR에도 LLM 사용 사실을 밝혀야 합니다.
- “중요하지 않음“이란 해당 PR이 건전성 회귀를 일으킬 가능성이 극히 낮다는 뜻입니다.
- ℹ️ 예시:
tidy,x setup,linkchecker와 같은 내부 도구에 대한 변경은 대체로 괜찮습니다.- 트레이트 시스템, MIR 빌드, 쿼리 시스템처럼 건전성에 큰 영향을 미치는 변경은 대체로 허용되지 않습니다.
- ℹ️ 예시:
- “고품질“이란 최소한 다른 코드 변경과 동일한 기준이 적용된다는 뜻입니다. 코드는 작성자와 리뷰어만이 아니라 모두가 읽습니다. 저희는 코드베이스의 품질을 떨어뜨리는 “바이브 코딩” PR에는 관심이 없습니다.
- “충분히 테스트됨“이란 작성자나 리뷰어가 생각할 수 있는 모든 엣지 케이스를 다루었다는 뜻입니다.
- ℹ️ LLM이 작성한 PR에는 사람이 작성한 PR보다 더 높은 기준이 적용됩니다. LLM을 사용하면 테스트를 더 쉽게 작성할 수 있기 때문입니다.
- ℹ️ 코드의 특정 부분에 기존 테스트 스위트가 없다면, 새 테스트 스위트를 작성하거나 PR을 닫아야 합니다. “테스트 작성이 어려워 보인다“는 이유로는 예외가 인정되지 않습니다.
- “충분히 리뷰됨“이란 작성자와 리뷰어 모두가 코드를 완전히 이해하기로 약속한다는 뜻입니다.
예외적으로, rust-lang 조직의 구성원은 “중요하지 않음” 조항의 적용을 받지 않습니다. 저희는 작성자와 리뷰어가 스스로 판단할 것이라고 신뢰합니다. 그러나 이 조항을 활용하는 것은 강력히 권장하지 않습니다. LLM은 그럴듯해 보이는 코드를 매우 매우 잘 생성하며, 건전성은 테스트하기 어렵습니다.
예외적으로, 이 정책이 시행되기 전에 작성된 PR은 “중요하지 않음” 조항에서 면제됩니다.
절차
LLM이 작성한 PR에는 새로운 llm-assisted 레이블을 붙여야 합니다. 이러한 모든 PR은 새로운 (비공개) Zulip 채널에 게시되며, 이 채널은 rust-lang 조직의 모든 구성원이 접근할 수 있습니다. 이 채널의 목적은 LLM이 작성한 PR에 대한 추가적인 관문 역할을 하는 것이 아닙니다. 대신 이 실험이 효과가 있는지에 대한 정보를 수집하는 것이 목적입니다. 사람들이 LLM으로 흥미롭고 유용한 일을 하고 있습니까? 사람들이 배우고 있습니까? 사람들이 반복적으로 기여하고 있습니까?
새 채널은 비공개이므로, 주제에 맞는 내용으로 인정되는 기준이 평소보다 높습니다. 예를 들어, 다음은 주제에 맞는 내용입니다.
- PR이 실험 예외 기준을 충족하는지 여부
- PR이 전반적으로 정책을 준수하는지 여부
반면 다음은 주제에서 벗어난 것입니다:
- 기술 및 설계에 관한 논의. 이러한 논의는 PR에 직접 게시하거나 공개 Zulip 채널에 게시해야 합니다.
- 들인 노력, 소통 방식 또는 의도에 관한 논의
- LLM 정책에 관한 일반적인 논의
서킷 브레이커
LLM이 코드베이스를 “압도“하거나 사실상 필수가 되는 위험을 피하기 위해, 병합될 수 있는 LLM이 작성한 PR의 수에 제한을 둡니다. 6주 기간 동안 병합된 PR 중 절반 이상이 LLM이 작성한 PR이면, 그 비율이 다시 50% 미만으로 내려갈 때까지 새로운 LLM이 작성한 PR의 병합을 허용하지 않으며, 최소 유예 기간은 10일입니다. 이 기간은 기존 릴리스 주기에 맞추어 정했으며, 냉각 기간은 허용과 불허 사이를 오락가락하는 것을 피하기 위한 것으로, 그 길이는 FCP 절차에 맞추어 정했습니다.
냉각 기간은 다음과 같은 논의를 장려하기 위한 것입니다:
- 실험은 어떻게 진행되고 있습니까?
- 우리는 AI를 지속 가능한 방식으로 도입하고 있습니까? LLM을 사용하지 않기로 선택한 기여자도 포용하고 있습니까?
- 정책에 적용하고 싶은 변경 사항이 있습니까?
일관성 없는 시행과 그로 인한 반감을 피하기 위해, 이 서킷 브레이커를 자동화할 것을 강력히 권장합니다.
부록
적용 범위
이 정책은 rust-lang/rust에만 적용되며, 이 정책을 비준한 팀인 컴파일러, 라이브러리, 타입, rustdoc, 부트스트랩 팀과 그 하위 팀에만 적용됩니다. 다음은 적용 범위에 포함되지 않으며 자체 정책을 자유롭게 정할 수 있습니다:
rust-lang의 다른 저장소- 서브모듈, 서브트리 및 crates.io 의존성
- 언어 팀과 에디션 팀처럼 이 정책을 비준하지 않은 팀
예를 들어, 다음은 이 정책의 적용을 받지 않습니다:
- T-lang의 추적 이슈
- T-lang 제안
- T-lang 안정화 보고서
- 언어 문서
- 스타일 가이드
- 컴파일러 린트의 이름. 이는 이름 자체에만 해당하며, 진단 메시지는 여전히 이 정책의 적용을 받습니다.
- 위 항목 중 어느 것이든 문서나 진단 메시지에 그대로 인용하는 것.
동기와 기본 원칙
AI 기반 도구를 언제, 어떻게, 어디에서 사용하는 것이 허용되는지에 대해 Rust 프로젝트 내에는 합의가 없으며, 앞으로도 합의에 이르지 못할 가능성이 큽니다. Rust 프로젝트와 커뮤니티의 많은 구성원은 AI에서 가치를 발견합니다. 반면 AI가 사회와 기후에 미치는 부정적 영향이 너무 심각하여 어떠한 사용도 용납할 수 없다고 느끼는 구성원도 많습니다. 아직 자신의 의견을 정리하고 있는 구성원도 있습니다.
이러한 차이에도 불구하고, 우리 모두가 공유하는 가치는 많습니다.
- 우리가 함께하는 프로젝트에서 깊은 전문성을 갖춘 사람들의 커뮤니티를 만드는 것.
- 모두가 환영받고 존중받는다고 느끼는 포용적인 커뮤니티를 만드는 것.
또한 우리가 동의하는 사실도 많습니다.
- 많은 사람은 LLM이 생성한 코드와 글을 읽거나 리뷰하는 것을 매우 불쾌하게 느낍니다.
- 많은 사람은 LLM이 학습과 탐구에 큰 도움이 된다고 느낍니다.
- LLM은 새로운 기술이며, 우리는 LLM을 사용하고, 관리하고, 개선하는 방법을 아직 배워 가는 중입니다.
이러한 사실과 가치를 염두에 두고, 이 정책은 다음 목표에 따라 설계되었습니다.
- Rust 하면 떠오르는 품질 기준을 유지할 수 있도록 의도적으로 보수적인 정책을 수립합니다.
- “더 빠르게가 아니라 더 좋게“라는 우리의 지침이 말뿐이 아님을 보여 주기 위해, LLM을 활용한 기여는 최고 수준의 품질을 갖춘 경우로만 제한합니다.
- 정책을 집행할 수 있고 모더레이션하기 쉽게 만듭니다.
- 정책을 자세히 읽지 않은 사람도 쉽게 이해하고 요약할 수 있도록 일관되고 알기 쉽게 만듭니다.
- LLM 사용을 기여의 필수 조건으로 만들지 않습니다. 이는 LLM을 사용하지 않기로 한 기여자도 포용하고, Rust가 “돈을 내야 참여할 수 있는” 프로젝트가 되지 않도록 하기 위함입니다.
모더레이션 정책
탐정 노릇을 하는 것은 여러분의 일이 아닙니다
“사기의 최적량은 0이 아닙니다”. 누군가가 LLM을 사용했는지 단속하려 하지 마십시오. LLM이 관여했는지 “적극적으로 찾아낼” 필요는 없습니다.
문체는 증거가 되지 않습니다. 영어를 제2언어로 사용하는 사람, 신경다양인, 설명을 길게 하는 사람이 LLM처럼 글을 쓴다는 의심을 가장 많이 받습니다.
누군가가 규칙을 어긴 것이 분명하다면 이 정책을 알려 주십시오. 그렇지 않다면 공개적으로 지적하기보다는 모더레이터에게 신고하는 것을 강력히 권장합니다. 모더레이션 팀에 신고하는 것은 처벌을 의도한 것이 아닙니다. 모더레이션 팀은 위반 사례뿐 아니라 위반이 아닌 사례도 확인하고자 합니다. 언제나 그렇듯이, 모더레이션 팀은 자체적인 판단과 재량을 자유롭게 행사할 수 있습니다.
정직하십시오
반대로, 여러분은 LLM 사용 여부와 그 사용 범위에 대해 정직해야 합니다. 하려는 일이 이 정책의 어디에 해당하는지 확실하지 않다면 모더레이션 팀과 상의하십시오. 숨기려 하지 마십시오.
LLM 사용에 대해 고의로 사실과 다르게 밝히는 것은 용납되지 않으며, 모더레이션 조치로 이어질 수 있습니다.
제재
🔨로 표시된 정책은 행동 강령과 같은 지침을 따릅니다. 위반하면 먼저 경고를 받게 되며, 위반이 반복되면 차단될 수 있습니다.
- 🔨 “정직하십시오” 절 위반
그 밖의 위반은 리뷰어와 모더레이터의 재량에 맡깁니다. 경미한 위반에 대해서는 PR이 정책을 준수할 때까지 리뷰할 수 없다는 점을 작성자에게 알리고, 정확히 무엇을 해야 하는지 안내할 것을 권장합니다. 중대한 위반이나 착취적인 PR에 대해서는 해당 PR이나 이슈를 닫을 것을 권장합니다.
LLM을 사용했다는 이유로 기여자를 괴롭히는 것은 허용되지 않습니다. 모든 기여자는 존중받아야 합니다. 행동 강령은 Rust 프로젝트의 모든 대화에 적용됩니다.
책임
여러분의 기여에 대한 책임은 여러분에게 있으며, 그 책임을 LLM에 돌릴 수 없습니다.
- ℹ️ 여기에는 원래 LLM이 작성한 리뷰 코멘트에 대응해 달라고 다른 사람에게 요청하는 경우도 포함됩니다. 위의 ⚠️ 항목에 있는 “리뷰 봇“을 참고하십시오.
“원래 LLM이 작성한“의 의미
이 문서에서 “원래 LLM이 작성한“이라는 표현은 “LLM이 생성한(그리고 이후 사람이 편집했을 수도 있는) 텍스트“를 의미합니다. 아무리 편집하더라도 텍스트가 원래 어떻게 작성되었는지는 바뀌지 않습니다. 출처가 초기 스타일을 정하며, 한번 정해진 스타일은 바꾸기가 매우 어렵습니다.
이와 유사한 논리에 관한 더 자세한 배경은 “What Colour are your bits?”를 참고하십시오.
이 정책은 채팅 인터페이스에서 나온 LLM 출력과 에디터 자동 완성에서 나온 출력을 구분하지 않습니다. 대부분의 경우 그러한 출력은 “사소한” 것이지만(위의 ⚠️ 항목 참고), 어느 쪽이든 이 정책에서 특별히 다르게 취급하지는 않습니다.
수정 또는 폐지 조건
이 정책은 고정불변이 아니며, LLM을 활용한 작업 경험이 쌓이면서 발전시켜 나갈 수 있습니다.
오타 수정과 같은 경미한 변경에는 일반적인 PR 승인만 있으면 됩니다. 새 규칙을 추가하거나 기존 규칙을 폐지하는 것과 같은 주요 변경에는 이 정책을 비준한 각 팀에서 MCP(주요 변경 제안)가 통과되어야 합니다(승인 2건, 우려 사항 없음). rustc-dev-guide의 지침을 변경하는 데에는 별도의 요건이 없습니다.
이 정책은 다음과 같은 몇 가지 방법으로 폐지될 수 있습니다.
- 이 정책을 비준한 팀들이 FCP를 승인하는 경우.
- 이 정책이 Rust 프로젝트에 실제로 해를 끼치고 있다는 객관적인 우려가 증거와 함께 제기되고, 리더십 위원회 FCP로 그렇게 결정된 경우.
- 제안된 LLM 위원회(또는 이와 유사하게 프로젝트 전체를 담당하는 전담 기구)가 구성되면, 그 기구가 정하는 정책이 이 정책보다 우선합니다.
위의 절차로 이 정책을 발전시키는 것이 불가능하다고 판명되는 경우, 특히 어떤 팀이 자신의 관할에 관한 결정을 자율적으로 내리는 데 크게 방해를 받게 되는 경우에는 리더십 위원회 FCP를 통해 이 수정 조건을 조정할 수 있습니다.
인프라
이 섹션에서는 Rust의 인프라와 그 유지 관리 방식을 설명합니다.
외부 링크
- rust-toolstate는 Rust 저장소에 포함된 외부 도구의 빌드 및 테스트 상태를 기록합니다.
기타 Rust 설치 방법
어떤 설치 프로그램을 사용해야 합니까?
Rust는 여러 플랫폼에서 실행되며, Rust를 설치하는 방법도 다양합니다. 가장 간단하고 권장되는 방법으로 Rust를 설치하려면 메인 설치 페이지의 안내를 따르십시오.
해당 페이지는 Rust가 지원하는 모든 플랫폼에서 여러 Rust 툴체인을 일관된 방식으로 관리하는 도구인 rustup을 통한 설치 방법을 설명합니다(rustup 문서 참고). 그렇다면 왜 이 안내에 따라 설치하기를 원하지 않을 수도 있겠습니까?
- 오프라인 설치.
rustup은 필요할 때 인터넷에서 구성 요소를 내려받습니다. 인터넷에 접속할 수 없는 환경에서 Rust를 설치해야 한다면rustup은 적합하지 않습니다. - 시스템 패키지 관리자 선호. 특히 Linux에서, 그리고 Homebrew, MacPorts 또는 pkgsrc를 사용하는 macOS와 Chocolatey 또는 Scoop을 사용하는 Windows에서도, 개발자들은 때때로 자신의 플랫폼 패키지 관리자로 Rust를 설치하는 것을 선호합니다.
curl | sh방식에 대한 거부감. Unix에서는 보통curl로 셸 스크립트를 실행하여rustup을 설치합니다. 이러한 방식의 보안을 우려하여 설치 프로그램을 직접 내려받아 실행하기를 선호하는 사람들도 있습니다.- 서명 검증.
rustup은 HTTPS를 통해 내려받기를 수행하지만, 현재 Rust 설치 프로그램의 서명을 검증하는 유일한 방법은 독립 실행형 설치 프로그램을 사용하여 수동으로 검증하는 것입니다.
Rust의 플랫폼 지원은 세 단계(tier)로 정의되며, 이는 사용 가능한 설치 방법과 밀접하게 대응합니다. 일반적으로 Rust 프로젝트는 모든 Tier 1 및 Tier 2 플랫폼에 대해 바이너리 빌드를 제공하며, 이들은 모두 rustup으로 설치할 수 있습니다. 다만 일부 Tier 2 플랫폼에서는 컴파일러 자체가 아니라 표준 라이브러리만 제공됩니다. 즉, 이들은 크로스 컴파일 대상일 뿐이며, 해당 플랫폼에서 Rust 코드는 실행할 수 있지만 컴파일러 자체는 실행되지 않습니다. 이러한 대상은 rustup target add 명령으로 설치할 수 있습니다.
rustup을 설치하는 다른 방법
rustup의 기타 설치 방법을 참고하십시오.
독립 실행형 설치 프로그램
공식 Rust 독립 실행형 설치 프로그램에는 Rust의 단일 릴리스가 포함되어 있으며, 오프라인 설치에 적합합니다. 설치 프로그램은 세 가지 형태로 제공됩니다. 모든 Unix 계열 환경에서 동작하는 tarball(확장자 .tar.xz), Windows 설치 프로그램(.msi), Mac 설치 프로그램(.pkg)입니다. 이 설치 프로그램에는 rustc, cargo, rustdoc, 표준 라이브러리, 표준 문서가 함께 제공되지만, rustup처럼 추가 크로스 타깃을 사용할 수 있게 해 주지는 않습니다.
이 설치 프로그램을 사용하는 가장 흔한 이유는 다음과 같습니다.
- 오프라인 설치
- Windows에서 플랫폼에 더 잘 통합된 그래픽 설치 프로그램을 선호하는 경우
각 바이너리는 Rust 빌드 인프라가 GPG를 사용해 Rust 서명 키로 서명하며, 이 키는 keybase.io에서 받을 수 있습니다. 아래 표에서 .asc 파일이 서명입니다.
이전 릴리스는 아카이브에서 찾을 수 있습니다.
소스 코드
소스 코드에서 Rust 툴체인을 빌드하려면 다음 링크에서 소스 코드 tarball을 내려받을 수 있습니다.
| Channel | Archives + Signatures |
|---|---|
| stable (1.99.1) | tar.xz tar.xz.asc |
| beta | tar.xz tar.xz.asc |
| nightly | tar.xz tar.xz.asc |
게시된 소스 tarball이 rust git 저장소의 내용과 일치하는지 확인하려면 다음 스크립트를 템플릿으로 사용할 수 있습니다.
소스 tarball 내용 재현용 스크립트
#!/bin/bash
set -e
# You can use either a commit SHA or a stable release version (e.g. 1.XY.Z)
TAG=a8cfc83801301c2b4f0fd030192e268eeb15d473
# TAG=1.77.1
# Clone Rust toolchain repository from GitHub
git clone https://github.com/rust-lang/rust
cd rust
git reset --hard ${TAG}
git submodule update --init --recursive
cat >config.toml << EOF
[rust]
# Use for a commit SHA
channel = "nightly"
# Use for a stable release
# channel = "stable"
[dist]
compression-formats = ["xz"]
compression-profile = "fast"
EOF
# Build the source tarball from git into build/dist/
./x dist rustc-src
# Download source tarball for a commit SHA
wget https://ci-artifacts.rust-lang.org/rustc-builds/${TAG}/rustc-nightly-src.tar.xz
# Download a source tarball for a stable release
# wget https://static.rust-lang.org/dist/rustc-${TAG}-src.tar.xz
# Decompress the tarballs and check if they're the same
xz --decompress rustc-*-src.tar.xz
xz --decompress build/dist/rustc-*-src.tar.xz
diff rustc-*-src.tar build/dist/rustc-*-src.tar
Rust stable 독립 실행형 설치 프로그램 아카이브
참고: Rust 프로젝트는 최신 stable 릴리스에 대해서만 보안 패치를 지원합니다. 일반적으로 이러한 아카이브는 패치를 적용할 별도의 수단 없이 사용해서는 안 됩니다.
공식 Rust 독립 실행형 설치 프로그램에는 Rust의 단일 릴리스가 포함되어 있으며, 오프라인 설치에 적합합니다. 설치 프로그램은 세 가지 형태로 제공됩니다. 모든 Unix 계열 환경에서 동작하는 tarball(확장자 .tar.xz), Windows 설치 프로그램(.msi), Mac 설치 프로그램(.pkg)입니다. 이 설치 프로그램에는 rustc, cargo, rustdoc, 표준 라이브러리, 표준 문서가 함께 제공되지만, rustup처럼 추가 크로스 타깃을 사용할 수 있게 해 주지는 않습니다.
이 설치 프로그램을 사용하는 가장 흔한 이유는 다음과 같습니다.
- 오프라인 설치
- Windows에서 플랫폼에 더 잘 통합된 그래픽 설치 프로그램을 선호하는 경우
각 바이너리는 Rust 빌드 인프라가 GPG를 사용해 Rust 서명 키로 서명하며, 이 키는 keybase.io에서 받을 수 있습니다. 아래 표에서 .asc 파일이 서명입니다.
Stable (1.99.0)
| platform | stable (1.99.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-freebsd | tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-musl | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.98.1)
| platform | stable (1.98.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-freebsd | tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.98.0)
| platform | stable (1.98.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-freebsd | tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.97.1)
| platform | stable (1.97.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.97.0)
| platform | stable (1.97.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.96.1)
| platform | stable (1.96.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.96.0)
| platform | stable (1.96.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.95.0)
| platform | stable (1.95.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.94.1)
| platform | stable (1.94.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.94.0)
| platform | stable (1.94.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.93.1)
| platform | stable (1.93.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.93.0)
| platform | stable (1.93.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
aarch64-unknown-linux-ohos | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.92.0)
| platform | stable (1.92.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.91.1)
| platform | stable (1.91.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.91.0)
| platform | stable (1.91.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-gnullvm | tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnullvm | tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.90.0)
| platform | stable (1.90.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.89.0)
| platform | stable (1.89.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
sparcv9-sun-solaris | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-solaris | tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.88.0)
| platform | stable (1.88.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.87.0)
| platform | stable (1.87.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.86.0)
| platform | stable (1.86.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.85.1)
| platform | stable (1.85.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.85.0)
| platform | stable (1.85.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-musl | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.84.1)
| platform | stable (1.84.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.84.0)
| platform | stable (1.84.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.83.0)
| platform | stable (1.83.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.82.0)
| platform | stable (1.82.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.81.0)
| platform | stable (1.81.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-musl | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.80.1)
| platform | stable (1.80.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.80.0)
| platform | stable (1.80.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.79.0)
| platform | stable (1.79.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.78.0)
| platform | stable (1.78.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.77.2)
| platform | stable (1.77.2) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.77.1)
| platform | stable (1.77.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.77.0)
| platform | stable (1.77.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.76.0)
| platform | stable (1.76.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.75.0)
| platform | stable (1.75.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.74.1)
| platform | stable (1.74.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.74.0)
| platform | stable (1.74.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.73.0)
| platform | stable (1.73.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.72.1)
| platform | stable (1.72.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.72.0)
| platform | stable (1.72.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.71.1)
| platform | stable (1.71.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.71.0)
| platform | stable (1.71.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
loongarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.70.0)
| platform | stable (1.70.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.69.0)
| platform | stable (1.69.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.68.2)
| platform | stable (1.68.2) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.68.1)
| platform | stable (1.68.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.68.0)
| platform | stable (1.68.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.67.1)
| platform | stable (1.67.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.67.0)
| platform | stable (1.67.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.66.1)
| platform | stable (1.66.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.66.0)
| platform | stable (1.66.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.65.0)
| platform | stable (1.65.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.64.0)
| platform | stable (1.64.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.63.0)
| platform | stable (1.63.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.62.1)
| platform | stable (1.62.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.62.0)
| platform | stable (1.62.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.61.0)
| platform | stable (1.61.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.60.0)
| platform | stable (1.60.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.59.0)
| platform | stable (1.59.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.58.1)
| platform | stable (1.58.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.58.0)
| platform | stable (1.58.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.57.0)
| platform | stable (1.57.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.56.1)
| platform | stable (1.56.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.56.0)
| platform | stable (1.56.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.55.0)
| platform | stable (1.55.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.54.0)
| platform | stable (1.54.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.53.0)
| platform | stable (1.53.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.52.1)
| platform | stable (1.52.1) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.52.0)
| platform | stable (1.52.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.51.0)
| platform | stable (1.51.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.50.0)
| platform | stable (1.50.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.49.0)
| platform | stable (1.49.0) |
|---|---|
aarch64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
aarch64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.48.0)
| platform | stable (1.48.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
aarch64-unknown-linux-musl | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.47.0)
| platform | stable (1.47.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
riscv64gc-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-illumos | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.46.0)
| platform | stable (1.46.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.45.2)
| platform | stable (1.45.2) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.45.1)
| platform | stable (1.45.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.45.0)
| platform | stable (1.45.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.44.1)
| platform | stable (1.44.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.44.0)
| platform | stable (1.44.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.43.1)
| platform | stable (1.43.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.43.0)
| platform | stable (1.43.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.42.0)
| platform | stable (1.42.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.41.1)
| platform | stable (1.41.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.41.0)
| platform | stable (1.41.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.40.0)
| platform | stable (1.40.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.39.0)
| platform | stable (1.39.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.38.0)
| platform | stable (1.38.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.37.0)
| platform | stable (1.37.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.36.0)
| platform | stable (1.36.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.35.0)
| platform | stable (1.35.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-linux-musl | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.34.2)
| platform | stable (1.34.2) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.34.1)
| platform | stable (1.34.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.34.0)
| platform | stable (1.34.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.33.0)
| platform | stable (1.33.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.32.0)
| platform | stable (1.32.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.31.1)
| platform | stable (1.31.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.31.0)
| platform | stable (1.31.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.30.1)
| platform | stable (1.30.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.30.0)
| platform | stable (1.30.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.29.2)
| platform | stable (1.29.2) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.29.1)
| platform | stable (1.29.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.29.0)
| platform | stable (1.29.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.28.0)
| platform | stable (1.28.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.27.2)
| platform | stable (1.27.2) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.27.1)
| platform | stable (1.27.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.27.0)
| platform | stable (1.27.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.26.2)
| platform | stable (1.26.2) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.26.1)
| platform | stable (1.26.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.26.0)
| platform | stable (1.26.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.25.0)
| platform | stable (1.25.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.24.1)
| platform | stable (1.24.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.24.0)
| platform | stable (1.24.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.23.0)
| platform | stable (1.23.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.22.1)
| platform | stable (1.22.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.22.0)
| platform | stable (1.22.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.21.0)
| platform | stable (1.21.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.20.0)
| platform | stable (1.20.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.19.0)
| platform | stable (1.19.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.18.0)
| platform | stable (1.18.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.17.0)
| platform | stable (1.17.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.16.0)
| platform | stable (1.16.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.15.1)
| platform | stable (1.15.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.15.0)
| platform | stable (1.15.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.14.0)
| platform | stable (1.14.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
mips-unknown-linux-gnu | tar.gz tar.gz.asc |
mips64-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mips64el-unknown-linux-gnuabi64 | tar.gz tar.gz.asc |
mipsel-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64-unknown-linux-gnu | tar.gz tar.gz.asc |
powerpc64le-unknown-linux-gnu | tar.gz tar.gz.asc |
s390x-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.13.0)
| platform | stable (1.13.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.12.1)
| platform | stable (1.12.1) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.12.0)
| platform | stable (1.12.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.11.0)
| platform | stable (1.11.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.10.0)
| platform | stable (1.10.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.9.0)
| platform | stable (1.9.0) |
|---|---|
aarch64-unknown-linux-gnu | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabi | tar.gz tar.gz.asc |
arm-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
armv7-unknown-linux-gnueabihf | tar.gz tar.gz.asc |
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-freebsd | tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-unknown-netbsd | tar.gz tar.gz.asc |
Stable (1.8.0)
| platform | stable (1.8.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.7.0)
| platform | stable (1.7.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.6.0)
| platform | stable (1.6.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.5.0)
| platform | stable (1.5.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.4.0)
| platform | stable (1.4.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.3.0)
| platform | stable (1.3.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.2.0)
| platform | stable (1.2.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-pc-windows-msvc | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.1.0)
| platform | stable (1.1.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Stable (1.0.0)
| platform | stable (1.0.0) |
|---|---|
i686-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
i686-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
i686-unknown-linux-gnu | tar.gz tar.gz.asc |
x86_64-apple-darwin | pkg pkg.asc tar.gz tar.gz.asc |
x86_64-pc-windows-gnu | msi msi.asc tar.gz tar.gz.asc |
x86_64-unknown-linux-gnu | tar.gz tar.gz.asc |
Rust 릴리스 채널 구성
참고 이 문서는 규범적인 문서가 아니라 서술적인 문서로 보아야 하며, 아직 완성되지 않았습니다. 여기에 설명된 내용이 모두 정확하다거나, 일을 어떻게 해야 하는지 정의한다고 여기지 마십시오.
이 문서의 내용 중 상당 부분은 2016년에 Brian Anderson이 Rust internals 포럼에 올린 게시물에서 가져왔습니다.
Rust 릴리스는 static.rust-lang.org에 배포되며, 이곳에서 저희 CDN을 거쳐 https로 제공됩니다. 릴리스 채널(stable, beta, nightly)은 여러 부분으로 이루어져 있지만, 모두 매니페스트 파일 하나에서 출발합니다.
채널 매니페스트
manifests.txt 파일에는 지금까지 게시된 채널 매니페스트의 경로가 나열되어 있습니다.
채널 매니페스트는 최상위 디렉터리 /dist/에 들어 있습니다. 매니페스트 파일의 이름은 channel-rust-[channelname].toml입니다. 각 채널 매니페스트에는 .sha256 파일이 함께 제공됩니다. 이 파일은 매니페스트 파일의 체크섬으로, 다운로드한 데이터의 무결성을 확인하는 데 사용할 수 있습니다. 또한 각 채널의 매니페스트에는 .asc 파일도 함께 제공됩니다. 이 파일은 분리된 GPG 서명으로, 채널 매니페스트의 무결성뿐 아니라 진위도 확인하는 데 사용할 수 있습니다.
stable, beta, nightly 채널 외에 각 릴리스마다 channel-rust-x.yy.z.toml이라는 이름의 매니페스트도 있으며, 이 매니페스트에도 해당하는 .sha256 파일과 .asc 파일이 함께 제공됩니다.
날짜 기반 채널을 지원하기 위해 날짜마다 아카이브 폴더(YYYY-MM-DD 형식의 이름)가 있으며, 이 폴더에는 그날의 필요한 채널 파일 사본이 들어 있습니다. 예를 들어 nightly-2019-02-16을 설치했다면 채널 파일은 https://static.rust-lang.org/dist/2019-02-16/channel-rust-nightly.toml입니다.
채널 매니페스트의 내용
채널 매니페스트는 toml 파일입니다. 이를 v2 매니페스트라고 부릅니다. v1 매니페스트는 릴리스에 포함된 파일을 나열한 목록일 뿐이며, 모든 채널에서 항상 생성되지는 않습니다. 현재는 v2 매니페스트만 사용할 것을 권장하며, 이 절에서도 이 매니페스트를 다룹니다.
.toml 파일의 최상위에는 중요한 키/값 쌍이 두 개 있습니다. 첫째는 manifest-version으로, 현재 값은 "2"입니다. 둘째는 매니페스트의 날짜(date)로, 값은 "YYYY-MM-DD" 형식입니다.
그 밖에 최상위 섹션(테이블)이 여러 개 있으며, 다음과 같습니다.
-
pkg- 매니페스트 내용의 대부분을 차지하며, 릴리스에 포함된 패키지를 나열합니다. 보통rust,rustc,cargo같은 패키지가 여기에 들어갑니다.rust패키지는 다소 특별한 패키지로, 현재는 다른 패키지 중 기본으로 설치할 패키지를 지정하는 데 사용됩니다.패키지 안에는
components와extensions가 있습니다. 현재rustup은components를 기본으로 설치합니다.extensions는 선택적 구성 요소이며,rustup component add같은 명령으로 설치할 수 있습니다. -
renames- 패키지 이름 변경 정보를 담고 있습니다. 사용자가 패키지의 별칭을 입력하면 이 정보를 사용하여 가져올 올바른 패키지를 결정할 수 있습니다.이름 변경은 보통 패키지가 미리 보기 단계를 벗어나 릴리스 품질에 이르렀다고 판단될 때 사용합니다. 예를 들어
rustfmt의 실제 패키지 이름은rustfmt-preview입니다. 하지만rustfmt가 릴리스된 이후로는renames.rustfmt테이블이 있으며, 이 테이블의to필드 값은rustfmt-preview입니다. 사용자가rustup component add rustfmt를 실행하면 이름이 자동으로rustfmt-preview로 변환되고,rustup component list를 실행하면 사용자에게 보여 줄 때rustfmt-preview가 다시rustfmt로 자동 변환됩니다. -
profiles- 이는 설치할 기본 구성 요소 집합을 결정하기 위한 향후 설정의 일부입니다.rustup은pkg.rust의components를 선택하는 대신profiles테이블의 항목 중 하나를 따르게 됩니다. 보통은default항목이 사용되며, 이 항목은 본질적으로(정확히 같지는 않지만)["rustc", "cargo", "rust-std", "rust-docs", "rustfmt", "clippy"]에 해당합니다.그 밖의 프로필로는
minimal(["rustc", "cargo", "rust-std"])과, 표준 라이브러리 소스 사본(rust-src),miri,lldb,llvm-tools,rust-analysis등을 추가로 포함하는complete가 있습니다.
채널 매니페스트의 패키지 항목
앞서 설명한 것처럼, 패키지는 자신의 구성 요소와 확장(대부분 rust 패키지에만 해당합니다)을 나열하며, 타깃별 tarball 및 sha256 데이터를 제공할 수 있습니다.
예를 들어 패키지는 다음과 같을 수 있습니다.
[pkg.cargo.target.powerpc64-unknown-linux-gnu]
available = true
url = "https://static.rust-lang.org/dist/2019-05-23/cargo-0.36.0-powerpc64-unknown-linux-gnu.tar.gz"
hash = "279f3a84f40e3547a8532c64643f38068accb91c21f04cd16e46579c893f5a06"
xz_url = "https://static.rust-lang.org/dist/2019-05-23/cargo-0.36.0-powerpc64-unknown-linux-gnu.tar.xz"
xz_hash = "cf93b387508f4aea4e64f8b4887d70cc07a00906b981dc0c143e92e918682e4a"
여기서 이 항목이 cargo 패키지의 powerpc64-unknown-linux-gnu 타깃용임을 알 수 있습니다. url/hash 조합은 .tar.gz용이고, xz_url/xz_hash 쌍은 같은 tarball을 xz로 압축한 파일용입니다. url과 hash 쌍은 둘 중 하나만 있을 수도 있고 둘 다 있을 수도 있지만, 둘 다 없는 것은 available이 false로 설정되어 해당 패키지와 타깃 조합을 현재 이 채널에서 사용할 수 없음을 나타내는 경우가 아니라면 의미가 없습니다.
또한 패키지의 버전을 제공하는 단일 항목이 다음 형식으로 존재합니다.
[pkg.cargo]
version = "0.36.0 (6f3e9c367 2019-04-04)"
여기서 version은 사실상 $tool --version 출력에서 도구 이름을 뺀 값입니다.
타깃
타깃은 cargo build --target=$target으로 무언가를 빌드할 때 사용하는 것과 같은 트리플이며, rustup target add $target을 사용해 설치 환경에 추가할 수 있습니다. 이렇게 하면 rustup은 실제로 해당 타깃용 rust-std 패키지를 찾아 설치합니다. 본질적으로 가상의 명령인 rustup component add rust-std.$target과 같습니다.
어떤 타깃의 rust-std 패키지가 available = true가 아니면 해당 타깃은 rustup으로 설치할 수 없습니다. 이런 일은 하위 티어 타깃에서 가끔 발생합니다.
pkg 테이블에서 구성 요소와 확장은 타깃별로 지정되므로, 모든 타깃의 rust-std가 모든 rust 타깃의 확장에 지정되어 있는 것을 확인할 수 있습니다. 따라서 어떤 빌드 시스템에서든 원하는 rust-std를 설치해 크로스 컴파일을 할 수 있습니다.
서비스 인프라
Rust 인프라 팀은 여러 서비스와 봇을 유지 관리합니다. 전체 목록은 여기에서 확인할 수 있습니다. 현재 상태를 포함하여 인프라에 관한 질문은 #t-infra Zulip 스트림에 남겨 주십시오.
안정성 보장: 저희 서비스 중 상당수는 공개적으로 접근할 수 있는 스토리지와 API에 의존하지만, 이들 모두가 외부 공개 사용을 위한 것은 아닙니다. 현재로서는 static.rust-lang.org 뒤에 있는 리소스만 stable로 간주되며, 이는 해당 리소스가 (최소한) 사전 공지 없이는 변경되지 않는다는 뜻입니다. 여러분의 작업이 Rust 프로젝트 인프라의 다른 부분에 의존하고 있다면 인프라 팀에 알려 주십시오.
팀 유지 관리
Rust 팀의 구성원 명단은 항상 바뀝니다. 때때로 새로운 사람이 추가되기도 하지만, 어떤 사람은 “전임 구성원 상태“를 선택하기도 합니다. 이는 그 사람이 현재 의사 결정 절차에 적극적으로 참여하지 않는다는 뜻입니다. 안타깝게도 새로운 사람이 추가되거나 누군가 전임 구성원 상태로 전환될 때마다 여러 곳을 따로따로 갱신해야 합니다.
팀 저장소
팀 구성원 자격은 주로 rust-lang/team 저장소의 설정 파일로 관리됩니다. 이 저장소와 연동된 시스템은 해당 저장소의 README를 참고하십시오.
팀 저장소 변경 규칙
저장소에 대한 풀 리퀘스트는 team-repo-admins(팀 저장소 관리자)가 병합하며, 이들은 다음 규칙에 따라 PR을 병합합니다:
people 및 teams 디렉터리
변경 사항이 개인과 관련되어 있고 권한을 확장하지 않는다면 해당 개인의 승인만 있으면 됩니다. 변경 사항이 이미 팀 저장소 외부에서 이루어진 경우(예: GitHub 사용자 이름 변경)에는 암묵적으로 승인된 것으로 간주합니다. 여기에는 다음과 같은 사항이 포함되며, 이에 국한되지 않습니다:
- 팀 구성원 자격을 전임 구성원으로 변경하거나 완전히 제거하기
- 이메일 주소 변경하기
- zulip-id 추가하기
변경 사항이 추가 권한을 부여한다면 팀 리드가 해당 변경을 승인해야 합니다. “상위 팀” 계통에 속한 팀 리드라면 누구든 승인할 수 있습니다. 여기에는 다음과 같은 사항이 포함됩니다:
- 기존 팀 아래에 새로운 하위 팀 추가하기
- 기타 메타데이터(웹사이트 설명, Zulip 그룹 등) 변경하기
repo 디렉터리
repo 디렉터리는 저장소 접근 권한을 관리하는 데 그치지 않고 저장소를 설정하고 자동화를 관리하는 데에도 사용된다는 점에서 다른 디렉터리와 조금 다릅니다.
다음 변경 사항은 반드시 team-repo-admin이 승인하고 병합해야 합니다:
- 자신의 팀이 소유한 저장소에 대한 접근 권한 변경하기
- 저장소의 소유권은 현재 공식적으로 추적되지 않습니다. 소유권 추적이 추가되기 전까지 team-repo-admins는 어느 팀이 해당 저장소를 소유하는지 스스로 판단해야 합니다. 확신이 서지 않으면 확인을 요청하고, 그 결과를 해당 저장소의 주석으로 명문화해야 합니다.
bors.rust.review권한 변경하기perf권한 변경하기crater권한 변경하기
반면, 저장소의 설정이나 자동화에 대한 변경은 infra-admins가 승인하고 병합할 수 있습니다:
- 일반 저장소 설정 변경
- 여기에는 봇에 저장소 접근 권한을 부여하는 것도 포함됩니다.
- 저장소 규칙 세트 변경
- GitHub Actions를 통해 게시되는 크레이트의 신뢰할 수 있는 게시(trusted publishing) 설정 변경
소스 코드 변경
팀 저장소에는 사용자가 편집하는 TOML 파일을 변환하고 검증하는 코드도 들어 있습니다. 이 코드는 인프라 팀이 소유하므로, 변경하려면 인프라 팀의 승인을 받아야 합니다.
team-repo-admins(팀 저장소 관리자)에는 누가 속합니까?
이 그룹의 구성원은 리더십 위원회가 지명하고 승인하지만, 공식적인 기준에 따라 선정되지는 않습니다. 궁극적으로는 위의 정책을 강제하는 자동화가 추가됨에 따라 이 그룹이 존재해야 할 필요성이 줄어들기를 기대합니다.
또한 infra-admins 팀은 인프라가 계속 운영될 수 있도록 필요할 때 변경할 수 있게 팀 저장소를 포함한 Rust 인프라에 대한 “root” 자격 증명을 보유하고 있다는 점에 유의하십시오. 다만 이러한 권한은 필요한 경우에만 행사해야 하며, 변경 사항에 대한 첫 번째 연락 창구는 team-repo-admins(팀 저장소 관리자)입니다. (두 팀의 구성원은 겹칠 수 있습니다.)
변경 시 추가 절차
정식 팀 구성원 자격
정식 팀 구성원으로 만들려면 다음 위치를 수정해야 합니다:
- 팀 저장소
- 해당 구성원이 리뷰 순번에 참여할 예정이라면, 리뷰를 맡을 저장소의
triagebot.toml파일 중[assign.owners]섹션에 추가해야 합니다
팀 구성원의 탈퇴
다음의 모든 위치에서 해당 팀 구성원을 제거하십시오:
- 팀 저장소
- 해당 구성원이 관여했던 모든 저장소의
triagebot.toml파일 - 1password
rustc 저장소에 포함된 도구 처리(“toolstate”)
Rust 저장소에는 여러 외부 git 서브모듈(예: Book, Reference)이 포함되어 있습니다. toolstate 시스템은 beta 릴리스를 제외하고 이러한 서브모듈이 깨진 상태로 있는 것을 허용하기 위해 사용됩니다.
이는 문서가 rust-lang/rust CI와 문서 저장소의 CI 양쪽에서 모두 테스트되기 때문에 필요합니다. rustc에 문서를 깨뜨리는 변경이 있으면, 문서를 깨뜨리는 그 rustc 버전은 아직 병합되지 않아 존재하지 않으므로 문서를 업데이트할 수 없습니다. 일반적으로 두 저장소 모두에서 CI가 통과 상태여야 합니다.
toolstate 시스템은 rust-lang/rust에서 문서가 일시적으로 “실패” 상태에 있는 것을 허용함으로써 이 문제를 해결합니다. 테스트가 실패하기 시작하면 해당 서브모듈의 메인테이너에게 알림이 전송됩니다. 그러면 메인테이너가 이를 수정할 책임을 집니다.
“도구“가 가질 수 있는 세 가지 상태는 test-pass, test-fail, build-fail입니다.
이 페이지에서는 toolstate 시스템이 어떻게 동작하는지, 그리고 어떤 도구가 언제 깨지는 것이 허용되는지(또는 허용되지 않는지)에 대한 규칙을 개략적으로 설명합니다.
참고: 과거에는 toolstate 시스템이 컴파일러와 밀접하게 결합된 도구(rustfmt나 miri 등)를 관리하는 데 사용되었습니다. 그러나 이후 이러한 도구는 git 서브트리를 사용하도록 전환되었으므로, 해당 도구는 항상 테스트를 통과해야 하며 모든 실패는 이를 깨뜨린 PR 안에서 해결되어야 합니다.
이 문서는 “도구“라는 용어를 사용하지만, 이 글을 쓰는 시점에서 추적되는 대상은 외부 문서뿐입니다.
Toolstate 규칙
-
모든 도구에 대해, PR이 해당 도구를 변경하는 경우(서브모듈이 사용하는 커밋을 변경하는 경우) 해당 PR 이후 도구는
test-pass상태여야 하며, 그렇지 않으면 CI가 실패합니다. -
“nightly 전용” 도구를 제외한 모든 도구에는 다음과 같은 추가 규칙이 적용됩니다.
- PR이
beta또는stable브랜치에 병합되는 경우, 도구는test-pass상태여야 합니다. - beta가 만들어지기 전 주에 PR이 기본 브랜치에 병합되고 해당 PR이 도구를 회귀시키는 경우(상태를 “악화“시키는 경우), CI가 실패합니다. 이는 beta를 만들 수 있도록 모든 도구가
test-pass상태가 되도록 돕기 위한 것입니다. (다음 beta 컷오프가 언제인지는 Forge 색인을 참고하십시오.)
이 글을 쓰는 시점에서 “nightly 전용” 도구는 embedded-book입니다.
- PR이
toolstate 저장소 업데이트
toolstate 저장소의 업데이트는 두 단계로 이루어집니다. CI가 auto 브랜치(bors가 PR을 통합해도 좋은지 테스트하기 위해 옮기는 곳)에서 실행될 때, 개별 플랫폼(이 글을 쓰는 시점에서는 Linux와 Windows)의 “도구” 러너가 각각 테스트 중인 커밋에 대한 각 도구의 상태를 기록한 JSON 파일을 저장소에 제출합니다. 이후 해당 커밋이 실제로 CI를 완전히 통과하고 bors가 이를 기본 브랜치로 옮기면, toolstate 저장소의 “현재 도구 상태“가 적절히 업데이트됩니다.
또한 이 스크립트들은 도구가 깨지면 자동으로 일부 사람들에게 핑을 보내고 이슈를 생성합니다.
자세한 내용은 관련 파일인 checktools.sh, publish_toolstate.py 및 그 안에서 언급된 다른 파일의 주석을 참고하십시오.
도구 업데이트
서브모듈을 적절한 커밋으로 업데이트하여 도구를 업데이트할 수 있습니다.
git submodule update --remote path/to/submodule을 실행하고, 업데이트 내용을 추가한 뒤, 테스트가 통과하는지 확인하고, 커밋한 다음 풀 리퀘스트를 보내십시오. 경로는 rust 저장소의 루트를 기준으로 하므로, 예를 들어 reference의 경로는 src/doc/reference입니다.
필수는 아니지만 subup이 이 작업에 도움이 될 수 있습니다.
도구 추가
참고: 저희는 점진적으로 서브모듈과 toolstate에서 벗어나려 하고 있습니다. 서브모듈 대신 서브트리를 추가하는 것을 고려하십시오: #70651
추적할 새 도구를 추가하려면 다음 단계를 거쳐야 합니다:
- 서브모듈과 함께 필요한 빌드 시스템 / bootstrap 변경 사항을 추가하는 PR을 rust-lang/rust에 생성하십시오. 이와 같은 이슈를 피하려면 테스트가
./x.py --no-fail-fast를 제대로 지원하는지 주의하십시오. checktools.sh에 대한 변경 사항을 포함하십시오:- 맨 위에서 도구를 빌드하십시오. 이 단계가 실제로 도구의 JSON 상태를 생성하는 단계입니다.
config.toml에save-toolstates가 설정되어 있으면 Rust 빌드 시스템이 각 테스트의 상태를 담은 JSON 파일을 작성합니다. - 도구를
status_check에 추가하고, beta 블로커로 지정할지 여부도 함께 지정하십시오.
- 맨 위에서 도구를 빌드하십시오. 이 단계가 실제로 도구의 JSON 상태를 생성하는 단계입니다.
publish_toolstate.py를 수정하여 도구를 추가하십시오. 여기에는 도구가 망가졌을 때 핑할 사람 목록과 도구의 소스 저장소가 포함됩니다. (참고: 이 글을 작성하는 시점 기준으로, 이 사용자들은 rust-lang/rust GitHub에서 담당자로 지정될 수 있는 권한을 가지고 있어야 합니다.)- toolstate 저장소에 PR을 제출하여
latest.json파일에 도구를 수동으로 추가하십시오.
인프라 팀의 정책
이 섹션에서는 인프라 팀이 만든 정책을 설명합니다.
손상된 nightly에 관한 정책
CI가 자동으로 릴리스하는 nightly가 일부 사용자 또는 모든 사용자에게 손상된 상태로 배포되는 경우가 있습니다. 이 정책은 그러한 경우에 인프라 팀이 어떻게 대응할지를 정의합니다.
롤백 대상 nightly
nightly는 다음과 같은 경우에만 롤백할 수 있습니다.
- 파괴적인 코드가 포함된 경우입니다. 예를 들어 포함된 컴파일러가 사용자의 모든 파일을 삭제하는 경우가 이에 해당합니다.
- 인프라 문제로 인해 Tier 1 플랫폼 중 어느 하나에서 상당수 사용자에게 nightly가 손상된 경우입니다. 하위 티어 플랫폼에만 영향을 주는 이슈는 롤백할 만한 사안이 아닙니다. 해당 플랫폼에서는 애초에 빌드가 제대로 동작한다고 보장하지 않기 때문입니다.
심각한 컴파일러 버그로 인해 손상된 nightly는 롤백하지 않습니다. 그러한 버그는 CI에서 잡아내야 하며, nightly에는 원래 컴파일러 회귀가 있을 수 있습니다. 이로 인해 대형 프로젝트가 손상되더라도 예외는 없습니다.
복구 대상
인프라 팀 구성원 중 누구라도 이 정책에 따라 nightly를 롤백하기로 결정하면, 가장 최근에 정상적으로 동작하던 nightly로 롤백합니다. 롤백을 통해 rustup으로 nightly를 설치하는 과정이 복구되어야 합니다.
$ rustup toolchain install nightly
문서나 수동으로 내려받을 수 있는 아티팩트 등 다른 항목은 롤백하지 않아도 됩니다. nightly를 롤백한 후에는 @rustlang 트위터 계정과 상태 페이지에 롤백 사실을 공지해야 합니다.
Rust 프로젝트의 다중 인증
Rust 인프라 팀은 여러 시스템에 대한 접근을 보호하기 위해 다중 인증을 도입하고 있으며, 특히 Rust의 핵심 인프라로 간주되는 서비스에는 더 엄격한 규칙을 적용합니다.
다중 인증과 보증 수준
Rust 인프라 팀은 NIST의 인증 보증 수준(Authentication Assurance Levels)을 사용하여 각 MFA 방식이 제공하는 보안 수준에 따라 점수를 매깁니다. 이에 따라 다음 방식을 안전하고 승인된 MFA 방식으로 간주합니다(선호 순서대로):
- FIDO2 / Webauthn과 호환되는 하드웨어 보안 키(예: YubiKey): AAL-3
- Webauthn 패스키를 지원하는 하드웨어(예: Apple TouchId): AAL-2
- TOTP 앱(예: Google Authenticator): AAL-2
MFA와 핵심 인프라 접근
원칙적으로, 핵심 인프라로 간주되는 서비스가 여러 MFA 방식을 지원하는 경우 특권 또는 관리자 접근 권한을 가진 프로젝트 구성원은 해당 서비스 제공자가 지원하는 가장 안전한 MFA 방식을 반드시 사용해야 합니다. 즉, 가능한 경우 항상 하드웨어 보안 키를 사용해야 하며, 하드웨어 키를 사용할 수 없는 경우에는 패스키나 TOTP 앱을 사용해야 합니다.
이러한 서비스에는 다음이 포함됩니다:
- Google Workspace 및 GCP(
rust-lang.org) - AWS(AWS SSO 세션을 통한 접근)
- Azure
- Github
- Datadog
- Fastly
- Heroku
- 1password
Rust 인프라 팀은 테스트를 거쳐 승인된 AAL-3 기기로 Yubico YubiKey 5 시리즈를 공식 지원합니다. 프로젝트 구성원은 원한다면 다른 제조사의 하드웨어 키를 사용해도 되지만, 이 경우 Rust 인프라 팀은 버그나 호환성 문제에 대한 지원을 제공할 수 없습니다.
또한 서비스가 안전한 MFA 방식과 기기를 여러 개 지원하는 경우, 추가하는 MFA 기기나 방식이 같은 AAL에 속한다면 프로젝트 구성원은 예비용으로 MFA 방식을 하나 이상 추가로 설정하는 것이 좋습니다. 예를 들어 heroku 계정에 MFA를 설정할 때 예비용으로 YubiKey(AAL-3)를 하나 더 설정할 수는 있지만, 같은 목적으로 1password를 TOTP(AAL-2)로 설정해서는 안 됩니다. 이렇게 하면 보안이 저하될 수 있기 때문이며, 특히 관리 작업 중에 TOTP 백업이 공격 경로에 노출될 수 있는 방식으로 설정되어 있다면 더욱 그렇습니다.
마지막으로, 핵심 인프라 접근 권한을 가진 프로젝트 구성원이 MFA에 사용하던 하드웨어 기기를 잃은 경우(예: 노트북을 도난당했거나 YubiKey를 분실한 경우) 이 사실을 Rust 인프라 팀에 알려야 하며, 해당 기기는 허용된 MFA 기기/방식으로 등록되어 있던 모든 시스템에서 반드시 즉시 등록 해제되어야 합니다.
Yubico 하드웨어 키 지원
Rust 재단은 Yubico Secure it Forward 프로그램의 일환으로 핵심 인프라 접근 권한을 가진 Rust 프로젝트 구성원에게 YubiKey를 제공합니다. 이러한 지원 대상에 해당하며 권장 YubiKey를 무료로 받고 싶다면 Zulip의 T-infra(인프라 팀)에 연락하십시오.
다음 팀의 구성원이 지원 대상입니다:
infracrates.iodocs.rsreleasetriagebotbors
인프라 가이드라인
이 섹션에는 프로젝트 인프라를 사용하려는 다른 팀을 위해 인프라 팀이 작성한 가이드라인이 담겨 있습니다.
정적 웹사이트를 위한 Rust 인프라 호스팅
Rust 인프라 팀은 모든 Rust 팀이 이용할 수 있는 정적 웹사이트 호스팅을 제공합니다. 이 문서는 웹사이트가 충족해야 하는 요구 사항과 웹사이트를 설정하는 방법을 설명합니다.
웹사이트 호스팅 요구 사항
- 웹사이트는 Rust 팀이 관리하거나 프로젝트와 공식적으로 연계되어 있어야 합니다. 인프라 팀은 자원이 한정되어 있어 커뮤니티 프로젝트에는 호스팅을 제공할 수 없습니다.
- 웹사이트의 콘텐츠와 빌드 도구는 rust-lang 또는 rust-lang-nursery 조직의 GitHub 저장소에 호스팅되어야 합니다. 인프라 팀은 언제든지(예를 들어 호스팅을 옮겨야 하는 경우) 웹사이트 콘텐츠를 다시 빌드할 수 있어야 하며, 인프라 팀이 관리하는 조직 내 GitHub 저장소에 호스팅하는 것이 이를 보장하는 가장 좋은 방법입니다. 모든 저장소가 공개되어 있기를 바라지만, 필수 요건은 아닙니다.
- 웹사이트는 CI 서비스로 빌드하고 배포해야 합니다. 저희는 인프라에서 정적 웹사이트를 호스팅하기 위한 자체 도구를 갖추고 있으며, 현재 이 도구는 Travis CI 및 Azure Pipelines와 함께 동작합니다. 다른 CI 서비스가 필요하다면 미리 문의해 주십시오. 원하시는 제공자에 맞게 도구를 조정해 드리겠습니다.
- 웹사이트는 Mozilla Observatory에서 A+ 등급을 받아야 합니다. 브라우저에는 HTTP 응답 헤더를 통해서만 켜고 끌 수 있는 여러 보안 기능이 있으며, 이 기능들은 사용자의 개인 정보 보호를 강화하고 익스플로잇이 동작하지 못하도록 막습니다. Observatory에서 A+ 등급을 받았다는 것은 중요한 헤더가 모두 올바르게 설정되어 있다는 뜻입니다.
- 웹사이트는 인프라 팀이 검증한 플랫폼에 호스팅되어야 합니다. 호스팅으로는 GitHub Pages 또는 (rust-lang AWS 계정의) Amazon S3를, CDN으로는 CloudFront를 권장합니다. 다른 플랫폼이 필요하더라도 저희가 안전하고 신뢰할 수 있다고 판단하는 플랫폼이라면 괜찮습니다.
정적 웹사이트 구성
일부 호스팅 제공자의 제약을 피하기 위해 저희는 CloudFront에서 추가 사용자 정의 동작을 사용할 수 있도록 설정해 두었습니다. 이러한 동작은 생성된 웹사이트 콘텐츠의 루트에 있는 website_config.json 파일을 통해 구성합니다.
사용자 정의 헤더 추가하기
인프라 팀이 정적 웹사이트를 호스팅하기 위한 요구 사항 중 하나는 Mozilla Observatory에서 A+ 등급을 받는 것이며, 이를 위해서는 사용자 정의 헤더를 설정해야 합니다. 사용자 정의 헤더를 설정하려면 website_config.json에 headers 섹션을 추가해야 합니다. 다음 예시에는 Observatory에서 B 등급을 받는 데 필요한 헤더가 모두 포함되어 있습니다(A+ 등급을 받으려면 Content Security Policy가 필요합니다):
{
"headers": {
"Strict-Transport-Security": "max-age=63072000",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "no-referrer, strict-origin-when-cross-origin"
}
}
GitHub Pages 리디렉션 문제 해결하기
GitHub Pages는 CloudFront 뒤에 있으면서 리디렉션을 발행해야 할 때 이상하게 동작하는 이슈가 있습니다. 실제 도메인 이름을 알지 못하므로 올바른 프로토콜과 도메인 대신 http://org-name.github.io/repo-name을 리디렉션의 기준으로 사용합니다. 이 동작을 막으려면 website_config.json에 github_pages_origin 키를 추가하고, 그 값으로 오리진 기본 URL(프로토콜 제외)을 지정해야 합니다:
{
"github_pages_origin": "org-name.github.io/repo-name"
}
배포 가이드
이 배포 단계는 저희 AWS 계정에 대한 접근 권한이 필요하므로 인프라 팀 구성원이 수행하도록 되어 있습니다.
AWS 구성하기
CloudFront 웹 배포를 생성하고 다음 속성을 설정하십시오:
- 원본 도메인 이름: rust-lang.github.io/repo-name
- Origin Protocol Policy: HTTPS Only
- Viewer Protocol Policy: Redirect HTTP to HTTPS
- Lambda Function Association:
- Viewer Response: arn:aws:lambda:us-east-1:890664054962:function:static-websites:4
- 대체 도메인 이름: your-subdomain-name.rust-lang.org
- SSL 인증서: 사용자 지정 SSL 인증서
- 해당 서브도메인 이름의 인증서를 ACM을 통해 요청해야 합니다(인증서 검증에는 DNS 챌린지를 사용하십시오).
- 설명: your-subdomain-name.rust-lang.org
배포가 전파될 때까지 기다린 후 해당 배포의 .cloudfront.net 도메인 이름을 기록해 두십시오.
해당 도메인의 Route 53 호스팅 영역으로 이동하여 새 레코드 세트를 생성하십시오:
- 이름: your-subdomain-name(사용할 서브도메인 이름)
- 유형: CNAME
- 값: 앞서 확인한
.cloudfront.net도메인 이름
웹사이트 변경 사항을 배포하는 데 사용하는 CI 제공자가 허용 목록에 등록된 자동 작업을 수행할 수 있도록 AWS IAM 사용자를 생성하십시오. 사용자 이름으로 ci--ORG-NAME--REPO-NAME(예: ci--rust-lang--rust)을 사용하고, 프로그래밍 방식 액세스를 허용한 뒤 ci-static-websites IAM 그룹에 추가하십시오. 그런 다음 액세스 키 ID와 비밀 액세스 키는 나중에 필요하므로 기록해 두십시오.
배포 키 추가하기
웹사이트를 배포할 때는 GitHub 토큰(세분화된 접근 범위 지정을 지원하지 않으므로)이 아니라 저장소마다 고유하며 쓰기 권한이 있는 배포 키를 사용합니다. 배포 키를 설정하려면 해당 저장소의 관리자여야 하며, simpleinfra 저장소를 클론한 후 다음 명령을 실행하십시오:
$ cargo run --bin setup-deploy-keys rust-lang/repo-name
이 명령을 실행하려면 GITHUB_TOKEN(여기에서 생성할 수 있습니다)과 TRAVIS_TOKEN(여기에서 확인할 수 있습니다)이 있어야 합니다. 이 명령은 새 키를 생성하여 GitHub에 업로드하고, 저장소가 Travis CI에서 활성화되어 있으면 해당 키를 사용하도록 Travis CI를 구성합니다.
Travis CI 구성하기
웹사이트를 실제로 배포하려면 .travis.yml에 다음 코드 조각을 추가해야 합니다(RUSTINFRA_DEPLOY_DIR 및 RUSTINFRA_CLOUDFRONT_DISTRIBUTION의 내용은 알맞게 바꾸십시오):
env:
RUSTINFRA_DEPLOY_DIR: path/to/be/deployed
RUSTINFRA_CLOUDFRONT_DISTRIBUTION: ABCDEFGHIJKLMN
import:
- rust-lang/simpleinfra:travis-configs/static-websites.yml
또한 Travis CI 웹 UI에서 앞서 생성한 IAM 사용자의 자격 증명으로 AWS_ACCESS_KEY_ID 및 AWS_SECRET_ACCESS_KEY 환경 변수의 값을 설정해야 합니다. 비밀 액세스 키는 빌드 로그에서 반드시 숨겨야 하며, 액세스 키 ID는 공개적으로 보이도록 두어야 합니다.
Azure Pipelines 구성하기
웹사이트를 실제로 배포하려면 파이프라인 YAML 파일의 맨 위에 다음 코드 조각을 추가해야 합니다:
resources:
repositories:
- repository: rustinfra
type: github
name: rust-lang/simpleinfra
endpoint: rust-lang
그런 다음 배포를 실행하려는 위치에 다음 단계를 추가하면 됩니다(deploy_dir 및 cloudfront_distribution의 내용은 알맞게 바꾸십시오):
- template: azure-configs/static-websites.yml@rustinfra
parameters:
deploy_dir: path/to/output
# Optional, only needed if GitHub pages is behind CloudFront
cloudfront_distribution: AAAAAAAAAAAAAA
또한 파이프라인에 다음 환경 변수를 설정해야 합니다:
GITHUB_DEPLOY_KEY: 앞서 배포 키를 추가할 때 출력된 값(비공개)AWS_ACCESS_KEY_ID: CloudFront 무효화 권한이 있는 IAM 사용자의 액세스 키 ID(공개)AWS_SECRET_ACCESS_KEY: CloudFront 무효화 권한이 있는 IAM 사용자의 액세스 키(비공개)
인프라 팀 문서
이 섹션에는 Rust 인프라 팀이 호스팅하고 관리하는 서비스에 대한 문서가 포함되어 있습니다. 다만 링크된 리소스와 안내 대부분은 인프라 팀 구성원만 이용할 수 있습니다.
팀 구성원을 위한 AWS 접근 권한
Rust 팀의 일부 구성원은 프로젝트의 AWS 계정에 접근할 수 있습니다. 여기에는 인프라 팀 구성원과 AWS에서 서비스를 호스팅하는 팀의 구성원이 모두 포함됩니다.
이 문서에서는 AWS 계정에 접근하는 방법과 계정을 다루는 방법을 설명합니다. 인프라 팀 구성원으로서 다른 사람의 접근 권한을 설정하거나 회수해야 한다면 “AWS 접근 관리” 페이지를 읽으십시오.
자격 증명을 받은 후 사용자 설정하기
자격 증명을 받은 후 가장 먼저 해야 할 일은 비밀번호를 변경하고 2단계 인증을 활성화하는 것입니다. 이 작업을 마치기 전까지는 접근 권한이 2FA 설정에 필요한 권한으로만 자동 제한됩니다.
사용자를 생성한 인프라 팀 구성원에게서 받은 임시 자격 증명으로 콘솔에 로그인하십시오. 임시 비밀번호를 변경하라는 안내가 표시됩니다. 비밀번호를 변경한 뒤 다시 로그인하십시오. 그런 다음, 상단 드롭다운에 있는 “My Security Credentials”(내 보안 자격 증명) 페이지로 이동하십시오.
아래로 스크롤하여 “Assign MFA device” 버튼을 클릭하십시오. “Virtual MFA device”(일반적인 TOTP 방식입니다)를 선택하고 인증 앱으로 설정하십시오. 설정을 마쳤다면 콘솔에서 로그아웃한 뒤 다시 로그인하여 사용 권한이 부여된 리소스에 접근하십시오.
U2F 보안 키를 가지고 있더라도 “U2F security key“는 선택하지 마십시오. AWS API의 제약 때문에 CLI를 사용할 수 없게 되어 콘솔로만 접근할 수 있게 됩니다.
AWS 콘솔 사용하기
AWS 콘솔은 AWS 계정에 있는 대부분의 리소스를 다룰 수 있는 시각적 인터페이스를 제공합니다.
AWS CLI 사용하기
AWS CLI를 사용하면 터미널이나 스크립트에서 AWS 계정을 다룰 수 있습니다. 처음 설정할 때는 Amazon 문서에 따라 CLI를 설치하고 자격 증명을 설정하십시오.
CLI는 인증에 콘솔 비밀번호를 사용하지 않습니다. 콘솔의 “My Security Credentials”(내 보안 자격 증명) 페이지에서 액세스 키를 생성해야 합니다.
액세스 키를 생성한 후 aws configure를 실행하고, 콘솔에서 받은 액세스 키 ID와 비밀 키를 붙여 넣어 설정하십시오. 기본 리전으로는 us-west-1을 사용하십시오.
2단계 인증
AWS 계정의 보안을 위해 CLI를 사용하려면 2단계 인증이 필요합니다. 인프라 팀은 현재 셸에 2FA로 검증된 임시 세션을 생성하여 인증 과정을 간편하게 해 주는 스크립트를 개발했습니다. 세션은 12시간 후에 만료되며, 만료 전까지는 횟수 제한 없이 사용할 수 있습니다.
스크립트를 사용하려면 rust-lang/simpleinfra 저장소를 원하는 디렉터리에 클론하십시오. 그런 다음 AWS CLI를 사용해야 할 때마다 셸에서 다음 명령을 실행하십시오.
eval $(~/PATH/TO/SIMPLEINFRA/aws-creds.py)
이 명령은 2FA 코드를 입력하라고 요청한 뒤, 임시 자격 증명이 담긴 환경 변수 몇 개를 현재 셸에 설정합니다. 12시간이 지났거나 다른 셸에서 자격 증명을 사용하려면 이 명령을 다시 실행해야 합니다.
평문 자격 증명
기본적으로 AWS CLI는 비밀 키를 포함한 자격 증명을 아무런 암호화 없이 ~/.aws/credentials 파일에 저장합니다. 홈 디렉터리에 평문 자격 증명이 저장되어 있는 위험은 2FA 요구 사항 덕분에 어느 정도 줄어들지만, 그래도 저장하지 않는 것이 가장 좋습니다.
CLI 인터페이스를 제공하는 비밀번호 관리자를 사용한다면, 이 문제를 피하는 한 가지 방법은 자격 증명을 비밀번호 관리자에 저장하고, 필요할 때 자격 증명을 가져오도록 CLI가 비밀번호 관리자를 호출하게 설정하는 것입니다.
AWS 접근 관리
이 문서는 Rust 팀 구성원의 AWS 접근 권한을 설정하고 관리하는 방법을 설명합니다. 팀 구성원으로서 기존 자격 증명으로 AWS에 접근해야 하거나 자격 증명을 처음 받았다면 “팀 구성원을 위한 AWS 접근” 페이지를 확인하십시오.
접근 권한 부여
어떤 사람에게 접근 권한을 부여하려면 Terraform 구성의 team-members-access/_users.tf로 이동하여 새 사용자를 추가하고, 해당 사용자가 어떤 팀의 구성원이 되어야 하는지 지정하십시오. 구성을 적용하는 즉시 사용자가 생성됩니다.
기본적으로 사용자에게는 자격 증명이 연결되어 있지 않습니다. 사용자가 로그인할 수 있게 하려면 IAM 콘솔로 이동하여 방금 생성한 사용자의 보안 자격 증명 페이지를 열고 콘솔 비밀번호를 활성화하십시오. AWS가 무작위 비밀번호를 생성하도록 하고, 첫 로그인 시 비밀번호를 변경하도록 요구하십시오.
마지막으로 사용자에게 생성된 비밀번호로 로그인할 수 있다는 점을 알리고, “팀 구성원을 위한 AWS 접근” 페이지를 따라 2FA를 활성화하고 계정에 접근하는 방법을 익히도록 안내하십시오.
접근 권한 회수
어떤 사람의 접근 권한을 회수하려면 IAM 콘솔에 로그인하여 삭제하려는 사용자의 보안 자격 증명 페이지를 열고 다음을 수행하십시오.
- 콘솔 비밀번호의 “Manage“를 클릭하여 콘솔 접근을 비활성화하십시오
- 할당된 MFA 기기의 “Manage“를 클릭하여 2단계 인증을 비활성화하십시오
- “x“를 클릭하여 비활성 상태인 것을 포함한 모든 액세스 키를 제거하십시오.
콘솔에서 모든 접근 권한을 제거했다면 Terraform 구성의 team-members-access/_users.tf로 이동하여 사용자를 제거하고 구성을 적용하십시오.
AWS 리전 선택
Rust 프로젝트는 AWS에 많은 리소스를 배포했으며, 그중 대부분은 us-west-1에 있습니다. 규모가 커지고 더 많은 해외 지역으로 확장함에 따라, 어떤 리전을 사용할지 재검토하고 있습니다.
이는 주로 새 AWS 계정처럼 새로 배포하는 리소스에 해당한다는 점에 유의하십시오. 기존 리소스도 마이그레이션될 수 있지만, 이는 상당한 노력이 드는 작업이므로 한정된 시간을 고려하면 그만한 가치가 없을 수도 있습니다.
선택 기준
이 결정을 내리는 데 사용하는 기준은 두 가지입니다.
- 가격 - 리전마다 가격이 다르므로, 더 저렴한 리전에 배포하면 비용을 줄일 수 있습니다.
- 위치 - 서비스를 대부분의 사용자와 가까운 곳에 호스팅하고자 합니다. 하지만 Rust는 전 세계적으로 사용되므로 모든 사용자를 만족시킬 수는 없습니다.
가격
현재 청구 내역의 분포를 보면, 아웃바운드 트래픽이 단연 가장 비싼 항목입니다. 이 때문에 더 저렴한 리전으로 전환해서 얻을 수 있는 비용 절감 효과는 크게 제한됩니다.
AWS의 아웃바운드 트래픽 비용을 (예를 들어 Fastly로 이전하여) 크게 줄일 수 있다고 가정하더라도, 리전 간 차이는 그리 크지 않습니다.
위치
트래픽 대부분이 미국에서 발생하므로, 인프라의 대부분을 미국에서 운영하고자 합니다. 고려 대상 리전은 다음과 같습니다.
us-east-1또는us-east-2(더 저렴함)us-west-1(이미 사용 중)
dev-desktop처럼 좀 더 전 세계적으로 분산하려는 서비스는 유럽에도 배포하고자 합니다. 유럽에서는 다음 리전이 가장 합리적인 것으로 보입니다.
eu-west-1(더 저렴함)eu-central-1(더 중심적인 위치)
결정
새 리소스에는 다음 리전을 사용하기로 결정했습니다.
us-east-2- 인프라 대부분이 미국에 호스팅되어 있으므로, 조금이라도 이득을 보기 위해 미국에서는 더 저렴한 리전을 사용하고자 합니다.eu-central-1- 유럽에는 그리 많은 리소스를 배포하지 않으므로, 유럽에서는 위치를 우선하여 최적화하고자 합니다.
새 리소스를 배포할 때는 기본적으로 us-east-2에 배포해야 합니다. 지리적으로 분산해야 하는 리소스만 eu-central-1에 배포해야 합니다.
Bastion 서버
- FQDN:
bastion.infra.rust-lang.org - 이 서버를 배포하기 위한 Ansible 플레이북입니다.
- AWS 리소스를 생성하기 위한 Terraform 구성입니다.
- 인스턴스 메트릭입니다(인프라 팀 구성원만 이용할 수 있습니다).
bastion을 통해 서버에 로그인하기
인프라의 보안을 강화하기 위해 SSH로 프로덕션 서버에 직접 연결할 수 없도록 되어 있습니다. 그 대신 모든 연결은 “bastion“이라는 작은 서버를 거쳐야 하며, 이 서버는 허용 목록에 등록된 몇몇 네트워크에서 오는 연결만 허용하고 모든 연결 시도를 기록합니다.
bastion을 통해 서버에 로그인하려면 다음 방법 중 하나를 사용하십시오.
-
SSH의
-J플래그를 사용하십시오.ssh -J <username>@bastion.infra.rust-lang.org <username>@servername.infra.rust-lang.org -
호스트에 연결할 때 항상 bastion을 경유하도록 SSH 클라이언트를 구성하십시오.
-
다음 코드 조각을 SSH 설정 파일(일반적으로
~/.ssh/config에 있음)에 추가하십시오:Host servername.infra.rust-lang.org ProxyJump <username>@bastion.infra.rust-lang.org -
SSH를 사용하십시오.
ssh <username>@servername.infra.rust-lang.org
-
각 계정에 로그인할 수 있도록 승인된 SSH 키는 simpleinfra 저장소에 저장되어 있습니다. 또한 민감한 1password 접근 권한이 있는 사람은 보관소(vault)에 저장된 마스터 키를 사용하여 모든 계정에 로그인할 수 있습니다.
일반적인 유지 관리 절차
bastion 서버에 새 사용자 추가하기
bastion에 새 사용자를 추가하려면 해당 사용자의 키를 ansible/roles/common/files/ssh-keys에 <username>.pub이라는 이름의 파일로 추가하고, Ansible 플레이북을 수정하여 해당 사용자를 권한 없는 사용자 목록에 추가해야 합니다. 해당 사용자가 어떤 서버에 접근할 수 있는지 밝히는 주석을 남겨 주십시오.
작업을 마쳤으면 플레이북을 적용하십시오.
Bors
인프라 팀은 rust-lang/rust에서 사용할 “Bors”라는 병합 큐 봇을 관리합니다. 해당 인스턴스는 bors.rust-lang.org에서 이용할 수 있으며, @bors GitHub 계정으로 운영됩니다.
이 서비스는 Terraform으로 구성되며, rust-lang/bors 저장소에서 ECS 클러스터로 자동 배포됩니다.
콘텐츠 전송 네트워크
Rust 프로그래밍 언어 사용자는 다양한 방식으로 프로젝트의 인프라와 상호작용합니다. 사용자는 프로젝트 웹사이트와 문서에 접속하고, 크레이트 인덱스를 조회하며, Rust 릴리스와 크레이트를 내려받습니다. 이러한 리소스는 Rust 프로젝트가 호스팅하며 콘텐츠 전송 네트워크(CDN)를 통해 제공됩니다.
이 문서에서는 CDN을 사용하는 이유와 용도, 그리고 CDN을 어떻게 구성했는지를 설명합니다.
목표
인프라에서 CDN을 사용하는 목표는 다음 세 가지입니다:
- 더 저렴한 요금과 캐싱을 통해 아웃바운드 트래픽 비용을 절감합니다
- 오리진 서버의 부하를 줄여 컴퓨팅 자원을 절약합니다
- 일부 리소스의 레거시 URL을 재작성할 수단을 제공합니다
비용 절감
오픈 소스 프로젝트로서 우리는 인프라 비용에 매우 신경을 써야 합니다. 아웃바운드 트래픽은 월별 청구서에서 단연 가장 비싼 항목 중 하나이며, Rust의 인기가 높아질수록 계속 증가할 항목입니다.
클라우드 제공업체는 일반적으로 서비스에 따라 아웃바운드 트래픽에 서로 다른 요금을 부과합니다. 예를 들어, Amazon S3에서 데이터를 직접 제공하는 것이 같은 데이터를 Amazon CloudFront 배포를 통해 제공하는 것보다 비쌉니다. 이러한 이유로 현재는 캐싱과 같은 CDN의 다른 기능을 활용할 수 없는 서비스에도 기본적으로 CDN을 사용합니다.
인프라
프로젝트 리소스의 대부분은 AWS에 호스팅됩니다. 정적 콘텐츠는 Amazon S3에 저장되고, 동적 콘텐츠는 서버에서 로드됩니다. 두 유형의 콘텐츠 모두 Amazon CloudFront와 Fastly CDN을 통해 제공됩니다.
사용자가 리소스에 접근할 때, 예를 들어 크레이트를 내려받으려 할 때, 사용자는 CDN을 통해 해당 리소스에 접근합니다. 각 배포는 도메인 이름을 구성 및 백엔드(오리진이라고 함)에 매핑합니다. 예를 들어, static.crates.io에서 크레이트를 내려받으면 S3 버킷에서 크레이트를 가져온 뒤 이후 요청에 대비해 캐시하는 배포를 거치게 됩니다.
┌──► S3 (static content)
│
User ───────► CDN ────┤
│
└──► Server (dynamic content)
배포
배포는 여러 개가 있으며, 모두 rust-lang/simpleinfra 저장소에서 구성됩니다. 그러나 사용량은 배포마다 매우 고르지 않습니다. 다음 배포들은 트래픽과 생태계에 대한 중요도 양쪽 측면에서 프로젝트에 가장 중요한 배포입니다.
Rust 릴리스
사용자가 Rust를 설치하거나 업데이트할 때마다 미리 컴파일된 바이너리를 static.rust-lang.org에서 내려받습니다. CI/CD 파이프라인에서 Rust를 설치할 때도 마찬가지이며, 이 때문에 이 배포의 트래픽 양이 단연 가장 많습니다.
Rust 바이너리는 정적이며 Amazon S3에 저장되고, 그곳에서 CDN을 통해 제공됩니다. 릴리스 채널 매니페스트에 대해 지원되는 공개 레이아웃은 릴리스 채널 레이아웃에 문서화되어 있습니다.
static.rust-lang.org의 배포에는 AWS Lambda 함수에서 실행되는 사용자 지정 라우터가 있습니다. 이 라우터는 릴리스의 파일 목록을 조회하는 기능을 제공하며, rustup.sh의 레거시 URL을 재작성합니다.
Rust 릴리스의 캐시는 매일 밤(nightly) 무효화됩니다.
크레이트
Rust 릴리스와 마찬가지로, 크레이트도 static.crates.io에서 정적 콘텐츠로 제공됩니다. 우리 인프라에서 두 번째로 큰 배포이기는 하지만, 릴리스 배포보다는 훨씬 작습니다.
크레이트는 정적이며 Amazon S3에 저장되고, CloudFront 배포를 통해 제공됩니다.
Crater 에이전트
- 소스 코드: rust-lang/crater
- 호스팅 위치:
- 메인테이너: pietroalbini
- 애플리케이션 지표(인프라 팀 구성원만 이용할 수 있습니다).
- 인스턴스 지표(인프라 팀 구성원만 이용할 수 있습니다):
서비스 구성
Crater 에이전트는 표준 구성을 갖춘 서버로, 에이전트를 호스팅하는 Docker 컨테이너를 실행합니다. 타이머가 5분마다 업데이트를 확인하며, 더 새로운 Docker 이미지가 있으면 컨테이너가 자동으로 업데이트되고 재시작됩니다. 이 서비스는 Ansible로 관리됩니다.
일반적인 유지 관리 절차
에이전트 시작 및 중지
에이전트는 container-crater-agent.service systemd 유닛으로 관리됩니다. 따라서 일반적인 systemctl 명령으로 에이전트를 시작, 중지, 재시작할 수 있습니다:
systemctl stop container-crater-agent.service
systemctl start container-crater-agent.service
systemctl restart container-crater-agent.service
에이전트 로그 확인
에이전트의 로그는 journald로 전달되어 수집됩니다. 로그를 보려면 journalctl을 사용하십시오:
journalctl -u container-crater-agent.service
컨테이너 이미지 수동 업데이트
컨테이너는 5분마다 자동으로 업데이트됩니다(더 새로운 이미지가 있는 경우). 더 빨리 업데이트해야 한다면 다음 명령을 실행하여 업데이터 서비스를 수동으로 시작할 수 있습니다:
systemctl start docker-images-update.service
Dev Desktop
Dev Desktop은 Rust 프로젝트의 메인테이너와 기여자에게 고성능 클라우드 컴퓨팅 자원을 무료로 제공합니다. 이는 Rust 재단의 Cloud Compute Program의 일부입니다.
| 머신 | 아키텍처 | Perf 사용 가능 | 위치 |
|---|---|---|---|
dev-desktop-eu-1 | aarch64 | 예 | 독일 |
dev-desktop-us-1 | aarch64 | 예 | 미국 버지니아 북부 |
dev-desktop-eu-2 | amd64 | 아니요 | 벨기에 |
dev-desktop-us-2 | amd64 | 아니요 | 미국 아이오와 |
프로그램 신청 방법
현재 이 프로그램과 컴퓨팅 인스턴스에 대한 접근 권한은 선별적으로 부여되며, 주로 프로젝트 메인테이너와 Google Summer of Code 같은 오픈 소스 프로그램 출신 기여자에게 부여됩니다. 프로그램이 개발 중인 동안에는 일부 팀으로 제한됩니다. 이러한 팀 중 하나에 속해 있다면 자동으로 접근 권한이 부여됩니다.
고성능 빌드 머신을 사용할 수 있다면 Rust 프로젝트에서의 작업이 크게 개선될 것이라고 생각되면, 팀 저장소에 자신을 cloud-compute 팀에 추가하는 풀 리퀘스트를 여십시오. Dev Desktop을 어떻게 사용할 것이며 어떤 이점을 얻을 수 있는지 간단한 설명을 포함하십시오. 이러한 PR의 예시는 여기에서 확인할 수 있습니다.
- Rust 프로젝트 팀 중 한 곳의 메인테이너가 여러분의 기여를 기꺼이 보증한다면, 인프라 팀은 일반적으로 그 보증에 따라 요청을 흔쾌히 수락합니다.
- 그 밖의 경우에는 인프라 팀이 접근 요청을 사안별로 검토합니다.
Dev Desktop에 접속하는 방법
각 사용자는 Dev Desktop에 자신의 계정을 가집니다. 계정 이름은 사용자의 GitHub 핸들 앞에 gh- 접두사를 붙인 것입니다. 예를 들어, GitHub 핸들이 user인 사용자는 Dev Desktop에서 gh-user라는 이름의 사용자 계정을 갖게 됩니다. GitHub 핸들은 대소문자를 구분하므로 gh-User와 gh-user는 서로 다른 계정으로 취급된다는 점에 유의하십시오.
사용자는 SSH로 Dev Desktop에 접속할 수 있습니다. Dev Desktop은 공개 키 인증을 사용하며, 사용자의 공개 키를 GitHub에서 자동으로 가져옵니다.
다음 명령으로 인스턴스에 접속할 수 있습니다.
ssh gh-<your-username>@<name>.infra.rust-lang.org
<name>을 페이지 상단 표에 있는 머신 이름으로 바꾸십시오. 예를 들어, dev-desktop-eu-1에 접속하려면 호스트 이름 dev-desktop-eu-1.infra.rust-lang.org를 사용하십시오.
GitHub에 공개 키가 없다면, SSH 키를 생성하고 GitHub 계정에 추가하는 방법을 설명하는 다음 가이드를 읽어 보십시오. 키를 추가한 뒤 Dev Desktop에 반영되기까지 몇 분 정도 걸릴 수 있습니다.
명령을 더 간편하게 사용하려면 다음과 같이 ~/.ssh/config에 별칭을 설정할 수 있습니다.
Host rustvm
User gh-<your-username>
HostName <name>.infra.rust-lang.org
그러면 ssh rustvm으로 접속할 수 있습니다.
SSH 호스트 키 지문
Dev Desktop에 처음 접속할 때는 SSH 호스트 키 지문을 확인해야 합니다. Dev Desktop에서는 다음 지문을 사용합니다.
| 머신 | ED25519 |
|---|---|
dev-desktop-eu-1 | SHA256:QPuC8ODm+no9wjAtrSMCuqj+iGXsFI5ZGR3C+nX7Im0 |
dev-desktop-us-1 | SHA256:UOZn6OKFrCRt+CLPl5fHZd93Tym4ckBrr8rgfgEPT5g |
dev-desktop-eu-2 | SHA256:biz6c8LJ6cdb6Ku9mlELzl1qh8sODZWtpNfAJPxBi2I |
dev-desktop-us-2 | SHA256:u6SNCQw++6LV+IhQYEqIcuFo4GIaiU7DyUhaqWEmQdI |
다음 스크립트를 실행하고 출력을 위 표와 비교하여 지문을 확인할 수 있습니다.
ssh-keygen -lf <(ssh-keyscan -t ed25519 <name>.infra.rust-lang.org 2>/dev/null | grep -v '^#')
편의를 위해 호스트 키는 known_hosts 항목 형식으로도 제공됩니다.
dev-desktop-eu-1.infra.rust-lang.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAIIWlDvWkMEX8XIu6lxvd4cFOxeFUpH4ZReKuyS3h9l
dev-desktop-us-1.infra.rust-lang.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEj0DZ/r/Sq5GdK8j80rNjCs6ctwptcHnFvP3bdjhv/5
dev-desktop-eu-2.infra.rust-lang.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOmnv10fXAimjPPfwiutVmJifRS+85rmN/zyIP/QNby6
dev-desktop-us-2.infra.rust-lang.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA2bWm/W7J3QJGRb2z63Mp8nP5rAXOEd1Owd54Wtt5AX
계정을 설정하는 방법
머신에 처음 접속할 때 해 두면 좋은 몇 가지 작업이 있습니다.
먼저 Git 사용자 이름과 이메일이 올바르게 설정되어 있는지 확인하십시오.
git config -l --global
다음 명령으로 사용자 이름과 이메일 주소를 설정할 수 있습니다:
git config --global user.name "Your name"
git config --global user.email "your-email"
셸을 사용자 지정하는 방법
rust-lang/simpleinfra 저장소의 설정 파일에 자신을 추가하면 Dev Desktop의 기본 셸을 설정할 수 있습니다. ansible/roles/dev-desktop/defaults/main.yml을 열고 vars_user_config 변수를 찾아 목록에 자신을 추가하십시오.
vars_user_config:
- username: gh-jdno
shell: /usr/bin/zsh
- username: gh-WaffleLapkin
shell: /usr/bin/fish
풀 리퀘스트를 열고 @rust-lang/infra에 리뷰를 요청하십시오(또는 Zulip의 #t-infra에서 저희를 핑하십시오).
풀 리퀘스트가 병합되면 인프라 관리자가 새 설정을 Dev Desktop에 배포해야 합니다. 배포가 끝나야 비로소 기본 셸이 변경됩니다.
Rust 툴체인을 설치하는 방법
Dev Desktop에는 Rust가 미리 설치되어 있지 않은 대신, 로컬 저장소나 워크트리에서 특정 툴체인을 쉽게 설치할 수 있습니다.
먼저 다음 명령을 실행하여 rustup을 설치하십시오:
/usr/local/bin/init.sh
자신만의 Rust 버전으로 작업하고 싶지 않거나 그럴 필요가 없다면 다음 섹션을 건너뛰고 바로 작업을 시작해도 됩니다.
아직 포크를 만들지 않았다면 GitHub에서 rust-lang/rust 저장소를 열고 개인 계정에 포크를 만드십시오. 그런 다음 Dev Desktop에 접속하여 다음 스크립트를 실행하십시오:
/usr/local/bin/setup_rust.sh
이 스크립트는 개인 포크를 Dev Desktop에 클론하고, rust-lang/rust에서 최신 버전을 체크아웃한 뒤 컴파일합니다. 이 작업이 끝나면 로컬에서 작업할 수 있도록 스테이지를 링크합니다.
이 디렉터리에는 워크트리와 Rust 버전을 관리하는 스크립트가 더 들어 있습니다. help.sh를 실행하면 스크립트 목록과 간단한 설명을 볼 수 있습니다.
GitHub와 상호 작용하는 방법
Dev Desktop은 사용자 계정에 속한 GitHub 저장소로 작업하도록 설계되었습니다. GitHub App을 사용하여 자격 증명을 보호하고, Dev Desktop이 접근할 수 있는 저장소를 세밀하게 제어할 수 있도록 합니다.
먼저 https://github.com/apps/rust-cloud-vms에 가서 앱이 저장소에 접근할 수 있도록 권한을 부여하십시오. Dev Desktop에서 사용하려는 저장소(예: rust-lang/rust의 포크)에만 접근 권한을 부여하는 것을 권장합니다.
그런 다음 Dev Desktop에 접속하여 작업하려는 저장소를 HTTPS로 클론하십시오. 그 이후에는 평소처럼 저장소로 작업할 수 있습니다.
내부적으로 GitHub App은 Git의 자격 증명 도우미 역할을 하며, 사용자가 앱에 부여한 권한 범위로 제한된 임시 액세스 토큰을 생성합니다. 오류가 발생하면 권한을 리뷰하고 앱이 저장소에 접근할 수 있도록 허용되어 있는지 확인하십시오.
Visual Studio Code에서 원격 개발을 설정하는 방법
대부분의 최신 코드 편집기는 SSH를 통한 원격 개발을 지원합니다. 이 기능을 이용하면 코드는 로컬에서 작성하고 실행은 Dev Desktop 안에서 할 수 있습니다. 구성은 편집기마다 조금씩 다르지만, 아래의 Visual Studio Code 예시는 다른 편집기에도 적용할 수 있을 것입니다.
VS Code로 원격 개발 환경을 구성하는 방법은 매우 간단하며, VS Code 문서 Remote Development using SSH에 자세히 설명되어 있습니다. 요약하면 다음과 같습니다.
- SSH로 Dev Desktop에 접속한 뒤 작업할 저장소를 로컬 폴더에 클론하십시오
- 그다음 자신의 컴퓨터에서 VS Code를 열고 Remote Development Extension Pack을 설치하십시오
- 명령 팔레트를 열고 “Remote-SSH: Connect to host”를 검색하십시오
- 사용자 이름과 인스턴스 이름(
gh-<your-username>@<instance>)을 입력하십시오 - 1단계에서 클론한 저장소의 경로를 선택하십시오
- 서버에서 실행할 확장 프로그램(예: rust-analyzer)을 설치하십시오
- VS Code를 사용해 코드를 원격으로 실행하거나 디버그하십시오
새 패키지와 도구를 요청하는 방법
Dev Desktop에 설치되어 있지 않은 도구나 패키지가 필요하다면 rust-lang/simpleinfra 저장소에 풀 리퀘스트를 열어 설치를 요청할 수 있습니다. Ansible 역할 dev-desktop에는 머신에 설치되는 시스템 패키지 목록이 담긴 dependencies.yml이라는 태스크가 있습니다. 필요한 패키지를 이 목록에 추가하고 풀 리퀘스트를 연 다음, @rust-lang/infra를 핑하여 리뷰를 요청하십시오.
먼저 https://packages.ubuntu.com/에서 해당 패키지가 arm64와 amd64 아키텍처 모두에서 제공되는지 확인해 두면 좋습니다. Dev Desktop은 현재 Ubuntu 22.04 LTS에서 실행되고 있습니다.
사용 가능한 디스크 공간
모든 사용자에게는 사용할 수 있는 디스크 공간 할당량이 정해져 있습니다. 현재 할당량은 200GB로 설정되어 있습니다.
할당량을 초과하면 Disk quota exceeded (os error 122) 오류가 발생합니다.
rustc 개발 가이드의 디스크 공간 관련 참고 사항 섹션에 디스크 공간을 정리하는 몇 가지 팁이 있습니다.
피드백을 보내고 이슈를 보고하는 방법
Dev Desktop을 사용하면서 문제가 생기거나 피드백 또는 제안이 있다면 인프라 팀에 연락하십시오.
rust-lang/simpleinfra 저장소에 이슈를 만들어 달라고 요청드릴 수도 있습니다.
Dev Desktop 변경 사항 공지
인프라 팀은 Dev Desktop의 중요한 변경 사항을 Zulip 채널 #t-infra/announcements를 통해 공지합니다.
외부 CI 러너
Rust 프로젝트는 주로 GitHub Actions에서 지속적 통합(CI)을 실행합니다. GitHub Actions가 특정 타깃을 기본적으로 지원하지 않는 경우에는 자체 호스팅 러너를 사용합니다.
사용 가능한 소프트웨어
GitHub Actions 자체 호스팅 러너에는 보통 GitHub Actions 호스팅 러너(예: ubuntu-latest)에서 사용할 수 있는 소프트웨어가 모두 들어 있지는 않습니다.
자체 호스팅 러너에서 CI가 실패하면 필요한 소프트웨어를 사용할 수 있는지 확인하십시오. 사용할 수 없다면 GitHub Actions 워크플로에서 직접 설치할 수 있습니다.
정책
새 외부 CI 러너를 추가하려면 먼저 저희 정책을 읽어 주십시오.
구성된 외부 CI 러너
이 절에서는 Rust 프로젝트의 CI에서 실행되도록 구성된 외부 CI 러너를 나열합니다.
아래 목록에 있는 각 필드의 설명은 다음과 같습니다.
max concurrency: 이 유형의 러너 중 사용 가능한 러너의 수입니다. 이 값에 따라 해당 러너에서 실행할 수 있는 GitHub Actions 작업의 최대 수가 정해집니다. 동시 실행 수는 저장소 간에 공유됩니다. 모든 러너가 사용 중이면 GitHub Actions는 사용 가능한 러너가 생길 때까지 작업을 대기열에 넣습니다.runs-on: 이 러너를 사용하려면 GitHub Actions 워크플로의runs-on필드에 지정해야 하는 레이블입니다. 러너가 어디에서 사용되는지 확인하려면 GitHub 검색에서 해당 레이블을 검색하십시오.repositories: 이 러너를 사용할 수 있도록 설정된 저장소입니다. 목록에 저장소를 추가하려면 이 파일에 대한 PR을 열어 주십시오. 러너에서 실행하려는 워크플로가zizmor검사를 통과하는지 확인하십시오.contact: 러너 유지 관리를 담당하는 사람의 연락처입니다. 권장하는 연락 방법은 Zulip이며, #t-infra 채널에 새 토픽을 열고 담당자를 멘션하시면 됩니다.
powerpc64-unknown-linux-musl
- 최대 동시성: 4
runs-on:["self-hosted", "linux", "powerpc64", "musl"]- 저장소:
- 연락처:
- Zulip: Aelin
- 이메일:
aelin@postmarketos.org(더 빠른 응답)
riscv64gc-unknown-linux-gnu
runs-on:- Ubuntu 24 러너의 경우
ubuntu-24.04-riscv입니다. - Ubuntu 26 러너의 경우
ubuntu-26.04-riscv입니다.
- Ubuntu 24 러너의 경우
- 저장소:
rustc_codegen_cranelift(활성화됨, 사용하지 않음)
- 문서
powerpc64le-unknown-linux-gnu
runs-on:ubuntu-24.04-ppc64le- 유형: IBM 러너
s390x-unknown-linux-gnu
이 타깃은 여러 외부 러너에서 사용할 수 있습니다.
Canonical 러너:
runs-on:self-hosted-linux-s390x-resolute-large-rust(ubuntu-26.04)- 저장소: 모든 저장소에서 작동하므로 따로 활성화할 필요가 없습니다.
- 유형: Canonical 러너.
IBM 러너:
runs-on:ubuntu-24.04-s390x- 유형: IBM 러너.
러너 유형
이 섹션에서는 둘 이상의 러너에 공통되는 정보를 모아 정리합니다.
Canonical 러너
- 최대 동시 실행 수: 10
- GitHub App
- 운영자 저장소
- 연락처: 다운타임은 Canonical 러너 다운타임 Zulip 토픽에 공지됩니다.
- 저장소: 앱이 조직 전체에 설치되어 있으므로 모든 저장소에서 러너를 사용할 수 있습니다.
IBM 러너
이 GitHub App은 실행하려면 관리자 권한이 필요하므로 rust-lang/rust 저장소에는 설치할 수 없습니다. 자세한 내용은 이 GitHub 이슈를 참고하십시오.
인프라 세부 사항
- GitHub App은 rust-lang-owner 사용자 계정에서 설치했습니다.
- 러너는 이 GitHub 이슈와 여기에 연결된 GitHub 이슈들에서 요청되었습니다.
dev-desktop에서 GitHub로 푸시하기 위한 GitHub App
이 안내는 dev-desktop GitHub App의 서버 측 설정과 디버깅을 위한 것입니다. 사용자는 앱 설치 URL로 안내받기만 하면 되며, 나머지는 모두 문제없이 작동할 것입니다.
모든 GitHub 작업에는 Python GitHub 라이브러리를 사용합니다. 문서는 https://pygithub.readthedocs.io/en/latest/introduction.html 에서 확인할 수 있습니다.
앱 설정 방법
- https://github.com/settings/apps 로 이동하십시오.
- 새 GitHub App(New GitHub App)
- 메타데이터(이름과 URL)를 입력하십시오.
- WebHook 체크박스를 비활성화하십시오.
Contents - Repository contents, commits, branches, downloads, releases, and merges.를 읽기/쓰기(read/write)로 설정하십시오.Workflows - Update GitHub Action workflow files.를 읽기/쓰기(read/write)로 설정하십시오.- “enable on any account“로 설정하십시오.
- 앱 생성(Create App)
- https://github.com/settings/apps/{your_app_name_here} 로 이동하여
App ID를app_id.txt(gen_temp_access_token.py와 같은 폴더)에 복사하십시오.
앱용 .pem 파일을 생성하는 방법
- https://github.com/settings/apps/{your_app_name_here}#private-key 로 이동하여 개인 키를 생성하십시오.
- 다운로드가 시작되면 파일을 비공개 위치에 저장하십시오.
- .pem 파일을
gen_temp_access_token.py와 같은 폴더에 복사하고 이름을dev-desktop.private-key.pem으로 지정하십시오.
사용자에게 앱을 설치하는 방법
- 사용자를 https://github.com/settings/apps/{your_app_name_here}/installations 로 안내하십시오.
- 사용자가 원하는 조직/사용자 계정에 앱을 설치하고, 사용하려는 저장소로 접근 범위를 제한하도록 하십시오.
특정 사용자를 위한 임시 액세스 토큰을 생성하는 방법
gen_temp_access_token.py <github_username> <github_repository_name>를 실행하십시오.
git 명령줄과의 통합
저희는 자격 증명 헬퍼(credential-helpers)를 사용하고 있습니다. 자격 증명 헬퍼를 디버깅하려면 사용자 공간에 두고 다음과 같이 호출하십시오.
git -c credential.helper -c credential.UseHttpPath=true /path/to/helper push origin branch
SSH URL로 등록된 원격 저장소에서는 이 방법이 작동하지 않는다는 점에 유의하십시오. 반드시 HTTPS를 사용해야 합니다!
첫 번째 명령줄 인수는 get, store, remove 중 하나입니다. 이 경우 어차피 호출할 때마다 자격 증명을 다시 생성하므로, get을 제외한 모든 경우에는 그냥 중단(exit(0))합니다.
실제 인자는 stdin을 통해 전달되며 보통 다음과 같은 형태입니다
protocol=https
host=github.com
path=your_repo.git
도메인 이름과 DNS
Rust 인프라 팀이 소유한 도메인의 모든 DNS 레코드는 AWS Route 53에 호스팅되어 있으며, 팀 구성원이 수정할 수 있습니다. 이 문서에는 팀 구성원이 변경 작업을 수행하는 방법에 대한 안내가 담겨 있습니다.
Terraform으로 관리되는 도메인의 DNS 레코드 변경하기
경고: 아직 모든 도메인 이름이 Terraform으로 관리되고 있지는 않습니다. 콘솔에서 영역(zone)의 설명이
[terraform]으로 시작하지 않는다면 UI에서 직접 변경해야 합니다. 다만 모든 도메인을 Terraform으로 이전하는 작업이 진행 중입니다.
경고:
terraform/services/dns에는 Terraform 외부에서 관리되는 리소스를 가리키는 DNS 레코드의 정의만 들어 있습니다. Terraform이 리소스를 관리하는 경우에는 필요한 레코드를 자동으로 추가합니다. 해당 서비스의 Terraform 구성이 어디에 있는지는 서비스 문서를 참조하십시오.
DNS 레코드는 Terraform 구성의 terraform/services/dns 디렉터리에서 관리합니다. 관리되는 각 도메인마다 도메인 이름을 따서 이름을 붙이고 .tf로 끝나는 파일이 있으며, 이 파일에는 몇 가지 기본 정보와 해당 도메인의 레코드가 들어 있습니다.
이 구성은 A, CNAME, MX, TXT 레코드 추가를 지원합니다. 도메인 파일에 포함된 모듈 정의 안에서 각 레코드 유형은 고유한 맵을 가집니다. 맵의 키는 레코드 이름이며, 값은 레코드 값의 목록입니다.
예를 들어 rust-lang.github.io를 가리키는 pages.rust-lang.org CNAME을 추가하려면 terraform/services/dns/rust-lang.org에 다음 내용을 추가해야 합니다:
module "rust_lang_org" {
# ...
CNAME = {
"pages.rust-lang.org." = ["rust-lang.github.io"],
# ...
}
}
모든 변경을 마쳤다면 다음 명령으로 적용할 수 있습니다:
terraform apply
Terraform으로 새 도메인의 DNS 관리하기
새 도메인 이름의 DNS 레코드를 관리하도록 Terraform을 설정하려면 몇 가지 단계를 거쳐야 합니다. 먼저 Terraform 내부에서 해당 도메인에 사용할 식별자를 정해야 합니다. 관례상 식별자는 도메인 이름에서 .과 -를 _로 바꾼 것입니다. 예를 들어 rust-lang.org는 rust_lang_org가 됩니다.
그런 다음 terraform/services/dns에 도메인 이름을 따서 이름을 붙이고 .tf로 끝나는 파일을 만들어 다음 내용을 넣으십시오(자리 표시자를 반드시 바꾸십시오):
module "<IDENTIFIER>" {
source = "./domain"
domain = "<DOMAIN-NAME>"
comment = "<COMMENT-FOR-THE-DOMAIN>"
ttl = 300
}
마지막으로 Terraform 구성의 다른 부분에서 레코드를 추가할 수 있도록 Route53 영역의 ID를 출력해야 합니다. terraform/services/dns/outputs.tf에 다음 코드 조각을 추가하십시오:
# ...
output "zone_<IDENTIFIER>" {
value = module.<IDENTIFIER>.zone_id
}
작업을 마쳤다면 다음 명령으로 변경 사항을 적용할 수 있습니다:
terraform init
terraform apply
서브도메인 리디렉션 추가하기
Terraform 구성은 우리가 관리하는 임의 개수의 서브도메인에서 하나의 URL로 리디렉션하는 기능을 지원합니다. 리디렉션은 다음 인프라 구성 요소로 만들어집니다:
-
리디렉션 세트마다
rust-http-redirect-<HASH>라는 이름의 S3 버킷이 하나씩 있습니다. 이 버킷은 웹사이트 호스팅이 활성화되어 있고, 들어오는 모든 요청을 선택한 URL로 리디렉션하도록 구성되어 있습니다. 이 방식을 사용하면 별도의 서버 없이 리디렉션을 구현할 수 있습니다. -
리디렉션 세트마다 ACM 인증서(및 이를 검증하기 위한 DNS 레코드)가 하나씩 있으며, 이 인증서에는 모든 소스가 대체 이름으로 포함됩니다. 이 인증서는 HTTPS 리디렉션을 활성화하는 데 사용됩니다.
-
HTTPS 요청을 지원하기 위해 리디렉션 세트마다 CloudFront 배포가 하나씩 있습니다. 이 배포는 앞서 생성한 ACM 인증서를 사용하고 요청을 S3 버킷으로 전달합니다.
-
관련 존에서 각 리디렉션에 대한 Route53 레코드: 서브도메인에는 CNAME, 에이펙스 도메인에는 ALIAS를 사용합니다.
모든 리디렉션은 terraform/redirects.tf에 정의되어 있으며, 대상 URL마다 모듈이 하나씩 있습니다. 새 URL로 리디렉션해야 하면 새 모듈을 만들고, 그렇지 않으면 기존 모듈에 새 서브도메인을 추가하십시오. 다음 예시 모듈을 참고하십시오(플레이스홀더는 반드시 실제 값으로 바꾸십시오):
module "redirect_<IDENTIFIER>" {
source = "./modules/subdomain-redirect"
providers = {
aws = "aws"
aws.east1 = "aws.east1"
}
to = "<DESTINATION-URL>"
from = {
"<SUBDOMAIN-1>" = module.dns.zone_<DOMAIN-1-IDENTIFIER>,
"<SUBDOMAIN-2>" = module.dns.zone_<DOMAIN-2-IDENTIFIER>,
}
}
모든 변경을 마쳤다면 다음 명령으로 구성을 적용할 수 있습니다:
terraform init
terraform apply
CloudFront 배포 변경 사항은 전파 속도가 매우 느리므로, 변경 사항 하나를 배포하는 데 약 15분이 걸린다는 점에 유의하십시오. 또한 기존 리디렉션에 도메인을 추가하거나 제거하면 ACM 인증서를 다시 생성해야 합니다. 따라서 여러 리소스가 다시 생성되더라도 정상입니다.
도메인 이름을 Rust로 이전하기
인프라 팀 구성원은 다음 단계에 따라 도메인 이름을 Rust 프로젝트의 등록 대행자로 이전해야 합니다:
-
해당 도메인 이름을 프로젝트가 소유하려고 하는지 인프라 팀 내부에서 확인하십시오. 더 복잡한 경우에는 이 문제를 리더십 위원회에 올려야 합니다.
-
도메인 이름이 아직 AWS Route 53을 네임서버로 사용하지 않는다면, 도메인의 현재 소유자에게 옮겨야 할 모든 DNS 레코드의 목록을 요청하십시오. 그런 다음 도메인을 이전하기 전에 모든 레코드를 Route 53의 새 호스팅 영역에 추가하십시오. 이 단계에 대한 자세한 내용은 DNS 이전에 관한 아래 섹션을 참고하십시오.
-
이전할 수 있도록 도메인 이름의 잠금을 해제해 달라고 현재 소유자에게 요청하고, 소유자에게서 이전 코드를 받으십시오. 이전 코드는 도메인 이전의 핵심이므로 공개된 커뮤니케이션 플랫폼으로 받지 마십시오.
-
AWS Route 53의 Transfer Domain 섹션으로 이동하여 도메인 이름을 입력하십시오. 오류가 나지 않으면 앞서 받은 이전 코드를 입력하고, 기존 Route 53 호스팅 영역을 사용하도록 선택하십시오. 오류가 나면 어떤 단계가 빠졌는지 자세히 표시됩니다. 호스팅 영역은 올바른 영역으로 자동 완성됩니다. Rust 재단이 출범하기 전까지는 Pietro의 정보를 도메인 연락처로 사용하십시오. 마지막으로 모든 내용을 리뷰하고 이전 절차를 완료하십시오.
-
현재 소유자에게 등록 대행자가 보낼 이메일을 기다려 달라고 알리십시오. 그 이메일에서 링크를 클릭하여 도메인 이름 이전을 확인해 달라고 요청할 것입니다.
-
이전 절차에는 시간이 꽤 걸립니다. 도메인이 이전되었다는 이메일이 admin@rust-lang.org로 오면 작업이 끝납니다! 🎉🎉🎉
DNS 이전하기
대부분의 도메인 이름은 등록 대행자를 DNS 서버로 사용합니다. 그러면 도메인을 다른 곳으로 이전한 뒤에는 기존 등록 대행자도 DNS 트래픽 처리를 중단합니다. 그러므로 실제로 이전 절차를 시작하기 전에 모든 DNS 레코드가 AWS Route 53으로 올바르게 복사되었는지 확인해야 합니다.
현재 도메인 소유자에게 모든 A, AAAA, CNAME, TXT, MX 레코드를 명시적으로 요청하십시오. MX 레코드를 제외한 모든 레코드는 Terraform DNS 구성에 복사해야 합니다(해당 도메인 이름으로 새 파일을 만들고, 다른 도메인 이름의 파일을 참고하십시오).
일부 레코드가 현재 등록 대행자가 제공하는 HTTP 리디렉션 서비스를 참조하고 있다면, 해당 레코드는 도메인 이전이 끝날 때까지 기다려야 합니다. 이전이 완료되면 Terraform에 새 도메인 리디렉션을 추가하십시오. HTTPS 리디렉션용 TLS 인증서를 요청하려면 이 작업은 이전이 끝난 후에 해야 합니다.
도메인에 MX 레코드가 있다면 해당 레코드는 Mailgun으로 이전해야 합니다. Mailgun에 접속하여 해당 도메인 이름을 추가하십시오. 도메인이 US 리전에 있고, 공유 IP를 사용하며, 1024비트 DKIM 키를 사용하는지 확인하십시오(2048비트 키는 AWS Route 53 레코드 하나에 들어가지 않습니다). 그런 다음 추적용 CNAME 레코드를 제외한 모든 레코드를 Terraform DNS 구성에 복사하고, 도메인 이전이 완료될 때까지 기다리십시오. 이전이 완료되면 Mailgun으로 돌아가 해당 도메인의 DNS 설정을 검증하십시오. 마지막으로 팀 저장소의 config.toml에 도메인을 추가하고, 필요한 메일링 리스트를 일반적인 절차에 따라 만드십시오.
ECS 서비스 관리
프로젝트 인프라에서 실행되는 일부 애플리케이션은 저희 AWS 계정의 ECS 클러스터에 호스팅되어 있습니다. 이 문서는 이러한 애플리케이션을 운영할 때 따라야 하는 일반적인 유지 관리 절차를 설명합니다. 여기에서 설명하는 작업 대부분은 AWS 접근 권한이 필요합니다.
참고: 저희 ECS 클러스터는 북부 캘리포니아(
us-west-1) AWS 리전에 있습니다. AWS 콘솔을 사용할 때는 이 리전이 선택되어 있는지 확인하십시오.
로그 확인하기
ECS에 호스팅된 애플리케이션의 로그는 CloudWatch Logs에 저장되며 AWS 콘솔에서 확인할 수 있습니다. 콘솔을 열고 CloudWatch Logs로 이동한 뒤 /ecs/<service-name>이라는 로그 그룹을 선택하십시오. 로그를 확인하는 방법은 두 가지입니다:
-
애플리케이션 전체를 살펴봐야 한다면 “View all log events” 버튼(클래식 인터페이스에서는 “Search Log Group”)을 클릭하여 통합된 보기를 확인할 수 있습니다.
-
특정 컨테이너 인스턴스를 디버깅해야 한다면 실행 중인 각 태스크별로 분리된 로그 스트림을 사용할 수 있습니다. 스트림 이름은 컨테이너 이름과 태스크 ID를 따라 지정됩니다.
로그는 주기적으로 삭제됩니다(보존 기간은 애플리케이션에 따라 다릅니다).
애플리케이션 재시작하기
애플리케이션을 재시작하려면 새 코드를 미리 푸시하지 않고도 새 배포를 강제로 실행할 수 있습니다. 이렇게 하려면 다음 명령을 실행하십시오:
aws ecs update-service --cluster rust-ecs-prod --service <service-name> --force-new-deployment
배포 롤백하기
잘못된 배포를 롤백하려면 셸에 AWS 자격 증명이 설정된 상태에서 simpleinfra 저장소에 있는 aws-rollback.py 스크립트를 실행하면 됩니다. 이 스크립트는 ECR 컨테이너 이미지 저장소의 이름을 첫 번째이자 유일한 인수로 받습니다:
./aws-rollback.py <image-repository-name>
스크립트는 저장소에 있는 이미지 목록을 보여 주고, 롤백할 이미지 번호를 묻습니다. 번호를 입력하면 스크립트는 latest 태그가 선택한 이미지를 가리키도록 변경하며, 저장소와 같은 이름의 ECS 서비스가 있으면 해당 서비스도 재시작합니다.
애플리케이션 변경 사항 배포하기
각 애플리케이션은 자체 Docker 컨테이너를 저희 AWS 계정의 ECR 저장소에 저장합니다. 변경 사항은 수동으로도, (GitHub Actions를 사용해) 자동으로도 배포할 수 있습니다.
프로덕션 애플리케이션에는 자동 배포를 설정하는 것을 권장합니다.
수동 배포
로컬 빌드를 수동으로 배포하려면 먼저 빌드한 이미지에 ECR 이름으로 태그를 지정해야 합니다:
docker tag <image-tag> 890664054962.dkr.ecr.us-west-1.amazonaws.com/<repository-name>:latest
그런 다음 ECR에 인증하고 이미지를 푸시할 수 있습니다:
$(aws ecr get-login --no-include-email --region us-west-1)
docker push 890664054962.dkr.ecr.us-west-1.amazonaws.com/<repository-name>:latest
마지막으로 다음 명령으로 ECS 서비스의 새 배포를 강제로 실행해야 합니다:
aws ecs update-service --cluster rust-ecs-prod --service <service-name> --force-new-deployment
GitHub Actions를 사용한 자동 배포
인프라 팀은 CI에서의 배포를 자동화하는 GitHub Actions용 액션을 준비해 두었습니다. 이 액션을 사용하려면 팀 구성원에게 저장소에 AWS 자격 증명을 설정해 달라고 요청한 뒤, 워크플로에 다음 코드 조각을 추가하십시오:
- name: Build the Docker image
run: docker build -t deploy-image .
- name: Deploy to production
uses: rust-lang/simpleinfra/github-actions/upload-docker-image@master
with:
image: deploy-image
repository: <ecr-repository-name>
region: us-west-1
redeploy_ecs_cluster: rust-ecs-prod
redeploy_ecs_service: <service-name>
aws_access_key_id: "${{ secrets.AWS_ACCESS_KEY_ID }}"
aws_secret_access_key: "${{ secrets.AWS_SECRET_ACCESS_KEY }}"
if: github.ref == 'refs/heads/<deploy-branch>'
<ecr-repository-name>, <service-name>, <deploy-branch>를 워크플로에 맞는 올바른 값으로 반드시 바꾸십시오. 배포용으로 선택한 브랜치에 워크플로 변경 사항이 병합되면, 이후 해당 브랜치에 푸시되는 모든 커밋이 ECS 클러스터에 배포됩니다.
Rust 프로젝트의 하드웨어 보안 키
개요
Rust 인프라의 하드웨어 보안 키
하드웨어 보안 키는 민감한 시스템과 인프라를 피싱이 불가능한 방식으로 보호하여 보안을 강화합니다. Rust 인프라 팀은 Yubico Secure it Forward 프로그램과 협력하여 하드웨어 보안 키를 공식적으로 지원합니다.
현재 다음 팀의 구성원이 이 지원 대상입니다.
infracrates.iodocs.rsreleasetriagebotbors
Rust 재단은 핵심 인프라 시스템에 접근해야 하는 Rust 프로젝트 구성원에게 YubiKey를 제공합니다. 이 지원 대상에 해당하고 권장 YubiKey를 무료로 받고 싶다면 Zulip의 T-infra에 연락하십시오.
Rust 인프라의 YubiKey 지원
지원 모델
Rust 인프라 팀은 Yubico Series 5 USB/NFC 모델 제품을 검증했으며, 프로젝트 구성원에게 발생할 수 있는 모든 문제에 대해 Yubico Series 5 키를 공식적으로 지원합니다.
지원 펌웨어 버전
기존 보안 권고에 따라 펌웨어 버전 v5.7.4 이상인 YubiKey만 허용됩니다. ykman CLI나 Yubico Authenticator를 사용하여 YubiKey의 현재 펌웨어 버전을 확인할 수 있습니다.
권장 첫 단계
Yubico가 제공하는 공식 오픈 소스 도구 중 YubiKey 설정에 도움이 되는 도구가 최소 두 가지 있습니다.
- Yubico Authenticator (소스: 데스크톱 및 Android | iOS):
- 데스크톱 앱으로 대부분의 작업을 충분히 처리할 수 있습니다.
- 모바일 앱은 Google Play나 Apple App Store에서 바로 내려받을 수 있습니다.
- Yubico Manager CLI (소스: yubikey-manager): 고급 사용 사례에 적합합니다.
좋은 첫 단계로서, Rust 인프라 팀은 Yubico Authenticator 데스크톱 앱을 사용하여 다음 항목의 기본값을 변경할 것을 권장합니다.
CLI를 선호하는 분이라면 ykman Python 유틸리티를 시스템에 전역으로 설치하거나 Python 설치를 관리하지 않고 실행하는 대안으로 uvx를 사용할 수 있습니다. 하위 명령에 대해 자세히 알아보려면 ykman 온라인 문서를 참고하십시오.
uvx --from yubikey-manager ykman --help
또는 다른 방법으로
alias ykman="uvx --from yubikey-manager ykman"
ykman --help
webauthn을 이용한 다중 인증
Yubico는 모든 제품에 FIDO2 표준을 구현하고 있습니다. 따라서 webauthn 기반의 2차 인증 요소를 지원하는 웹 시스템이라면 YubiKey를 다중 인증 장치로 설정할 때 추가 설정 없이 그대로 동작해야 합니다.
예를 들어 Github에서 YubiKey를 MFA 장치로 설정하려면 이 공식 절차를 참고하십시오. 진행 중 어느 시점에 다음과 같은 대화 상자가 표시됩니다.
설정 흐름은 시스템마다 다릅니다. 또한 설치된 웹 브라우저 확장 프로그램에 따라서도 달라질 수 있습니다(예: 1password는 이러한 설정 과정에서 자신을 선택지로 제시합니다). 그러나 일반적으로는 웹 브라우저의 보안 키 대화 상자를 주의 깊게 확인하십시오.
이 단계에서는 하드웨어 키를 통해 사용자가 실제로 있는지 확인합니다. YubiKey를 터치하여 인증을 확인하십시오.
저희 MFA 정책은 호환되는 모든 핵심 Rust 인프라 시스템에서 이 옵션을 사용하도록 의무화하고 있습니다.
하드웨어 기반 다중 인증
다중 인증을 요구하지만 안전한 MFA 방식으로 TOTP 코드만 지원하는 시스템에서도 YubiKey를 Google Authenticator 같은 기존 솔루션의 대안으로 사용할 수 있습니다. 이 경우 Yubico Authenticator 앱을 사용하여 하드웨어 기반 TOTP 코드 생성을 설정할 수 있습니다.
이 옵션을 사용하면 서드파티 클라우드 시스템에 의존하지 않고도 휴대폰과 데스크톱 시스템 양쪽에서 TOTP 코드를 사용할 수 있습니다. 다만 TOTP 코드 생성이 물리적 장치에 묶인다는 단점도 있습니다. 오프라인 전용 TOTP 인증 앱을 사용할 때와 마찬가지로 Yubikey를 잃어버리면 TOTP 코드에도 접근할 수 없게 됩니다. 따라서 복구 코드 백업에 각별히 주의해야 합니다.
Rust 프로젝트 구성원에게 하드웨어 기반 TOTP 코드 설정은 선택 사항입니다.
하드웨어 기반 SSH 키
SSH 인증 보안을 강화하고 싶다면 YubiKey가 제공하는 여러 하드웨어 기반 SSH 키 쌍 옵션을 사용할 수 있습니다. SSH 키를 하드웨어 기반으로 두면 SSH 개인 키를 여러 기기에서 손쉽게 옮겨 가며 사용할 수 있습니다.
예를 들어 Github 계정에서 Yubikey 기반 SSH 키를 사용하려면 OpenSSH에 내장된 FIDO2 인증 지원으로 시작하는 것이 가장 쉬울 수 있습니다.
설정 방법은 운영 체제에 따라 다릅니다. 또한 커밋 서명 같은 작업이 계속 예상대로 동작하도록 Git 설정을 조정해야 할 수도 있다는 점에 유의하십시오. 특히 Git 작업을 할 때마다 패스키 PIN을 묻도록 설정하면 오히려 역효과가 날 수 있습니다. 따라서 그렇게 설정하지 않을 것을 권장합니다.
예를 들어 다음 명령은 FIDO2를 통해 새 레지던트 SSH 키를 정의합니다. 이 키는 매번 패스키를 묻지 않으며, Git 작업을 할 때 하드웨어 키를 터치할 필요도 없습니다.
ssh-keygen -t ed25519-sk \
-O resident \
-O application=ssh:git \
-C "me@email.com"
하드웨어 기반 SSH 키는 현재 선택 사항이지만, 앞으로 일부 인프라 관리 작업에서는 필수가 될 수 있습니다. YubiKey에 어떤 레지던트 SSH 키가 있는지는 CLI나 Yubico Authenticator 앱으로 확인할 수 있습니다.
$ ykman fido credentials list
Enter your PIN:
Credential ID RP ID Username Display name
86707903... ssh:git openssh openssh
PIV 기반 키와 증명
YubiKey는 스마트 카드용 개인 신원 확인(PIV) 표준과 호환됩니다. 따라서 이 표준을 기반으로 암호화 및 서명 작업에 YubiKey를 사용할 수 있습니다.
규칙 및 요구 사항
PIV를 사용하기 전에 기본 PIN과 PUK를 반드시 변경해야 합니다. ykman 또는 Yubico Authenticator 앱을 사용하십시오. ykman을 사용하는 경우 다음을 실행하십시오.
ykman piv access change-pin
ykman piv access change-puk
Yubico Authenticator로도 PIV 키를 생성하고 관리할 수 있지만, 일부 보안 옵션은 ykman에서만 사용할 수 있습니다.
PIV 기반 키 생성
PIV 기반 키를 생성할 때는 다음 보안 요구 사항을 따르십시오.
ECCP256암호화 알고리즘을 우선적으로 사용하십시오.- PIV 슬롯에 접근할 때 최소
once수준의 PIN 확인을 요구하도록 설정하십시오. - YubiKey를 사람이 직접 조작(터치)하도록 요구하십시오.
권장 보안 기본값으로 키를 생성하여 9a 슬롯(인증 용도)에 저장하려면 다음을 실행하십시오.
ykman piv keys generate 9a - --algorithm ECCP256 --pin-policy once --touch-policy always
마찬가지로, 키를 생성하여 9c 슬롯(서명 용도)에 저장하려면 다음을 실행하십시오.
ykman piv keys generate 9c - --algorithm ECCP256 --pin-policy once --touch-policy always
PIV 슬롯에서 공개 키와 인증서 내보내기
키를 생성한 후에는 PIV 슬롯과 관련된 공개 키를 내보내어 외부에 보관할 수 있습니다.
ykman piv keys export 9a pubkey-9a.pem
ykman piv keys export 9c pubkey-9c.pem
이 공개 키는 자체 서명 X.509 인증서를 생성할 때에도 필요합니다. 예를 들어, 9a PIV 슬롯과 연결된 인증서를 생성하려면 다음을 실행하십시오.
ykman piv certificates generate 9a pubkey-9a.pem --subject "CN=<your name>" --valid-days 2000
다음 사항에 유의하십시오.
subject는 RFC-4514 문자열이어야 합니다valid-days는 인증서 유효 기간을 설정합니다(기본값은 365일입니다)
증명 내보내기 및 팀 DB를 통한 공유
자체 서명 인증서 외에도 PIV 기반 키 쌍에 대한 증명 인증서를 생성할 수도 있습니다.
ykman piv keys attest 9a attestation-9a.pem
ykman piv keys attest 9c attestation-9c.pem
이 증명을 검증할 수 있도록 하려면, YubiKey에 미리 탑재되어 있으며 Yubico의 루트 증명 CA로 서명된 Yubico의 중간 증명 인증서도 내보내야 합니다.
ykman piv certificates export f9 f9-intermediate.pem
이 파일들을 팀 DB에 추가하려면 다음 단계를 따르십시오.
- 팀 저장소의 team/hardware-keys 아래에 YubiKey의 일련번호를 이름으로 하는 디렉터리를 만드십시오.
- 설정한 PIV 슬롯에 대한
attestation-9*.pem파일을 추가하십시오. - YubiKey의
f9-intermediate.pem증명 파일을 추가하십시오. team/people/<your-user>.toml에서 이 파일들을 링크하십시오.
자세한 내용은 TOML 스키마를 참고하십시오.
자주 묻는 질문
어떤 서비스에서 YubiKey로 2FA를 사용해야 합니까?
MFA 정책을 확인하십시오.
선호하는 클라우드 CLI 인증을 YubiKey로 어떻게 처리합니까?
Gcloud CLI의 경우, 웹 기반 인증 흐름이 선택한 2FA 방식을 자동으로 요청하며, 그 이후에는 추가 단계가 필요하지 않습니다. YubiKey를 Google 계정의 MFA 방식 중 선호하는 방식으로 설정하기만 하면 됩니다.
AWS CLI의 경우, CLI 설정을 구성할 때 IAM 사용자 세션이 아닌 AWS SSO 사용자 세션을 사용해야 합니다. 이 흐름은 선택한 웹 브라우저로 로그인할 때 2FA 방식을 요청합니다.
Rust 인프라는 AWS Identity Center 구성을 통해 프로젝트 구성원에게 SSO 접근을 제공합니다. 다만 YubiKey를 AWS 사용자 계정에서 선택한 MFA 방식으로 설정하는 작업은 여전히 필요합니다.
모니터링
- 호스팅 위치:
monitoring.infra.rust-lang.org(bastion 뒤에 있습니다 – 접속 방법) - 메인테이너: pietroalbini, 인프라 팀
- 공개 URL: grafana.rust-lang.org
- 이 서버를 배포하는 Ansible 플레이북입니다.
- 인스턴스 메트릭입니다(인프라 팀 구성원만 이용할 수 있습니다).
서비스 구성
저희 모니터링 서비스는 세 부분으로 구성됩니다. 메트릭을 스크레이프, 수집, 모니터링하는 Prometheus, Prometheus가 생성한 알림을 발송하는 Alertmanager, 메트릭을 표시하는 Grafana입니다. 모든 부분은 Ansible을 통해 구성됩니다.
메트릭은 어차피 Prometheus가 7일 후에 삭제하므로 백업하지 않지만, Grafana 대시보드는 PostgreSQL 데이터베이스에 저장되며 이 데이터베이스는 restic으로 rust-backups 버킷(monitoring 하위 디렉터리)에 백업됩니다. 백업을 복호화하는 비밀번호는 1password에 있습니다.
일반적인 유지 관리 절차
새 메트릭 소스 스크레이프하기
Prometheus는 자체 형식으로 작성된 메트릭을 얻기 위해 HTTP 엔드포인트 목록을 주기적으로 스크레이프하는 방식으로 동작합니다. 저희 구성에서 이 목록은 simpleinfra 저장소에 있는 ansible/playbooks/monitoring.yml 파일의 prometheus_scrape 섹션에 있습니다.
새 메트릭 소스를 추가하려면 엔드포인트를 기존 job에 추가하거나, 스크레이프하려는 메트릭이 다른 어떤 job과도 관련이 없다면 새 job에 추가하십시오. 엔드포인트는 모니터링 인스턴스에서 접근할 수 있어야 합니다. 사용 가능한 모든 옵션은 Prometheus 문서에서 확인할 수 있습니다.
새 알림 만들기
알림은 Prometheus 구성에 정의된 사용자 정의 규칙이 참으로 평가될 때마다 Prometheus가 생성합니다. 저희 구성에서 규칙 목록은 simpleinfra 저장소에 있는 ansible/playbooks/monitoring.yml 파일의 prometheus_rule_groups 섹션에 있습니다.
새 알림을 추가하려면 기존 그룹이나 새 그룹에 알림 규칙을 만들어야 합니다. 전체 옵션 목록은 Prometheus 문서에서 확인할 수 있습니다.
사용자에게 권한 추가하기
사용자에게 저희 Grafana 인스턴스에 대한 접근 권한을 부여하려면 두 단계가 필요합니다.
먼저, 사용자가 GitHub 계정으로 인스턴스에 로그인할 수 있게 하려면 해당 사용자가 로그인이 허용된 팀의 구성원이어야 합니다. 팀 목록은 simpleinfra 저장소에 있는 ansible/playbooks/monitoring.yml 파일의 grafana_github_teams 섹션에 정의되어 있으며, GitHub 팀 ID 목록을 담고 있습니다. ID를 가져오려면 다음 명령을 실행하십시오:
curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/orgs/<ORG>/teams/<NAME> | jq .id
사용자가 로그인이 허용된 팀의 구성원이 되면 “viewer” 권한으로 기본 Grafana 조직에 자동으로 추가됩니다. 인프라 팀 구성원이라면 이 권한을 “admin“으로 변경해야 하며(“Configuration” -> “Users“에서), 그 외에는 viewer로 두십시오.
기본적으로 뷰어는 제한이 없는 대시보드에만 접근할 수 있습니다. 다른 대시보드에 대한 접근 권한을 부여하려면 “Configuration” -> “Teams” 페이지에서 사용자를 팀에 추가해야 합니다. 전체 Grafana 인스턴스에 대한 관리자 권한은 “Server Admin”(서버 관리) -> “Users” -> “<username>” 페이지에서 부여할 수도 있습니다. 신뢰할 수 있는 인프라 팀 구성원이 아니면 이러한 권한을 부여하지 마십시오.
추가 자료
Renovate
Renovate는 크레이트, GitHub Actions, Docker 베이스 이미지 등의 의존성을 최신 상태로 유지하도록 저희(인프라 팀)가 권장하는 도구입니다.
의존성 업데이트에 관하여
의존성을 최신 상태로 유지해야 하는 이유는 무엇입니까?
버그 수정, 성능 개선, 보안 패치, 새로운 기능을 얻고 전반적으로 더 나은 개발자 경험을 누리기 위해서입니다.
의존성은 얼마나 자주 업데이트해야 합니까?
의존성 업데이트 PR을 너무 자주 받으면 감당하기 어렵습니다. 예를 들어, 의존성의 새 버전이 나올 때마다 PR을 받는 것은 권장하지 않습니다.
그 대신, 주 1회나 월 1회처럼 정기적인 일정에 따라 몇 개의 PR만 받는 것을 권장합니다. 예를 들어, GitHub Actions 업데이트용 PR 하나, 호환되는 크레이트 업데이트용 PR 하나, 호환되지 않는 크레이트 업데이트마다 PR 하나씩을 받는 방식입니다.
의존성 업데이트를 자동으로 병합해야 합니까?
신뢰할 수 있는 테스트 스위트가 있고, PR을 병합할 때 CI가 프로덕션에 자동으로 배포하거나 아티팩트를 게시하지 않는다면, CI 검사를 통과한 의존성 업데이트는 자동 병합해도 안전합니다.
저장소에 Renovate를 추가하는 방법
1. renovate GitHub App 설치하기
team 저장소에 있는 해당 저장소의 toml 파일에 bots = ["renovate"] 또는 bots = ["forking-renovate"]를 추가하십시오.
예시로 annotate-snippets-rs를 참고하십시오.
두 앱의 차이점은 다음과 같습니다.
renovateGitHub App은 대상 저장소에 직접 업데이트 브랜치를 생성합니다. 이를 위해서는 저장소 콘텐츠에 대한 쓰기 권한이 필요합니다. 이 권한 덕분에 자동 병합도 지원합니다.forking-renovateGitHub App은 자체 포크에 브랜치를 생성한 뒤 대상 저장소로 PR을 엽니다. 대상 저장소에 대한 권한은 전혀 필요하지 않지만, 공개 저장소에서만 동작하며 자동 병합을 지원하지 않습니다.
2. Renovate 구성하기
다음 내용으로 .github/renovate.json5 파일을 생성하십시오.
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>rust-lang/renovate"]
}
참고:
- 기본 구성이 마음에 들지 않는다면, rust-lang/renovate를 살펴보고 저희 조직 프리셋을 사용자 지정하는 방법을 알아보십시오.
- 저희 조직 프리셋이 모두 마음에 들지 않는다면, 어떤 프리셋도 확장하지 않는 자체 구성을 사용할 수도 있습니다. 저희 조직 프리셋을 개선할 아이디어가 있다면 언제든지 PR을 보내 주십시오!
- 다른 파일 형식과 위치도 지원합니다. 자세한 내용은 Renovate 문서를 참고하십시오.
renovate.json경로에 대한 GitHub 코드 검색으로 Rust 조직의 다른 설정 파일을 찾아 참고할 수 있습니다.
3. Renovate가 작동하는지 확인하기
Renovate가 의존성 대시보드 GitHub 이슈를 생성했는지 확인하십시오. 이 이슈와 상호작용하여 저장소에서 PR을 트리거할 수 있습니다.
4. Renovate 동작 문제 해결
Renovate가 예상대로 동작하지 않는 이유를 모르겠다면 로컬에서 dry-run 모드로 실행하여 의존성 업데이트를 어떻게 해석하는지 확인할 수 있습니다:
RENOVATE_DRYRUN_TOKEN=$(gh auth token) &&\
docker run --rm -it\
-e RENOVATE_TOKEN="$RENOVATE_DRYRUN_TOKEN"\
-e GITHUB_COM_TOKEN="$RENOVATE_DRYRUN_TOKEN"\
-v /tmp:/tmp\
-v $PWD:/usr/src/app\
renovate/renovate:latest renovate --platform=local --repository-cache=reset --dry-run=lookup
로그가 더 필요하면 위 명령에 -e LOG_LEVEL=debug를 추가하십시오.
지원
Renovate가 작동하지 않거나 궁금한 점이 있으면 #t-infra Zulip 채널에 질문하십시오.
rust-bots(Rust 봇)
일반적인 유지 관리 절차
새 도메인 추가하기
먼저 sudo vim /etc/nginx/nginx.conf로 nginx 설정을 편집하여 도메인을 추가하십시오.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name <domain>.infra.rust-lang.org; # Edit <domain> to match here
location /.well-known/acme-challenge {
root /home/ssl-renew/challenges;
}
location / {
# configure the domain here
}
}
그런 다음 sudo -i -u ssl-renew vim renew.sh를 실행하십시오. 추가하려는 도메인을 지정하는 --domains 줄을 스크립트에 추가하십시오.
그런 다음 스크립트를 실행하십시오: sudo -i -u ssl-renew ./renew.sh
Rust CI의 작동 방식
rust-lang/rust 저장소의 지속적 통합(CI) 워크플로는 기본 브랜치가 항상 유효한 상태를 유지하도록 보장합니다.
CI 인프라는 rustc-dev-guide에 자세히 설명되어 있습니다.
rust-lang/rust CI 불변 조건
rust-lang/rust 저장소의 기본 브랜치와 beta/stable 릴리스 브랜치에 대한 변경 사항은 두 가지 CI 작업 세트를 거칩니다.
- PR CI. 더 빠른 피드백을 제공하기 위해 PR을 대상으로 실행되는, 빠르고 제한된 CI 작업 세트입니다.
- Merge CI: rust-lang/rust에 대한 모든 변경 사항이 통과해야 하는 전체 CI 작업 세트입니다. 여기에는 모든 Tier 1 타깃에 대한 테스트 작업 등이 포함되며, 이에 국한되지 않습니다.
저희는 예외로 분리한 부분을 제외하면 PR CI 작업이 Merge CI 작업의 부분집합이어야 한다는 rust-lang/rust CI 불변 조건을 강제합니다. PR CI 작업과 Merge CI 작업 간의 예외 차이는 https://github.com/rust-lang/rust/issues/144259에서 추적합니다. 이러한 부분집합 불변 조건을 두는 이유는, PR 전용 CI 작업이 있으면 어떤 PR이 PR 전용 작업에서는 실패하면서도 Merge CI는 통과할 수 있기 때문입니다. 이렇게 되면 전혀 관련 없는 PR에서도 이후의 모든 PR CI가 실패할 수 있으며, 이는 다른 기여자에게 매우 답답한 일입니다.
Sentry
인프라 팀은 Rust 팀이 사용할 수 있도록 sentry.io에서 Sentry 조직을 관리합니다. 이 인스턴스는 Sentry의 후원으로 운영되며, 이 문서에서는 그 사용 방법을 설명합니다.
인스턴스에 로그인하기
rust-lang GitHub 조직의 모든 구성원은 자신의 GitHub 자격 증명으로 Sentry 인스턴스에 인증할 수 있습니다. 인증 페이지에 방문하여 “Single Sign-On” 탭을 클릭하고 rust-lang 조직 ID를 입력하십시오. 그러면 GitHub 계정으로 로그인하라는 안내가 표시됩니다!
Sentry 조직에 처음 로그인하는 경우 소속 팀에 대한 접근 권한을 요청해야 할 수도 있습니다. 접근 권한을 요청하면 인프라 팀 구성원이 이를 승인합니다.
새 프로젝트 요청하기
Rust 팀의 구성원으로서 팀이 관리하는 프로젝트에 Sentry를 사용하려면 다음 단계를 따라야 합니다.
-
프로젝트가 외부에 공개되는 경우(즉, 팀 외부 사람들이 접근하도록 되어 있는 경우) 리더십 위원회에 연락하여 개인정보 처리방침 개정에 대한 지원을 요청해야 합니다. 이때 기존 항목과 비슷하게 해당 서비스도 Sentry를 사용한다는 내용을 추가합니다.
-
(필요한 경우) 개인정보 처리방침 문제가 정리되면, 인프라 팀에 연락하여 Sentry 인터페이스에서 새 프로젝트를 만들고 필요하면 새 Sentry 팀도 만들어 달라고 요청할 수 있습니다.
-
마지막으로 Sentry SDK를 프로젝트에 통합하면 됩니다.
새 프로젝트 만들기
이 섹션에서는 요청이 있을 때 인프라 팀이 실제로 새 프로젝트를 만드는 방법을 설명합니다. “Owner” 권한이 있는 개인 Sentry 계정이 있거나, 관리자 자격 증명이 보관된 Sensitive 1Password 보관함에 접근할 수 있어야 합니다.
프로젝트를 만들려면 Sentry에 인증한 뒤 새 프로젝트 만들기 페이지에 방문하십시오. 팀이 사용하는 기술 스택, 적절한 이름, 담당 팀을 선택하십시오(“+” 아이콘을 클릭하면 새 팀을 만들 수 있습니다). 마지막으로, 새 팀을 만들었다면 관련 인원을 그 팀에 추가하십시오.
신뢰할 수 있는 게시
crates.io 신뢰할 수 있는 게시를 사용하면 수명이 긴 crates.io API 토큰을 GitHub Actions 시크릿에 저장하지 않고도 GitHub Actions 워크플로에서 크레이트를 게시할 수 있습니다.
신뢰할 수 있는 게시는 Rust 프로젝트 크레이트를 게시할 때 권장되는 방법입니다.
이 페이지에서는 rust-lang/team 저장소를 사용하여 Rust 프로젝트 저장소에 신뢰할 수 있는 게시를 구성하는 방법을 설명하고, 가장 흔히 쓰이는 게시 패턴을 소개합니다.
저장소가 Rust GitHub 조직 아래에 호스팅되어 있지 않다면 rust-lang/team 저장소를 통해 신뢰할 수 있는 게시를 구성할 수 없습니다. 이 경우 crates.io 신뢰할 수 있는 게시 문서의 안내를 따르십시오.
1. 크레이트 소유권 설정
신뢰할 수 있는 게시로 관리하려는 크레이트를 rust-lang-owner 계정이 소유하고 있는지 확인하십시오. 소유하고 있지 않다면 해당 계정을 소유자로 초대한 뒤, #t-infra Zulip 채널에서 초대를 수락해 달라고 요청하십시오.
2. 팀 저장소 구성
crates.io 크레이트 관리 문서에 설명된 대로 저장소 TOML 파일에 [[crates-io]] 섹션을 추가하십시오.
publish-environment가 참조하는 환경도 정의되어 있는지 확인하십시오. 예를 들면 다음과 같습니다.
[environments.publish]
branches = ["main"]
[[crates-io]]
publish-environment = "publish"
...
환경 규칙
환경 규칙은 게시용 GitHub Actions 워크플로를 트리거하는 이벤트와 일치해야 합니다.
예를 들어 게시가 다음 이벤트로 트리거되는 경우:
on:
push:
tags: ["v*"]
환경도 동일한 태그 푸시에서 워크플로가 실행되도록 허용해야 합니다.
[environments.publish]
tags = ["v*"]
3. GitHub Actions 워크플로 작성
신뢰할 수 있는 게시를 통해 crates.io에 게시하는 모든 워크플로에는 다음이 필요합니다.
environment: <publish-environment>가 지정된 작업(job);- GitHub가 OIDC 토큰을 발급(이슈)할 수 있도록 해당 job에 설정한
permissions: id-token: write; - 이름이
rust-lang/team의publish-workflow와 일치하는 워크플로 파일.
다음 방법 중 하나를 사용하여 GitHub Actions로 크레이트를 게시할 수 있습니다.
3a. cargo를 사용하여 게시
crates-io-auth-action을 사용하고 워크플로에서 cargo publish를 직접 실행하려면 이 방법을 사용하십시오.
워크플로를 설정하고 액션을 사용하는 방법에 대한 자세한 내용은 crates.io 신뢰할 수 있는 게시 문서의 “GitHub Actions Setup” 섹션을 참조하십시오.
Rust 프로젝트 크레이트에서는 작업의 environment 필드가 필수이며, 이 값은 rust-lang/team 저장소의 publish-environment 필드와 일치해야 합니다.
예를 들면 다음과 같습니다.
jobs:
publish:
environment: publish
...
이 GitHub 검색을 사용하면 Rust 프로젝트 저장소에서 이 방식의 예시를 찾을 수 있습니다.
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: ubuntu-latest
이 워크플로는 다음과 같은 다른 GitHub 이벤트로도 트리거할 수 있다는 점에 유의하십시오.
- 릴리스 시:
on: release: types: [created] - workflow dispatch를 사용한 수동 실행:
on: workflow_dispatch:
3b. 기본 브랜치에서 release-plz로 릴리스하기
더 많은 자동화를 원한다면 워크플로에서 cargo publish를 직접 실행하는 대신 릴리스 자동화 도구인 release-plz를 살펴보십시오.
이 GitHub 검색을 사용하면 Rust 프로젝트 저장소에서 release-plz의 사용 예시를 찾을 수 있습니다.
내부용 인프라 공지
중요한 내부용 인프라 변경 사항이나 업데이트가 있을 때, 인프라 팀은 인프라 공지 Zulip 채널인 #t-infra/announcements에 해당 변경 사항을 공지할 수 있습니다. 이 채널은 게시물이 적고 신호 대 잡음비가 높은 상태로 유지하고자 한다는 점을 유념해 주십시오.
여기서 내부용이란 주로 프로젝트 팀 구성원과 그 외 특정 프로젝트 내부 사용자(예: Dev Desktop 사용자)가 사용하거나 이들에게만 영향을 미치는 인프라를 의미합니다.
인프라 공지 게시 전 멤버십 동기화
현재 #t-infra/announcements는 공개 Zulip 스트림이므로 team 저장소를 통해 관리되지 않습니다. 따라서 인프라 공지를 게시하려는 인프라 팀 구성원은 먼저 다음 Zulip 그룹을 스트림에 수동으로 다시 동기화해야 합니다.
T-all(모든 프로젝트 팀 구성원)gsoc-contributors: Dev Desktop에 접근할 수 있기 때문입니다.ospp-contributors: 같은 이유입니다.cloud-compute-users: 같은 이유입니다.T-program: 같은 이유입니다.
이 작업은 Zulip에서 다음 경로로 수행할 수 있습니다.
Channel Settings > t-infra/announcements > Subscribers > Add subscribers
이 작업을 수행하려면 Zulip 관리자 권한이 필요하다는 점에 유의하십시오.
공지 시 핑 에티켓
모든 프로젝트 구성원에게 영향을 미치는 중대한 공지를 제외하고는 T-all Zulip 그룹 전체 핑을 피하십시오. T-all 구성원은 명시적으로 나가지 않는 한 이미 공지 채널을 구독하고 있습니다.
다음 중 하나를 택하십시오.
- 모든 프로젝트 구성원에게 명시적으로 핑을 보내지 않고 공지 토픽만 작성하거나,
- 특정 프로젝트 팀만 영향을 받는 경우에는 해당 프로젝트 팀에만 대상을 지정한 핑을 사용하십시오.
언어
이 섹션에서는 언어 팀의 메타 프로세스를 설명합니다.
외부 링크
- 언어 팀은 Zulip에 소통 채널을 두고 있습니다.
RFC 병합 절차
RFC가 수락되면(즉, 최종 의견 수렴 기간이 끝나고 중대한 이슈가 제기되지 않았다면) 해당 RFC를 병합해야 합니다. 현재 이 작업은 수동으로 진행하지만, 거의 누구나 할 수 있습니다(단, 하위 팀 구성원이 아니라면 RFC를 직접 병합하는 대신 PR을 열어야 합니다). 다음은 RFC를 병합하는 전체 절차입니다. 경우에 따라서는 일부 단계가 해당되지 않을 수도 있습니다.
1단계: 추적 이슈 열기
rust-lang/rust에 추적 이슈를 여십시오. 다음은 이슈 본문 템플릿입니다. XXX로 표시된 여러 곳을 적절한 내용(예: RFC 이름이나 가장 적합한 팀)으로 바꿔야 합니다.
This is a tracking issue for the RFC "XXX" (rust-lang/rfcs#NNN).
**Steps:**
- [ ] Implement the RFC (cc @rust-lang/XXX -- can anyone write up mentoring
instructions?)
- [ ] Adjust documentation ([see instructions on rustc-dev-guide][doc-guide])
- [ ] Stabilization PR ([see instructions on rustc-dev-guide][stabilization-guide])
[stabilization-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#stabilization-pr
[doc-guide]: https://rustc-dev-guide.rust-lang.org/stabilization_guide.html#documentation-prs
**Unresolved questions:**
XXX --- list all the "unresolved questions" found in the RFC to ensure they are
not forgotten
이슈에 다음 레이블을 추가하십시오.
B-rfc-approvedC-tracking-issue- 적절한
T-XXX레이블
(이 작업을 할 권한이 없다면 적절한 팀을 cc로 언급하여 대신 해 달라고 요청하는 메모를 남기십시오.)
2단계: RFC PR 자체를 병합하기
해결되지 않은 우려 사항 없이 FCP가 끝나면, 로컬 git 체크아웃에서 다음을 수행하십시오.
- RFC PR의 브랜치를 체크아웃하십시오
- 파일 이름을 0000-에서 해당 RFC 번호로 바꾸는 커밋을 추가하십시오
- 새 파일의 헤더에 RFC PR과 방금 만든 추적 이슈의 링크가 들어가도록 수정하십시오
- RFC PR에 푸시하십시오
rust-lang/rfcs에 PR을 병합할 권한이 있다면 GitHub UI에서 해당 PR의 병합 버튼을 누르십시오. PR을 병합할 권한이 없다면 r? internal-sites라고 댓글을 남겨 메인테이너가 대신 병합하도록 하십시오.
3단계: 댓글 남기기
PR에 모든 사람을 추적 이슈로 안내하는 마지막 댓글을 남기십시오. 다음과 같은 식으로 작성하되, 자유롭게 자신만의 개성을 더하셔도 됩니다(팀 이름은 알맞게 바꾸십시오).
**Huzzah!** The @rust-lang/lang team has decided **to accept** this RFC.
To track further discussion, subscribe to the tracking issue here:
rust-lang/rust#41517
이것으로 모든 작업이 끝났습니다!
언어 기능의 안정화 절차
안정화 절차에 관한 문서는 rustc-dev-guide의 “안정화 요청” 장을 참고하십시오.
라이브러리
이 절에서는 라이브러리 팀의 메타 프로세스를 설명합니다.
연락처
rust-lang/libs-team GitHub 저장소는 라이브러리 팀의 본거지입니다. 이 저장소에서 현재 프로젝트 그룹, 예정된 회의, 추적 이슈의 상태에 대한 자세한 내용을 확인할 수 있습니다.
요즘 라이브러리 팀은 주로 rust-lang Zulip의 #t-libs 스트림에서 활동합니다.
Zulip과 Rust 커뮤니티가 이를 사용하는 방식에 대한 자세한 내용도 확인할 수 있습니다.
표준 라이브러리 유지 관리하기
누군가 제게
r+를 주기 전에 알았더라면 좋았을 모든 것
이 문서는 Rust 표준 라이브러리를 개발하고 유지 관리하는 데 필요한 맥락의 일부를 담아내려는 노력입니다. 이 문서의 목표는 라이브러리 팀 구성원들이 표준 라이브러리 작업에 적용하는 절차와 경험을 공유하여 다른 구성원들도 도움을 받을 수 있도록 하는 것입니다. 아마 더 넓은 Rust 커뮤니티의 구성원들에게도 흥미로울 만한 소소한 정보가 많이 쌓일 것입니다.
이 문서는 모범 사례나 좋은 스타일을 논의하려 하지 않습니다. 이에 대해서는 API 가이드라인을 참고하십시오.
기여하기
오래되었거나, 충분히 명시되지 않았거나, 누락되었거나, 그냥 틀린 내용을 발견하시면 언제든지 rust-lang/rust-forge 저장소에 PR을 열어 주십시오!
용어
- 라이브러리 팀(Libs). 바로 저희입니다! 표준 라이브러리의 개발과 유지 관리(그 외 여러 업무 포함)를 담당하는 팀입니다.
- 풀 리퀘스트(PR).
rust-lang/rust에 대한 일반적인 GitHub 풀 리퀘스트입니다. - 의견 요청(RFC).
rust-lang/rfcs에 작성되며 새로운 기능을 도입하는 공식 문서입니다. - 추적 이슈.
C-tracking-issue태그가 붙은 GitHub의 일반 이슈입니다. - 최종 의견 수렴 기간(FCP). 관련 팀이 RFC와 PR을 리뷰할 기회를 갖도록
rfcbot이 조율합니다.
확신이 서지 않을 때…
표준 라이브러리를 유지 관리하는 일은 벅찬 책임처럼 느껴질 수 있습니다! triagebot을 통한 자동 리뷰어 배정으로 인해 여러분은 수많은 새로운 맥락에 놓이게 될 것입니다.
언제든지 GitHub에서 @rust-lang/libs 팀에 핑을 보내십시오. 저희 모두 도와드리기 위해 여기 있습니다!
자신이 PR을 리뷰하기에 가장 적합한 사람이 아니라고 생각되면 triagebot을 사용해 다른 사람에게 배정하십시오.
여러분의 의견을 기다리는 리뷰 찾기
https://rfcbot.rs/ 를 정기적으로 확인하는 것을 잊지 마십시오. 자신의 닉네임이 나타난 곳 중 아무 곳이나 클릭하면 https://rfcbot.rs/fcp/SimonSapin 과 같이 여러분의 의견을 기다리는 리뷰만 보여 주는 페이지로 이동합니다.
PR 리뷰하기
라이브러리 팀 구성원으로서 여러분은 리뷰가 필요한 PR에 배정되고, Rust 프로젝트의 이슈에 대해 의견을 요청받게 될 것입니다.
RFC는 언제 필요합니까?
새로운 불안정 기능은 병합되기 전에 RFC가 필요하지 않습니다. 기능이 작고 설계 공간이 단순하다면, 해당 기능을 안정화하려면 보통 FCP만 거치면 됩니다. 그러나 때로는 안정화하기 전에 RFC를 요청할 수도 있습니다.
unsafe가 있습니까?
표준 라이브러리의 unsafe 코드 블록에는 그것이 왜 괜찮은지 설명하는 주석이 필요합니다. 이를 검사하는 tidy 린트가 있습니다. 또한 unsafe 코드는 실제로도 괜찮아야 합니다.
무엇이 건전하고 무엇이 그렇지 않은지에 관한 규칙은 미묘할 수 있습니다. 현재의 견해는 Unsafe Code Guidelines WG를 참고하시고, 조금이라도 의심스럽다면 @rust-lang/libs, @rust-lang/lang 및/또는 해당 WG의 누군가에게 핑하는 것을 고려하십시오. 저희는 unsafe 코드의 건전성에 대해 토론하는 것을 매우 좋아하며, 더 많은 사람이 살펴볼수록 더 좋습니다!
그 #[inline]은 올바릅니까?
인라이닝은 잠재적인 실행 속도, 컴파일 시간, 코드 크기 사이의 트레이드오프입니다. hashbrown 크레이트에 대한 이 PR에서 이에 관한 논의를 볼 수 있습니다. 해당 스레드에서 인용합니다:
#[inline]은 단순한 인라인 힌트와는 매우 다릅니다. 앞서 말씀드렸듯이, C++에는#[inline]이 하는 일에 해당하는 것이 없습니다. 디버그 모드에서 rustc는 기본적으로#[inline]을 무시하며, 마치 그것을 작성하지 않은 것처럼 취급합니다. 릴리스 모드에서 컴파일러는 기본적으로#[inline]함수를 그것을 참조하는 모든 코드 생성 단위 각각에 코드 생성하며, 그런 다음inlinehint도 추가합니다. 즉, CGU가 16개 있고 모두 어떤 항목을 참조한다면, 각각의 CGU 모두에 해당 항목의 전체 구현이 인라인된다는 뜻입니다.
다음의 경우 #[inline]을 추가할 수 있습니다:
- 공개되어 있고, 작고, 제네릭이 아닌 함수.
다음의 경우에는 #[inline]이 필요하지 않습니다:
- 스코프 내에 제네릭이 있는 메서드.
- 기본 구현이 없는 트레이트의 메서드.
#[inline]은 언제든지 나중에 도입할 수 있으므로, 의심스럽다면 그냥 제거해도 됩니다.
#[inline(always)]는 어떻습니까?
#[inline(always)]가 필요한 경우는 거의 없습니다. 제한된 수의 위치에서 사용되는 비공개 헬퍼 메서드나 사소한 연산자에는 유용할 수 있습니다. 이 속성을 사용하려면 마이크로 벤치마크로 그 타당성을 입증해야 합니다.
호환성이 깨질 가능성이 있습니까?
호환성을 깨는 변경은 가능한 한 피해야 합니다. RFC 1105는 무엇이 호환성을 깨는 변경에 해당하는지에 대한 기초가 되는 재단을 마련합니다. 호환성 깨짐을 허용할 수 있는지는 실제 영향에 따라 판단하며, 그 영향은 crater 실행으로 대략 추정할 수 있습니다.
영향의 정도에 따라 호환성 깨짐을 완화하는 여러 전략이 있습니다.
가치도 크고 영향도 큰 변경의 경우:
- 컴파일러 린트를 사용해 깨지는 동작을 단계적으로 없애도록 시도합니다.
영향이 그리 크지 않은 경우:
- 깨지는 크레이트의 메인테이너를 논의에 참여시키고, 이를 고치는 PR을 제출합니다.
동작이 변경됩니까?
호환성을 깨는 변경은 컴파일 실패에만 국한되지 않습니다. stable 함수의 동작을 바꾸는 변경은 일반적으로 받아들여질 수 없습니다. 예시로 home_dir 이슈를 참고하십시오.
stable 트레이트에 대한 새 impl이 있습니까?
표준 라이브러리에 대한 많은 PR은 이미 stable인 트레이트에 새 impl을 추가하는데, 이는 온갖 기상천외한 방식으로 사용자 코드를 깨뜨릴 수 있습니다. 다음 절에서는 새 트레이트 impl로 인한 호환성 깨짐 가운데 표준 라이브러리에 가해진 변경만 보아서는 분명하지 않을 수 있는 몇 가지 예를 소개합니다.
두 번째 제네릭 impl이 도입되면 타입 추론이 깨집니다
Rust는 타입 추론 과정에서 제네릭 트레이트에 대한 impl이 하나뿐이라는 사실을 활용합니다. 두 번째 impl이 추가되어 해당 제네릭의 타입이 모호해지면 이 추론이 깨집니다. 다음과 같은 코드가 있다고 합시다.
#![allow(unused)]
fn main() {
// in `std`
impl From<&str> for Arc<str> { .. }
}
#![allow(unused)]
fn main() {
// in an external `lib`
let b = Arc::from("a");
}
여기에 다음을 추가하면
impl From<&str> for Arc<str> { .. }
+ impl From<&str> for Arc<String> { .. }
다음 코드는
#![allow(unused)]
fn main() {
let b = Arc::from("a");
}
더 이상 컴파일되지 않습니다. 이전에는 Box<T>의 T를 알아내는 데 타입 추론에 의존했기 때문입니다.
이런 종류의 호환성 깨짐은 괜찮을 수도 있지만, crater 실행으로 그 범위를 추정해야 합니다.
새 impl이 도입되면 deref 강제 변환이 깨집니다
Rust는 인자가 타입 검사를 직접 통과하지 못하면 deref 강제 변환을 사용해 유효한 트레이트 impl을 찾습니다. 이는 impl이 하나뿐일 때만 일어나는 것으로 보이므로, 새 impl을 도입하면 deref 강제 변환에 의존하는 사용자 코드가 깨질 수 있습니다. 다음과 같은 코드가 있다고 합시다.
#![allow(unused)]
fn main() {
// in `std`
impl Add<&str> for String { .. }
impl Deref for String { type Target = str; .. }
}
#![allow(unused)]
fn main() {
// in an external `lib`
let a = String::from("a");
let b = String::from("b");
let c = a + &b;
}
여기에 다음을 추가하면
impl Add<&str> for String { .. }
+ impl Add<char> for String { .. }
다음 코드는
#![allow(unused)]
fn main() {
let c = a + &b;
}
더 이상 컴파일되지 않습니다. &String을 &str로 강제 변환하기 위해 deref를 사용하려고 시도하지 않기 때문입니다.
이런 종류의 호환성 깨짐은 괜찮을 수도 있지만, crater 실행으로 그 범위를 추정해야 합니다.
기존 기능을 사용해 구현할 수 있습니까?
String 같은 타입은 Vec<u8>를 기반으로 구현되며, deref 강제 변환을 통해 str의 메서드를 사용할 수 있습니다. Vec<T>는 deref 강제 변환을 통해 [T]의 메서드를 사용할 수 있습니다. 가능하다면 String 같은 래핑 타입의 메서드는 내부 저장소나 deref 대상에 이미 존재하는 메서드에 위임해야 합니다.
#[fundamental] 항목이 관련되어 있습니까?
#[fundamental] 타입은 일관성(coherence) 규칙이 다르기 때문에 이러한 타입에는 포괄(blanket) 트레이트 impl을 추가할 수 없습니다. 자세한 내용은 RFC 1023을 참고하십시오. 여기에는 다음이 포함됩니다:
&T&mut TBox<T>Pin<T>
특수화(specialization)가 관련되어 있습니까?
특수화는 현재 불안정(unstable) 기능입니다. 진행 상황은 여기에서 확인할 수 있습니다.
저희는 특수화에 지나치게 의존하지 않으려 하며, 그 사용을 특정 구현의 최적화로 제한합니다. 이러한 특수화 최적화는 공개 메서드 자체를 특수화하는 대신, 비공개 트레이트를 사용하여 올바른 구현을 찾습니다. 외부 호출자에 대한 메서드 디스패치 방식을 바꾸는 특수화 사용은 모두 신중하게 검토해야 합니다.
표준 라이브러리에서 특수화를 사용하는 방법의 예로, &[T]로부터 Rc<[T]>를 생성하는 경우를 살펴봅시다:
#![allow(unused)]
fn main() {
impl<T: Clone> From<&[T]> for Rc<[T]> {
#[inline]
fn from(v: &[T]) -> Rc<[T]> {
unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) }
}
}
}
T: Copy인 경우에는 최적화된 구현이 있으면 좋을 것입니다:
#![allow(unused)]
fn main() {
impl<T: Copy> From<&[T]> for Rc<[T]> {
#[inline]
fn from(v: &[T]) -> Rc<[T]> {
unsafe { Self::copy_from_slice(v) }
}
}
}
안타깝게도 이 두 impl은 서로 겹치기 때문에 일반적으로는 둘 다 가질 수 없습니다. 바로 이런 경우에 비공개 특수화를 사용하여 내부적으로 올바른 구현을 선택할 수 있습니다. 이 경우에는 구현을 전환하는 RcFromSlice라는 트레이트를 사용합니다:
#![allow(unused)]
fn main() {
impl<T: Clone> From<&[T]> for Rc<[T]> {
#[inline]
fn from(v: &[T]) -> Rc<[T]> {
<Self as RcFromSlice<T>>::from_slice(v)
}
}
/// Specialization trait used for `From<&[T]>`.
trait RcFromSlice<T> {
fn from_slice(slice: &[T]) -> Self;
}
impl<T: Clone> RcFromSlice<T> for Rc<[T]> {
#[inline]
default fn from_slice(v: &[T]) -> Self {
unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) }
}
}
impl<T: Copy> RcFromSlice<T> for Rc<[T]> {
#[inline]
fn from_slice(v: &[T]) -> Self {
unsafe { Self::copy_from_slice(v) }
}
}
}
min_specialization 기능을 사용하는 특수화만 사용해야 합니다. 완전한 specialization 기능은 건전하지 않은(unsound) 것으로 알려져 있습니다.
공개 열거형이 있습니까?
새로운 변형(variant)이 추가될 가능성이 조금이라도 있다면, 호환성을 깨뜨리지 않고 추가할 수 있도록 공개 열거형에 #[non_exhaustive] 속성을 지정해야 합니다.
이 변경이 드롭 순서를 바꿉니까?
컬렉션 내부 구조를 변경하면 항목이 드롭되는 순서에 영향을 줄 수 있습니다. 이는 과거에도 허용된 적이 있지만, 반드시 명시해야 합니다.
수동으로 작성한 Drop 구현이 있습니까?
Drop을 수동으로 구현하는 제네릭 Type<T>는 T에 #[may_dangle] 속성이 적절한지 검토해야 합니다. #[may_dangle]이 무엇인지에 대한 자세한 내용은 Nomicon에서 확인할 수 있습니다.
제네릭 Type<T>에 T의 drop까지 수반할 수 있는 수동 drop 구현이 있다면, dropck가 이를 알아야 합니다. Type<T>의 T 소유권이 ManuallyDrop<T>, *mut T, MaybeUninit<T>처럼 스스로 T를 drop하지 않는 타입을 통해 표현된다면, Type<T>에는 T가 drop될 수 있음을 dropck에 알리기 위한 PhantomData<T> 필드도 필요합니다. 내부 포인터 타입인 Unique<T>를 사용하는 표준 라이브러리의 타입에는 PhantomData<T> 마커 필드가 필요하지 않습니다. 이 부분은 Unique<T>가 대신 처리해 줍니다.
이것이 잘못될 수 있는 실제 사례로, 대략 다음과 같은 형태의 OptionCell<T>를 생각해 보십시오:
#![allow(unused)]
fn main() {
struct OptionCell<T> {
is_init: bool,
value: MaybeUninit<T>,
}
impl<T> Drop for OptionCell<T> {
fn drop(&mut self) {
if self.is_init {
// Safety: `value` is guaranteed to be fully initialized when `is_init` is true.
// Safety: The cell is being dropped, so it can't be accessed again.
unsafe { self.value.assume_init_drop() };
}
}
}
}
PhantomData<T> 마커 필드가 없던 이 OptionCell<T>에 #[may_dangle] 속성을 추가하자, OptionCell<T>보다 엄밀하게 더 오래 살지 않는 T에 대해 건전성 구멍이 생겼고, 그 결과 이러한 T는 자신의 Drop 구현 안에서 drop된 이후에도 접근될 수 있었습니다. #[may_dangle]를 올바르게 적용하려면 PhantomData<T> 필드도 필요했습니다:
struct OptionCell<T> {
is_init: bool,
value: MaybeUninit<T>,
+ _marker: PhantomData<T>,
}
- impl<T> Drop for OptionCell<T> {
+ unsafe impl<#[may_dangle] T> Drop for OptionCell<T> {
mem은 어떻게 가정을 깨뜨릴 수 있습니까?
mem::replace와 mem::swap
&mut 참조 뒤에 있는 모든 Sized 값은 mem::replace나 mem::swap을 사용해 새 값으로 교체될 수 있으므로, 코드는 도달 가능한 가변 참조의 내부가 교체로 인해 바뀌지 않는다고 가정해서는 안 됩니다.
mem::forget
Rust는 값이 누수될 때(mem::forget으로 누수시킬 수 있습니다) 소멸자가 실행된다는 것을 보장하지 않으므로, 코드는 안전성을 유지하기 위해 소멸자에 의존하는 것을 피해야 합니다. 기억하십시오, 누구나 똥을 쌉니다.
값이 누수될 때 소멸자를 실행하지 않아도 괜찮습니다. 그 저장 공간이 해제되거나 다른 용도로 재사용되지 않기 때문입니다. 저장 공간이 초기화된 상태에서 해제되거나 다른 용도로 재사용된다면 소멸자를 먼저 실행해야 합니다. 메모리가 고정(pin)되어 있을 수 있기 때문입니다. 그렇기는 하지만, 고정이 전혀 관여하지 않음을 보장할 수 있다면 해제 시 소멸자 실행을 건너뛰는 예외가 여전히 있을 수 있습니다.
성능에는 어떤 영향이 있습니까?
핫 코드를 변경하면 좋든 나쁘든 사용하는 쪽의 성능에 영향을 줄 수 있습니다. 적절한 벤치마크를 통해 성능 특성이 어떻게 변하는지 파악할 수 있어야 합니다. rustc 자체에 영향을 주는 변경이라면 rust-timer 실행을 할 수도 있습니다.
커밋 로그가 깔끔합니까?
PR에는 병합 커밋이 포함되어서는 안 됩니다. PR이 기본 브랜치에 비해 오래된 상태가 되면 리베이스해야 합니다.
PR 병합
rust-lang/rust에 대한 PR은 GitHub UI를 사용하거나 원격 브랜치를 푸시하는 방식으로 수동 병합하지 않습니다. 모든 것은 bors를 거칩니다.
rollup을 사용해야 할 때
라이브러리 PR은 대개 롤업해도 괜찮으며, 특히 새로운 unstable 항목 추가만 있거나 문서만 수정하는 경우에는 더욱 그렇습니다.
언제 롤업해야 하는지에 대한 자세한 내용은 롤업 가이드라인을 참고하십시오. 롤업의 취지는 여러 PR을 개별적으로 병합하는 대신 한데 모아 한 번에 병합하는 것입니다. 이렇게 하면 병합 속도를 높일 수 있지만, 충돌할 가능성이 높은 PR이나 롤업 안에서 성능 특성이 가려질 수 있는 PR에는 적절하지 않을 수 있습니다.
새로운 공개 항목이 있을 때
새로운 기능이라면 해당 기능의 추적 이슈를 열어야 합니다. 추적 이슈에 무엇을 담아야 하는지 감을 잡으려면 이전 추적 이슈 몇 개를 살펴보십시오. #[unstable] 속성의 issue 필드를 추적 이슈 번호로 갱신해야 합니다.
불안정 기능은 준비가 된 것으로 보이면 평소처럼 bors를 통해 병합할 수 있습니다.
새로운 트레이트 impl이 있을 때
stable 트레이트에 대한 트레이트 impl을 불안정하게 만들 방법은 없으므로, 이미 안정화된 트레이트에 새 impl을 추가하는 모든 PR은 병합 전에 FCP를 거쳐야 합니다. 다만 트레이트 자체가 불안정하다면 impl도 불안정해야 합니다.
기능을 안정화할 때
기능은 #[unstable] 속성을 #[stable] 속성으로 교체하는 PR을 통해 안정화할 수 있습니다. 기능을 안정화하려면 먼저 승인된 RFC가 있어야 합니다. 또한 병합 전에 FCP를 거쳐야 합니다.
#[stable] 속성에 사용할 올바른 버전은 Forge에서 확인할 수 있습니다.
const 함수를 안정화할 때
const 함수는 #[rustc_const_unstable] 속성을 #[rustc_const_stable] 속성으로 교체하는 PR을 통해 안정화할 수 있습니다. 해당 const 특성을 앞으로 보장할 것인지에 대해 Constant Evaluation WG에 핑을 보내 의견을 구해야 합니다. const 안정화되는 대상이 새로 노출되는 인트린식이라면 @rust-lang/lang도 FCP에 포함해야 합니다.
함수가 #[allow_internal_unstable] 속성을 통해 내부적으로 다른 불안정 const 함수에 의존하는지 확인하고, 내부의 불안정 호출을 제거한다면 함수를 어떻게 구현할 수 있을지 검토하십시오. #[allow_internal_unstable]에 대한 자세한 내용은 Stability attributes 페이지를 참고하십시오.
unsafe와 const가 함께 관련된 경우, 예를 들어 “unconst“인 연산의 경우에는 그 사용에 대한 const 안전성 논거도 문서화해야 합니다. 즉, const fn에는 반드시 지켜야 하는 추가적인 결정성 제약(예: 런타임과 컴파일 타임의 결과가 일치해야 하며 함수의 출력은 입력에만 의존해야 함 등)이 있으며, unsafe를 사용할 때는 이러한 제약이 지켜진다는 근거를 제시해야 합니다.
기능을 지원 중단할 때
지원 중단된 항목으로 인해 문서가 어수선해지는 것을 줄이기 위해, 해당 항목은 모듈이나 impl 블록의 맨 아래로 옮겨 문서 페이지의 맨 아래에 표시되도록 해야 합니다. 그런 다음 문서 내용은 항목의 사용 방법보다는 해당 항목이 지원 중단된 이유에 초점을 맞추도록 줄여야 합니다.
릴리스
이 섹션에서는 컴파일러와 도구의 새 릴리스를 만드는 절차와 Rust 프로그래밍 언어의 플랫폼 지원에 관한 정보를 다룹니다.
외부 링크
- Bors 페이지는
rust-langGitHub 조직의 풀 리퀘스트 테스트 큐 링크를 제공하며, 봇과 상호작용할 때 사용할 수 있는 구문도 간략히 소개합니다. - Rustup Component History는 nightly에서 특정 플랫폼용 구성 요소를 마지막으로 사용할 수 있었던 시점(사용할 수 있었던 경우)을 기록합니다.
- PR Tracking은
rust-lang/rust저장소에 올라온 풀 리퀘스트를 시각화하여 보여 줍니다. - kennytm의
rustup-toolchain-install-master는 CI에서 가장 최근에 생성된 아티팩트를rustup에 설치하는 유틸리티입니다.
백포트
백포트란 무엇입니까?
백포트란 더 새로운 Rust 릴리스(또는 다른 소프트웨어)에 반영된 수정 사항이나 기능을 가져와 아직 지원 중인 이전 브랜치에 다시 적용하는 작업입니다. 이 작업은 모든 업스트림 변경 사항을 더 이상 받지 않는 채널(Stable이나 Beta 등)에 중요한 버그 수정이나 보안 패치를 배포할 때 가장 흔히 사용됩니다.
메인 브랜치에 병합된 후 beta 브랜치와 stable 브랜치로 포팅해야 하는 패치(대부분 충분히 심각한 버그의 수정)가 꾸준히 조금씩 발생합니다. 이 절차를 알고 있는 사람조차 소수에 불과하지만, 사실 이는 누구나 할 수 있는 일입니다.
rust-lang/rust에서의 beta 백포트
beta 브랜치로의 PR 백포트는 보통 회귀를 수정하기 위해서만 이루어집니다. PR을 beta 브랜치로 백포트하려면 다음 절차를 거칩니다.
-
백포트할 PR에
beta-nominated레이블을 추가하십시오. 이 레이블은 해당 PR이 백포트 여부를 결정하기 위해 담당 팀의 검토가 필요한 상태임을 표시합니다. 트리아지 권한이 있는 사람이라면 누구나 자유롭게 이 레이블을 추가할 수 있습니다. -
팀이 백포트해야 한다고 판단하면
beta-accepted레이블을 추가해야 합니다. 그렇지 않으면 nominated 레이블을 제거해야 합니다. -
때때로 누군가가 beta 롤업 PR을 만듭니다. 이 작업은 주로 릴리스 팀이 수행하지만, 누구나 할 수 있습니다. 절차는 다음과 같습니다.
-
beta브랜치에서 로컬 브랜치를 생성하십시오. -
beta-nominated와beta-accepted레이블이 모두 붙은 PR을 전부 체리픽하십시오. 막판에 변경 사항이 생기거나 전체 CI 테스트 실행 중 실패할 수 있으므로, 아직 병합되지 않은 PR은 포함하지 않는 것이 일반적으로 바람직합니다. -
./x.py run replace-version-placeholder를 실행하고, 변경 사항이 있으면 새 커밋에 넣으십시오. -
(권장) 로컬에서 몇 가지 테스트를 실행하십시오. 백포트가 깔끔하게 적용되지 않거나, 출력에 차이가 있어 UI 테스트를 다시 bless해야 하는 경우가 드물지 않습니다.
-
제목이
[beta]로 시작하는 PR을 beta 브랜치를 대상으로 여십시오(리뷰어가 특별한 PR임을 알아볼 수 있도록). -
PR 설명에 백포트되는 모든 PR을 나열하십시오. 예시는 여기에 있습니다.
-
백포트되는 모든 PR을 하나씩 살펴보며 다음을 수행하십시오.
- 마일스톤을 해당 beta 릴리스에 맞는 값으로 변경하십시오.
beta-nominated레이블을 제거하십시오. 이는 백포트가 완료되었음을 나타냅니다.
PR이 많다면 nominated + accepted 쿼리를 열어 백포트할 PR을 모두 선택한 뒤 “Milestones” 및 “Label” 드롭다운으로 여러 PR의 마일스톤과 레이블을 한꺼번에 변경하면 빠르게 처리할 수 있습니다.
이 마지막 단계는 beta PR이 병합되기 전이나 후에 수행할 수 있지만, 병합될 때까지 기다리면 잊어버리기 쉽습니다.
-
-
리뷰어(보통 릴리스 팀 소속)는 백포트가 올바른지, 그리고 beta 브랜치로 제출되었는지 확인해야 합니다. 그런 다음 리뷰어는
@bors r+ rollup=never로 승인합니다(실수로 롤업되는 것을 막기 위함입니다). PR 작성자가 r+ 권한을 가지고 있고 백포트 과정에서 큰 변경을 하지 않았다면, 작성자가 직접 PR을 승인할 수도 있습니다.
요약하면, PR이 거칠 수 있는 상태는 다음 세 가지입니다.
beta-nominated: 팀의 검토가 필요합니다.beta-nominated+beta-accepted: 백포트를 기다리는 중입니다.beta-accepted: 백포트가 완료되었습니다.
rust-lang/rust에서의 stable 백포트
stable 브랜치로의 백포트는 beta 백포트와 완전히 동일하게 진행되며, 레이블 이름만 약간 다릅니다. stable-nominated는 백포트 여부를 논의할 PR을 나타내고, stable-accepted는 백포트가 수락된 PR을 나타냅니다. stable 상정이 거절되면 stable-nominated 레이블이 제거됩니다.
T-release는 stable 백포트가 포인트(.패치) 릴리스(예: 1.50과 1.51 사이에 1.50.1 릴리스)를 낼 만한지 사안별로 결정합니다.
rust-lang/cargo에서의 beta 백포트
Cargo에 수정 사항을 백포트하는 절차는 rust-lang/rust 저장소의 절차와 비슷하지만 조금 더 깁니다. 현재 Cargo 저장소에는 백포트 태그가 없지만, 관련 PR에 백포트를 요청하는 댓글을 남겨 백포트 절차를 시작할 수 있습니다. Cargo 팀 구성원이 백포트 진행을 승인하면 PR을 보내기 시작하셔도 됩니다!
-
먼저 Cargo의
rust-1.21.0브랜치로 PR을 보냅니다(1.21은 현재 rustc beta 버전 번호로 바꾸십시오).rust-lang/rust와 마찬가지로 PR 제목 앞에[beta]를 붙이고, beta로 향하는 PR로 표시되었는지 확인하십시오. -
다음으로 Cargo 리뷰어가 PR에
@bors: r+를 달아 큐에 넣습니다. 이후 (테스트가 통과하면) bors가 해당 PR을 적절한 Cargo 브랜치에 자동으로 병합합니다. -
마지막으로 Cargo 서브모듈을 업데이트하는 PR을
rust-lang/rust저장소의beta브랜치로 보냅니다. Cargo 서브모듈은rust-1.21.0브랜치(Cargo PR이 병합된 브랜치)의 최신 커밋으로 업데이트해야 합니다. 앞에서와 마찬가지로 PR 제목에[beta]가 포함되어 있는지 확인하십시오.
이 모든 과정을 마치면 Cargo 변경 사항을 beta 릴리스 일정에 포함할 준비가 끝납니다!
릴리스 노트 준비
이 문서는 Rust 릴리스를 알리는 릴리스 노트(저수준 변경 로그)와 블로그 게시물(고수준 변경 로그)을 준비하는 절차를 다룹니다.
자동화/커뮤니티: 1단계: PR 또는 이슈에 릴리스 노트 검토용 레이블이 붙습니다
이 단계는 rust-lang/rust의 relnotes, relnotes-perf, finished-final-comment-period 레이블에 대해 진행되며 triagebot이 관리합니다(구현).
이 단계는 이슈/PR이 병합되거나 닫힐 때가 아니라 레이블이 붙을 때 진행된다는 점에 유의하십시오. (FIXME: 특히 추적 이슈의 경우, 필요할 때 작성자가 relnotes 이슈를 볼 가능성을 높이려면 이 단계를 닫히는 시점으로 옮겨야 하지 않겠습니까?)
자동화/커뮤니티: 2단계: 블로그 게시물 또는 릴리스 노트 문구를 제안합니다
레이블이 지정되면 “Tracking issue for release notes of #xxxx: $original pr title”(#xxxx의 릴리스 노트에 대한 추적 이슈: $original pr title)라는 제목으로 새 이슈가 자동으로 생성됩니다. 이 이슈에는 relnotes + relnotes-tracking-issue 레이블이 붙습니다.
해당 영역을 담당하는 Rust 팀 및/또는 PR을 작성한 기여자는 이 이슈에서 릴리스 노트 문구를 제안할 수 있습니다(또한 제안해야 합니다). 이 이슈에는 릴리스 노트 내용(RELNOTES.md에 들어갈 내용)과 블로그 내용을 위한 코드 블록이 들어 있습니다.
문구는 코드 블록에서 직접 수정하는 것이 가장 좋지만, 권한이 없다면 스레드에서 논의해도 되며 그 내용은 나중에 릴리스 팀이 반영합니다.
릴리스 노트 문구는 이후 단계에서 자동으로 가져가므로, 가능하면 이 목록에 있는 제목을 사용해야 합니다.
- 호환성 참고 사항
- 라이브러리
- 안정화된 API
- const로 안정화된 API
- Rustdoc
- 언어
- 컴파일러
- 플랫폼 지원
- 내부 변경 사항
- 기타
안정화된 API와 const로 안정화된 API는 모두 대략 다음과 같은 형식으로 작성해야 합니다.
- [`std::ptr::null_mut`](https://doc.rust-lang.org/std/ptr/fn.null_mut.html)
<!-- for trait implementations: -->
- [`impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T>`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#impl-From%3C%26%5BT;+N%5D%3E-for-Vec%3CT,+Global%3E)
다음 사항에 유의하십시오.
- 이것은 PR 링크가 아니라 표준 라이브러리 문서로 바로 연결되는 링크입니다.
- 링크는 stable 문서를 가리키므로 작성 시점에는 실제로 동작하지 않을 수 있습니다.
- API를 직접 적습니다. 문구가 너무 길어지지 않도록 API를 축약하기도 합니다(예: 부호 없는 정수를
uN으로 표기). - 링크 프래그먼트는 길고 예측하기 어려울 수 있으므로 직접 작성하기보다 URL을 복사해 붙여 넣는 편이 나을 때가 많습니다(항목이 stable 문서에 없다면 nightly 문서에서 복사한 뒤 URL을 수정하면 됩니다).
- 트레이트 구현만 안정화되고 해당 트레이트는 안정화되지 않는 경우, 모든 impl을 나열하고 각 impl 블록으로 가는 링크를 붙입니다.
릴리스 팀: 3단계: 릴리스 노트가 필요한 모든 이슈/PR에 relnotes 레이블이 붙어 있는지 확인합니다
이 단계는 beta 기간의 처음 3주 이내에 진행해야 합니다(빠를수록 좋습니다). 이 작업은 더 넓은 Rust 프로젝트의 도움을 받아 진행할 수도 있습니다.
GitHub의 rust-lang/rust 풀 리퀘스트에서 is:pr milestone:1.85.0 is:merged -label:relnotes -label:relnotes-perf -label:finished-final-comment-period를 검색하고, 마일스톤은 상황에 맞게 바꾸십시오.
이렇게 하면 아직 상정되지 않은 병합된 PR을 모두 찾을 수 있습니다. 보통 수백 개가 나오며, 목표는 상정되었어야 했던 것으로 눈에 띄는 항목을 찾아 relnotes 레이블을 붙여 상정하는 것입니다. 항목을 하나하나 클릭해 들어가지 않고 목록을 스크롤하면서, GitHub 체크박스 UI로 이슈에 레이블을 일괄로 붙이는 것이 좋은 방법입니다.
여기서의 목표는 100% 빠짐없이 찾는 것이 아니라 주로 명백한 것을 잡아내는 것입니다. 일부를 놓치더라도 대체로 괜찮습니다. 일관된 패턴이 보인다면 triagebot의 자동 이슈 등록에 포함할 수 있도록 기록해 두십시오.
FIXME: 이 단계에는 relnotes 레이블이 붙은 채 닫힌 이슈에 마일스톤을 지정하려는 시도도 포함해야 할 수 있습니다. 현재 이러한 이슈에는 마일스톤이 지정되지 않으므로, 이에 대응하는 relnotes 추적 이슈는 아마 한 번도 포함되지 않을 것입니다(그래서 무기한 누락될 수 있습니다).
FIXME: 호환성 참고 사항(Compatibility Notes)에 대한 절차는 어떻게 합니까?
릴리스 팀: 4단계: 릴리스 노트 생성 시작
이 단계는 beta 기간의 처음 3주 이내에 진행해야 합니다(빠를수록 좋습니다). 현재 릴리스를 맡은 릴리스 팀 담당자가 relnotes 도구를 실행합니다.
cargo build
GITHUB_TOKEN=$(gh auth token) cargo run --bin relnotes -- 1.85.0 > relnotes.md
이 도구는 다음과 같은 콘솔 출력(stderr)을 생성합니다.
Did not use "Libraries" from Tracking issue for release notes of #132515: Fix and undeprecate home_dir() <https://github.com/rust-lang/rust/issues/132650>
Did not use "Category (e.g. Language, Compiler, Libraries, Compatibility notes, ...)" from Tracking issue for release notes of #132187: Add Extend impls for tuples of arity 1 through 12 <https://github.com/rust-lang/rust/issues/133975>
이러한 줄은 대개 누군가가 내용을 갱신하지 않았거나 위 목록에 없는 키워드를 사용했다는 뜻입니다. 예를 들면 다음과 같습니다.
- Libraries는 Library여야 합니다
- Category …를 갱신해야 합니다.
도구를 실행한 사람은 해당 이슈로 가서 올바른 내용으로 갱신해야 합니다(예: 이름 변경, 분류 지정). 기준이 되는 원본은 도구의 로컬 출력이 아니라 이슈입니다. 장기적으로는 도구 실행을 자동화하려 하므로 이 점이 중요합니다. 따라서 결과물인 relnotes.md를 로컬에서 수정하는 양을 최소화하도록 하십시오.
이 도구의 표준 출력은 위의 relnotes.md로 보내집니다. 현재 이 파일에는 각 섹션별로 서식이 적용된 헤더가 들어 있으며, 각 섹션에는 “Tracking issue for …” 추적 이슈에서 가져온 내용이 들어가거나, 또는 도구가 “tracking issue for …” 추적 이슈를 찾지 못한 경우에는 표준 내용(PR/이슈 제목 + 링크)이 들어갑니다.
이슈가 누락되었다면 3단계(이슈에 태그를 지정하고 도구를 다시 실행)를 참고하십시오. 이슈가 있어서는 안 되는데 포함되어 있다면, 해당 이슈에 relnotes 태그를 지정하고(또는 기존 relnotes 추적 이슈를 찾고) 그 추적 이슈를 닫는 것이 가장 좋습니다. 이렇게 하면 해당 항목이 도구의 출력에서도 빠집니다.
릴리스 팀: 5단계: relnotes PR 게시
1.84의 예시를 참고하십시오: https://github.com/rust-lang/rust/pull/134568
로컬에 있는 relnotes.md를 가져와(현재는 보통 라이브러리 안정화 항목이 빠진 상태이며, 이 항목은 나중에 추가합니다. 이 항목이 빠진 사본을 가능한 한 일찍 받아 두려는 것입니다) rust-lang/rust의 RELEASES.md 맨 위에 삽입하고, 그 내용으로 새 풀 리퀘스트를 여십시오. 이번 주기에 실제로 릴리스를 게시하는 담당자를 r?로 지정하고 릴리스 팀을 cc하면 됩니다.
PR 설명에 이 문서(https://forge.rust-lang.org/release/release-notes.html)의 링크를 포함하고 6단계를 가리키도록 하십시오(즉, 수정 사항은 PR이 아닌 곳에서 제안하도록 권장합니다).
다음 릴리스 팀 회의에서는 블로그 게시물 주제를 선정하기 위해 이 풀 리퀘스트도 논의해야 합니다(블로그 게시물 절차는 아래를 참고하십시오).
relnotes PR과 릴리스 블로그 게시물을 위해 relnotes-interest-group에 핑 보내기
기여자는 (예를 들어 소속 팀을 대표하여) relnotes PR과 릴리스 블로그 게시물 리뷰를 돕는 데 관심이 있을 수 있습니다. relnotes-interest-group 마커 팀에 자신을 추가하면 핑을 받도록 옵트인할 수 있습니다.
relnotes PR과 릴리스 블로그 게시물을 작성할 때는 다음을 통해 이 알림 그룹에 핑을 보내 주십시오.
@rustbot ping relnotes-interest-group
전체: 6단계: relnotes PR의 수정 사항 반영
보통 PR에는 오타 수정, 대체 문구 제안 등이 담긴 댓글이 많이(수십 개) 달립니다. 좋은 전략은 이슈/PR의 원래 이슈를 업데이트하는 것(또는 이슈를 새로 등록하고 업데이트하는 것)으로, 이는 기본적으로 4단계에서 이미 로컬로 수행한 반복 작업과 맞추는 것입니다. 상태가 이슈에 오래 남아 있을수록 그 이슈의 업데이트를 알아차리고 PR에 반영하기가 더 쉬워집니다(도구를 다시 실행하기만 하면 됩니다).
수정 사항을 이슈에 반영하면 적절한 사람(예: PR 작성자/리뷰어)이 논의되고 있는 내용을 알 수 있도록 하는 데 도움이 됩니다.
릴리스 팀: 7단계: 추적 이슈 닫기
적절한 시점에 릴리스 팀 담당자는 해당 PR이 최종본임을 선언하고 현재 마일스톤과 연관된 모든 relnotes 추적 이슈를 닫아야 합니다(검색 예시). 이 작업은 GitHub UI에서 하는 것이 가장 쉽습니다.
FIXME: 이상적으로는 이 이슈들이 모두 relnotes PR에서 링크되어 서로 오가기가 더 쉬워야 합니다.
블로그 게시물 절차
릴리스 노트 PR을 게시한 후, 릴리스 팀의 다음 회의에서 어떤 블로그 게시물 주제를 원하는지 논의합니다. 이 논의는 보통 꽤 비공식적이며 반드시 최종적인 것은 아닙니다. 목표는 더 많은 관점을 제공하여 블로그 게시물 작성자의 선택에 의견을 보태는 것입니다.
그런 다음 블로그 게시물 작성자는 가능한 한 빨리(보통 릴리스 3일에서 7일 전이지만, 더 짧은 기간에 처리한 적도 있습니다) 블로그 게시물 PR을 올리는 것을 목표로 하며, 이 PR은 리뷰와 수정을 거쳐 최종적으로 릴리스 당일에 병합됩니다.
Rust 릴리스 절차
현재 Rust는 다음과 같이 릴리스됩니다:
start-release.py 스크립트에 관한 참고 사항
릴리스 절차 중 프로덕션 환경과 상호 작용해야 하는 단계는 start-release.py 스크립트를 통해 실행합니다. 이 스크립트를 사용하려면 프로그램을 설치하고 로컬 환경을 구성해야 하며, 스크립트가 설정 과정을 안내합니다.
스크립트를 처음 실행할 때(또는 사전 요구 사항이 변경되었을 때)에는 모든 설정이 올바르게 완료될 때까지 스크립트를 여러 번 실행해야 합니다.
start-release.py는 항상 백그라운드에서 CI 작업을 시작합니다. 작업이 언제 끝나는지 알려면 로그를 지켜보아야 합니다. 빌드가 끝나면 로그에 다음과 같은 줄이 나타납니다:
Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
stable 버전 번호 올리기(전주 금요일)
src/version의 버전 번호를 올리는 PR을 여십시오. 이 PR에 r+ rollup=never를 지정하십시오(직접 승인하십시오).
이 PR은 rollup=never로 지정하십시오. 롤업에서 첫 번째 PR이 아닌 상태로 병합되면 해당 롤업의 다른 풀 리퀘스트가 이전 릴리스와 잘못 연결되기 때문입니다.
사실상 이 시점에 beta 브랜치가 분기됩니다. beta가 승격될 때 이 버전 번호 상향 PR 바로 직전에 병합된 PR을 기반으로 하게 됩니다.
브랜치 승격(월요일)
두 승격 모두 월요일에 진행해야 합니다. 두 PR을 동시에 열어도 되지만, stable 승격을 먼저 병합하는 것을 우선하십시오(사전 릴리스 테스트 기간을 최대한 확보하기 위함입니다).
beta 및 stable 브랜치의 기준 업데이트
rust-lang/release-team(릴리스 팀) 저장소1에서 다음 명령을 실행하십시오:
./scripts/start-release.py update-rust-branches
start-release.py는 백그라운드에서 작업을 시작하며, 스크립트는 브랜치가 업데이트되기 전에 종료된다는 점을 기억하십시오. 다음 단계로 진행하기 전에 로그를 지켜보며 백그라운드 작업이 끝나는 시점을 확인하십시오.
stable PR
새 stable 브랜치를 대상으로 다음 변경 사항을 담은 PR을 rust-lang/rust에 보내십시오:
-
릴리스 노트를 사용 가능한 최신본으로 업데이트하십시오:
-
릴리스 노트 PR이 병합된 경우:
git checkout origin/HEAD -- RELEASES.md -
그렇지 않은 경우, 대기 중인 릴리스 노트 PR에서
RELEASES.md를 직접 복사하십시오
-
-
src/ci/channel을stable로 업데이트하십시오
또한 브랜치 업데이트 전에 병합되지 않은 beta 백포트가 있는지 확인하고, 있다면 stable PR에 체리픽하십시오:
- beta 브랜치를 대상으로 하는 PR 목록.
- beta 백포트가 승인된 PR 목록.
- beta 백포트에 상정된 PR 목록. 이 목록의 PR은 승인되지 않은 상태라는 점에 유의하십시오. 관련 팀과 후속 논의를 거쳐 처리 방법을 결정해야 합니다.
r+ rollup=never p=1000으로 PR을 직접 승인하십시오.
사전 릴리스 테스트 시간을 최대한 확보하려면 이 PR을 가능한 한 빨리 병합해야 한다는 점에 유의하십시오. bors가 다른 PR을 테스트하고 있고 CI가 곧 끝날 것 같지 않다면(이 부분은 직접 판단하십시오), 해당 PR에 들어가 다음 댓글을 작성하여 stable 릴리스 PR에 우선순위를 “양보“할 수 있습니다.
@bors yield stable 릴리스에 우선순위를 양보합니다.
beta PR
다음 변경 사항을 담아 새 beta 브랜치를 대상으로 하는 PR을 rust-lang/rust에 보내십시오.
-
다음 명령을 실행하고 그 출력만 담은 별도의 커밋을 만드십시오.
./x.py run replace-version-placeholder -
src/ci/channel을beta로 업데이트하십시오.
r+ rollup=never p=10으로 PR을 직접 승인하십시오.
dev-static 환경에 사전 릴리스 게시
stable PR이 병합되면 사전 릴리스를 시작해야 합니다. rust-lang/release-team(릴리스 팀) 저장소1에서 다음 명령을 실행하십시오:
./scripts/start-release.py publish-rust-dev-stable YYYY-MM-DD
YYYY-MM-DD를 릴리스 날짜(목요일)로 바꿔야 합니다.
기본 브랜치 부트스트랩 업데이트 및 Crater(화요일)
이 단계는 새 beta가 릴리스된 후에만 수행할 수 있습니다. beta의 릴리스 절차는 매일 00:00 UTC에 자동으로 진행되므로, beta PR이 그 이후에 반영되었다면 하루를 더 기다려야 합니다. rustup으로 beta를 설치해 보면 beta가 릴리스되었는지 확인할 수 있습니다.
다음 작업을 위해 기본 브랜치에 PR을 보내십시오.
-
이제 병합된 beta 브랜치 PR에서
replace-version-placeholder를 실행한 커밋을 체리픽하십시오. 기본 브랜치에는 분기된 beta에 포함되지 않은 다른 안정화가 있을 수 있고 이는 현재 릴리스에 속하지 않을 수 있으므로, 이 도구를 다시 실행하지 마십시오. -
다음을 실행하여 부트스트랩 컴파일러를 어제 만든 beta로 업데이트하십시오.
./x.py run src/tools/bump-stage0 -
bootstrap및not(bootstrap)조건부 컴파일 속성에 대한 참조를 제거하십시오. ripgrep을 설치하고 다음 명령을 실행하면 이들을 모두 찾을 수 있습니다.rg '#!?\[.*\(bootstrap' -t rust -t toml일반적인 지침(
#[]와#![]모두 해당)은 다음과 같습니다.#[cfg(bootstrap)]이 붙은 항목은 모두 제거하십시오.#[cfg(not(bootstrap))]속성은 항목을 유지한 채 모두 제거하십시오.#[cfg_attr(bootstrap, $attr)]속성은 항목을 유지한 채 모두 제거하십시오.#[cfg_attr(not(bootstrap), doc="$doc")]는 모두 관련 문서 블록(또는 새 문서 블록)의$doc으로 바꾸십시오.#[cfg_attr(not(bootstrap), $attr)]는 모두#[$attr]로 바꾸십시오.
beta PR과 기본 브랜치 부트스트랩 업데이트 사이에
cfg(bootstrap)을 추가하는 PR이 병합되면, 제거할 필요가 없는데도rg실행 결과에 해당 항목이 나타난다는 점에 유의하십시오. 이를 처리하는 가장 쉬운 방법은 일단 모두 변경한 뒤 CI가 실패를 알려 주도록 하는 것입니다. -
코드베이스에 영향을 주는 새로운 경고나 Clippy 린트가 없는지 확인하십시오.
./x clippy ci
새 beta가 나온 지금이 다음 릴리스 주기를 위한 Crater 실행을 시작하기에도 좋은 시점입니다. 이렇게 하면 다음 주기에 맞춰 실행을 시작했을 때 걸렸을 시간에 따라, 결과를 트리아지받기까지의 지연을 며칠에서 최대 2주 정도까지 줄일 수 있습니다.
이 단계는 Crater 실행을 시작하는 것일 뿐이며, Crater 실행을 실제로 트리아지하고 처리하는 일은 릴리스를 진행하는 사람의 책임이 아니라는 점에 유의하십시오. 시간이 부족하다면 이 단계는 건너뛰어도 되며, 다음 주기의 Crater 실행을 담당하는 사람이 실행을 시작할 것입니다.
- Crater 실행을 시작하는 방법은 릴리스 Crater 실행에 관한 장을 참고하십시오
릴리스 당일(목요일)
릴리스를 진행할 시각을 정하십시오. 릴리스 시각은 전적으로 여러분이 결정하므로, 가장 편한 시각을 고르십시오. 유일한 제약은 릴리스 절차가 릴리스 당일(UTC 기준) 안에 시작되고 끝나야 한다는 것입니다.
소셜 미디어 코디네이터(현재 Mara)에게 시각을 알려, 프로젝트의 소셜 미디어 채널에 릴리스 소식을 게시할 준비를 할 수 있도록 하십시오.
2024년 9월 기준으로 릴리스를 완료하는 데 75분에서 90분이 걸리므로, 계획한 시각에 맞출 수 있도록 릴리스 절차를 충분히 일찍 시작하십시오.
릴리스를 시작하려면 rust-lang/release-team(릴리스 팀) 저장소1에서 다음 명령을 실행하십시오:
./scripts/start-release.py publish-rust-prod-stable
이 명령은 프로덕션 환경을 대상으로 promote-release를 호출하는 백그라운드 작업을 시작하며, 그 로그를 확인하는 방법을 안내합니다.
릴리스 절차가 완료되면 블로그 게시물 PR을 병합하고, Mara에게 소셜 미디어에 릴리스를 알리도록 전달하십시오. 마지막으로, 성공의 기쁨을 만끽하십시오 🎉
beta stage0 업데이트(금요일)
게시한 stable 릴리스로 stage0을 업데이트하는 PR을 beta 브랜치에 보내십시오:
./x run src/tools/bump-stage0
부록: stable 사전 릴리스 다시 빌드하기
문제가 생겨 stable 아티팩트를 다시 빌드해야 한다면, rust-lang/rust 저장소의 stable 브랜치에 PR을 병합하십시오. 커밋이 병합되면 AWS로 인증한 후 rust-lang/release-team(릴리스 팀) 저장소에서 다음 명령을 실행하십시오:
./scripts/start-release.py publish-rust-dev-stable-rebuild
또한 이전에 블로그와 internals에 게시한 사전 릴리스 공지도 새 정보로 업데이트하는 것이 좋습니다.
롤업 절차
배경
Rust 프로젝트에는 모든 풀 리퀘스트가 기본 브랜치에 푸시되기 전에 병합 후 테스트를 거쳐야 한다는 정책이 있습니다. PR 수가 늘어날수록 이 방식은 확장성이 떨어질 수 있습니다. 현재 Rust의 CI 소요 시간이 약 3.5시간으로 길다는 점을 고려하면 더욱 그렇습니다.
그래서 롤업이 등장합니다! 규모가 작거나, 성능에 민감하지 않거나, 플랫폼에 종속되지 않는 변경 사항은 bors의 rollup 명령으로 표시합니다(@bors r+ rollup은 PR을 승인하면서 롤업으로 표시하고, @bors rollup은 이미 승인된 PR을 표시하며, @bors rollup-는 롤업 표시를 해제합니다). ’롤업 수행’이란 이러한 변경 사항을 하나의 PR로 모아 한꺼번에 병합하는 것을 의미합니다. 롤업 명령은 always, maybe, iffy, never의 네 가지 값을 받습니다. 이러한 각 상태가 무엇을 의미하는지에 대한 안내는 리뷰 정책의 롤업 섹션을 참고하십시오.
롤업 PR 목록은 Rust의 Bors 큐에서 확인할 수 있습니다. 롤업 PR은 ‘approved’ 큐의 맨 아래에 ‘rollup’ 우선순위로 나열되며, 이는 큐에서 앞에 있는 모든 항목이 병합되기 전까지는 단독으로 병합되지 않는다는 의미입니다.
롤업 만들기
-
Bors 큐의 인터페이스에서 풀 리퀘스트를 선택한 다음 “Create rollup” 버튼을 사용해 롤업 풀 리퀘스트를 만드십시오. (공정성에 관한 문구는 무시해도 됩니다.) 중요 사항: 롤업 섹션의 리뷰 정책에 따라
rollup=always,rollup=maybe,rollup=iffy로 표시된 PR을 추가 대상으로 고려하십시오. 무엇을 포함할지 결정할 때, 특히rollup=maybe및rollup=iffyPR에 대해서는 각별히 주의하십시오. 위험을 감수하다가 회귀(버그 또는 성능 저하)를 겪는 일은 최대한 피해야 합니다. 또한 기여자들이 rollup=never 태그를 달아야 하는데도 잊어버리는 경우가 많다는 점을 고려하여, 롤업 태그가 명시적으로 달리지 않은 PR은 각별히 주의하십시오. -
풀 리퀘스트 스레드에서 다음 명령을 실행하십시오.
@bors r+ p=5 -
롤업이 실패하면 rust-log-analyzer가 제공하는 로그를 사용해 실패 원인을 특정 PR로 좁힌 다음
@bors r-로 해당 PR의 승인을 취소하십시오. 그런 다음 롤업 PR을 닫고, 문제가 된 PR을 제외한 채 1.부터 다시 시작하여 롤업을 새로 만드십시오. 롤업 PR 본문에는 기존 PR(r-된 PR 제외)로 롤업 UI를 자동으로 채워 주는 링크가 있습니다. 자세한 내용은 실패한 롤업을 참고하십시오. -
롤업이 성공하면 다음 롤업으로 넘어갈 수 있습니다(가끔씩
rollup=neverPR도 진행되도록 하십시오).
풀 리퀘스트 선택하기
큐는 롤업 상태별로 정렬되어 있습니다. 일반적으로 좋은 롤업에는 (있는 경우) iffy PR 한두 개, 여러 개의 maybe(표시되지 않은) PR, 그리고 다수의 always PR이 포함됩니다. 롤업에는 rollup=never PR을 절대 포함해서는 안 됩니다(bors가 이를 보장합니다).
롤업의 실제 절대 크기는 경험에 따라 달라질 수 있습니다. 롤업을 처음 만드는 사람은 iffy 1개, maybe 4개, always 5개로 시작할 수 있지만, 경험이 많은 사람은 iffy 1~2개, maybe 8개, always 10개로 롤업을 만들기도 합니다! 대규모 롤업이 필요한 경우는 드물지만, 직관이 쌓일수록 롤업에 PR을 포함할 때 위험을 더 잘 판단하게 됩니다.
PR의 롤업 상태를 낮추는 것을 주저하지 마십시오! rollup=always PR이 실패할 가능성이 있다는 직감이 든다면 rollup=maybe 또는 rollup=iffy로 표시하십시오. 표시되지 않은 maybe PR 중 상당수는 리뷰어가 롤업 적합성을 고려하지 않았기 때문에 그렇게 분류되었을 수 있으므로, 항상 비판적인 시각으로 골라내는 것이 좋습니다. 마찬가지로, 어떤 PR 때문에 롤업이 실패했다면 해당 PR의 롤업 상태를 변경하는 것을 고려해 볼 만합니다
일반적으로 CI 구성이나 부트스트래핑 과정을 건드리는 PR은 iffy일 가능성이 높으므로 신중하게 다뤄야 합니다. 반면 문서만 수정하는 PR은 보통 rollup=always입니다.
큰 diff나 서브모듈 변경이 있는 PR을 같은 롤업에 너무 많이 넣지 마십시오. 성능에 큰 영향을 줄 것으로 의심되는 PR도 넣지 말고, 그런 PR은 rollup=never로 표시하십시오.
롤업이 성공하기를 바라는 것이 당연하므로 iffy PR을 아예 넣지 않고 싶은 유혹이 들 수 있습니다. 하지만 PR 큐의 역할은 PR을 병합하는 것이 아니라 테스트하는 것임을 기억해 둘 필요가 있습니다. 따라서 iffy PR 때문에 롤업이 실패하는 것은 좋은 일입니다. 그 PR은 어차피 언젠가는 테스트해야 하고, 롤업에 포함되지 않았더라도 테스트하는 데 같은 시간이 들었을 것이기 때문입니다. iffy PR과 관련해 롤업을 바라보는 한 가지 관점은, 롤업이란 iffy PR에 어차피 필요한 CI 사이클에 다른 여러 PR이 편승하는 방법이라는 것입니다. 롤업에서 iffy PR을 완전히 피하면 결국 이런 PR이 큐에 오랫동안 방치되기 쉬우며, 이는 바람직하지 않습니다.
마찬가지로, never PR에도 기회가 돌아가도록 여분의 CI 사이클을 반드시 남겨 두십시오! 롤업을 만드는 사람이 본인뿐이라면 큐를 지켜보지 않는 시간에 그런 PR이 실행되도록 두는 것이 좋습니다. 하지만 요즘은 여러 시간대에 롤업 작성자가 있으므로, 큐의 상대적인 크기를 지켜보면서 never PR을 위해 CI 사이클 몇 개를 따로 남겨 두는 것이 대개 가장 좋습니다. 이런 PR이 쌓여 있을 때는 특히 그렇습니다.
롤업은 공정하게 만들도록 노력하십시오. 롤업은 큐에서 새치기하는 수단입니다. rollup=maybe PR의 경우, 새 PR이 오래된 PR보다 무조건 먼저 처리되지 않도록 가장 오래된 PR(해당 섹션의 맨 위)을 포함하도록 노력하십시오. 롤업에 넣은 PR보다 오래된 PR을 모두 포함할 필요는 없지만, 가장 오래된 PR은 포함하도록 노력하십시오. iffy에 대한 관점과 마찬가지로, 롤업을 큐에서 가장 오래된 PR의 CI 사이클에 다른 PR이 편승하는 방법으로 보면 유용합니다.
실패한 롤업
롤업이 실패했을 때 그 실패가 일시적인 것이었다면(예: 네트워크 문제나 타임아웃) @bors retry 명령을 실행하십시오. 일시적인 실패가 아니었다면 문제를 일으킨 PR을 찾은 다음, rust-logs-analyzer 댓글의 링크를 복사하여 Failed in <link_to_comment>, @bors r-라고 작성해 그 PR을 제외하십시오. 그러면 작성자나 리뷰어가 PR을 고칠 수 있도록 피드백을 주거나, 해당 PR이 원인이 아님을 확인해 줄 것입니다. 실패한 롤업 PR은 닫아도 됩니다.
문제를 일으킨 PR을 제외했다면, 그 PR을 빼고 롤업을 다시 만드십시오(1. 참조). 그러나 문제를 일으킨 PR을 찾기 어려울 때도 있습니다. 그런 경우에는 직관을 활용해 문제로 의심되는 PR을 빼고 롤업을 다시 만드십시오. 또 다른 방법은 의심되는 PR의 우선순위를 높이고 rollup=never(또는 iffy)로 표시한 다음, bors가 해당 PR을 단독으로 테스트하게 하여 가설을 확인하거나 기각하는 것입니다.
롤업이 계속 실패한다면 @bors rollup=never 명령을 실행하여 해당 PR이 앞으로 롤업에 포함되지 않도록 할 수 있습니다.
트리아지 절차
풀 리퀘스트 트리아지
상태 태그
-
S-waiting-on-author - 작성자가 리뷰어의 의견을 반영하도록 변경해야 하거나, 병합 충돌이나 테스트 실패가 있는 상태입니다. 이 태그는 PR이 다른 PR 때문에 막혀 있는 경우(보통 S-blocked 레이블도 함께 붙습니다)나 crater 실행을 기다리는 경우처럼 드문 경우도 포함합니다. 이런 경우에도 PR을 진행시킬 책임은 작성자에게 있습니다.
작업 중인 PR에도 사용하며, 이런 PR은 GitHub에서 초안(draft)으로 표시되기도 합니다.
-
S-waiting-on-review - 리뷰가 끝나지 않은 상태입니다.
-
S-waiting-on-t-lang - T-lang의 피드백을 기다리는 중입니다.
-
S-waiting-on-t-compiler - T-compiler의 피드백을 기다리는 중입니다.
-
S-waiting-on-t-libs - T-libs의 피드백을 기다리는 중입니다.
-
S-waiting-on-bors - 승인을 받았고 병합을 기다리는 중입니다. bors가 관리합니다.
-
S-waiting-on-crater - PR이 생태계에 미칠 영향을 확인하기 위해 기다리는 중입니다.
-
S-waiting-on-bikeshed - 사소한 세부 사항에 대한 합의를 기다리는 중입니다.
-
S-waiting-on-perf - perf 실행 결과를 기다리는 중입니다.
-
S-waiting-on-ACP - API 변경 제안(ACP)을 기다리는 중입니다.
-
S-blocked - 다른 PR이 병합되거나 논의가 결론 나기를 기다리는 중입니다.
-
S-inactive - 한동안 활동이 없는 상태입니다.
-
S-experimental - 트리아지하지 않아야 하는 실험적인 PR입니다. 예전에는 이 용도로 S-waiting-on-author를 사용했지만, S-experimental은 해당 PR이 일부 변경 사항을 시험해 보기 위한 실험임을 알려 줍니다.
또한 상태 태그가 없는 PR도 확인하십시오. 이 목록은 rustbot이 제대로 작동하지 않아 리뷰어를 지정하지 못했고, 그래서 S-waiting-on-review도 붙지 않은 PR을 찾을 때 유용합니다. 이 목록을 확인하지 않으면 이런 PR은 놓치기 쉽습니다. (r? @ghost가 있는 PR은 작성자가 아직 리뷰를 원하지 않는다는 뜻이므로 트리아지하지 않는 것이 좋습니다.)
절차
우리는 주로 세 가지 상태 레이블을 트리아지합니다. S-waiting-on-review, S-waiting-on-author, 그리고 가끔 S-blocked입니다. 각 레이블의 절차는 다음과 같습니다.
S-waiting-on-review
S-waiting-on-review 레이블이 붙은 모든 PR을 보려면 이 링크를 클릭하십시오. 마지막 업데이트 후 15일 이상(하루 정도 차이는 괜찮습니다) 지난 PR만 트리아지하십시오.
각 PR에 대해 다음을 수행하십시오.
-
PR에 새 충돌이 생겼거나, CI가 실패했거나, 새 리뷰가 달렸다면 레이블을 S-waiting-on-author로 바꾸고 작성자에게 핑을 보내십시오.
-
해당 PR을 보고서에 추가하십시오.
S-waiting-on-author
이 링크를 클릭하면 S-waiting-on-author 레이블이 붙은 모든 PR을 볼 수 있습니다. 마지막으로 업데이트된 지 15일 이상 지난 PR만 트리아지하십시오(하루 정도의 오차는 허용됩니다).
각 PR에 대해 다음을 수행하십시오.
-
PR이 작성자에게 기다리던 작업을 작성자가 완료했다면 레이블을 S-waiting-on-review로 변경하십시오.
그렇지 않고 작성자가 아직 해야 할 일이 남아 있다면, 작성자가 Rust 팀의 구성원이 아닌 경우에 작성자에게 핑을 보내십시오(워킹 그룹은 포함하지 않으며,
T-compiler,T-lang,T-rustdoc등과 같은 팀만 해당합니다). -
해당 PR을 보고서에 추가하십시오.
S-blocked
S-blocked PR은 가끔씩만(예: 한 달에 한 번) 확인하면 됩니다. 이 링크를 클릭하면 S-blocked 레이블이 붙은 모든 PR을 볼 수 있습니다.
각 PR에 대해 다음을 수행하십시오.
-
여전히 블록된 상태라면 그대로 두십시오.
그렇지 않고 더 이상 블록된 상태가 아니라면 S-blocked를 제거하십시오(적절한 경우 S-waiting-on-review와 같은 상태 레이블도 추가하십시오).
-
해당 PR을 보고서에 추가하십시오.
트리아지 보고서
트리아지한 각 PR에 관한 정보를 보고서에 기록해야 합니다. 보고서는 다음과 같은 형태의 간단한 문서일 뿐입니다.
S-waiting-on-review
#12345 20일 - 여전히 리뷰 대기 중 - 작성자: ferris, 담당자: bors
[…]
보고서의 형태는 달라도 되지만, 각 PR에 대해 다음 정보는 반드시 포함하십시오.
-
PR 번호(예:
#12345). 링크를 직접 추가할 필요는 없습니다. Rust Zulip이 PR(및 이슈) 번호에 자동으로 링크를 걸어 줍니다. -
마지막 활동 이후 경과한 일수. “활동“이란 다음을 의미합니다.
- 작성자, 리뷰어 또는 팀 구성원이 댓글을 달거나 리뷰한 경우, 또는
- bors가 병합 충돌에 관해 댓글을 단 경우, 또는
- PR에 푸시가 이루어진 경우,
- 기타 등등.
-
작성자, 리뷰어, 그리고 PR이 기다리고 있는 대상(사람, 팀, 다른 PR 등).
-
현재 상태와 가장 최근 활동의 내용(예: 병합 충돌, 리뷰어의 댓글).
PR 트리아지를 마쳤다면 #t-release/triage Zulip 스트림에 있는 이번 주 트리아지 토픽에 보고서를 게시하십시오. 토픽 이름은 YYYY-MM-DD to YYYY-MM-DD와 같은 형식이어야 합니다. 여기서 한 주는 월요일부터 일요일까지라는 점에 유의하십시오.
토픽이 없다면 다음 bash 한 줄 명령으로 토픽 제목을 생성할 수 있습니다(GNU date 필요).
echo "$(date -I --date="$([ "z$(date +%a)" = "zMon" ] && echo 'today' || echo 'last monday')") to $(date -I --date="$([ "z$(date +%a)" = "zSun" ] && echo 'today' || echo 'next sunday')")"
중복 작업 피하기
트리아지는 가장 오래된 이슈부터 살펴보는 방식으로 진행되기도 하므로, S-* 레이블 중 하나를 다시 적용하면 이슈/PR의 마지막 수정 시각이 갱신되어 다른 트리아지 담당자에게 이미 처리된 항목임을 알릴 수 있습니다.
이슈 트리아지
이 페이지는 rust-lang/rust 저장소에 관한 내용입니다. rust-lang GitHub 조직에 속한 다른 저장소는 다른 절차를 따를 가능성이 높습니다.
추적 이슈(C-tracking-issue 레이블이 붙은 이슈)는 이 절차에 해당하지 않으며 다르게 처리됩니다.
동기
rust-lang/rust 저장소에는 수천 개의 이슈가 있고, 수백 명이 이 저장소에서 작업하고 있습니다. 모든 사람이 모든 이슈를 확인하고 해결하는 것은 불가능합니다. 트리아지의 목표는 다음과 같습니다.
- 발견 가능성 향상:
- 이슈가 어떤 팀과 관련될 수 있는지 분류합니다.
- 회귀에 태그를 제대로 붙이고 추적하도록 합니다.
- 이슈를 관련된 사람(특히 관련 팀)과 연결합니다.
- 이슈 성격에 대한 1차 대략적 분류:
- 이 이슈는 버그입니까, 논의입니까, 아니면 사용자 측 문제입니까?
- 이슈가 충분히 조치 가능한지 파악:
- 보고에 재현 단계, 재현 코드, 빌드/실행 환경 정보와 같은 추가 정보가 필요합니까?
- 재현 코드를 최소화해야 합니까?
- 적절한 레이블을 붙여 기여자가 이슈를 필터링하기 쉽게 만듭니다.
현실적으로 모든 이슈가 빠르게 해결되고 적절한 사람에게 발견되기를 기대하기는 어렵습니다. 레이블을 붙이면 나중에 참고할 때 이슈 트래커를 더 쉽게 검색할 수 있으므로, 앞으로 사람들이 관련 이슈나 작업하고 싶은 이슈를 더 쉽게 찾을 수 있습니다.
트리아지는 rust-lang/rust에 대한 쓰기 권한이 있는지와 관계없이 누구나 할 수 있습니다. 트리아지 작업은 여러 사람이 동시에 나누어 하기 쉽고 시작하기도 쉬우므로, 모든 분의 참여를 권장합니다.
rust-lang/rust에 대한 쓰기 권한이 필요한 작업으로는 다음과 같은 것들이 있습니다.
@rustbot transfer를 사용하여 이슈를rust-langGitHub 조직에 속한 다른 저장소로 옮기는 것.- 이슈를 닫는 것.
@rustbot을 사용하여 쓰기 권한이 필요한 특정 레이블을 붙이는 것(아래의 “레이블 추가 및 제거” 절을 참고하십시오).
쓰기 권한은 없지만 이러한 작업이 필요하다고 판단된다면, t-release/triage Zulip 채널에 새 토픽을 열거나 t-release/triage 채널의 기존 토픽에 해당 이슈의 링크를 담은 댓글을 남기면 됩니다.
초기 트리아지
이슈가 열리면 보통 needs-triage 레이블이 자동으로 붙습니다. 이는 트리아지되지 않은 이슈를 쉽게 발견하고 필터링할 수 있게 하여, 새 이슈가 초기 트리아지 과정(아래에서 설명합니다)을 반드시 거치도록 하기 위함입니다. needs-triage는 초기 점검 단계입니다. 이슈가 이 레이블을 벗어나는 데 드는 노력은 적어야 합니다.
초기 트리아지를 수행하고 needs-triage 레이블을 제거하려면 다음 조건을 충족하거나 고려해야 합니다. 이 조건을 항상 모두 고려하지 못하더라도 괜찮습니다. 이 목록은 모든 경우를 망라하지 않으므로 엄격한 체크리스트가 아니라 지침으로 활용하십시오.
- 이슈는 이해할 수 있는 내용이어야 합니다. 즉, 문제를 제시해야 합니다.
- 이 이슈가 앞서 보고된 이슈와 중복되는지 확인하십시오.
- 중복이 확실하다면 이 이슈를 이전 이슈의 중복으로 닫으십시오. 이전 이슈의 백링크에서 이 사실이 분명히 드러나는지 확인하거나, 중복 이슈에 명시적으로 링크하십시오.
- 확실하지 않더라도 다른 이슈와 중복이거나 비슷하거나 관련이 있을 수 있다는 댓글은 남길 수 있습니다.
- 적절한 레이블을 추가하십시오(레이블).
- 특히 팀 레이블(
T-*)과 이슈 분류 레이블(C-*)이 가장 중요합니다. 팀 구성원은 대개 이 레이블을 기준으로 팀과 관련된 사항을 걸러 내기 때문입니다(예: 트리아지 회의 중).
- 특히 팀 레이블(
- 이슈에 재현 방법이 없는데 필요하다면(확실하지 않다면 필요한 것으로 봅니다) 재현 방법을 요청하고
S-needs-repro레이블을 추가하십시오. - 이슈에 정보가 부족하다면(예: 시스템 정보, 실제로 사용한 정확한 실행 명령) 문제를 재현하는 데 중요한 정보를 제공해 달라고 보고자에게 요청하십시오.
S-needs-info레이블을 추가하십시오. - 일부 기능 요청은 이슈 트래커에 올리기에 적절하지 않습니다. 작성자를 적절한 채널로 안내하십시오.
- 표준 라이브러리 API 요청은 라이브러리 팀 절차를 따라야 합니다.
- 언어 변경은 IRLO 또는 T-lang Zulip 채널(t-lang)로 안내해야 합니다.
- 회귀를 이분 탐색하는 것이 이슈에 도움이 될 수 있다면(확실하지 않다면 도움이 되는 것으로 봅니다)
E-needs-bisection을 추가하십시오(또는 직접 이분 탐색을 수행하십시오). 유용한 이분 탐색 도구로는cargo-bisect-rustc가 있습니다.- 이분 탐색이 이루어졌고 그 결과가 설득력 있다면
S-has-bisection을 추가하십시오.
- 이분 탐색이 이루어졌고 그 결과가 설득력 있다면
- 보고된 예제가 크거나 복잡하다면(예: 코드가 많거나, 외부 의존성이 많거나, 실제 문제를 가리는 무관한 경고나 오류가 있다면) 예제를 최소화해야 함을 나타내도록
E-needs-mcve를 추가하십시오.- 최소한의 완전하고 검증 가능한 예제(MCVE)가 있다면 이슈에
S-has-mcve태그를 다십시오. 최소화가 올바른지 다시 확인하십시오. 예를 들어 MCVE가 원래 문제를 다루는지, 다른 문제로 바뀌지 않았는지 확인하십시오.
- 최소한의 완전하고 검증 가능한 예제(MCVE)가 있다면 이슈에
- 이 이슈가 회귀입니까? 그렇다면
regression-untriaged레이블을 적용하십시오(또는 정확히 어떤 회귀인지 파악하십시오). 일반적으로 이전에는 동작하던 것이 더 이상 동작하지 않는다면 그 이슈를 회귀로 봅니다. - 이 이슈와 관련된 사람을 알고 있다면 그 사람을 핑하십시오.
- 예를 들어
ThatPerson이 최근 해당 기능 작업을 많이 해 왔다면cc @ThatPerson이라고 작성하십시오.
- 예를 들어
- 이 이슈가 어떤 기능을 사용합니까? 해당하는
F-*레이블을 추가하십시오. 예:F-const_trait_impl. - 이 이슈는 기능을 사용하지 않는데도 nightly가 필요합니까?
requires-nightly를 추가하십시오. - 이 이슈는 내부 기능이 필요합니까?
requires-internal-features를 추가하십시오.
레이블 추가 및 제거
rust-lang/rust에 쓰기 권한이 없는 사용자는 우회 방법으로 @rustbot을 사용하여 triagebot.toml 설정에서 허용하는 레이블을 추가하거나 제거할 수 있습니다.
쓰기 권한이 있는 사용자는 이슈를 구독하는 모든 사람에게 불필요한 알림이 가지 않도록 레이블을 직접 변경해야 합니다.
예를 들면 다음과 같습니다.
@rustbot label +T-compiler +C-bug +A-linkage +O-macos -needs-triage
전체 레이블 목록을 보려면 이슈 트래커의 검색창 옆에 있는 “labels” 페이지를 확인하십시오.
일부 레이블은 rust-lang/rust에 쓰기 권한이 있는 사용자만 적용할 수 있다는 점에 유의하십시오. 쓰기 권한이 없는 사용자가 사용할 수 있는 레이블을 확인하려면 triagebot.toml의 [relabel] 섹션 아래에 있는 allow-unauthenticated 목록을 참고하십시오.
relnotes 트리아지
relnotes-tracking-issue 레이블이 붙은 이슈의 경우, 릴리스 노트 문구가 정리될 때까지 needs-triage 태그를 제거해서는 안 됩니다.
레이블
이슈에 적용할 수 있는 레이블은 매우 다양합니다. 트리아지에 사용하는 레이블 집합은 필요에 따라 계속 바뀌며, 저희는 이 목록을 최신 상태로 유지하려고 노력합니다. 레이블은 팀 구성원이 적절하다고 판단할 때 수동으로 추가합니다.
needs-triage: 새로 등록되어 초기 트리아지가 필요한 이슈임을 나타냅니다.T-*: 이 이슈와 관련된 하나 이상의 팀을 지정합니다. 예를 들어T-compiler,T-types,T-libs등이 있습니다. 자세한 내용은 팀 예시를 참고하십시오.WG-*: 이 이슈와 관련된 워킹 그룹을 지정합니다. 예를 들어WG-debugging이 있습니다.PG-*: 이 이슈와 관련된 프로젝트 그룹을 지정합니다. 예를 들어PG-exploit-mitigations가 있습니다.C-*: 레이블의 범주를 지정합니다. 예를 들어 버그, 추적 이슈, 논의 등이 있습니다.C-optimization: 컴파일러가 놓친 최적화에 사용합니다.C-defective-hardware: 저희가 통제할 수 없는 하드웨어 버그에 사용합니다.C-gub: 정의되지 않은 동작과 같은 사용자 오류로 인해 문제가 발생한 경우에 사용합니다.C-discussion: 버그는 아니지만 논의할 가치가 있는 경우에 사용합니다.C-external-bug: 저희에게 영향을 주지만 저희가 직접 통제할 수는 없으면서도 저희 쪽에서 추적할 가치가 있는 소프트웨어 버그에 사용합니다.
O-*: 타깃별 이슈의 경우, 컴파일 타깃1 또는 컴파일 타깃 계열(특히 플랫폼, 즉 아키텍처나 운영 체제)을 지정합니다. 예를 들어O-macos,O-aarch64,O-windows,O-windows-msvc등이 있습니다.- 가능하면 가장 구체적인 대상 카테고리를 지정하십시오. 예를 들어 이슈가
*-windows-gnu에는 영향을 주지만*-windows-msvc에는 영향을 주지 않는다면, 일반적인O-windows보다O-windows-gnu를 사용하십시오.
- 가능하면 가장 구체적인 대상 카테고리를 지정하십시오. 예를 들어 이슈가
A-*: 이슈와 관련된 영역입니다. 예를 들면A-linkage,A-patterns,A-diagnostics등이 있습니다. 이는 이슈를 필터링할 때 특히 유용합니다.A-diagnostics: 진단 이슈 템플릿으로 생성된 이슈에는A-diagnostics만 있고C-bug는 없습니다.
B-*: 블로커인 이슈입니다.D-*: 진단 이슈를 위한 레이블입니다.D-diagnostic-infra: 이 이슈는 진단 인프라 자체에 관한 것입니다.
L-*: 이슈가 특정 린트와 관련된 경우입니다.L-false-positive: 린트가 발동하지 않아야 할 경우에 발동한 경우입니다.L-false-negative: 린트가 발동해야 할 경우를 놓친 경우입니다.
F-*: 이슈가 특정 기능(대개 불안정한 기능이며, 대개 언어 기능)과 관련된 경우입니다.-Z*: 이슈가 특정한 불안정-Z컴파일러 플래그와 관련된 경우입니다.requires-nightly: 이 이슈는 nightly 채널 컴파일러에만 영향을 주며, beta 또는 stable 채널 컴파일러에는 영향을 주지 않습니다.requires-internal-features: 이 이슈는 내부 기능을 필요로 합니다. 이는 보통 컴파일러 MCP 620에 따라 해당 이슈를 닫아야 한다는 뜻입니다.regression-*: 회귀인 이슈를 추적하기 위한 레이블입니다.regression-untriaged: 회귀의 성격과 종류가 불분명하며, 추가적인 회귀 트리아지가 필요함을 나타냅니다.regression-from-stable-to-stable: 이전 stable 릴리스에서 회귀가 발생하여, 이미 최신 stable 릴리스 또는 그보다 이전의 stable 릴리스에까지 도달한 경우입니다.regression-from-stable-to-{beta,nightly}: stable 릴리스에서 beta 또는 nightly 채널로 가면서 회귀가 발생한 경우입니다.
I-*: 이슈의 성격2을 나타내는 여러 레이블입니다.I-ICE: 내부 컴파일러 오류입니다.I-prioritize: 해당하는 경우 긴급도를 나타내는 우선순위P-*를 지정할 수 있도록 T-compiler/ops가 이 이슈를 추가로 트리아지해야 함을 나타냅니다.I-heavy: 무거운 코드(바이너리 크기)입니다.I-slow: 느린 런타임 성능입니다.I-hang: 오랜 시간이 지나도 컴파일이 완료되지 않는 경우입니다.I-crash: 컴파일러 또는 생성된 코드가 크래시하지만 ICE로 나타나지는 않는 경우입니다. 예를 들면 SIGSEGV나 접근 위반 등이 있습니다.I-unsound: 라이브러리, 컴파일러, 타입 시스템 또는 언어의 건전성 문제입니다.I-compilemem: 컴파일 중 과도한 메모리 사용입니다.I-compiletime: 느린 컴파일 시간입니다.I-{team}-nominated:{team}의 논의 안건으로 상정된 이슈입니다. 예:I-compiler-nominated.I-lang-easy-decision: (레이블을 붙인 사람이 보기에) T-lang에 필요한 결정이 쉽거나 형식적일 것으로 추정됨을 나타냅니다. 이 레이블이I-lang-nominated를 의미하지는 않는다는 점에 유의하십시오. 레이블을 붙이는 사람이 해당 이슈를 T-lang 논의에 상정하려면 상정 레이블도 함께 붙여야 합니다.
P-*: 우선순위 레이블입니다. 컴파일러 우선순위 지정 절차에 따라 적용합니다.E-*: 참여 요청3으로, 예를 들어 이슈를 최소화하는 작업이 이에 해당합니다.E-mentor: 이 이슈를 도와줄 멘토가 있으므로 처음 시작하기 좋은 이슈입니다. 멘토로 예정된 사람이 실제로 멘토링할 의향과 여유가 있는지 확인하십시오.E-needs-mcve: 이 이슈에는 재현 사례가 있지만 최소화되어 있지 않습니다. 최소화해야 합니다.E-needs-bisection: 이 이슈는 이분 탐색이 필요합니다. 예를 들어cargo-bisect-rustc를 사용할 수 있습니다.E-needs-investigation: 이 이슈는 근본 원인과 이슈의 성격을 파악하기 위해 추가 조사가 필요합니다.E-needs-design: 이 이슈는 상당한 설계 작업(탐색, 프로토타이핑, 논의 등)이 필요합니다.E-needs-test: 이슈는 수정되었지만 이에 대한 테스트가 추가되지 않았습니다. 누군가 테스트를 추가하면 이슈를 닫을 수 있습니다.E-{easy,medium,hard}: 누군가가 이슈 수정의 난이도를 추정했습니다. 이는 처음 시작하기 좋은 이슈를 찾는 데 도움이 될 수 있지만, 부정확할 수밖에 없습니다.
S-*: 이슈의 상태를 나타내며, 예를 들어S-needs-repro또는S-needs-info가 있습니다.S-needs-info: 이슈 보고자의 추가 정보가 필요합니다.S-needs-repro: 재현 자료(코드 샘플, 재현 단계 등)가 필요합니다.S-bug-has-test: 이슈에 대한 known-bug 테스트가rust-lang/rust의tests/crashes테스트 스위트나 다른 곳에 있습니다.S-has-bisection: 이분 탐색이 수행되었으며 그 결과가 신뢰할 만합니다.S-waiting-on-LLVM: 업스트림 LLVM PR 또는 수정을 기다리고 있습니다.S-tracking-forever: 닫을 의도가 전혀 없는 추적 이슈입니다.
beta-nominated: beta로 백포트하도록 상정된 변경 사항을 추적합니다.beta-accepted: beta로 백포트하도록 승인된 변경 사항을 추적합니다. 백포트는 보통T-release가 처리합니다.stable-nominated: 포인트 릴리스에 대비해 stable로 백포트하도록 상정된 변경 사항을 추적합니다.stable-accepted: stable로 백포트하도록 승인된 변경 사항을 추적합니다. 백포트는 보통T-release가 처리합니다.relnotes: 다음 릴리스의 릴리스 노트에 문서화하도록 제안된 변경 사항입니다- triagebot이 새 relnotes 이슈를 생성하도록 합니다(예시)
- 또한 relnotes 이슈를 표시하여 T-release relnotes 도구가 처리할 수 있도록 합니다
- 이슈에서 FCP가 시작되는 경우에도 relnotes 이슈가 생성됩니다.
metabug: 다른 버그들을 추적합니다.
팀 예시
이 섹션에서는 특정 팀에 배정해야 하는 이슈 유형의 예시를 나열합니다.
T-compiler
진단 및 ICE와 같이 컴파일러 구현과 관련된 모든 것입니다.
T-libs
- 라이브러리 함수의 구현 세부 사항 변경
- 라이브러리 문서의 철자, 문법 및 구성 변경
- 함수 및 메서드와 같은 새로운 라이브러리 API
- 불안정 함수의 시그니처 변경
- 특정 상황에서 함수가 특정 오류 코드를 반환함을 보장하는 것과 같은 라이브러리 문서의 의미적 변경
- 새로운 트레이트 구현과 같은 라이브러리 변경으로 인한 타입 추론 손상
T-lang
- 새로운 키워드/언어 기능
- 표준 라이브러리의 키워드 문서 변경
T-spec
- 레퍼런스 변경
T-opsem
- 노미콘 변경
- 원자적 연산의 의미론과 같은 추상 기계의 의미론 변경
- unsafe 포인터 함수 문서 변경
core::ptr문서 변경
레이블 생성
triagebot은 마침표나 공백으로 끝나는 @rustbot label: xxx 사용(인라인 호출)을 지원해야 하므로, 레이블 이름은 영숫자, 하이픈(-) 또는 밑줄(_) 문자로만 구성되어야 합니다.
- 기존 레이블을 확인하여 중복되지 않도록 하십시오.
- 새 레이블이 관례에서 벗어나거나 논란의 여지가 있을 수 있다면 https://rust-lang.zulipchat.com/#narrow/channel/242269-t-release.2Ftriage/topic/New.20labels에서 논의하십시오. 다른 사람들이 참고할 수 있도록 새 레이블에 대한 코멘트를 남기십시오.
레이블 별칭
별칭을 사용하면 여러 레이블을 한 번에 추가하거나 제거할 수 있습니다. 별칭에 대해 더 알아보려면 관련 문서를 참고하십시오.
Relnotes 이슈
현재 릴리스 노트 이슈에는 기본적으로 needs-triage가 붙습니다. relnotes 트리아지는 보통 충분한 맥락을 알고 있을 때 하는 것이 가장 좋습니다. 맥락을 잘 모른다면 그대로 두십시오.
- 실제 PR과는 관련이 있지만 사용자에게 보이는 실제 relnotes와는 관련이 없는 영역
A-*레이블을 제거하십시오. - 관련 없는
T-*또는WG-*레이블은 제거하십시오. - PR 작성자, 리뷰어 또는 관련 팀 구성원이 relnotes 문구를 확인했거나 변경했다면
needs-triage를 제거하십시오.
내부 컴파일러 오류(ICE) 이슈 트리아지
내부 컴파일러 오류(ICE)가 포함된 이슈는 다음과 같이 처리합니다.
- 이슈에
I-ICE,T-compiler,C-bug레이블이 붙어 있는지 확인하십시오. - 이슈가 실제로 ICE인지, 아니면
I-crash나I-hang으로 분류하는 것이 더 정확한지 확인하십시오. - 이전 버전(예: 최신 stable)의 Rust에서 발생한 것이라면 다음을 물어보십시오.
- beta와 최신 nightly에서도 ICE가 재현됩니까?
- 그보다 오래된 stable이라면 최신 stable에서도 ICE가 재현되는지 물어보십시오.
- 중복 이슈가 있는지 확인하되, 근본적으로 같은 이슈라고 확신할 때만 중복으로 닫으십시오. 관련 이슈나 중복일 수 있는 이슈로 링크만 걸어 두는 편이 좋습니다.
- ICE 메시지로 검색해 보는 것이 좋은 방법입니다. ICE 메시지가 같아도 근본 원인은 다를 수 있으므로 중복으로 닫을 때는 주의하십시오.
- 재현 코드가 없다면 재현 코드를 요청하는 댓글을 남기고
S-needs-repro를 추가하십시오.- 한 달 정도 지나도 재현 코드가 없다면 일반적으로 이슈를 닫아야 합니다.
- 재현 코드가 최소화되어 있지 않다면
E-needs-mcve를 추가하거나 직접 MCVE를 만드십시오. MCVE 절차에 대한 자세한 내용은 “초기 트리아지” 섹션을 참고하십시오. - 이슈를 일으키는 코드(백트레이스를 확인하십시오!)와 재현 사례의 성격에 따라
A-*레이블을 추가하십시오(예: 재현 사례가 특이한 트레이트 impl이거나 백트레이스가rustc_trait_selection을 가리킨다면A-traits를 추가하십시오). - 필요에 따라
T-*,WG-*,PG-*,F-*,requires-*,regression-*레이블을 추가하십시오.- 일반적으로 ICE에는
T-compiler레이블을 붙여야 합니다. 이슈가 타입 시스템(예: 트레이트 솔버)과 관련된 경우 해당 이슈에T-types태그도 추가하십시오.
- 일반적으로 ICE에는
- rustdoc에서 발생한 ICE라면
T-compiler대신T-rustdoc를 추가하십시오.
추가 트리아지
초기 트리아지 단계를 거친 이슈(즉, 더 이상 needs-triage 레이블이 없는 이슈)에도 보통 개선할 점이 남아 있습니다. 추가로 붙일 수 있는 레이블이 훨씬 많은 경우가 많습니다(rust-lang/rust에 대한 쓰기 권한이 없다면 이번에도 rustbot을 사용하십시오).
또한 오래된(얼마나 지나야 오래된 것인지 아직 명확한 기준은 없지만 대략 몇 달 정도) S-needs-repro 이슈는 재현 코드 없이 진행할 방법이 없다면 닫을 수 있습니다. 이 작업에는 rust-lang/rust에 대한 쓰기 권한이 필요하지만, 권한이 없다면 Zulip(예: t-release/triage 또는 general)에 이슈 링크를 올리기만 해도 쓰기 권한이 있는 사람이 대신 닫아 줄 수 있습니다.
E-needs-mcve 및 E-needs-bisection 이슈를 살펴보면서 최소화된 재현 코드를 만들거나 (cargo-bisect-rustc를 사용하여) 이슈를 이분 탐색하는 것도 유용합니다.
- 이분 탐색(bisection) 결과를 제공할 때는 이분 탐색 상태 레이블을 조정하십시오:
@rustbot label -E-needs-bisection +S-has-bisection. - MCVE를 제공할 때는 MCVE 상태 레이블을 조정하십시오:
@rustbot label -E-needs-mcve +S-has-mcve.
Crater 실행 트리아지
Crater 실행하기
우리는 정기적으로 Crater를 실행하며, 이 문서는 beta 실행을 트리아지하는 절차를 설명합니다. 약간만 수정하면 릴리스 팀이 아닌 곳에서 수행하는 실행(예: PR Crater 실행)에도 적용할 수 있습니다.
먼저 “1.x에 대한 Crater 실행“이라는 제목으로 새 이슈를 등록하십시오(예시).
beta가 나오는 즉시 beta에 대한 Crater 실행을 시작해야 합니다. 다음 craterbot 명령을 사용하십시오.
$BETA_VERSION은 예를 들어 1.81.0-1이며, 첫 번째 beta Crater 실행이 아니라면 1을 증가시키십시오. beta rustc --version에 표시되는 자동 증가 카운터를 사용할 수도 있습니다.
$STABLE은 예를 들어 1.80.0(stable 릴리스)이고, $BETA는 1.81.0-beta.1입니다. https://static.rust-lang.org/manifests.txt 를 보고 가장 최근 channel-rust-beta.toml의 날짜를 확인하여, beta-YYYY-MM-DD와 같이 날짜로 beta를 지정할 수도 있습니다.
@craterbot run name=beta-$BETA_VERSION start=$STABLE end=$BETA mode=build-and-test cap-lints=warn p=10
@craterbot run name=beta-rustdoc-$BETA_VERSION start=$STABLE end=$BETA mode=rustdoc cap-lints=warn p=5
@craterbot run name=beta-release-$BETA_VERSION start=$STABLE+cargoflags=--release end=$BETA+cargoflags=--release mode=build-and-test cap-lints=warn p=3
실행이 완료되면 결과를 트리아지해야 합니다.
트리아지
이 단계는 일반적으로 일반 rustc 실행에 대해 먼저 수행하고, 이어서 rustdoc 실행을 트리아지한 다음, --release 실행을 트리아지합니다. rustdoc 실행과 --release 실행에서 발생한 실패 중 일반 rustc 실행과 중복되는 것은 무시하십시오.
보통 회귀가 꽤 많이 발생합니다. 해야 할 작업량을 줄이는 데 도움이 되는 도구가 몇 가지 있습니다. 어느 도구가 더 유용한지는 대체로 개인 취향의 문제입니다.
- https://github.com/Mark-Simulacrum/crater-generate-report/
- 이 도구는 로그를 파싱하여 Cargo가 출력한 컴파일 실패 메시지를 찾아 회귀를 ’근본 원인’별로 그룹화합니다.
- https://github.com/Centril/crater-cat-errors
- 이 도구도 로그를 파싱하여 회귀를 “error” 메시지별로 그룹화합니다.
직접 작성한 도구가 있다면 자유롭게 여기에 추가하십시오! 이를 위한 최선의 UI가 무엇인지는 아직 찾아가는 중입니다.
어떤 도구를 실행했든, 결국에는 많은 로그를 읽으면서 그것이 진짜 실패인지 허위 실패인지 빠르게 판단해야 합니다. 대부분의 경우 컴파일러 실패는 진짜 실패이고 테스트 실패는 대부분 허위 실패이지만, 이를 판단하려면 보통 어느 정도의 추측이 필요합니다.
어떤 것이 진짜 실패라고 판단되면 오류 “범주“와 함께 어딘가(로컬 파일, HackMD 등 무엇이든)의 목록에 추가하십시오. 대체로 하나의 그룹에 속한 회귀는 모두 같은 커밋 집합에 의해 발생하고, 서로 다른 그룹은 서로 다른 원인을 갖도록 그룹화하는 것이 목표입니다.
이 작업을 마치고 모든 회귀를 각각의 그룹으로 트리아지했다면, 그룹마다 새 이슈를 등록해야 합니다. 이 이슈에는 기본적으로 regression-from-stable-to-beta 및 T-compiler 레이블을 달아야 하며, 표준 라이브러리 회귀인 경우에는 T-libs를 달 수도 있지만 이런 경우는 비교적 드뭅니다. 실패를 일으킨 PR을 알고 있다고 생각되면 별도의 댓글에서 해당 PR 작성자를 cc하고 PR 링크를 거십시오. 그렇지 않으면 컴파일러 팀이 곧 이슈를 트리아지할 것입니다.
Crater 실행에 대한 원래 이슈에 방금 등록한 모든 이슈의 링크를 담은 댓글을 남기십시오. 가능하면 이슈 제목도 함께 적으십시오.
모두 끝났습니다!
크레이트에 대해 rustc 다시 실행하기
확실하지 않은 크레이트의 경우 Crater를 로컬에서 실행해 보거나 크레이트를 직접 빌드해 볼 수 있습니다(cratesio-curl이 도움이 될 수 있습니다). 주의하십시오. 어떤 방법을 쓰든 로컬에서 임의의 코드를 실행하게 됩니다. 확실하지 않은 크레이트에 대해 이슈를 등록하고 트리아지 과정에서 오류가 자연스럽게 분류되도록 하는 것도 괜찮지만, 모든 크레이트에 대해 이렇게 하는 것은 좋지 않습니다. Crater 실행 결과를 몇 번 트리아지해 보면 어떤 것이 일시적인 오류이고 어떤 것이 아닌지도 꽤 잘 감이 잡히게 됩니다.
(적어도 현재로서는) 다음과 같이 하여 단일 크레이트에 대해서만 Crater를 실행할 수 있습니다. 이 작업은 (처음 사용할 때) 수 기가바이트를 내려받으며 Docker가 실행 중이어야 한다는 점에 유의하십시오.
git clone https://github.com/rust-lang/crater
cd crater
cargo run -- prepare-local
CRATES="crates-io-crate-0.4.0,owner/repository-name" # Edit this.
cargo run -- define-ex --crate-select=list:$CRATES --cap-lints=forbid 1.38.0 beta # Edit the stable version.
cargo run -- run-graph --threads 4
cargo run -- gen-report work/ex/default/
# view report for this crate
크레이트 일부를 공식 빌더의 대기열에 다시 넣는 것도 가능하며, 이에 대해서는 다음을 참고하십시오: https://gist.github.com/ecstatic-morse/be799bfa4d3b3d6e163fa61a9c30706f
회귀의 근본 원인 파악하기
크레이트가 왜 빌드되지 않게 되었는지 항상 명확한 것은 아닙니다. 이는 일반적으로 Crater 트리아지의 일부로 수행하는 작업은 아니지만, 좋은 후속 작업이 될 수 있습니다. 이때 cargo-bisect-rustc와 Felix의 최소화 가이드는 활용하기에 훌륭한 도구입니다.
에디션 릴리스
이 문서는 에디션 릴리스를 관리하는 방법을 개략적으로 설명합니다. 이 문서는 독자가 에디션이 무엇인지 이미 알고 있다고 가정합니다(에디션 가이드를 참고하십시오).
에디션 프로젝트 그룹
RFC 3501은 에디션 관리를 책임지는 프로젝트 그룹을 구성하는 일이 리더십 위원회의 책임이라고 규정했습니다.
프로젝트 그룹 리드
프로젝트 그룹에는 리드를 두세 명 두는 것을 권장합니다. 그래야 비교적 촉박한 일정 안에서 필요한 결정과 조치를 더 쉽게 조율하고 신속하게 내릴 수 있습니다.
또 다른 고려 사항은 헌신과 가용 시간입니다. Rust에서 하는 대부분의 작업과 달리 에디션에는 정해진 일정이 있습니다. 따라서 평소와는 다른 종류의 헌신이 필요합니다. 평소에는 사람들이 합류하거나 떠나거나 예기치 않은 지연을 겪더라도 우리 체계가 꽤 너그럽게 받아들입니다. 에디션에서는 그럴 여지가 더 적습니다. 리드는 정기적으로 회의에 참석하고 조치 항목을 제때 후속 처리하겠다고 약속할 수 있어야 합니다. (물론 이런저런 일이 생기기도 하고 휴가를 가기도 하는 등 여러 사정이 있을 수 있으며, 그런 경우에는 어떻게든 대처하여 해낼 것입니다. 하지만 무슨 뜻인지는 이해하실 것입니다.)
또 하나 고려할 점은 필요한 시간이 달마다 크게 달라진다는 것입니다. 할 일이 전혀 없는 달도 있고, 상당히 많은 시간(한 주에 10시간 이상)이 필요한 달도 몇 번 있을 수 있습니다. 이는 에디션에 포함되는 변경 사항이 얼마나 많은지에 따라서도 크게 달라집니다.
프로젝트 그룹 구성원
에디션 프로젝트 그룹의 추가 구성원은 비교적 단기적인 조치 항목을 돕거나 프로세스의 특정 측면(문서 작성, 마이그레이션 린트 구현, 버그 수정, 진행 상황 업데이트 및 블로그 게시물 작성 등)을 도울 수 있습니다.
단계
에디션을 진행하려면 프로젝트 전반에 걸쳐 조율되는 여러 단계를 거쳐야 합니다.
- 준비 단계. 에디션이 출시되기 약 1년에서 3년 전의 시기로, 모든 준비 작업이 이 단계에 포함됩니다. 이러한 작업은 빨리 수행할수록 좋습니다.
- 다음 에디션에 대한 예비 지원을 도구에 추가해야 합니다. 앞으로는 이를 자동화하는 것도 좋을 것입니다. 예시:
- 각 팀이 제안 및 구현 작업을 시작합니다.
- 리더십 위원회가 에디션을 운영할 프로젝트 그룹을 구성합니다(최종 릴리스 약 1년 전).
- 최종 마감 단계입니다. 에디션 릴리스 약 1년 전부터 시작되는 기간입니다. 이 시점부터 에디션에 추가될 모든 항목에 대한 일련의 최종 마감이 시작됩니다. 한 해 동안의 마감 일정은 아래의 예시 일정을 참고하십시오.
기능 단계
각 기능은 일련의 단계를 거칩니다. 이 과정은 언제든지 시작할 수 있습니다. 이 과정에 걸리는 시간은 매우 다양하여, 아주 빨리 끝나기도 하고 여러 해가 걸리기도 합니다.
- 개인과 팀이 에디션 변경을 제안합니다. 정확한 절차는 팀마다 다르지만, 일반적으로는 IRLO에 Pre-RFC를 게시하는 것으로 시작합니다.
- 제안하는 에디션 변경 사항과 함께 RFC를 게시해야 합니다.
- 팀이 RFC를 승인합니다. 이는 팀이 원칙적으로 그 아이디어를 원한다는 뜻이지만, 특정 에디션에 맞춰 반영된다는 것을 보장하지는 않습니다.
- RFC 또는 팀은 이전 에디션으로부터의 마이그레이션을 어떻게 처리할지 정의하는 마이그레이션 계획을 마련해야 합니다. 일부 유형의 호환성 깨짐 때문에 사람들이 코드를 직접 수정해야 하는 것은 괜찮지만, 그런 경우는 드물어야 하며, 이상적으로는 눈에 잘 띄어야 합니다(즉, 런타임에 예상치 못한 의미 변화가 생기는 것이 아니라 컴파일 오류가 발생해야 합니다). 무엇이 “충분히 드문” 것인지는 에디션 리드가 판단합니다.
- 기능과 마이그레이션 지원을 구현합니다.
- 해당 기능에 가장 관심이 많은 사람들이 nightly에서 비공식 테스트를 진행해야 합니다. 이 기간 동안 이슈를 찾아내고 수정해야 합니다.
- 기능을 담당하는 팀은 기능 마감일까지 해당 기능이 다음 에디션에 포함될 준비가 되었는지 최종 결정을 내려야 합니다. 이 결정은 에디션 프로젝트 그룹과 함께 내려야 합니다.
- 에디션 가이드와 레퍼런스 같은 곳에 변경 사항을 문서화합니다.
예시 일정
다음은 에디션 마감 일정의 예시입니다.
릴리스를 기준으로 한 마일스톤으로 표시되어 있습니다. 이전 에디션 릴리스는 연말(10월)에 stable에 도달했지만, 향후 에디션은 6월처럼 연중 더 이른 시기에 릴리스할 것을 적극 권장합니다.
이 날짜들은 확정된 것이 아니며(예를 들어 Rust는 6주 주기로 릴리스되므로 정확한 릴리스 날짜가 조금씩 달라집니다), 에디션 프로젝트 그룹이 필요에 따라 조정해야 합니다.
- 에디션 릴리스 날짜 1~3년 전
- 각 팀은 에디션 변경 사항을 계획하고 구현해야 합니다.
- T-11개월
- 리더십 위원회는 에디션 프로젝트 그룹이 구성되어 준비를 마쳤는지 확인합니다.
- 에디션 일정을 발표하는 블로그 게시물을 작성합니다.
- 에디션 프로젝트 그룹은 각 팀과 변경 사항 목록을 조율하기 시작하고, 변경 사항을 추적할 추적 도구를 마련해야 합니다.
- 도구는 다음 에디션을 예비로 지원해야 합니다(이전 에디션 직후에 이 작업을 하는 것이 이상적입니다).
- 최종 기능 목록을 요청하고 최종 마감일을 알리는 공개 블로그 게시물을 작성합니다. 예시
- T-10개월
- Pre-RFC 제안을 제출할 마지막 기회입니다.
- T-9개월
- RFC를 승인받을 마지막 기회입니다.
- T-8개월
- 에디션 변경 사항의 최종 목록이 확정되고 모든 RFC가 승인됩니다.
- 에디션에 포함되는 내용을 알리는 공개 블로그 게시물을 작성합니다. 예시
- T-7개월
- T-6개월
- T-5개월
- 모든 기능과 마이그레이션이 nightly에 구현됩니다. 모든 기능 게이트를 제거해야 합니다.
- T-4개월
- 모든 마이그레이션을 Crater로 테스트합니다(아래 Crater 마이그레이션 테스트를 참조하십시오).
- 에디션 프로젝트 그룹은 모든 에디션 이슈를 추적하여 제때 해결되도록 해야 합니다.
- T-3개월
- nightly에서 최종 테스트를 요청하는 공개 블로그 게시물을 작성합니다. 예시
- T-2개월
- T-1개월
- 에디션이 beta에 도달하며, 백포트할 수 있는 마지막 기회입니다.
- 릴리스 팀과 협력하여 릴리스 공지를 준비합니다. 예시
- T-0개월
- 에디션이 stable로 릴리스됩니다.
Crater 마이그레이션 테스트
Crater는 마이그레이션 린트 테스트를 직접 지원하지 않습니다. crater 실행을 수행하려면 크레이트 마이그레이션에 필요한 단계를 수행하도록 수정된 버전의 cargo를 사용해야 합니다. #87190에 그 예시가 있습니다. 이는 대략 다음 단계를 수행합니다.
- Crater 실행은 이전 메인 브랜치 빌드를 사용하여
cargo check를 실행합니다. - Crater 실행은 수정된
cargo를 사용하여cargo check를 실행합니다. 이 수정된cargo check는 일반적인 검사 대신 다음 단계를 수행합니다. - 패키지를 임시 디렉터리로 복사합니다(crater에서는 소스 디렉터리가 읽기 전용이기 때문입니다).
- 패키지의 에디션이 현재 에디션보다 오래되었는지 확인합니다. 그렇다면 해당 패키지를 건너뜁니다. 현재 에디션으로의 마이그레이션만 테스트하려는 것이기 때문입니다.
cargo fix --edition --allow-no-vcs --allow-dirty를 실행합니다.- 새 에디션을 설정하도록
Cargo.toml을 수정합니다. - 실제
cargo check가 실행될 수 있도록 특수 환경 변수를 설정하여cargo check를 실행합니다.
수정된 cargo는 cargo-features를 사용하지 않고도 새 에디션을 설정할 수 있게 해 줍니다.
마지막 cargo fix 또는 cargo check 단계가 실패했는데 이전 메인 브랜치 빌드에서는 검사가 성공했다면, 이는 마이그레이션이 실패한 회귀임을 나타냅니다.
그런 다음 에디션 프로젝트 그룹이 보고서를 분석하고, 문제가 있으면 이슈를 등록하며, 문제가 수정되도록 각 팀과 후속 조치를 진행할 책임을 집니다.
문제가 수정되면 다시 테스트해야 하므로 이 과정을 여러 번 반복해야 할 수도 있습니다.
에디션에 포함된 변경 사항이 얼마나 많은지에 따라 이를 실행하고 보고서를 분석하는 과정이 오래 걸릴 수 있다는 점에 유의하십시오. 2021 에디션은 약 한 달이 걸렸으며, 여기에는 수백 건의 회귀를 분석하고, 근본 원인을 파악하고, 수정 사항이 구현된 후 crater를 다시 실행하는 작업이 포함되었습니다.
에디션 안정화하기
에디션 팀이 안정화를 승인하면 실제 안정화 작업은 누구나 수행할 수 있습니다. 에디션을 안정화하려면 컴파일러, 문서, 모든 도구를 업데이트해야 합니다.
- rustc에서 안정화합니다. rustc 안정화 지침을 참고하십시오.
- cargo에서 안정화합니다(rustc 업데이트가 nightly에 반영된 후). cargo 안정화 지침을 참고하십시오.
- cargo 서브모듈을 업데이트합니다.
- 에디션 가이드를 업데이트하여 안정화되었음을 표시합니다. Rust 2024의 예시입니다.
- 대기 중인 모든 에디션 PR을 레퍼런스에 병합하고, 새로 안정화된 에디션을 기본값으로 사용하도록 레퍼런스를 업데이트하십시오(Rust 2024 예시).
- 모든 책 서브모듈을 업데이트합니다.
- 새 에디션을 제대로 지원하는 mdbook의 새 버전을 게시합니다. Rust 2024의 예시입니다.
블로그 게시물 및 공지
에디션 프로젝트 그룹과 관련 팀은 모든 사람과 일찍부터 자주 소통할 것을 강력히 권장합니다. 주요 마일스톤은 Rust 블로그에 공지해야 합니다. 전체 진행 상황과 일정에 관한 최신 소식을 담은 Inside Rust 블로그 게시물을 정기적으로(예: 매월) 작성해야 합니다.
적어도 공개 메시지에서는, 이상적으로는 모든 곳에서 에디션이 무엇이며 어떻게 동작하는지(코드가 깨지지 않습니다! 생태계가 분열되지 않습니다!) 거듭 설명하십시오. 혼란은 항상 생깁니다. 제 기억이 맞다면, 지난번에는 몇몇 기자가 사실을 확인하려고 Rust 재단에 연락하기도 했습니다. 재단 팀도 질문을 받게 될 것이므로, 에디션 프로젝트 그룹은 공개 메시지와 관련하여 재단 팀과 조율해야 합니다.
추적 도구
에디션 프로젝트 그룹은 에디션의 진행 상황을 추적하는 데 어떤 도구를 사용할지 결정해야 합니다. 개별 팀도 각자에게 가장 적합한 도구를 선택하고 싶어 할 것입니다. 과거에는 GitHub 프로젝트, GitHub 이슈 레이블, Google Sheets, HackMd 등 여러 도구를 함께 사용했습니다. 편한 도구를 사용하되, 공개적으로 접근할 수 있어야 한다는 점을 유념하십시오.
예시:
- 2018 추적 이슈
- 2018 GitHub 프로젝트
- 2021 추적 스프레드시트
- 2021 GitHub 프로젝트
- 2021 HackMd
- 2021 추적 이슈
- 2024 추적 이슈
- 2024 HackMd
개별 팀의 추적 예시:
구현 참고 사항
개별 팀과 프로젝트에는 에디션 변경 사항을 구현하는 방법에 관한 각자의 자료가 있습니다. 에디션 변경 사항이 어떻게 구현되는지 알아보려는 경우 참고할 만한 추가 정보 링크는 다음과 같습니다.
- 기반 시스템이 어떻게 동작하는지 다시 살펴보려면 마이그레이션 동작 방식을 참고하십시오.
- rustc-dev-guide에는 rustc에서 에디션별 변경 사항을 구현하는 방법에 관한 정보를 담은 에디션 장이 있습니다.
- Rust 스타일 가이드에는 에디션별 스타일 변경 사항을 정의하는 Rust 스타일 에디션에 관한 정보가 있습니다.
역사적 배경
아카이브
이 섹션은 더 이상 최신 정보가 아니지만, 역사적·기록 보존 목적으로 계속 열람할 수 있도록 남겨 두고자 하는 내용을 위한 곳입니다.
커뮤니티
이 섹션은 현재 해산되어 더 이상 활동하지 않는 커뮤니티 팀의 아카이브입니다. 아래 링크는 오래되었거나 보관된 프로젝트로 연결될 수 있습니다.
외부 링크
- 커뮤니티 팀 GitHub 저장소에는 커뮤니티 팀이 어떻게 조직되는지에 관한 정보가 담겨 있습니다.
- RustBridge 웹사이트에는 여러분의 지역에서 직접 RustBridge 행사를 주최하는 방법에 관한 정보가 담겨 있습니다.
- Rustlings는 새로 온 분들이 Rust 코드를 읽고 쓰는 데 익숙해지도록 설계된 작은 연습 문제들로 구성된 프로젝트입니다.
- State of Rust는 Rust 사용자와, 더 넓게는 Rust의 미래에 관심이 있는 모든 분들로부터 통찰과 피드백을 수집하는 연례 커뮤니티 설문 조사입니다.
나무의 친구들
Rust 팀은 Rust 프로젝트와 그 생태계, 그리고 커뮤니티에 뛰어난 기여를 한 사람들을 때때로 기리고 있습니다. 이분들이 바로 ’나무의 친구들’이며, 그 영광을 영원히 기리기 위해 이곳에 기록합니다.
2016-02-26 @mitaa
이번 주에는 @mitaa를 나무의 친구로 추천하고자 합니다. 최근 @mitaa는 rustdoc에 수정의 물결을 보내왔으며(네, 모두 별개의 링크입니다) 앞으로도 더 많은 수정이 올라올 예정입니다! Rustdoc은 오래전부터 관심과 손길이 필요한 도구였기에, 버그 수정을 위한 추가적인 도움이 특히 고맙습니다. 고맙습니다, @mitaa!
2016-02-12 Jeffrey Seyfried (@jseyfried)
이번 주의 나무의 친구는 Jeffrey Seyfried (@jseyfried)입니다!
Jeffrey Seyfried (@jseyfried)는 이름 해석(name resolution)에 훌륭한 기여를 해 주었습니다. 그는 수많은 버그를 수정하고, 이전에 알려지지 않았던 엣지 케이스를 보고했으며, 대규모 리팩터링도 여러 차례 진행했습니다. 이 모든 작업은 컴파일러에서 복잡하면서도 다소 소홀히 다뤄지던 부분을 개선하는 데 도움이 되었습니다.
2015-12-04 Vadim Petrochenkov @petrochenkov
이번 주에는 @petrochenkov를 나무의 친구로 추천하고자 합니다. Vadim은 최근 프라이버시 버그 수정, 위생성(hygiene) 버그 수정, 패턴 버그 수정, 기반 마련 및 #[deprecated] 구현, 여러 프라이버시 허점의 수정 및 차단, HIR의 리팩터링 및 개선, 그리고 오래된 타입 표기(type ascription) PR의 부활 등 정말 놀라운 컴파일러 작업을 해 왔습니다. 이제 컴파일러에 남아 있는 버그와 프로젝트 목록이 점점 줄어들고 있습니다. 고맙습니다, @petrochenkov!
2015-11-16 Peter Atashian (WindowsBunny, retep998)
WindowsBunny는 스스로를 “Windows 사용자가 겪을 수 있는 모든 문제와 그 해결 방법을 담은, 깡충깡충 뛰어다니는 백과사전“이라고 소개합니다. Rust가 Windows에서 동작하도록 만드는 영웅 중 한 명인 그는 이 플랫폼에서 Rust가 할 수 있는 일의 한계를 적극적으로 넓혀 가고 있습니다. 특히 그는 Windows 시스템 API에 대한 포괄적인 바인딩 모음인 winapi 계열 크레이트의 메인테이너이기도 합니다. WindowsBunny, 당신이야말로 최고의 토끼입니다. 그리고 나무의 친구이기도 합니다.
출처.
2015-10-31 Marcus Klaas
오늘 @nrc는 @marcusklaas를 나무의 친구로 추천하고자 합니다:
Marcus는 rustfmt의 주요 저자 중 한 명입니다. 그는 초창기부터 참여해 왔으며 현재 최다 기여자입니다. 그는 셀 수 없이 많은 버그를 수정하고, 새로운 기능을 구현하고, 엄청난 양의 PR을 리뷰했으며, 프로젝트 설계에도 기여했습니다. 그의 노고가 없었다면 Rustfmt는 지금과 같은 소프트웨어가 되지 못했을 것입니다. 그는 진정한 나무의 친구입니다.
2015-10-16 Ryan Prichard
nmatsakis는 Ryan Prichard를 트리의 친구로도 선정하고자 합니다. 지난 몇 달 동안 Ryan은 Rust 컴파일러의 파싱 동작을 rust-grammar 프로젝트의 파싱 동작과 비교해 왔습니다. rust-grammar 프로젝트는 Rust를 파싱하기 위한 LALR(1) 문법을 만드는 것을 목표로 합니다. Ryan은 둘 사이에서 여러 불일치와 버그를 발견했습니다. 이러한 작업이 유용한 이유는 두 가지입니다. 첫째, 당연하게도 다른 방법으로는 찾아내기 어려운 경우가 많은 버그를 찾아냅니다. 둘째, 컴파일러 소스 자체와 별개인 진정한 Rust 참조 문법을 마련하는 길을 닦는 데 도움이 됩니다. Ryan Prichard 님, 감사합니다!
2015-10-02 Vikrant Chaudhary
Vikrant Chaudhary (nasa42)는 Rust 커뮤니티를 믿는 사람입니다. 그는 6월부터 This Week in Rust에 기여하며 urlo에서 발행을 조율하고 기여를 독려해 왔습니다. 최근에는 사이트 디자인을 전면 개편하여 메인 웹사이트와 더 잘 어울리도록 했습니다. 현재 Vikrant는 llogiq와 다른 기여자들의 도움을 받아 이 주간 뉴스레터의 주 편집자를 맡고 있습니다. TWiR를 꾸준히 이어 주셔서 감사합니다, 트리의 친구 Vikrant 님.
출처.
2015-07-24 Tshepang Lekhonkhobe
이번 주에는 @Gankra가 @tshepang을 트리의 친구로 추천했습니다.
지난 한 해 동안 Tshepang은 우리 문서에 100건이 넘는 개선 사항을 반영했습니다. Tshepang은 문서가 없는 곳을 보고 “안 됩니다. 이대로는 안 됩니다.“라고 말했습니다.
우리 모두 Tshepang만큼 문서에 관심을 기울이도록 노력해야 합니다.
출처.
2015-05-19 Chris Morgan
오늘은 Chris Morgan(@chris-morgan)을 트리의 친구로 추천하고자 합니다. Chris는 최근 1.0 릴리스에 맞춰 play.rust-lang.org 사이트를 새로 디자인하여 사이트에 더 현대적이고 Rust다운 느낌을 더했습니다. Chris는 꽤 오래전부터 Rust에 기여해 왔습니다. 첫 기여는 2013년 7월로 거슬러 올라가며, Rust로 작성된 HTTP 라이브러리 분야의 초기 개척자 중 한 명이기도 합니다. Chris는 진정한 트리의 친구입니다!
2015-03-24 Andrew Gallant (BurntSushi)
BurntSushi는 사실상 따로 소개가 필요 없는 사람입니다. 그는 docopt.rs, regex, quickcheck, cbor, byteorder를 비롯해 세계에서 가장 인기 있는 크레이트를 여럿 작성했습니다. rust-csv를 기반으로 만든 그의 CSV 만능 도구 xsv도 잊지 마십시오. 라이브러리에 관한 그의 초기 작업에서 나온 피드백은 Rust 개발의 중요한 시기에 Rust의 발전 방향을 잡는 데 도움이 되었으며, BurntSushi는 숙련된 friendofthetree만이 만들어 낼 수 있는 Rust의 보석 같은 결과물을 계속해서 쏟아내고 있습니다.
2015-03-03 Manish Goregaokar (Manishearth)
Manish는 2014년 GSoC 프로그램을 통해 Servo 작업을 시작했으며, 이때 XMLHttpRequest를 구현했습니다. 그 뒤로 그는 대학 학업을 마치고 Rust 커뮤니티 행사를 조직하는 동안 Servo 팀에서 없어서는 안 될 구성원이 되었습니다. 2015년에는 bors의 큐에 관심을 갖게 되었고, 통합 과정을 앞당기기 위해 롤업 PR을 만들기 시작했습니다. PR 큐를 돌보는 일은 시간이 많이 드는 노동이며, 바로 이러한 노동이 롤업 트리의 친구인 Manish와 같은 트리의 친구를 만들어 냅니다.
2015-02-17 Toby Scrace
오늘 저는 Toby Scrace를 트리의 친구로 추천하고자 합니다. Toby는 주말 동안 crates.io의 로그인 취약점을 이메일로 알려 주었습니다. 이 취약점은 GitHub 인증이 성공했는지와 관계없이 직전에 로그인했던 사용자로 로그인할 수 있는 문제였습니다. Toby가 공개하기 전에 비공개로 이메일을 보내 준 데 깊이 감사드리며, Toby는 트리의 친구가 될 자격을 충분히 갖추었다고 확신합니다.
2015-02-10 Jonathan Reem (reem)
Jonathan Reem은 2014년 5월부터 Rust에 영향을 미쳐 왔습니다. 그의 가장 큰 기여는 널리 알려진 Iron 웹 프레임워크의 주 저자로서 한 일이며, 테스트 프레임워크인 stainless를 비롯한 여러 인기 프로젝트도 만들었습니다. 이러한 프로젝트에서 쌓은 실무 경험은 업스트림 rust의 여러 개선으로 이어졌으며, 그중 가장 두드러진 것은 TaskPool 타입을 완전히 새로 작성한 일입니다. Reem은 Rust의 발전을 위해 할 수 있는 모든 일을 하고 있습니다.
2015-01-20 Barosl Lee (barosl)
오늘 저는 Barosl Lee(@barosl)를 트리의 친구로 추천하고자 합니다. Barosl은 최근 저희 bors cron 작업을 homu라는 새 프로젝트로 다시 작성했습니다. Homu에는 다음과 같은 여러 장점이 있습니다.
- 서로 다른 PR을 테스트하는 사이의 “다운타임“이 전혀 없습니다(bors는 30분 이상 걸렸습니다!).
- 다른 PR들을 모아 별도의 롤업 PR을 만드는 새 롤업 버튼이 있습니다.
- 여러 저장소를 지원합니다(Cargo와 Rust를 한곳에서 함께 관리합니다).
Barosl이 여러 이슈를 해결해 준 덕분에 Homu는 최근 rust-lang/rust에 배포되었으며, 지금까지 훌륭하게 작동하고 있습니다! 또한 Barosl은 새로 생기는 이슈마다 매우 신속하게 대응해 왔습니다. Barosl은 진정한 트리의 친구입니다!
2015-01-13 강성훈 (lifthrasiir, Yurume)
성훈 님은 2013년 초부터 Rust 커뮤니티에서 활발히 활동해 왔으며, Rust 자체에도 여러 가치 있는 기여를 했지만 가장 큰 업적은 트리 바깥에서 핵심 라이브러리를 개발한 것입니다. Cargo에서 가장 인기 있는 크레이트 중 하나인 rust-encoding은 문자 인코딩을, rust-chrono는 날짜/시간 처리를 담당하며, 둘 다 표준 라이브러리 기능의 중요한 빈틈을 메워 줍니다. rust-strconv는 효율적인 숫자-문자열 변환의 프로토타입으로, 앞으로 표준 라이브러리에 포함될 후보입니다. 그는 자신의 작업을 소개하는 블로그를 운영하고 있습니다.
2015-01-06 Jorge Aparicio (japaric)
Jorge Aparicio(japaric)를 Friend of the Tree로 추천합니다(무려 두 번째 추천입니다!). japaric은 이제 사용할 수 있게 된 새로운 언어 기능을 쓰도록 코드베이스를 옮기는 데 엄청난 작업을 해냈습니다. 먼저, 그는 DST가 도입된 뒤 표준 라이브러리의 API가 DST를 최대한 활용하도록 바꿨습니다. 다음으로, 그는 API가 unboxed 클로저를 사용하도록 바꿨습니다. 그다음, 그는 라이브러리의 상당 부분이 연관 타입을 사용하도록 바꿨습니다. 마지막으로, 그는 컴파일러에서 boxed 클로저를 완전히 제거했습니다. 그는 또한 연산자 오버로딩과 비교 트레이트를 변경하는 RFC를 적용하는 작업도 했으며, 여기에는 그 정의뿐 아니라 표준 라이브러리에 미치는 영향까지 모두 포함됩니다. 게다가 이 목록에는 오래된 문법을 사용 중단(deprecate)하는 것과 같은 여러 작은 변경 사항은 들어 있지 않습니다. 그가 없었다면 알파 릴리스는 지금의 모습이 되지 못했을 것입니다. Japaric은 그야말로 트리가 만난 최고의 친구 중 한 명입니다.
2014-12-30 Kevin Ballard (kballard, Eridius)
뒤늦게나마 Kevin Ballard(일명 @kballard, 일명 Eridius)를 트리의 친구로 인정합니다. Kevin은 Rust의 유니코드 문제, 특히 플랫폼별 제약과 관련된 문제에 많은 노력을 쏟았습니다. 그는 부분적으로는 이러한 제약을 수용하기 위해 현재의 path 모듈을 작성했으며, 최근 이 모듈의 재설계에도 참여했습니다. 그는 또한 헌신적이고 꼼꼼한 리뷰어였습니다. Kevin, 기여해 주셔서 감사합니다!
2014-12-16 Gábor Lehel (glaebhoerl)
Gabor가 Rust에 주로 기여한 분야는 언어 설계입니다. 지난 한 해 동안 그는 매우 수준 높은 RFC를 여럿 작성했습니다. 그중 상당수가 아직 채택되지 않았지만, 그의 아이디어는 종종 깊은 생각을 불러일으켰고 언어의 방향에 큰 영향을 미쳤습니다. 그의 트레이트 기반 예외 처리 RFC는 특히 혁신적이었으며, checked arithmetic의 미래 대비를 위한 RFC도 마찬가지였습니다. Gabor는 대단히 명석한 Friend of the Tree입니다.
2014-11-11 Brian Koropoff (unwound)
지난 몇 주 동안 그는 컴파일러 전반에 걸쳐 까다로운 ICE를 정말 많이 고쳤으며, 특히 unboxed 클로저와 borrow checker 영역에서 많이 고쳤습니다. 그는 또한 unboxed 클로저가 단형화(monomorphization)와 상호 작용하는 방식을 완전히 다시 작성했고, unboxed 클로저를 실제로 쓸 수 있게 만드는 데 크게 기여했습니다. Brian Koropoff는 진정한 Friend of the Tree입니다.
2014-10-07 Alexis Beingessner (Gankra)
Alexis Beingessner(일명 @Gankra)는 7월에 Rust에 기여하기 시작했으며, 벌써 라이브러리 관련 여러 분야에 큰 영향을 미쳤습니다. 그녀가 주로 집중한 분야는 컬렉션입니다. 그녀는 BTree를 완전히 다시 작성하여 훨씬 더 완전하고 효율적인 구현을 제공했습니다. 그녀는 새로운 Entry API를 제안하고 구현했습니다. 그녀는 collections 크레이트에 대한 방대한 새 문서를 작성했습니다. 그녀는 컬렉션 개편에도 힘을 보탰습니다.
그리고 그녀는 rustdoc에 모두 접기(collapse-all) 기능을 추가했습니다!
Alexis는 의심할 여지 없이 FOTT입니다.
2014-09-02 Jorge Aparicio (japaric)
Jorge는 Rust 커뮤니티 전반에 큰 영향을 미친 여러 기여를 했습니다. 그는 rustbyexample.com의 주 저자이며, 지난주에는 프로젝트 오일러(Project Euler) 문제를 기준으로 여러 언어의 성능을 비교한 “eulermark“를 공개했습니다. 반갑게도 이 비교에서 Rust는 꽤 좋은 성능을 보였습니다. 벤치마킹 작업의 일환으로 그는 ‘criterion’ 벤치마킹 프레임워크를 Rust로 포팅했습니다.
2014-07-29 Björn Steinbrink (dotdash, doener)
2013년 4월부터 기여해 왔습니다. Björn은 Rust에 많은 최적화를 적용했습니다. 이터레이터, fmt, 관리 박스(managed box)의 불필요한 할당 제거, fail! 최적화, 라이브러리의 전략적 인라이닝 추가, 컴파일러 자료 구조의 속도 개선, 번역 과정의 이차적 폭증과 그 밖의 IR 비대화 문제 해소 등이 그 예입니다.
그는 정말 놀라울 만큼 많은 최적화를 Rust에 적용했습니다.
가장 최근에는 LLVM이 변수의 수명을 알 수 있게 하여 Rust가 스택을 훨씬 더 효율적으로 사용할 수 있도록 함으로써 큰 찬사를 받았습니다.
Björn은 그야말로 진정한 FOTT입니다.
2014-07-22 Jonas Hietala (treeman)
@treeman으로도 알려진 Jonas Hietala는 최근 hashmap, treemap, priority_queue, collections, bigint, vec 같은 모듈에 대한 문서 예제를 대량으로 기여해 왔습니다. 그는 또한 format!과 관련된 버그를 비롯해 컴파일러의 UI 버그도 수정해 왔습니다.
Jonas는 매일 새로운 예제와 문서를 계속 추가하여, 모든 신규 참여자가 문서에 더 쉽게 다가가고 이해할 수 있도록 만들고 있습니다. Jonas는 진정한 트리의 친구입니다!
2014-07-08 Sven Nilson (bvssvni, long_void)
Sven Nilson은 Rust 크레이트 생태계를 구축하기 위해 많은 일을 해 왔습니다. 그 시작은 좋은 평가를 받은 rust-empty 프로젝트로, 이 프로젝트는 상용구 빌드 인프라를 제공하며, 무엇보다도 Cargo 같은 다른 도구와 잘 통합됩니다.
그의 Piston 프로젝트는 가장 유망한 Rust 프로젝트 중 하나이며, 여러 크레이트를 통합하는 프로젝트로서 대규모 외부 프로젝트를 지원하는 방법을 배우기 시작해야 하는 바로 적절한 시점에 Rust의 도구를 철저히 시험하고 있습니다.
Sven은 트리의 친구입니다.
2014-06-24 Jakub Wieczorek (jakub-)
Jakub Wieczorek로도 알려진 jakub-는 최근 match 관련 기능을 개선하고 많은 부분을 수정하기 위해 매우 열심히 작업해 왔습니다. 이 영역은 감히 발을 들이는 사람이 거의 없는 곳입니다! 이 코드의 대부분은 꽤 오랫동안 손대지 않은 것으로 보이며, 이제야 마땅히 받아야 할 관심을 받고 있습니다.
Jakub은 이번 달에만 10개의 버그를 수정했으며, 그중 상당수는 컴파일러에 오랫동안 남아 있던 문제였습니다. 그는 또한 버그 수정뿐만 아니라 흥미로운 match 어서션에서 발생하는 이슈를 트리아지하는 데에도 매우 신속하게 대응해 왔습니다.
Jakub은 진정한 트리의 친구입니다!
2014-04-22 klutzy
klutzy는 수년째 놀라울 만큼 많은 Windows 관련 작업을 해 오고 있습니다. 그는 Windows에서의 품질에 영향을 미치는 이슈를 찾아 하나씩 해결해 나가고 있습니다. 이는 지루한 일이고 많은 감사를 받지도 못하지만, 저희는 이를 대단히 고맙게 여기고 있습니다. 그는 한국 커뮤니티의 일원으로서 그곳 현지 커뮤니티를 위해서도 많은 일을 해 왔습니다. 그는 트리의 친구입니다. 감사합니다!
- Windows용 Rust의 투사
- x86 C ABI 구조체 인자 관련 문제를 수정했습니다
- 미국 외 로케일 관련 여러 문제를 수정했습니다
2014-03-18 Clark Gaebel (cgaebel)
이번 주 트리의 친구는 Clark Gaebel입니다. 그는 첫 기여로 Rust에 대규모 변경을 막 반영했습니다. 그는 적극적으로 뛰어들어 Robin Hood 해싱을 구현함으로써 해시맵을 크게 빠르게 만들었습니다. 그는 훌륭한 트리의 친구입니다.
2014-02-25 Erick Tryzelaar (erickt)
- 2011년 5월부터 기여하고 있습니다
- 직렬화 크레이트를 작성했습니다
- 베이 에어리어 Rust 밋업을 주최합니다
- 얼마 전 Hash 트레이트를 다시 작성했습니다
2014-02-11 Flavio Percoco (FlaPer87)
- 9월부터 기여하고 있습니다
- 이슈 트리아지를 맡고 있습니다
- 이탈리아에서 커뮤니티 행사를 주최하고 있습니다
- ‘pow’ 함수를 최적화했습니다
- 최근 작지만 중요한 버그를 많이 수정하고 있습니다
2014-01-27 - Jeff Olson (olsonjefferey)
- 2012년 2월부터 기여하고 있습니다
- 최초의 libuv 통합 작업을 했습니다
- libuv를 처음으로 사용한, 두 번째 I/O 구현 시도를 구현했습니다
- C++ 런타임의 일부를 Rust로 이식했습니다
- 최신 런타임의 파일 I/O를 구현했습니다
- 지난주 Safari books 블로그에 파일 I/O에 관한 글을 게재했습니다
2014-01-21 - Steven Fackler (sfackler)
- 작년 5월부터 기여하고 있습니다
- CMU 졸업생입니다
- Base64, Bitv, I/O 등 라이브러리를 많이 개선했습니다
- Rustdoc을 개선했습니다
- Mut/RefCell
- std::io::util
- 외부 모듈 로딩
2014-01-14 - Eduard Burtescu (eddyb)
- 10월부터 기여하고 있습니다
- trans를 포함한 컴파일러 작업을 하고 있습니다
- rustc 메모리 사용량을 줄였습니다
- 벡터 연산을 최적화했습니다
- 더 이상 권장되지 않는 기능을 사용하지 않도록 컴파일러를 리팩터링하는 작업을 돕고 있습니다
- 컴파일러에 남아 있던 오래된 코드를 정리했습니다
- self 매개변수를 전달하기 위해 환경 인수를 잘못 사용해 오던 오랜 방식을 제거했습니다
2014-01-07 - Vadim Chugunov (vadimcn)
- 6월부터 기여하고 있습니다
- Windows에서 발생하던 수많은 버그를 수정했습니다
- 깨진 테스트를 고치고 있습니다
- 최신 mingw 버전과의 호환성을 개선했습니다
- libunwind를 이용한 언와인딩을 구현하여 런타임의 C++ 의존성을 제거했습니다
Rust 릴리스 기록
이곳은 0.1부터 1.7.0까지의 Rust 릴리스 산출물을 모아 둔 보관소입니다. 각 릴리스는 Rust GPG 서명 키(이전 키, 그보다 더 이전 키)로 서명되어 있습니다.
1.7.0
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows x86_64 .exe gnu 설치 프로그램 (서명)
- Windows x86_64 .msi gnu 설치 프로그램 (서명)
- Windows x86_64 .exe MSVC 설치 프로그램 (서명)
- Windows x86_64 .msi MSVC 설치 프로그램 (서명)
- Windows i686 .exe gnu 설치 프로그램 (서명)
- Windows i686 .msi gnu 설치 프로그램 (서명)
- Windows i686 .exe MSVC 설치 프로그램 (서명)
- Windows i686 .msi MSVC 설치 프로그램 (서명)
- Linux x86_64 tarball (서명)
- Linux i686 tarball (서명)
- Mac OS X i686 pkg (서명)
- Mac OS X i686 tarball (서명)
- Mac OS X x86_64 pkg (서명)
1.6.0
1.5.0
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
1.0.0-beta
1.0.0-alpha.2
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows x86_64 .exe 설치 프로그램 (서명)
- Windows i686 .exe 설치 프로그램 (서명)
- Windows x86_64 .msi 설치 프로그램 (서명)
- Windows i686 .msi 설치 프로그램 (서명)
- Linux x86_64 tarball (서명)
- Linux i686 tarball (서명)
- Mac OS X x86_64 pkg (서명)
- Mac OS X i686 pkg (서명)
- Mac OS X x86_64 tarball (서명)
- Mac OS X i686 tarball (서명)
- 문서
1.0.0-alpha
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows x86_64 설치 프로그램 (서명)
- Windows i686 설치 프로그램 (서명)
- Linux x86_64 tarball (서명)
- Linux i686 tarball (서명)
- Mac OS X x86_64 pkg (서명)
- Mac OS X i686 pkg (서명)
- Mac OS X x86_64 tarball (서명)
- Mac OS X i686 tarball (서명)
- 문서
Rust 0.x
메일링 리스트에 포함된 짧은 형식의 릴리스 안내 외에도, 각 0.x 릴리스에는 릴리스 노트에 더 자세한 설명이 있습니다.
0.12.0
- 발표
- 릴리스 노트
- 소스 코드 (서명)
- Windows x86_64 설치 프로그램 (서명)
- Windows i686 설치 프로그램 (서명)
- Linux x86_64 tarball (서명)
- Linux i686 tarball (서명)
- Mac OS X x86_64 pkg (서명)
- Mac OS X i686 pkg (서명)
- Mac OS X x86_64 tarball (서명)
- Mac OS X i686 tarball (서명)
- 문서
0.11.0
- 발표
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- Linux x86_64 tarball (서명)
- Linux i686 tarball (서명)
- Mac OS X x86_64 pkg (서명)
- Mac OS X i686 pkg (서명)
- Mac OS X x86_64 tarball (서명)
- Mac OS X i686 tarball (서명)
- 문서
0.10
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- Linux x86_64 tarball (서명)
- Linux i686 tarball (서명)
- Mac OS X x86_64 pkg (서명)
- Mac OS X i686 pkg (서명)
- Mac OS X x86_64 tarball (서명)
- Mac OS X i686 tarball (서명)
- 문서
0.9
0.8
0.7
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- 매뉴얼 (PDF)
- 표준 라이브러리 문서
- 추가 라이브러리 문서
0.6
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- 매뉴얼 (PDF)
- 코어 라이브러리 문서
- 표준 라이브러리 문서
0.5
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- 매뉴얼 (PDF)
- 코어 라이브러리 문서
- 표준 라이브러리 문서
0.4
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- 매뉴얼 (PDF)
- 코어 라이브러리 문서
- 표준 라이브러리 문서
0.3.1
OS X 버그 수정 릴리스였습니다.
0.3
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- 매뉴얼 (PDF)
- 코어 라이브러리 문서
- 표준 라이브러리 문서