From 3d938e243cc698cc522c923af83a17fcdae13dbe Mon Sep 17 00:00:00 2001
From: Dmitry Murzin <diralik@yandex.ru>
Date: Sat, 22 Jun 2024 21:58:55 +0300
Subject: [PATCH] Fix factorio-mods-helper contributions counting

---
 ...github_fork_all_not_forked_repositories.rs | 14 ++++++
 src/github.rs                                 | 43 ++++++++++++++++---
 src/server/trigger_update.rs                  |  4 +-
 src/webhooks.rs                               | 17 +++++---
 tests/all_github_repositories_are_forked.rs   | 23 ++++++++++
 5 files changed, 88 insertions(+), 13 deletions(-)
 create mode 100644 examples/github_fork_all_not_forked_repositories.rs
 create mode 100644 tests/all_github_repositories_are_forked.rs

diff --git a/examples/github_fork_all_not_forked_repositories.rs b/examples/github_fork_all_not_forked_repositories.rs
new file mode 100644
index 0000000..9e58342
--- /dev/null
+++ b/examples/github_fork_all_not_forked_repositories.rs
@@ -0,0 +1,14 @@
+use fml::github;
+
+#[tokio::main]
+async fn main() {
+    fml::init();
+
+    let not_forked = github::get_not_forked_repositories().await.not_forked;
+
+    let api_personal = github::as_personal_account();
+    for full_name in not_forked {
+        println!("Forking {}", full_name);
+        github::fork_repository_without_check(&api_personal, &full_name).await;
+    }
+}
diff --git a/src/github.rs b/src/github.rs
index c99726b..a46940c 100644
--- a/src/github.rs
+++ b/src/github.rs
@@ -236,21 +236,29 @@ pub fn as_personal_account() -> Octocrab {
         .unwrap()
 }
 
