summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Anshul Gupta <ansg191@anshulg.com> 2025-04-11 12:54:54 -0700
committerGravatar Anshul Gupta <ansg191@anshulg.com> 2025-04-11 12:57:13 -0700
commitc43c233c9b14e65d461405857c6e6c758cd9fb23 (patch)
treea2dc536661010dae8ed1e3dfc2a899476b4a2779 /src
parent11f44a70736de47ec7eca82860ed6780d273749e (diff)
downloadgithub-mirror-c43c233c9b14e65d461405857c6e6c758cd9fb23.tar.gz
github-mirror-c43c233c9b14e65d461405857c6e6c758cd9fb23.tar.zst
github-mirror-c43c233c9b14e65d461405857c6e6c758cd9fb23.zip
Allow for multiple owners and tokens
Diffstat (limited to 'src')
-rw-r--r--src/config.c93
-rw-r--r--src/config.h27
-rw-r--r--src/git.c10
-rw-r--r--src/git.h3
-rw-r--r--src/main.c52
5 files changed, 112 insertions, 73 deletions
diff --git a/src/config.c b/src/config.c
index 3fd09e6..154127e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -93,22 +93,21 @@ static char *trim(char *start, char *end)
* - "ghf_"
* - "github_pat_"
* @param value token value
- * @param cfg config struct
- * @return 0 on success, -1 on error
+ * @return Owned token value or NULL on error
*/
-static int parse_token(char *value, struct config *cfg)
+static char *parse_token(char *value)
{
- char *token = value;
+ char *token = NULL;
// Open the file to check if it is a file
const int fd = open(value, O_RDONLY);
if (fd == -1) {
if (errno != ENOENT) {
perror("Error opening token file");
- return -1;
+ return NULL;
}
// Not a file, token check
- cfg->token_owned = 0;
+ token = strdup(value);
goto token_check;
}
@@ -117,7 +116,7 @@ static int parse_token(char *value, struct config *cfg)
if (!token) {
perror("Error allocating token buffer");
close(fd);
- return -1;
+ return NULL;
}
// Read the file contents
@@ -126,7 +125,7 @@ static int parse_token(char *value, struct config *cfg)
perror("Error reading token file");
free(token);
close(fd);
- return -1;
+ return NULL;
}
token[bytes_read] = '\0';
@@ -136,24 +135,21 @@ static int parse_token(char *value, struct config *cfg)
if (!tmp) {
perror("Error allocating token buffer");
close(fd);
- return -1;
+ return NULL;
}
token = tmp;
- cfg->token_owned = 1;
close(fd);
token_check:
if (!strncmp(token, "ghp_", 4) || !strncmp(token, "gho_", 4) ||
!strncmp(token, "ghu_", 4) || !strncmp(token, "ghs_", 4) ||
!strncmp(token, "ghf_", 4) || !strncmp(token, "github_pat_", 11)) {
- cfg->token = token;
- } else {
- fprintf(stderr, "Error: invalid token format: %s\n", token);
- free(token);
- return -1;
+ return token;
}
- return 0;
+ fprintf(stderr, "Error: invalid token format: %s\n", token);
+ free(token);
+ return NULL;
}
static int parse_line_inner(struct config *cfg, enum config_section section,
@@ -167,14 +163,13 @@ static int parse_line_inner(struct config *cfg, enum config_section section,
return -1;
case section_github:
if (!strcmp(key, "endpoint"))
- cfg->endpoint = value;
+ cfg->head->endpoint = value;
else if (!strcmp(key, "token")) {
- if (parse_token(value, cfg) < 0)
- return -1;
+ cfg->head->token = parse_token(value);
} else if (!strcmp(key, "user_agent"))
- cfg->user_agent = value;
+ cfg->head->user_agent = value;
else if (!strcmp(key, "owner"))
- cfg->owner = value;
+ cfg->head->owner = value;
else {
fprintf(stderr,
"Error parsing config file: unknown key: %s\n",
@@ -217,9 +212,20 @@ static int parse_line(struct config *cfg, char *line,
}
*close = '\0';
char *section_name = trim(line + 1, close);
- if (!strcmp(section_name, "github"))
+ if (!strcmp(section_name, "github")) {
*section = section_github;
- else if (!strcmp(section_name, "git"))
+
+ // Add the new owner to the list
+ struct github_cfg *owner = calloc(1, sizeof(*owner));
+ if (!owner) {
+ perror("Error allocating owner");
+ return -1;
+ }
+ owner->endpoint = GH_DEFAULT_ENDPOINT;
+ owner->user_agent = GH_DEFAULT_USER_AGENT;
+ owner->next = cfg->head;
+ cfg->head = owner;
+ } else if (!strcmp(section_name, "git"))
*section = section_git;
else {
fprintf(stderr,
@@ -282,24 +288,23 @@ static int config_parse(struct config *cfg)
return 0;
}
-static void config_defaults(struct config *cfg)
-{
- cfg->endpoint = GH_DEFAULT_ENDPOINT;
- cfg->user_agent = GH_DEFAULT_USER_AGENT;
- cfg->git_base = "/srv/git";
-}
+static void config_defaults(struct config *cfg) { cfg->git_base = "/srv/git"; }
static int config_validate(const struct config *cfg)
{
- if (!cfg->token) {
- fprintf(stderr,
- "Error: missing required field: github.token\n");
- return -1;
- }
- if (!cfg->owner) {
- fprintf(stderr,
- "Error: missing required field: github.owner\n");
- return -1;
+ const struct github_cfg *owner = cfg->head;
+ while (owner) {
+ if (!owner->token) {
+ fprintf(stderr, "Error: missing required field: "
+ "github.token\n");
+ return -1;
+ }
+ if (!owner->owner) {
+ fprintf(stderr, "Error: missing required field: "
+ "github.owner\n");
+ return -1;
+ }
+ owner = owner->next;
}
return 0;
}
@@ -341,7 +346,15 @@ void config_free(struct config *config)
if (!config || !config->contents)
return;
munmap(config->contents, config->contents_len);
- if (config->token_owned)
- free((char *) config->token);
+
+ // Free the github_cfg list
+ struct github_cfg *owner = config->head;
+ while (owner) {
+ struct github_cfg *next = owner->next;
+ free((char *) owner->token);
+ free(owner);
+ owner = next;
+ }
+
free(config);
}
diff --git a/src/config.h b/src/config.h
index ece19d9..13f2649 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,20 +12,29 @@
extern const char *config_locations[];
+struct github_cfg {
+ // Borrowed
+ /// Github graphql API endpoint
+ const char *endpoint;
+ /// Client user agent
+ const char *user_agent;
+ /// The owner of the repositories
+ const char *owner;
+
+ // Owned
+ /// Github auth token
+ const char *token;
+ /// Next owner in the list
+ struct github_cfg *next;
+};
+
struct config {
/// The content of the config file
char *contents;
size_t contents_len;
- const char *token;
- /// Whether the token's memory is owned or borrowed from contents
- int token_owned;
-
- const char *endpoint;
- const char *user_agent;
-
- /// The owner of the repositories
- const char *owner;
+ /// Repo owners to mirror
+ struct github_cfg *head;
/// The filepath to the git mirrors
/// Default: /srv/git
diff --git a/src/git.c b/src/git.c
index 7289362..8d5673f 100644
--- a/src/git.c
+++ b/src/git.c
@@ -207,8 +207,7 @@ static int create_mirror(const char *path, const struct repo_ctx *ctx)
static int create_git_path(const struct repo_ctx *ctx)
{
// Create owner directory if it doesn't exist
- char *owner_path =
- get_git_path(ctx->cfg->git_base, ctx->cfg->owner, NULL);
+ char *owner_path = get_git_path(ctx->git_base, ctx->cfg->owner, NULL);
if (!owner_path)
return -1;
if (mkdir(owner_path, 0755) == -1 && errno != EEXIST) {
@@ -219,8 +218,8 @@ static int create_git_path(const struct repo_ctx *ctx)
free(owner_path);
// Create repo directory if it doesn't exist
- char *repo_path = get_git_path(ctx->cfg->git_base, ctx->cfg->owner,
- ctx->name);
+ char *repo_path =
+ get_git_path(ctx->git_base, ctx->cfg->owner, ctx->name);
if (!repo_path)
return -1;
if (mkdir(repo_path, 0755) == -1 && errno != EEXIST) {
@@ -275,8 +274,7 @@ static int update_mirror(const char *path)
int git_mirror_repo(const struct repo_ctx *ctx)
{
int ret = 0;
- char *path = get_git_path(ctx->cfg->git_base, ctx->cfg->owner,
- ctx->name);
+ char *path = get_git_path(ctx->git_base, ctx->cfg->owner, ctx->name);
if (!path) {
perror("get_git_path");
return -1;
diff --git a/src/git.h b/src/git.h
index 1240b0f..ad3902d 100644
--- a/src/git.h
+++ b/src/git.h
@@ -8,7 +8,8 @@
#include "config.h"
struct repo_ctx {
- const struct config *cfg;
+ const char *git_base;
+ const struct github_cfg *cfg;
/// Name of the repo
const char *name;
diff --git a/src/main.c b/src/main.c
index db11d5a..64c369f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -63,21 +63,8 @@ static int load_config(int argc, char **argv, struct config **cfg_out)
return 1;
}
-int main(int argc, char **argv)
+static int mirror_owner(const char *git_base, const struct github_cfg *cfg)
{
- curl_global_init(CURL_GLOBAL_DEFAULT);
-
- struct config *cfg = NULL;
- const int ret = load_config(argc, argv, &cfg);
- if (ret != 0 || !cfg)
- return ret;
-
- if (precheck_self(cfg)) {
- fprintf(stderr, "Precheck failed\n");
- config_free(cfg);
- return 1;
- }
-
const struct github_ctx ctx = {
.endpoint = cfg->endpoint,
.token = cfg->token,
@@ -98,13 +85,14 @@ int main(int argc, char **argv)
do {
if (github_client_list_user_repos(client, cfg->owner,
end_cursor, &res))
- return 1;
+ return -1;
for (size_t i = 0; i < res.repos_len; i++) {
printf("Repo: %s\t%s\n", res.repos[i].name,
res.repos[i].url);
const struct repo_ctx repo = {
+ .git_base = git_base,
.cfg = cfg,
.name = res.repos[i].name,
.url = res.repos[i].url,
@@ -112,7 +100,7 @@ int main(int argc, char **argv)
};
if (git_mirror_repo(&repo) != 0) {
fprintf(stderr, "Failed to mirror repo\n");
- status = 1;
+ status = -1;
break;
}
}
@@ -124,8 +112,38 @@ int main(int argc, char **argv)
} while (res.has_next_page);
free(end_cursor);
- github_client_free(client);
free(login);
+ github_client_free(client);
+ return status;
+}
+
+int main(int argc, char **argv)
+{
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+
+ struct config *cfg = NULL;
+ const int ret = load_config(argc, argv, &cfg);
+ if (ret != 0 || !cfg)
+ return ret;
+
+ if (precheck_self(cfg)) {
+ fprintf(stderr, "Precheck failed\n");
+ config_free(cfg);
+ return 1;
+ }
+
+ int status = 0;
+ struct github_cfg *owner = cfg->head;
+ while (owner) {
+ printf("Mirroring owner: %s\n", owner->owner);
+ if (mirror_owner(cfg->git_base, owner)) {
+ fprintf(stderr, "Failed to mirror owner: %s\n",
+ owner->owner);
+ status = 1;
+ }
+ owner = owner->next;
+ }
+
config_free(cfg);
curl_global_cleanup();
return status;