diff options
author | 2025-04-11 12:54:54 -0700 | |
---|---|---|
committer | 2025-04-11 12:57:13 -0700 | |
commit | c43c233c9b14e65d461405857c6e6c758cd9fb23 (patch) | |
tree | a2dc536661010dae8ed1e3dfc2a899476b4a2779 /src | |
parent | 11f44a70736de47ec7eca82860ed6780d273749e (diff) | |
download | github-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.c | 93 | ||||
-rw-r--r-- | src/config.h | 27 | ||||
-rw-r--r-- | src/git.c | 10 | ||||
-rw-r--r-- | src/git.h | 3 | ||||
-rw-r--r-- | src/main.c | 52 |
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 @@ -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; @@ -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; @@ -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; |