Download raw body.
gotd: allow repository directives in global scope
Hello,
I just started playing with got{,d} for the first time and after
wrapping my head around the fact that the repository needs to live
outside the working tree I'm starting to like it.
In setting up a gotd server, I quickly became bored with the copy-paste
work of the different per repository settings.
Diff below allows to declare repository directives in the global scope
and are stored in a repo_template. If a directive is not specified in
the repository it will be copied over from the template. The path
directive will be used as a root-directory and the name of the
repository will be appended to complete the per repository path.
thoughts?
martijn@
diff /home/martijn/src/got
commit - 14eb0fefd04d63b1a8d626e72c953a811a403f7d
path + /home/martijn/src/got
blob - 09928aa29395cb1acfaff6303c26cda1adfaf34a
file + gotd/gotd.conf.5
--- gotd/gotd.conf.5
+++ gotd/gotd.conf.5
@@ -143,6 +143,8 @@ may contain path-separators,
.Dq / ,
to expose repositories as part of a virtual client-visible directory hierarchy.
.Pp
+The repository directives can be specified in the global scope, which can be
+overwritten per repository.
The available repository configuration directives are as follows:
.Bl -tag -width Ds
.It Ic deny Ar identity
@@ -155,7 +157,13 @@ to
Numeric IDs are also accepted.
.It Ic path Ar path
Set the path to the Git repository.
-Must be specified.
+If set in the global scope this will be used as the root directory and the
+.Ic repository
+.Ar name
+will be appended for the full path.
+A
+.Ar path
+must be specified.
.It Ic permit Ar mode Ar identity
Permit repository access to users with the username
.Ar identity .
blob - acb40dee8cd351b48669c3c3247c42ed5f44501b
file + gotd/gotd.h
--- gotd/gotd.h
+++ gotd/gotd.h
@@ -76,6 +76,9 @@ struct gotd_repo {
char name[NAME_MAX];
char path[PATH_MAX];
+ int rules_set;
+ int protect_set;
+
struct gotd_access_rule_list rules;
struct got_pathlist_head protected_tag_namespaces;
struct got_pathlist_head protected_branch_namespaces;
@@ -119,6 +122,7 @@ struct gotd {
char user_name[32];
struct gotd_repolist repos;
int nrepos;
+ struct gotd_repo repo_template;
struct gotd_child_proc *listen_proc;
struct timeval request_timeout;
struct timeval auth_timeout;
blob - cc7231514a823861b662e419be141a4492c833fd
file + gotd/parse.y
--- gotd/parse.y
+++ gotd/parse.y
@@ -91,6 +91,9 @@ static int errors;
static struct gotd *gotd;
static struct gotd_repo *new_repo;
static int conf_limit_user_connections(const char *, int);
+static void conf_repo_fill_from_template(
+ struct gotd_repo *, struct gotd_repo *);
+static void conf_repo_init(struct gotd_repo *);
static struct gotd_repo *conf_new_repo(const char *);
static void conf_new_access_rule(struct gotd_repo *,
enum gotd_access, int, char *);
@@ -117,7 +120,7 @@ typedef struct {
%token PATH ERROR LISTEN ON USER REPOSITORY PERMIT DENY
%token RO RW CONNECTION LIMIT REQUEST TIMEOUT
-%token PROTECT NAMESPACE BRANCH TAG
+%token PROTECT NAMESPACE BRANCH TAG NONE
%token <v.string> STRING
%token <v.number> NUMBER
@@ -213,6 +216,7 @@ main : LISTEN ON STRING {
free($2);
}
| connection
+ | repoopts1
;
connection : CONNECTION '{' optnl conflags_l '}'
@@ -240,8 +244,19 @@ conflags : REQUEST TIMEOUT timeout {
}
;
-protect : PROTECT '{' optnl protectflags_l '}'
- | PROTECT protectflags
+protect : PROTECT '{' optnl protectflags_l '}' {
+ new_repo->protect_set = 1;
+ }
+ | PROTECT protectflags {
+ new_repo->protect_set = 1;
+ }
+ | PROTECT {
+ new_repo->protect_set = 1;
+ }
+ | PROTECT '{' optnl '}' {
+ new_repo->protect_set = 1;
+ }
+ ;
protectflags_l : protectflags optnl protectflags_l
| protectflags optnl
@@ -298,7 +313,8 @@ repository : REPOSITORY STRING {
new_repo = conf_new_repo($2);
}
free($2);
- } '{' optnl repoopts2 '}' {
+ } repoopts3 {
+ new_repo = &gotd->repo_template;
}
;
@@ -372,6 +388,11 @@ repoopts2 : repoopts2 repoopts1 nl
| repoopts1 optnl
;
+repoopts3 : '{' optnl repoopts2 '}'
+ | '{' optnl '}'
+ | /* empty */
+ ;
+
nl : '\n' optnl
;
@@ -765,6 +786,8 @@ parse_config(const char *filename, enum gotd_procid pr
gotd = env;
gotd_proc_id = proc_id;
TAILQ_INIT(&gotd->repos);
+ conf_repo_init(&gotd->repo_template);
+ new_repo = &gotd->repo_template;
/* Apply default values. */
if (strlcpy(gotd->unix_socket_path, GOTD_UNIX_SOCKET,
@@ -806,6 +829,7 @@ parse_config(const char *filename, enum gotd_procid pr
return (-1);
TAILQ_FOREACH(repo, &gotd->repos, entry) {
+ conf_repo_fill_from_template(repo, &gotd->repo_template);
if (repo->path[0] == '\0') {
log_warnx("repository \"%s\": no path provided in "
"configuration file", repo->name);
@@ -879,6 +903,79 @@ conf_limit_user_connections(const char *user, int maxi
return 0;
}
+static void
+conf_repo_fill_from_template(struct gotd_repo *repo, struct gotd_repo *template)
+{
+ struct gotd_access_rule *rule;
+ const struct got_error *error;
+ struct got_pathlist_entry *pe, *npe;
+ char *s;
+
+ if (repo->path[0] == '\0' && template->path[0] != '\0') {
+ if (snprintf(repo->path, sizeof(repo->path), "%s/%s",
+ template->path, repo->name) >= sizeof(repo->path)) {
+ log_warnx("repository \"%s\": %s", repo->name,
+ strerror(ENAMETOOLONG));
+ repo->path[0] = '\0';
+ }
+ }
+ if (!repo->rules_set) {
+ STAILQ_FOREACH(rule, &template->rules, entry) {
+ s = strdup(rule->identifier);
+ if (s == NULL)
+ fatal("strdup");
+ conf_new_access_rule(repo, rule->access,
+ rule->authorization, s);
+ }
+ }
+ if (!repo->protect_set) {
+ TAILQ_FOREACH(pe, &template->protected_tag_namespaces, entry) {
+ s = strdup(pe->path);
+ if (s == NULL)
+ fatal("strdup");
+ error = got_pathlist_insert(&npe,
+ &repo->protected_tag_namespaces, s, NULL);
+ if (error || pe == NULL) {
+ /* duplicate shouldn't be possible */
+ fatalx("got_pathlist_insert: %s",
+ error == NULL ? "???" : error->msg);
+ }
+ }
+ TAILQ_FOREACH(pe,
+ &template->protected_branch_namespaces, entry) {
+ s = strdup(pe->path);
+ if (s == NULL)
+ fatal("strdup");
+ error = got_pathlist_insert(&npe,
+ &repo->protected_branch_namespaces, s, NULL);
+ if (error || pe == NULL) {
+ fatalx("got_pathlist_insert: %s",
+ error == NULL ? "???" : error->msg);
+ }
+ }
+ TAILQ_FOREACH(pe, &template->protected_branches, entry) {
+ s = strdup(pe->path);
+ if (s == NULL)
+ fatal("strdup");
+ error = got_pathlist_insert(&npe,
+ &repo->protected_branches, s, NULL);
+ if (error || pe == NULL) {
+ fatalx("got_pathlist_insert: %s",
+ error == NULL ? "???" : error->msg);
+ }
+ }
+ }
+}
+
+static void
+conf_repo_init(struct gotd_repo *repo)
+{
+ STAILQ_INIT(&repo->rules);
+ TAILQ_INIT(&repo->protected_tag_namespaces);
+ TAILQ_INIT(&repo->protected_branch_namespaces);
+ TAILQ_INIT(&repo->protected_branches);
+}
+
static struct gotd_repo *
conf_new_repo(const char *name)
{
@@ -896,10 +993,7 @@ conf_new_repo(const char *name)
if (repo == NULL)
fatalx("%s: calloc", __func__);
- STAILQ_INIT(&repo->rules);
- TAILQ_INIT(&repo->protected_tag_namespaces);
- TAILQ_INIT(&repo->protected_branch_namespaces);
- TAILQ_INIT(&repo->protected_branches);
+ conf_repo_init(repo);
if (strlcpy(repo->name, name, sizeof(repo->name)) >=
sizeof(repo->name))
@@ -926,6 +1020,8 @@ conf_new_access_rule(struct gotd_repo *repo, enum gotd
rule->identifier = identifier;
STAILQ_INSERT_TAIL(&repo->rules, rule, entry);
+
+ repo->rules_set = 1;
}
static int
gotd: allow repository directives in global scope