Rust Forge
Rust Forge에 오신 것을 환영합니다! Rust Forge는 The Rust Programming Language 구성원에게 유용한 보충 문서 저장소 역할을 합니다. 오류나 오타를 발견하셨거나 Rust Forge에 내용을 추가하고 싶으시면 on GitHub에서 자유롭게 이슈나 PR을 제출해 주십시오.
도움이 필요합니다
Rust에 기여하고 싶지만 어디서부터 시작해야 할지 모르십니까? 이 가이드를 확인하십시오.
현재 릴리스 버전
| 채널 | 버전 | 다음 날짜에 stable로 전환됩니다 | main에서 브랜치가 생성되는 시점 |
|---|---|---|---|
| Stable | |||
| Beta | |||
| Nightly | |||
| Nightly +1 |
릴리스에 앞서 며칠 동안 어떤 일이 일어나는지에 대한 자세한 내용은 릴리스 프로세스 문서를 참고하십시오.
도구 손상 없는 주간
beta 릴리스에 모든 도구가 포함되도록 보장하기 위해, beta 컷오프 이전 한 주 동안은 (nightly 전용 도구를 제외하고) tool breakages가 허용되지 않습니다.
| Beta Cut | 브레이킹 없는 주간 |
|---|---|
외부 링크
- Rust에 영향을 미친 연구 논문 및 기타 프로젝트의 Bibliography입니다.
- 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에서 팀에 연락하십시오(더 알아보려면 여기를 참고하십시오)
- Bootstrap 팀: 팀의 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 playground | 도움이 필요합니다 | Rust 온라인 playground | |
| 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의 최종 의견 수렴 기간(Final Comment Periods)에 합리적인 시간 내에 응답할 것
트리아지 합류하기
위 사항의 한 가지 예외는 트리아지 팀으로, 프로젝트 참여를 위한 입문 과정으로 적극 추천합니다. 이 팀은 rust-lang/rust 저장소의 이슈와 PR을 트리아지하는 작업을 하며, 프로그래밍이나 컴파일러에 대한 사전 경험을 요구하지 않습니다. Rust 프로젝트와 상호작용한 적이 있다면 언제든 트리아지 팀에 합류하십시오(우리가 알아볼 정도로 자주 상호작용하는 것이 권장되지만 필수는 아닙니다).
이 팀에 합류하려면 Dylan-DPC에게 이야기한 다음 rust-lang/team에 PR을 열기만 하면 됩니다. 어떤 변경을 해야 하는지에 대한 예시는 team#2447을 참고하십시오.
wg-triage에 대한 더 자세한 정보는 Triage Procedure를 참고하십시오.
팀에 속하지 않고 구성 요소 유지 관리하기
특정 모듈이나 파일에 큰 기여를 했지만 팀에 추가될 만큼 오래 기여하지 않았다면, 주로 작업한 파일/디렉터리에 대해 triagebot 멘션을 설정하는 것이 유익할 수 있습니다. 이를 통해 해당 파일을 변경하는 모든 PR에 대해 알림을 받고 피드백이나 맥락을 제공할 수 있으며, 이는 작성자가 더 빠르게 피드백을 받고 리뷰어가 해야 할 작업량을 줄이는 데 도움이 됩니다.
위원회와의 관계
리더십 위원회는 필요할 때 프로젝트를 대표하여 공식적으로 입장을 취합니다. 모든 Rust 프로젝트 구성원은 대략 한 명의 위원회 구성원에 의해 공식적으로 대표됩니다(일부 팀은 두 명 이상의 구성원을 상위로 둡니다).
일반적으로, 더 광범위하게 적용되는(즉, 자신의 팀에만 국한되지 않는) 우려 사항이나 자신의 팀 내에서 충분히 처리되지 않는다고 느끼는 우려 사항은 위원회로 상정할 수 있습니다. 대인 관계 문제 및/또는 행동 강령 위반은 항상 모더레이션 팀으로 향해야 함에 유의하십시오.
위원회로의 상정은 다음을 통해 할 수 있습니다:
- GitHub에서 새 이슈를 통해 하는 방법. 이는 정기 이사회 회의에서 논의됩니다(모든 프로젝트 구성원이 참관 초대됩니다). 이는 정기 위원회 회의에서 논의될 것입니다(모든 프로젝트 구성원이 참관 초대됩니다).
- Zulip의 #council 채널을 이용하십시오. 이는 위원회에 관한 사안을 다룹니다.
- Zulip DM으로는 자신의 위원회 대표에게 보낼 수 있습니다(각 대표가 어느 팀을 대표하는지는 council을 참고하십시오).
둘 다 합리적인 출발점이 될 수 있으니 더 편하게 느껴지는 쪽을 선택하시되, 공개적인 방식을 선호하는 편이 좋습니다.
리더십 위원회 직위는 프로젝트 팀들에 의해 순환 일정에 따라 선출됩니다. 자세한 내용은 Council term limits를 참고하십시오.
재단과의 관계
재단은 프로젝트를 지원하기 위해 활동하며, 프로젝트는 재단 이사회에 직접 대표권(프로젝트 이사 5명)을 가지고 있습니다. 그 이사들은 bylaws에 따라 리더십 위원회에 의해 선정됩니다.
이 이사들은 프로젝트 소속이 아닌 이사의 수와 관계없이 재단의 이사회 표결에서 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 볼트를 통해 함께 부여되며, 더 세분화된 권한으로 나누지는 않습니다. 일부 자동화는 status 계정들의 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@로 메시지를 보낼 때는 all@을 To에 넣지 마십시오. 그렇게 하면 여러분의 공지에 대한 모든 답장 또한 전체 구성원에게 전송됩니다. 대신 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 등)은 인프라 팀의 재량에 따라 작업에 필요한 어떤 수준의 접근 권한도 부여받을 수 있습니다.
-
team 저장소가 관리되는 방식에 관한 정책은 Team Maintenance를 참고하십시오. ↩
Zulip
Rust의 Zulip은 Rust 팀 구성원들이 Rust의 개발을 논의하는 데 사용하는 채팅 플랫폼입니다.
Zulip은 처음 시작하기에 다소 직관적이지 않은 플랫폼일 수 있습니다. 시작하려면 시작 가이드를 살펴보십시오. 더 자세한 내용은 Zulip 사용자 문서를 살펴보십시오!
저희는 모든 Project 구성원과 연락할 수 있는 방법을 확보하기 위해, 모든 Rust Project 구성원이 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을 사용하고자 한다면, 오픈소스를 위한 무료 이용이 가능합니다.
새 스트림이 생성되면, #announce에서 이를 공지해야 합니다. 이는 일반적으로 Zulip에 의해 자동으로 이루어집니다.
토픽
토픽은 주어진 스트림 내의 모든 메시지에 첨부됩니다(이는 스트림 내부의 세부 구분입니다). 토픽은 일반적으로 일시적이며, 해당 토픽에 대한 활발한 논의가 있는 동안만 유지됩니다. 토픽을 이메일 제목처럼 생각하면 도움이 됩니다.
주어진 스트림에서의 새로운 대화는 거의 항상 기존 토픽이 아닌 새 토픽에서 시작해야 합니다. (예를 들어) GitHub 이슈와 달리, 동일한 주제에 대한 과거 토픽을 검색하려 해서는 안 됩니다. 토픽 이름을 짧게 만들려는 것 이상으로 이름을 정하는 데 너무 많은 시간을 들이지 마십시오. 토픽은 일반적으로 20자를 넘지 않아야 하며(대략 두세 단어 정도), 사용자에게 잘 보이도록 해야 합니다.
새로운 논의는 적극적으로 새 토픽으로 분기해야 합니다. 이는 (실수로 다른 논의 영역으로 벗어난 경우) 다른 토픽의 끝부분을 가지고도 수행할 수 있다는 점에 유의하십시오.
기존 토픽에서 분기하는 방법은 여기에서 Zulip의 문서를 참고하십시오.
메시지
Zulip은 동기식 통신과 비동기식 통신을 한곳에 결합한 독특한 플랫폼입니다. 일반적으로 메시지가 빠르게 응답을 받을 것이라고 기대해서는 안 되며, (예를 들어) Discord와 달리, 특정 이슈에 대해 몇 시간마다 “재알림“해야 할 이유가 별로 없습니다. 메시지가 특정 토픽에 국한되어 있어 히스토리 속으로 사라질 가능성이 낮기 때문입니다.
링크화 도구
저희 Zulip은 다양한 유용한 링크화 기능을 지원하며, 요청이 있으면 대체로 기꺼이 더 추가합니다. 형식에 대해서는 문서를 참고하십시오. #zulip에서 제안해 주십시오!
일반적으로, github-org/repo#123는 이슈나 PR에 링크하는 데 작동합니다. 아래 목록은 몇 가지 더 “특별 취급되는” 저장소를 제공합니다.
표준 Markdown 링크 문법도 작동한다는 점을 잊지 마십시오.
저희는 rust-lang/ 접두사 없이도 rust-lang GitHub 조직 내 저장소의 이슈에 링크하는 것을 지원합니다. 예를 들면:
- 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 archive였는데, 이는 이제 웹 공개 뷰로 리디렉션됩니다.
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 블로그의 많은 글이 개인 명의로 서명되며, 사실 이것이 선호되는 방식입니다. 하지만 이러한 글들은 대개 팀의 공식 입장을 문서화한 것입니다 — 좋은 예로 Aaron Turon의 고전적인 글인 Rust의 언어 인체공학 이니셔티브를 들 수 있습니다. 때로는 흥미로운 프로젝트를 설명하는 글도 있는데, 이 경우에도 프로젝트 전체를 대표하는 방식으로 작성됩니다(예: Manish Goregaokar의 Firefox Quantum에서의 두려움 없는 동시성 보고서).
메인 블로그와 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 | 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을 게시할 때 환영 인사를 처리합니다.
rust-lang/rust 저장소의 기여자는 Zulip 통합을 사용하여 자신의 작업 큐를 추적하고 관리할 수 있습니다. 리뷰 큐 추적을 참고하십시오.
rust-lang 조직에서 자신에게 할당된 풀 리퀘스트를 이 GitHub URL에서 확인할 수 있습니다.
사용법
새 PR의 자동 할당은 triagebot.toml의 설정으로 처리되며, 아래에서 설명합니다.
수동 할당은 PR에 다음 텍스트로 댓글을 게시하여 할 수 있습니다:
r? @octocat— 특정 사용자를 할당합니다.r? octocat—@는 선택 사항입니다.r? libs—triagebot.toml에 정의된 libs 애드혹 그룹에서 무작위로 사람을 선택합니다. 예를 들어 rust-lang/rust 저장소의 경우, ad-hoc 그룹 이름 목록은triagebot.toml을 참고하십시오.r? rust-lang/libs—rust-lang/조직 이름 접두사는 선택 사항입니다.r? rustdoc— rustdoc 팀에서 무작위로 한 사람을 선택합니다. 팀 이름 목록은 팀 데이터베이스를 참조하십시오.r? rust-lang/rustdoc— 조직 이름 접두사는 선택 사항입니다.@를 사용하지 않을 것을 강력히 권장하는데, 이는 팀 전체가 해당 PR을 구독하고 알림을 받게 되기 때문입니다.
팀에서 사용자를 선택할 때 triagebot은 직접 소속된 팀 구성원만 확인합니다(하위 팀은 무시합니다).
이름을 조회할 때 triagebot은 먼저 ad-hoc 그룹을, 그다음 rust-lang 팀을 확인하며, 둘 중 어느 것과도 일치하지 않으면 GitHub 사용자로 간주합니다.
풀 리퀘스트는 저장소에 쓰기 권한이 있는 사용자, 읽기 권한이 있는 rust-lang 조직 구성원, 또는 해당 풀 리퀘스트에 댓글을 남긴 사람에게만 할당될 수 있습니다.
사용자가 r? name 형태의 댓글을 게시하여 특정 사용자에게 할당을 설정할 수 있는 기능을 활성화하려면, triagebot.toml에 [assign] 테이블을 추가하십시오.
Ghost
풀 리퀘스트를 열 때 최초의 최상위 댓글에서 r? ghost를 사용하면 triagebot의 자동 할당이 비활성화됩니다. ghost는 삭제된 계정을 위한 GitHub의 대체 계정입니다. 여기서는 편의를 위해 사용됩니다. 이는 일반적으로 롤업이나 실험처럼 할당이나 알림을 원하지 않는 경우에 사용됩니다.
다시 굴리기
파일 diff를 기반으로 풀 리퀘스트를 다른 리뷰어에게 할당하고 싶다면(다시 말해, 명시적인 r? 없이 풀 리퀘스트를 열었을 때 일어나는 일을 재현하고 싶다면) @rustbot reroll 명령을 사용할 수 있습니다.
설정
[!NOTE] 이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
r?를 사용한 PR 할당은 triagebot.toml에 [assign] 테이블을 두어 저장소에서 활성화됩니다.
[assign.owners] 테이블이 있으면 triagebot은 풀 리퀘스트에서 수정된 파일을 기준으로 리뷰어를 자동으로 선택합니다.
# 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 통합을 사용하여 휴가 상태를 설정할 수도 있습니다.
커뮤니티 리뷰
저장소는 “커뮤니티 리뷰” 우선 방식을 설정할 수 있으며, 이 경우 (누구로부터든) 최소 개수의 풀 리퀘스트 리뷰 승인이 이루어질 때까지 리뷰어의 자동 할당이 지연됩니다.
[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>"
또한 풀 리퀘스트가 서브모듈을 수정하는 경우 triagebot은 경고 댓글을 게시합니다.
기본 브랜치 경고에 대한 예외
일부 풀 리퀘스트는 나머지 풀 리퀘스트와 다른 기본 브랜치를 가질 수 있으며, 이런 경우 풀 리퀘스트 제목을 기준으로 예외를 추가할 수 있어, 풀 리퀘스트가 지정된 것과 다른 브랜치를 대상으로 할 때 경고하게 됩니다.
[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와도 상호작용하는데, 이는 특정 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, backports 참고)에 백포트할지 여부를 평가합니다. 관련 팀이 수정 사항을 검토하여 백포트를 수락하거나 거부합니다.
중요한 회귀를 수정하는 패치에 더 많은 가시성을 부여하고 팀이 결정을 내리는 데 도움을 주기 위해, 저희 Zulip 채팅에 토론 주제가 자동으로 개설될 수 있습니다(예시).
백포트를 위한 풀 리퀘스트 자동 상정은 git 저장소의 모든 새 패치를 스캔하여 첫 코멘트에서 텍스트 마커(예: “Fixes #123”, GitHub documentation 참조)를 찾도록 설정할 수 있습니다. 이러한 텍스트가 발견되고 수정 대상 이슈가 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에 라벨을 자동으로 적용합니다.
사용법
자동 라벨은 수동으로 제어할 수 없습니다. 라벨을 수동으로 변경하려면 labeling을 참고하십시오.
설정
라벨에 의해 트리거됨
다른 레이블이 추가될 때 레이블을 추가할 수 있습니다. 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",
]
제외 라벨은 셸과 유사한 * 글롭 패턴을 지원합니다.
파일에 의해 트리거됨
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이 열릴 때 또는 이후 상태가 전환될 때 라벨을 추가할 수 있습니다. 해당 조건을 더 이상 충족하지 않으면 라벨이 제거됩니다.
이를 활성화하려면 new_pr = true 설정 옵션을 설정하십시오. 예를 들면 다음과 같습니다:
[autolabel."S-waiting-on-review"]
new_pr = true
새 초안 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의 코드가 업스트림 브랜치의 매우 오래된 커밋을 기반으로 할 때: 로컬에서 테스트하면 통과하지만, PR을 CI를 통해 테스트에 제출하면 실패합니다.
이는 CI가 현재 업스트림 브랜치에 커밋 패치를 적용하는데, 이 브랜치에 새로운 테스트 케이스가 있을 수 있어 통과하지 못하기 때문입니다. PR을 가장 가까운 업스트림 브랜치로 리베이스해야 합니다.
설정
현재 기본 임계값은 7일로 설정되어 있습니다.
이 기능은 저장소의 triagebot.toml에 [behind-upstream] 테이블을 두면 활성화됩니다.
[behind-upstream]
또는 일수 임계값을 직접 설정할 수 있습니다.
[behind-upstream]
days-threshold = 7
구현
src/handlers/check_commits/behind_upstream.rs를 참고하십시오.
이슈 링크 정규화
이 핸들러는 [issue-links]로 이름이 변경되었습니다. Issue Links 이슈를 참고하십시오.
닫기
close 명령은 GitHub 이슈 또는 풀 리퀘스트를 닫는 데 사용할 수 있습니다.
사용법
이슈나 풀 리퀘스트를 닫으려면 rust-lang 언어 팀 구성원이 다음 명령을 입력하면 됩니다:
@rustbot close
이렇게 하면 즉시 이슈 또는 PR이 닫힙니다.
설정
이 기능은 triagebot.toml에 [close] 테이블을 두면 저장소에서 활성화됩니다:
[close]
구현
src/handlers/close.rs 및 parser/src/command/close.rs를 참고하십시오.
우려 사항
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주마다 자동으로 rust-lang/rust에 모든 book 서브모듈을 업데이트하는 PR을 생성합니다. 이 PR은 수동 승인이 필요합니다. 이러한 업데이트는 현재 @ehuss가 관리하고 있습니다.
사용법
이 기능에는 설정이나 수동 제어 수단이 없습니다.
구현
src/handlers/docs_update.rs를 참고하십시오.
GitHub 릴리스
Triagebot은 태그가 푸시될 때 changelog의 관련 절을 릴리스 본문으로 사용하여 GitHub에 릴리스를 자동으로 생성하는 데 사용할 수 있습니다. 이 작업을 수행할 때 아티팩트는 업로드되지 않습니다.
사용법
git 태그를 푸시하거나 changelog의 내용을 갱신할 때마다, 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이 changelog를 찾을 때 살펴볼 위치를 정의합니다.
구현
src/handlers/github_releases.rs와 src/changelogs/를 참고하십시오.
Git rebase (범위) diff
강제 푸시가 PR의 베이스 커밋을 변경하면 GitHub 자체 비교 기능에는 관련 없는 변경 사항이 대량으로 표시됩니다. 이 핸들러는 그러한 상황이 발생한 후 triagebot의 range-diff 기능으로 연결되는 댓글을 게시합니다.
설정
[!NOTE] 이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
이 기능은 triagebot.toml에 빈 [range-diff] 테이블을 두면 해당 저장소에서 활성화됩니다:
[range-diff]
포함할 수 있는 선택적 키는 다음과 같습니다:
consider-push-from-bots(기본값:false) — 봇의 푸시를 range-diff 코멘트 대상으로 고려할지 여부입니다.
구현
src/handlers/check_commits/range_diff.rs를 참고하십시오.
이슈 링크
이슈 링크 정규화
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가 모든 데이터를 이전하는 데 잠시 시간이 걸릴 수 있습니다.
경고: 이전은 부분적으로 파괴적인 명령입니다. 예를 들어, 대상 저장소에 존재하지 않는 레이블과 마일스톤은 이슈에서 제거됩니다.
transfer 명령은 rust-lang 조직의 팀 구성원과 wg-triage, wg-async의 구성원으로 제한됩니다. 전송은 rust-lang 조직 내의 저장소로만 가능합니다. 또한, 대상 저장소에는 triagebot이 활성화되어 있어야 합니다.
설정
[!NOTE] 이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
원본 저장소에는 해당 저장소로부터의 이전을 활성화하기 위해 빈 transfer 테이블이 있어야 합니다. 이슈는 (triagebot이 활성화된) rust-lang 조직 내의 어떤 저장소로도 전송될 수 있습니다.
[transfer]
구현
parser/src/command/transfer.rs와 src/handlers/transfer.rs를 참고하십시오.
레이블링
댓글을 작성하여 이슈나 PR에 GitHub 레이블을 적용할 수 있습니다. 이슈에 레이블을 지정하면 검색, 이슈들 간의 연결, 상태와 같은 정보를 공식적인 방식으로 표시하는 데 매우 유용할 수 있습니다.
트리아지 워킹 그룹은 이슈에 레이블을 지정하는 작업을 돕습니다. 이슈 트리아지를 돕는 데 관심이 있으시다면, 트리아지 워킹 그룹 절차를 참고하십시오.
사용법
댓글의 일반적인 형식은 @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레이블-목록레이블-델타 →
+레이블
|-레이블
| 레이블
|"+레이블-quoted"
|"-레이블-quoted"
|"레이블-quoted"레이블-quoted → [^“]*
레이블 → [^.,:!?;\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)
]
별칭
이 설정은 별칭도 지원하는데, 별칭은 단일 명령으로 여러 레이블을 설정할 수 있도록 레이블 집합으로 확장되는 단일 단어이며, 동일한 레이블 집합을 반복해서 추가하거나 제거할 때 유용합니다. alias를 구성하려면 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]
구현
src/handlers/lock.rs와 parser/src/command/lock.rs를 참고하십시오.
주요 변경사항
Triagebot는 주요 변경 제안의 자동 처리를 돕습니다.
사용법
이 프로세스는 이슈에 적절한 레이블이 설정되면 시작됩니다. 예를 들어, rust-lang/compiler-team 저장소에는 major-change 레이블을 자동으로 설정하는 major change template이 있습니다. 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은 특정 파일을 건드리는 풀 리퀘스트에 댓글을 남길 수 있습니다. 이는 해당 파일에 대한 변경 사항을 검토하고 싶어 하는 사람들에게 알리거나, 작성자에게 안내 메시지를 제공하는 데 유용할 수 있습니다.
사용법
멘션은 저장소의 triagebot.toml 설정에 따라 풀 리퀘스트가 열릴 때(또는 새 변경 사항이 푸시될 때) 자동으로 트리거됩니다.
설정
멘션을 활성화하려면 triagebot.toml의 [mentions] 테이블에 항목을 추가하십시오.
테이블의 각 키는 저장소 내 경로이거나(type="content"인 경우) 문자열이어야 합니다. 자세한 내용은 아래 전용 절을 참고하십시오.
테이블에 지정할 수 있는 선택적 값은 네 가지입니다.
type— 충족되어야 하는 매칭 유형을 지정하며,filename(기본값) 또는content중 하나입니다.trigger_files—type = "content"멘션의 경우, 콘텐츠 매칭을 지정된 파일 글롭 배열로만 제한합니다.cc— 알림을 보낼 사용자들의 문자열 목록입니다. 이들은@ehuss나@rust-lang/clippy처럼@로 시작해야 합니다. 이것이 지정되지 않으면 아무도 알림을 받지 않습니다.message— 댓글에 포함될 메시지입니다. 이것이 지정되지 않으면 댓글에는Some changes occurred in {path}라고 표시됩니다.
경로 기반 멘션
기본적으로 triagebot은 주어진 UNIX 스타일 경로로 시작하는1 모든 파일을 확인합니다. type="filename"으로 명시적으로 요청할 수 있습니다.
글롭 매칭은 다음 구문을 지원합니다.
?는 임의의 한 문자와 일치합니다.*는 0개 이상의 문자와 일치합니다.**는 디렉터리를 재귀적으로 매칭합니다.{a,b}는a나b중 하나와 일치하며, 여기서a와b는 임의의 글롭 패턴입니다.[ab]는a또는b와 일치하며, 여기서a와b는 문자입니다.[!ab]는a와b를 제외한 모든 문자와 일치합니다.
예를 들어 library/std(또는 library/std*)는 library/std/src/process.rs처럼 library/std 디렉터리 아래의 모든 것과 일치합니다.
콘텐츠 기반 멘션
선택적으로 triagebot은 type="content"로 풀 리퀘스트에 추가된 모든 줄을 확인할 수 있습니다.
이 경우 키는 찾아야 할 콘텐츠입니다.
예시
[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를 참고하십시오.
-
시작하는 것을 구현하기 위해, 경로 끝에 암묵적인 글롭
*가 추가됩니다. ↩
병합
merge 기능은 병합 큐를 사용하는 저장소에서 PR을 병합할(그리고 그 권한을 위임할) 수 있도록 해줍니다.
참고: 현재 이는 bors를 사용하거나 병합 큐 없이 PR을 직접 병합하는 저장소에는 적용되지 않습니다.
사용법
병합
풀 리퀘스트를 병합하려면, 권한이 있는 사용자(아래 참조)는 다음 명령어를 입력할 수 있습니다:
@rustbot merge
이렇게 하면 PR이 병합 큐에 등록됩니다.
이 명령어는 다음 사용자만 사용할 수 있습니다:
- 저장소에 대해 최소 write 권한을 가진 멤버
- 병합 권한을 위임받은 사용자
위임
작성자에게 위임하기
풀 리퀘스트의 병합 권한을 PR 작성자에게 위임하려면, 해당 저장소에 최소 write 권한을 가진 멤버가 다음 명령을 입력할 수 있습니다:
@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분 정도 걸릴 수 있다는 점에 유의하십시오.
설정
참고: 이 기능은 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.
"""
구현
src/handlers/check_commits/no_merges.rs를 참고하십시오.
지명
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는 요약 목록에서 항목을 제거합니다.
설정
[!참고] 이 기능은 rust-lang 조직에서 기본적으로 활성화되어 있습니다.
이 기능은 triagebot.toml에 [note] 테이블을 두어 활성화합니다:
[note]
구현
parser/src/command/note.rs와 src/handlers/note.rs를 참조하십시오.
핑
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 레이블도 추가하게 됩니다.
동일한 대상 팀을 가리키기 위해 추가 라벨을 붙이는 별칭(alias)도 정의할 수 있습니다. 별칭은 기억하기 쉬운 라벨을 추가하거나 약간의 오타(예: “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 설정을 확인하십시오.
구현
parser/src/command/ping.rs와 src/handlers/ping.rs를 참고하십시오.
렌더링된 링크
렌더링된 링크는 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 이슈 템플릿을 사용해 회귀가 제출되면, 해당 이슈의 우선순위 지정이 자동으로 요청됩니다.
이 절차에 대해 더 알아보려면 prioritization 문서를 방문하십시오.
사용법
우선순위 지정이 구성된 저장소에서는 누구나 다음과 같은 댓글을 게시할 수 있습니다:
@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가 느릴 수 있기 때문에 이는 유용합니다. 이 정보를 가져오는 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이 풀 리퀘스트나 이슈 설명에 자동으로 추가하는 단순한 하이퍼링크입니다. 이 링크는 GitHub 이슈와 풀 리퀘스트를 위한 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
모든 옵션은 선택 사항입니다.
“모든 댓글 보기” 링크는 triagebot의 /gh-comments 엔드포인트, 즉 저희 GitHub 댓글 뷰어를 가리킵니다.
구현
src/handlers/view_all_comments_link.rs를 참고하십시오.
Triagebot Zulip 명령어
Rust Zulip 서버에서 두 가지 별도의 방법을 사용하여 triagebot에 명령을 보낼 수 있습니다:
- triagebot 계정에 다이렉트 메시지(DM)를 보내는 방법입니다.
@**triagebot**를 태그하고 뒤에 명령을 붙여(예:@**triagebot** end-meeting) 어떤 스트림에 메시지를 보냅니다.
Triagebot 명령어는 team 데이터베이스에 등록된 사용자만 보낼 수 있습니다.
다이렉트 메시지 명령
이러한 명령어는 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이 다이렉트 메시지를 통해 당신이 사칭했다는 사실을 해당 사용자에게 알립니다.
사칭(impersonation) 기능은 다른 사용자의 상태(예: 리뷰어 워크큐나 Rust 팀)를 확인하거나 간헐적인 디버깅을 위한 용도임에 유의하십시오. 악의적으로 사용하지 마십시오.
스트림 명령
- Meeting 명령어는 Zulip 회의의 진행을 제어하는 데 사용됩니다. 이는 여기에 문서화되어 있습니다.
- Rust Project Goals 명령어는 Rust 프로젝트 목표 추적을 제어하는 데 사용됩니다.
@**triagebot** ping-goals <threshold> <next-update>: 목표팀이 Zulip에서 목표 소유자에게 핑을 보내 자신의 목표에 대한 업데이트를 요청하는 데 사용합니다.<threshold>일 이내에 댓글이 있었다면 알림을 보내지 않습니다.<next-update>는 다음 블로그 업데이트가 언제 시작되는지를 나타내는 문자열입니다.
@**triagebot** docs-update: 문서 서브모듈을 업데이트하기 위한 풀 리퀘스트를 생성합니다(예시). Documentation Updates를 참고하십시오.@**triagebot** backport [approve | decline ] [stable | beta ] <PR>(예: “@triagebot backport approve beta 123456”) PR 백포트를 승인하거나 거부하는 댓글을 GitHub에 게시합니다(Backports 참조).@**triagebot** assign-prio <issue #> [ critical | high | medium | low | none ]는 이슈에 우선순위 레이블을 할당합니다(Prioritization 참조). “none“은I-prioritize레이블을 제거하기만 합니다.@**triagebot** unlock [--org <org>] <repo> <issue-id>: 주어진 이슈나 풀 리퀘스트의 잠금을 해제할 수 있게 합니다(기본 조직: rust-lang).
구현
src/zulip.rs를 참고하십시오.
Zulip 회의 관리
Triagebot은 회의 진행을 돕기 위해 Zulip에서 일부 명령에 응답할 수 있습니다.
사용법
@triagebot을 수신자로 하여 아래에 나열된 명령을 Zulip 메시지로 입력하십시오.
문서 읽기
@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” 스트림에 자동으로 메시지를 게시하도록 구성되어 있으며, 에디션과 관련된 이 알림은 대략 다음과 같은 모습입니다:
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 캘린더
- 왼쪽 사이드바의 “Other Calendars” 옆에 있는 “+” 아이콘을 누릅니다. “From URL“을 선택하고 컴파일러 팀 캘린더 링크를 필드에 붙여넣은 후 “Add calendar“를 누릅니다.
- Outlook 웹
- 왼쪽 사이드바의 아이콘을 사용해 캘린더로 이동합니다. 왼쪽에서 “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에 저장됩니다.
트리아지 회의 안건 생성하기
트리아지 회의 안건을 생성하는 방법에 대한 문서는 Prioritization을 참조하십시오.
스티어링/계획 회의
팀은 또한 정기적인 주기로 만나 고수준의 주제들을 논의합니다. 스티어링/계획 회의는 반복되는 일정으로 운영됩니다:
- 1주차: 계획 회의
- 팀이 제안한 회의들 중에서 다음 세 회의의 주제를 선정합니다.
- 2-4주차: 스티어링 회의
- 계획된 주제에 대해 논의합니다.
계획 회의 동안, 회의를 진행하는 팀 리드는 논의할 만한 관련 주제를 파악하고자 합니다. 일부 주제는 논의 전에 더 많은 조사가 필요하거나, 시기가 지났거나, 다른 팀과 더 관련이 있을 수 있습니다. 회의 제안자와 관련 팀원들의 일정 가능 여부에 따라, 해당 회의들은 이후 몇 주간의 스티어링 회의 시간대에 배정됩니다. 모든 회의 시간대를 반드시 채워야 하는 것은 아닙니다.
회의는 “meeting proposal” 템플릿을 사용하여 컴파일러 팀의 저장소에 이슈를 열어 제안합니다. 스티어링 회의는 결정/공감대 확인이 필요하고 트리아지 회의에서 지명된 이슈를 논의할 때 보통 확보 가능한 시간보다 더 오래 걸릴 만한 이슈를 더 넓은 팀과 논의하기에 좋은 기회입니다.
스티어링 회의 제안자는 주제를 설명하고 논의에 필요한 모든 맥락을 포함한 짧고 비공식적인 문서를 준비할 것으로 기대되지만, 이는 회의 당일까지만 준비되면 되며, 최초 회의 제안 시점에는 필요하지 않습니다.
어떤 기여자든 회의 주제를 제안할 수 있습니다. 좋은 스티어링 회의 주제의 예로는 다음이 있습니다:
- 팀의 피드백이 필요한 제안
- 즉, 컴파일러의 서브시스템을 리팩터링하거나 새로운 트리 외부 의존성을 생성하는 것
- 대규모 기여에 대한 심층 검토
- 즉, 작성자가 자신의 작업을 설명하고 그에 대한 질문에 답변하는 것
- 팀과 프로젝트의 나머지 부분 간의 조율
- 즉, 제안된 프로젝트 목표를 검토하는 것
- 팀을 위한 정책 결정
- 즉, 외부 의존성으로 인한 블로커를 어떻게 처리할 것인가
예정된 계획 및 방향 설정 회의는 컴파일러 팀 캘린더에서 확인할 수 있습니다.
방향 설정 회의록은 HackMD에 저장되어 있습니다.
멤버십
현재 멤버십에는 두 가지 등급이 있습니다:
- 멤버(members): r+ 권한, 봇 권한, [인프라] 접근 권한을 가진 정기 기여자입니다
- 메인테이너(maintainers): 컴파일러의 품질과 컴파일러 팀의 건전성에 투자하기로 스스로 다짐한 멤버입니다
멤버십에 이르는 길
컴파일러에 기여하고자 하는 사람들은 대개 두 가지 방식 중 하나로 시작합니다. “일회성” 이슈를 처리하거나, 기존의 어떤 워킹 그룹에 참여할 수 있습니다. 이들은 아직 컴파일러에 대해 잘 알지 못하며 특별한 권한도 없습니다. 그들은 triagebot을 이용해 이슈에 배정되며, (일반적으로) 멘토나 멘토링 지침과 함께 작업합니다.
컴파일러 팀 구성원
어떤 개인이 일정 기간 동안 꾸준히 기여해 왔다면, 컴파일러 팀 구성원 수준으로 승격될 수 있습니다(아래 의사 결정 방식 절 참고). 이 직함은 그가 정기적으로 기여하는 사람임을 나타냅니다.
이러한 승격이 적절한 정확한 조건을 정의하기는 어렵습니다. 멤버로 승격되는 것은 단순히 여러 항목을 확인하는 것으로 결정되지 않습니다. 하지만 일반적으로 다음 세 가지를 보여주었을 때 준비가 되었다고 봅니다:
- “지속력” – 해당 인물이 어떤 방식으로든 정기적으로 기여하고 있어야 합니다. 예를 들어 몇 가지 프로젝트를 완료했다는 것을 의미할 수 있습니다. 이는 예를 들어 몇 가지 프로젝트를 완료했음을 의미할 수 있습니다.
- “독립성과 익숙함” – 최소한 워킹 그룹의 범위 내에서는 작업을 맡을 때 어느 정도 독립적으로 행동할 수 있어야 합니다. 간단한 PR에 대해서는 다른 사람을 멘토링할 수 있을 정도가 되어야 합니다.
- “예의” – 컴파일러 팀 구성원은 Rust 조직의 일원이 되며 행동 강령과 관련하여 더 높은 기준을 적용받습니다. 이들은 행동 강령의 문구뿐만 아니라 그 정신도 지켜야 합니다.
멤버로 승격되면 여러 권한이 부여됩니다:
-
멤버는
r+(풀 리퀘스트 승인) 권한을 가지며 리뷰를 수행할 수 있습니다(앞서 논의했듯이 이러한 권한을 적절히 사용할 것으로 기대됩니다). 또한 perf/rustc-timer 및 기타 유사한 봇을 제어할 수 있는 접근 권한을 갖습니다.bors와r+에 관한 문서는 https://bors.rust-lang.org를 참고하십시오.팁: bors 권한과 관련한 몇 가지 기본 규칙은 다음과 같습니다: 악성 코드 여부를 먼저 확인하지 않았다면
try빌드를 하지 마십시오. 그리고 해당 코드를 효과적으로 리뷰할 수 있다고 합리적으로 확신하지 않는다면r+를 하지 마십시오. -
컴파일러 팀 구성원은 Rust 조직의 일원이므로 레이블을 수정하고 이슈에 배정될 수 있습니다.
-
멤버는 GitHub의
rust-lang/compiler팀의 일원이 되며, 이를 통해 사람들이 팀 전체에 연락하고자 할 때 핑(ping)을 받게 됩니다. -
구성원은 rust-lang.org 웹 페이지에 게시됩니다.
이는 또한 몇 가지 의무(경우에 따라 선택적 의무)도 수반합니다:
- 구성원에게는 리뷰어 순환에 추가되기를 원하는지 물어봅니다.
- 멤버는 팀을 돕기 위한 다양한 다른 메인테이너 활동에 참여할 수 있습니다.
- 구성원은 행동 강령과 관련하여 일반인보다 더 높은 기준을 적용받습니다.
컴파일러 팀 멤버가 된다는 것의 의미
컴파일러 팀의 멤버가 되면 여러 가지 일들이 일어납니다:
-
여러분은 비공개 Zulip 스트림에 접근할 수 있게 되며, 이곳에서 내부 논의가 이루어지거나 매우 초안 상태의 아이디어가 공유됩니다. 새로운 팀 멤버들에게 와서 인사해 보십시오!
-
여러 Github 저장소를 구독하고 쓰기 권한을 얻게 됩니다. 현재 어떤 저장소에 접근 권한이 있는지 확인하려면 이 GitHub 페이지를 확인하십시오. 그중 일부는 꽤 조용하거나 오래된 것들이니, 모든 저장소에 대해 걱정할 필요는 없습니다.
팁: Github은 쓰기 권한을 받은 모든 저장소에 자동으로 구독자로 추가합니다. 설정(여기)에서 이를 비활성화할 수 있습니다.
-
또한
all@rust-lang.org메일링 리스트에도 구독됩니다. 메일링 리스트 구독이 어떻게 동작하는지 확인하려면 이 파일을 참고하십시오. 이는 매우 저용량 메일링 리스트로(연간 몇 통 정도), 모든 기여자에게 소식을 전달하는 수단입니다. 이 주소로 스팸을 보내는 일은 없을 것입니다.
메인테이너
컴파일러 팀 구성원으로 1년을 보낸 후에는, 구성원이 컴파일러 팀 메인테이너가 되기를 요청하거나 요청받을 수 있습니다. 이는 그들이 단순한 정기 기여자일 뿐만 아니라, 팀이나 컴파일러의 일부(또는 여러 부분)의 방향을 적극적으로 형성하는 데 기여하고 있음을 의미합니다.
- 컴파일러 팀 메인테이너는 최소 한 가지 이상의 유지보수 활동에 참여할 것으로 기대됩니다.
- 컴파일러 팀 메인테이너는 rust-lang 웹사이트에서 “Maintainer” 역할로 식별됩니다.
승격 결정이 이루어지는 방식
어떤 개인이 컴파일러에 한동안 기여해 왔다면, 기존 컴파일러 팀 구성원의 지명을 받거나, 자신의 기여 이력이 팀 구성원 자격에 충분한지 컴파일러 팀 리드에게 직접 물어볼 수 있습니다.
컴파일러 팀 리드는 해당 개인에게 멤버십 초대를 확대하는 데 우려 사항이 있는지 나머지 컴파일러 팀과 확인합니다. 반대 의견이 없으면 초대가 이루어집니다. 일주일 내에 답변을 드리는 것을 목표로 하지만, (결정 자체의 범위를 벗어난 이유들로 인해) 더 오래 걸릴 수도 있습니다.
해당 개인이 초대를 수락하면, 컴파일러 팀 리드는 새로운 역할을 반영하도록 team 저장소를 업데이트합니다.
코드만이 아닙니다
컴파일러 팀의 멤버가 된다는 것이 반드시 PR 작성을 의미하지는 않는다는 점을 강조할 필요가 있습니다. 컴파일러를 지원하기 위해 수행되어야 하며 멤버십 자격을 갖추게 할 수 있는 다양한 작업들이 있습니다. 그러한 작업에는 회의 조직, 회의 참여, 이슈 이분 탐색 및 트리아지, 문서 작성, rustc-dev-guide 작업 등이 포함됩니다.
특히 컴파일러 팀 구성원이 되기 위한 가장 중요한 기준은 정기적이고 꾸준한 참여입니다. 컴파일러 팀 메인테이너가 되기 위한 가장 중요한 기준은 팀이나 컴파일러의 방향을 적극적으로 형성하는 것입니다.
전임 구성원 상태
컴파일러 팀 구성원 또는 메인테이너가 언제든 참여를 잠시 쉬고 싶을 경우, 스스로 전임 구성원 상태로 전환하는 것을 선택할 수 있습니다. 전임 구성원 상태가 되면 GitHub 별칭 등에서 제거되어, 핑이나 메시지로 방해받지 않아도 됩니다. 또한 r+ 권한도 갖지 않게 됩니다. 다만 전임 구성원은 전체적으로 GitHub 조직의 구성원 자격은 계속 유지합니다.
전임 구성원 상태에 있는 사람은 언제든지 “활동” 상태로 복귀를 요청할 수 있습니다. 이 요청은 특별한 사정이 없는 한 통상적으로 자동 승인됩니다.
전임 구성원 상태에 있는 사람은 이전에 도달했던 수준의 팀 구성원 자격을 여전히 유지하며, 이를 공개적으로 밝힐 수 있으나, 활동했던 기간도 함께 명시해야 합니다.
메인테이너 역할의 시작과 종료
컴파일러 팀 구성원이 메인테이너가 됨으로써 컴파일러를 적극적으로 유지 관리하기로 약속한 이후에도, 이러한 지속적인 책임을 일시적으로나 무기한으로 쉬고 싶을 수 있습니다. 어느 경우든 메인테이너는 컴파일러 팀 리드에게 알리거나 직접 team 저장소에 풀 리퀘스트를 열어, 메인테이너 마커 팀에서 스스로를 제외하고 전임 구성원 목록에 자신을 올릴 수 있습니다.
향후 이전 메인테이너가 관리 업무를 재개하고 싶다면, 컴파일러 팀 리드에게 복귀를 요청할 수 있습니다. 이 요청은 특별한 사정이 없는 한 통상적으로 자동 승인됩니다.
컴파일러 팀 전임 구성원
마찬가지로 컴파일러 팀의 어떤 구성원이든 팀에 대한 기여와 상호작용을 장기간 쉬고 싶을 경우, 컴파일러 팀 리드에게 알리거나 직접 team 저장소에 풀 리퀘스트를 열어 스스로를 전임 구성원 상태로 옮길 수 있습니다.
전임 구성원이 향후 컴파일러 팀 구성원 자격을 재개하고자 할 경우, 컴파일러 팀 리드에게 복귀를 요청할 수 있으며 이는 통상적으로 승인됩니다.
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
-
새 후보자를 팀 저장소와 컴파일러 팀에 추가합니다. 이는 Zulip, GitHub 등과 동기화되어 새 팀 구성원에게 접근 권한과 권한을 부여합니다.
-
새 팀 구성원을 소개하는 Inside Rust 블로그 게시물 초안을 작성합니다. 템플릿은 previous examples를 참고하십시오.
알림 그룹
컴파일러 팀은 사람들에게 알림을 보내 이슈에 대한 주의를 환기시키는 데 사용되는 여러 알림 그룹을 운영하고 있습니다. 알림 그룹은 원하는 사람이면 누구나 참여할 수 있도록 설정되어 있습니다.
Rust 프로젝트 GitHub 팀의 구성원만 이러한 알림 그룹을 사용할 수 있다는 점에 유의하십시오. 팀 구성원이 아닌 경우 자동화 봇에서 오류가 발생합니다. 팀 구성원이 아닌 사람이 사용하면 저희 자동화 봇에서 오류가 발생합니다.
알림 그룹 만들기
알림 그룹을 만들고자 하는 경우 절차는 다음과 같습니다. 먼저 컴파일러 팀의 승인을 받아야 합니다:
- 진행 상황을 수집하기 위해 rust-lang/compiler-team 저장소에 추적 이슈를 등록하십시오.
- rust-lang/team 저장소에 알림 그룹을 추가하는 PR을 만드십시오 (예시 PR)
- 이 그룹에 대한 triagebot 명령을 받아들이도록 rust-lang/rust 저장소를 설정하십시오. (예시 PR)
- rustc-dev-guide에 대해 여러분의 그룹을 언급하도록 알림 그룹 섹션을 수정하는 PR을 만드십시오.
- 스스로를 추가하는 방법을 보여주는 예시 PR을 rust-lang/team 저장소에 만드십시오. 이는 사람들에게 참여 방법을 보여주기 위해 여러분의 블로그 게시글에서 참조될 것입니다. (예시 PR)
- Inside Rust에 공지 블로그 글을 작성하고 blog.rust-lang.org에 대한 PR을 여십시오 (예시 PR)
운영
“운영“은 컴파일러 팀의 일부로서, 조직 및 유지보수 업무를 담당하며 전반적으로 일이 진행되도록 돕습니다. T-compiler ops는 Zulip의 #t-compiler/ops에 있습니다.
다음은 반복적으로 수행하는 작업 목록입니다. 이상적으로는 매주 이 목록을 훑어보십시오. 막힌 부분이나 의문이 있다면, 적절한 맥락을 파악한 후 주저하지 말고 주변 사람들에게 핑을 보내십시오. 기여자는 프로젝트의 가장 훌륭한 자원이며(그들의 시간을 소중히 여겨야 합니다), 언제나 도움이 됩니다.
Zulip #t-compiler에서 새 토픽을 열어 특정 주제, 이슈, 풀 리퀘스트에 대한 논의를 시작할 수 있으며, 팀의 합의와 더 많은 관심이 필요한 경우 I-compiler-nominated 레이블을 붙여 회의에서 논의할 수 있습니다(섹션 Meetings 참고).
이슈 정리
- 우선순위를 매겨야 할 이슈: 우선순위 지정을 참고하십시오.
- 담당자가 없는 P-high 이슈: 이상적으로는 이 범주의 이슈에는 담당자가 있어야 합니다(PR이 없는 것은 걸러내십시오). 드문 경우에는 담당자가 없어도 괜찮습니다.
- FCP 상태의 MCP는 10일 이상 seconded 상태인 경우 종료하고, 미해결 우려 사항이 없는지 확인하십시오.
- 열려 있는 MCP 확인: MCP는 프로토콜로, 제안을 컴파일러 팀의 관심으로 가져오기 위한 것입니다. MCP가 지지되거나 지지 부족으로 종료되는 두 결과 중 하나를 향해 나아가고 있는지 확인하십시오. MCP가 seconded되지 않을 것이 명확하거나 방치된 경우, 약 2~3개월 후 그 상태를 문의하고 종료 여부를 평가해도 됩니다. 그렇지 않다면 막힌 상태에서 벗어나도록 도와주십시오.
- MCVE가 필요한 이슈
- FCP를 거치고 있는 이슈와 PR: 팀이 체크박스를 체크해야 하는지 확인하십시오. 이 이슈들은 주간 트리아지 회의 안건에 포함됩니다.
T-compiler를 위한 우선순위 지정
우선순위 지정이 어떻게 작동하는지는 여기를 참고하십시오.
회귀를 살펴볼 때 유용한 필터 몇 가지입니다.
PR 정리
- 모든 PR에는 팀이 지정되어 있어야 합니다.
릴리스 일주일 전에 해야 할 일:
- 우선순위가 없는 회귀 없음: 이들이 수정되었는지 확인하고, 그렇지 않다면 팀의 관심을 받도록 하십시오.
- 담당자가 없는 beta 회귀나 stable 회귀가 없도록 하고, PR이 없는 것은 걸러내십시오.
- 진행 중인 beta 회귀나 stable 회귀가 없도록 하고, 이상적으로는 모두 병합되어야 합니다.
- 파괴적 변경(즉, 허용하기로 합의된 회귀)에는
relnotes-tracking-issue태그가 붙은 대응 이슈가 있는지 확인하십시오. 릴리스 노트 목록을 참고하십시오. 그러면 T-release가 이를 가져와 릴리스 노트에 추가합니다.
릴리스 이후
- 어떤 리그레션이 “승인됨“으로 종료될 수 있는지 신중하게 확인하십시오. 회귀를 유발한 PR이 파괴적 변경으로 승인되었음을 명확히 하는 댓글을 추가하십시오. 예: “PR #123456이 릴리스 노트에 언급될 것이므로 종료합니다”. 이 관행에 대한 논의와 댓글은 Zulip에서 진행할 수 있습니다.
회의
T-compiler에는 트리아지 회의와 설계 회의, 두 종류의 회의가 있습니다. 트리아지 회의는 매주 목요일에 열리며(이 저장소에서 Team Compiler 캘린더를 구독할 수 있습니다), 회의 안건 대부분을 생성해주는 도구가 있습니다(자세한 내용은 트리아지 회의 참고). 설계 회의 제안은 T-compiler 저장소에서 진행되며, 정기적인 steering 회의(다음 design 회의 일정이 정해지는 자리) 중에 일정이 잡힙니다. 설계 회의 또한 안건이 필요하며, 주제를 요약하고 관련 문서를 취합하며 관계자를 초대하는 등의 작업이 조금 필요합니다.
주간 트리아지 회의
먼저, 관련 이슈에 T-compiler 레이블이 붙어 있는지 확인하십시오:
I-prioritize레이블이 붙은 이슈- stable 릴리스 채널 백포트로 지명된 풀 리퀘스트
- beta 릴리스 채널 백포트로 지명된 풀 리퀘스트
I-compiler-nominated레이블이 붙은 이슈(즉, T-compiler 논의가 필요한 이슈)- 팀의 피드백을 기다리는 풀 리퀘스트
- 우선순위
P-high로 분류된 이슈 - 우선순위
P-critical로 분류된 이슈
..그리고 우선순위 지정이 완료되었는지 확인하십시오. I-prioritize 레이블이 붙은 리그레션은 우선순위 평가가 대기 중임을 알리는 신호입니다. 이 레이블이 이슈에 추가되면 triagebot이 Zulip 채널 #t-compiler/prioritization/alerts로 알림을 보냅니다.
이상적으로는 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” 공간의 새 HackMD에 내용을 복사하십시오. 이 작업은 컴파일러 팀 관련 문서를 정리하는 과정의 일부입니다. 이 도구는 최신 주간 컴파일러 트리아지 로그도 다운로드합니다. 제대로 되지 않은 경우, 가장 최근의 성능 트리아지 로그를 수동으로 복사하십시오(Zulip에서 제대로 표시되지 않을 부분을 정리하면서).
안건에 수동으로 추가 세부사항을 더하십시오:
- stable/beta 지명에 대한 요약을 추가하십시오(예: 누가 어떤 이유로 백포트를 지명했는지).
- 팀의 결정을 기다리는 PR들에 대한 요약을 추가하십시오(즉, 왜 대기 중인지)
P-critical/P-high버그에 대한 초기 소견을 추가하십시오- 지명된 이슈들에 대한 요약을 추가하십시오(예: 담당자가 누구인지, 왜 지명되었는지 등)
- 리뷰를 가장 오래 기다린 PR들을 채워 넣으십시오
- 핑이 적절한지 여부는 판단력을 사용하여 결정하십시오(예: 풀 리퀘스트가 실험적인 것이라면 리뷰가 필요 없을 수 있습니다. 마지막 리뷰 활동 이후 얼마나 지났는지, 최근 댓글이 어떤 내용인지 등)
회의 약 2시간 전에, 완성된 안건을 다가오는 회의의 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 목록(모든 팀), 이들을 앞으로 진행시키는 데 도움이 될 만한 것이 있습니까?
유용한 팁
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을 승인하는 데만 사용할 수 있습니다.
- 세컨딩(Seconding, 주요 변경 제안에만 해당, 아래 참조)
- 제안은 팀 구성원이 공식적으로 지지할 때 세컨딩됩니다. 세컨딩은 다른 팀원들이 우려 사항을 제기할 수 있도록 10일간의 대기 기간을 조건으로 제안을 잠정적으로 수락하는 것입니다.
- MCP에서
final-comment-period레이블을 제거하여 “언세컨드(unsecond)“할 수 있습니다.
- 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는 어떻게 제출합니까?
- major change template을 사용하여 rust-lang/compiler-team 저장소에 추적 이슈를 여십시오.
#t-compiler/major changes스트림의 Zulip 토픽이 봇에 의해 자동으로 생성됩니다.- 우려 사항이 제기되면 이를 해소하기 위해 제안을 수정하고 싶을 수 있습니다.
- 또는 design meeting proposal을 제출하여 더 길고 집중적인 논의를 진행할 수도 있습니다.
- 승인되려면 주요 변경 제안에는 세 가지가 필요합니다:
- 동의자(second) — 아이디어를 승인하지만 제안을 처음 낸 사람은 아닌 컴파일러 팀 구성원입니다.
- 최종 의견 수렴 기간(사람들이 의견을 남길 시간을 주기 위한 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에는 실험적임을 표시해야 하며, (원하는 사람이 있지 않은 한) 머지되어서도 안 되고 누군가 리뷰할 것으로 기대되어서도 안 됩니다. 또한 팀이 해당 제안을 어떻게 느끼는지 파악해 보십시오. 제안을 보여주는 코드는 감사히 여기지만, 동시에 승인받지 못할 수도 있는 작업에 매달리는 것은 원치 않으므로, 개념 증명(proof-of-concept)이나 더 정교한 패치가 승인을 촉진할 수는 있어도 승인을 보장하지는 않는다는 점을 유념하십시오.
좋은 제안이란 무엇입니까?
좋은 제안은 다음 사항을 다룹니다:
- 동기(Motivation): 이 제안이 왜 필요합니까? 이것이 어떤 문제를 해결합니까? 그 문제가 왜 중요합니까?
- 설계(Design): 무엇을 제안하는 것입니까?
- 구현 노트(Implementation notes): 보통은 구현에 대해 이야기할 필요가 없지만, 언급할 만한 중요한 사항이 있다면(예: 구현이 매우 침습적이었다면) 여기에 적어도 좋습니다.
- 선례, 링크, 관련 자료(Precedent, links, and related material):
clang이나lld같은 다른 컴파일러/링커/도구에서 유사한 제안이 있었습니까? - 대안, 우려 사항, 핵심 결정(Alternatives, concerns, and key decisions): 고려된 대안이 있었습니까? 있었다면 왜 이 설계를 선택했습니까?
어떤 제안/승인이 필요합니까?
이 섹션은 주어진 모든 상황에 대해 어떤 제안과 승인이 필요한지를 빠짐없이 상세히 설명하는 것을 목표로 합니다.
내부
- 알림 그룹 생성
- 제안 방법: PR
- 승인 방법: r+
- 팀 구성원이 새 그룹이 타당하다고 판단하면 해당 그룹을 추가하는 변경을 병합할 수 있습니다.
- 중요한 내부 리팩터링/변경 사항
- 제안 방법: MCP
- 승인 방식: 세컨딩
- MCP에 제안하는 리팩터링을 상세히 기술하십시오 - 더 집중적인 논의가 필요하다면 선택적으로 조율 회의(steering meeting)를 예약할 수 있습니다. 논의가 마무리되면 팀 구성원이 제안에 동의(second)할 수 있습니다.
- 작은 팀 정책 정의/변경
- 다음을 사용하여 제안: MCP
- 승인 방식: 세컨딩
- MCP로 충분한 소규모 정책 변경의 예로는 대소문자를 구분하지 않는 파일 시스템에 대한 지원 수준이나 팀이 추적 이슈에서 논의를 진행할 의도가 있는지 여부 등이 있습니다.
- 큰 팀 정책 정의/변경
- 제안 방식: RFC
- 다음을 사용하여 승인: FCP
- FCP가 필요한 대규모 정책 변경에는 팀 구조 및 구성원 자격 기준 등에 대한 제안이 포함됩니다.
컴파일러 플래그
- 내부 전용 컴파일러 옵션 추가 (예:
-Ztreat-bug-as-err)- 제안 방식: PR
- 승인 방식: r+
- 팀 구성원이 새로운 옵션이 합리적이라고 판단하면 해당 옵션을 추가하는 변경을 병합할 수 있습니다.
- 추후 안정화를 의도한 간단한 컴파일러 옵션 추가
- 다음을 사용하여 제안: MCP
- 승인 방식: 세컨딩
- LLVM에서 논란의 여지가 없는 옵션을 노출하는 것과 같은 단순한 옵션은 동의(second)된 MCP와 리뷰어의 r+ 승인만으로 구현하여 병합할 수 있습니다. 이후 안정화될 때는 완전한 FCP가 필요합니다.
- 추후 안정화를 의도한 복잡한 컴파일러 옵션 추가
- 제안 방식: RFC
- 다음을 사용하여 승인: FCP
- 옵션이 복잡하고 설계 고려사항이 필요한 경우,
t-compilerRFC를 작성하여 제출하십시오
- 내부 전용 플래그 제거
- 다음을 사용하여 제안: MCP
- 승인 방식: 세컨딩
- 불안정한 구현을 제거하는 근거를 기술하십시오. 논의가 마무리되면 팀 구성원이 제안에 동의(second)할 수 있습니다.
- 궁극적인 안정화를 의도했던 플래그 제거
- 다음을 사용하여 제안: MCP
- 승인 방법: 세컨딩
- 불안정 구현을 제거하는 근거를 설명합니다. 논의가 마무리되면 팀 구성원이 제안에 동의(second)할 수 있습니다.
- 컴파일러 옵션 안정화
- 제안 방법: PR
- 다음을 사용하여 승인: FCP
- PR을 열고 안정화 가이드를 따르십시오. 배정된 리뷰어는 안정화 가이드가 준수되었는지 확인하고, 코드를 리뷰한 뒤 FCP를 시작합니다.
- 컴파일러 옵션 안정화 되돌리기
- 제안 방법: PR
- 다음을 사용하여 승인: FCP
- PR을 열고 안정화 가이드를 따르십시오. 배정된 리뷰어는 안정화 가이드가 준수되었는지 확인하고, 코드를 리뷰한 뒤 FCP를 시작합니다.
- stable 플래그의 동작 확장
- 다음을 사용하여 제안: MCP
- 승인 방법: 세컨딩
- 플래그의 동작을 확장하는 근거를 설명합니다. 논의가 마무리되면 팀 구성원이 제안에 동의(second)할 수 있습니다.
애트리뷰트
- 내부 전용 애트리뷰트 추가(예:
rustc_attrs)- 제안 방법: PR
- 승인 방법: r+
- 팀 구성원이 새로운 속성이 합리적이라고 판단하면 해당 속성을 추가하는 변경을 병합할 수 있습니다.
- 추후 안정화를 염두에 둔 애트리뷰트 추가
- 언어 팀의 프로세스를 따르고 구현 PR을 컴파일러 팀원의 검토를 받도록 합니다
- 내부 전용 애트리뷰트 제거
- 다음을 사용하여 제안: MCP
- 승인 방법: 세컨딩
- 불안정 구현을 제거하는 근거를 설명합니다. 논의가 마무리되면 팀 구성원이 제안에 동의(second)할 수 있습니다.
- 궁극적인 안정화를 의도했던 애트리뷰트 제거
- 언어 팀의 절차를 따르고, 제거 풀 리퀘스트를 컴파일러 팀 구성원에게 검토받으십시오
- 속성 안정화
- 언어 팀의 절차를 따르고, 안정화 풀 리퀘스트를 컴파일러 팀 구성원에게 검토받으십시오
- 속성 안정화 되돌리기
- 언어 팀의 절차를 따르고, 되돌리기 풀 리퀘스트를 컴파일러 팀 구성원에게 검토받으십시오
기능
- 아직 제안되지 않은 언어 기능의 실험적 구현 추가
- 다음을 사용하여 제안: MCP
- 승인 방법: 동의(Seconding)
- 언어 팀의 승인(해당 기능이 실험해볼 가치가 있다고 판단하는 것)을 얻은 후 RFC를 제출하고, 논의가 마무리된 뒤 컴파일러 팀 구성원이 구현이 실현 가능하며 컴파일러 메인테이너에게 과도한 부담을 주지 않는다고 동의하면, 해당 구성원이 MCP에 동의(second)하여 구현을 진행할 수 있습니다.
- 구현의 소유자가 컴파일러 팀 구성원인 경우에는 이 과정이 필요하지 않습니다
- 언어 기능 안정화
- 언어 팀의 절차를 따르고, 안정화 풀 리퀘스트를 컴파일러 팀 구성원에게 검토받으십시오
- 언어 기능 안정화 되돌리기
- 언어 팀의 절차를 따르고, 되돌리기 풀 리퀘스트를 컴파일러 팀 구성원에게 검토받으십시오
타겟
서로 다른 타겟 티어에 대한 상세한 요구사항은 타겟 티어 정책을 참고하십시오.
타겟 승격
타겟 승격에 대한 간략한 개요:
| 현재 타겟 티어 | 목표 타겟 티어 | 제안 | 필요한 승인 |
|---|---|---|---|
| 해당 없음(새 타겟 제안) | 3 | 풀 리퀘스트 | r+ (컴파일러 리드) |
| 3 | 2 | MCP | FCP |
| 2 | 1 | RFC | FCP |
- 새 타겟 제안하기
- 다음으로 제안: PR
- 다음으로 승인: r+ (컴파일러 리드)
- PR에서
r? compiler_leads를 사용하여 컴파일러 리드 중 한 명을 리뷰어로 무작위 배정할 수 있습니다. - 새 타겟에 대해(관련 문서 업데이트를 포함하여) PR을 열고, 설명에 타겟 티어 정책 준수 여부를 문서화하십시오.
- 새 타겟은 반드시 Tier 3로 시작해야 합니다.
- 새 타겟은 라이선스 관련 우려 사항을 확인하고, 프로젝트 인프라에 대한 요구 사항이 관련 팀들과 함께 검토·확인되도록 하기 위해 컴파일러 팀 공동 리드에게 배정되어야 합니다.
- Tier 3에서 Tier 2로 타겟 승격하기
- 다음을 사용하여 제안: MCP
- 승인 방법: FCP
- 해당 target으로 MCP를 열고 설명에 Tier 2 target policy를 준수하는지 문서화하십시오.
- 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
- 해당 target과 근거를 담아 MCP를 여십시오.
- 티어 3 타깃 제거
- 제안 방법: PR
- 승인 방법: r+ (컴파일러 리드)
- 티어 3 타깃을 제거하는 대상 타깃과 근거를 담아 PR을 엽니다.
그 외 종류의 타깃 변경
- 타깃 이름을 변경하거나 티어 3 타깃에 호환성을 깨는 변경을 가하는 경우
- 제안 방법: PR
- 승인 방법: r+
- 제안된 이름 변경 사항을 담은 PR을 열고 변경 동기를 설명한 뒤 리뷰어로부터 r+를 받으십시오.
- 타깃 이름을 변경하거나 티어 2 타깃에 호환성을 깨는 변경을 가하는 경우
- 제안 방법: MCP
- 승인 방법: FCP
- 변경 동기를 설명하는 MCP를 열고 승인을 위한 FCP를 시작하며, FCP를 시작하십시오.
- 승인될 경우, 해당 변경이 적용되기 최소 한 릴리스 전에 공지 기간을 두고 이를 알리는 블로그 게시물이 함께 있어야 합니다.
- 2단계 타겟의 호스트 도구 상태를 변경(호스트 도구 없음 => 호스트 도구 있음, 호스트 도구 있음 => 호스트 도구 없음)
- 제안 방법: MCP
- 승인 방법: FCP
- 변경 동기를 설명하는 MCP를 열고 승인을 위한 FCP를 시작하며, FCP를 시작하십시오.
- 타깃 이름을 변경하거나 티어 1 타깃에 호환성을 깨는 변경을 가하는 경우
- 제안 방법: RFC
- 승인 방법: FCP
- 변경 동기를 설명하는 RFC를 열고 승인을 위한 FCP를 시작하며, FCP를 시작하십시오.
- 승인된 경우, 해당 변경은 적용 시점보다 최소 한 릴리스 이전에 통지 기간을 두고 변경을 알리는 블로그 게시물과 함께 이루어져야 합니다.
- 1단계 타겟의 호스트 도구 상태를 변경(호스트 도구 없음 => 호스트 도구 있음, 호스트 도구 있음 => 호스트 도구 없음)
- 제안 방법: RFC
- 승인 방법: FCP
- 변경 동기를 설명하는 RFC를 열고 승인을 위한 FCP를 시작하며, FCP를 시작하십시오.
- 승인된 경우, 해당 변경은 적용 시점보다 최소 한 릴리스 이전에 통지 기간을 두고 변경을 알리는 블로그 게시물과 함께 이루어져야 합니다.
- 호스트 도구를 제거하는 경우, 필요에 따라 1회 릴리스 유예 기간을 생략해야 할 때도 있습니다.
- 타겟 기준선 변경(예: 최소 Darwin 또는 Windows 버전 상향)
- 제안 방법: MCP
- 승인 방법: FCP
- 해당 target이 왜 baseline을 변경해야 하는지 설명하는 MCP를 작성하고, 논의가 마무리되면 baseline 변경을 승인하기 위한 FCP를 시작할 수 있습니다.
- 타겟 메인테이너 추가/제거
- 제안 방법: PR
- 승인 방법: r+
- target 문서에 대한 변경 사항을 담은 PR을 열고 리뷰어로부터 r+를 받으십시오.
- 타겟 기능 추가
- 제안 방법: PR
- 승인 방법: r+
- target 기능을 추가하는 PR을 열고 리뷰어로부터 r+를 받으십시오.
- 타겟 기능 안정화
- 제안 방법: PR
- 승인 방법: FCP
- target 기능을 안정화하는 PR을 열고, 리뷰어가 변경 사항에 만족하면 FCP를 시작할 수 있습니다
린트, 오류 및 경고
- 새 경고/오류 추가
- 제안 방법: PR
- 승인 방법: r+
- 구현 내용을 담은 PR을 열고 리뷰어로부터 r+를 받으십시오
- 새 린트 그룹 추가
- 언어 팀의 프로세스를 따르고, 구현 풀 리퀘스트를 컴파일러 팀 멤버가 검토하도록 합니다
- 컴파일러 기능과 관련된 새 린트 추가
- 제안 방법: MCP
- 승인 방법: FCP
- 컴파일러 팀의 책임 영역(예: 컴파일러 플래그 등)에 해당하는 세부 사항과 관련된 린트는 언어 팀이 아니라 컴파일러 팀이 승인할 책임이 있습니다.
- 린트와 그 근거를 설명하는 MCP를 작성하고, 논의가 마무리되면 새 린트를 승인하기 위한 FCP를 시작할 수 있습니다
- 컴파일러 기능과 관련된 새로운 향후 호환성 경고(FCW) 추가
- 제안 방법: MCP
- 승인 방법: FCP
- 컴파일러 팀의 책임 영역(예: 컴파일러 플래그 등)에 해당하는 세부 사항과 관련된 FCW는 언어 팀이 아니라 컴파일러 팀이 승인할 책임이 있습니다.
- FCW와 그 근거를 설명하는 MCP를 작성하고, 논의가 마무리되면 새 FCW를 승인하기 위한 FCP를 시작할 수 있습니다
- 컴파일러 기능과 관련된 린트의 기본 린트 레벨 변경
- 제안 방법: MCP
- 승인 방법: FCP
- 컴파일러 팀의 책임 영역(예: 컴파일러 플래그 등)에 해당하는 세부 사항과 관련된 린트는 언어 팀이 아니라 컴파일러 팀이 승인할 책임이 있습니다.
- 기본 린트 레벨 변경 근거를 설명하는 MCP를 작성하고, 논의가 마무리되면 새 린트를 승인하기 위한 FCP를 시작할 수 있습니다
- 언어 기능과 관련된 새 린트 추가
- 언어 팀의 프로세스를 따르고, 구현 풀 리퀘스트를 컴파일러 팀 멤버가 검토하도록 합니다
- 언어 기능과 관련된 새로운 향후 호환성 경고(FCW) 추가
- 언어 팀의 프로세스를 따르고, 구현 풀 리퀘스트를 컴파일러 팀 멤버가 검토하도록 합니다
- 언어 기능과 관련된 린트의 기본 린트 레벨 변경
- 언어 팀의 프로세스를 따르고, 구현 풀 리퀘스트를 컴파일러 팀 멤버가 검토하도록 합니다
라이선싱
- 새 의존성 도입/라이선스 변경/의존성 버전 상향
- 제안 방식: PR
- 승인 방식: r+ (컴파일러 리드)
- 라이선싱에 영향을 미치는 변경 사항으로 풀 리퀘스트를 열고 검토를 위해 팀 리드에게 배정합니다
컴파일러 변경 사항의 stable/beta 채널 백포트 지명
백포트를 참고하십시오.
rust-lang/rust CI에 생태계/통합 테스트 작업/구성 요소 추가하기
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에 링크하십시오.
- 라이브러리 팀이 제안을 검토하고 막는 우려 사항이 없으면, rust-lang/rust에 구현 풀 리퀘스트를 제출하십시오.
- 해당 풀 리퀘스트에는 트리 내 rustc-dev-guide에 부속 생태계/통합 테스트 작업/구성 요소 지원 페이지를 포함해야 하며, 실패 프로토콜이 잘 문서화되어 있는지 확인해야 합니다(아래 참조). MCP와 이슈에 링크하십시오.
- 인프라 팀이 구현 풀 리퀘스트를 검토합니다. 승인되면, 해당 생태계/통합 테스트 작업/구성 요소는 rust-lang/rust PR CI 또는 Full Merge CI에서 실행됩니다.
에코시스템 및 통합 테스트 작업/컴포넌트 정책
MCP의 일환으로 이 템플릿(구분선 아래)을 복사하여 작성해 주십시오. 정책 질문/설명 자체는 인용 블록으로 표시됩니다. MCP 작성자가 제공해야 하는 정보는 내용이 대체되어야 하는 기울임체 문장으로 표시되어 있습니다.
참고: 이 정책의 의도는 생태계 테스트 작업/구성 요소를 추가하는 것을 성가시고 번거롭게 만들려는 것이 아닙니다. 저희는 단지 테스트 작업/구성 요소가 실패할 경우, 그리고 실패할 때 완전히 무관한 풀 리퀘스트의 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가 관리하고 있습니다. 이는 디버그 모드에서 컴파일 시간을 개선할 잠재력이 있습니다. 현재 bjorn3가 관리하고 있습니다. - 컴파일러 팀이 관리하는
LLVM::APFloat라이브러리의 Rust 포트입니다. LLVM 라이브러리의 포트로서, 이 저장소는 다른 저장소들과 미묘하게 다른 라이선스 체계를 가지고 있습니다. rustc_apfloat#licensing을 참조하십시오. - LLVM 및 MLIR용 고성능 자동 미분기인 Enzyme의 포크입니다(자세한 정보는 이 링크 참조). 이 포크는 ZuseZ4가 관리하고 있습니다.
- GNU 확장 및 DWARF 5 패키지 형식을 지원하는 DWARF 패키징 유틸리티인
thorin입니다. 주로 davidtwco가 관리하고 있습니다. - 여러분이 읽고 있는 문서 웹사이트인 Rust Forge입니다. Rust 프로젝트가 공동으로 관리하고 있습니다. Rust 프로젝트가 공동으로 관리하고 있습니다.
ar_archive_writer: 다른LLVM::APFloat라이브러리와 마찬가지로, 이 라이브러리의 라이선스는 다른 저장소들과 약간 다릅니다. ar_archive_writer#licensing을 참조하십시오.- 다른 Rust 프로그램에 임베드되도록 의도된 경량 Datalog 엔진인
datafrog입니다(TODO: 상태?) ena는 Rust로 구현된 union-find / 합동 폐쇄(congruence-closure)로, 우리 추론 변수 테이블의 기저 구현을 포함합니다. 즉 추론 변수의 인스턴스화와 병합을 추적하는 역할을 합니다.literal-escaper는 문자열 리터럴을 언이스케이프하는 라이브러리입니다. 이는rustc_lexer와proc_macro에서 사용됩니다.miri는 Rust의 중간 수준 중간 표현(mid-level intermediate representation)을 위한 인터프리터입니다. 안전 요구 사항을 지키지 못하는 unsafe 코드를 탐지합니다. 안전성 요구 사항을 지키지 못하는 안전하지 않은 코드를 탐지합니다.measureme는rustc이벤트를 기록하고 바이너리 형식으로 직렬화하는 라이브러리입니다. 현재는rustc자체 내부용으로만 사용됩니다.odht는 사전 디코딩 없이 디스크에서 메모리로 매핑할 수 있는 해시 테이블을 위한 크레이트입니다. 현재는rustc자체 내부용으로만 사용됩니다.rustc-demangle:rustc심볼을 위한 디맹글링입니다(문서).rustc-hash는rustc가 사용하는 비암호화 해싱 알고리즘입니다rustc-rayon은 Rust용 Rayon 데이터 병렬화 라이브러리의 포크입니다. 이는rustc컴파일을 병렬화하기 위한 지속적인 노력의 일환입니다. 자세한 내용은 저희의 working areas를 참조하십시오.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주석의 최신 목록입니다.
- 컴파일러 내 모든
기여자나 컴파일러 팀 구성원에게 유용할 만한 추가 자료가 있다면, 여기에 추가하는 풀 리퀘스트를 부담 없이 제출해 주십시오.
검토 정책
이 문서는 Rust 컴파일러에 대한 기여물에 관한 저희의 리뷰 정책을 설명합니다. 이 문서의 대상 독자는 기여자와 검토자 모두입니다.
이 정책의 목적은 Rust 프로젝트의 기대치를 명확히 함으로써 기여자가 더 리뷰하기 쉬운 풀 리퀘스트를 만들도록 돕고, 리뷰어에게도 확인해야 할 공통 사항의 편리한 목록을 제공하는 것입니다. 양측이 함께 작업하는 방식을 명확히 이해하면 프로젝트에 도움이 될 것입니다.
코드 검토의 목적은 다음과 같습니다.
- 버그와 사용성 및 성능 회귀가 도입될 위험을 줄이는 것입니다.
- 저희 코드를 유지보수 가능하게, 즉 읽기 쉽고, 문서화되어 있으며, 잘 테스트된 상태로 유지하는 것입니다.
- 변경 사항이 큰 그림과 적절한 맥락을 염두에 두고 이루어지도록 보장하는 것입니다. 이는 개별적으로는 무해해 보이지만 더 큰 맥락에서는 문제가 있거나 바람직하지 않은 변경 사항에 특히 중요합니다.
검토는 제안된 변경 사항을 다른 관점에서 바라볼 또 다른 시각을 도입함으로써 이를 달성하며, 이는 실수를 조기에 발견하고 변경 사항 이면의 추론에 존재하는 잠재적 사각지대를 찾아낼 가능성을 높여줍니다.
기본 검토 요구사항
검토가 효과적이려면 충족되어야 할 몇 가지 요구사항이 있습니다.
- 검토자는 검토 대상 코드에 대해 충분한 이해를 갖추고 있어야 합니다.
- 이는 주어진 변경 사항의 명백하지 않고 의도하지 않은 부작용을 발견하는 데 중요합니다.
- 풀 리퀘스트 작성자는 다음을 제공해야 합니다.
- 오타 수정과 같이 매우 사소한 변경이 아닌 이상, 변경 사항에 대한 간결하고 상위 수준의 설명과 그 뒤에 있는 (2) 근거입니다.
- 근거가 더욱 유용하도록, 작성자는 잠재적인 쟁점, 이루어져야 했던 타협, 고려된 대안적 접근 방식, 관련 문서·논의·맥락 등을 나열하는 것이 권장됩니다.
- 코드 검토는 어려운 작업이며, 검토자에게는 이를 수행할 시간이 한정되어 있습니다. 리뷰어가 풀 리퀘스트의 의도와 맥락을 스스로 조합해내지 않도록 리뷰 과정을 순조롭게 시작하게 하면, 속도가 빨라질 뿐만 아니라 리뷰의 품질도 향상됩니다.
- 검토자는 자신이 해당 변경 사항을 승인할 적임자인지에 대해 명확한 판단을 갖고 있어야 합니다.
- 검토 대상 코드에 대한 지식은 이 질문에 답하는 데 명백하지만 유일한 기준은 아닙니다.
- 절차상으로 리뷰어는 다음 사항도 결정해야 합니다.
- 리뷰어가 단독으로 결정을 내릴 수 있습니까?
- 해당 풀 리퀘스트가 승인 절차를 거쳐야 하는가?
- 해당 풀 리퀘스트가 다른 팀, 특히
t-lang의 검토 및/또는 승인을 필요로 하는가? - 이 변경이 stable 코드를 망가뜨리거나, 저희가 의도하지 않은 새 코드를 받아들이기 시작할 수 있습니까? 풀 리퀘스트에 위험 요소가 포함되어 있다면, 그것이 충분히 정당화되는가? 이 변경이 crater 실행을 통한 생태계 영향 평가를 필요로 합니까?
- 해당 풀 리퀘스트가 상당한 성능 변화를 가져올 것인가? 성능 회귀가 발생할 가능성이 있다면, 그것이 정당화됩니까? 해당 풀 리퀘스트가 성능 실행을 필요로 하는가?
- 리뷰어가 충분히 철저하고 시기적절하게 리뷰를 수행할 수 있습니까?
- 리뷰어가 충분히 편향되지 않은 관점을 제공할 만큼 공정합니까? 예를 들어 공동 저작(리뷰어가 PR에 충분히 유의미한 변경을 가한 경우) 또는 기타 이해 상충 때문에 그렇지 않을 수 있습니까?
리뷰 체크리스트
다음 질문 목록은 리뷰어와 PR 작성자 모두가 PR을 좋은 형태로 만들고 위의 기준을 충족하도록 돕습니다:
PR 작성자 및 리뷰어를 위한 체크리스트
- PR 메시지에 다음이 포함되어 있습니까?
- ..변경 사항에 대한 간결한 상위 수준 설명? (무엇이 변경되는지)
- ..그렇게 하는 이유에 대한 명확한 근거? (왜 변경되는지)
- ..사소하지 않고 적절한 경우, 버그가 어떻게 수정되었는지 또는 변경 사항이 어떻게 구현되었는지?
- ..잠재적인 쟁점들의 목록? 대안? 트레이드오프? 위험?
- ..관련 이슈, RFC, MCP 등에 대한 링크?
- 이 PR은 회귀 테스트가 필요합니까? 이 변경 사항이 **주요 변경 제안(MCP)**으로 다뤄져야 합니까? 이미 다뤄지고 있습니까? 이미 열려 있는 MCP가 있다면 이미 승인되었습니까, 아니면 PR이 그것에 막혀 있습니까?
- 이 변경은 **주요 변경 제안(MCP)**의 대상이어야 합니까? 이미 다뤄지고 있습니까? 이미 열려 있는 MCP가 있다면, 그것이 이미 승인되었습니까, 아니면 PR이 그것에 막혀 있습니까?
- PR에 성능 실행이 필요합니까?
- PR에 다른 팀의 리뷰 및/또는 승인이 필요합니까?
- 예를 들어, 생태계 영향이 크거나 언어 변경이 있는 린트 확장의 경우
t-lang- PR이 사소하지 않은 방식으로 다른 팀에 영향을 미칩니까? 영향을 받는 팀에게 미리 알려야 합니까?
- 예: rustfmt나 rust-analyzer, 또는 서브트리에 대한 변경.
- 1년 후에 이 PR을 이해하려는 사람이 무슨 일이 일어나고 있는지 빠르게 재구성할 수 있겠습니까?
- 새 코드가 적절히 문서화되어 있습니까? 기존 문서가 여전히 최신 상태입니까?
- PR의 변경 사항에 Reference나 에디션 가이드의 업데이트가 필요합니까?
- 이 PR은 다음 중 어떤 회귀를 초래합니까?
- 오류 메시지 품질
- 유지보수성(예: 복잡한 코드, 문서 부재, unsafe)
- 특정 대상 플랫폼
- 다운스트림 도구(예: 링커, 디버거)
- 컴파일 시간
- 메모리 사용량
- 타겟(예: 베이스라인, 타겟 기능, 호출 규약 등)
리뷰어를 위한 체크리스트
- 제가 이 PR을 검토하기에 적합한 사람입니까:
- 이 PR의 변경 사항은
t-compiler의 관할에 속합니까?- 코드를 충분히 이해하고 있습니까?
- 명백하지 않은 부작용을 발견할 수 있습니까?
- 이 PR로 인해 발생한 버그를 고칠 수 있습니까?
- 적절한 시간 내에 리뷰를 수행할 수 있습니까?
- 어떤 이유로든 PR을 빨리 승인해야 한다는 압박감을 느끼고 있습니까?
- 충분히 공정합니까?
- 병합 전에:
- PR 제목과 설명이 여전히 정확합니까?
- 커밋 이력이 충분히 깔끔합니까? PR 이력에 “오타 수정” 커밋이 16개나 있을 필요는 없습니다.
- 이 PR이 관련 이슈를 올바르게(또는 올바르지 않게) 닫습니까?
- 다른 관련 팀에서 리뷰어를 굴려야 합니까?
일반적인 상황을 다루기 위한 안내
대부분의 경우 이 정책을 어떻게 적용할지 결정하는 데는 상식만으로 충분합니다. 하지만 때로는 어떻게 진행해야 할지 즉시 명확하지 않은 회색 지대가 존재합니다. 이 섹션에서는 몇 가지 흔한 사례와 그것들을 다루는 방법에 대한 안내를 함께 나열합니다.
저는 리뷰하기에 적합한 사람이 아닌 것 같습니다 - 이제 어떻게 해야 합니까?
(rustbot 등을 통해) PR이 무작위로 배정되었지만 리뷰하기가 편하지 않다고 느끼는 것은 지극히 정상적인 일입니다. 구체적인 사례에 따라 할 수 있는 일은 다음과 같습니다:
- 변경 사항이 정말로 크거나 논쟁의 여지가 있어 보인다면, 작성자에게 적절한 승인 절차를 거치도록 권하는 것을 고려하십시오.
- 리뷰에 딱 맞는 사람을 알고 있다면,
r? @<github-name>을 통해 그들을 배정하십시오. 그들이 맡을 수 있는지 묻는 댓글을 남기는 것이 예의 바르지만 – 사전에 그들이 실제로 할 수 있는지 확인할 필요는 없습니다. - 변경이 그리 복잡하지 않고, 무작위로 배정된 다른 컴파일러 리뷰어 역시 이 PR에서 어려움을 겪지 않을 것으로 예상된다면,
r? compiler로 무작위 컴파일러 리뷰어를 다시 뽑을 수 있습니다. - 변경이 복잡하거나, 다른 컴파일러 리뷰어를 무작위로 다시 뽑아도 여러 번 재추첨으로 이어질 것으로 예상된다면,
#t-compiler/private에 스레드를 열어 팀의 나머지 구성원에게 — 리뷰할 수 있는 사람이 있는지, 혹은 팀이 이 변경을 아예 받아들이는 데 편안함을 느끼는지 물어봐야 합니다. - 변경이 다른 팀을 대상으로 한다면, 해당 팀에서 리뷰어를 뽑으십시오(예:
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에 I-compiler-nominated를 붙이거나, 이슈에 컴파일러 팀 리드를 배정하고 S-waiting-on-team 레이블을 추가하는 것이 좋은 방법입니다.
두 경우 모두, 해당 PR은 주간 트리아지 회의에 상정됩니다. 또한 수정 중인 문제에 대한 설명, 불명확한 부분, 잠재적 위험, 고려된 대안 등 해당 이슈에 대해 가능한 한 많은 정보를 수집하고 문서화하는 것이 특히 가치 있습니다. 또한 해당 영역에 대한 이해 부족을 문서화하기 위해 추적 이슈를 여는 것도 좋은 방법이며, 구체적인 질문과 우려 사항, 버그를 문서화해 두면 이후 컴파일러 팀 구성원들이 더 나은 이해를 회복했을 때 해결할 수 있습니다.
리뷰어는 PR 작성자에게 이런 종류의 정보를 코드 내 주석이나 PR 메시지(이는 git 커밋 이력의 일부가 됩니다)에 추가하도록 요청해야 합니다.
PR이 외부 프로젝트에서 rustc 내부 요소를 사용할 수 있도록 지원하는 변경을 가하는 경우
이는 사안별로 판단해야 합니다.
일반적으로, 컴파일러 영역의 소유자가 동의하는 한(따라서 그들에게 할당하기만 하면 됩니다), 무언가를 공개하거나 정리하거나 더 일반화하는 변경은 허용해야 합니다.
구체적인 예를 들면, 누군가 MIR 인터프리터를 사용 중이고 무언가를 공개하고 싶어하는 경우, 이는 대체로 문제가 되지 않지만, 일부 함수는 MIR 인터프리터 내의 불변 조건을 유지하기 위해 의도적으로 모듈 또는 크레이트 전용으로 설정되어 있습니다. 그러니 기본적으로 그러한 PR은 관련된 사람들에게 배정하기만 하면 됩니다(대개 이들은 이 부분의 변경에 대해 핑을 받고 싶다고 rustbot에 알려두었기 때문에 어차피 핑을 받게 됩니다).
이러한 API에는 어떤 외부 소비자가 해당 API와 관련이 있는지, 그리고 어떤 목적을 위한 것인지 명시하는 문서 주석을 요구하십시오.
어떤 기여가 더 넓은 범위의 팀 승인을 필요로 할 수 있다고 생각된다면, 제안, 승인, 안정화 문서를 확인하십시오.
이는 겉보기와 달리, 내부용으로 여겨지던 컴파일러 API를 외부 소비자에게 종속시킬 수 있다는 점에 유의하십시오. (rust-lang/ 프로젝트가 아닌) 외부 소비자에게는, 이것이 컴파일러에 상당한 유지보수 부담을 주지 않는 한(예: 리팩터링에 방해가 되지 않는 한) 편의를 제공할 수 있지만, 엄격한 안정성 보장은 약속되지 않는다는 점을 전달하십시오.
PR이 매우 크고 복잡한 경우
리뷰어가 매우 크고 복잡한 PR을 무조건 감내해야 하는 것은 아닙니다. 기여자는 리뷰가 가능하도록 자신의 작업을 분할할 것이 기대되며, 예를 들어 개별적으로 논리적으로 더 자기완결적인, 더 소화하기 쉬운 일련의 PR로 나누는 것이 그 예입니다. 일반적으로 큰 영향을 미치는 변경을 제출하기 전에, 기여자는 관련 팀과 미리 설계에 대해 논의했어야 하며, 따라서 그러한 논의를 참조하는 것은 기여자의 의무입니다.
확신이 서지 않을 때는 PR을 팀의 관심에 맡기고(zulip 스레드를 통하거나, 컴파일러 트리아지 회의에 지명하는 방식으로), 팀이 다음을 결정할 수 있습니다:
- 팀은 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에만 한정됩니다).
되돌리기(Revert)
병합된 PR이 예상치 못한 유의미한 회귀를 유발한 것으로 밝혀지면, 가장 좋은 정책은 이를 신속히 되돌린 뒤 수정 사항과 회귀 테스트가 추가되면 다시 반영하는 것입니다.
이 경우 “유의미한 회귀“는 되돌리기를 승인하는 사람의 판단에 달려 있습니다.
되돌리기가 정당한지 고려할 기준은 다음과 같습니다.
- stable 또는 그 밖의 중요한 기능에서 코드 컴파일을 중단시키거나, 런타임 동작을 바꾸거나, 실제 사용 코드에서 (기본적으로 경고 이상 수준의) 린트를 잘못 트리거하는 버그. 특히 불안정한 기능 게이트 없이도 버그에 도달할 수 있는 경우.
- 버그나 변경 사항(ICE 포함)이 특히 발생시키기 쉬운 경우.
- 버그나 변경이 기여자 경험을 크게 저하시키는 경우.
- 테스트가 불안정하고 신뢰할 수 없는 경우.
이러한 기준이 의심스러울 때, 특히 실제 코드에 영향이 있을 때는 PR을 되돌리십시오. 이는 세 가지 이점이 있습니다.
- 이는 최첨단 사용자(특히 nightly나 beta 사용자)가 HEAD를 계속 사용하고 버그를 보고할 수 있게 하며, 새로운 버그가 어디서 유입되었는지에 대해 더 높은 확실성을 갖게 합니다.
- 원래 PR 작성자와 팀에게서 부담을 덜어주어, 누구도 즉시 고쳐야 한다는 압박을 느끼지 않도록 합니다.
- 이는 중대한 버그나 회귀가 다른 nightly/beta/stable 빌드에 도달하는 것을 막을 수 있습니다.
되돌리기 전에, 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을 끼워 넣을 수 있습니다.
전방 수정(Forward fixes)
회귀를 해결하기 위해 회귀를 유발한 PR을 되돌리는 대신, 원래 PR을 전체적으로 되돌리지 않고 작은 방식으로 보완하는 후속 PR을 올리고 싶은 유혹이 종종 있습니다. 그러나 실제 사용자가 영향을 받았다고 보고한 경우, 다음 중 하나가 사실이 아닌 한 이러한 방식은 강력히 권장되지 않습니다:
- 신뢰도 높은 수정이 이미 bors 큐에 있는 경우.
- 회귀가 릴리스 브랜치(beta 또는 stable)에 도달하여 [백포트]가 필요한 경우. 백포트의 경우 종종 “가능한 한 작은 변경“이 요구됩니다(수정이 새로운 회귀를 유발하지 않도록 하기 위함입니다). 문제가 된 PR은 메인 브랜치에서 여전히 리버트될 수도, 되지 않을 수도 있습니다. 이는
r+할 수 있는 사람의 재량에 맡겨집니다.
PR이 리버트되는 것이 큰 후퇴처럼 느껴질 수 있지만, 대부분의 경우 수정이 확인되면 PR을 리랜드하는 것이 훨씬 쉽습니다. 리버트가 랜딩되도록 허용하면 여러분과 리뷰어가 서둘러 대응해야 한다는 압박이 줄어들고, 이슈를 완전히 해결할 시간을 확보할 수 있습니다. 이는 또한 한 걸음 물러나 테스트 커버리지를 재평가할 기회이기도 합니다.
롤업
모든 리뷰어는 PR이 [롤업]의 일부가 되어야 하는지 여부를 명시적으로 표시하도록 강력히 권장됩니다. 이는 보통 @bors r+ $ROLLUP_STATUS로 PR을 승인하거나 @bors $ROLLUP_STATUS를 사용할 때 이루어지며, 여기서 $ROLLUP_STATUS는 다음 중 하나로 대체됩니다.
rollup=always: 이러한 PR들은 테스트를 깨뜨리거나 성능에 영향을 줄 가능성이 매우 낮습니다. 예시 시나리오:- 변경 사항이 빌드를 실패시킬 가능성이 매우 낮은 문서, 주석 등에 국한됩니다.
- 변경 사항이 성능에 영향을 줄 수 없습니다.
- 여러분의 PR이 문제를 일으킬 수 있거나 동작을 변경하는 변경 사항을 반영하지 않습니다.
- 다만 다른 변경 사항 없는 기능 안정화는 롤업해도 무방할 가능성이 높습니다.
- 확신이 서지 않으면 이 옵션을 사용하지 마십시오!
rollup=maybe:@bors r+가 롤업 상태를 전혀 지정하지 않을 경우의 기본값입니다. 변경 사항이 테스트를 깨뜨리지 않는다는 확신이 다소 부족한 경우 이를 사용하십시오. 다른 범주 중 어느 것에 해당하는지 확신이 서지 않는 경우에도 사용할 수 있습니다. 이것이 기본값이므로, 이전 명령의 롤업 수준을 해제하는 경우가 아니라면 보통 명시적으로 지정할 필요가 없습니다.rollup=iffy: 약간 위험한(즉 “maybe“보다 더 위험한) PR에 이를 사용하십시오. 예시 시나리오:- PR이 크고 비추가적인 경우(참고: 완전히 새로운 테스트 2000줄을 추가하는 것은 롤업해도 괜찮습니다).
- 일반적인 PR 검사로는 확인되지 않는 플랫폼별 변경 사항이 있는 경우입니다.
- MIR 마이그레이트 모드의 영향을 받을 수 있는 경우입니다.
rollup=never: 이는 롤업에 절대 포함되어서는 안 됩니다. 예시 시나리오:- 성능에 영향을 줄 수 있는 경우입니다.
- 불분명한 회귀를 유발할 수 있는 경우(이 PR을 구체적으로 이분 탐색하고 싶을 것이며, 롤업에서는 원인으로 식별하기 어려울 것입니다).
- 실패할 가능성이 높은 경우입니다.
- 그 밖에 롤업하기에 위험한 경우.
- 다음 항목과 지나치게 얽혀 있는 경우입니다.
- LLVM 또는 코드 생성
- 부트스트랩 또는 빌드 시스템
- build-manifest
rollup: 이는rollup=always와 동일합니다.rollup-: 이는rollup=maybe와 동일합니다.
rollup=iffy 또는 rollup=never를 설정할 때는, 그 이유가 명확하지 않다면 왜 이를 선택했는지에 대한 간단한 근거를 승인 코멘트에 포함하십시오.
우선순위
리뷰어는 우선순위를 설정하는 대신 위에 나열된 롤업 상태 중 하나를 설정하는 것이 권장됩니다. Bors는 롤업 상태(never가 가장 높은 우선순위이고 always가 가장 낮은 우선순위)와 PR의 경과 시간을 기준으로 자동 정렬합니다. 우선순위를 변경하는 경우, 다른 PR과의 공정성과 긴급성 사이의 균형을 최선의 판단으로 맞춰 주십시오.
다음은 우선순위 설정에 대한 지침입니다:
- 1-5
- P-high 이슈 수정
- toolstate 수정
- 위 항목을 포함하는 되돌리기
- beta로 지명된 풀 리퀘스트
- 서브모듈/서브트리 업데이트
- 5 이상
- P-critical 이슈 수정
- 10 이상
- 비트롯(bitrot)이 발생하기 쉬운 풀 리퀘스트(특히 많은 파일을 건드리는 매우 큰 것)
- 긴급한 풀 리퀘스트(예: 긴급한 되돌리기)
- beta 백포트
- 20 이상
- 모든 롤업보다 앞서 처리되어야 하는 높은 우선순위
- 대기열 내 다른 풀 리퀘스트에 의해 다시 깨질 위험이 높은 것을 수정하거나 변경하는 경우
- 1000
- 절대적으로 중요한 수정
- 릴리스 승격
우선순위를 설정할 때, 그 이유가 명확하지 않은 경우 승인 코멘트에 이를 선택한 이유를 간단히 포함해 주십시오.
r+에 대한 기대사항
bors 권한은 이분법적입니다: 봇은 여러분이 어떤 코드에 익숙하고 어떤 코드에 익숙하지 않은지 알지 못합니다. 따라서 신중하게 사용해야 합니다. 잘 알지 못하는 코드에 r+를 하지 마십시오 – 그런 코드를 리뷰하는 것은 분명히 가능하지만, 최종 r+는 다른 사람에게 넘기도록 하십시오.
마찬가지로, 리뷰를 수행한 당사자가 아니고, 리뷰가 이루어진 이후 코드가 실질적으로 변경되지 않았으며, 해당 인물이 다른 기여자가 자신을 대신하여 r=을 사용해도 좋다고 명시적으로 밝힌 경우가 아니라면, 결코 r=username 명령을 실행하지 마십시오.
리베이스는 괜찮으며 종종 필요하지만, 기능상의 변경은 일반적으로 재리뷰가 필요합니다. PR 작성자가 개별 리뷰 코멘트에 답하는 것에 더해 마지막 리뷰 이후 변경된 사항에 대한 간단한 요약을 작성해 주면 리뷰어에게 매우 도움이 됩니다.
봇 사용에 관한 bors 문서를 참고하십시오.
리뷰의 사회적 측면
무엇보다도 PR 작성자와 컴파일러 리뷰어 모두 행동 강령을 준수할 것이 기대됩니다. 간단히 말해, 리뷰어는 변경 사항에 동의하지 않더라도 PR 작성자를 존중해야 합니다.
리뷰어는 PR 작성자의 관점에서도 문제를 고려하도록 권장됩니다. 절차상의 이유나 리뷰어의 여력 부족으로 인해 어떤 해결도 없이(컴파일러가 현재로서는 그러한 변경을 받아들일 준비가 되어 있지 않을 수 있다는 결론도 해결에 포함되지만, 그런 경우에도 PR 작성자에게 기여에 대해 감사를 표해야 합니다) 몇 달간 변경 사항이 정체되고 계속해서 병합 충돌이 누적된다면, 매우 답답한 상황이 될 수 있습니다.
논의가 격해지고 있다면 모더레이션 팀에 개입을 요청하십시오.
보조 도구 정책
T-compiler의 임무는 정상적으로 동작하는 Rust 컴파일러(rustc)를 출시하는 데 필요한 일을 하는 것이지만, rustc는 단독으로 동작한 적이 없습니다. 컴파일러 툴체인과 함께 제공되든 환경에서 기대되든, 항상 사용에 추가 도구를 필요로 했습니다. 다음은 rustc와 그것이 함께 사용되거나 요구할 수 있는 도구들에 대한 기대사항 일부를 명확히 하고, 이미 암묵적으로 존재하는 몇 가지 기대사항을 명시적으로 드러내려는 시도입니다.
정의
- 컴파일 환경(compilation environment) - 제공된 도구가 실행되는 호스트 시스템으로, 파일시스템 및 환경 변수와 같은 일시적 상태를 포함합니다.
- 실행(invoke) - 프로세스 생성, 라이브러리 로딩, 또는 컴파일 환경이 지원할 수 있는 다른 수단을 통해 어떤 아티팩트의 코드를 실행하는 것입니다.
- 중간 산출물(intermediates) - 컴파일러 도구를 실행하여 출력된, 컴파일러 도구에 입력으로 제공될 아티팩트입니다.
- 컴파일러 도구 - Rust 툴체인의 일부로 제공되는 산출물로서, 코드를 컴파일하고 바이너리 코드 객체로 링크하는 과정의 일부로 호출되는 것입니다.
- 바이너리 도구(binary tool) - 바이너리 코드 객체를 생성하거나 편집하는 데 사용되는 컴파일러 도구입니다.
- 제공 도구 - T-compiler와 T-release의 지원을 받아 Rust 툴체인의 일부로 배포되는 바이너리 도구 또는 컴파일러 도구입니다. 여기에는 특히
rustc,rust-lld,rust-objcopy와 같은 바이너리 도구가 포함됩니다. - 보조 도구(supplemental tool) - 컴파일러 팀과 릴리스 팀이 배포하지는 않지만, 제공된 도구와 함께 사용될 수 있는 컴파일러 도구입니다. 여기에는 특히
ld.bfd,link.exe,llvm-bolt, 그리고 “링커 스크립트”(Link Editor Command Language로 작성된 프로그램)와 같은 바이너리 도구가 포함됩니다.
보조 도구의 사용
rustc를 포함한 어떤 제공된 도구든, 명시적인 사용자 입력을 요구하지 않고도 보조 도구를 암묵적으로 실행할 수 있습니다. 제공된 도구는 어떤 도구를 실행할지 결정하기 위해 컴파일 환경을 조사할 수 있습니다.1 보조 도구가 실행될 때 컴파일 환경은 관례적이거나 특이한 방식으로 구성되어 있을 것으로 기대될 수 있습니다.
관례적 기대의 예로는 다음이 있습니다
link.exe라는 이름의 실행 파일이 인자로 주어진 경로에 위치한 바이너리 코드 객체를 링크하는 작업을 수행할 것으로 기대되는 경우, 또는- 유닉스 도구가 POSIX에서 지정한 방식으로 인자를 따를 것으로 기대되는 경우, 또는
$CC와 같은 환경 변수가 C 컴파일러 경로를 담고 있을 것으로 기대되는 경우입니다.
특이한 기대는 관례적 기대와 비슷해 보이지만 이를 부당하게 적용하는 경우로, 예를 들어 유닉스 시스템(macOS까지 포함하여)이 GNU 사용자 영역을 가진 리눅스 시스템과 유사하다고 가정하는 것입니다. 또는 순전히 가정된 것일 수도 있는데, 예를 들어 컴파일 환경 내 데이터가 Rust 툴체인의 다른 제공 도구에 의해 생성되거나 수정되었다고 가정하는 경우가 그렇습니다.
이러한 기대는 컴파일 환경의 내용물이 사용하기에 안전한지, 올바르게 작동하는지, 올바르게 명명되었는지를 판별할 필요가 없다는 데까지 확장됩니다. 파일시스템은 특정 방식으로 구성되어 있을 것으로 기대될 수 있으며, 파일에 부여된 어떤 이름이든 올바른 레이블로서 암묵적으로 신뢰될 수 있습니다. 보조 도구를 기대되는 경로에서 찾고, 우리가 어떤 도구에 어떤 방식으로 의존하는지 문서화하려는 합리적인 시도가 이루어져야 하지만, 이것이 필수는 아닙니다. 환경으로부터 보조 도구를 계속 실행할 필요조차 없는데, 이는 릴리스 팀과 컴파일러 팀의 재량으로 제공된 도구가 확장되어 환경에서 발견된 보조 도구를 대체하는 명시적 목적으로 사용될 수 있기 때문입니다.
제공된 도구가 보조 도구를 실행해야 하며 그 출력이 컴파일 과정의 다른 부분을 위한 중간 산출물로 사용된다고 판단한 경우, 그 결과 출력은 해당 도구가 출력하는 형식을 엄격히 준수할 것으로 기대될 수 있습니다. 또한 링크 가능하거나 로드 가능하거나 심지어 실행 가능한 바이너리 코드 객체여야 한다거나, 단순히 Rust 툴체인이 기대하는 추가 메타데이터 섹션을 유지해야 한다는 등 다른 요구 사항을 충족해야 할 것으로 기대될 수도 있습니다.
제공된 도구에 대한 어떤 기대도 위반되지 않았다고 가정할 때, 제공된 도구는 실행 가능하거나 동적으로 로드 가능한 바이너리 코드 객체를 생성할 때 최소한 해당 객체의 바이너리 형식 명세를 준수하는 객체를 생성해야 합니다. 중간 산출물은 대개 어떤 바이너리 형식을 준수하더라도 올바르게 포맷되어 있지 않을 수 있습니다. 문서화가 부족한 바이너리 형식의 경우, 준수 여부는 컴파일러 팀이 원하는 대로 해석될 수 있습니다.
컴파일러 팀 입장에서, 이는 대체로 원하는 대로 환경에서 항목들을 호출하는 것을 의미합니다. 예를 들어 소스에 정의되지 않은 동작이 없다고 가정하고, 그저 출력이 올바르게 나오도록 하려고 시도합니다. 이상적으로는 디버거 등을 포함한 “네이티브” 툴체인이 우리 코드와 잘 작동할 만큼 충분히 준수하고 싶지만, 사용하고 싶을 수 있는 도구의 목록은 무한합니다.
보조 도구 이슈 트리아지하기
보조 도구에 관한 이슈를 해결하기 전에, 우리 툴체인이 의존하는 기대 사항이 “당연“하지 않다면 문서화되어야 합니다. 예를 들어 최소 도구 버전에 대한 의존성, 특히 그것이 운영 체제에 기본으로 딸려 오는 버전보다 높은 경우가 그렇습니다. 당연하지 않은 기대 사항만 문서화하는 이유는 이 문서 도입부의 장황한 정의의 정의 때문입니다. 아무것도 전제하지 않는다면 컴파일러가 어떻게 작동하는지 설명하기에 충분한 어휘를 정의하는 데 오랜 시간이 걸릴 수 있습니다.
누군가 컴파일러 플래그나 설정을 통해 보조 도구를 능동적으로 포함시킨 뒤 버그를 신고했다면, 보조 도구 없이도 해당 객체가 올바른 형식인지 판단해 보십시오. 그러한 경우라면, 보통은 고치려 하지 말고 이슈를 종료해야 합니다
때로는 rustc가 암묵적으로 호출했기 때문에 보조 도구가 관련되는 경우가 있습니다. 툴체인이 기능적으로 이를 대체할 수 있는 제공 도구를 갖추고 있다면, 이슈를 해결하기 전에 그렇게 하는 편이 바람직합니다.
문제가 특정 운영 체제의 지역적 관례 준수에 관한 것이라면, 이를 위한 별도의 타깃이 있는 한 그에 대응하기 위한 어느 정도의 노력을 기울일 수 있습니다. 자원이 그렇게 하기에 개방적이고 이용 가능하다면, 보통은 참여를 시도해야 합니다.
-
이는 이름으로 어떤 도구든 OS가 제공하는 실행이 가능하거나 선호된다는 사실에서 비롯되는 기능적 결과이며, 그것은 곧 암묵적으로든 명시적으로든
$PATH를 뒤진다는 것을 의미합니다. ↩
서드파티 및 아웃오브트리 크레이트 정책
이 정책은 컴파일러에서 사용할 아웃오브트리 크레이트를 만들고 컴파일러 내에서 서드파티 크레이트를 사용하는 것에 대한 가이드라인을 설명합니다.
아웃오브트리 크레이트
이 정책의 주요 목표 중 하나는 컴파일러에서 사용되는 아웃오브트리 크레이트(적어도 컴파일러 팀이 관리하며 rust-lang에 있는 것들)를 구성하는 방식에 일관성이 있도록 하고, rust-lang/rust와 이러한 크레이트 전반에 걸쳐 경험이 균일하도록 하는 것입니다.
컴파일러의 일부는 언제 아웃오브트리 크레이트로 분리해야 합니까?
이는 컴파일러 팀 구성원의 재량에 맡겨져 있지만, 주간 트리아지 회의에서 문제를 제기하거나 승인 결정을 통해 비동기적으로 나머지 팀과 논의해야 합니다. 크레이트가 워킹 그룹의 산물인 경우, 아웃오브트리 크레이트가 적합하다는 합의가 워킹 그룹 내에 이미 있어야 합니다.
아웃오브트리 크레이트를 만드는 것을 고려할 때, 크레이트가 얼마나 범용적이어야 하는지와 널리 사용될 경우 이로 인해 늘어날 유지보수 부담 사이의 균형을 고려할 가치가 있습니다.
컴파일러 크레이트는 어디에 위치해야 합니까?
아웃오브트리 컴파일러 크레이트는 rust-lang 조직에서 호스팅되어야 합니다 - 이는 외부 인프라 도구와의 통합을 단순화하며 GitHub에서 기존 팀 권한을 물려받게 됩니다. 어떤 문서에서든 컴파일러 팀과 적절한 워킹 그룹이 해당 크레이트에 대한 책임이 있다는 점이 명확히 드러나야 합니다. 다른 조직이나 개인 저장소에서 프로토타입으로 시작하는 것은 권장되지 않습니다.
개인 계정이나 다른 조직에 있는 기존 아웃오브트리 크레이트를 이전할 수 있습니까?
예, 이는 권장됩니다. 이를 위해서는 팀과 논의하고 저장소 이전에 대한 GitHub 문서를 숙지한 다음 이전을 수행하도록 준비하십시오. 이전이 완료되면 원래 계정이나 조직에 리다이렉트가 남게 되며, 이는 이전된 저장소의 새로운 포크 이름과 충돌하게 됩니다 - 이를 해결하려면 GitHub에 이메일을 보내야 합니다.
이 크레이트들은 누가 소유합니까?
새 버전이 게시되고 풀 리퀘스트가 검토되도록 책임지는 명확한 소유자가 있도록, 컴파일러 팀 구성원이나 워킹 그룹이 크레이트에 대한 느슨한 소유권을 갖는 것이 바람직합니다.
때로는 프로젝트 구성원이 아닌 사람이 크레이트의 주 소유자이고, 주 메인테이너에게 더 이상 연락이 닿지 않을 경우를 대비해 컴파일러 팀이 “예비” 메인테이너로 추가되는 경우가 있습니다. 이는 권장되지 않지만 경우에 따라 정당화될 수 있습니다.
이 크레이트들은 어떤 이름을 붙여야 합니까?
크레이트 이름은 새로운 아웃오브트리 크레이트가 컴파일러 팀에 제안될 때 논의됩니다.
크레이트 이름은 경우에 따라 다르게 정해집니다. 본질적으로 컴파일러와 연결된 크레이트는 rustc_ 접두사가 붙은 이름을 사용하는 것이 유리합니다. 이는 잠재적 사용자에게 해당 크레이트가 얼마나 stable할지를 나타내는 지표입니다. 좀 더 범용적인 다른 크레이트들은 컴파일러와 분리된 이름을 갖게 됩니다.
아웃오브트리 크레이트에 대한 검토 정책에 제한이 있습니까?
일반적으로 워킹 그룹과 팀 구성원들은 자신들의 그룹에 가장 적합한 방식을 사용하여 크레이트를 자유롭게 유지보수할 수 있지만, 컴파일러와 아웃오브트리 크레이트 전반에 어느 정도의 통일성이 있도록 몇 가지 제한이 있습니다:
rust-lang/rust에서 r+ 권한을 가진 모든 사람은 PR을 검토하고 승인할 수 있어야 합니다.- 가능한 경우, 크레이트(또는 관련 워킹 그룹)의 활발한 참여자만이 해당 크레이트의 검토 순환에 있으면 됩니다.
- 해당 리뷰어가 워킹 그룹이나 크레이트 유지 관리에 적극적으로 참여하고 있다면, Rust 전체에 대한 r+ 권한이 없는 추가 리뷰어를 크레이트에 두어도 무방합니다.
- 주요 풀 리퀘스트에는 여러 명의 검토자가 있어야 합니다.
out-of-tree 크레이트에는 무엇이 요구됩니까?
out-of-tree 크레이트는 다음을 반드시 준수해야 합니다:
- 컴파일러 팀이 유지 관리하는 경우(컴파일러 자체가 그러하듯이) 특별한 이유가 없는 한 Apache 2.0과 MIT로 이중 라이선스를 적용해야 합니다.
- 다른 라이선스를 원할 경우, 새 크레이트를 제안할 때 이를 언급하여 컴파일러 팀 구성원들의 동의를 받아야 합니다. 다른 요구사항(다른 프로젝트에서 이식한 코드 등)이 없는 한 tidy가 허용하는 라이선스를 우선적으로 사용하십시오.
- Rust의 행동 강령을 준수하십시오.
- 해당 크레이트가 Rust 컴파일러 팀과 적절한 워킹 그룹에 의해 유지 관리된다는 점을 명시하십시오.
- 특히, 이는 향후 사용자들을 위해 예상되는 유지 관리 수준과 안정성 수준을 상세히 기술해야 합니다.
- 또한 이 저장소 내 워킹 그룹 상세 정보로 연결되는 링크도 포함해야 합니다.
- 이 페이지 하단 목록에 추가되어야 합니다.
- 시맨틱 버저닝을 따라야 합니다.
- GitHub 병합 큐와
@triagebot을 사용해야 합니다. - 기존 트리아지 프로세스와 호환되는 레이블을 사용해야 합니다. 이를 통해 out-of-tree 크레이트에서 지명된 이슈들이 트리아지 회의에서 논의될 수 있습니다.
- 예:
T-compiler,I-compiler-nominated(전체 목록은 추후 결정 예정)
- 예:
- 가능하다면 신뢰할 수 있는 게시(trusted publishing)를 사용해야 합니다.
- 컴파일러 팀은 crates.io에서 해당 크레이트의 소유자로 추가되어야 합니다.
out-of-tree 크레이트에 커뮤니티 인프라가 요구됩니까?
트리 외부 크레이트를 위해 커뮤니티 인프라(예: Zulip 서버/스트림)를 만들어야 한다는 요구 사항은 없습니다. out-of-tree 크레이트가 대규모 기여자·사용자 커뮤니티를 형성하게 되면 이것이 바람직할 수 있으나, 그렇지 않은 경우 처음에는 워킹 그룹이나 컴파일러 팀 스트림을 사용해야 합니다.
주 Rust Zulip 서버에서 이슈와 PR에 자동 링크되는 링크화 기능은 요청 시 추가할 수 있습니다.
out-of-tree 크레이트 작업에 관한 안내가 있습니까?
rustc-dev-guide의 Using external repositories를 참고하십시오.
out-of-tree 크레이트에서 안정화/시맨틱 변경 사항은 어떻게 처리해야 합니까?
언어에 대한 안정화 또는 시맨틱 변경을 초래하는 out-of-tree 크레이트의 모든 변경 사항에는 언어 팀을 참여시키는 것이 중요합니다. rust-lang/rust에 대한 PR에서 서브모듈 변경 사항은 마치 그 변경이 rust-lang/rust에 직접 구현된 것처럼 적절히 레이블(예: relnotes, t-compiler, t-lang)을 붙여야 하며, 컴파일러나 해당 트리 외부 크레이트에 익숙하지 않은 사람에게 명확하지 않은 경우 변경 사항에 대한 설명을 포함해야 합니다.
out-of-tree 크레이트에서 릴리스는 언제 이루어져야 합니까?
트리 외부 저장소에서 패치를 병합할 권한을 가진 팀 구성원은 누구든 적절하다고 판단될 때 새 릴리스를 낼 풀 리퀘스트를 생성할 수 있으며, 다른 모든 풀 리퀘스트와 마찬가지로 다른 팀 구성원의 검토를 받아야 합니다. 새 릴리스는 테스트를 통과하고 새 릴리스를 정당화할 만한 변경 사항(예: 의존성 업그레이드, 새 기능, 버그 수정 등)이 있는 버전의 크레이트여야 합니다.
저장소에 신뢰할 수 있는 게시(trusted publishing)가 구성되어 있다면 이는 자동으로 crates.io에 게시되어야 하며, 그렇지 않다면 팀 구성원이 수동으로 crates.io에 게시할 수 있습니다(그리고 그럴 권한을 가지고 있어야 합니다).
요약하면, out-of-tree 크레이트를 설립하는 절차는 다음과 같습니다:
- 적절한 경우, out-of-tree 크레이트의 필요성을 워킹 그룹 내에서 논의하고 확인하십시오.
- 이 문서를 수정하여 아래 목록에 크레이트를 추가하는 PR을 생성하십시오.
@rfcbot merge를 사용하여 컴파일러 팀 구성원들의 동의를 얻으십시오. - 팀 저장소를 사용하여
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와 통합하기 전에 필요한 초기 개발을 수행하십시오.
- 시맨틱 버저닝을 따라 초기 버전을 게시하십시오.
- 해당 크레이트를 인트리 크레이트의 의존성으로 추가하고 사용을 시작하십시오.
서드파티 크레이트
컴파일러 내에서 기존 서드파티 크레이트의 기능을 사용하는 것이 바람직할 때가 있습니다.
서드파티 크레이트를 컴파일러 의존성으로 추가할 수 있는 경우는 언제입니까?
컴파일러에 포함되는 서드파티 크레이트는 잘 유지 관리되는 것이 바람직하며, 가능한 경우 컴파일러 팀 구성원이 메인테이너로 추가되는 것이 바람직합니다. 이 결정을 내리기 전에 컴파일러 팀의 나머지 구성원들과 상의해야 합니다.
아웃오브트리 크레이트에 대한 서드파티 의존성은 어떻습니까?
컴파일러에서 사용되는 모든 컴파일러 팀 유지 관리 크레이트에는 동일한 정책이 적용됩니다.
아웃오브트리 크레이트 목록
이 절에는 기존의 아웃오브트리, 컴파일러 팀 유지 관리 크레이트 목록이 포함되어 있습니다:
우선순위 지정
컴파일러 팀이 우선순위가 높은 이슈를 신속히 식별할 수 있는 것이 중요하며, 이에 따라 아래에서 설명하는 우선순위 지정 프로세스가 마련되었습니다.
일반 프로세스
- 이슈의 현재 상태를 파악합니다
- 가능하다면 이슈를 진전시켜 보십시오(예: 이슈 작성자/리뷰어에게 업데이트를 요청).
- 해당 이슈에 대한 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 저장된 답글을 사용할 수 있습니다.
우선순위는 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문서에는 요소를 드롭하는 순서가 변경될 수 있다고 명시되어 있습니다)
- 코드가 명시적으로 명세되지 않은 기능을 사용하는 경우(예:
- 우선순위를 낮출 수 있는 완화 조건:
- 기능 게이트 없이 접근 가능한 기능 게이트된 기능
- 우선순위를 낮출 수 있는 완화 조건:
- 해당 패턴이 발생할 가능성이 매우 낮은 경우
- 우선순위를 낮출 수 있는 완화 조건:
- 실제 영향이 있는 건전성 결함
- 우선순위를 낮출 수 있는 완화 조건:
- 유발하기 어려운 건전성 결함
- stable에 영향을 미치지 않을 건전성 결함(예: 그 결함이 게이트된 unstable 기능을 사용하는 경우).
- 우선순위를 낮출 수 있는 완화 조건:
- 진단이 매우 흔하고 상황이 매우 혼란스러운 진단 회귀
- 흔한 시나리오나 코드 패턴에 대한 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 개발에 Rust를 반영하기 위해 LLVM 업스트림과 협업합니다 | rustc, LLVM | #t-compiler/llvm |
| MIR 최적화 | MIR 최적화를 작성하고 MIR을 더 최적화하기 쉽게 리팩터링합니다. | MIR transform | #t-compiler/mir-opts |
| Parallel-rustc | rustc의 병렬 컴파일을 기본값으로 만듭니다 | rustc | #t-compiler/parallel-rustc |
| Polonius | “NLL 2.0“과 유사한 “Polonius 분석”을 rustc에 통합하는 방안을 탐구합니다 | borrow check, rust-lang/polonius, rust-lang/datafrog | #t-types/polonius |
| RLS 2.0 | IDE에 맞춘 새로운 컴파일러 아키텍처를 실험합니다 | rust-analyzer | #t-compiler/rust-analyzer |
| Rust Compiler Development Guide | rustc-dev-guide를 “완전하게” 만들어 컴파일러를 더 쉽게 배울 수 있도록 합니다 | rustc, rustc-dev-guide | #t-compiler/rustc-dev-guide |
| Enzyme | GPU 오프로딩을 위한 실험적 LLVM 기능을 노출합니다 | Project Goal | #wg-autodiff |
| Linker | 링커 관련 이슈와 컴파일러 내 통합을 처리하는 것을 돕습니다 | - | #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 |
| Self-Profile | -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) 문서가 더 낫습니다. 문서가 없는 것보다는 불완전하거나 작업 중인(WIP) 문서가 더 낫습니다. 장의 하위 부분에서 누락된 부분을 추적하는 이슈가 포함된 스텁 형태의 TODO는, 그렇지 않으면 독자를 혼란스럽게 만들 수 있는 문서상의 알려진 공백을 나타내는 데 유용합니다.
문서화 대상 영역에 자신 있는 기여자는 검토 없이 자신의 문서를 직접 병합해도 무방합니다. 이러한 이유로 dev guide는 각 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 큐에 대한 부담이 줄어듭니다.
서브트리 동기화
dev guide는 메인 rust-lang/rust 저장소의 josh 서브트리입니다. 이로써 컴파일러 기여자가 컴파일러 자체의 변경과 함께 개발 가이드의 문서를 갱신하기가 더 쉬워집니다.
이로 인해 dev guide에 이루어진 변경 사항을 dev guide 자체 저장소와 rust-lang/rust 저장소에 유지되는 사본 간에 수동으로 동기화하는 과정이 필요합니다.
- 마지막 서브트리 동기화 이후 최소 1주일을 기다리십시오.
- 자동으로 열리는 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를 수행하는 방법에 대한 문서는 dev guide의 README에서 확인할 수 있습니다.
-
자동으로 열린 rustc-pull에 dev guide에 대한 실제 변경 사항이 포함되어 있지 않다면, 서브트리 동기화는 실제 동기화할 변경 사항이 생길 때까지 지연될 수 있습니다. ↩
crates.io
crates.io는 Rust 생태계를 위한 크레이트를 호스팅합니다.
외부 링크
- 소스 코드:
rust-lang/crates.io - 호스팅 위치: https://crates.io/
- 메인테이너: crates.io team
- 운영 가이드 (팀 구성원만 이용 가능)
docs.rs
docs.rs는 crates.io에 게시된 크레이트를 위한 문서를 호스팅하는 웹사이트입니다.
외부 링크
- 소스 코드: rust-lang/docs.rs
- 호스팅 위치:
docsrs.infra.rust-lang.org(bastion 뒤에 있음 – 연결 방법) - 메인테이너: docs.rs team
- 개발 가이드
Rustdoc
Rust의 rustdoc 팀 구성원은 Rustdoc 도구를 유지 관리하고, 성능을 개선하며, rustdoc 기능의 안정화를 검토하는 일을 담당합니다.
저희는 팀의 프로세스, 정책, 작업 방식을 문서화하기 위해 Forge를 사용합니다. 컴파일러와 rustdoc이 어떻게 동작하는지, 그리고 개발 환경을 설정하는 방법에 대해 알고 싶으시다면 rustc-dev-guide를 참고하시기 바랍니다.
- Calendar
- rustdoc 팀의 캘린더는 어떻게 구독합니까?
- Meetings
- rustdoc 팀은 어떤 회의를 진행하며 어떻게 참석할 수 있습니까?
- Membership
- rustdoc 팀 구성원에게 요구되는 사항은 무엇이며 어떻게 가입합니까?
- Resources
- 기여자와 팀 구성원이 활용할 수 있는 유용한 자료에는 무엇이 있습니까?
- Review Policy
- 검토하기 쉬운 기여는 어떻게 합니까? 팀 구성원으로서 리뷰는 어떻게 시작합니까?
- 제안, 승인 및 안정화
- rustdoc 팀에 변경 사항을 어떻게 제안합니까? 제 변경 사항에는 어떤 승인이 필요합니까?
캘린더
Rustdoc 팀의 일정은 Rust 프로젝트의 rust-lang/calendar 저장소에서 확인할 수 있습니다.
미팅
rustdoc 팀 회의 일정은 여기에서 확인할 수 있습니다.
참석하고자 하는 분들에게 시간과 안건을 미리 알리기 위해 한 달 전에 새로운 스레드가 열립니다.
누구나 미팅에 참여할 수 있습니다.
안건에 항목을 추가하고 싶은데 rustdoc 팀 구성원이 아니라면, 다음 회의에 대한 Zulip 스레드에 논의하고 싶은 항목에 대해 댓글을 남겨 주십시오. rustdoc 팀 구성원이 이를 살펴보고 안건에 추가할 수 있는지, 그리고 그 우선순위는 어떻게 되는지 결정합니다. 해당 항목이 이미 논의된 적이 있다면, 설명을 제공하거나 이전 논의가 이루어진 곳으로의 링크를 제공합니다.
멤버십
이 절에서는 rustdoc 팀의 구성원 자격에 대해 다룹니다.
멤버십에 이르는 길
rustdoc 도구에 기여하고자 하는 사람들은 일반적으로 버그 수정이나 새로운 기능 구현으로 시작합니다. 안내나 도움이 필요하시면 Zulip의 t-rustdoc 채널에서 주저하지 말고 물어보십시오!
Rustdoc 구성원
개인이 일정 기간 동안 꾸준히 기여해 온 경우, rustdoc 팀 구성원으로 승격될 수 있습니다(아래 의사 결정 방식 절 참고).
이러한 승격이 적절한 정확한 조건을 정의하기는 어렵습니다. 멤버로 승격되는 것은 단순히 여러 항목을 체크하는 것으로 결정되지 않습니다. 하지만 일반적으로 다음 세 가지를 입증했을 때 준비가 되었다고 여겨집니다.
- “지속력” – 해당 인물은 어떤 형태로든 정기적으로 기여하고 있어야 합니다. 예를 들어 이는 몇 가지 프로젝트를 완료했음을 의미할 수 있습니다.
- “독립성과 친숙함” – 작업을 맡을 때 최소한 자신의 “rustdoc 영역” 범위 내에서는 어느 정도 독립적으로 행동해야 합니다. 또한 간단한 PR에 대해서는 다른 사람을 멘토링할 수 있을 정도가 되어야 합니다.
- “우호성” – rustdoc 팀 구성원은 Rust 조직의 일원이 되며 행동 강령과 관련하여 더 높은 기준을 적용받습니다. 이들은 CoC의 문구뿐 아니라 그 정신도 준수해야 합니다.
멤버로 승격되면 여러 권한이 따라옵니다.
-
멤버는
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메일링 리스트에도 구독됩니다. 메일링 리스트 구독이 어떻게 동작하는지 확인하려면 이 파일을 참고하십시오. 둘 다 발송량이 매우 적은 메일링 리스트입니다(연간 몇 통 정도).all@rust-lang.org에 관해서는, 모든 기여자에게 소식을 전하기 위한 방법입니다. 이 주소로부터 스팸을 보내는 일은 없을 것입니다.
rustdoc 팀 내의 역할
Rustdoc에는 여러 개의 서로 다른 영역이 있으며 팀 구성원 모두가 그 모든 영역에서 작업하는 것은 아닙니다. 현재 세 가지 주요 영역이 있습니다:
- 프론트엔드: 검색, 프론트엔드 기능, 프론트엔드 설정을 포함하여 HTML/CSS/JS 및 (HTML) UI/UX 변경과 관련된 모든 것입니다.
- JSON 백엔드:
rustdoc-json-types크레이트를 포함하여--output-format=json백엔드 작업입니다. - 내부: rustdoc의 내부, 즉 컴파일러와의 상호작용, doctest, rustdoc 내부 코드 표현 생성, 명령줄 인자 파싱, lint, 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개월간 비활동 시 자동 전임 구성원 상태 전환
구성원 또는 메인테이너가 rustdoc에서 6개월간 비활동 상태였다면, 전임 구성원 상태로 전환됩니다.
자료
컨트리뷰터와 팀원에게 유용한 다양한 자료가 있습니다.
- rustdoc internals
- rustc 개발자 가이드
- 컴파일러 내부 구조 및 개발 환경 설정에 관한 문서입니다.
- rustc 생성 문서
- rustdoc 생성 문서
- 컴파일러 소스에 대한 rustdoc 출력
기여자나 컴파일러 팀 구성원에게 유용할 만한 추가 자료가 있다면, 이곳에 추가하는 풀 리퀘스트를 자유롭게 제출해 주십시오.
검토 정책
rustdoc 팀은 컴파일러 팀과 동일한 리뷰 정책을 따릅니다. 이에 대해서는 해당 챕터를 참고하십시오. 이에 대해서는 해당 챕터를 참고하십시오.
또한 다음 사항에 유의하는 것이 중요합니다:
- rustdoc 팀의 구성원이 아니더라도, 누구나 코드 리뷰의 형태로 의견을 제시할 수 있습니다.
- 오직 팀 멤버만이 풀 리퀘스트의 병합을 승인할 수 있습니다.
- 새로운 기능은 보통 더 심층적인 리뷰의 대상이 되며, RFC나 FCP를 거쳐야 할 수도 있습니다.
제안, 승인 및 안정화
rustdoc에 기여할 때는 실험이나 리팩터링을 진행할 권한을 얻기 위해서든, 기능을 안정화할 때든 피드백과 승인을 모아야 하는 경우가 매우 흔합니다. 이 문서는 rustdoc 팀이 승인 결정을 내리기 위해 사용하는 다양한 절차와 각 절차를 언제 사용해야 하는지를 정리하는 것을 목표로 합니다.
승인
팀이 제안을 승인할 때 사용할 수 있는 메커니즘은 두 가지입니다(모든 승인 메커니즘이 각 제안 방식에 적합한 것은 아니므로 아래를 참고하십시오):
- r+
- 제안(RFC 또는 FCP)은 병합 승인이 되면 r+ 처리됩니다.
- r+는 PR을 승인하는 데에만 사용할 수 있습니다.
- FCP
- 최종 의견 수렴 기간에는 제안을 승인하기 위해 rustdoc 팀 과반수(전체 구성원 중 2명을 제외한 인원)의 서명이 필요하며, 그 후 10일의 대기 기간을 거칩니다.
- FCP는 모든 형태의 제안을 승인하는 데 사용할 수 있습니다.
제안
rustdoc 팀에 변경을 제안하는 방법에는 세 가지가 있습니다. 적절한 선택은 아래에서 설명하는 제안의 성격에 따라 달라집니다.
- rustdoc zulip thread에서 논의를 시작하십시오.
- 이것이 선호되는 방법입니다. 이 방법은 결국 팀이 대폭적인 변경을 요구하거나 심지어 거부할 경우, 사용자가 구현에 너무 많은 시간을 낭비하는 것을 방지할 수 있습니다. 논의 후 수용되면, 변경 사항에 따라 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를 작성하도록 요청해야 합니다. 특정 기여에 대해 풀 리퀘스트 승인만으로 충분하다면(아래 참조), 승인 절차를 시작할 수 있습니다.
승인을 받기 전에 실험적으로 코드를 작업해도 됩니까?
물론입니다! 풀 리퀘스트를 작업하거나 코드를 작성하는 것은 자유입니다. 하지만 그러한 풀 리퀘스트는 실험적인 것으로 표시되어야 하며, 병합되어서는 안 되고, (사람들이 원하지 않는 한) 누구도 이를 검토할 의무가 없습니다.
좋은 제안이란 무엇입니까?
좋은 제안은 다음 사항을 다룹니다.
- 동기: 이 제안이 왜 필요합니까? 이것이 어떤 문제를 해결합니까? 그 문제는 왜 중요합니까?
- 설계: 무엇을 제안하고 있습니까?
- 구현 노트: 보통은 구현에 대해 이야기할 필요가 없지만, 특별히 언급할 만한 핵심 사항(예: 구현하기에 매우 침습적이었다는 점)이 있다면 여기에 적으면 됩니다.
- 선례, 링크, 관련 자료: Haddock, Wikipedia, Racket와 같은 다른 문서 웹사이트에도 비슷한 제안이 있었습니까?
- 대안, 우려 사항, 핵심 결정: 고려된 대안이 있었습니까? 그렇다면 왜 이 설계를 선택했습니까?
거버넌스
리더십 위원회
리더십 위원회는 Rust 프로젝트 내 팀들을 대표하는 집단으로, 팀 간 조정과 Rust 프로젝트의 성공적인 운영을 보장하는 임무를 맡고 있습니다.
리더십 위원회를 관장하는 정책은 리더십 위원회 장에 명시되어 있습니다.
조정(Moderation)
모더레이션 팀은 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) 팀은 우산형 팀입니다. 즉, 직접적인 구성원은 없고 하위 팀 대표자만 있습니다.
위원회는 발사대의 각 하위 팀에 더 적합한 상위 팀을 찾거나 만들기 위해 노력해야 하며, 이후 해당 하위 팀들을 새로운 상위 팀으로 이동시켜야 합니다.
경우에 따라서는 적합한 상위 팀이 이미 존재하지만 아직 하위 팀을 받아들일 준비가 되어 있지 않을 수 있으며, 이런 경우 런칭 패드가 임시 거처 역할을 할 수 있습니다.
런칭 패드는 또한, 폐지되거나 재편된 팀의 하위 팀들이 그 폐지나 재편 과정에서 조직 내 다른 곳에 명시적으로 배치되지 않은 경우, 그 하위 팀들의 기본 거처 역할도 합니다.
위원회는 모든 하위 팀에 대해 새로운 상위 팀을 찾는 작업이 적절히 진행되고 있는지 확인하기 위해 6개월마다 발사대 내 하위 팀 구성을 검토해야 합니다. 다른 최상위 팀과 마찬가지로, 위원회가 더 이상 필요하지 않다고 판단하면 발사대 팀은 폐지될 수 있으며(위원회 내 대표권도 제거됩니다). 런칭 패드 팀을 폐지하는 절차는 다른 최상위 팀의 경우와 동일합니다. 또는 위원회는 발사대 팀에 자체 관할을 부여할 수도 있습니다.
최상위 팀 제거
팀의 최상위 지정을 제거하는(또는 그 외에 위원회 자격에 영향을 미치는) 결정은 제거 대상이 되는 최상위 팀의 대표자를 제외한 모든 위원회 대표자의 동의를 필요로 합니다. 이러한 예외 조항에도 불구하고, 검토 대상 팀의 대표자는 해당 팀의 제거에 관한 위원회 심의에 초대되어야 하며, 위원회는 극단적인 경우에만 해당 팀의 반대를 무릅쓰고 제거해야 합니다.
위원회는 모더레이션 팀을 제거할 수 없습니다. 위원회는 모더레이션 팀의 동의 없이 모더레이션 팀의 관할을 변경할 수 없습니다.
대체 대표 및 대표성 포기
대표는 가용성이나 상황 변화 등의 이유로 필요한 경우 임기를 조기에 종료할 수 있습니다. 이후 해당 최상위 팀은 새로운 대표자 선정을 시작해야 합니다. 대표 역할은 자원봉사직입니다. 누구도 그 역할을 맡을 의무는 없으며, 어떤 팀도 대표직 수행을 팀 구성원으로서의 필수 의무로 만들 수 없습니다. 하지만 대표는 대표직의 의무를 이행하거나, 그 직위에서 사임할 의무가 있습니다.
최상위 팀은 예를 들어 팀이 일시적으로 인력이 부족하고 대표를 맡으려는 사람이 없는 경우와 같이, 일시적으로 대표권을 포기하기로 결정할 수 있습니다. 다만 팀이 위원회 대표자를 지정하지 않으면, 프로젝트 전반의 의사 결정에 능동적으로 참여할 권리를 포기하는 것입니다. 의사 결정을 포함한 모든 위원회 절차는 이러한 누락으로 인해 막혀서는 안 됩니다. 위원회는 여전히 모든 프로젝트 구성원으로부터의 새로운 정보와 이의 제기를 고려할 의무가 있습니다. 다만 위원회는 대표가 없는 팀의 피드백을 특별히 고려하거나 취합하기 위해 결정을 막을 의무는 없습니다.
위원회에 대표자를 보내는 것은 최상위 팀의 의무로 간주되며, 이를 정기적으로 이행하지 못하는 것은 해당 팀이 의무를 다하지 못하고 있음을 의미합니다. 다만 위원회 대표자는 일시적인 질병, 휴가 등으로 인한 단기 부재의 경우에는 그 역할을 포기하지 않습니다.
최상위 팀은 주 대표자가 참석할 수 없는 경우를 대비하여 대리 대표자를 지정할 수 있습니다. 이 대리 대표자는 주 대표자가 복귀할 때까지 위원회 대표자의 역할을 온전히 맡습니다. 대체 대표는 주 대표가 참석할 때는 (참석자 수가 두 배가 되는 것을 피하기 위해) 정기적으로 회의에 참석하지 않습니다.
팀의 대표자와 모든 대리 대표자가 3주 연속으로 위원회 절차에 참여하지 못할 경우, 해당 팀이 참여 가능한 대표자를 제공할 수 있을 때까지 그 팀의 대표자는 위원회의 의사 결정 정족수 요건에 산입되지 않습니다. 위원회는 이것이 발효되기 전에 해당 팀에 이를 통지해야 합니다. 팀이 위원회가 자신들의 의견 없이 또는 자신들을 대신하여 이의를 제기할 수단 없이 결정을 내리지 않도록 하고자 한다면, 이용 가능한 대리 대표자를 반드시 확보해야 합니다.
최상위 팀은 필요한 경우 임기가 끝나기 전에 대표자를 교체할 수 있습니다. 하지만 연속성을 유지하는 데는 부담이 따르므로, 팀은 필요 이상으로 대표를 자주 교체하지 않도록 해야 합니다. 팀은 자신들이 지속적으로 다루고자 하는 팀 고유의 이슈나 입장에 대해 자신의 대표와 대체 대표에게 브리핑할 일차적인 책임이 있습니다. 위원회와 팀은 위원회 내 진행 중인 이슈에 대한 연속성을 유지하는 책임과, 대리 대표자 및 기타 신규 대표자에게 맥락을 제공하는 책임을 공유합니다.
비공개 사안의 경우, 위원회는 불필요하게 비공개 정보가 퍼지는 것을 막기 위해 대리 대표자에게 알리는 것을 신중하게 판단해야 하며, 대리 대표자가 대신 나서야 할 필요가 있을 경우 위원회는 그들에게 브리핑할 수 있습니다.
임기 제한
위원회 대표자의 임기는 1년입니다. 각 대표자는 특정 대표 위임(특정 최상위 팀으로부터의 위임)에 대해 연속 3개 완전 임기라는 소프트 한도를 가집니다. 대표자는 해당 팀으로부터 다른 팀 구성원을 대표자로 내세울 수 없다는 명시적 확인(예: 대체 가능한 후보가 없거나, 팀 구성원들이 다른 후보에 대해 막는 이의를 가지고 있는 경우)을 위원회가 받은 경우에 한해서만 이 소프트 한도를 초과할 수 있습니다.
이 외에는, 대표자가 다른 최상위 팀을 위해 봉사할 수 있는 임기 수나 하나의 최상위 팀에 대해 비연속적으로 봉사할 수 있는 임기 수에는 엄격한 한도가 없습니다. 팀은 경험의 연속성과 여러 사람에게 그러한 경험을 제공하기 위한 대표 순환 사이의 균형을 추구해야 합니다.6
대표자 임명의 절반은 3월 말에, 절반은 9월 말에 이루어집니다. 이는 모든 위원회 대표자가 동시에 교체되는 것을 방지합니다. 초기 위원회의 경우, 그리고 최상위 팀의 구성이 변경될 때마다, 위원회와 최상위 팀들은 임기 종료일이 3월과 9월 사이에 대략 균등하게 나뉘도록 함께 노력해야 합니다. 다만 각 임기는 최소 6개월은 지속되어야 합니다(지나치게 짧은 임기를 피하기 위해 일시적인 불균형은 허용됩니다).
위원회와 최상위 팀들이 적절한 임기 종료일 변경에 합의하지 못하는 경우, 균형을 유지하기 위해 대표자들은 (최소 6개월 이후의) 두 종료일 중 하나에 무작위로 배정됩니다.
단일 회사/단체 출신 대표자 수 제한
위원회 대표는 부적절함이나 부적절해 보이는 것을 피하기 위해 어느 한 회사, 법인, 또는 밀접하게 연관된 법인 집단에서 편중되어 나와서는 안 됩니다. 위원회의 대표가 5명 이하이면 동일한 소속을 가진 대표는 1명을 초과할 수 없으며, 위원회의 대표가 6명 이상이면 동일한 소속을 가진 대표는 2명을 초과할 수 없습니다.
밀접하게 연관된 법인에는 동일 법인의 지사/부문/자회사, 상당한 지분 관계로 연결된 법인 등이 포함됩니다. 위원회는 이례적인 경우에 판단을 내릴 수 있으며, 그러한 결정에서 이해 상충을 피하도록 유의해야 합니다.
위원회 대표는 어떤 회사나 다른 법인으로부터 소득의 상당 부분을 얻는 경우(고용주, 클라이언트, 또는 주요 후원자 등으로부터) 그 법인과 소속 관계에 있는 것입니다. 대표자는 소속 변경 사항을 신속히 공개해야 합니다.
대표가 소속을 변경하거나, 최상위 팀이 새 대표를 임명하거나, 위원회 규모가 변경되는 등의 이유로 이 제약이 성립하지 않게 되면, 다음과 같이 제약을 복원합니다:
- 동일한 소속을 가진 대표자들은 먼저 스스로 이슈를 해결하고자 시도할 수 있으며, 이 경우 한 대표자가 자발적으로 물러나고 해당 팀이 다른 사람을 새로 임명합니다.
- 이는 소속 법인이 아니라 대표자 본인의 결정이어야 하며, 소속 법인이 이 결정에 영향을 미치는 것은 부적절한 것으로 간주됩니다.
- 이러한 논의에서 대표들은 동등한 지위를 가지며, 프로젝트나 위원회 내 연차와 같은 요소를 사람들에게 압력을 가하는 데 사용해서는 안 됩니다.
- 해당 소속을 가진 대표자들이 합의에 이르지 못하면, 그중 한 명이 무작위로 제외됩니다. (이 제약이 여전히 충족되지 않으면, 남은 대표자들이 다시 한 번 스스로 이슈를 해결하고자 시도한 후 이 과정을 반복할 수 있습니다.) 이는 최적이 아닌 결과를 낳을 가능성이 높으므로, 일반적으로 자발적인 해결책이 더 바람직합니다.
- 팀은 즉시 후임자 선정 절차를 시작해야 하지만, 팀의 기존 대표자는 남은 임기 중 최대 3개월까지 계속 재직할 수 있습니다.
- 기존 대표자는 신임 대표자와 전환을 조율해야 하지만, 최대 3개월의 기간 동안 누가 실제 대표자가 될지는 해당 팀의 선택입니다. 최상위 팀에서 나오는 대표는 항상 단 한 명뿐입니다.
후보자 기준
다음은 이상적인 후보자를 결정하기 위한 기준입니다. 이는 효과적인 팀 리드나 공동 리드의 기준과 유사하지만 동일하지는 않습니다. 팀 리드가 위원회 대표로서도 좋은 자질을 가질 수는 있지만, 팀 리드 역할과 위원회 대표 역할은 모두 상당한 시간 투자를 필요로 하므로, 이는 그 역할들을 서로 다른 사람들에게 나누어 맡기는 동기가 될 가능성이 높습니다. 이 기준은 엄격한 요건이 아니라 누가 팀의 대표자로 가장 적합한 위치에 있는지 판단하는 데 사용할 수 있습니다. 요컨대 대표자는 다음을 갖추어야 합니다:
- 위원회의 필요에 할애할 충분한 시간과 에너지.
- 프로젝트 운영 및 프로젝트 거버넌스 주제를 돕는 데 대한 관심.
- 자신의 팀이나 활발히 기여하는 영역을 벗어난 프로젝트의 필요에 대한 폭넓은 인식.
- 자신의 팀이 무엇을 필요로 하는지에 대한 예리한 감각.
- 개인적인 의도보다 타인의 필요를 대변하고 중심에 두는 기질과 능력.
- 자기 팀의 일부 견해나 자신이 동의하는 견해만이 아니라 모든 관점을 대변할 능력과 의지.
일부 팀은 현재 이 기준에 맞는 후보가 풍부하지 않을 수 있지만, 위원회는 더 큰 프로젝트 내에서 이러한 역량을 적극적으로 육성해야 합니다. 이는 위원회 구성원 자격뿐 아니라 프로젝트 전체에 걸쳐 도움이 되기 때문입니다.
자격 증명
위원회는 프로젝트의 관리 자격 증명에 대한 특권적 접근 권한을 갖지 않습니다. 이 접근 권한은 오직 인프라 팀에만 있습니다7. 인프라 팀의 책임에는 우리 인프라의 보안과 유지보수성 사이에서 균형을 유지하면서, 팀들이 효과적으로 작업을 수행하는 데 필요한 도구와 접근 권한을 갖추도록 보장하는 것이 포함됩니다. 위원회는 정책을 통해 어떤 팀이 접근 권한을 가져야 하는지 조율하는 데 도움을 줄 수 있습니다.
Rust 재단과의 관계
위원회는 프로젝트 이사(Project directors)를 선임하는 절차를 수립할 책임이 있습니다. 프로젝트 이사는 Rust 프로젝트의 이해관계가 Rust 재단 이사회에 반영되도록 하는 메커니즘입니다.
위원회는 재단 이사회에서 프로젝트의 이해관계를 대변하고 재단 관련 사안에 관해 특정 결정을 내리는 관할을 프로젝트 이사에게 위임합니다. 그 관할의 정확한 경계는 아직 명시되지 않았습니다.
위원회의 의사결정 절차
위원회는 운영 결정과 정책 결정이라는 두 가지 유형의 결정을 내립니다. 특정 고려사항은 결정의 분류에 따라 해당 결정에 적용될 수 있습니다. 그러나 기본적으로 위원회는 분류와 관계없이 모든 결정에 대해 합의(consent) 의사결정 절차를 사용합니다.
운영 결정 대 정책 결정
운영 결정은 위원회가 목표를 수행하기 위해 일상적으로 내리는 결정으로, (수립된 정책에 근거하여) 회의 밖에서 이루어지는 정기적인 조치도 포함됩니다. 정책 결정은 운영을 틀 짓고, 안내하며, 지원하기 위한 일반적이고 재사용 가능한 패턴이나 프레임워크를 제공합니다. 특히, 정책 결정은 운영 결정이나 운영의 다른 측면에 대한 부분적인 자동화를 제공할 수 있습니다. 위원회는 달리 명시되지 않는 한 모든 결정에 대해 기본적으로 합의 의사결정 절차를 사용합니다.
어떤 결정이 운영에 해당하고 어떤 결정이 정책에 해당하는지는 정확히 정의되어 있지 않으며, 오히려 하나의 연속선상 어딘가에 위치합니다. 이 구분의 목적은 위원회의 의사결정 절차를 지시하거나 제약하는 데 있지 않습니다. 대신, 이 구분은 위원회에 지침을 제공하며, 위원회가 시간이 지남에 따라 자신의 결정을 어떻게 기록·검토·개선하고자 하는지를 명확히 합니다. 운영/정책 분류와 관련된 요건이나 지침의 목적상, 본 정책이나 향후 정책에서 운영 또는 정책 어느 쪽으로도 명시되지 않은 것은 기본적으로 정책으로 간주됩니다.
반복과 예외
정책 결정은 그렇지 않으면 반복적인 운영 결정이 필요했을 사안을 체계적으로 다루는 경우가 많습니다. 위원회는 반복되는 운영 결정이 정책 결정이나 정책 변경의 필요성을 나타내는 시점을 인식하도록 노력해야 합니다. 특히, 위원회는 반복되는 운영 결정이 사실상의 정책으로 굳어지도록 방치하는 것을 피해야 합니다.
기존 정책에 대한 예외는, 해당 정책에서 명시적으로 그러한 예외를 허용하지 않는 한 운영 결정을 통해 만들어질 수 없습니다. 임의적인 예외를 피하는 것은 “일탈의 정상화”를 방지하는 데 도움이 됩니다.
동의 의사결정 절차
합의(consent)란 어떤 대표의 요구사항도(그리고 따라서 그가 대표하는 최상위 팀과 하위 팀의 요구사항도) 무시될 수 없음을 의미합니다. 위원회는 모든 관련 의견을 청취하며, 모든 목소리가 동등하게 반영되어 함께 공평하게 일할 수 있는 좋은 토대를 마련합니다.
위원회는 “동의합니까?“라고 묻는 대신 “반대합니까?“라고 묻는 합의 의사결정 방식을 사용합니다. 이는 제안을 충분히 검토했음에도 이유에 대한 명확한 피드백 없이 승인하지 않기로 결정하는 “숨겨진 거부권(pocket veto)“을 없애줍니다. 우려사항, 피드백, 선호사항 등 상대적으로 덜 중요한 형태의 피드백은 의사결정을 막지 않지만, 초안 작성과 논의의 더 이른 단계에서 반영을 고려해야 합니다. 충족되지 않은 요구사항이나 필요를 나타내는 이의는 결정을 진행하기 위해 반드시 고려되고 해결되어야 합니다.
승인 기준
동의 의사결정 프로세스에는 다음과 같은 승인 기준이 있습니다.
- 위원회가 지정한 소통 공간(회의 또는 특정 채널) 중 한 곳에 제안을 게시합니다.
- 위원회 대표자 중 최소 N-2명(N은 전체 위원회 대표자 수)이 최종 제안을 완전히 검토하고 동의를 표했다는 확인을 받은 경우.
- 어떤 위원회 대표자로부터도 미해결 상태의 명시적 반대가 없는 경우.
- 피드백을 위해 최소 10일을 제공하는 것입니다.
승인 기준은 정족수 메커니즘과 함께 대표자들이 제안을 확인할 충분한 시간을 제공합니다. 두 명의 미승인을 허용하는 것은 이 프로젝트의 자원봉사적 성격을 인정하는 것으로, 동의와 무이의에 필요한 확인의 양과 의사결정 속도의 균형을 맞춘 경험에 기반합니다. 이는 해당 대표자들이 이의를 제기하고자 했다면 그럴 시간이 있었다는 것을 전제로 합니다. (이는 오늘날 RFC 승인에 사용되는 프로세스를 본떠 만든 것입니다.)
의사결정 프로세스는 제안을 제기한 대표자가 자신의 제안을 철회하기로 결정하면 언제든지 종료될 수 있습니다. 다른 대표자는 언제든지 제안을 인수하여 이를 계속 유지할 수 있습니다.
이해상충으로 인해 위원회가 결정에 필요한 N-2 정족수를 충족하지 못하는 경우, 위원회는 이해상충이 기록된 상태로 결정을 진행하는 방법에 관한 “이해상충” 섹션에 문서화된 절차를 따르지 않는 한 해당 결정을 내릴 수 없습니다. 이러한 경우 위원회는 유사한 이해상충이 향후 재발하지 않도록 적절한 절차와 정책을 검토해야 합니다.
의사결정 프로세스의 수정 및 조정
공개 정책 절차를 사용하여 위원회는 결정 유형별로 서로 다른 의사결정 절차를 수립할 수 있습니다.
특정 유형의 결정에 어떤 의사결정 절차를 채택할지 결정할 때, 위원회는 신속한 결정의 필요성과 완전한 합의에 대한 확신의 중요성 사이에서 균형을 맞춥니다. 동의 의사결정 프로세스는 다음과 같은 스펙트럼에 걸쳐 있습니다.
- 만장일치 의사결정(신속한 의사결정을 희생하고 완전한 합의에 대한 확신을 우선시함): 팀 구성원은 반드시 검토하고 제안을 다른 모든 것보다 선호해야 하며, 어떤 팀 구성원이든 저지 이의를 제기할 수 있음
- 동의 의사결정(위원회의 기본값이며, 신속한 결정과 합의에 대한 확신 사이에서 균형을 이룹니다): 팀 구성원들이 검토해야 하며 저지 반대를 제기할 수 있습니다
- 1인 승인 및 무반대(합의에 대한 확신을 희생하여 신속한 의사결정을 우선시합니다): 팀 구성원 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 프로젝트 내에서 상당한 권한을 지니고 있습니다. 이 문서는 이들이 갈등을 해결할 수 있는 여러 도구를 제공합니다. 이 절에서는 이러한 팀들이 서로에게 책임을 물을 수 있는 최후의 수단 메커니즘을 설명합니다. 이 절은 이러한 메커니즘이 결코 필요하지 않기를, 그리고 각 팀이 이 지점에 이르지 않고 갈등을 해결하기 위해 가능한 모든 노력을 기울이기를 바라는 마음으로 작성되었습니다.
위원회가 모더레이션 팀에 체계적인 문제가 있다고 판단하고(컨틴전트 모더레이션 팀의 감사 보고서에 근거한 것이든 그렇지 않든), 위원회와 모더레이션 팀이 이 상황을 어떻게 해결할지에 대해 자발적으로 합의에 이르지 못하는 경우, 최후의 수단으로서 위원회는 (만장일치 결정으로) 자기 자신과 모더레이션 팀을 동시에 해산할 수 있습니다. 그런 다음 최상위 팀들은 위원회에 새로운 대표를 임명해야 하며, 컨틴전트 모더레이션 팀이 새로운 임시 모더레이션 팀이 됩니다.
반대로, 모더레이션 팀이 위원회에 체계적인 문제가 있다고 판단하고, 위원회와 모더레이션 팀이 이 상황을 어떻게 해결할지에 대해 자발적으로 합의에 이르지 못하는 경우, 최후의 수단으로서 모더레이션 팀은 (만장일치 결정으로) 자기 자신과 위원회를 동시에 해산할 수 있습니다. 이 절차는 모더레이션 팀 구성원이 최소 3명 이상인 경우에만 시행될 수 있습니다. 그런 다음 최상위 팀들은 위원회에 새로운 대표를 임명해야 하며, 컨틴전트 모더레이션 팀이 새로운 임시 모더레이션 팀이 됩니다.
모더레이션 팀 대표는 이해 상충을 피하기 위해 위원회와 모더레이션 팀을 해산하는 결정에서 제외되지만, 해당 대표 역시 사임해야 합니다.
해임된 대표와 모더레이터는 최소 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“와 “매크로 2.0” 기능이 있으며, 이들의 목표는 최초 RFC에서 많이 바뀌어서 현재 상태를 파악하기 어려울 수 있습니다.
프로젝트 그룹 생성
프로젝트 그룹은 다음을 갖추어야 합니다.
- 리드(Leads) — 그룹의 리더 역할을 하는 최소 한 명 이상의 인원으로, 일반적으로 초기 헌장 작성, 행정 및 커뮤니케이션 업무 처리, 그리고 이러한 책임을 그룹 내 다른 구성원에게 위임하는 일을 담당합니다.
- 연락 담당자(Liaisons) — 해당 작업을 후원하는 공식 Rust 팀의 구성원으로, 팀과 그룹 사이의 연락 창구 역할을 합니다. 이들은 직접적으로 깊이 관여하지 않을 수도 있지만, 주기적으로 상황을 확인하고 팀과의 회의에서 작업을 대표할 수 있어야 합니다. 또한 이 작업이 워킹 그룹 자체를 넘어 팀 내에서 진행 중인 다른 작업과 교차할 수 있는 시점을 주시해야 합니다.
- 리에종은 리드 중 한 명일 수도 있지만, 반드시 그럴 필요는 없습니다.
- 구성원(Members) — 프로젝트 그룹에 정기적으로 참여하거나 기여하는 개인들입니다.
- 그룹의 구성원 자격 요건은 셰퍼드가 결정하며 헌장에 명시되어야 합니다.
- 초기 구성원은 해당 분야에서 이미 정기적으로 생산적으로 참여해 온 사람들을 대표하려고 노력해야 합니다.
- 다만 프로젝트 그룹이 반드시 많은 구성원을 가질 필요는 없으며, 일부 프로젝트 그룹은 리드와 연락 담당자를 포함해 단 한두 명의 구성원만 있을 수도 있습니다.
- 그룹의 범위와 취지를 정의하는 헌장.
rust-lang조직 아래에 호스팅되는 GitHub 저장소로, 헌장과 커뮤니티 구성원이 그룹을 모니터링하거나 참여할 수 있는 방법에 대한 안내를 포함합니다.- 공식 rust-lang.org 웹사이트에서의 대표성.
- *“공식적인 의사결정 권한”*은 없습니다: 즉
rust-lang/rfcs에서 RFC를 승인할 수 없다는 의미입니다.- 그룹은 물론 RFC를 작성하고 Rust 팀 및 커뮤니티에 우려 사항과 원하는 변경 사항을 알리는 것이 권장됩니다.
- Rust가 공식적으로 관리하는 토론 플랫폼 내의 전용 공간.
- 이 글을 작성하는 시점 기준으로는 Zulip입니다.
- 소통을 원활히 하기 위해 그룹은 이상적으로는 상위 팀과 같은 플랫폼을 사용해야 하지만, 팀이 그룹의 다른 플랫폼 시도에 동의하는 경우도 있을 수 있습니다.
헌장 작성하기
프로젝트 그룹은 해당 상위 팀의 승인을 받으므로, 구체적인 요건을 정하는 것은 각 팀에 달려 있습니다. 다만 저자는 그룹이 다음 질문들에 답하는 헌장을 작성해 보기를 권장합니다.
- 여러분의 그룹이 조직에 가져다줄 가치는 무엇이라고 생각하십니까?
- Rust 조직으로부터 필요로 하는 지원과, 별도로 원하는 지원은 무엇입니까?
- 왜 이것이 커뮤니티 활동이 아니라 프로젝트 그룹이어야 합니까?
- 여러분의 그룹의 목표는 무엇입니까?
- 단기적으로도, 관련이 있다면 더 긴 기간에 걸쳐서도 마찬가지입니다.
- 여러분의 그룹이 명시적으로 목표로 삼지 않는 것은 무엇입니까?
- 팀과의 관계가 어떻게 되기를 기대하십니까?
- 여러분의 작업을 그룹 외부 사람들에게 어떻게 접근 가능하게 만들 계획입니까?
- 초기 셰퍼드/리더는 누구입니까? (2~3명이 바람직하지만 필수는 아닙니다.)
- 여러분의 그룹은 장기적으로 운영됩니까, 아니면 일시적입니까?
- 일시적이라면 얼마나 오래 운영될 것으로 예상하십니까?
- 여러분의 그룹의 장기적인 비전은 무엇입니까?
- 해당되는 경우, 어떤 다른 그룹이나 팀과 긴밀히 접촉할 것으로 예상하십니까?
- 여러분의 그룹이 어디에서 도움이 필요할 것으로 보십니까?
초기 설정
그룹이 승인되면, 초기 구성원 명단을 담은 풀 리퀘스트를 rust-lang/team에 제출해야 합니다. 그룹을 생성하는 방법에 대해서는 team의 문서를 참조하십시오.
이후 프로젝트 그룹은 project group template을 사용하여 rust-lang 조직 아래에 저장소를 생성하고, 필요한 변경 및 개인화 작업을 수행하는 것이 권장됩니다.
평가
상위 팀은 정기 트리아지의 일환으로 프로젝트 그룹과의 확인 절차를 추가해야 합니다. 프로젝트 그룹은 또한 진행 상황 업데이트와 회의록을 “Inside Rust” 블로그에 블로그 게시물로 올리는 것이 권장됩니다.
아카이빙
어느 시점에서는 그룹의 작업이 종료됩니다. 작업이 완료되었거나, 구성원들이 작업을 끝낼 수 없거나, 그룹이 프로젝트를 더 이상 진행할 가치가 없다고 느끼기 때문입니다. 저자는 이 과정을 “아카이빙“이라고 부르고 있습니다.
공지
아카이빙을 고려하는 그룹은 먼저 자신들이 시작한 크레이트, 저장소, 또는 프로젝트에 어떤 일이 일어나야 하는지를 파악해야 합니다. 일반적으로 이러한 프로젝트는 다른 그룹이나 개인에게 이관되어야 하며, 프로젝트를 유지 관리할 적절한 후보가 없다면 아카이빙되어야 합니다.
이 문제가 해결되고 나면 그룹은 프로젝트의 이관 및/또는 아카이빙과 관련된 세부 사항과 함께 아카이빙 공지를 작성해야 합니다.
회고
이 RFC는 조직 내 현재의 몇 가지 조직적 문제를 해결하고자 시도하지만, 저자는 이것이 그러한 문제들에 대한 만병통치약이 되리라고도, 우리가 앞으로 새로운 문제를 겪지 않으리라고도 믿지 않습니다. 그 일환으로, 이 RFC는 상당한 시간이 지났거나 그룹이 프로젝트를 마쳤을 때 그룹과 함께 회고를 진행하는 것을 도입합니다.
여기에는 그룹 구성원들 간의, 그리고 이상적으로는 상위 팀과 거버넌스 워킹 그룹 간의 논의가 포함될 것입니다. 회고는 Inside Rust 블로그에 공개 게시물을 산출해야 하지만, 구성원이 비공개로 유지하고자 하는 피드백은 생략됩니다.
블로그 게시물은 RFC나 프로젝트 같은 그룹의 산출물뿐만 아니라, 그룹이 무엇이 잘 되었다고 생각했는지, 그리고 중요하게는 무엇이 잘 되지 않았는지를 다루려고 노력해야 합니다. 이는 우리가 이 초기 RFC를 반복적으로 개선하고, 그 과정에서 발생하는 이슈를 찾아 해결하는 데 도움이 되어야 합니다.
회고와 아카이빙 공지는 모두 하나의 게시물로 작성될 수 있으며, 아마도 그렇게 하는 것이 바람직할 것입니다. 다만 시기적절한 회고를 진행하는 것이 불가능한 경우도 있을 것이며, 그런 경우에는 더 짧은 별도의 공지 게시물이 적절합니다.
프로젝트 그룹의 생명 주기
이는 프로젝트 그룹의 전체 과정에 대한 높은 수준의 개요입니다.
Figure 1. Project Group Lifecycle
단계
-
탐색 기간.
- 문제 영역에 대한 초기 논의.
- 팀은 초기 논의를 살펴보거나 응답할 의무가 없습니다. 물론 관심 있는 구성원은 자유롭게 참여할 수 있습니다.
- 프로젝트에 대한 짧은 동기를 작성하십시오.
- 연락 담당자 역할을 맡을 의향이 있는 사람을 관련 팀에서 찾으십시오.
- 연락 담당자를 찾는 방식은 팀마다 다르므로, 프로젝트 그룹을 제안하는 방법에 대해서는 해당 팀의 문서를 참고해야 합니다.
- 연락 담당자 역할을 맡을 의향이 있는 사람을 항상 찾을 수 있는 것은 아닙니다. 각 팀이 얼마나 많은 새로운 활동을 감당할 여력이 있는지는 각 팀이 결정할 사안이며, 때로는 그 여력이 전혀 없을 수도 있습니다.
- 문제 영역에 대한 초기 논의.
-
그룹을 만들기 위해 팀의 합의를 얻습니다.
- 연락 담당자와 셰퍼드를 명시하십시오. (프로젝트 그룹 생성 참고)
- 간단한 동기와 가능한 해결책에 대한 메모를 작성하십시오.
- 합의에 도달하는 방식은 팀마다 다르며, 어떤 팀은 RFC를 요구하는 반면 다른 팀은 회의에서 결정할 수도 있습니다.
-
그룹을 위한 인프라를 만듭니다.
- 초안 RFC 등 작업과 논의를 진행하기 위한
rust-lang산하의 GitHub 저장소. - 소통을 위한 Zulip 스트림입니다.
rust-lang/team의 프로젝트 그룹이며, 권한 처리를 위해 GitHub상의 팀이기도 합니다.
- 초안 RFC 등 작업과 논의를 진행하기 위한
-
Inside Rust 블로그에 그룹 생성을 알리는 글을 작성하십시오. 다음 정보를 반드시 포함해야 합니다. 다음 정보를 반드시 포함하십시오.
- 소개
- 헌장(링크로 연결하거나 본문에 포함) [헌장 작성하기 참고]
- 그룹의 GitHub 저장소 링크
- 그룹이 참여자에게 열려 있다면, 어떻게 기여할 수 있는지에 대한 정보를 제공하십시오.
- 정기 회의도 진행할 계획이라면, 그룹이 언제 모일 예정인지와 회의 캘린더 이벤트 링크를 함께 포함하십시오.
-
그룹은 자신의 헌장에 명시된 목표를 향해 나아갑니다.
-
활발한 작업이 중단되면 그룹은 “보관(archived)“됩니다.
- 보관(archival)은 필요한 경우 프로젝트 그룹의 셰퍼드, 연락 담당자, 또는 상위 팀의 리드가 시작할 수 있습니다.
- 보관이 반드시 영구적인 상태를 의미하지는 않으며, 이는 단지 그룹의 현재 상태를 반영할 뿐입니다.
- 마찬가지로 그룹의 보관이 해당 영역의 작업이 모두 소진되었음을 의미하지는 않습니다
- 보관하는 이유(완전한 목록은 아님):
- 그룹 내 아무도 더 이상 시간을 낼 수 없거나 더 우선순위가 높은 일이 생겼습니다.
- 해결할 수 없는 차단 이슈가 있습니다.
- 가까운 미래에 이 영역에서 추가로 할 작업이 보이지 않습니다.
- 작업이 만족스러운 수준까지 완료되었습니다.
- 그룹이 결국 그 아이디어가 그다지 좋지 않았다고 판단했습니다.
-
그룹의 보관을 알리는 블로그 게시물을 작성하십시오.
- 이 게시물의 범위는 그룹의 범위에 따라 다르지만, 이상적으로는 다음 사항 중 일부를 포함해야 합니다.
- 그룹이 만들어낸 결정, RFC, 그 밖의 산출물에 대한 개요.
- 진행 과정에 대한 소감, (경우에 따라) 그것이 어떻게 작동했는지 혹은 작동하지 않았는지, 겪은 어려움, 그리고 개선하고 싶었던 점.
- 이 게시물의 범위는 그룹의 범위에 따라 다르지만, 이상적으로는 다음 사항 중 일부를 포함해야 합니다.
-
인프라를 보관합니다.
- GitHub 저장소를 읽기 전용으로 보관합니다.
- 어떤 플랫폼에서든 채팅 채널을 보관하십시오.
프로젝트 목표
소개
프로젝트 목표는 프로젝트 내부 또는 외부의 사람들이 중간 규모에서 큰 규모의 변경을 제안하는 것을 자원과 도움을 제공하여 돕기 위해 우리가 운영하는 프로그램입니다.
목표를 세우는 것은 자금을 찾는 데 도움이 될 수 있습니다: 프로젝트는 자금을 찾고 있는 목표들을 나열하며, 자금 지원 팀과 RCN 또한 잠재적인 자금 지원 파트너가 관심 있는 목표를 찾아볼 수 있는 곳입니다.
이 페이지는 간단한 개요만 제공하며, 전체 문서는 이 링크에서 확인할 수 있습니다.
구조와 조정
프로젝트 목표 제안은 GitHub에서 이슈로 추적됩니다.
자금 지원
프로젝트 목표 제안자는 자금 지원을 신청할 수 있습니다. 자세한 내용은 documentation을 참고하십시오.
자금 지원을 받는 방법에 대한 자세한 내용은 Zulip에서 자금 지원 팀에 연락하십시오.
정책
이 장들은 Rust 프로젝트와 그 구성원들을 다루는 정책을 담고 있습니다.
Rust 크레이트 소유권 정책
소개
이 문서는 Rust 프로젝트가 게시하는 크레이트에 대한 정책을 다룹니다. 이는 RFC 3119를 통해 처음 채택되었습니다.
분류
Rust 프로젝트가 게시하는 Rust 크레이트는 다음 범주 중 하나에 속합니다:
- 의도적 산출물(Intentional artifacts): 이는 어떤 팀(보통 라이브러리 팀)이 의도적으로 릴리스하며, 적극적으로 관리되고, 외부 사용자가 사용하도록 의도되었으며, 의도적으로 공식적인 느낌을 갖는 크레이트입니다. 예시: libc
- 내부 사용: rustc, crates.io, docs.rs 등과 같은 어떤 “내부 클라이언트“가 사용하는 크레이트입니다. 이들의 주된 목적은 외부 사용자가 사용하는 것이 아니지만, 이를 유지 관리하는 팀(일반적으로 해당 내부 클라이언트의 팀)은 크레이트가 더 널리 채택되기를 바랄 수도 있습니다. 이들과 “의도적 산출물” 사이의 경계는 모호할 수 있으며, 궁극적으로는 해당 팀의 목표에 달려 있습니다. 예시: conduit, measureme. 전이 의존성(transitive dependency)으로 나타나도록 의도되었는지 여부에 따라 두 개의 하위 분류가 있습니다.
- 전이적으로 의도됨(Transitively intentional): 이는 의도적 산출물 라이브러리의 의존성으로, 직접 사용되도록 의도되지 않았더라도 사용자의 의존성 트리에 나타나게 됩니다. Rust 프로젝트는 여전히 이러한 크레이트가 마치 “의도적 산출물“인 것처럼 보안 이슈를 처리해야 합니다.
- 전이적으로 의도되지 않음(Not transitively intentional): 이는 배포되는 바이너리, CI 도구, 표준 라이브러리의 의존성이거나, 그 외에 사용자의 의존성 트리에 나타날 것으로 예상되지 않는 것들입니다. Rust 프로젝트는 이러한 크레이트의 보안 이슈를 내부적으로 처리해야 할 수 있지만, 이러한 크레이트의 보안 이슈를 반드시 더 넓은 대중에게 알려야 하는 것은 아닙니다. 이러한 크레이트 중 하나에서 발생한 보안 이슈가 게시된 바이너리(또는 crates.io 등)에 영향을 미치는 경우, 이는 여전히 해당 바이너리 또는 웹사이트의 버그로 처리해야 합니다.
- 실험(Experiment): 이는 어떤 팀이 API 설계(또는 그 밖의 것)를 더 잘 파악하기 위해 사용자들이 사용해 보도록 의도한 실험이었으며, 장기적인 메인테이너십 유지에 대한 약속은 없었습니다. 예시: failure
- 더 이상 사용되지 않음(Deprecated): 이는 과거에는 “의도적 산출물”(또는 실험/내부 사용)이었지만 더는 그렇지 않습니다. 예시: rustc-serialize
- 플레이스홀더: 기능하는 크레이트가 아니며, 공식 도구의 이름 등을 유지하기 위해 사용됩니다. 예시: rustup
- 이관됨: Rust 프로젝트가 소유했을 수 있고 여전히 외부 사용자가 사용하도록 의도되어 있지만, 더 이상 공식적으로 취급되지 않습니다. 이러한 경우 해당 크레이트는 더 이상 Rust 프로젝트가 소유/관리하지 않습니다. 예시: rand
정책
조직 내 모든 크레이트는 crates.io에서 최소 하나의 팀이 소유해야 합니다. 팀들은 이를 위해 rust-lang/foo 팀을 사용해야 합니다. 이관되지 않은 크레이트는 개인 계정을 소유자로 둘 수 없습니다. 크레이트에 팀 소속이 아닌 추가 소유자가 필요한 경우, 해당 팀은 프로젝트 그룹을 만들어야 합니다. 이는 팀(또는 프로젝트 그룹) 소속이 아닌 사용자가 저장소에 대한 메인테이너 접근 권한을 갖는 것을 금지하지는 않으며, 단지 이들이 게시하는 것만 금지한다는 점에 유의하십시오.
현재로서는 크레이트가 팀 하나에 의해서만 소유되는 것은 불가능합니다. 이러한 경우에는 rust-lang-owner 계정(또는 인프라 팀이 결정할 유사한 계정)을 임시방편으로 사용할 수 있습니다. 각 크레이트에 대한 책임 소재를 명확히 하기 위해, 이 계정은 가능한 한 단계적으로 폐지하도록 노력해야 합니다. 자동으로 게시되는 크레이트의 경우, GitHub Actions에서 신뢰된 게시(trusted publishing)를 사용하십시오.
조직 내의 각 크레이트, 그리고 향후 조직에 추가될 모든 크레이트는 위의 분류에 따라 어느 범주에 속하는지 결정해야 합니다. 크레이트를 등록할 때 어떤 범주에 속해야 할지 확신이 서지 않거나 아직 결정을 내리고 싶지 않다면, “실험적(Experimental)“을 선택하십시오.
게시되는 각 크레이트는 README를 포함해야 합니다. 최소한 이 README는 주 소유 팀을 언급해야 합니다. 크레이트는 해당 범주에 따라 README와 문서 루트에 다음 정보도 포함해야 합니다.
의도적 산출물(Intentional artifact)
“의도적 산출물” 크레이트는 자신의 약속(commitment)을 자유롭게 선택할 수 있지만, 그것이 무엇인지 메시지에서 명확히 밝혀야 합니다. 팀에 헌장이 있게 되면, 해당 크레이트 역시 헌장에서 의도적 산출물로 언급되어야 합니다. 의도적 산출물을 폐기(deprecate)하는 것은 가볍게 여겨서는 안 되며 RFC가 필요합니다.
이러한 메시지의 예시는 다음과 같은 텍스트일 수 있습니다.
이 크레이트는 더 넓은 생태계에서 사용하기 위해 Rust [team] 팀이 유지 관리합니다. 이 크레이트는 1.0 이후 버전이며 API에 대해 semver 호환성을 따릅니다.
이러한 크레이트의 보안 이슈는 보안 대응 워킹 그룹(Security Response WG)이 적절한 비중과 신중한 메시지로 처리해야 하며, 프로젝트의 보안 정책에 따라 보고되어야 합니다.
내부 사용(Internal use)
“내부 사용” 크레이트는 readme/문서 상단 근처에 다음 텍스트를 포함해야 합니다.
이 크레이트는 [team]이 유지 관리하며, 주로 [rust project(s)]가 사용하기 위한 것이고 외부 사용을 의도하지 않습니다(전이적 의존성으로서의 사용은 예외). 이 크레이트는 사전 경고 없이 API에 중대한 변경을 가하거나 폐기될 수 있습니다.
“전이적 의존성으로서인 경우를 제외하면“이라는 텍스트는 해당 크레이트가 의도적 산출물 라이브러리의 의존성(“전이적으로 의도적”)인 경우 포함되어야 합니다.
전이적으로 의도적인 라이브러리의 보안 이슈는 의도적 산출물인 것처럼 처리되어야 합니다.
실험(Experiment)
“실험” 크레이트는 자신이 실험임을 언급해야 합니다. 실험 크레이트는 범위가 한정된 방식으로 사용되도록 의도될 수 있으므로, 사용을 의도한다면 무엇을 보장하는지 명확히 밝혀야 합니다.
이러한 메시지의 예시는 다음과 같은 텍스트일 수 있습니다.
이 크레이트는 [thingy]에 관한 실험의 일환으로 [team]이 관리합니다. 저희는 사람들이 자신의 프로젝트에서 이 크레이트를 사용해 보고 [method]를 통해 피드백을 제공하기를 권장하지만, 장기적인 유지 관리는 보장하지 않습니다.
또는, 전혀 사용될 의도가 없는 실험의 경우:
이 크레이트는 [team]이 관리하며 내부 실험입니다. 저희는 안정성이나 장기적인 유지 관리를 보장하지 않으므로, 사용에 따른 위험은 스스로 감수하십시오.
이상적으로는, 피드백 목적으로 게시되는 실험적 크레이트는 실험의 목적, 대략적인 기간, 진행 절차를 정리한 문서를 링크로 제공해야 합니다.
폐기됨
“폐기됨” 크레이트는 readme/문서 상단 근처에 다음 텍스트를 포함해야 합니다:
이 크레이트는 폐기되었으며 사용을 의도하지 않습니다.
자리표시자
“자리표시자” 크레이트는 게시된 readme/문서에 다음 텍스트를 포함해야 합니다:
이 크레이트는 [tool]의 크레이트 이름을 예약하기 위해 존재하는, 기능적으로 비어 있는 크레이트입니다. 사용해서는 안 됩니다.
일반적으로 크레이트 이름을 얀크를 통해 예약하는 것보다 빈 자리표시자 크레이트를 게시하는 편이 더 낫습니다. 그래야 사람들이 해당 크레이트를 사용할 수 없는 이유를 이해하는 데 도움이 되는 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은 사고를 대체하는 것이 아니며, 프로젝트에 대한 우리의 공유된 사회적·기술적 이해를 잃을 위험이 있는 방식으로 사용하는 것을 허용하지 않으며, 강력한 커뮤니티를 만들고자 하는 우리의 목표를 해치는 방식으로도 허용하지 않습니다.
이 정책의 많은 조항이 강제할 수 없다는 것을 알고 있습니다. 저희의 목표는 모든 위반을 적발하는 것이 아닙니다 (아래 탐정 역할은 여러분의 일이 아닙니다 참조). 대신 저희의 목표는 그럴듯한 부인 가능성을 제거하는 것입니다: 정책을 따르는 것과 의도적으로 위반하는 것 사이에서 선택을 강제하는 것입니다. 동기에 대한 더 많은 배경은 자금 세탁과 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에 풀 리퀘스트로 존재해야 하지만 리뷰 대상은 아닌, 명백히 실험적인 코드 변경을 만드는 데 LLM을 사용하는 경우입니다.- “명백히 실험적인“이라 함은
S-experimental레이블,[PERF]제목,r? ghost댓글과 같은 표식을 포함합니다. - 실험적 PR이 LLM 사용 사실을 공개할 것을 강력히 권장하지만, 의무는 아닙니다. 이는 다른 사람들이 LLM이 생성한 것임을 모른 채 초안 작업을 이어받는 상황을 방지하기 위한 것입니다.
- ℹ️ PR이 더 이상 명백히 실험적인 것으로 표시되지 않는 경우, 그 시점부터는 공개가 필요합니다.
- “명백히 실험적인“이라 함은
❌ 금지
다음 사항들은 금지됩니다.
- 개인 사용자 계정에서 게시된, 원래 LLM이 작성한 댓글.
- ℹ️ 이는 이슈 본문 및 PR 설명에도 적용됩니다.
- ℹ️ 이는 LLM이 원래 작성하거나 대본을 작성한 음성/영상 콘텐츠에도 적용됩니다.
- ℹ️ LLM 콘텐츠가 명확히 인용되고 표시된 경우에는 적용되지 않으며, 이러한 경우에는 게시할 수 있습니다. 다만, 댓글의 내용은 LLM 콘텐츠 없이도 그 자체로 성립해야 하며, 이는 여러분 자신의 말을 대체할 수 없습니다.
- ℹ️ 아래 ⚠️의 “기계 번역” 항목도 참고하십시오.
- ℹ️ 아래 부록의 “범위” 항목도 참고하십시오.
- LLM이 원래 작성한 문서입니다.
- ℹ️ 이는 문서 주석, 안전성 주석, 또는 여러 단락으로 이루어진 비문서 주석 등 사소하지 않은 소스 주석을 포함합니다.
- ℹ️ 이는 컴파일러 진단 메시지를 포함합니다. LLM은 진단 메시지를 둘러싼 로직을 지원하는 데에는 조건부로 허용됩니다(아래 “실험: LLM이 생성한 코드 변경” 참고). 하지만 메시지 자체를 작성하는 데에는 사용될 수 없습니다.
- ℹ️ 이는 “사소한” 변경(아래 ⚠️ 참고)을 포함하지 않습니다.
- LLM이 실행해야 하도록 작성된 정책 또는 프로세스입니다.
- 예를 들어,
AGENTS.md로 테스트가 어디에 있는지 알리기만 해서는 안 됩니다. 문서는 우선적으로 사람을 위해 작성되어야 하며, LLM 문서는 이를 요약할 수 있을 뿐 새로운 세부 사항을 추가해서는 안 됩니다.
- 예를 들어,
- LLM 리뷰를 변경 사항을 병합하거나 거부하기에 충분한 조건으로 취급하는 것입니다. LLM 리뷰는 활성화되어 있더라도 반드시 참고용이어야 합니다. 팀은 리뷰 없이 코드를 병합할 수 있다는 정책을 가질 수도 있고, 최소 한 명의 사람이 코드를 리뷰해야 한다는 정책을 가질 수도 있지만, LLM 리뷰가 사람의 리뷰를 대체한다는 정책을 가질 수는 없습니다.
- ℹ️ 아래 ⚠️의 “리뷰 봇” 항목을 참고하십시오.
- ℹ️ LLM 리뷰는 자체 검토를 대체하지 않습니다. 작성자는 게시 전과 각 변경 이후에 자신의 코드를 스스로 검토해야 합니다.
⚠️ 유의사항을 전제로 허용
이러한 사용은 아래 규칙에 따라 사안별로 허용됩니다. 신규 기여자라면 리뷰어와 아직 신뢰를 쌓지 못했으므로 기존 기여자보다 더 면밀히 검토받게 될 것으로 예상해야 합니다.
“⚠️ 조건부로 허용됨“에 해당하는 모든 사용은 반드시 LLM이 사용되었음을 밝혀야 합니다.
- 원문 메시지를 게시하지 않고 모국어에서 기계 번역(예: 구글 번역)을 사용하는 것은 허용되지만 권장되지 않습니다. 이렇게 하면 원래는 없었던 새로운 오해가 생길 수 있으며, 해당 언어를 구사하는 사람이 더 나은 번역을 제공할 기회를 막게 됩니다.
- ℹ️ 원문 메시지와 번역본을 모두 게시하는 것은 언제나 괜찮지만, 그럼에도 기계 번역이 사용되었음을 공개해야 합니다.
- “사소한” 코드 또는 문장 변경입니다.
- ℹ️ 다른 방식으로 작성할 방법이 없거나, 다른 방식들이 거의 동일한 경우 변경 사항은 사소한 것으로 간주됩니다. 예를 들어, 다음은 모두 사소한 변경입니다.
- 오타 수정
- 마크다운 링크
- 단어를 동의어로 바꾸기
- 트레이트 구현에 대한 타입 시그니처
- ℹ️ 사소한 변경만으로 구성된 PR에는 주의를 기울이십시오. 컴파일러 팀의 오타 수정 정책도 참고하십시오.
- 💡 추가 제안 사항은 dev-guide를 참고하십시오.
- 이 정책에 영감을 준 개념에 대한 더 자세한 배경은 독창성 임계값과, API 선언을 복사하는 것이 공정 이용이라는 구글 대 오라클 판결을 참고하십시오.
- ℹ️ 다른 방식으로 작성할 방법이 없거나, 다른 방식들이 거의 동일한 경우 변경 사항은 사소한 것으로 간주됩니다. 예를 들어, 다음은 모두 사소한 변경입니다.
- 직접 버그를 검증하는 한, LLM을 사용해 버그를 발견하는 것. 퍼저에 대한 저희 가이드라인을 참고하십시오.
- ℹ️ 여기에는 병합되지 않은 코드에서 결함을 발견하기 위해 LLM을 사용하는 리뷰어도 포함됩니다.
- ℹ️ 위 ❌ 항목의 “개인 사용자 계정에서 게시된 댓글 […]“도 참고하십시오.
- PR에 대한 “리뷰 봇“으로 LLM을 사용하는 것.
- ℹ️ 메인테이너의 승인 없이 게시하는 리뷰 봇은 차단됩니다. (12번 내용에 통합됨)
- ℹ️ 리뷰 봇은 LLM임을 명확히 표시하는 별도의 GitHub 계정을 반드시 가져야 합니다. 봇의 분석에 대한 본인의 개인적인 해석과 함께 명확히 인용된 경우가 아니라면, 개인 계정으로 LLM 리뷰를 그대로 게시(또는 도구가 게시하도록 허용)해서는 안 됩니다.
- ℹ️ 리뷰 봇 계정은 표준 GitHub 사용자 차단 메커니즘을 통해 개별 사용자가 차단할 수 있어야 합니다. (일부 GitHub “앱” 계정은 사용자처럼 보이는 댓글을 게시하지만 차단할 수 없다는 점에 유의하십시오.)
- ℹ️ LLM 댓글은 차단용으로 사용되어서는 안 되며, 리뷰어는 어떤 댓글을 해결해야 하는지 명시해야 합니다.
- 다시 말해, 리뷰어는 PR을 차단하기 전에 LLM 댓글을 명시적으로 지지해야 합니다. 리뷰어는 LLM 댓글에 대한 본인의 분석에 책임을 지며, 이를 CI 실패로 취급할 수 없습니다.
- ℹ️ 이는 리뷰를 위해 LLM을 개인적으로 사용하는 경우에는 적용되지 않습니다. 위의 ✅를 참고하십시오.
- 💡 추가 제안 사항은 dev-guide를 참고하십시오.
실험: 검토를 위해 만들어진 LLM 생성 코드 변경
저희는 향후 정책에 참고하기 위해 LLM으로 실험할 여지를 남겨두고 있습니다. (23번 내용에 통합됨)
규칙
원래 LLM이 만든 것으로, 사전 조율되고 중요하지 않으며 품질이 높고 충분히 테스트되고 충분히 검토된 코드 변경은 공개를 전제로 허용됩니다.
- “사전 조율됨“이란 리뷰어가 LLM으로 작성된 풀 리퀘스트를 리뷰할 의향이 있음을 미리 전달했다는 것을 의미합니다.
- ℹ️ 신규 기여자는 먼저 리뷰어와 이야기하지 않고서는 LLM을 사용하여 풀 리퀘스트를 만들 수 없습니다. 이는 해당 풀 리퀘스트에 배정될 리뷰어와 동일한 리뷰어여야 합니다.
- 물론 저자는 리뷰어를 찾기 전에 변경 작업을 시작하는 것이 허용됩니다. 그러나 풀 리퀘스트를 열기 전에 리뷰어를 찾아야 합니다. 리뷰어를 찾기 전에
r? @ghost로 LLM이 작성한 풀 리퀘스트를 여는 것은 허용되지만 권장되지는 않습니다. 대신 포크에 푸시한 뒤 예비 리뷰어를 위해 Zulip에 링크를 게시할 것을 제안합니다. 이러한 PR도 여전히 공개가 필요합니다.
- “비핵심적“이란 해당 풀 리퀘스트가 건전성 회귀를 일으킬 가능성이 극히 낮다는 것을 의미합니다.
- ℹ️ 예시:
tidy,x setup,linkchecker와 같은 내부 도구에 대한 변경은 대체로 괜찮습니다.- 트레이트 시스템, MIR 빌딩, 쿼리 시스템처럼 건전성에 강한 영향을 미치는 변경은 아마도 허용되지 않을 것입니다.
- ℹ️ 예시:
- “고품질“은 다른 코드 변경과 최소한 동일한 기준을 적용받는다는 것을 의미합니다. 저자와 리뷰어뿐 아니라 모두가 코드를 읽습니다. 우리는 코드베이스의 품질을 저하시키는 “바이브 코딩된” 풀 리퀘스트에는 관심이 없습니다.
- “충분히 테스트됨“이란 여러분이나 리뷰어가 생각할 수 있는 모든 엣지 케이스를 다루었다는 것을 의미합니다.
- ℹ️ LLM이 작성한 PR은 사람이 작성한 PR보다 더 높은 기준을 적용받습니다. LLM은 테스트 작성을 더 쉽게 만들기 때문입니다.
- ℹ️ 코드의 특정 부분에 기존 테스트 스위트가 없다면, 새 테스트 스위트를 작성하거나 PR을 닫아야 합니다. “테스트 작성이 어려워 보인다“는 이유로 예외는 인정되지 않습니다.
- “충분히 리뷰됨“이란 저자와 리뷰어 모두가 코드를 완전히 이해하기로 약속했다는 것을 의미합니다.
예외적으로 rust-lang 조직의 구성원은 “중요하지 않음” 조항에 구속되지 않습니다. 저자와 리뷰어가 스스로 판단을 내릴 것으로 신뢰하기 때문입니다. 그러나 이 조항을 활용하는 것은 강력히 권장하지 않습니다. LLM은 그럴듯해 보이는 코드를 생성하는 데 매우 뛰어나며, 건전성은 테스트하기 어렵습니다.
예외적으로 이 정책이 시행되기 전에 작성된 PR은 “중요하지 않음” 조항에서 면제됩니다.
절차
LLM으로 작성된 풀 리퀘스트에는 새로운 llm-assisted 레이블을 붙여야 합니다. 이러한 모든 풀 리퀘스트는 rust-lang 조직의 모든 구성원이 접근할 수 있는 새로운 (비공개) Zulip 채널에 게시됩니다. 이 채널의 목표는 LLM이 작성한 PR에 대한 추가적인 관문 역할을 하는 것이 아닙니다. 대신 이 실험이 효과가 있는지에 대한 정보를 수집하는 것이 목표입니다: 사람들이 LLM으로 흥미롭고 유용한 일을 하고 있습니까? 그들이 배우고 있습니까? 그들이 반복적으로 기여하고 있습니까?
새 채널이 비공개이기 때문에, 주제에 부합한다고 인정되는 기준이 평소보다 높게 적용됩니다. 예를 들어 다음은 주제에 부합합니다:
- PR이 실험적 예외 기준을 충족하는지 여부
- PR이 정책을 전반적으로 준수하는지 여부
그리고 다음은 주제와 무관합니다:
- 기술 및 설계 논의. 이는 풀 리퀘스트에 직접 또는 공개 Zulip 채널에 게시되어야 합니다.
- 노력, 커뮤니케이션 스타일, 의도에 관한 논의
- LLM 정책에 관한 일반적인 논의
서킷 브레이커
LLM이 코드베이스를 “압도“하거나 사실상 필수 요건이 되는 위험을 피하기 위해, 병합될 수 있는 LLM 생성 PR의 수에 제한을 둡니다. 6주 기간 동안 병합된 PR 중 절반 이상이 LLM 생성 PR인 경우, 50% 미만으로 돌아갈 때까지 새로운 LLM 생성 PR의 병합을 허용하지 않으며, 최소 냉각 기간은 10일입니다. 이 기간은 기존 릴리스 주기에 맞춰 선택되었으며, 냉각 기간은 허용과 금지 사이를 오락가락하는 것을 방지하기 위한 것으로, FCP 프로세스에 맞춰 기간이 선택되었습니다.
냉각 기간은 다음과 같은 논의를 장려하기 위한 것입니다:
- 실험은 어떻게 진행되고 있습니까?
- 우리는 AI를 지속 가능한 방식으로 채택하고 있습니까? LLM을 사용하지 않기로 선택한 기여자들을 포함하고 있습니까?
- 우리 정책에 변경하고 싶은 사항이 있습니까?
일관되지 않은 시행과 그로 인한 반감을 피하기 위해, 이 서킷 브레이커는 자동화할 것을 강력히 권장합니다.
부록
범위
이 정책은 rust-lang/rust에만 적용되며, 이를 승인한 팀들 — 컴파일러, 라이브러리, 타입, rustdoc, bootstrap 및 그 하위 팀들 — 에만 적용됩니다. 다음은 범위에 포함되지 않으며 자체 정책을 자유롭게 정할 수 있습니다:
rust-lang의 다른 저장소- 서브모듈, 서브트리, crates.io 의존성
- 언어 팀, 에디션 팀 등 정책을 비준하지 않은 팀
예를 들어, 다음은 이 정책의 적용 대상이 아닙니다:
- T-lang을 위한 추적 이슈
- T-lang 제안
- T-lang 안정화 보고서
- 언어 문서
- 스타일 가이드
- 컴파일러 린트의 이름. 이는 이름 자체에만 적용되며, 진단 메시지는 여전히 이 정책의 적용 대상입니다.
- 문서나 진단 메시지에서 위 내용을 그대로 인용하는 경우입니다.
동기와 지침 원칙
Rust 프로젝트 내에서 AI 기반 도구를 언제/어떻게/어디서 사용하는 것이 허용 가능한지에 대한 합의는 존재하지 않으며, 아마 앞으로도 존재하지 않을 것입니다. Rust 프로젝트와 커뮤니티의 많은 구성원이 AI에서 가치를 찾는 반면, 다른 많은 이들은 사회와 기후에 미치는 부정적 영향이 심각하여 어떠한 사용도 용납할 수 없다고 느낍니다. 또 다른 이들은 아직 자신의 의견을 정리하는 중입니다.
이러한 차이에도 불구하고, 우리 모두가 공유하는 가치가 많이 있습니다.
- 우리 공동 프로젝트에 대한 깊은 전문 지식을 갖춘 커뮤니티를 구축하는 것입니다.
- 모두가 환영받고 존중받는다고 느끼는 포용적인 커뮤니티를 구축하는 것입니다.
그리고 우리가 동의하는 많은 사실들이 있습니다.
- 많은 사람들이 LLM이 생성한 코드와 글을 읽거나 검토하는 것이 매우 불쾌하다고 느낍니다.
- 많은 사람들이 LLM을 학습과 발견에 상당한 도움이 된다고 느낍니다.
- LLM은 새로운 기술이며, 우리는 아직도 이를 사용하고, 조정하고, 개선하는 방법을 배우는 중입니다.
이러한 사실과 가치를 염두에 두고, 이 정책은 다음 목표를 위해 설계되었습니다.
- Rust가 알려진 품질 기준을 유지할 수 있도록 의도적으로 보수적인 정책을 수립합니다.
- “더 나은 것이지, 더 빠른 것이 아니다“라는 우리의 지침이 단순한 말뿐이 아님을 보여주기 위해, LLM 기여를 최고 수준의 품질로 제한하는 것입니다.
- 정책을 강제할 수 있고 조정하기 쉽게 만드는 것입니다.
- 자세히 읽지 않은 사람들도 이해하고 요약하기 쉽도록 정책을 일관되게 만드는 것입니다.
- LLM을 사용하지 않기로 선택한 기여자를 포용하고 Rust가 “페이 투 플레이“가 되는 것을 피하기 위해, LLM을 기여의 필수 요건으로 만드는 것을 피합니다.
조정 정책
탐정 역할을 하는 것은 여러분의 일이 아닙니다
“사기의 최적 수준은 0이 아닙니다”. 누군가 LLM을 사용했는지에 대해 경찰 노릇을 하려 하지 마십시오. LLM이 관여했는지 여부를 “적극적으로 찾아볼” 의무는 없습니다. 누군가 LLM을 사용했는지에 대해 경찰 노릇을 하려 하지 마십시오. LLM이 관여했는지 여부를 “적극적으로 찾아볼” 의무는 없습니다.
문체는 증거가 아니며, 영어를 외국어로 사용하는 화자, 신경다양인, 그리고 지나치게 설명이 많은 사람들이 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입니다.
- 리더십 위원회 FCP로 결정되는, 해당 정책이 Rust 프로젝트에 끼치고 있는 실질적인 해악에 대해 증거와 함께 제기된 객관적인 우려입니다.
- 제안된 LLM 위원회(또는 이와 유사한 프로젝트 전체 전담 기구)가 구성될 경우, 그 위원회가 정하는 정책은 이 정책보다 우선합니다.
위의 메커니즘으로 이 정책을 발전시키는 것이 실행 불가능하다고 판명될 경우, 특히 어떤 팀이 자신의 관할에 속하는 사안을 자율적으로 결정하는 데 있어 상당히 방해를 받게 될 경우, 이러한 수정 조건은 리더십 위원회 FCP로 조정될 수 있습니다.
인프라
이 섹션에서는 Rust의 인프라와 그 유지 방법을 문서화합니다.
외부 링크
- rust-toolstate는 Rust 저장소에 번들로 포함된 외부 도구의 빌드 및 테스트 상태를 기록합니다.
다른 Rust 설치 방법
어떤 설치 프로그램을 사용해야 합니까?
Rust는 여러 플랫폼에서 실행되며, Rust를 설치하는 방법도 여러 가지입니다. 가장 간단하고 권장되는 방식으로 Rust를 설치하고 싶다면, 메인 installation page에 있는 안내를 따르십시오.
해당 페이지에서는 rustup을 통한 설치를 설명하는데, 이는 Rust가 지원하는 모든 플랫폼에서 일관된 방식으로 여러 Rust 툴체인을 관리하는 도구입니다 (rustup 문서 참조). 그렇다면 왜 이러한 안내를 따라 설치하고 싶지 않을 수도 있을까요?
- 오프라인 설치.
rustup은 필요에 따라 인터넷에서 구성 요소를 다운로드합니다. 인터넷에 접속하지 않고 Rust를 설치해야 한다면,rustup은 적합하지 않습니다. - 시스템 패키지 매니저를 선호하는 경우. 특히 Linux에서, 그리고 Homebrew, MacPorts 또는 pkgsrc를 사용하는 macOS와 Chocolatey 또는 Scoop을 사용하는 Windows에서도, 개발자들은 때때로 자신의 플랫폼 패키지 매니저로 Rust를 설치하는 것을 선호합니다.
curl | sh방식을 선호하지 않는 경우. Unix에서는 보통curl을 통해 셸 스크립트를 실행하여rustup을 설치합니다. 이러한 방식의 보안성에 우려를 가진 사람들도 있으며, 이들은 설치 프로그램을 직접 다운로드하여 실행하는 것을 선호합니다.- 서명 검증.
rustup이 HTTPS를 통해 다운로드를 수행하기는 하지만, 오늘날 Rust 설치 프로그램의 서명을 검증하는 유일한 방법은 독립형 설치 프로그램으로 수동으로 확인하는 것입니다.
Rust의 플랫폼 지원은 three tiers에서 정의되며, 이는 사용 가능한 설치 방법과 밀접하게 대응합니다. 일반적으로 Rust 프로젝트는 모든 Tier 1 및 Tier 2 플랫폼에 대해 바이너리 빌드를 제공하며, 이들은 모두 rustup을 통해 설치할 수 있습니다. 다만 일부 Tier 2 플랫폼은 컴파일러 자체가 아니라 표준 라이브러리만 제공됩니다. 즉, 이들은 크로스 컴파일 타깃일 뿐이며, Rust 코드는 해당 플랫폼에서 실행될 수 있지만 컴파일러 자체는 그 플랫폼에서 실행되지 않습니다. 이러한 대상은 rustup target add 명령으로 설치할 수 있습니다.
rustup을 설치하는 다른 방법
rustup의 다른 설치 방법을 참조하십시오.
독립 실행형 설치 프로그램
공식 Rust 독립 실행형 설치 프로그램은 Rust의 단일 릴리스를 포함하며, 오프라인 설치에 적합합니다. 이들은 세 가지 형태로 제공됩니다: 모든 유닉스 계열 환경에서 동작하는 tarball(.tar.xz 확장자), Windows 설치 프로그램(.msi), Mac 설치 프로그램(.pkg)입니다. 이 설치 프로그램에는 rustc, cargo, rustdoc, 표준 라이브러리, 표준 문서가 포함되어 있지만, rustup이 제공하는 것과 같은 추가 크로스 타깃에 대한 접근은 제공하지 않습니다.
이들을 사용하는 가장 흔한 이유는 다음과 같습니다:
- 오프라인 설치
- Windows에서 플랫폼에 더 잘 통합된 그래픽 설치 프로그램을 선호하는 경우
이 바이너리들은 각각 Rust 빌드 인프라에 의해 GPG를 사용하여 [keybase.io에서 확인할 수 있는] [Rust 서명 키]로 서명됩니다. 아래 표에서 .asc 파일이 서명입니다.
이전 릴리스는 아카이브에서 찾을 수 있습니다.
소스 코드
Rust 툴체인을 소스 코드에서 빌드하고 싶다면, 다음 링크를 이용해 소스 코드 tarball을 다운로드할 수 있습니다.
| Channel | Archives + Signatures |
|---|---|
| stable (1.98.1) | tar.xz tar.xz.asc |
| beta | tar.xz tar.xz.asc |
| nightly | tar.xz tar.xz.asc |
게시된 소스 타르볼이 rust git 저장소의 내용과 일치하는지 확인하고자 한다면, 다음 스크립트를 템플릿으로 사용할 수 있습니다.
Script for reproducing source tarball contents
#!/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의 단일 릴리스를 포함하며, 오프라인 설치에 적합합니다. 이들은 세 가지 형태로 제공됩니다: 모든 유닉스 계열 환경에서 동작하는 tarball(.tar.xz 확장자), Windows 설치 프로그램(.msi), Mac 설치 프로그램(.pkg)입니다. 이 설치 프로그램에는 rustc, cargo, rustdoc, 표준 라이브러리, 표준 문서가 포함되어 있지만, rustup이 제공하는 것과 같은 추가 크로스 타깃에 대한 접근은 제공하지 않습니다.
이들을 사용하는 가장 흔한 이유는 다음과 같습니다:
- 오프라인 설치
- Windows에서 플랫폼에 더 잘 통합된 그래픽 설치 프로그램을 선호하는 경우
이 바이너리들은 각각 Rust 빌드 인프라에 의해 GPG를 사용하여 [keybase.io에서 확인할 수 있는] [Rust 서명 키]로 서명됩니다. 아래 표에서 .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 파일이 함께 제공되며, 이는 다운로드한 데이터의 무결성을 확인하는 데 사용할 수 있습니다. 이에 더해 각 채널의 매니페스트에는 분리된 GPG 서명인 .asc 파일도 함께 제공되어, 채널 매니페스트의 무결성뿐 아니라 진위 여부까지 확인할 수 있습니다.
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 파일의 최상위 계층은 두 개의 중요한 키/값 쌍으로 구성됩니다. 첫째로 현재 "2"인 manifest-version, 둘째로 값이 "YYYY-MM-DD" 형식인 매니페스트의 날짜(date)입니다.
그다음에는 여러 최상위 절(테이블)들이 있으며, 다음과 같습니다:
-
pkg- 이는 매니페스트의 대부분을 차지하며 릴리스에 포함된 패키지들을 나열합니다. 일반적으로 이는rust,rustc,cargo등과 같은 것들입니다.rust패키지는 다소 특수하며, 현재는 기본적으로 설치될 다른 패키지들의 부분집합을 지정하는 데 사용됩니다.패키지 안에는
components와extensions가 있습니다. 현재components는rustup에 의해 기본적으로 설치되며,extensions는 선택적 구성 요소로서rustup component add등을 통해 사용할 수 있습니다. -
renames- 이는 사용자가 별칭을 입력했을 때 가져올 올바른 패키지를 결정하는 데 사용할 수 있는 패키지 이름 변경 집합을 담고 있습니다.일반적으로 이름 변경은 패키지가 미리보기 상태를 벗어나 릴리스 품질로 간주될 때 사용됩니다. 예를 들어,
rustfmt의 실제 패키지 이름은rustfmt-preview이지만, 그 릴리스 이후로to필드가rustfmt-preview인renames.rustfmt테이블이 존재해 왔습니다. 사용자가rustup component add rustfmt를 실행하면 그 이름은 자동으로rustfmt-preview로 변환되며, 사용자가rustup component list를 실행하면rustfmt-preview는 사용자에게 표시될 때 자동으로 다시rustfmt로 변환됩니다. -
profiles- 이것은 설치할 기본 컴포넌트 집합을 결정하기 위한 향후 설정의 일부입니다.pkg.rust의components를 선택하는 대신,rustup은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 타겟의 확장에 모든 타겟에 대한 rust-std가 명시되어 있음을 확인할 수 있습니다. 이를 통해 어떤 빌드 시스템에서든 임의의 rust-std를 설치함으로써 크로스 컴파일이 가능해집니다.
서비스 인프라
Rust 인프라 팀은 여러 서비스와 봇을 유지 관리합니다. 그 목록은 여기에서 확인할 수 있습니다. 현재 상태를 포함한 인프라 관련 질문은 #t-infra Zulip stream으로 보내주십시오.
저희의 안정성 보장: 저희 서비스 중 다수는 공개적으로 접근 가능한 스토리지와 API에 의존하지만, 이들이 모두 공개 사용을 염두에 둔 것은 아닙니다. 현재로서는 static.rust-lang.org 뒤에 있는 리소스만 stable로 간주되며, 이는 해당 리소스가 (최소한) 사전 공지 없이 변경되지 않음을 의미합니다. 여러분의 작업을 위해 Rust 프로젝트 인프라의 다른 부분에 의존하고 있다면, 인프라 팀에 알려주십시오.
팀 유지 관리
Rust 팀의 구성원 명단은 항상 유동적입니다. 때때로 새로운 사람이 추가되기도 하지만, 사람들이 스스로 “전임 구성원 상태“를 선택하는 경우도 있는데, 이는 그들이 현재 의사 결정 과정에 적극적으로 참여하지 않음을 의미합니다. 안타깝게도 새로운 사람이 추가되거나 누군가 전임 구성원 상태가 될 때마다 갱신해야 할 여러 개별적인 곳들이 존재합니다.
팀 저장소
팀 구성원 자격은 주로 rust-lang/team repo의 설정 파일에 의해 관리됩니다. 해당 저장소와 통합된 시스템에 대해서는 그 저장소의 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 팀은 인프라를 계속 운영 가능한 상태로 유지하기 위해 필요한 경우 변경을 수행할 수 있도록, team repo를 포함한 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 index를 참고하십시오.)
이 글을 쓰는 시점 기준, 다음 도구가 “nightly 전용“입니다: embedded-book.
- PR이
toolstate 저장소 업데이트하기
toolstate 저장소 업데이트는 두 단계로 이루어집니다. CI가 auto 브랜치(bors가 PR을 통합에 적합한지 테스트하기 위해 옮기는 곳)에서 실행될 때, 개별 플랫폼(이 글을 쓰는 시점 기준 Linux와 Windows)의 “tool” 러너들이 각각 테스트 중인 커밋에 대한 각 도구의 상태를 기록한 JSON 파일을 저장소에 제출합니다. 이후 해당 커밋이 실제로 CI를 전부 통과하여 bors가 기본 브랜치로 옮기면, toolstate 저장소의 “현재 도구 상태“가 그에 맞게 업데이트됩니다.
이 스크립트들은 또한 도구가 깨질 때 자동으로 몇몇 사람들에게 알리고 이슈를 생성합니다.
자세한 내용은 관련 파일의 주석을 참고하십시오: checktools.sh, publish_toolstate.py 및 거기서 언급된 다른 파일들.
도구 업데이트
도구는 서브모듈을 적절한 커밋으로 업데이트함으로써 갱신할 수 있습니다.
git submodule update --remote path/to/submodule을 실행하고, 업데이트 사항을 추가하고, 테스트가 통과하는지 확인한 다음, 커밋하고 풀 리퀘스트를 보내십시오. 경로는 rust 저장소의 루트를 기준으로 하므로, 예를 들어 reference는 src/doc/reference입니다.
필수는 아니지만, subup이 이 작업에 도움이 될 수 있습니다.
도구 추가하기
참고: 저희는 시간이 지남에 따라 서브모듈과 toolstate를 벗어나려 하고 있습니다. 서브모듈 대신 서브트리를 추가하는 것을 고려하십시오: #70651
새로운 도구를 추적 대상으로 추가하려면 다음 단계를 거쳐야 합니다:
- 서브모듈과 필요한 빌드 시스템/부트스트랩 업데이트를 함께 추가하는 PR을 rust-lang/rust에 생성하십시오. 이와 같은 문제를 피하기 위해 테스트가
./x.py --no-fail-fast를 제대로 지원하는지 주의 깊게 확인하십시오. checktools.sh에 대한 변경 사항을 포함합니다:- 최상단에서 도구를 빌드합니다. 이 단계에서 도구에 대한 JSON 상태를 실제로 생성합니다.
config.toml에서save-toolstates가 설정되면, rust 빌드 시스템이 각 테스트의 상태를 담은 JSON 파일을 작성합니다. - 해당 도구가 beta 블로커여야 하는지 여부와 함께
status_check에 도구를 추가하십시오.
- 최상단에서 도구를 빌드합니다. 이 단계에서 도구에 대한 JSON 상태를 실제로 생성합니다.
publish_toolstate.py를 업데이트하여 도구를 추가합니다. 여기에는 도구가 깨졌을 때 핑할 사람들의 목록과 소스 저장소가 포함됩니다. (참고: 이 글을 쓰는 시점 기준, 이 사용자들은 rust-lang/rust GitHub에서 지정 가능한 권한을 가지고 있어야 합니다.)- toolstate 저장소에
latest.json파일에 도구를 수동으로 추가하는 PR을 제출하십시오.
인프라 팀의 정책
이 섹션은 인프라 팀이 만든 정책을 문서화합니다.
깨진 nightly에 관한 정책
때때로 저희 CI가 자동으로 배포한 nightly가 일부 사용자, 심지어는 모든 사용자에게 깨진 상태로 배포되는 경우가 있습니다. 이 정책은 그러한 경우 인프라 팀의 대응 방침을 정의합니다.
어떤 nightly가 롤백되는가
nightly는 다음 경우에만 롤백될 수 있습니다:
- 포함된 컴파일러가 사용자의 모든 파일을 삭제하는 등, 파괴적인 코드가 포함된 경우.
- 인프라 문제로 인해 Tier 1 플랫폼에서 상당 비율의 사용자에게 손상이 발생한 경우. 하위 티어 플랫폼에만 영향을 미치는 이슈는 롤백할 가치가 없습니다. 어차피 저희는 해당 플랫폼에 대해 작동하는 빌드를 보장하지 않기 때문입니다.
심각한 컴파일러 버그로 인해 깨진 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 방식을 지원하는 경우, 권한 있는(privileged) 또는 관리자(administrator) 접근 권한을 가진 프로젝트 구성원은 서비스 제공자가 지원하는 가장 안전한 MFA 방식을 반드시 사용해야 합니다. 이는 가능한 한 하드웨어 보안 키를 사용해야 하며, 하드웨어 키를 사용할 수 없는 경우에는 패스키 또는 TOTP 앱을 사용해야 함을 의미합니다.
이러한 서비스에는 다음이 포함됩니다:
- Google Workspace 및 GCP(
rust-lang.org) - AWS(AWS SSO 세션을 통해)
- Azure
- Github
- Datadog
- Fastly
- Heroku
- 1password
Rust 인프라 팀은 Yubico YubiKeys Series-5를 AAL-3 테스트를 거쳐 승인된 장치로 공식 지원합니다. 프로젝트 구성원은 원할 경우 다른 제조사의 하드웨어 키를 사용할 수 있지만, Rust 인프라 팀은 버그나 호환성 문제에 대한 지원을 제공할 수 없습니다.
이에 더해, 서비스가 여러 안전한 MFA 방식 및 장치를 지원하는 경우, 프로젝트 구성원은 추가 MFA 장치나 방식이 동일한 AAL에 속하는 한, 이중화를 위해 최소 하나의 추가 MFA 방식을 설정해야 합니다. 예를 들어 heroku 계정에 MFA를 설정할 때, 이중화 목적으로 추가 YubiKey(AAL-3)를 설정할 수 있지만, 같은 목적으로 1password를 TOTP(AAL-2)로 설정해서는 안 됩니다. 이는 특히 관리자 작업 중 공격 벡터에 노출될 수 있는 방식으로 TOTP 백업이 설정된 경우, 보안을 오히려 저하시킬 수 있기 때문입니다.
마지막으로, 중요 인프라에 대한 접근 권한을 가진 프로젝트 구성원이 MFA에 사용하던 하드웨어 장치에 대한 접근을 상실한 경우(예: 노트북 도난 또는 YubiKey 분실), 이는 Rust 인프라 팀에 반드시 알려야 하며, 해당 장치는 허용된 MFA 장치/방법으로 설정되어 있던 모든 시스템에서 즉시 철회되어야 합니다.
Yubico 하드웨어 키 지원
Yubico Secure it Forward Program의 일환으로, Rust 재단은 중요 인프라에 접근 권한이 있는 Rust 프로젝트 구성원에게 YubiKey를 제공할 예정입니다. 이러한 지원 대상 자격이 있으며 권장 YubiKey를 무료로 받고 싶다면, T-infra team in Zulip으로 문의하십시오.
다음 팀의 구성원은 이 지원을 받을 자격이 있습니다:
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 또는 Amazon S3(rust-lang AWS 계정 내)를, 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 challenge를 사용하십시오).
- 설명: 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
또한 앞서 생성한 IAM 사용자의 자격 증명으로 Travis CI 웹 UI에서 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 security key“는 선택하지 마십시오: AWS API의 제약으로 인해 CLI를 사용할 수 없게 되어 접근이 콘솔로만 제한됩니다.
AWS 콘솔 사용하기
AWS 콘솔은 저희 AWS 계정 내 대부분의 리소스에 대한 시각적 인터페이스를 제공합니다.
AWS CLI 사용하기
AWS CLI를 사용하면 터미널이나 스크립트에서 저희 AWS 계정을 다룰 수 있습니다. 처음 설정할 때는 Amazon의 문서를 따라 설치하고 자격 증명을 설정하십시오.
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 console로 이동하여 방금 생성한 사용자의 보안 자격 증명 페이지를 열고 콘솔 비밀번호를 활성화하십시오. AWS가 무작위 비밀번호를 생성하도록 하고, 최초 로그인 시 비밀번호를 변경하도록 요구하십시오.
마지막으로 사용자에게 생성된 비밀번호로 로그인할 수 있다는 것과, “AWS access for team members” 페이지를 따라 2FA를 활성화하고 계정에 접근하는 방법을 배우도록 안내하십시오.
접근 권한 회수
사용자의 접근 권한을 회수하려면 IAM console에 로그인하여 삭제하려는 사용자의 보안 자격 증명 페이지를 열고 다음을 수행하십시오.
- 콘솔 비밀번호에서 “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-desktops와 같이 좀 더 전 세계적으로 분산시키고자 하는 서비스는 유럽에도 배포하고자 합니다. 여기서는 다음 리전들이 가장 합리적으로 보입니다:
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 접근 권한이 있는 사람들은 볼트에 저장된 마스터 키를 사용하여 모든 계정에 로그인할 수 있습니다.
일반적인 유지 관리 절차
Bastion 서버에 새 사용자 추가하기
Bastion에 새 사용자를 추가하려면 ansible/roles/common/files/ssh-keys에 <username>.pub라는 이름의 파일로 해당 사용자의 키를 추가하고, Ansible playbook을 수정하여 해당 사용자를 비특권 사용자 목록에 추가해야 합니다. 해당 사용자가 어떤 서버에 접근할 수 있는지를 명확히 하는 코멘트를 남겨 주십시오.
그 작업이 끝나면 플레이북을 적용하십시오.
Bors
인프라 팀은 rust-lang/rust에서 사용하기 위한 “Bors”라는 병합 큐 봇을 관리합니다. 해당 인스턴스는 bors.rust-lang.org에서 이용할 수 있으며, @bors GitHub 계정으로 운영됩니다.
이 서비스는 Terraform으로 구성되며, rust-lang/bors 저장소에서 우리의 ECS 클러스터로 자동 배포됩니다.
콘텐츠 전송 네트워크
Rust 프로그래밍 언어 사용자는 다양한 방식으로 프로젝트의 인프라와 상호작용합니다. 이들은 프로젝트의 웹사이트와 문서에 접근하고, 크레이트 인덱스를 조회하며, Rust 릴리스와 크레이트를 다운로드합니다. 이러한 리소스는 Rust 프로젝트가 호스팅하며 콘텐츠 전송 네트워크 (CDN)를 통해 제공됩니다.
이 문서는 우리가 CDN을 사용하는 이유, 무엇에 사용하는지, 그리고 어떻게 설정했는지를 설명합니다.
목표
우리가 인프라에서 CDN을 사용하는 목표는 세 가지입니다:
- 더 저렴한 요금제와 캐싱을 통해 아웃바운드 트래픽 비용을 절감합니다
- 오리진 서버의 부하를 줄여 컴퓨팅 자원을 절약합니다
- 일부 리소스에 대한 레거시 URL을 재작성할 수 있는 방법을 제공합니다
비용 절감
오픈소스 프로젝트로서, 우리는 인프라 비용에 매우 신경을 써야 합니다. 아웃바운드 트래픽은 단연 월간 청구서에서 가장 비용이 많이 드는 항목 중 하나이며, Rust가 더 인기를 얻을수록 계속 증가할 항목입니다.
클라우드 제공업체는 일반적으로 서비스에 따라 아웃바운드 트래픽에 서로 다른 요금을 부과합니다. 예를 들어, Amazon S3에서 데이터를 직접 제공하는 것은 Amazon CloudFront 배포를 통해 동일한 데이터를 제공하는 것보다 더 비쌉니다. 이것이 우리가 캐싱과 같은 CDN의 다른 기능을 활용할 수 없는 서비스에서도 이제 기본적으로 CDN을 사용하는 이유입니다.
인프라
프로젝트 리소스 대부분은 AWS에서 호스팅됩니다. 정적 콘텐츠는 Amazon S3에 저장되는 반면, 동적 콘텐츠는 서버에서 로드됩니다. 두 유형의 콘텐츠 모두 CDN인 Amazon CloudFront와 Fastly를 통해 제공됩니다.
사용자가 리소스에 접근할 때, 예를 들어 크레이트를 다운로드하려고 할 때, 이들은 CDN을 통해 리소스에 접근하게 됩니다. 서로 다른 distribution은 도메인 이름을 구성 및 백엔드(origin이라고 함)에 매핑합니다. 예를 들어, static.crates.io에서 크레이트를 다운로드하는 것은 S3 버킷에서 크레이트를 가져온 다음 향후 요청을 위해 캐싱하는 distribution을 거칩니다.
┌──► S3 (static content)
│
User ───────► CDN ────┤
│
└──► Server (dynamic content)
Distributions
많은 배포판이 있으며, 이들은 모두 rust-lang/simpleinfra 저장소에 설정되어 있습니다. 하지만 이들의 사용량은 매우 고르지 않게 분포되어 있습니다. 다음 distribution들은 트래픽 양과 생태계에 대한 중요성 양 측면에서 프로젝트에 가장 중요한 것들입니다.
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는 Rust 프로젝트의 메인테이너와 기여자에게 고성능 클라우드 컴퓨팅에 대한 무료 접근을 제공합니다. 이는 [Rust 재단]의 Cloud Compute Program의 일부입니다.
| 머신 | 아키텍처 | Perf 활성화 | 위치 |
|---|---|---|---|
dev-desktop-eu-1 | aarch64 | 예 | 독일 |
dev-desktop-us-1 | aarch64 | 예 | N. 버지니아, 미국 |
dev-desktop-eu-2 | amd64 | 아니오 | 벨기에 |
dev-desktop-us-2 | amd64 | 아니오 | 아이오와, 미국 |
프로그램에 신청하는 방법
현재 이 프로그램과 컴퓨팅 인스턴스에 대한 접근 권한은 선별적으로 부여되며, 주로 프로젝트 메인테이너와 Google Summer of Code와 같은 오픈소스 프로그램의 컨트리뷰터에게 제공됩니다. 프로그램이 개발 중인 동안에는 certain teams로 제한됩니다. 이러한 팀 중 하나에 속해 있다면 자동으로 접근 권한을 갖게 됩니다.
Rust 프로젝트에서의 작업이 강력한 빌드 머신에 접근함으로써 크게 개선될 것 같다면, team repo에 풀 리퀘스트를 열어 자신을 cloud-compute 팀에 추가하십시오. 개발 데스크톱을 어떻게 사용하고 이를 통해 어떤 이점을 얻을지에 대한 간단한 설명을 포함하십시오. 그러한 PR의 예시는 여기에서 확인할 수 있습니다.
- Rust 프로젝트 팀 중 한 곳의 메인테이너가 귀하의 기여를 기꺼이 보증한다면, Infra 팀은 일반적으로 그 보증에 따라 요청을 기꺼이 수락합니다.
- 그렇지 않은 경우, Infra 팀이 사안별로 접근 요청을 평가합니다.
Dev Desktop에 접속하는 방법
각 사용자는 개발용 데스크톱에 자신의 계정을 가지고 있습니다. 계정 이름은 사용자의 GitHub 핸들에 gh- 접두사를 붙여 지어집니다. 예를 들어, GitHub 핸들이 user인 사용자는 Dev Desktop에서 gh-user라는 이름의 사용자 계정을 갖게 됩니다. GitHub 핸들은 대소문자를 구분하므로 gh-User와 gh-user는 서로 다른 계정으로 취급된다는 점에 유의하십시오.
사용자는 SSH를 통해 Dev Desktop에 접속할 수 있습니다. 개발용 데스크톱은 공개 키 인증을 사용하며, GitHub에서 사용자의 공개 키를 자동으로 가져옵니다.
다음 명령으로 인스턴스에 연결할 수 있습니다:
ssh gh-<your-username>@<name>.infra.rust-lang.org
<name>을 페이지 상단 표에 있는 머신 이름으로 바꾸십시오. 예를 들어 dev-desktop-eu-1.infra.rust-lang.org 호스트네임을 사용하여 dev-desktop-eu-1에 연결합니다.
GitHub에 공개 키가 없다면, SSH 키를 생성하여 GitHub 계정에 추가하는 방법을 설명하는 다음 가이드를 읽으십시오. 키가 추가된 후 개발용 데스크톱이 업데이트되기까지 몇 분이 걸릴 수 있습니다.
명령을 더 간편하게 만들기 위해 ~/.ssh/config에 다음과 같이 별칭(alias)을 설정할 수 있습니다:
Host rustvm
User gh-<your-username>
HostName <name>.infra.rust-lang.org
그러면 ssh rustvm으로 연결할 수 있습니다.
SSH 호스트 키 지문
Dev Desktop에 처음 접속할 때는 SSH 호스트 키 지문을 확인해야 합니다. 다음 지문들이 개발용 데스크톱에서 사용됩니다.
| 머신 | 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 데스크톱의 기본 셸을 설정할 수 있습니다. 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 데스크톱에 새 설정을 배포해야 합니다. 그 이후에야 기본 셸이 변경됩니다.
Rust 툴체인을 설치하는 방법
Dev Desktop에는 Rust가 사전 설치되어 있지 않지만, 대신 로컬 저장소나 worktree로부터 특정 툴체인을 쉽게 설치할 수 있게 해줍니다.
먼저 rustup을 설치하기 위해 다음 명령을 실행하십시오:
/usr/local/bin/init.sh
자신만의 버전의 Rust로 작업할 필요가 없거나 원하지 않는다면, 다음 섹션을 건너뛰고 작업을 시작해도 됩니다.
아직 하지 않았다면, GitHub에서 rust-lang/rust 저장소를 열고 개인 계정에 포크를 생성하십시오. 그런 다음 Dev Desktop에 접속하여 다음 스크립트를 실행하십시오:
/usr/local/bin/setup_rust.sh
이 스크립트는 개인 포크를 Dev Desktop으로 클론하고, rust-lang/rust의 최신 버전을 체크아웃한 뒤 컴파일합니다. 작업이 끝나면 로컬에서 작업할 수 있도록 스테이지들을 링크합니다.
이 디렉터리에는 worktree와 Rust 버전을 관리하기 위한 스크립트가 더 포함되어 있습니다. help.sh를 실행하면 이들의 목록과 간단한 설명을 확인할 수 있습니다.
GitHub와 상호작용하는 방법
dev 데스크톱은 사용자 계정에 속한 GitHub 상의 저장소와 함께 작동하도록 설계되어 있습니다. GitHub 앱은 자격 증명을 보호하고, dev 데스크톱이 접근할 수 있는 저장소를 세밀하게 제어할 수 있도록 사용됩니다.
먼저 https://github.com/apps/rust-cloud-vms로 이동하여 앱에 저장소 접근 권한을 부여하십시오. Dev Desktop에서 사용하려는 저장소(예: rust-lang/rust의 포크)에만 접근 권한을 부여하는 것이 권장됩니다.
그런 다음 Dev Desktop에 접속하여 작업하려는 저장소를 HTTPS로 클론하십시오. 이후에는 평소처럼 저장소로 작업할 수 있습니다.
내부적으로 GitHub 앱은 Git의 자격 증명 헬퍼 역할을 하며, 사용자가 앱에 부여한 권한 범위 내에서 임시 접근 토큰을 생성합니다. 오류가 발생하면 권한을 검토하여 앱이 저장소에 접근할 수 있도록 허용되어 있는지 확인하십시오.
Visual Studio Code에서 원격 개발을 설정하는 방법
대부분의 최신 코드 에디터는 SSH를 통한 원격 개발을 지원합니다. 이는 로컬에서 코드를 작성하되 Dev Desktop 내부에서 실행하는 데 사용될 수 있습니다. 설정은 다소 다르겠지만, 다음 Visual Studio Code 예제는 다른 에디터에도 적용 가능할 것입니다.
VS Code로 원격 개발을 설정하는 것은 매우 간단하며, VS Code 문서인 Remote Development using SSH에 자세히 설명되어 있습니다. 요약하면 다음과 같습니다:
- Dev Desktop에 SSH로 접속하여 작업하려는 저장소를 로컬 폴더로 클론하십시오
- 그런 다음 로컬 머신에서 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 guide의 [디스크 공간에 관한 remarks] 섹션에는 디스크 공간을 정리하는 방법에 대한 몇 가지 팁이 있습니다.
피드백을 전달하고 이슈를 보고하는 방법
Dev Desktop에 문제가 있거나 피드백 및 제안 사항이 있다면 [인프라 팀]에 연락하십시오:
저희가 rust-lang/simpleinfra 저장소에 이슈를 생성해 달라고 요청할 수도 있습니다.
Dev Desktop 변경 사항 공지
인프라 팀은 중요한 Dev Desktop 변경 사항을 #t-infra/announcements Zulip 채널을 통해 공지합니다.
외부 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 러너의 경우
- repositories:
rustc_codegen_cranelift(활성화됨, 미사용)
- 문서
powerpc64le-unknown-linux-gnu
runs-on:ubuntu-24.04-ppc64le- type: IBM 러너
s390x-unknown-linux-gnu
이 대상은 다양한 외부 러너에서 사용할 수 있습니다.
캐노니컬 러너:
runs-on:self-hosted-linux-s390x-resolute-large-rust(ubuntu-26.04)- repositories: 모든 저장소에서 동작하므로 별도로 활성화할 필요가 없습니다.
- type: 캐노니컬 러너.
IBM 러너:
runs-on:ubuntu-24.04-s390x- type: IBM 러너.
러너의 종류
이 절에서는 둘 이상의 러너에 공통되는 정보를 모아 정리합니다.
캐노니컬 러너
- 최대 동시 실행 수: 10
- GitHub 앱
- 운영자 저장소
- contact: 다운타임은 Canonical Runners downtime Zulip 토픽에 보고됩니다.
- repositories: 이 앱은 조직 전체에 설치되어 있으므로 모든 저장소에서 러너를 사용할 수 있습니다.
IBM 러너
- GitHub 앱
- repositories:
이 GitHub 앱은 실행하는 데 관리자 권한이 필요하기 때문에 rust-lang/rust 저장소에 설치할 수 없습니다. 자세한 내용은 이 GitHub 이슈를 참고하십시오.
Infrastructure details
- 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 에서 확인할 수 있습니다.
App 설정 방법
- https://github.com/settings/apps 로 이동하십시오.
- New Github App
- 메타데이터(이름과 url)를 입력하십시오.
- WebHook 체크박스를 비활성화하십시오.
Contents - Repository contents, commits, branches, downloads, releases, and merges.를 읽기/쓰기로 설정하십시오.Workflows - Update GitHub Action workflow files.를 읽기/쓰기로 설정하십시오.- “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와 같은 폴더)에 복사하십시오.
App용 .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를 사용하고 있습니다. credential helper를 디버깅하려면 이를 사용자 공간에 두고 다음과 같이 실행하십시오.
git -c credential.helper -c credential.UseHttpPath=true /path/to/helper push origin branch
이는 ssh url로 등록된 원격 저장소에는 작동하지 않는다는 점에 유의하십시오. 반드시 https를 사용해야 합니다!
첫 번째 명령줄 인자는 get, store 또는 remove입니다. 저희의 경우, 어차피 호출할 때마다 자격 증명을 재생성하므로 get을 제외한 모든 경우에는 그냥 중단합니다(exit(0)).
실제 인자는 표준 입력을 통해 전달되며 보통 다음과 같은 형태입니다
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
}
마지막으로 Route53 존의 ID를 출력해야 하며, 이를 통해 우리 Terraform 구성의 다른 부분에서 레코드를 추가할 수 있습니다. 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을, apex 도메인에는 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 서비스가 존재할 경우 해당 서비스도 재시작됩니다.
애플리케이션 변경 사항 배포
각 애플리케이션은 저희 AWS 계정의 ECR 저장소에 자체 Docker 컨테이너를 저장합니다. 변경 사항은 수동으로도, (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를 무료로 받고 싶다면 T-infra in Zulip에 문의하십시오.
Rust 인프라에서의 YubiKey 지원
지원되는 모델
Rust 인프라 팀은 Yubico Series 5 USB/NFC models 제품을 검증했으며, 프로젝트 구성원에게 발생할 수 있는 문제에 대해 Yubico Series 5 키를 공식적으로 지원합니다.
지원되는 펌웨어 버전
기존 security advisories에 따르면 버전 v5.7.4 이상의 펌웨어를 가진 YubiKey만 허용됩니다. ykman CLI 또는 Yubico Authenticator를 사용하여 YubiKey의 check the current firmware version을 확인할 수 있습니다.
권장하는 첫 단계
Yubico가 제공하는 공식 오픈소스 도구 중 Yubikey 설정에 도움이 되는 것이 최소 두 가지 있습니다.
- Yubico Authenticator (소스: Desktop and Android | iOS):
- 데스크톱 앱은 대부분의 작업을 상당히 잘 처리할 수 있습니다.
- 모바일 앱은 Google Play 또는 Apple App Store에서 직접 다운로드할 수 있습니다.
- Yubico Manager CLI (소스: yubikey-manager): 고급 사용 사례를 위한 것입니다.
좋은 첫 단계로서, Rust 인프라 팀은 Yubico Authenticator Desktop 앱을 사용하여 다음 항목의 기본값을 변경할 것을 권장합니다:
- 패스키 PIN 기본값은 Passkeys PIN default value reference를 참고하십시오.
- PIV 앱 PIN 및 PUK 기본값은 PIV app PIN and PUK default values reference를 참고하십시오.
CLI를 선호하는 분들에게는, 시스템에 전역으로 설치하거나 Python 설치를 관리하지 않고도 ykman Python 유틸리티를 실행할 수 있는 대안으로 uvx를 사용할 수 있습니다. 하위 명령에 대해 더 알아보려면 ykman online documentation을 참조하십시오.
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 policy는 호환되는 모든 핵심 Rust 인프라 시스템에 대해 이 옵션을 사용하도록 의무화하고 있습니다.
하드웨어 기반 다중 요소 인증
다중 요소 인증이 필요하지만 안전한 MFA 방법으로 TOTP 코드만 지원하는 시스템의 경우, YubiKey는 Google Authenticator와 같은 기존 솔루션의 대안으로도 작동할 수 있습니다. 이 경우, Yubico Authenticator 앱을 사용해 [하드웨어 기반 TOTP 코드 생성]을 설정할 수 있습니다.
이 옵션은 서드파티 클라우드 시스템에 의존하지 않고도 모바일 폰과 데스크톱 시스템 모두에서 작동하는 TOTP 코드를 갖는 방법을 제공하지만, TOTP 코드 생성을 물리적 장치와 결합시키는 단점도 있습니다. 오프라인 전용 TOTP 인증 앱을 사용하는 것과 마찬가지로, YubiKey를 분실하면 TOTP 코드에 대한 접근 권한도 잃게 되므로 복구 코드 백업에 관해 추가적인 주의가 필요합니다.
하드웨어 기반 TOTP 코드 설정은 Rust 프로젝트 구성원에게 선택 사항임을 유의하십시오.
하드웨어 기반 SSH 키
SSH 인증에 추가적인 보안을 원하는 분들을 위해, YubiKey는 [하드웨어 기반 SSH 키 쌍을 위한 다양한 옵션]을 제공합니다. SSH 키를 하드웨어 기반으로 하면 개인 SSH 키를 서로 다른 머신 간에 손쉽게 이동할 수 있습니다.
예를 들어, Github 계정에서 Yubikey 기반 SSH 키를 사용하고 싶다면, [FIDO2 인증에 대한 OpenSSH 내장 지원]이 시작하기 가장 쉬운 방법일 수 있습니다.
설정은 운영체제에 따라 다르지만, 커밋 서명과 같은 작업이 예상대로 계속 작동하도록 Git 설정을 조정해야 할 수도 있음을 유념하십시오. 특히, 모든 Git 작업마다 패스키 PIN을 요청하지 않도록 하는 것을 권장합니다. 이는 역효과를 낳을 수 있기 때문입니다.
예를 들어, 이 명령은 FIDO2를 통해 새로운 상주(resident) SSH 키를 정의하며, 매번 패스키를 요청하지 않고 Git 작업에서 하드웨어 키를 터치할 필요도 없게 만듭니다.
ssh-keygen -t ed25519-sk \
-O resident \
-O application=ssh:git \
-C "me@email.com"
하드웨어 기반 SSH 키는 현재로서는 선택 사항이지만, 향후 특정 관리 인프라 작업에서 필수가 될 수도 있습니다. CLI나 Yubico Authenticator 앱을 통해 YubiKey에서 사용 가능한 상주 SSH 키를 확인할 수 있습니다.
$ ykman fido credentials list
Enter your PIN:
Credential ID RP ID Username Display name
86707903... ssh:git openssh openssh
PIV 기반 키와 증명(attestation)
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 슬롯에 접근할 때 PIN 확인을 최소
once(1회) 요구하도록 설정하십시오. - 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 string이어야 합니다valid-days는 인증서 수명을 설정합니다(기본값은 365일입니다)
Attestation 내보내기 및 팀 DB를 통한 공유
자체 서명 인증서 외에도, PIV 기반 키 쌍에 대한 attestation certificate를 생성할 수 있습니다.
ykman piv keys attest 9a attestation-9a.pem
ykman piv keys attest 9c attestation-9c.pem
이러한 attestation을 검증할 수 있도록 하려면, YubiKey에 사전 로드되어 있으며 Yubico의 루트 attestation CA로 서명된 Yubico의 중간 attestation 인증서도 내보내야 합니다.
ykman piv certificates export f9 f9-intermediate.pem
이 파일들을 team DB에 추가하려면 다음 단계를 따르십시오.
- team/hardware-keys 아래에 YubiKey의 시리얼 번호를 이름으로 하는 디렉터리를 생성하십시오.
- 설정한 PIV 슬롯에 대한
attestation-9*.pem파일들을 추가하십시오. - YubiKey의
f9-intermediate.pemattestation 파일을 추가하십시오. team/people/<your-user>.toml에서 이 파일들에 링크하십시오.
자세한 내용은 TOML schema를 참고하십시오.
FAQ
YubiKey로 2FA를 사용해야 하는 서비스는 무엇입니까?
저희 MFA policy를 확인해 주십시오.
YubiKey로 선호하는 클라우드 CLI 인증을 처리하려면 어떻게 해야 합니까?
Gcloud CLI의 경우, web-based authentication flow가 자동으로 선택한 2FA 방법을 요청하며, 그 이후에는 추가 단계가 필요하지 않습니다. Google 계정에서 선호하는 MFA method in your Google account로 설정하기만 하면 됩니다.
AWS CLI의 경우, CLI 구성을 설정할 때 IAM 사용자 세션이 아니라 AWS SSO user sessions를 사용해야 합니다. 이 흐름은 선택한 웹 브라우저로 로그인할 때 2FA 방법을 요청합니다.
Rust 인프라는 AWS Identity Center configuration을 통해 프로젝트 구성원에게 SSO 접근을 제공합니다. 다만, MFA method of choice in your AWS user account로 YubiKey를 구성해야 하는 것은 여전히 필요합니다.
모니터링
- 호스팅 위치:
monitoring.infra.rust-lang.org(bastion 뒤에 있음 – 연결하는 방법) - 메인테이너: pietroalbini, 인프라 팀
- 공개 URL: grafana.rust-lang.org
- 이 서버를 배포하기 위한 Ansible 플레이북입니다.
- 인스턴스 메트릭(인프라 팀 구성원만 이용 가능합니다).
서비스 설정
저희의 모니터링 서비스는 세 부분으로 구성됩니다: 메트릭을 스크레이핑, 수집, 모니터링하는 Prometheus, Prometheus가 생성한 알림을 전달하는 Alertmanager, 그리고 메트릭을 표시하는 Grafana입니다. 모든 부분은 Ansible을 통해 설정됩니다.
Prometheus가 어차피 7일 후에 메트릭을 삭제하므로 메트릭 자체는 백업되지 않지만, Grafana 대시보드는 PostgreSQL 데이터베이스에 저장되며, 이는 rust-backups 버킷(monitoring 하위 디렉터리)에서 restic으로 백업됩니다. 백업을 복호화하는 비밀번호는 1password에 있습니다.
일반적인 유지보수 절차
새 메트릭 소스 스크레이핑
Prometheus는 HTTP 엔드포인트 목록을 주기적으로 스크레이핑하여 메트릭을 자체 형식으로 가져오는 방식으로 동작합니다. 저희 설정에서 이 목록은 simpleinfra 저장소의 ansible/playbooks/monitoring.yml 파일 내 prometheus_scrape 섹션에 위치합니다.
새 메트릭 소스를 추가하려면 기존 작업(job)에 엔드포인트를 추가하거나, 스크레이핑하려는 메트릭이 다른 작업과 관련이 없다면 새 작업을 추가하십시오. 엔드포인트는 모니터링 인스턴스에서 접근 가능해야 합니다. 사용 가능한 모든 옵션을 확인하려면 Prometheus 문서를 참고하실 수 있습니다.
새 알림 생성
알림은 Prometheus 설정에 정의된 사용자 지정 규칙이 참(true)으로 평가될 때마다 생성됩니다. 저희 설정에서 규칙 목록은 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
사용자가 로그인 권한이 있는 팀의 구성원이 되면, 자동으로 메인 Grafana 조직에 “viewer” 권한으로 추가됩니다. 인프라 팀 구성원의 경우 이를 “관리자(admin)“로 변경해야 하며(“Configuration” -> “Users“에서), 그 외에는 뷰어 권한으로 두십시오.
기본적으로 뷰어는 제한되지 않은 대시보드에만 접근할 수 있습니다. 다른 대시보드에 대한 접근 권한을 부여하려면 “Configuration” -> “Teams” 페이지에서 해당 팀에 추가해야 합니다. “Server Admin” -> “Users” -> “<username>” 페이지에서 전체 Grafana 인스턴스에 대한 관리자 권한을 부여하는 것도 가능합니다. 신뢰할 수 있는 인프라 팀 구성원 외에는 이러한 권한을 부여하지 마십시오.
추가 자료
Renovate
Renovate는 크레이트, GitHub Actions, Docker 베이스 이미지와 같은 의존성을 최신 상태로 유지하기 위해 저희(인프라 팀)가 권장하는 도구입니다.
의존성 업데이트에 대하여
왜 의존성을 최신 상태로 유지해야 할까요?
버그 수정, 성능 개선, 보안 패치, 새로운 기능을 얻고 전반적으로 더 나은 개발자 경험을 누리기 위해서입니다.
의존성은 얼마나 자주 업데이트해야 할까요?
의존성 업데이트 PR을 너무 자주 받는 것은 부담스럽습니다. 예를 들어, 의존성의 모든 새 버전마다 PR을 받는 것은 권장하지 않습니다.
대신, 일주일에 한 번 또는 한 달에 한 번처럼 정기적인 일정으로 몇 개의 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가 예상대로 동작하지 않는 이유를 이해할 수 없다면, 드라이런 모드로 로컬에서 실행하여 의존성 업데이트를 어떻게 해석하는지 평가할 수 있습니다:
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
일반적인 유지 관리 절차
새 도메인 추가하기
먼저 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 대상에 대한 테스트 작업 등을 포함하지만 이에 국한되지 않습니다. 여기에는 모든 티어 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 CI가 전혀 관련 없는 PR에서도 실패하게 만들 수 있으며, 이는 다른 기여자들에게 매우 곤혹스러운 일입니다.
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 trusted publishing는 GitHub Actions 시크릿에 장기 유효한 crates.io API 토큰을 저장하지 않고도 GitHub Actions 워크플로가 크레이트를 게시할 수 있게 해줍니다.
신뢰 게시(trusted publishing)는 Rust 프로젝트 크레이트를 게시하는 데 권장되는 방법입니다.
이 페이지에서는 rust-lang/team 저장소를 사용하여 Rust 프로젝트 저장소에서 신뢰 게시를 구성하는 방법을 설명하며, 가장 일반적인 게시 패턴을 다룹니다.
저장소가 Rust GitHub 조직 아래에 호스팅되어 있지 않은 경우, rust-lang/team 저장소를 통해 신뢰 게시를 구성할 수 없습니다. 대신 crates.io trusted publishing 문서의 안내를 따르십시오.
1. 크레이트 소유권 설정
신뢰 게시로 관리하려는 크레이트가 rust-lang-owner 계정 소유인지 확인하십시오. 그렇지 않은 경우, 해당 계정을 소유자로 초대하고 #t-infra Zulip 채널에서 초대를 수락해 달라고 요청하십시오.
2. 팀 저장소 구성
저장소 TOML 파일에 crates.io 크레이트 관리 문서에 설명된 대로 [[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);- 해당 작업(job)에
permissions: id-token: write를 설정하여 GitHub가 OIDC 토큰을 발급할 수 있도록 합니다; - 이름이
rust-lang/team의publish-workflow와 일치하는 워크플로 파일.
GitHub Actions로 크레이트를 게시할 때 다음 접근 방식 중 하나를 사용할 수 있습니다.
3a. cargo를 사용하여 게시하기
crates-io-auth-action을 사용하고 워크플로에서 직접 cargo publish를 실행하려면 이 접근 방식을 사용하십시오.
워크플로를 설정하고 액션을 사용하는 방법에 대한 자세한 내용은 crates.io trusted publishing 문서의 “GitHub Actions Setup” 섹션을 참조하십시오.
작업(job)의 environment 필드는 Rust 프로젝트 크레이트에 필수이며, rust-lang/team 저장소의 publish-environment 필드와 일치해야 합니다.
예를 들면:
jobs:
publish:
environment: publish
...
이 접근 방식의 예시는 이 GitHub 검색을 사용하여 Rust 프로젝트 저장소에서 찾을 수 있습니다.
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: ubuntu-latest
이 워크플로는 다음과 같은 다른 GitHub 이벤트에서도 트리거할 수 있다는 점에 유의하십시오.
- release 시:
on: release: types: [created] - workflow dispatch로 수동 실행:
on: workflow_dispatch:
3b. 기본 브랜치에서의 release-plz
더 많은 자동화를 원하신다면, 워크플로에서 cargo publish를 직접 실행하는 대신 release-plz를 확인해 보십시오.
release-plz의 예시는 이 GitHub 검색을 사용하여 Rust 프로젝트 저장소에서 찾을 수 있습니다.
내부용 인프라 공지
중요한 내부용 인프라 변경 또는 업데이트가 있을 경우, 인프라 팀은 인프라 공지 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 관리자 권한이 필요하다는 점에 유의하십시오.
공지 핑(ping) 에티켓
모든 프로젝트 구성원에게 영향을 미치는 중요한 공지를 제외하고는, 일반적인 T-all Zulip 그룹 핑을 피하십시오. T-all 구성원은 명시적으로 탈퇴하지 않는 한 이미 공지 채널을 구독하고 있을 것입니다.
다음 중 하나를 우선하십시오.
- 모든 프로젝트 구성원에게 명시적으로 핑을 보내지 않고 그냥 공지 토픽만 작성하거나,
- 특정 프로젝트 팀만 영향을 받는 경우에는 해당 프로젝트 팀에만 타겟 핑을 사용하십시오.
언어
이 섹션에서는 언어 팀의 메타 프로세스를 문서화합니다.
외부 링크
- 언어 팀은 Zulip에 소통 채널을 두고 있습니다.
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 Guidelines를 참고하십시오.
기여하기
오래되었거나, 명세가 부족하거나, 누락되었거나, 그냥 틀린 부분을 발견하시면 rust-lang/rust-forge 저장소에 자유롭게 PR을 열어 주십시오!
용어
- 라이브러리 팀(Libs). 바로 저희입니다! 표준 라이브러리의 개발과 유지관리(를 비롯한 여러 일)를 담당하는 팀입니다. 바로 저희입니다! 표준 라이브러리의 개발과 유지 관리(및 그 외 여러 업무)를 담당하는 팀입니다.
- 풀 리퀘스트(PR).
rust-lang/rust에 대한 일반적인 GitHub 풀 리퀘스트입니다. - 코멘트 요청(RFC).
rust-lang/rfcs에 작성되는, 새로운 기능을 도입하는 공식 문서입니다. - 추적 이슈.
C-tracking-issue태그가 붙은 일반적인 GitHub 이슈입니다. - 최종 의견 수렴 기간(FCP)입니다.
rfcbot이 조율하며, 관련 팀들에게 RFC와 PR을 검토할 기회를 부여합니다.
확신이 서지 않으신다면…
표준 라이브러리를 유지 관리하는 것은 부담스러운 책임처럼 느껴질 수 있습니다! triagebot를 통한 자동 리뷰어 배정으로 인해, 여러분은 수많은 새로운 맥락 속에 놓이게 될 것입니다.
언제든 GitHub에서 @rust-lang/libs 팀에 핑을 보내십시오. 저희 모두가 도와드리기 위해 여기 있습니다!
자신이 PR을 검토하기에 가장 적합한 사람이 아니라고 생각되신다면 triagebot을 사용해 다른 사람에게 배정하십시오.
여러분의 입력을 기다리는 리뷰 찾기
https://rfcbot.rs/ 를 정기적으로 확인하는 것을 잊지 마십시오. 닉네임이 등장하는 부분을 클릭하면 https://rfcbot.rs/fcp/SimonSapin 과 같은 페이지로 이동하며, 그곳에는 여러분의 입력을 기다리는 리뷰만 표시됩니다.
PR 검토하기
라이브러리 팀의 구성원으로서 여러분은 리뷰가 필요한 풀 리퀘스트에 배정되거나, Rust 프로젝트의 이슈에 대해 의견을 요청받게 됩니다.
RFC는 언제 필요합니까?
새로운 불안정 기능은 병합되기 전에 RFC가 필요하지 않습니다. 기능이 작고 설계 공간이 단순하다면, 안정화는 대개 해당 기능이 FCP를 거치기만 하면 됩니다. 하지만 때로는 안정화 전에 RFC를 요청할 수도 있습니다.
unsafe가 있습니까?
표준 라이브러리 내 unsafe 코드 블록에는 왜 안전한지 설명하는 주석이 필요합니다. 이를 검사하는 tidy 린트가 있습니다. unsafe 코드는 실제로도 문제가 없어야 합니다.
무엇이 sound하고 무엇이 그렇지 않은지에 관한 규칙은 미묘할 수 있습니다. 현재의 견해를 확인하려면 Unsafe Code Guidelines WG를 참고하십시오. 그리고 어떤 의심이라도 든다면 @rust-lang/libs, @rust-lang/lang, 및/또는 WG의 누군가에게 핑을 보내는 것을 고려하십시오. 저희는 unsafe 코드의 건전성에 대해 논의하는 것을 좋아하며, 더 많은 눈이 살펴볼수록 좋습니다!
그 #[inline]은 올바릅니까?
인라이닝은 잠재적인 실행 속도, 컴파일 시간, 코드 크기 사이의 트레이드오프입니다. 이에 관해서는 hashbrown 크레이트에 대한 이 PR에서 논의가 있었습니다. 그 스레드에서 발췌하면:
#[inline]은 단순한 인라인 힌트와는 매우 다릅니다. 앞서 언급했듯이, C++에는#[inline]이 하는 일에 대응하는 것이 없습니다. 디버그 모드에서 rustc는 기본적으로#[inline]을 무시하며, 여러분이 그것을 작성하지 않은 것처럼 취급합니다. 릴리스 모드에서 컴파일러는 기본적으로#[inline]함수를 참조하는 모든 코드생성 유닛마다 코드생성을 하며, 여기에inlinehint도 추가합니다. 이는 만약 16개의 CGU가 있고 이들이 모두 어떤 항목을 참조한다면, 각각 모두에 그 항목의 전체 구현이 인라인된다는 의미입니다.
#[inline]을 추가할 수 있는 경우:
- 공개되고, 작고, 제네릭이 아닌 함수에 대해.
#[inline]이 필요하지 않아야 하는 경우:
- 스코프 내에 제네릭이 있는 메서드에 대해.
- 기본 구현이 없는 트레이트의 메서드에 대해.
#[inline]은 언제든 나중에 도입할 수 있으므로, 의심스럽다면 그냥 제거하면 됩니다.
#[inline(always)]는 어떻습니까?
#[inline(always)]가 필요한 경우는 거의 없어야 합니다. 제한된 소수의 위치에서 사용되는 비공개 헬퍼 메서드나 사소한 연산자에는 유익할 수 있습니다. 마이크로 벤치마크가 해당 속성을 정당화해야 합니다.
잠재적인 breakage가 있습니까?
가능하다면 호환성 저해 변경은 피해야 합니다. RFC 1105는 호환성 저해 변경을 구성하는 요소에 대한 기초를 마련합니다. 호환성 저해는 실제 영향에 따라 허용 가능한지 여부가 판단될 수 있으며, 이는 crater 실행으로 근사할 수 있습니다.
영향의 정도에 따라 호환성 저해를 완화하는 전략들이 존재합니다.
가치가 높고 영향도 역시 높은 변경의 경우:
- 컴파일러 린트를 사용하여 문제가 있는 동작을 단계적으로 제거하는 것입니다.
영향이 그다지 크지 않다면:
- 문제가 되는 크레이트의 메인테이너에게 연락하고 이를 수정하는 풀 리퀘스트를 제출하는 것입니다.
동작이 변경되었습니까?
호환성 저해 변경은 컴파일 실패에만 국한되지 않습니다. stable 함수의 동작 변경은 일반적으로 받아들여질 수 없습니다. 예시로 home_dir 이슈를 참고하십시오.
stable 트레이트에 대한 새로운 impl이 있습니까?
표준 라이브러리에 대한 많은 풀 리퀘스트는 이미 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는 인자가 직접 타입 검사를 통과하지 못할 경우 유효한 트레이트 impl을 찾기 위해 deref 강제 변환을 사용합니다. 이는 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>을 기반으로 구현되며, 역참조 강제 변환을 통해 str의 메서드를 사용할 수 있습니다. Vec<T>는 역참조 강제 변환을 통해 [T]의 메서드를 사용할 수 있습니다. 가능한 경우, String과 같은 래핑 타입의 메서드는 기저 저장소나 역참조 대상에 이미 존재하는 메서드에 위임해야 합니다.
#[fundamental] 항목이 관련되어 있습니까?
#[fundamental] 타입은 코히런스 규칙이 다르기 때문에 블랭킷 트레이트 구현을 추가할 수 없습니다. 자세한 내용은 RFC 1023을 참조하십시오. 여기에는 다음이 포함됩니다:
&T&mut TBox<T>Pin<T>
특수화가 관련되어 있습니까?
특수화는 현재 불안정합니다. 진행 상황은 여기에서 추적할 수 있습니다.
저희는 특수화에 지나치게 의존하지 않도록 노력하며, 그 사용을 특정 구현의 최적화로 제한합니다. 이러한 특수화된 최적화는 공개 메서드 자체를 특수화하는 대신, 비공개 트레이트를 사용해 올바른 구현을 찾습니다. 외부 호출자에 대한 메서드 디스패치 방식을 바꾸는 특수화의 사용은 신중히 검토되어야 합니다.
표준 라이브러리에서 특수화(specialization)를 사용하는 방법의 예로, &[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) }
}
}
}
안타깝게도 이 두 구현은 서로 겹치기 때문에 일반적으로는 동시에 가질 수 없었습니다. 이때 비공개 특수화를 사용해 내부적으로 올바른 구현을 선택할 수 있습니다. 이 경우, 구현을 전환하는 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) 것으로 알려져 있습니다.
공개 열거형이 있습니까?
새로운 배리언트가 도입될 가능성이 있다면, 공개 열거형은 #[non_exhaustive] 속성을 가져야 하며, 이를 통해 호환성을 깨지 않고 배리언트를 추가할 수 있습니다.
이 변경이 드롭 순서에 영향을 줍니까?
컬렉션 내부 구조의 변경은 항목이 드롭되는 순서에 영향을 줄 수 있습니다. 이는 과거에도 용인된 바 있지만, 명시해 두어야 합니다.
수동으로 구현된 Drop이 있습니까?
Drop을 수동으로 구현하는 제네릭 Type<T>는 T에 #[may_dangle] 속성이 적절한지 검토해야 합니다. 노미콘(Nomicon)에 #[may_dangle]이 무엇인지에 대한 자세한 내용이 나와 있습니다.
만약 제네릭 Type<T>가 T를 드롭하는 것도 포함할 수 있는 수동 드롭 구현을 가지고 있다면, dropck는 이를 알아야 합니다. Type<T>가 T를 소유하는 방식이 ManuallyDrop<T>, *mut T, MaybeUninit<T>처럼 T 자체를 드롭하지 않는 타입으로 표현되어 있다면, Type<T>는 T가 드롭될 수 있음을 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에 대해 건전성 구멍을 열어, 자신의 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으로 할 수 있습니다) 소멸자가 실행될 것을 보장하지 않으므로, 코드는 안전성 유지를 위해 소멸자에 의존하는 것을 피해야 합니다. 기억하십시오, 모두가 실수를 합니다.
값이 누수될 때 소멸자를 실행하지 않아도 괜찮은 이유는 그 저장소가 해제되거나 재사용되지 않기 때문입니다. 저장소가 초기화되어 있고 해제되거나 재사용되고 있다면 소멸자가 먼저 실행되어야 합니다. 왜냐하면 메모리가 고정(pinned)되어 있을 수 있기 때문입니다. 그렇긴 하지만, 고정이 절대 관여하지 않는다는 것을 보장할 수 있다면 해제 시 소멸자를 건너뛰는 예외가 여전히 있을 수 있습니다.
성능에는 어떤 영향이 있습니까?
핫 코드에 대한 변경은 사용자 측 성능에 좋든 나쁘든 영향을 미칠 수 있습니다. 적절한 벤치마크는 성능 특성이 어떻게 변하는지에 대한 아이디어를 제공해야 합니다. rustc 자체에 영향을 미치는 변경의 경우, rust-timer 실행도 할 수 있습니다.
커밋 로그가 깔끔합니까?
PR에는 병합 커밋이 있어서는 안 됩니다. 기본 브랜치와 어긋나 오래되면 리베이스가 필요합니다.
PR 병합하기
rust-lang/rust로의 PR은 GitHub UI를 사용하거나 원격 브랜치를 푸시하여 수동으로 병합되지 않습니다. 모든 것은 bors를 거칩니다.
rollup은 언제 하는가
라이브러리 PR의 경우, 특히 새로운 불안정 추가일 뿐이거나 문서만 다루는 경우라면 롤업해도 대체로 괜찮습니다.
언제 롤업해야 하는지에 대한 자세한 내용은 rollup guidelines를 참고하십시오. 그 취지는 여러 PR을 개별적으로 병합하기보다는 함께 모아서 한 번에 병합하려는 것입니다. 이렇게 하면 병합을 더 빠르게 할 수 있지만, 충돌 가능성이 높거나 롤업 시 가려질 수 있는 성능 특성을 지닌 일부 풀 리퀘스트에는 적합하지 않을 수 있습니다.
새로운 공개 아이템이 있을 때
기능이 새로운 것이라면 이를 위한 추적 이슈를 열어야 합니다. 그곳에 무엇을 넣어야 할지 감을 잡으려면 이전 추적 이슈들을 살펴보십시오. #[unstable] 속성의 issue 필드는 추적 이슈 번호로 갱신되어야 합니다.
불안정 기능은 준비가 되면 bors를 통해 평소대로 병합될 수 있습니다.
새로운 트레이트 구현이 있는 경우
stable 트레이트에 대한 트레이트 impl을 unstable로 만들 방법은 없으므로, 이미 stable인 트레이트에 새로운 impl을 추가하는 모든 풀 리퀘스트는 병합 전 반드시 FCP를 거쳐야 합니다. 다만 트레이트 자체가 unstable이라면, impl도 unstable이어야 합니다.
기능이 안정화되는 경우
기능은 #[unstable] 속성을 #[stable] 속성으로 교체하는 PR을 통해 안정화될 수 있습니다. 안정화 전에 해당 기능은 승인된 RFC를 갖추어야 합니다. 이들 또한 병합 전 FCP를 거쳐야 합니다.
Forge를 확인하면 #[stable] 속성에 사용할 올바른 버전을 알 수 있습니다.
const 함수가 안정화되는 경우
const 함수는 #[rustc_const_unstable] 속성을 #[rustc_const_stable] 속성으로 교체하는 PR을 통해 안정화될 수 있습니다. Constant Evaluation WG에 해당 const성이 우리가 확정하고자 하는 것인지에 대한 의견을 요청해야 합니다. const로 안정화되는 것이 노출되는 intrinsic이라면 @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레이블을 추가해야 합니다. 그렇지 않으면 지명 레이블을 제거해야 합니다. -
가끔 누군가 beta 롤업 PR을 만듭니다. 이는 주로 릴리스 팀이 수행하지만, 누구나 할 수 있습니다. 여기서의 과정은 다음과 같습니다.
-
beta브랜치에서 로컬 브랜치를 생성하십시오. -
beta-nominated와beta-accepted레이블을 모두 가진 모든 PR을 체리픽하십시오. 막판 변경 사항이 있거나 전체 CI 테스트 실행 시 실패할 경우를 대비해, 아직 병합되지 않은 PR은 포함하지 않는 것이 보통 선호됩니다. -
./x.py run replace-version-placeholder를 실행하고, 변경 사항이 있다면 새 커밋에 담으십시오. -
(권장) 로컬에서 몇 가지 테스트를 실행하십시오. 백포트가 깔끔하게 적용되지 않거나, 출력 결과에 차이가 있어 UI 테스트를 다시 승인해야 하는 경우가 드물지 않습니다.
-
리뷰어가 특별함을 알아볼 수 있도록
[beta]로 시작하는 제목으로 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 백포트가 포인트(.patch) 릴리스를 정당화하는지(예: 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로 갈 것임을 표시해야 합니다. -
다음으로 Cargo 리뷰어가 PR에
@bors: r+를 남기고 큐에 넣습니다. 결국 bors가 (테스트가 통과하면) PR을 해당 Cargo 브랜치에 자동으로 병합합니다. -
마지막으로
rust-lang/rust저장소의beta브랜치에 PR을 보내 Cargo 서브모듈을 업데이트합니다. 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“라는 제목으로 새 이슈가 자동으로 생성됩니다. 이는 해당 릴리스 노트를 위한 추적 이슈입니다. 이 이슈에는 relnotes + relnotes-tracking-issue 라벨이 붙습니다.
해당 영역을 담당하는 Rust 팀 및/또는 풀 리퀘스트를 작성한 기여자는 이 이슈를 사용하여 릴리스 노트 텍스트를 제안할 수 있습니다(제안해야 합니다). 이 이슈에는 릴리스 노트 내용(RELNOTES.md에 들어갈 내용)과 블로그용 코드 블록이 포함되어 있습니다.
이상적으로는 코드 블록에 텍스트를 직접 편집해 넣지만, 권한이 없는 경우 스레드에서 논의가 이루어질 수 있으며 이후 릴리스 팀이 이를 반영합니다.
릴리스 노트 텍스트는 이후 단계에서 자동으로 가져오며, 가능하면 this list의 헤더를 사용해야 합니다:
- 호환성 참고 사항
- 라이브러리
- 안정화된 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)
다음 사항에 유의하십시오:
- 이것은 풀 리퀘스트 링크가 아니라 표준 라이브러리 문서로 직접 연결됩니다.
- 이 링크는 stable 문서를 가리키므로, 작성 시점에는 실제로 작동하지 않을 수 있습니다.
- API가 직접 명시됩니다. 텍스트가 너무 많아지지 않도록 때때로 API를 압축합니다(예: 부호 없는 정수에 대해
uN). - 링크 프래그먼트는 길고 예측하기 어려울 수 있으므로, 직접 작성하기보다는 URL을 복사해 붙여넣는 것이 더 나은 경우가 많습니다(해당 항목이 stable 문서에 나타나지 않는다면 nightly에서 복사한 뒤 URL을 편집하면 됩니다).
- 안정화되는 대상이 (해당 트레이트가 아니라) 트레이트 구현뿐인 경우, 모든 impl을 해당 impl 블록 링크와 함께 나열합니다.
릴리스 팀: 3단계: 릴리스 노트가 필요한 모든 이슈/풀 리퀘스트에 relnotes 레이블이 붙었는지 확인
이 단계는 beta 기간의 처음 3주 이내에 이루어져야 합니다(더 이를수록 좋습니다). 이는 더 넓은 Rust 프로젝트의 도움을 받아 수행할 수도 있습니다.
rust-lang/rust 풀 리퀘스트에 대해 GitHub에서 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 풀 리퀘스트 게시
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 풀 리퀘스트와 릴리스 블로그 게시물을 작성할 때는 다음을 통해 이 알림 그룹에 핑을 보내 주십시오
@rustbot ping relnotes-interest-group
전체: 6단계: relnotes PR의 수정 사항 반영
일반적으로 PR에는 오타 수정, 대체 문구 제안 등 수십 건에 달하는 많은 댓글이 달립니다. 좋은 전략은 이슈/PR의 원래 이슈를 업데이트하려고 시도하는 것입니다(또는 새로 작성하고 업데이트하는 것입니다), 이는 본질적으로 4단계에서 로컬로 이미 수행한 반복 작업과 일치합니다. 상태가 이슈에 오래 남아 있을수록 그 이슈로부터의 업데이트를 알아차리고 PR에 반영하기가 더 쉬워집니다(도구를 다시 실행하기만 하면 됩니다).
이슈에 편집 내용을 추가하면 적절한 사람들(예: 풀 리퀘스트 작성자/리뷰어)이 논의 중인 내용을 파악하는 데 도움이 됩니다.
릴리스 팀: 7단계: 추적 이슈 닫기
어느 시점에서, 릴리스 팀 담당자는 해당 풀 리퀘스트를 공식으로 선언하고 현재 마일스톤과 연관된 모든 relnotes 추적 이슈를 닫아야 합니다(검색 예시). GitHub UI에서 이를 수행하는 것이 가장 쉽습니다. 이 작업은 GitHub UI에서 하는 것이 가장 쉽습니다.
FIXME: 이상적으로는 이들이 모두 relnotes PR에서 링크되어야 서로 오가기가 더 쉬울 것입니다.
블로그 포스트 절차
릴리스 노트 풀 리퀘스트를 게시한 후, 릴리스 팀의 다음 회의에서는 원하는 블로그 게시물 주제를 논의합니다. 일반적으로 이는 상당히 비공식적인 논의이며 반드시 최종 결정은 아닙니다. 그 목적은 더 많은 관점을 제공함으로써 블로그 포스트 작성자의 선택에 참고할 의견을 제공하는 것입니다.
그런 다음 블로그 포스트 작성자는 가능한 한 빨리 블로그 포스트 PR을 게시하는 것을 목표로 하며(일반적으로 릴리스로부터 3~7일 전이지만 더 짧게 진행된 경우도 있었습니다), 이후 검토와 편집을 거쳐 마침내 릴리스 당일에 병합됩니다.
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로 처리합니다(직접 승인).
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 브랜치를 대상으로 rust-lang/rust에 다음 변경 사항을 담은 PR을 보내십시오:
-
릴리스 노트를 최신 사본으로 업데이트하십시오:
-
릴리스 노트 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을 가능한 한 빨리 병합해야 한다는 점에 유의하십시오. 다른 PR이 bors에 의해 테스트 중이고 CI가 곧 끝나지 않을 것 같다면(이 부분은 판단이 필요합니다), 해당 PR로 이동하여 다음 댓글을 입력함으로써 stable 릴리스 PR에 우선순위를 “양보“할 수 있습니다:
@bors yield
beta PR
새 beta 브랜치를 대상으로 rust-lang/rust에 다음 변경 사항을 담은 PR을 보내십시오:
-
이 명령을 실행하고 그 출력만을 담은 별도의 커밋을 생성하십시오:
./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를 릴리스 날짜(목요일)로 교체해야 합니다.
기본 브랜치 bootstrap 업데이트 및 Crater (화요일)
이 단계는 새 beta가 릴리스된 이후에만 수행할 수 있습니다. beta의 릴리스 프로세스는 매일 UTC 00:00에 자동으로 진행되므로, beta PR이 그 이후에 병합되었다면 하루 더 기다려야 합니다. rustup으로 설치해 봄으로써 beta가 릴리스되었는지 확인할 수 있습니다.
기본 브랜치에 다음 내용으로 PR을 보내십시오:
-
방금 병합된 beta 브랜치 PR에서
replace-version-placeholder를 실행한 커밋을 체리픽하십시오. 이 도구를 다시 실행하지 마십시오. 기본 브랜치에는 분기된 beta에 포함되지 않은 다른 안정화 작업들이 있을 수 있으며, 이는 현재 릴리스에 귀속되지 않을 수 있기 때문입니다. -
어제 만든 beta로 bootstrap 컴파일러를 업데이트하려면 다음을 실행하십시오:
./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]로 교체하십시오.
PR이
cfg(bootstrap)을 추가하고 beta PR과 기본 브랜치 bootstrap 업데이트 사이에 병합된 경우,rg호출 결과에 해당 항목들이 나타나더라도 제거할 필요는 없다는 점에 유의하십시오. 이를 처리하는 가장 쉬운 방법은 어차피 변경한 다음 CI가 실패를 보여주도록 두는 것입니다. -
코드베이스에 영향을 미치는 새로운 경고나 Clippy 린트가 없는지 확인하십시오:
./x clippy ci
새 beta가 나온 이 시점에 다음 릴리스 주기를 위한 crater 실행을 시작해 두는 것도 좋은 방법입니다. 이는 다음 주기의 일환으로 실행이 시작되기까지 걸릴 시간에 따라, 결과가 트리아지되기까지의 지연을 며칠에서 몇 주까지 줄일 수 있습니다.
이는 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을 업데이트하는 풀 리퀘스트를 beta 브랜치로 보내십시오:
./x run src/tools/bump-stage0
부록: stable 사전 릴리스 재빌드
문제가 발생하여 stable 아티팩트를 재빌드해야 하는 경우, rust-lang/rust 저장소의 stable 브랜치에 풀 리퀘스트를 병합하십시오. 커밋이 병합되면 AWS로 인증한 후 rust-lang/release-team 저장소에서 다음 명령을 실행하십시오:
./scripts/start-release.py publish-rust-dev-stable-rebuild
이미 게시한 사전 릴리스 공지도 블로그와 internals에서 새 정보로 업데이트해야 합니다.
-
릴리스 게시에는 인증이 필요하며, 릴리스 팀의 승인된 구성원만 이를 실행할 수 있습니다. 처음 실행할 때 이 명령은 환경을 설정하는 방법과 AWS로 인증하는 방법을 안내할 것입니다. ↩ ↩2 ↩3
롤업 절차
배경
Rust 프로젝트에는 모든 풀 리퀘스트가 기본 브랜치에 푸시되기 전에 병합 후 테스트를 거쳐야 한다는 정책이 있습니다. PR 물량이 늘어남에 따라 이는 확장성이 떨어질 수 있으며, 특히 현재 Rust의 CI 소요 시간(약 3.5시간)이 긴 것을 감안하면 더욱 그렇습니다.
롤업을 소개합니다! 규모가 작거나, 성능에 민감하지 않거나, 플랫폼에 종속되지 않는 변경 사항에는 bors에 대한 rollup 명령을 표시합니다(@bors r+ rollup은 PR을 승인하고 롤업으로 표시, @bors rollup은 이미 승인된 PR을 표시, @bors rollup-은 롤업 표시를 해제). ’롤업 수행’이란 이러한 변경 사항들을 하나의 PR로 모아 한 번에 병합하는 것을 의미합니다. 롤업 명령은 always, maybe, iffy, never의 네 가지 값을 받습니다. 이러한 각 상태가 무엇을 의미하는지에 대한 안내는 리뷰 정책의 [롤업 섹션]을 참고하십시오.
Rust의 Bors queue에서 롤업 PR 목록을 볼 수 있으며, 이들은 ‘approved’ 큐의 맨 아래에 우선순위 ’rollup’으로 나열됩니다. 이는 큐에서 앞에 있는 모든 항목이 병합될 때까지 단독으로는 병합되지 않는다는 것을 의미합니다.
롤업 만들기
-
Bors queue의 인터페이스를 사용하여 풀 리퀘스트를 선택한 다음 “Create rollup” 버튼을 사용해 롤업 풀 리퀘스트를 만드십시오. (공정성에 관한 텍스트는 무시해도 됩니다.) 중요 참고 사항: the Rollups section의 리뷰 정책에 따라
rollup=always,rollup=maybe,rollup=iffy로 표시된 PR들을 추가 대상으로 고려하십시오. 특히rollup=maybe와rollup=iffyPR에 대해서는 무엇을 포함할지 결정할 때 더욱 신중해야 합니다. 회귀(버그 또는 성능 저하)의 위험을 감수하고 이를 겪는 일을 최대한 피하려고 노력해야 합니다. 또한 기여자들이 마땅히rollup=never로 표시해야 할 때도 종종 그렇게 표시하는 것을 잊어버린다는 점을 고려하십시오. 따라서 풀 리퀘스트에 롤업 관련 태그가 명시적으로 붙어 있지 않을 때는 더욱 주의를 기울여야 합니다. -
풀 리퀘스트 스레드에서 다음 명령을 실행하십시오:
@bors r+ p=5 -
롤업이 실패하면 rust-log-analyzer가 제공하는 로그를 사용해 특정 PR로 실패 원인을 이분 탐색하고,
@bors r-로 승인을 취소하십시오. 그런 다음 롤업 PR을 닫고, 문제가 된 PR을 제외한 채 **1.**부터 다시 시작하여 재생성하십시오. 롤업 PR 본문에는 (r-된 PR을 제외한) 기존 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을 너무 많이 넣는 것은 피해야 합니다. 또한 큰 성능 영향이 있을 것으로 예상되는 풀 리퀘스트는 포함을 피하고, rollup=never로 표시하십시오.
이상적으로는 롤업이 성공하기를 바라기 때문에 iffy PR을 아예 포함하지 않고 싶은 유혹이 듭니다. 하지만 PR 큐가 하는 일은 PR을 테스트하는 것이지, 병합하는 것이 아니라는 점을 기억해 둘 필요가 있습니다. 따라서 iffy PR로 인해 롤업이 실패하는 것은 오히려 좋은 일인데, 그 PR은 어차피 언젠가는 테스트를 거쳐야 하고, 롤업에 포함되지 않았더라도 테스트하는 데 동일한 시간이 걸렸을 것이기 때문입니다. iffy PR에 관해 롤업을 바라보는 한 가지 방법은, 롤업이란 다른 여러 PR들이 어차피 iffy PR에 필요한 CI 주기에 편승하는 방법이라는 것입니다. 롤업이 iffy 풀 리퀘스트를 완전히 배제한다면, 결국 이러한 풀 리퀘스트가 큐에서 오랫동안 방치되는 결과가 생기는데, 이는 바람직하지 않습니다.
마찬가지로, never 풀 리퀘스트도 기회를 얻을 수 있도록 여유 CI 사이클을 남겨두어야 합니다! 롤업을 만드는 사람이 자신뿐이라면 큐를 주시하지 않는 시간대에 실행되도록 두는 것이 좋지만, 요즘은 여러 시간대에 걸쳐 롤업 작성자가 있으므로, 큐의 상대적 크기를 주시하면서 never PR을 위해, 특히 그것들이 쌓이고 있다면, 몇 번의 CI 주기를 따로 마련해 두는 것이 대개 최선입니다.
롤업에 있어 공정성을 유지하도록 노력하십시오: 롤업은 순서를 앞당기는 방법입니다. rollup=maybe 풀 리퀘스트의 경우, (섹션 맨 위에 있는) 가장 오래된 것을 포함하도록 노력하여, 더 새로운 풀 리퀘스트가 더 오래된 풀 리퀘스트를 완전히 앞지르지 않도록 하십시오. 롤업에 포함된 PR보다 오래된 모든 PR을 포함할 필요는 없지만, 가장 오래된 것은 포함하도록 노력하십시오. iffy에 관한 관점과 비슷하게, 롤업을 큐에서 가장 오래된 PR의 CI 주기에 다른 PR들이 편승하는 방법으로 바라보는 것이 유용합니다.
실패한 롤업
롤업이 실패한 경우, 실패가 우발적인 것이었다면(예: 네트워크 문제나 타임아웃으로 인한 것) @bors retry 명령을 실행하십시오. 우발적인 것이 아니었다면 문제가 된 PR을 찾아내어, rust-logs-analyzer 댓글 링크를 복사하고 Failed in <link_to_comment>, @bors r-라고 작성하여 큐에서 제외하십시오. 바라건대, 작성자나 리뷰어가 PR을 수정하기 위한 피드백을 주거나 문제가 없음을 확인해 줄 것입니다. 실패한 롤업 PR은 닫아도 됩니다.
문제가 되는 PR을 제거한 후에는 그것을 제외하고 롤업을 다시 생성하십시오(1번 참조). 그러나 때로는 문제가 된 풀 리퀘스트를 찾기 어려운 경우가 있습니다. 그런 경우에는 문제가 있다고 의심되는 PR을 직관적으로 피하여 롤업을 다시 생성하십시오. 또 다른 전략은 의심되는 PR들의 우선순위를 높이고 rollup=never(또는 iffy)로 표시하여 bors가 이를 단독으로 테스트하게 함으로써 가설을 기각하거나 확인하는 것입니다.
롤업이 계속 실패하는 경우 @bors rollup=never 명령을 실행하여 해당 PR을 롤업에 포함되지 않도록 할 수 있습니다.
트리아지 절차
풀 리퀘스트 트리아지
상태 태그
-
S-waiting-on-author - 작성자가 리뷰어 의견을 반영하기 위해 변경을 가해야 하거나, 병합 충돌·테스트 실패가 존재하는 상태입니다. 이는 PR이 다른 PR에 의해 막혀 있는 경우(보통 S-blocked 레이블이 함께 붙습니다)나 crater 실행을 기다리는 경우 등 더 모호한 사례도 포함합니다 – PR을 진전시키는 것은 작성자의 책임입니다.
진행 중인(work-in-progress) 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 레이블을 제거하려면 다음 조건들이 충족/고려되어야 합니다. 이 조건들이 항상 모두 고려되지 않아도 괜찮습니다. 이 목록은 완전한 것이 아니며, 엄격한 체크리스트가 아니라 지침으로 다루십시오:
- 이슈는 말이 되어야 합니다. 즉, 문제를 제시해야 합니다.
- 이 이슈가 이전에 보고된 이슈의 중복인지 확인하십시오.
- 중복이 확실하다면, 이 이슈를 이전 이슈의 중복으로 처리하여 닫으십시오. 이전 이슈의 백링크에서 이 사실이 명확히 드러나도록 하거나, 중복 이슈에 명시적으로 링크하십시오.
- 확신이 서지 않는다면, 다른 이슈가 중복이거나 유사하거나 관련이 있을 가능성을 나타내는 댓글만 남겨도 됩니다.
- 적절한 라벨을 추가하십시오 (Labels).
- 특히 팀 라벨(
T-*)과 이슈 카테고리 라벨(C-*)이 가장 중요합니다. 팀원들은 보통 이를 기준으로 삼아 (예: 트리아지 회의 중) 자신의 팀과 관련된 것을 걸러내기 때문입니다.
- 특히 팀 라벨(
- 이슈에 재현 방법이 없지만 필요한 경우(의심스러우면 필요한 것입니다), 재현 방법을 요청하고
S-needs-repro레이블을 추가하십시오. - 이슈에 정보가 부족한 경우(예: 시스템 정보, 정확한 호출 방식), 문제를 재현하는 데 중요한 정보를 제공하도록 보고자에게 요청하십시오.
S-needs-info레이블을 추가하십시오. - 이슈 트래커는 일부 종류의 기능 요청에는 적절하지 않은 장소입니다. 작성자를 적절한 채널로 안내하십시오:
- 이슈가 회귀 지점을 이분 탐색(bisect)함으로써 도움이 될 수 있다면(의심스러우면 도움이 될 수 있는 것입니다),
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: 진단 이슈 템플릿으로 생성된 이슈는C-bug가 아니라A-diagnostics만 가집니다.
B-*: 차단 요소(blocker)인 이슈입니다.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: 컴파일러 또는 생성된 코드가 SIGSEGV나 접근 위반(access violation)처럼, ICE로는 드러나지 않는 방식으로 충돌하는 경우입니다.I-unsound: 라이브러리, 컴파일러, 타입 시스템 또는 언어의 불건전성(unsoundness)입니다.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: 이슈에rust-lang/rust의tests/crashes테스트 스위트나 다른 곳에 알려진 버그 테스트가 있습니다.S-has-bisection: 이분 탐색이 수행되었고 그 결과가 설득력이 있습니다.S-waiting-on-LLVM: 업스트림 LLVM 풀 리퀘스트나 수정을 기다리는 중입니다.S-tracking-forever: 추적 이슈를 종료할 의도가 전혀 없습니다.
beta-nominated: [beta로 백포트](backported to beta)하도록 지명된 변경 사항을 추적합니다.beta-accepted: [beta로 백포트](backported to beta)하도록 승인된 변경 사항을 추적합니다. 백포트는 보통T-release가 처리합니다.stable-nominated: 포인트 릴리스를 예상하여 [stable로 백포트](backported to stable)하도록 지명된 변경 사항을 추적합니다.stable-accepted: [stable로 백포트](backported to stable)하도록 승인된 변경 사항을 추적합니다. 백포트는 보통T-release가 처리합니다.relnotes: 다음 릴리스의 릴리스 노트에 문서화하도록 제안된 변경 사항입니다- 이는 triagebot이 새로운 relnotes 이슈를 생성하게 만듭니다(예시)
- 또한 relnotes 이슈를 표시하여 T-release relnotes 도구가 처리할 수 있도록 합니다.
- FCP도 이슈에서 시작될 경우 relnotes 이슈 생성을 유발합니다.
metabug: 다른 버그들을 추적합니다.
팀 예시
이 섹션에서는 특정 팀에 배정되어야 하는 이슈 종류의 예시 목록을 제공합니다.
T-compiler
진단이나 ICE 등 컴파일러 구현과 관련된 모든 것
T-libs
- 라이브러리 함수의 구현 세부 사항 변경
- 라이브러리 문서의 철자, 문법, 구성상의 변경
- 함수 및 메서드와 같은 새로운 라이브러리 API
- 불안정 함수 시그니처 변경
- 특정 상황에서 함수가 특정 에러 코드를 생성함을 보장하는 것과 같이, 라이브러리 문서의 의미론적 변경
- 새로운 트레이트 구현 등 라이브러리 변경으로 인한 타입 추론 손상
T-lang
- 새로운 키워드/언어 기능
- 표준 라이브러리의 키워드 문서 변경 사항
T-spec
- reference에 대한 변경
T-opsem
- nomicon에 대한 변경
- 원자적 연산의 의미론과 같은, 추상 기계의 의미론에 대한 변경
- 안전하지 않은(unsafe) 포인터 함수 문서의 변경
core::ptr문서의 변경
레이블 생성
Triagebot는 @rustbot label: xxx 사용법이 마침표나 공백으로 끝나는 경우(인라인 호출로서)를 지원해야 하므로, 레이블 이름은 영숫자, 하이픈(-), 밑줄(_) 문자로만 구성되어야 합니다.
- 기존 레이블을 확인하여 중복되지 않도록 하십시오.
- 새 레이블이 관례에 어긋나거나 논란의 여지가 있을 수 있다면 https://rust-lang.zulipchat.com/#narrow/channel/242269-t-release.2Ftriage/topic/New.20labels에서 논의하십시오. 다른 사람들에게 참고가 되도록 새 레이블에 대한 코멘트를 남기십시오.
레이블 별칭
*별칭(aliases)*을 사용하면 한 번에 여러 레이블을 추가하거나 제거할 수 있습니다. 별칭에 대해 더 자세히 알아보려면 관련 문서를 방문하십시오.
Relnotes 이슈
릴리스 노트 이슈는 현재 기본적으로 needs-triage가 붙어서 생성됩니다. relnotes 트리아지는 보통 충분한 맥락이 있을 때 가장 잘 수행할 수 있습니다. 그렇지 않다면 그대로 두십시오.
- 실제 사용자 대상 relnotes와는 관계없이, 실제 PR과만 관련된 무관한 영역
A-*레이블을 제거하십시오. - 무관한
T-*또는WG-*레이블을 제거하십시오. - PR 작성자, 리뷰어 또는 관련 팀 구성원이 relnotes 문구를 확인했거나 변경했다면
needs-triage를 제거하십시오.
내부 컴파일러 오류(ICE) 이슈 트리아지
내부 컴파일러 오류(ICE)를 포함하는 이슈의 경우:
- 이슈에
I-ICE,T-compiler,C-bug태그가 붙어 있는지 확인하십시오. - 해당 이슈가 실제로 ICE인지, 아니면
I-crash나I-hang으로 더 정확히 설명되는지 확인하십시오. - rust의 이전(최신 stable 정도) 버전이라면 다음을 물어보십시오:
- ICE가 beta와 최신 nightly에서 재현되는지 여부.
- 더 이전 stable이라면, ICE가 최신 stable에서 재현되는지 물어보십시오.
- 중복 여부를 확인하되, 동일한 근본 이슈를 나타낸다고 확신하지 않는 한 중복으로 닫지 마십시오. 가능한 관련/중복 항목으로 단순히 해당 이슈에 링크를 남기는 편이 좋습니다.
- ICE 메시지를 검색하는 것이 좋은 방법입니다. 동일한 ICE 메시지라도 근본 원인이 다를 수 있으므로 중복으로 닫을 때 주의하십시오.
- 재현 방법이 없다면, 재현 방법을 요청하는 댓글을 남기고
S-needs-repro를 추가하십시오.- 약 한 달 동안 재현 방법이 제공되지 않으면 일반적으로 닫아야 합니다.
- 재현 방법이 최소화되어 있지 않다면
E-needs-mcve를 추가하거나 직접 MCVE를 작성하십시오. MCVE 절차에 대한 자세한 내용은 “Initial triaging” 섹션을 참고하십시오. - 이슈를 유발하는 코드(백트레이스를 확인하십시오!)와 재현 사례의 성격에 따라
A-*라벨을 추가하십시오(예: 재현 사례가 이상한 트레이트 구현이거나 백트레이스가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를 사용하여) 이슈를 비섹션하는 것이 있습니다.
- 이분 탐색 결과를 제공하실 때는 이분 탐색 상태 레이블을 조정하십시오:
@rustbot label -E-needs-bisection +S-has-bisection. - MCVE를 제공하실 때는 MCVE 상태 레이블을 조정하십시오:
@rustbot label -E-needs-mcve +S-has-mcve.
-
O-*레이블의O는 원래 *운영체제(OS)*를 의미했습니다. ↩ -
I-*레이블의I는 원래 *중요도(importance)*를 의미했습니다. 이는I-*-nominated레이블에 가장 잘 들어맞습니다. 그러나 대부분의I-*레이블에서는I를 *이슈(종류)*로 해석하는 것이 타당합니다. ↩ -
E-*레이블의E는 *경험(experience)*을 의미합니다. ↩
Crater 실행 트리아지
Crater 실행하기
저희는 정기적으로 Crater 실행을 수행하며, 이 문서는 beta 실행을 트리아지하는 절차를 기술합니다. 약간의 수정을 거치면 릴리스 팀이 아닌 실행(예: PR crater 실행)에도 적용할 수 있습니다.
먼저 “Crater runs for 1.x“라는 제목의 새 이슈를 등록하십시오 (예시)
beta용 crater 실행은 beta가 나오는 즉시 시작해야 합니다. 다음 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 실행의 트리아지를 진행해야 합니다. 정상적인 rustc 실행과 중복되는 rustdoc 및 --release 실행의 실패는 무시하십시오.
일반적으로 상당히 많은 리그레션이 발생합니다 – 필요한 작업량을 줄이는 데 도움이 되는 도구가 몇 가지 있습니다. 어느 것이 더 도움이 되는지는 대체로 개인 취향의 문제입니다.
- https://github.com/Mark-Simulacrum/crater-generate-report/
- 이것은 Cargo가 출력하는 compilation failed 메시지를 찾기 위해 로그를 파싱하여 회귀를 ’근본 원인’별로 그룹화합니다
- https://github.com/Centril/crater-cat-errors
- 이 도구도 마찬가지로 로그를 파싱하여 “에러” 메시지별로 리그레션을 그룹화합니다.
도구를 직접 작성하셨다면 언제든지 여기에 추가해 주십시오! 이를 위한 최선의 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의 minimization guide가 적용하기에 훌륭한 도구입니다.
에디션 릴리스
이 문서는 에디션 릴리스를 관리하는 방법에 대한 개요를 제공합니다. 이 문서는 독자가 에디션이 무엇인지 이미 알고 있다고 가정합니다([에디션 가이드]를 참고하십시오).
에디션 프로젝트 그룹
RFC 3501은 리더십 위원회가 에디션 관리를 책임지는 프로젝트 그룹을 구성할 책임이 있다고 규정합니다.
프로젝트 그룹 리드
비교적 촉박한 일정 내에 필요한 조정과 신속한 결정 및 조치를 더 쉽게 하기 위해, 프로젝트 그룹은 2~3명의 리드를 두는 것이 권장됩니다.
또 다른 고려 사항은 헌신도와 시간적 여유입니다. 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개월
- 대부분의 이슈가 수정되었습니다.
- 문서화 완료 (에디션 가이드, 레퍼런스 등).
- 에디션 프로젝트 그룹은 에디션 안정화 여부, 또는 다음 릴리스로 연기해야 하는지에 대해 최종 진행/보류 결정을 내려야 합니다.
- 에디션은 모든 도구(rustc, cargo 등)에 대해 nightly에서 안정화됩니다.
- T-1개월
- 에디션이 beta에 도달하며, 이는 백포트를 위한 마지막 기회입니다.
- 릴리스 팀과 협력하여 릴리스 공지를 준비합니다. 예시
- T-0개월
- 에디션이 stable로 릴리스됩니다.
Crater 마이그레이션 테스트
Crater는 마이그레이션 린트 테스트를 직접 지원하지 않습니다. crater 실행을 수행하려면 크레이트를 마이그레이션하는 데 필요한 단계를 수행하는 수정된 버전의 cargo를 사용해야 합니다. #87190에 이것이 어떤 모습인지에 대한 예시가 있습니다. 이는 대략 다음 단계를 수행합니다:
- Crater는 이전 main 브랜치 빌드를 사용하여
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 단계가 실패하고 이전 main 브랜치 빌드에서는 검사가 성공했다면, 이는 마이그레이션이 실패한 회귀를 나타냅니다.
이후 에디션 프로젝트 그룹은 보고서를 분석하고, 문제에 대한 이슈를 등록하며, 해당 문제가 해결되도록 팀들과 후속 조치를 취할 책임이 있습니다.
이 과정은 문제가 수정되고 재테스트가 필요함에 따라 여러 번 반복해야 할 수도 있습니다.
이를 실행하고 보고서를 분석하는 과정은 에디션에 얼마나 많은 변경 사항이 있는지에 따라 오랜 시간이 걸릴 수 있음에 유의하십시오. 2021 에디션은 약 한 달이 걸렸으며, 수백 건의 회귀를 분석하고 근본 원인을 파악하며 수정 사항이 구현된 후 크레이터를 재실행하는 작업이 포함되었습니다. 2021 에디션은 약 한 달이 걸렸으며, 여기에는 수백 건의 회귀를 분석하고 근본 원인을 규명하며 수정 사항이 적용된 후 crater를 재실행하는 과정이 포함되었습니다.
에디션 안정화하기
에디션 팀이 안정화를 승인하면, 실제 안정화 작업은 누구든지 수행할 수 있습니다. 에디션을 안정화하는 작업에는 컴파일러, 문서, 그리고 모든 도구를 업데이트하는 것이 포함됩니다:
- rustc에서 안정화합니다. rustc 안정화 지침을 참고하십시오.
- cargo에서 안정화합니다(rustc 업데이트가 nightly에 반영된 이후). cargo 안정화 지침을 참고하십시오.
- cargo 서브모듈을 업데이트합니다.
- 에디션 가이드를 업데이트하여 안정화되었음을 표시합니다. Rust 2024 예시.
- 대기 중인 모든 에디션 풀 리퀘스트를 reference에 병합하고, 새로 안정화된 에디션이 기본값이 되도록 업데이트합니다(Rust 2024 예시).
- 모든 책 서브모듈을 업데이트합니다.
- 새 에디션을 제대로 지원하는 mdbook의 새 버전을 배포합니다. Rust 2024 예시.
블로그 게시물 및 공지
에디션 프로젝트 그룹과 관련 팀들이 모든 사람과 조기에 자주 소통할 것을 강력히 권장합니다. 주요 마일스톤은 Rust 블로그에 발표해야 합니다. Inside Rust 블로그 게시물은 전반적인 진행 상황과 일정에 대한 업데이트와 함께 정기적으로(예: 매월) 작성되어야 합니다.
일부는 공개 메시지에서, 하지만 이상적으로는 모든 곳에서 에디션이 무엇이고 어떻게 동작하는지(코드가 깨지지 않습니다! 생태계가 분열되지 않습니다!)를 반복해서 강조하십시오. 항상 혼란이 있습니다. 제가 기억하기로 지난번에는 일부 기자들이 확인을 위해 Rust 재단에 연락한 적이 있습니다. 에디션 프로젝트 그룹은 재단 팀이 관련 질문을 받게 될 것이므로 공개 메시지 작성에 있어 재단 팀과 조율해야 합니다.
추적 도구
에디션 프로젝트 그룹은 에디션의 진행 상황을 추적하는 데 어떤 도구를 사용할지 결정해야 합니다. 개별 팀들 또한 자신들에게 가장 적합한 도구를 선택하고 싶어할 가능성이 큽니다. 과거에는 GitHub 프로젝트, GitHub 이슈 라벨, Google 스프레드시트, HackMd 등 다양한 도구를 혼용해 왔습니다. 편한 것을 사용하되, 공개적으로 접근 가능해야 한다는 점만 유념하십시오.
예시:
- 2018년 추적 이슈
- 2018년 GitHub 프로젝트
- 2021년 추적 스프레드시트
- 2021년 GitHub 프로젝트
- 2021년 HackMd
- 2021년 추적 이슈
- 2024년 추적 이슈
- 2024년 HackMd
개별 팀 추적 예시:
구현 노트
개별 팀과 프로젝트는 에디션 변경 사항을 구현하는 방법에 관한 자체 자료를 가지고 있습니다. 다음은 에디션 변경 사항이 어떻게 구현되는지 알아보고자 할 때 참고할 수 있는 추가 정보 링크입니다.
- 기반 시스템이 어떻게 동작하는지 다시 살펴봐야 한다면 마이그레이션 동작 방식을 참고하십시오.
- rustc-dev-guide에는 rustc에서 에디션별 변경 사항을 구현하는 방법에 대한 정보를 담은 Editions 챕터가 있습니다.
- Rust 스타일 가이드에는 에디션 간 스타일 변경 사항을 정의하는 Rust 스타일 에디션에 대한 정보가 있습니다.
역사적 맥락
- RFC 2052는 에디션 시스템을 시작했으며, 2018 에디션의 시작을 알렸습니다.
- RFC 3085는 2021 에디션의 계획을 세웠을 뿐만 아니라, 에디션의 의미를 명확히 하고 변경했습니다.
- RFC 3501은 2024 에디션의 시작을 알렸을 뿐만 아니라, 3년 주기를 공식화하고 향후 에디션 관리를 위한 프로세스를 확립했습니다.
아카이브
이 섹션은 오래되어 더 이상 유효하지 않지만, 역사적·기록적 이유로 계속 읽을 수 있도록 보관해두고자 하는 콘텐츠를 위한 것입니다.
커뮤니티
이 섹션은 이제 활동을 중단하고 더 이상 운영되지 않는 커뮤니티 팀의 기록 보관소입니다. 아래 링크는 오래되었거나 보관된 프로젝트로 연결될 수 있습니다.
외부 링크
- Community team 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) 풀 리퀘스트의 부활 등, 그야말로 놀라운 컴파일러 작업을 해왔습니다. 컴파일러의 미해결 버그와 프로젝트 목록은 이제 점점 더 줄어들고 있습니다. 감사합니다, @petrochenkov!
2015-11-16 Peter Atashian (WindowsBunny, retep998)
WindowsBunny 본인의 말에 따르면 그는 “Windows 사용자가 마주칠 수 있는 모든 문제와 그 해결 방법을 담은 뛰뛰거리는 백과사전“입니다. Windows에서 Rust가 동작하도록 만든 영웅 중 한 명으로서, 그는 이 플랫폼에서 Rust가 할 수 있는 일의 한계를 적극적으로 넓혀가고 있습니다. 그는 또한 Windows 시스템 API에 대한 포괄적인 바인딩 모음인 winapi 계열 크레이트들의 메인테이너로도 유명합니다. 당신이 최고입니다, WindowsBunny. 또한, 나무의 친구입니다.
출처.
2015-10-31 Marcus Klaas
오늘 @nrc는 @marcusklaas를 나무의 친구로 지명하고자 합니다:
Marcus는 rustfmt의 주요 저자 중 한 명입니다. 그는 초창기부터 참여해왔으며 지금은 최다 기여자입니다. 그는 수많은 버그를 수정하고, 새로운 기능을 구현했으며, 엄청난 양의 풀 리퀘스트를 검토했고, 프로젝트의 설계에도 기여했습니다. 그의 노고가 없었다면 Rustfmt는 오늘날의 모습이 될 수 없었을 것입니다. 그는 진정한 나무의 친구입니다.
2015-10-16 Ryan Prichard
nmatsakis는 또한 Ryan Prichard를 트리의 친구로 선언하고자 합니다. 지난 몇 달 동안 Ryan은 Rust 컴파일러의 파싱 동작을, Rust 파싱을 위한 LALR(1) 문법 제작을 목표로 하는 rust-grammar 프로젝트의 파싱 동작과 비교해왔습니다. 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 사이트를 재설계하여 더 현대적이고 rustic한 느낌을 주었습니다. 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 개발의 중요한 시기에 그 진화에 영향을 주었으며, BurntSushi는 뛰어난 friendofthetree만이 만들어낼 수 있는 종류의 Rust 보석들을 계속해서 만들어내고 있습니다.
2015-03-03 Manish Goregaokar (Manishearth)
Manish는 2014년 GSoC 프로그램의 일환으로 Servo 작업을 시작했으며, 이때 XMLHttpRequest를 구현했습니다. 그 이후로 그는 대학 공부를 마치고 Rust 커뮤니티 행사를 조직하는 와중에도 Servo 팀의 핵심 구성원이 되었습니다. 2015년에는 bors의 큐에 관심을 갖게 되어 통합 과정을 가속화하기 위한 롤업 풀 리퀘스트를 만들기 시작했습니다. 풀 리퀘스트 큐를 돌보는 일은 Manish과 같은 tree의 친구, 즉 롤업의 친구를 만들어내는 시간이 많이 드는 노동입니다.
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를 포함한 여러 인기 프로젝트를 만들었습니다. 이러한 프로젝트들에서 얻은 실전 경험은 upstream rust의 여러 개선으로 이어졌으며, 그중 가장 주목할 만한 것은 TaskPool 타입의 완전한 재작성입니다. Reem은 Rust의 대의를 발전시키기 위해 할 수 있는 모든 것을 하고 있습니다.
2015-01-20 Barosl Lee (barosl)
오늘 저는 Barosl Lee(@barosl)를 나무의 친구로 추천하고자 합니다. Barosl은 최근 우리의 bors cron job을 homu라는 새 프로젝트로 다시 작성했습니다. Homu에는 다음과 같은 여러 이점이 있습니다:
- 서로 다른 풀 리퀘스트를 테스트하는 사이에 “다운타임“이 전혀 없습니다 (bors의 경우 30분 이상 걸리는 것과 비교됩니다!)
- 다른 풀 리퀘스트로부터 별도의 롤업 풀 리퀘스트를 만드는 새로운 롤업 버튼.
- 여러 저장소가 지원됩니다 (Cargo와 Rust가 같은 화면에 있습니다)
Homu는 Barosl이 여러 이슈를 해결해준 덕분에 최근 rust-lang/rust에 배포되었으며, 지금까지 아주 훌륭하게 동작하고 있습니다! Barosl은 또한 새로 발생하는 모든 이슈에 매우 신속하게 대응해왔습니다. Barosl은 진정으로 나무의 친구입니다!
2015-01-13 Kang Seonghoon (lifthrasiir, Yurume)
Seonghoon은 2013년 초부터 Rust 커뮤니티의 활발한 구성원이었으며, Rust 자체에 여러 값진 기여를 했지만, 그의 가장 큰 업적은 tree 밖에서 핵심 라이브러리를 개발한 것입니다. Cargo에서 가장 인기 있는 크레이트 중 하나인 rust-encoding은 문자 인코딩을 수행하고, rust-chrono는 날짜/시간 처리를 담당하는데, 둘 다 표준 라이브러리 기능의 중요한 공백을 채웁니다. rust-strconv는 효율적인 수치-문자열 변환의 프로토타입으로, 향후 표준 라이브러리에 포함될 후보입니다. 그는 자신의 작업에 대해 논의하는 블로그를 운영하고 있습니다.
2015-01-06 Jorge Aparicio (japaric)
Jorge Aparicio(japaric)를 Friend of the Tree로 추천합니다(이번이 무려 두 번째입니다!). japaric은 이제 사용 가능해진 새로운 언어 기능을 사용하도록 코드베이스를 이식하는 데 엄청난 작업을 해왔습니다. 첫째, 그는 DST가 도입된 이후 표준 라이브러리의 API들이 이를 최대한 활용하도록 변환했습니다. 다음으로, 언박스 클로저를 사용하도록 API를 변환했습니다. 그다음, 라이브러리의 상당 부분을 연관 타입을 사용하도록 변환했습니다. 마지막으로, 컴파일러에서 박스 클로저를 완전히 제거했습니다. 그는 또한 연산자 오버로딩과 비교 트레이트를 변경하는 RFC를, 그 정의와 표준 라이브러리에 미치는 영향까지 포함하여 반영하는 작업을 해왔습니다. 그리고 이 목록에는 오래된 문법을 폐기하는 것과 같은 여러 소소한 변경 사항은 포함되어 있지 않습니다. 알파 릴리스는 그가 없었다면 지금의 모습이 되지 못했을 것입니다. 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는 특히 혁신적이었으며, 검사된 산술 연산의 미래 대비를 위한 RFC 역시 그러했습니다. Gabor는 대단히 영리한 Friend of the Tree입니다.
2014-11-11 Brian Koropoff (unwound)
지난 몇 주 동안, 그는 컴파일러 전반에 걸쳐 매우 까다로운 ICE를 수없이 수정했는데, 특히 언박스 클로저와 빌림 검사기(borrow checker) 영역에서 그러했습니다. 그는 또한 언박스 클로저가 단형화(monomorphization)와 상호작용하는 방식을 완전히 다시 작성했으며, 이를 사용 가능하게 만드는 데 지대한 영향을 미쳤습니다. 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의 주 작성자이며, 지난주에는 프로젝트 오일러 문제에서 언어별 성능을 비교하는 “eulermark“를 발표했는데, 다행히 Rust가 꽤 좋은 성능을 보이는 것으로 나타났습니다. 벤치마킹 작업의 일환으로 그는 ‘criterion’ 벤치마킹 프레임워크를 Rust로 이식했습니다.
2014-07-29 Björn Steinbrink (dotdash, doener)
2013년 4월부터 기여하고 있습니다. Björn은 반복자(iterator), fmt, 관리형 박스(managed box)의 할당 비대화 제거, fail! 최적화, 라이브러리 내 전략적 인라이닝 추가, 컴파일러 내 자료 구조 속도 향상, 변환 과정에서의 이차 폭증 제거 및 기타 IR 비대화 문제 해결 등 Rust에 대해 많은 최적화 작업을 해왔습니다.
그는 실로 놀라운 수의 최적화를 Rust에 대해 수행했습니다.
가장 최근에는 변수의 수명(lifetime)을 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 구조체 인자 관련 이슈를 수정했습니다
- 비 US 로케일 관련 여러 이슈를 수정했습니다
2014-03-18 Clark Gaebel (cgaebel)
이번 주 트리의 친구는 Clark Gaebel입니다. 그는 방금 Rust에 대한 첫 번째 대규모 기여를 성사시켰습니다. 그는 뛰어들어 Robin Hood 해싱을 구현함으로써 우리의 해시맵을 상당히 빠르게 만들었습니다. 그는 훌륭한 트리의 친구입니다.
2014-02-25 Erick Tryzelaar (erickt)
- 2011년 5월부터 기여 중입니다
- serialization 크레이트를 작성했습니다
- 베이 지역 Rust 밋업을 조직합니다
- 방금 Hash 트레이트를 다시 작성했습니다
2014-02-11 Flavio Percoco (FlaPer87)
- 9월부터 기여 중입니다
- 이슈 트리아지를 담당합니다
- 이탈리아에서 커뮤니티 행사를 조직하고 있습니다
- ‘pow’ 함수를 최적화했습니다
- 최근 작지만 중요한 버그를 다수 수정하고 있습니다
2014-01-27 - Jeff Olson (olsonjefferey)
- 2012년 2월부터 기여 중입니다
- 최초의 libuv 통합 작업을 했습니다
- I/O에 대한 두 번째 시도를 구현했으며, 처음에는 libuv를 사용했습니다
- 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 매개변수를 전달하기 위해 environment 인자를 잘못 사용해 오던 오랜 관행을 제거했습니다
2014-01-07 - Vadim Chugunov (vadimcn)
- 6월부터 기여하고 있습니다
- Windows에서 수많은 버그를 수정했습니다
- 깨진 테스트를 수정하고 있습니다
- 최신 mingw 버전과의 호환성을 개선했습니다
- libunwind를 통한 unwinding을 구현하여 런타임 C++ 의존성을 제거했습니다
Rust 릴리스 역사
이 문서는 0.1–1.7.0 버전의 Rust 릴리스 산출물 아카이브입니다. 각 릴리스는 Rust GPG 서명 키(이전 키는 older key, 더 이전 키는 even older key)로 서명됩니다.
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 (signature)
- Linux i686 tarball (signature)
- Mac OS X x86_64 pkg (signature)
- Mac OS X i686 pkg (signature)
- Mac OS X x86_64 tarball (signature)
- Mac OS X i686 tarball (signature)
- 문서
Rust 0.x
메일링 리스트에 포함된 짧은 형식의 릴리스 외에도, 각 0.x 릴리스는 릴리스 노트에 더 긴 설명이 있습니다.
0.12.0
- 발표
- 릴리스 노트
- 소스 코드 (signature)
- Windows x86_64 설치 프로그램 (signature)
- Windows i686 설치 프로그램 (signature)
- Linux x86_64 tarball (signature)
- Linux i686 tarball (signature)
- Mac OS X x86_64 pkg (signature)
- Mac OS X i686 pkg (signature)
- Mac OS X x86_64 tarball (signature)
- Mac OS X i686 tarball (signature)
- 문서
0.11.0
- 발표
- 릴리스 노트
- 소스 코드 (signature)
- Windows 설치 프로그램 (signature)
- Linux x86_64 tarball (signature)
- Linux i686 tarball (signature)
- Mac OS X x86_64 pkg (signature)
- Mac OS X i686 pkg (signature)
- Mac OS X x86_64 tarball (signature)
- 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
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- borrowed pointers | conditions | containers | ffi | macros | rustpkg | tasks
- 매뉴얼 (PDF)
- Rustpkg 매뉴얼
- 표준 라이브러리 문서
- Extra 라이브러리 문서
0.7
0.6
0.5
0.4
0.3.1
이는 OS X 버그 수정 릴리스였습니다.
0.3
- 공지
- 릴리스 노트
- 소스 코드 (서명)
- Windows 설치 프로그램 (서명)
- 튜토리얼
- 매뉴얼 (PDF)
- 코어 라이브러리 문서
- 표준 라이브러리 문서