Skip to content

Commit

Permalink
feat: Re-implement majority of backend
Browse files Browse the repository at this point in the history
  • Loading branch information
GeckoEidechse committed Jan 3, 2025
1 parent d8c220f commit 7ae9254
Show file tree
Hide file tree
Showing 19 changed files with 3,069 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src-tauri/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This file stores various global constants values
use const_format::concatcp;
use std::time::Duration;

/// FlightCore user agent for web requests
pub const APP_USER_AGENT: &str = concatcp!("FlightCore/", env!("CARGO_PKG_VERSION"));
Expand All @@ -9,3 +10,51 @@ pub const MASTER_SERVER_URL: &str = "https://northstar.tf";

/// server list endpoint
pub const SERVER_BROWSER_ENDPOINT: &str = "/client/servers";

/// List of core Northstar mods
pub const CORE_MODS: [&str; 3] = [
"Northstar.Client",
"Northstar.Custom",
"Northstar.CustomServers",
];

/// List of Thunderstoremods that shouldn't be installable
/// as they behave different than common Squirrel mods
pub const BLACKLISTED_MODS: [&str; 3] = [
"northstar-Northstar",
"northstar-NorthstarReleaseCandidate",
"ebkr-r2modman",
];

/// List of Thunderstoremods that have some specific install requirements that makes them different from standard mods
pub const MODS_WITH_SPECIAL_REQUIREMENTS: [&str; 1] = ["NanohmProtogen-VanillaPlus"];

/// Order in which the sections for release notes should be displayed
pub const SECTION_ORDER: [&str; 11] = [
"feat", "fix", "docs", "style", "refactor", "build", "test", "i18n", "ci", "chore", "other",
];

/// Statistics (players and servers counts) refresh delay
pub const REFRESH_DELAY: Duration = Duration::from_secs(5 * 60);

/// Flightcore repo name and org name on GitHub
pub const FLIGHTCORE_REPO_NAME: &str = "R2NorthstarTools/FlightCore";

/// Northstar release repo name and org name on GitHub
pub const NORTHSTAR_RELEASE_REPO_NAME: &str = "R2Northstar/Northstar";

/// NorthstarLauncher repo name on GitHub
pub const NORTHSTAR_LAUNCHER_REPO_NAME: &str = "NorthstarLauncher";

/// NorthstarMods repo name on GitHub
pub const NORTHSTAR_MODS_REPO_NAME: &str = "NorthstarMods";

/// URL to launcher commits API URL
pub const NS_LAUNCHER_COMMITS_API_URL: &str =
"https://api.github.com/repos/R2Northstar/NorthstarLauncher/commits";

/// Filename of DLL that Northstar uses
pub const NORTHSTAR_DLL: &str = "Northstar.dll";

/// Profile that Northstar defaults to and ships with
pub const NORTHSTAR_DEFAULT_PROFILE: &str = "R2Northstar";
84 changes: 84 additions & 0 deletions src-tauri/src/development/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use crate::constants::NS_LAUNCHER_COMMITS_API_URL;
use crate::github::{
pull_requests::{check_github_api, download_zip_into_memory, get_launcher_download_link},
CommitInfo,
};

#[tauri::command]
pub async fn install_git_main(game_install_path: &str) -> Result<String, String> {
// Get list of commits
let commits: Vec<CommitInfo> = serde_json::from_value(
check_github_api(NS_LAUNCHER_COMMITS_API_URL)
.await
.expect("Failed request"),
)
.unwrap();

// Get latest commit...
let latest_commit_sha = commits[0].sha.clone();
// ...and according artifact download URL
let download_url = get_launcher_download_link(latest_commit_sha.clone()).await?;

let archive = match download_zip_into_memory(download_url).await {
Ok(archive) => archive,
Err(err) => return Err(err.to_string()),
};

let extract_directory = format!(
"{}/___flightcore-temp/download-dir/launcher-pr-{}",
game_install_path, latest_commit_sha
);
match std::fs::create_dir_all(extract_directory.clone()) {
Ok(_) => (),
Err(err) => {
return Err(format!(
"Failed creating temporary download directory: {}",
err
))
}
};

let target_dir = std::path::PathBuf::from(extract_directory.clone()); // Doesn't need to exist
match zip_extract::extract(std::io::Cursor::new(archive), &target_dir, true) {
Ok(()) => (),
Err(err) => {
return Err(format!("Failed unzip: {}", err));
}
};

// Copy only necessary files from temp dir
// Copy:
// - NorthstarLauncher.exe
// - Northstar.dll
let files_to_copy = vec!["NorthstarLauncher.exe", "Northstar.dll"];
for file_name in files_to_copy {
let source_file_path = format!("{}/{}", extract_directory, file_name);
let destination_file_path = format!("{}/{}", game_install_path, file_name);
match std::fs::copy(source_file_path, destination_file_path) {
Ok(_result) => (),
Err(err) => {
return Err(format!(
"Failed to copy necessary file {} from temp dir: {}",
file_name, err
))
}
};
}

// delete extract directory
match std::fs::remove_dir_all(&extract_directory) {
Ok(()) => (),
Err(err) => {
return Err(format!(
"Failed to delete temporary download directory: {}",
err
))
}
}

log::info!(
"All done with installing launcher from {}",
latest_commit_sha
);
Ok(latest_commit_sha)
}
162 changes: 162 additions & 0 deletions src-tauri/src/github/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,163 @@
pub mod pull_requests;
pub mod release_notes;