-pub async fn fork_repository(personal_api: &Octocrab, owner: &str, repo: &str) -> bool {
-    if let Some(result) = check_fork_exists(personal_api, owner, repo).await {
-        return result;
+pub async fn fork_repository(personal_api: &Octocrab, full_name: &str) -> bool {
+    if let Some(is_fork_name_correct) = check_fork_exists(personal_api, full_name).await {
+        return is_fork_name_correct;
     }
+    fork_repository_without_check(personal_api, full_name).await;
+    true
+}
 
-    info!("[update-github-from-crowdin] [{}/{}] forking repository...", owner, repo);
+pub async fn fork_repository_without_check(personal_api: &Octocrab, full_name: &str) {
+    let (owner, repo) = full_name.split_once('/').unwrap();
+    info!("[update-github-from-crowdin] [{}] forking repository...", full_name);
     personal_api
         .repos(owner, repo)
         .create_fork()
         .send().await.unwrap();
     sleep(Duration::from_secs(120)).await;
-    true
 }
 
-async fn check_fork_exists(api: &Octocrab, owner: &str, repo: &str) -> Option<bool> {
+// None => no fork
+// Some(false) => fork with different name
+// Some(true) => fork exists and can be used
+async fn check_fork_exists(api: &Octocrab, full_name: &str) -> Option<bool> {
+    let (owner, repo) = full_name.split_once('/').unwrap();
     let forks = api
         .repos(owner, repo)
         .list_forks()
@@ -272,6 +280,29 @@ async fn check_fork_exists(api: &Octocrab, owner: &str, repo: &str) -> Option<bo
     None
 }
 
+#[derive(Default)]
+pub struct GetNotForkedResult {
+    pub not_forked: Vec<String>,
+    pub forked_with_diferrent_name: Vec<String>,
+}
+
+pub async fn get_not_forked_repositories() -> GetNotForkedResult {
+    let api_app = as_app();
+    let repositories = get_all_repositories(&api_app).await;
+
+    let api_personal = as_personal_account();
+    let mut result = GetNotForkedResult::default();
+    for (repo_info, _id) in repositories {
+        let full_name = repo_info.full_name;
+        match check_fork_exists(&api_personal, &full_name).await {
+            None => result.not_forked.push(full_name),
+            Some(false) => result.forked_with_diferrent_name.push(full_name),
+            Some(true) => continue,
+        }
+    }
+    result
+}
+
 pub async fn star_repository(api: &Octocrab, full_name: &str) {
     let _response: octocrab::Result<EmptyBody> = api
         .put(format!("/user/starred/{}", full_name), None::<&()>)
diff --git a/src/server/trigger_update.rs b/src/server/trigger_update.rs
index 483bf33..edc2740 100644
--- a/src/server/trigger_update.rs
+++ b/src/server/trigger_update.rs
@@ -160,10 +160,10 @@ async fn push_crowdin_changes_to_repository(
 
 async fn push_changes_using_pull_request(path: &Path, full_name: &str, base_branch: &str) {
     let personal_api = as_personal_account();
-    let (owner, repo) = full_name.split_once('/').unwrap();
-    if !github::fork_repository(&personal_api, owner, repo).await {
+    if !github::fork_repository(&personal_api, full_name).await {
         return;
     }
+    let (_owner, repo) = full_name.split_once('/').unwrap();
     let pushed = git_util::push_to_my_fork(path, repo);
     if pushed {
         sleep(Duration::from_secs(30)).await;
diff --git a/src/webhooks.rs b/src/webhooks.rs
index 6c80597..2b15ccb 100644
--- a/src/webhooks.rs
+++ b/src/webhooks.rs
@@ -78,9 +78,7 @@ async fn on_repositories_added(repositories: Vec<InstallationEventRepository>, i
             continue;
         };
         on_repository_added(repo_info, installation_id).await;
-
-        let api_personal = github::as_personal_account();
-        github::star_repository(&api_personal, &repository).await;
+        star_and_fork_repository(&repository).await;
     }
 }
 
@@ -137,8 +135,7 @@ pub async fn on_push_event(
     info!("[push-webhook] [{}] success", full_name);
 
     if created {
-        let api_personal = github::as_personal_account();
-        github::star_repository(&api_personal, &full_name).await;
+        star_and_fork_repository(&full_name).await;
     }
 }
 
@@ -174,3 +171,13 @@ fn get_all_changed_files(event: &PushWebhookEventPayload) -> impl Iterator<Item=
             added.chain(modified).chain(removed).map(Deref::deref)
         })
 }
+
+// This is needed for correct counting of contributions,
+// so they will be displayed at https://github.com/factorio-mods-helper.
+// Previously it was enough to star repository, but it was changed somewhere in 2023-2024.
+// https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/managing-contribution-settings-on-your-profile/why-are-my-contributions-not-showing-up-on-my-profile
+async fn star_and_fork_repository(repository: &str) {
+    let api_personal = github::as_personal_account();
+    github::star_repository(&api_personal, repository).await;
+    github::fork_repository(&api_personal, repository).await;
+}
diff --git a/tests/all_github_repositories_are_forked.rs b/tests/all_github_repositories_are_forked.rs
new file mode 100644
index 0000000..9b0e112
--- /dev/null
+++ b/tests/all_github_repositories_are_forked.rs
@@ -0,0 +1,23 @@
+use fml::github;
+
+#[tokio::test]
+async fn main() {
+    fml::init();
+    let result = github::get_not_forked_repositories().await;
+
+    let forked_with_diferrent_name = result.forked_with_diferrent_name;
+    if !forked_with_diferrent_name.is_empty() {
+        for full_name in &forked_with_diferrent_name {
+            println!("{}", full_name);
+        }
+        panic!("{} repositories have forks with different name", forked_with_diferrent_name.len());
+    }
+
+    let not_forked = result.not_forked;
+    if !not_forked.is_empty() {
+        for full_name in &not_forked {
+            println!("{}", full_name);
+        }
+        panic!("{} repositories not forked", not_forked.len());
+    }
+}