compliance: Allow Zippy release channel changes (#54572)

This adds support for tracking Zed Zippy channel changes with the
compliance automation.

Release Notes:

- N/A
This commit is contained in:
Finn Evers 2026-04-23 12:37:53 +02:00 committed by GitHub
parent 43d6ab5386
commit 75fe5a3c9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 338 additions and 87 deletions

View file

@ -160,7 +160,7 @@ jobs:
name: steps::bot_commit
uses: IAreKyleW00t/verified-bot-commit@126a6a11889ab05bcff72ec2403c326cd249b84c
with:
message: ${{ needs.resolve_versions.outputs.preview_branch }} preview
message: ${{ needs.resolve_versions.outputs.preview_branch }} preview for @${{ github.actor }}
ref: refs/heads/${{ needs.resolve_versions.outputs.preview_branch }}
files: crates/zed/RELEASE_CHANNEL
token: ${{ steps.generate-token.outputs.token }}
@ -206,7 +206,7 @@ jobs:
name: steps::bot_commit
uses: IAreKyleW00t/verified-bot-commit@126a6a11889ab05bcff72ec2403c326cd249b84c
with:
message: ${{ needs.resolve_versions.outputs.stable_branch }} stable
message: ${{ needs.resolve_versions.outputs.stable_branch }} stable for @${{ github.actor }}
ref: refs/heads/${{ needs.resolve_versions.outputs.stable_branch }}
files: crates/zed/RELEASE_CHANNEL
token: ${{ steps.generate-token.outputs.token }}

View file

@ -3,24 +3,23 @@ use std::{fmt, ops::Not as _, rc::Rc};
use itertools::Itertools as _;
use crate::{
git::{CommitDetails, CommitList, ZED_ZIPPY_LOGIN},
git::{AutomatedChangeKind, CommitDetails, CommitList, ZED_ZIPPY_LOGIN},
github::{
Approvable, CommitAuthor, GithubApiClient, GithubLogin, PullRequestComment,
PullRequestData, PullRequestReview, Repository, ReviewState,
Approvable, CommitAuthor, CommitFileChange, CommitMetadata, GithubApiClient, GithubLogin,
PullRequestComment, PullRequestData, PullRequestReview, Repository, ReviewState,
},
report::Report,
};
const ZED_ZIPPY_COMMENT_APPROVAL_PATTERN: &str = "@zed-zippy approve";
const ZED_ZIPPY_GROUP_APPROVAL: &str = "@zed-industries/approved";
const EXPECTED_VERSION_BUMP_LOC: u64 = 2;
#[derive(Debug)]
pub enum ReviewSuccess {
ApprovingComment(Vec<PullRequestComment>),
CoAuthored(Vec<CommitAuthor>),
PullRequestReviewed(Vec<PullRequestReview>),
ZedZippyCommit(GithubLogin),
ZedZippyCommit(AutomatedChangeKind, GithubLogin),
}
impl ReviewSuccess {
@ -36,7 +35,7 @@ impl ReviewSuccess {
.iter()
.map(|comment| format!("@{}", comment.user.login))
.collect_vec(),
Self::ZedZippyCommit(login) => vec![login.to_string()],
Self::ZedZippyCommit(_, login) => vec![login.to_string()],
};
let reviewers = reviewers.into_iter().unique().collect_vec();
@ -59,8 +58,8 @@ impl fmt::Display for ReviewSuccess {
Self::ApprovingComment(_) => {
formatter.write_str("Approved by an organization approval comment")
}
Self::ZedZippyCommit(_) => {
formatter.write_str("Fully untampered automated version bump commit")
Self::ZedZippyCommit(kind, _) => {
write!(formatter, "Fully untampered automated {kind}")
}
}
}
@ -71,7 +70,7 @@ pub enum ReviewFailure {
// todo: We could still query the GitHub API here to search for one
NoPullRequestFound,
Unreviewed,
UnexpectedZippyAction(VersionBumpFailure),
UnexpectedZippyAction(AutomatedChangeFailure),
Other(anyhow::Error),
}
@ -90,17 +89,25 @@ impl fmt::Display for ReviewFailure {
}
#[derive(Debug)]
pub enum VersionBumpFailure {
pub enum AutomatedChangeFailure {
NoMentionInTitle,
MissingCommitData,
AuthorMismatch,
UnexpectedCoAuthors,
NotSigned,
InvalidSignature,
UnexpectedLineChanges { additions: u64, deletions: u64 },
UnexpectedLineChanges {
kind: AutomatedChangeKind,
additions: u64,
deletions: u64,
},
UnexpectedFiles {
kind: AutomatedChangeKind,
found: Vec<String>,
},
}
impl fmt::Display for VersionBumpFailure {
impl fmt::Display for AutomatedChangeFailure {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoMentionInTitle => formatter.write_str("No @-mention found in commit title"),
@ -112,19 +119,62 @@ impl fmt::Display for VersionBumpFailure {
Self::NotSigned => formatter.write_str("Commit is not signed"),
Self::InvalidSignature => formatter.write_str("Commit signature is invalid"),
Self::UnexpectedLineChanges {
kind,
additions,
deletions,
} => {
write!(
formatter,
"Unexpected line changes ({additions} additions, {deletions} deletions, \
expected {EXPECTED_VERSION_BUMP_LOC} each)"
"Unexpected line changes for {kind} \
({additions} additions, {deletions} deletions, \
expected {} each)",
kind.expected_loc()
)
}
Self::UnexpectedFiles { kind, found } => {
let expected = kind.expected_files().join(", ");
let actual = found.join(", ");
write!(
formatter,
"Unexpected files changed for {kind} \
(expected [{expected}], found [{actual}])"
)
}
}
}
}
impl AutomatedChangeKind {
fn validate_changes(
self,
metadata: &CommitMetadata,
files: &[CommitFileChange],
) -> Result<(), AutomatedChangeFailure> {
let expected_loc = self.expected_loc();
if metadata.additions() != expected_loc || metadata.deletions() != expected_loc {
return Err(AutomatedChangeFailure::UnexpectedLineChanges {
kind: self,
additions: metadata.additions(),
deletions: metadata.deletions(),
});
}
let files_differ = files.len() != self.expected_files().len()
|| files
.iter()
.any(|f| self.expected_files().contains(&f.filename.as_str()).not());
if files_differ {
return Err(AutomatedChangeFailure::UnexpectedFiles {
kind: self,
found: files.into_iter().map(|f| f.filename.clone()).collect(),
});
}
Ok(())
}
}
pub(crate) type ReviewResult = Result<ReviewSuccess, ReviewFailure>;
impl<E: Into<anyhow::Error>> From<E> for ReviewFailure {
@ -162,7 +212,7 @@ impl Reporter {
) -> Result<ReviewSuccess, ReviewFailure> {
let Some(pr_number) = commit.pr_number() else {
if commit.author().is_zed_zippy() {
return self.check_zippy_version_bump(commit).await;
return self.check_zippy_automated_change(commit).await;
} else {
return Err(ReviewFailure::NoPullRequestFound);
}
@ -194,15 +244,15 @@ impl Reporter {
Err(ReviewFailure::Unreviewed)
}
async fn check_zippy_version_bump(
async fn check_zippy_automated_change(
&self,
commit: &CommitDetails,
) -> Result<ReviewSuccess, ReviewFailure> {
let responsible_actor =
let (change_kind, responsible_actor) =
commit
.version_bump_mention()
.detect_automated_change()
.ok_or(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::NoMentionInTitle,
AutomatedChangeFailure::NoMentionInTitle,
))?;
let commit_data = self
@ -210,54 +260,54 @@ impl Reporter {
.get_commit_metadata(&Repository::ZED, &[commit.sha()])
.await?;
let authors = commit_data
.get(commit.sha())
.ok_or(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::MissingCommitData,
))?;
let metadata =
commit_data
.get(commit.sha())
.ok_or(ReviewFailure::UnexpectedZippyAction(
AutomatedChangeFailure::MissingCommitData,
))?;
if !authors
if !metadata
.primary_author()
.user()
.is_some_and(|login| login.as_str() == ZED_ZIPPY_LOGIN)
{
return Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::AuthorMismatch,
AutomatedChangeFailure::AuthorMismatch,
));
}
if authors.co_authors().is_some() {
if metadata.co_authors().is_some() {
return Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::UnexpectedCoAuthors,
AutomatedChangeFailure::UnexpectedCoAuthors,
));
}
let signature = authors
let signature = metadata
.signature()
.ok_or(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::NotSigned,
AutomatedChangeFailure::NotSigned,
))?;
if !signature.is_valid() {
return Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::InvalidSignature,
AutomatedChangeFailure::InvalidSignature,
));
}
if authors.additions() != EXPECTED_VERSION_BUMP_LOC
|| authors.deletions() != EXPECTED_VERSION_BUMP_LOC
{
return Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::UnexpectedLineChanges {
additions: authors.additions(),
deletions: authors.deletions(),
},
));
}
let files = self
.github_client
.get_commit_files(&Repository::ZED, commit.sha())
.await?;
Ok(ReviewSuccess::ZedZippyCommit(GithubLogin::new(
responsible_actor.to_owned(),
)))
change_kind
.validate_changes(metadata, &files)
.map_err(ReviewFailure::UnexpectedZippyAction)?;
Ok(ReviewSuccess::ZedZippyCommit(
change_kind,
GithubLogin::new(responsible_actor.to_owned()),
))
}
async fn check_commit_co_authors(
@ -391,19 +441,23 @@ mod tests {
use std::rc::Rc;
use std::str::FromStr;
use crate::git::{CommitDetails, CommitList, CommitSha, ZED_ZIPPY_EMAIL, ZED_ZIPPY_LOGIN};
use crate::git::{
AutomatedChangeKind, CommitDetails, CommitList, CommitSha, ZED_ZIPPY_EMAIL, ZED_ZIPPY_LOGIN,
};
use crate::github::{
AuthorAssociation, CommitMetadataBySha, GithubApiClient, GithubLogin, GithubUser,
PullRequestComment, PullRequestData, PullRequestReview, Repository, ReviewState,
AuthorAssociation, CommitFileChange, CommitMetadataBySha, GithubApiClient, GithubLogin,
GithubUser, PullRequestComment, PullRequestData, PullRequestReview, Repository,
ReviewState,
};
use super::{Reporter, ReviewFailure, ReviewSuccess, VersionBumpFailure};
use super::{AutomatedChangeFailure, Reporter, ReviewFailure, ReviewSuccess};
struct MockGithubApi {
pull_request: PullRequestData,
reviews: Vec<PullRequestReview>,
comments: Vec<PullRequestComment>,
commit_metadata_json: serde_json::Value,
commit_files: Vec<CommitFileChange>,
org_members: Vec<String>,
}
@ -441,6 +495,14 @@ mod tests {
serde_json::from_value(self.commit_metadata_json.clone()).map_err(Into::into)
}
async fn get_commit_files(
&self,
_repo: &Repository<'_>,
_sha: &CommitSha,
) -> anyhow::Result<Vec<CommitFileChange>> {
Ok(self.commit_files.clone())
}
async fn check_repo_write_permission(
&self,
_repo: &Repository<'_>,
@ -546,6 +608,7 @@ mod tests {
reviews: Vec<PullRequestReview>,
comments: Vec<PullRequestComment>,
commit_metadata_json: serde_json::Value,
commit_files: Vec<CommitFileChange>,
org_members: Vec<String>,
commit: CommitDetails,
}
@ -564,6 +627,7 @@ mod tests {
reviews: vec![],
comments: vec![],
commit_metadata_json: serde_json::json!({}),
commit_files: vec![],
org_members: vec![],
commit: make_commit(
"abc12345abc12345",
@ -600,6 +664,16 @@ mod tests {
self
}
fn with_commit_files(mut self, filenames: Vec<&str>) -> Self {
self.commit_files = filenames
.into_iter()
.map(|f| CommitFileChange {
filename: f.to_owned(),
})
.collect();
self
}
fn zippy_version_bump() -> Self {
Self {
pull_request: PullRequestData {
@ -622,6 +696,14 @@ mod tests {
"deletions": 2
}
}),
commit_files: vec![
CommitFileChange {
filename: "Cargo.lock".to_owned(),
},
CommitFileChange {
filename: "crates/zed/Cargo.toml".to_owned(),
},
],
org_members: vec![],
commit: make_commit(
"abc12345abc12345",
@ -633,12 +715,49 @@ mod tests {
}
}
fn zippy_release_channel_update() -> Self {
Self {
pull_request: PullRequestData {
number: 0,
user: None,
merged_by: None,
labels: None,
},
reviews: vec![],
comments: vec![],
commit_metadata_json: serde_json::json!({
"abc12345abc12345": {
"author": zippy_author(),
"authors": { "nodes": [] },
"signature": {
"isValid": true,
"signer": { "login": ZED_ZIPPY_LOGIN }
},
"additions": 1,
"deletions": 1
}
}),
commit_files: vec![CommitFileChange {
filename: "crates/zed/RELEASE_CHANNEL".to_owned(),
}],
org_members: vec![],
commit: make_commit(
"abc12345abc12345",
"Zed Zippy",
ZED_ZIPPY_EMAIL,
"v0.233.x stable for @cole-miller",
"",
),
}
}
async fn run_scenario(self) -> Result<ReviewSuccess, ReviewFailure> {
let mock = MockGithubApi {
pull_request: self.pull_request,
reviews: self.reviews,
comments: self.comments,
commit_metadata_json: self.commit_metadata_json,
commit_files: self.commit_files,
org_members: self.org_members,
};
let client = Rc::new(mock);
@ -901,8 +1020,14 @@ mod tests {
#[tokio::test]
async fn zippy_version_bump_with_valid_signature_succeeds() {
let result = TestScenario::zippy_version_bump().run_scenario().await;
assert!(matches!(result, Ok(ReviewSuccess::ZedZippyCommit(_))));
if let Ok(ReviewSuccess::ZedZippyCommit(login)) = &result {
assert!(matches!(
result,
Ok(ReviewSuccess::ZedZippyCommit(
AutomatedChangeKind::VersionBump,
_
))
));
if let Ok(ReviewSuccess::ZedZippyCommit(_, login)) = &result {
assert_eq!(login.as_str(), "cole-miller");
}
}
@ -922,7 +1047,7 @@ mod tests {
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::NoMentionInTitle
AutomatedChangeFailure::NoMentionInTitle
))
));
}
@ -943,7 +1068,7 @@ mod tests {
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::NotSigned
AutomatedChangeFailure::NotSigned
))
));
}
@ -968,7 +1093,7 @@ mod tests {
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::InvalidSignature
AutomatedChangeFailure::InvalidSignature
))
));
}
@ -993,7 +1118,7 @@ mod tests {
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::UnexpectedLineChanges { .. }
AutomatedChangeFailure::UnexpectedLineChanges { .. }
))
));
}
@ -1018,7 +1143,7 @@ mod tests {
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::AuthorMismatch
AutomatedChangeFailure::AuthorMismatch
))
));
}
@ -1043,11 +1168,42 @@ mod tests {
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
VersionBumpFailure::UnexpectedCoAuthors
AutomatedChangeFailure::UnexpectedCoAuthors
))
));
}
#[tokio::test]
async fn zippy_version_bump_with_wrong_files_fails() {
let result = TestScenario::zippy_version_bump()
.with_commit_files(vec!["crates/zed/RELEASE_CHANNEL"])
.run_scenario()
.await;
assert!(matches!(
result,
Err(ReviewFailure::UnexpectedZippyAction(
AutomatedChangeFailure::UnexpectedFiles { .. }
))
));
}
#[tokio::test]
async fn zippy_release_channel_update_succeeds() {
let result = TestScenario::zippy_release_channel_update()
.run_scenario()
.await;
assert!(matches!(
result,
Ok(ReviewSuccess::ZedZippyCommit(
AutomatedChangeKind::ReleaseChannelUpdate,
_
))
));
if let Ok(ReviewSuccess::ZedZippyCommit(_, login)) = &result {
assert_eq!(login.as_str(), "cole-miller");
}
}
#[tokio::test]
async fn non_zippy_commit_without_pr_is_no_pr_found() {
let result = TestScenario::single_commit()

View file

@ -18,6 +18,37 @@ use serde::Deserialize;
pub(crate) const ZED_ZIPPY_LOGIN: &str = "zed-zippy[bot]";
pub(crate) const ZED_ZIPPY_EMAIL: &str = "234243425+zed-zippy[bot]@users.noreply.github.com";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AutomatedChangeKind {
VersionBump,
ReleaseChannelUpdate,
}
impl AutomatedChangeKind {
pub(crate) fn expected_files(&self) -> &'static [&'static str] {
match self {
Self::VersionBump => &["Cargo.lock", "crates/zed/Cargo.toml"],
Self::ReleaseChannelUpdate => &["crates/zed/RELEASE_CHANNEL"],
}
}
pub(crate) fn expected_loc(&self) -> u64 {
match self {
Self::VersionBump => 2,
Self::ReleaseChannelUpdate => 1,
}
}
}
impl fmt::Display for AutomatedChangeKind {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::VersionBump => formatter.write_str("version bump"),
Self::ReleaseChannelUpdate => formatter.write_str("release channel update"),
}
}
}
pub trait Subcommand {
type ParsedOutput: FromStr<Err = anyhow::Error>;
@ -246,15 +277,25 @@ impl CommitDetails {
&self.sha
}
pub(crate) fn version_bump_mention(&self) -> Option<&str> {
pub(crate) fn detect_automated_change(&self) -> Option<(AutomatedChangeKind, &str)> {
static VERSION_BUMP_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^Bump to [0-9]+\.[0-9]+\.[0-9]+ for @([a-zA-Z0-9][a-zA-Z0-9-]*)$").unwrap()
});
static RELEASE_CHANNEL_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^v[0-9]+\.[0-9]+\.x (?:stable|preview) for @([a-zA-Z0-9][a-zA-Z0-9-]*)$")
.unwrap()
});
VERSION_BUMP_REGEX
.captures(&self.title)
.and_then(|cap| cap.get(1))
.map(|m| m.as_str())
.and_then(|capture| capture.get(1))
.map(|r#match| (AutomatedChangeKind::VersionBump, r#match.as_str()))
.or_else(|| {
RELEASE_CHANNEL_REGEX
.captures(&self.title)
.and_then(|capture| capture.get(1))
.map(|r#match| (AutomatedChangeKind::ReleaseChannelUpdate, r#match.as_str()))
})
}
}
@ -630,53 +671,69 @@ mod tests {
}
#[test]
fn version_bump_mention_extracts_username() {
fn automated_change_detects_version_bump() {
let line = format!(
"abc123{d}Zed Zippy{d}bot@test.com{d}Bump to 0.230.2 for @cole-miller",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
assert_eq!(commit.version_bump_mention(), Some("cole-miller"));
let (kind, actor) = commit.detect_automated_change().unwrap();
assert_eq!(kind, AutomatedChangeKind::VersionBump);
assert_eq!(actor, "cole-miller");
}
#[test]
fn version_bump_mention_returns_none_without_mention() {
fn automated_change_detects_stable_release_channel() {
let line = format!(
"abc123{d}Zed Zippy{d}bot@test.com{d}v0.233.x stable for @cole-miller",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
let (kind, actor) = commit.detect_automated_change().unwrap();
assert_eq!(kind, AutomatedChangeKind::ReleaseChannelUpdate);
assert_eq!(actor, "cole-miller");
}
#[test]
fn automated_change_detects_preview_release_channel() {
let line = format!(
"abc123{d}Zed Zippy{d}bot@test.com{d}v0.234.x preview for @cole-miller",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
let (kind, actor) = commit.detect_automated_change().unwrap();
assert_eq!(kind, AutomatedChangeKind::ReleaseChannelUpdate);
assert_eq!(actor, "cole-miller");
}
#[test]
fn automated_change_returns_none_for_regular_commit() {
let line = format!(
"abc123{d}Alice{d}alice@test.com{d}Fix a bug",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
assert!(commit.version_bump_mention().is_none());
assert!(commit.detect_automated_change().is_none());
}
#[test]
fn version_bump_mention_rejects_wrong_prefix() {
fn automated_change_rejects_wrong_prefix() {
let line = format!(
"abc123{d}Zed Zippy{d}bot@test.com{d}Fix thing for @cole-miller",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
assert!(commit.version_bump_mention().is_none());
assert!(commit.detect_automated_change().is_none());
}
#[test]
fn version_bump_mention_rejects_bare_mention() {
let line = format!(
"abc123{d}Zed Zippy{d}bot@test.com{d}@cole-miller bumped something",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
assert!(commit.version_bump_mention().is_none());
}
#[test]
fn version_bump_mention_rejects_trailing_text() {
fn automated_change_rejects_trailing_text() {
let line = format!(
"abc123{d}Zed Zippy{d}bot@test.com{d}Bump to 0.230.2 for @cole-miller extra",
d = CommitDetails::FIELD_DELIMITER
);
let commit = CommitDetails::parse(&line, "").unwrap();
assert!(commit.version_bump_mention().is_none());
assert!(commit.detect_automated_change().is_none());
}
#[test]

View file

@ -177,6 +177,11 @@ impl CommitSignature {
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct CommitFileChange {
pub filename: String,
}
#[derive(Debug, Deserialize)]
pub struct CommitMetadata {
#[serde(rename = "author")]
@ -301,6 +306,11 @@ pub trait GithubApiClient {
repo: &Repository<'_>,
commit_shas: &[&CommitSha],
) -> Result<CommitMetadataBySha>;
async fn get_commit_files(
&self,
repo: &Repository<'_>,
sha: &CommitSha,
) -> Result<Vec<CommitFileChange>>;
async fn check_repo_write_permission(
&self,
repo: &Repository<'_>,
@ -430,8 +440,8 @@ mod octo_client {
};
use super::{
AuthorAssociation, CommitMetadataBySha, GithubApiClient, GithubLogin, GithubUser,
PullRequestComment, PullRequestData, PullRequestReview, ReviewState,
AuthorAssociation, CommitFileChange, CommitMetadataBySha, GithubApiClient, GithubLogin,
GithubUser, PullRequestComment, PullRequestData, PullRequestReview, ReviewState,
};
fn convert_author_association(association: OctocrabAuthorAssociation) -> AuthorAssociation {
@ -612,6 +622,27 @@ mod octo_client {
.map(|response| response.repository)
}
async fn get_commit_files(
&self,
repo: &Repository<'_>,
sha: &CommitSha,
) -> Result<Vec<CommitFileChange>> {
let response = self
.client
.commits(repo.owner.as_ref(), repo.name.as_ref())
.get(sha.as_str())
.await?;
Ok(response
.files
.into_iter()
.flatten()
.map(|file| CommitFileChange {
filename: file.filename,
})
.collect())
}
async fn check_repo_write_permission(
&self,
repo: &Repository<'_>,

View file

@ -320,7 +320,7 @@ mod tests {
use crate::{
checks::{ReviewFailure, ReviewSuccess},
git::{CommitDetails, CommitList},
git::{AutomatedChangeKind, CommitDetails, CommitList},
github::{AuthorAssociation, GithubLogin, GithubUser, PullRequestReview, ReviewState},
};
@ -382,9 +382,10 @@ mod tests {
);
report.add(
make_commit("ddd", "Dave", "dave@test.com", "Bump Version", ""),
Ok(ReviewSuccess::ZedZippyCommit(GithubLogin::new(
"dave".to_string(),
))),
Ok(ReviewSuccess::ZedZippyCommit(
AutomatedChangeKind::VersionBump,
GithubLogin::new("dave".to_string()),
)),
);
let summary = report.summary();

View file

@ -175,7 +175,10 @@ fn create_preview_branch(
let main_sha = StepOutput::new(&main_sha_step, "main_sha");
let commit_step: Step<Use> = steps::BotCommitStep::new(
format!("{} preview", outputs.preview_branch),
format!(
"{} preview for @${{{{ github.actor }}}}",
outputs.preview_branch
),
&outputs.preview_branch,
&token,
)
@ -229,7 +232,10 @@ fn promote_to_stable(
let write_channel = named::bash("echo -n stable > crates/zed/RELEASE_CHANNEL");
let commit_step: Step<Use> = steps::BotCommitStep::new(
format!("{} stable", outputs.stable_branch),
format!(
"{} stable for @${{{{ github.actor }}}}",
outputs.stable_branch
),
&outputs.stable_branch,
&token,
)