summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2025-06-13 15:55:55 -0700
committerGravatar Anshul Gupta <ansg191@anshulg.com> 2025-06-13 15:59:55 -0700
commit6bf51383587f7d6ff31f253dba5fa6d2209fee6d (patch)
treec33fe7e87b312e3618aaebe72da6c8bd6a6e6be4 /src
parentd814e2111a539bacf8be4958a6cea14040490015 (diff)
downloadgithub-mirror-6bf51383587f7d6ff31f253dba5fa6d2209fee6d.tar.gz
github-mirror-6bf51383587f7d6ff31f253dba5fa6d2209fee6d.tar.zst
github-mirror-6bf51383587f7d6ff31f253dba5fa6d2209fee6d.zip
Add ssh transport option for github
Diffstat (limited to 'src')
-rw-r--r--src/config.c13
-rw-r--r--src/config.h7
-rw-r--r--src/git.c61
-rw-r--r--src/github/types.c24
-rw-r--r--src/github/types.h1
-rw-r--r--src/main.c8
6 files changed, 112 insertions, 2 deletions
diff --git a/src/config.c b/src/config.c
index f27e72f..f267d96 100644
--- a/src/config.c
+++ b/src/config.c
@@ -210,6 +210,18 @@ static int parse_line_inner(struct config *cfg, enum config_section section,
value);
return -1;
}
+ } else if (!strcmp(key, "transport")) {
+ if (!strcmp(value, "ssh"))
+ cfg->head->gh.transport = git_transport_ssh;
+ else if (!strcmp(value, "https"))
+ cfg->head->gh.transport = git_transport_https;
+ else {
+ fprintf(stderr,
+ "Error parsing config file: "
+ "invalid value for transport: %s\n",
+ value);
+ return -1;
+ }
} else {
fprintf(stderr,
"Error parsing config file: unknown key: %s\n",
@@ -281,6 +293,7 @@ static int parse_line(struct config *cfg, char *line,
remote->type = remote_type_github;
remote->gh.endpoint = GH_DEFAULT_ENDPOINT;
remote->gh.user_agent = DEFAULT_USER_AGENT;
+ remote->gh.transport = git_transport_https;
remote->next = cfg->head;
cfg->head = remote;
} else if (!strcmp(section_name, "srht")) {
diff --git a/src/config.h b/src/config.h
index 4d08b7a..c0beb0a 100644
--- a/src/config.h
+++ b/src/config.h
@@ -13,11 +13,18 @@
extern const char *config_locations[];
+enum git_transport {
+ git_transport_https,
+ git_transport_ssh,
+};
+
struct github_cfg {
/// Whether to skip mirroring fork repositories
int skip_forks;
/// Whether to skip mirroring private repositories
int skip_private;
+ /// Transport protocol to use for mirroring
+ enum git_transport transport;
// Borrowed
/// Github graphql API endpoint
diff --git a/src/git.c b/src/git.c
index 75f9888..4dc9c97 100644
--- a/src/git.c
+++ b/src/git.c
@@ -251,6 +251,62 @@ static int create_git_path(const struct repo_ctx *ctx)
}
/**
+ * Updates the mirror URL of the git repository at the specified path.
+ * @param path Full path to the git repository
+ * @param ctx Context containing the repository information
+ * @return 0 on success, -1 on error
+ */
+static int update_mirror_url(const char *path, const struct repo_ctx *ctx)
+{
+ // Prepare the URL
+ char *url = prepare_git_url(ctx->url, ctx->username, ctx->token);
+ if (!url) {
+ perror("prepare_git_url");
+ return -1;
+ }
+
+ const pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ return -1;
+ }
+
+ if (pid == 0) {
+ // Child process
+ char *args[8];
+ int i = 0;
+ args[i++] = "git";
+ args[i++] = "--git-dir";
+ args[i++] = (char *) path;
+ args[i++] = "remote";
+ args[i++] = "set-url";
+ args[i++] = "origin";
+ args[i++] = url;
+ args[i] = NULL;
+
+ execvp("git", args);
+ perror("execvp");
+ _exit(127); // execvp only returns on error
+ }
+ free(url);
+
+ int status;
+ pid_t result;
+ while ((result = waitpid(pid, &status, 0)) == -1 && errno == EINTR) {
+ }
+ if (result == -1) {
+ perror("waitpid");
+ return -1;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ return 0; // Success
+ fprintf(stderr, "Error: git remote set-url failed with status %d\n",
+ WEXITSTATUS(status));
+ return -1; // Error occurred
+}
+
+/**
* Updates the git repository at the specified path from the remote.
* @param path Full path to the git repository
* @param quiet Suppress output if non-zero
@@ -314,6 +370,11 @@ int git_mirror_repo(const struct repo_ctx *ctx, int quiet)
// Repo exists, so we can just update it
if (!quiet)
printf("Repo already exists, updating...\n");
+ if (update_mirror_url(path, ctx) == -1) {
+ perror("update_mirror_url");
+ ret = -1;
+ goto end;
+ }
if (update_mirror(path, quiet) == -1) {
perror("update_mirror");
ret = -1;
diff --git a/src/github/types.c b/src/github/types.c
index ce9441e..a80e8fc 100644
--- a/src/github/types.c
+++ b/src/github/types.c
@@ -119,6 +119,29 @@ int gh_list_repos_from_json(cJSON *root, struct gh_list_repos_res *res)
status = -1;
goto end;
}
+ cJSON *ssh_url_v = cJSON_GetObjectItemCaseSensitive(repo,
+ "sshUrl");
+ if (!ssh_url_v || !cJSON_IsString(ssh_url_v)) {
+ fprintf(stderr, "Error: sshUrl not found\n");
+ status = -1;
+ goto end;
+ }
+ const char *prefix = "ssh://";
+ char *ssh_url = malloc(strlen(prefix) +
+ strlen(ssh_url_v->valuestring) + 1);
+ if (!ssh_url) {
+ fprintf(stderr, "Error: malloc failed\n");
+ status = -1;
+ goto end;
+ }
+ strcpy(ssh_url, prefix);
+ strcat(ssh_url, ssh_url_v->valuestring);
+ // Replace 2nd colon with slash
+ char *colon = strchr(ssh_url + strlen(prefix), ':');
+ if (colon)
+ *colon = '/';
+
+
cJSON *is_fork = cJSON_GetObjectItemCaseSensitive(repo,
"isFork");
if (!is_fork || !cJSON_IsBool(is_fork)) {
@@ -136,6 +159,7 @@ int gh_list_repos_from_json(cJSON *root, struct gh_list_repos_res *res)
res->repos[res->repos_len].name = strdup(name->valuestring);
res->repos[res->repos_len].url = strdup(url->valuestring);
+ res->repos[res->repos_len].ssh_url = ssh_url;
res->repos[res->repos_len].is_fork = cJSON_IsTrue(is_fork);
res->repos[res->repos_len].is_private =
cJSON_IsTrue(is_private);
diff --git a/src/github/types.h b/src/github/types.h
index dec3551..fa88e27 100644
--- a/src/github/types.h
+++ b/src/github/types.h
@@ -16,6 +16,7 @@ struct gh_list_repos_res {
struct {
char *name;
char *url;
+ char *ssh_url;
int is_fork;
int is_private;
} *repos;
diff --git a/src/main.c b/src/main.c
index 44dd901..fdb8211 100644
--- a/src/main.c
+++ b/src/main.c
@@ -126,16 +126,20 @@ static int mirror_github(const char *git_base, const struct github_cfg *cfg,
continue;
}
+ const char *url = cfg->transport == git_transport_ssh
+ ? res.repos[i].ssh_url
+ : res.repos[i].url;
+
if (!quiet)
printf("Repo: %s\t%s\n", res.repos[i].name,
- res.repos[i].url);
+ url);
const struct repo_ctx repo = {
.git_base = git_base,
.owner = cfg->owner,
.token = cfg->token,
.name = res.repos[i].name,
- .url = res.repos[i].url,
+ .url = url,
.username = login,
};
if (git_mirror_repo(&repo, quiet) != 0) {