use crate::constants::{
APP_USER_AGENT, FLIGHTCORE_REPO_NAME, NORTHSTAR_RELEASE_REPO_NAME, SECTION_ORDER,
};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use ts_rs::TS;

#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct Tag {
name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, TS)]
#[ts(export)]
pub enum Project {
FlightCore,
Northstar,
}

/// Wrapper type needed for frontend
#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct TagWrapper {
label: String,
value: Tag,
}

#[derive(Debug, Deserialize)]
pub struct CommitInfo {
pub sha: String,
commit: Commit,
author: Option<CommitAuthor>,
}

#[derive(Debug, Deserialize)]
struct Commit {
message: String,
}

#[derive(Debug, Deserialize)]
struct CommitAuthor {
login: String,
}

#[derive(Debug, Deserialize)]
struct Comparison {
commits: Vec<CommitInfo>,
}

/// Get a list of tags on the FlightCore repo
#[tauri::command]
pub fn get_list_of_tags(project: Project) -> Result<Vec<TagWrapper>, String> {
todo!()
}

/// Use GitHub API to compare two tags of the same repo against each other and get the resulting changes
#[tauri::command]
pub fn compare_tags(project: Project, first_tag: Tag, second_tag: Tag) -> Result<String, String> {
match project {
Project::FlightCore => compare_tags_flightcore(first_tag, second_tag),
Project::Northstar => compare_tags_northstar(first_tag, second_tag),
}
}

pub fn compare_tags_flightcore(first_tag: Tag, second_tag: Tag) -> Result<String, String> {
todo!()
}

/// Generate release notes in the format used for FlightCore
fn generate_flightcore_release_notes(commits: Vec<String>) -> String {
let grouped_commits = group_commits_by_type(commits);
let mut release_notes = String::new();

// Go over commit types and generate notes
for commit_type in SECTION_ORDER {
if let Some(commit_list) = grouped_commits.get(commit_type) {
if !commit_list.is_empty() {
let section_title = match commit_type {
"feat" => "**Features:**",
"fix" => "**Bug Fixes:**",
"docs" => "**Documentation:**",
"style" => "**Code style changes:**",
"refactor" => "**Code Refactoring:**",
"build" => "**Build:**",
"ci" => "**Continuous integration changes:**",
"test" => "**Tests:**",
"chore" => "**Chores:**",
"i18n" => "**Translations:**",
_ => "**Other:**",
};

release_notes.push_str(&format!("{}\n", section_title));

for commit_message in commit_list {
release_notes.push_str(&format!("- {}\n", commit_message));
}

release_notes.push('\n');
}
}
}

let release_notes = release_notes.trim_end_matches('\n').to_string();
release_notes
}

/// Group semantic commit messages by type
/// Commmit messages that are not formatted accordingly are marked as "other"
fn group_commits_by_type(commits: Vec<String>) -> HashMap<String, Vec<String>> {
let mut grouped_commits: HashMap<String, Vec<String>> = HashMap::new();
let mut other_commits: Vec<String> = vec![];

for commit in commits {
let commit_parts: Vec<&str> = commit.splitn(2, ':').collect();
if commit_parts.len() == 2 {
let commit_type = commit_parts[0].to_lowercase();
let commit_description = commit_parts[1].trim().to_string();

// Check if known commit type
if SECTION_ORDER.contains(&commit_type.as_str()) {
let commit_list = grouped_commits.entry(commit_type.to_string()).or_default();
commit_list.push(commit_description);
} else {
// otherwise add to list of "other"
other_commits.push(commit.to_string());
}
} else {
other_commits.push(commit.to_string());
}
}
grouped_commits.insert("other".to_string(), other_commits);

grouped_commits
}

/// Compares two tags on Northstar repo and generates release notes over the diff in tags
/// over the 3 major repos (Northstar, NorthstarLauncher, NorthstarMods)
pub fn compare_tags_northstar(first_tag: Tag, second_tag: Tag) -> Result<String, String> {
todo!()
}

/// Takes the commit title and repo slug and formats it as
/// `[commit title(SHORTENED_REPO#NUMBER)](LINK)`
fn turn_pr_number_into_link(input: &str, repo: &str) -> String {
// Extract `Mods/Launcher` from repo title
let last_line = repo
.split('/')
.next_back()
.unwrap()
.trim_start_matches("Northstar");
// Extract PR number
let re = Regex::new(r"#(\d+)").unwrap();

// Generate pull request link
let pull_link = format!("https://github.com/{}/pull/", repo);
re.replace_all(input, format!("[{}#$1]({}$1)", last_line, pull_link))
.to_string()
}
Loading

0 comments on commit 7ae9254

Please sign in to comment.