Skip to content

Commit

Permalink
Add merge-upstream command
Browse files Browse the repository at this point in the history
Resolves #376
  • Loading branch information
russellbanks committed Jul 19, 2024
1 parent 8ab589d commit 260cd96
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 26 deletions.
6 changes: 5 additions & 1 deletion src/commands/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use indexmap::IndexMap;
use indicatif::{ProgressBar, ProgressStyle};
use inquire::MultiSelect;
use std::num::NonZeroUsize;
use std::time::Duration;

/// Finds branches from the fork of winget-pkgs that have had a merged or closed pull request to microsoft/winget-pkgs
/// from them, prompting for which ones to delete
Expand Down Expand Up @@ -57,7 +58,7 @@ impl Cleanup {

// Retrieve an associated pull request for each branch
let pb = ProgressBar::new(branches.len() as u64)
.with_style(pb_style.clone())
.with_style(pb_style)
.with_message(format!(
"Retrieving branches that have a {merge_state} pull request associated with them"
));
Expand Down Expand Up @@ -121,9 +122,12 @@ impl Cleanup {

// Delete all selected branches
let pb = ProgressBar::new_spinner().with_message("Deleting selected branches");
pb.enable_steady_tick(Duration::from_millis(50));

github
.delete_branches(&repository_id, branches_to_delete)
.await?;

pb.finish_and_clear();

Ok(())
Expand Down
77 changes: 77 additions & 0 deletions src/commands/merge_upstream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::time::Duration;

use clap::Parser;
use color_eyre::Result;
use crossterm::style::Stylize;
use indicatif::ProgressBar;

use crate::credential::handle_token;
use crate::github::github_client::GitHub;
use crate::hyperlink::Hyperlink;

/// Merges changes from microsoft/winget-pkgs into the fork repository
#[derive(Parser)]
pub struct MergeUpstream {
/// Merges changes even if the fork's default branch is not fast-forward. This is not
/// recommended as you should instead have a clean default branch that has not diverged from the
/// upstream default branch
#[arg(short, long)]
force: bool,

/// GitHub personal access token with the `public_repo` scope
#[arg(short, long, env = "GITHUB_TOKEN")]
token: Option<String>,
}

impl MergeUpstream {
pub async fn run(self) -> Result<()> {
let token = handle_token(self.token).await?;
let github = GitHub::new(&token)?;

// Fetch repository data from both upstream and fork repositories asynchronously
let winget_pkgs = github.get_winget_pkgs(None);
let winget_pkgs_fork = github
.get_winget_pkgs(Some(&github.get_username().await?))
.await?;
let winget_pkgs = winget_pkgs.await?;

// Create hyperlinks to the repository's URLs when their full name is printed
let winget_pkgs_hyperlink = winget_pkgs.full_name.hyperlink(winget_pkgs.url).blue();
let winget_pkgs_fork_hyperlink = winget_pkgs_fork
.full_name
.hyperlink(winget_pkgs_fork.url)
.blue();

// Check whether the fork is already up-to-date with upstream by their latest commit OID's
if winget_pkgs.default_branch_oid == winget_pkgs_fork.default_branch_oid {
println!(
"{winget_pkgs_fork_hyperlink} is already {} with {winget_pkgs_hyperlink}",
"up-to-date".green()
);
return Ok(());
}

// Show an indeterminate progress bar while upstream changes are being merged
let pb = ProgressBar::new_spinner().with_message(format!(
"Merging upstream changes from {} into {}",
winget_pkgs.full_name.as_str().blue(),
winget_pkgs_fork.full_name.as_str().blue(),
));
pb.enable_steady_tick(Duration::from_millis(50));

github
.merge_upstream(
&winget_pkgs_fork.default_branch_ref_id,
winget_pkgs.default_branch_oid,
self.force,
)
.await?;

pb.finish_with_message(format!(
"{} merged upstream changes from {winget_pkgs_hyperlink} into {winget_pkgs_fork_hyperlink}",
"Successfully".green(),
));

Ok(())
}
}
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod cleanup;
pub mod list_versions;
pub mod merge_upstream;
pub mod new_version;
pub mod remove_version;
pub mod show_version;
Expand Down
3 changes: 2 additions & 1 deletion src/commands/new_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ use crate::commands::utils::{
use crate::credential::{get_default_headers, handle_token};
use crate::download_file::{download_urls, process_files};
use crate::github::github_client::{GitHub, WINGET_PKGS_FULL_NAME};
use crate::github::graphql::create_commit::{Base64String, FileAddition};
use crate::github::graphql::create_commit::FileAddition;
use crate::github::graphql::types::Base64String;
use crate::github::utils::{
get_branch_name, get_commit_title, get_package_path, get_pull_request_body,
};
Expand Down
3 changes: 2 additions & 1 deletion src/commands/update_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use crate::commands::utils::{
use crate::credential::{get_default_headers, handle_token};
use crate::download_file::{download_urls, process_files};
use crate::github::github_client::{GitHub, WINGET_PKGS_FULL_NAME};
use crate::github::graphql::create_commit::{Base64String, FileAddition};
use crate::github::graphql::create_commit::FileAddition;
use crate::github::graphql::types::Base64String;
use crate::github::utils::{
get_branch_name, get_commit_title, get_package_path, get_pull_request_body,
};
Expand Down
41 changes: 36 additions & 5 deletions src/github/github_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ use crate::github::graphql::get_existing_pull_request::{
use crate::github::graphql::get_pull_request_from_branch::{
GetPullRequestFromBranch, GetPullRequestFromBranchVariables, PullRequest,
};
use crate::github::graphql::get_repository_info::{
GetRepositoryInfo, GitObjectId, RepositoryVariables,
};
use crate::github::graphql::update_refs::{GitRefname, RefUpdate, UpdateRefs, UpdateRefsVariables};
use crate::github::graphql::get_repository_info::{GetRepositoryInfo, RepositoryVariables};
use crate::github::graphql::merge_upstream::{MergeUpstream, MergeUpstreamVariables};
use crate::github::graphql::types::{GitObjectId, GitRefName};
use crate::github::graphql::update_refs::{RefUpdate, UpdateRefs, UpdateRefsVariables};
use crate::github::utils::get_package_path;
use crate::manifests::default_locale_manifest::DefaultLocaleManifest;
use crate::manifests::installer_manifest::InstallerManifest;
Expand Down Expand Up @@ -288,8 +288,11 @@ impl GitHub {

Ok(RepositoryData {
id: repository.id,
full_name: repository.name_with_owner,
url: repository.url,
default_branch_name: default_branch.name,
default_branch_oid,
default_branch_ref_id: default_branch.id,
})
}

Expand Down Expand Up @@ -531,7 +534,7 @@ impl GitHub {
after_oid: GitObjectId(DELETE_ID.to_string()),
before_oid: None,
force: None,
name: GitRefname(format!("refs/heads/{branch_name}")),
name: GitRefName(format!("refs/heads/{branch_name}")),
})
.collect(),
repository_id,
Expand Down Expand Up @@ -660,6 +663,31 @@ impl GitHub {
topics: Option::from(topics).filter(|topics| !topics.is_empty()),
})
}

pub async fn merge_upstream(
&self,
branch_ref_id: &Id,
upstream_target_oid: GitObjectId,
force: bool,
) -> Result<()> {
let response = self
.0
.post(GITHUB_GRAPHQL_URL)
.run_graphql(MergeUpstream::build(MergeUpstreamVariables {
branch_ref_id,
upstream_target_oid,
force,
}))
.await?;
if response.data.is_some() {
Ok(())
} else {
Err(response.errors.unwrap_or_default().into_iter().fold(
eyre!("Failed to merge upstream branch into fork branch"),
Report::wrap_err,
))
}
}
}

pub struct Manifests {
Expand Down Expand Up @@ -689,6 +717,9 @@ pub struct GitHubFile {

pub struct RepositoryData {
pub id: Id,
pub full_name: String,
pub url: Url,
pub default_branch_name: String,
pub default_branch_oid: GitObjectId,
pub default_branch_ref_id: Id,
}
5 changes: 1 addition & 4 deletions src/github/graphql/create_commit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::github::graphql::get_repository_info::GitObjectId;
use crate::github::graphql::github_schema::github_schema as schema;
use crate::github::graphql::types::{Base64String, GitObjectId};
use url::Url;

/*
Expand Down Expand Up @@ -80,6 +80,3 @@ pub struct CommitMessage<'a> {
pub body: Option<&'a str>,
pub headline: &'a str,
}

#[derive(cynic::Scalar)]
pub struct Base64String(pub String);
3 changes: 1 addition & 2 deletions src/github/graphql/create_ref.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::github::graphql::get_repository_info::GitObjectId;
use crate::github::graphql::github_schema::github_schema as schema;

use crate::github::graphql::types::GitObjectId;
/*
mutation CreateRef($repositoryId: ID!, $name: String!, $oid: GitObjectID!) {
createRef(input: {
Expand Down
2 changes: 0 additions & 2 deletions src/github/graphql/get_branches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ query GetBranches($owner: String!, $name: String!) {
refs(first: 100, refPrefix: "refs/heads/") {
nodes {
name
id
}
}
}
Expand Down Expand Up @@ -43,5 +42,4 @@ pub struct RefConnection {
#[derive(cynic::QueryFragment)]
pub struct Ref {
pub name: String,
pub id: cynic::Id,
}
10 changes: 5 additions & 5 deletions src/github/graphql/get_repository_info.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::github::graphql::github_schema::github_schema as schema;

use crate::github::graphql::types::GitObjectId;
use url::Url;
/*
query GetRepositoryInfo($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
Expand Down Expand Up @@ -30,20 +31,19 @@ pub struct GetRepositoryInfo {
#[derive(cynic::QueryFragment)]
pub struct Repository {
pub id: cynic::Id,
pub name_with_owner: String,
pub url: Url,
pub default_branch_ref: Option<Ref>,
}

#[derive(cynic::QueryFragment)]
pub struct Ref {
pub name: String,
pub id: cynic::Id,
pub target: Option<GitObject>,
}

#[derive(cynic::QueryFragment)]
pub struct GitObject {
pub oid: GitObjectId,
}

#[derive(cynic::Scalar)]
#[cynic(graphql_type = "GitObjectID")]
pub struct GitObjectId(pub String);
32 changes: 32 additions & 0 deletions src/github/graphql/merge_upstream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::github::graphql::github_schema::github_schema as schema;
use crate::github::graphql::types::GitObjectId;

/*
mutation MergeUpstream($branchRefId: ID!, $upstreamTargetOid: GitObjectID!) {
updateRef(input: {
refId: $branchRefId,
oid: $upstreamTargetOid,
}) {
clientMutationId
}
}
*/

#[derive(cynic::QueryVariables)]
pub struct MergeUpstreamVariables<'a> {
pub branch_ref_id: &'a cynic::Id,
pub upstream_target_oid: GitObjectId,
pub force: bool,
}

#[derive(cynic::QueryFragment)]
#[cynic(graphql_type = "Mutation", variables = "MergeUpstreamVariables")]
pub struct MergeUpstream {
#[arguments(input: { oid: $upstream_target_oid, refId: $branch_ref_id })]
pub update_ref: Option<UpdateRefPayload>,
}

#[derive(cynic::QueryFragment)]
pub struct UpdateRefPayload {
pub client_mutation_id: Option<String>,
}
2 changes: 2 additions & 0 deletions src/github/graphql/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ pub mod get_existing_pull_request;
pub mod get_pull_request_from_branch;
pub mod get_repository_info;
pub mod github_schema;
pub mod merge_upstream;
pub mod types;
pub mod update_refs;
12 changes: 12 additions & 0 deletions src/github/graphql/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use crate::github::graphql::github_schema::github_schema as schema;

#[derive(cynic::Scalar)]
pub struct Base64String(pub String);

#[derive(cynic::Scalar, PartialEq, Eq)]
#[cynic(graphql_type = "GitObjectID")]
pub struct GitObjectId(pub String);

#[derive(cynic::Scalar)]
#[cynic(graphql_type = "GitRefname")]
pub struct GitRefName(pub String);
7 changes: 2 additions & 5 deletions src/github/graphql/update_refs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::github::graphql::get_repository_info::GitObjectId;
use crate::github::graphql::github_schema::github_schema as schema;
use crate::github::graphql::types::{GitObjectId, GitRefName};

/*
mutation UpdateRefs($repositoryId: ID!, $refUpdates: [RefUpdate!]!) {
Expand Down Expand Up @@ -35,8 +35,5 @@ pub struct RefUpdate {
pub after_oid: GitObjectId,
pub before_oid: Option<GitObjectId>,
pub force: Option<bool>,
pub name: GitRefname,
pub name: GitRefName,
}

#[derive(cynic::Scalar)]
pub struct GitRefname(pub String);
13 changes: 13 additions & 0 deletions src/hyperlink.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pub trait Hyperlink<S: AsRef<str>, T: AsRef<str>> {
fn hyperlink(&self, url: T) -> String;
}

impl<S: AsRef<str>, T: AsRef<str>> Hyperlink<S, T> for S {
fn hyperlink(&self, url: T) -> String {
format!(
"\u{1b}]8;;{}\u{1b}\\{}\u{1b}]8;;\u{1b}\\",
url.as_ref(),
self.as_ref()
)
}
}
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod download_file;
mod editor;
mod file_analyser;
mod github;
mod hyperlink;
mod manifest;
mod manifests;
mod match_installers;
Expand All @@ -18,6 +19,7 @@ mod zip;

use crate::commands::cleanup::Cleanup;
use crate::commands::list_versions::ListVersions;
use crate::commands::merge_upstream::MergeUpstream;
use crate::commands::new_version::NewVersion;
use crate::commands::remove_version::RemoveVersion;
use crate::commands::show_version::ShowVersion;
Expand All @@ -42,6 +44,7 @@ async fn main() -> Result<()> {
},
Commands::ListVersions(list_versions) => list_versions.run().await,
Commands::Show(show_version) => show_version.run().await,
Commands::MergeUpstream(merge_upstream) => merge_upstream.run().await,
}
}

Expand All @@ -63,4 +66,5 @@ enum Commands {
Token(TokenArgs),
ListVersions(ListVersions),
Show(ShowVersion),
MergeUpstream(MergeUpstream),
}

0 comments on commit 260cd96

Please sign in to comment.