From bcdf221ca724cba7c9126b3d9eb6125987ec7699 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 28 Jun 2024 18:24:40 +0000 Subject: [PATCH 01/50] Update version for new devel version Signed-off-by: Nico Sonack --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 429e3463..7faaf71d 100755 --- a/configure +++ b/configure @@ -4,7 +4,7 @@ CONFIGURE_CMD_ARGS="${*}" -PACKAGE_VERSION="2.4.0-devel" +PACKAGE_VERSION="2.5.0-devel" PACKAGE_DATE="UNRELEASED" PACKAGE_STRING="gcli $PACKAGE_VERSION" PACKAGE_BUGREPORT="https://lists.sr.ht/~herrhotzenplotz/gcli-discuss" From 04f49f0f92bc797032ddf5e6b351bccd18798d74 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 17:29:46 +0200 Subject: [PATCH 02/50] changelog: add new unreleased version Signed-off-by: Nico Sonack --- Changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Changelog.md b/Changelog.md index eb53925e..6c8f306a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,16 @@ This changelog does not follow semantic versioning. +## UNRELEASED + +### Added + +### Fixed + +### Changed + +### Removed + ## 2.4.0 (2024-June-28) ### Added From d485941ee8bac9f1c233dc0f49df935792c9750c Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:04 +0200 Subject: [PATCH 03/50] ui: print comment ids in comment list Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- src/cmd/comment.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cmd/comment.c b/src/cmd/comment.c index be72e765..8f258117 100644 --- a/src/cmd/comment.c +++ b/src/cmd/comment.c @@ -166,9 +166,11 @@ gcli_print_comment_list(struct gcli_comment_list const *const list) { for (size_t i = 0; i < list->comments_size; ++i) { printf("AUTHOR : %s%s%s\n" - "DATE : %s\n", + "DATE : %s\n" + "ID : %"PRIid"\n", gcli_setbold(), list->comments[i].author, gcli_resetbold(), - list->comments[i].date); + list->comments[i].date, + list->comments[i].id); pretty_print(list->comments[i].body, 9, 80, stdout); putchar('\n'); } From 53b0207b2907b8c5ef8f972088c510b6c9e09445 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:05 +0200 Subject: [PATCH 04/50] comments: const-ptrify submit options struct This allows for better optimisations due to avoided copy through the stack. Also, it aligns better with our code style. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/comments.h | 2 +- include/gcli/forges.h | 2 +- include/gcli/gitea/comments.h | 2 +- include/gcli/github/comments.h | 2 +- include/gcli/gitlab/comments.h | 2 +- src/cmd/comment.c | 2 +- src/comments.c | 2 +- src/gitea/comments.c | 3 ++- src/github/comments.c | 10 +++++----- src/gitlab/comments.c | 13 +++++++------ src/gitlab/merge_requests.c | 4 ++-- 11 files changed, 23 insertions(+), 21 deletions(-) diff --git a/include/gcli/comments.h b/include/gcli/comments.h index f82b6363..1cc29eca 100644 --- a/include/gcli/comments.h +++ b/include/gcli/comments.h @@ -72,6 +72,6 @@ int gcli_get_pull_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, struct gcli_comment_list *out); -int gcli_comment_submit(struct gcli_ctx *ctx, struct gcli_submit_comment_opts opts); +int gcli_comment_submit(struct gcli_ctx *ctx, struct gcli_submit_comment_opts const *opts); #endif /* COMMENTS_H */ diff --git a/include/gcli/forges.h b/include/gcli/forges.h index 2e191261..4fc8890e 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -59,7 +59,7 @@ struct gcli_forge_descriptor { * Submit a comment to a pull/mr or issue */ int (*perform_submit_comment)( struct gcli_ctx *ctx, - struct gcli_submit_comment_opts opts); + struct gcli_submit_comment_opts const *opts); /** * List comments on the given issue */ diff --git a/include/gcli/gitea/comments.h b/include/gcli/gitea/comments.h index 04c0f03c..56ec3367 100644 --- a/include/gcli/gitea/comments.h +++ b/include/gcli/gitea/comments.h @@ -41,6 +41,6 @@ int gitea_get_comments(struct gcli_ctx *ctx, char const *owner, char const *repo gcli_id issue, struct gcli_comment_list *out); int gitea_perform_submit_comment(struct gcli_ctx *ctx, - struct gcli_submit_comment_opts opts); + struct gcli_submit_comment_opts const *opts); #endif /* GITEA_COMMENTS_H */ diff --git a/include/gcli/github/comments.h b/include/gcli/github/comments.h index dc1a6c53..2325b663 100644 --- a/include/gcli/github/comments.h +++ b/include/gcli/github/comments.h @@ -38,7 +38,7 @@ #include int github_perform_submit_comment(struct gcli_ctx *ctx, - struct gcli_submit_comment_opts opts); + struct gcli_submit_comment_opts const *opts); int github_get_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, struct gcli_comment_list *out); diff --git a/include/gcli/gitlab/comments.h b/include/gcli/gitlab/comments.h index c886519d..3f71dbbf 100644 --- a/include/gcli/gitlab/comments.h +++ b/include/gcli/gitlab/comments.h @@ -38,7 +38,7 @@ #include int gitlab_perform_submit_comment(struct gcli_ctx *ctx, - struct gcli_submit_comment_opts opts); + struct gcli_submit_comment_opts const *opts); int gitlab_get_issue_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, diff --git a/src/cmd/comment.c b/src/cmd/comment.c index 8f258117..c987e0f4 100644 --- a/src/cmd/comment.c +++ b/src/cmd/comment.c @@ -121,7 +121,7 @@ comment_submit(struct gcli_submit_comment_opts opts, int always_yes) errx(1, "Aborted by user"); } - rc = gcli_comment_submit(g_clictx, opts); + rc = gcli_comment_submit(g_clictx, &opts); free(message); opts.message = NULL; diff --git a/src/comments.c b/src/comments.c index 169f228c..bbbbf1e0 100644 --- a/src/comments.c +++ b/src/comments.c @@ -70,7 +70,7 @@ gcli_get_pull_comments(struct gcli_ctx *ctx, char const *owner, char const *repo } int -gcli_comment_submit(struct gcli_ctx *ctx, struct gcli_submit_comment_opts opts) +gcli_comment_submit(struct gcli_ctx *ctx, struct gcli_submit_comment_opts const *const opts) { gcli_null_check_call(perform_submit_comment, ctx, opts); } diff --git a/src/gitea/comments.c b/src/gitea/comments.c index 36e18368..ecd02287 100644 --- a/src/gitea/comments.c +++ b/src/gitea/comments.c @@ -38,7 +38,8 @@ gitea_get_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, } int -gitea_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_opts opts) +gitea_perform_submit_comment(struct gcli_ctx *ctx, + struct gcli_submit_comment_opts const *const opts) { return github_perform_submit_comment(ctx, opts); } diff --git a/src/github/comments.c b/src/github/comments.c index 6b2f9698..a17865bb 100644 --- a/src/github/comments.c +++ b/src/github/comments.c @@ -38,19 +38,19 @@ int github_perform_submit_comment(struct gcli_ctx *ctx, - struct gcli_submit_comment_opts opts) + struct gcli_submit_comment_opts const *const opts) { int rc = 0; struct gcli_jsongen gen = {0}; - char *e_owner = gcli_urlencode(opts.owner); - char *e_repo = gcli_urlencode(opts.repo); + char *e_owner = gcli_urlencode(opts->owner); + char *e_repo = gcli_urlencode(opts->repo); char *payload = NULL, *url = NULL; gcli_jsongen_init(&gen); gcli_jsongen_begin_object(&gen); { gcli_jsongen_objmember(&gen, "body"); - gcli_jsongen_string(&gen, opts.message); + gcli_jsongen_string(&gen, opts->message); } gcli_jsongen_end_object(&gen); @@ -58,7 +58,7 @@ github_perform_submit_comment(struct gcli_ctx *ctx, gcli_jsongen_free(&gen); url = sn_asprintf("%s/repos/%s/%s/issues/%"PRIid"/comments", - gcli_get_apibase(ctx), e_owner, e_repo, opts.target_id); + gcli_get_apibase(ctx), e_owner, e_repo, opts->target_id); rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, NULL); diff --git a/src/gitlab/comments.c b/src/gitlab/comments.c index 22885c43..fde9fd9e 100644 --- a/src/gitlab/comments.c +++ b/src/gitlab/comments.c @@ -35,17 +35,18 @@ #include int -gitlab_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_opts opts) +gitlab_perform_submit_comment(struct gcli_ctx *ctx, + struct gcli_submit_comment_opts const *const opts) { char *url = NULL, *payload = NULL, *e_owner = NULL, *e_repo = NULL; char const *type = NULL; struct gcli_jsongen gen = {0}; int rc = 0; - e_owner = gcli_urlencode(opts.owner); - e_repo = gcli_urlencode(opts.repo); + e_owner = gcli_urlencode(opts->owner); + e_repo = gcli_urlencode(opts->repo); - switch (opts.target_type) { + switch (opts->target_type) { case ISSUE_COMMENT: type = "issues"; break; @@ -58,7 +59,7 @@ gitlab_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_o gcli_jsongen_begin_object(&gen); { gcli_jsongen_objmember(&gen, "body"); - gcli_jsongen_string(&gen, opts.message); + gcli_jsongen_string(&gen, opts->message); } gcli_jsongen_end_object(&gen); @@ -67,7 +68,7 @@ gitlab_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_o url = sn_asprintf("%s/projects/%s%%2F%s/%s/%"PRIid"/notes", gcli_get_apibase(ctx), e_owner, e_repo, type, - opts.target_id); + opts->target_id); rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, NULL); diff --git a/src/gitlab/merge_requests.c b/src/gitlab/merge_requests.c index 325bb336..6eff06bf 100644 --- a/src/gitlab/merge_requests.c +++ b/src/gitlab/merge_requests.c @@ -1147,7 +1147,7 @@ post_diff_comment(struct gcli_ctx *ctx, int gitlab_mr_create_review(struct gcli_ctx *ctx, - struct gcli_pull_create_review_details const *details) + struct gcli_pull_create_review_details const *const details) { int rc; struct gcli_diff_comment const *comment; @@ -1172,7 +1172,7 @@ gitlab_mr_create_review(struct gcli_ctx *ctx, .message = details->body, }; - rc = gitlab_perform_submit_comment(ctx, opts); + rc = gitlab_perform_submit_comment(ctx, &opts); if (rc < 0) return rc; } From 4cfd1917623e8fac5e00256ee9445b1d52f52c4d Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:06 +0200 Subject: [PATCH 05/50] comments: add routine for fetching a specific issue comment Also adds an implementation for GitLab as I am currently testing with it. This will in the end be used for replies to comments. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/comments.h | 4 +++ include/gcli/forges.h | 10 ++++++++ include/gcli/gitlab/comments.h | 4 +++ src/comments.c | 8 ++++++ src/forges.c | 1 + src/gitlab/comments.c | 47 ++++++++++++++++++++++++++++++++++ 6 files changed, 74 insertions(+) diff --git a/include/gcli/comments.h b/include/gcli/comments.h index 1cc29eca..b91b555d 100644 --- a/include/gcli/comments.h +++ b/include/gcli/comments.h @@ -64,6 +64,10 @@ void gcli_comments_free(struct gcli_comment_list *list); void gcli_comment_free(struct gcli_comment *const it); +int gcli_get_issue_comment(struct gcli_ctx *ctx, char const *owner, char const *repo, + gcli_id issue_id, gcli_id comment_id, + struct gcli_comment *out); + int gcli_get_issue_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, struct gcli_comment_list *out); diff --git a/include/gcli/forges.h b/include/gcli/forges.h index 4fc8890e..2a0e700d 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -79,6 +79,16 @@ struct gcli_forge_descriptor { gcli_id pr, struct gcli_comment_list *out); + /** + * Get a specific comment on an issue with the given ID */ + int (*get_issue_comment)( + struct gcli_ctx *ctx, + char const *owner, + char const *repo, + gcli_id issue_id, + gcli_id comment_id, + struct gcli_comment *out); + /** * List forks of the given repo */ int (*get_forks)( diff --git a/include/gcli/gitlab/comments.h b/include/gcli/gitlab/comments.h index 3f71dbbf..7cadc6a7 100644 --- a/include/gcli/gitlab/comments.h +++ b/include/gcli/gitlab/comments.h @@ -40,6 +40,10 @@ int gitlab_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_opts const *opts); +int gitlab_get_issue_comment(struct gcli_ctx *ctx, char const *owner, + char const *repo, gcli_id issue_id, + gcli_id comment_id, struct gcli_comment *out); + int gitlab_get_issue_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, struct gcli_comment_list *out); diff --git a/src/comments.c b/src/comments.c index bbbbf1e0..54c1337b 100644 --- a/src/comments.c +++ b/src/comments.c @@ -55,6 +55,14 @@ gcli_comments_free(struct gcli_comment_list *const list) list->comments_size = 0; } +int +gcli_get_issue_comment(struct gcli_ctx *ctx, char const *const owner, + char const *repo, gcli_id const issue_id, + gcli_id const comment_id, struct gcli_comment *const out) +{ + gcli_null_check_call(get_issue_comment, ctx, owner, repo, issue_id, comment_id, out); +} + int gcli_get_issue_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id const issue, struct gcli_comment_list *out) diff --git a/src/forges.c b/src/forges.c index 5aa2bc04..b1194ba6 100644 --- a/src/forges.c +++ b/src/forges.c @@ -178,6 +178,7 @@ static struct gcli_forge_descriptor const gitlab_forge_descriptor = { /* Comments */ + .get_issue_comment = gitlab_get_issue_comment, .get_issue_comments = gitlab_get_issue_comments, .get_pull_comments = gitlab_get_mr_comments, .perform_submit_comment = gitlab_perform_submit_comment, diff --git a/src/gitlab/comments.c b/src/gitlab/comments.c index fde9fd9e..403d4dee 100644 --- a/src/gitlab/comments.c +++ b/src/gitlab/comments.c @@ -149,3 +149,50 @@ gitlab_get_issue_comments(struct gcli_ctx *ctx, char const *owner, return gitlab_fetch_comments(ctx, url, out); } + +static int +gitlab_fetch_comment(struct gcli_ctx *ctx, char const *const url, + struct gcli_comment *const out) +{ + int rc = 0; + struct gcli_fetch_buffer buffer = {0}; + struct json_stream stream = {0}; + + rc = gcli_fetch(ctx, url, NULL, &buffer); + if (rc < 0) + return rc; + + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_gitlab_comment(ctx, &stream, out); + json_close(&stream); + + gcli_fetch_buffer_free(&buffer); + + return rc; +} + +int +gitlab_get_issue_comment(struct gcli_ctx *ctx, char const *const owner, + char const *const repo, gcli_id const issue_id, + gcli_id const comment_id, + struct gcli_comment *const out) +{ + char *url, *e_owner, *e_repo; + int rc; + + e_owner = gcli_urlencode(owner); + e_repo = gcli_urlencode(repo); + + url = sn_asprintf("%s/projects/%s%%2F%s/issues/%"PRIid"/notes/%"PRIid, + gcli_get_apibase(ctx), e_owner, e_repo, issue_id, + comment_id); + + free(e_owner); + free(e_repo); + + rc = gitlab_fetch_comment(ctx, url, out); + + free(url); + + return rc; +} From 76b22bbb9926c682851ff320ab7e1e92307cb805 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:07 +0200 Subject: [PATCH 06/50] comments: add new reply-to option When specifying -R to the comment subcommand this will now fetch the comment and put it into the file prefixed with '> ' such that one can easily reply to it. Currently it only works with issues and merge requests on GitLab but I will extend it for other use cases too. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/comments.h | 6 +- include/gcli/forges.h | 7 ++- include/gcli/gitlab/comments.h | 7 ++- src/cmd/comment.c | 101 ++++++++++++++++++++++++--------- src/comments.c | 9 +-- src/forges.c | 2 +- src/gitlab/comments.c | 24 +++++--- 7 files changed, 107 insertions(+), 49 deletions(-) diff --git a/include/gcli/comments.h b/include/gcli/comments.h index b91b555d..4e508cd5 100644 --- a/include/gcli/comments.h +++ b/include/gcli/comments.h @@ -64,9 +64,9 @@ void gcli_comments_free(struct gcli_comment_list *list); void gcli_comment_free(struct gcli_comment *const it); -int gcli_get_issue_comment(struct gcli_ctx *ctx, char const *owner, char const *repo, - gcli_id issue_id, gcli_id comment_id, - struct gcli_comment *out); +int gcli_get_comment(struct gcli_ctx *ctx, char const *owner, char const *repo, + enum comment_target_type target_type, gcli_id target_id, + gcli_id comment_id, struct gcli_comment *out); int gcli_get_issue_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, diff --git a/include/gcli/forges.h b/include/gcli/forges.h index 2a0e700d..420f562b 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -80,12 +80,13 @@ struct gcli_forge_descriptor { struct gcli_comment_list *out); /** - * Get a specific comment on an issue with the given ID */ - int (*get_issue_comment)( + * Get a specific comment on an issue or pull request with the given ID */ + int (*get_comment)( struct gcli_ctx *ctx, char const *owner, char const *repo, - gcli_id issue_id, + enum comment_target_type target_type, + gcli_id target_id, gcli_id comment_id, struct gcli_comment *out); diff --git a/include/gcli/gitlab/comments.h b/include/gcli/gitlab/comments.h index 7cadc6a7..68932eb9 100644 --- a/include/gcli/gitlab/comments.h +++ b/include/gcli/gitlab/comments.h @@ -40,9 +40,10 @@ int gitlab_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_opts const *opts); -int gitlab_get_issue_comment(struct gcli_ctx *ctx, char const *owner, - char const *repo, gcli_id issue_id, - gcli_id comment_id, struct gcli_comment *out); +int gitlab_get_comment(struct gcli_ctx *ctx, char const *owner, + char const *repo, enum comment_target_type target_type, + gcli_id issue_id, gcli_id comment_id, + struct gcli_comment *out); int gitlab_get_issue_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, diff --git a/src/cmd/comment.c b/src/cmd/comment.c index c987e0f4..9a1b63e4 100644 --- a/src/cmd/comment.c +++ b/src/cmd/comment.c @@ -52,18 +52,42 @@ usage(void) fprintf(stderr, " -r repo The repository name\n"); fprintf(stderr, " -p pr PR id to comment under\n"); fprintf(stderr, " -i issue issue id to comment under\n"); + fprintf(stderr, " -R comment-id Reply to the comment with the given ID\n"); fprintf(stderr, " -y Do not ask for confirmation\n"); version(); copyright(); } +struct submit_ctx { + struct gcli_submit_comment_opts opts; + struct gcli_comment reply_comment; +}; + +void +gcli_print_prefixed(FILE *f, char const *const text, char const *const prefix) +{ + char const *bol = text; + + while (bol) { + char const *const eol = strchr(bol, '\n'); + size_t const len = eol ? (size_t)(eol - bol) : strlen(bol); + + fprintf(f, "%s%.*s\n", prefix, (int)len, bol); + + if (!eol) + break; + + bol = eol + 1; + } +} + static void comment_init(struct gcli_ctx *ctx, FILE *f, void *_data) { - struct gcli_submit_comment_opts *info = _data; + struct submit_ctx *sctx = _data; const char *target_type = NULL; - switch (info->target_type) { + switch (sctx->opts.target_type) { case ISSUE_COMMENT: target_type = "issue"; break; @@ -84,29 +108,34 @@ comment_init(struct gcli_ctx *ctx, FILE *f, void *_data) } break; } + /* In case we reply to a comment, put the comment prefixed with + * '> ' into the file first. */ + if (sctx->reply_comment.body) + gcli_print_prefixed(f, sctx->reply_comment.body, "> "); + fprintf( f, "! Enter your comment above, save and exit.\n" "! All lines with a leading '!' are discarded and will not\n" "! appear in your comment.\n" "! COMMENT IN : %s/%s %s #%"PRIid"\n", - info->owner, info->repo, target_type, info->target_id); + sctx->opts.owner, sctx->opts.repo, target_type, sctx->opts.target_id); } static char * -gcli_comment_get_message(struct gcli_submit_comment_opts *info) +gcli_comment_get_message(struct submit_ctx *info) { return gcli_editor_get_user_message(g_clictx, comment_init, info); } static int -comment_submit(struct gcli_submit_comment_opts opts, int always_yes) +comment_submit(struct submit_ctx *sctx, int always_yes) { int rc = 0; char *message; - message = gcli_comment_get_message(&opts); - opts.message = message; + message = gcli_comment_get_message(sctx); + sctx->opts.message = message; if (message == NULL) errx(1, "gcli: empty message. aborting."); @@ -114,17 +143,17 @@ comment_submit(struct gcli_submit_comment_opts opts, int always_yes) fprintf( stdout, "You will be commenting the following in %s/%s #%"PRIid":\n%s\n", - opts.owner, opts.repo, opts.target_id, opts.message); + sctx->opts.owner, sctx->opts.repo, sctx->opts.target_id, sctx->opts.message); if (!always_yes) { if (!sn_yesno("Is this okay?")) errx(1, "Aborted by user"); } - rc = gcli_comment_submit(g_clictx, &opts); + rc = gcli_comment_submit(g_clictx, &sctx->opts); free(message); - opts.message = NULL; + sctx->opts.message = NULL; return rc; } @@ -179,10 +208,10 @@ gcli_print_comment_list(struct gcli_comment_list const *const list) int subcommand_comment(int argc, char *argv[]) { - int ch, target_id = -1, rc = 0; - char const *repo = NULL, *owner = NULL; + int ch, rc = 0; + struct submit_ctx sctx = {0}; bool always_yes = false; - enum comment_target_type target_type; + gcli_id reply_to_id = 0; struct option const options[] = { { .name = "yes", @@ -205,31 +234,41 @@ subcommand_comment(int argc, char *argv[]) .has_arg = required_argument, .flag = NULL, .val = 'p' }, + { .name = "in-reply-to", + .has_arg = required_argument, + .flag = NULL, + .val = 'R' }, {0}, }; - while ((ch = getopt_long(argc, argv, "yr:o:i:p:", options, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "yr:o:i:p:R:", options, NULL)) != -1) { switch (ch) { case 'r': - repo = optarg; + sctx.opts.repo = optarg; break; case 'o': - owner = optarg; + sctx.opts.owner = optarg; break; case 'p': - target_type = PR_COMMENT; + sctx.opts.target_type = PR_COMMENT; goto parse_target_id; case 'i': - target_type = ISSUE_COMMENT; + sctx.opts.target_type = ISSUE_COMMENT; parse_target_id: { char *endptr; - target_id = strtoul(optarg, &endptr, 10); + sctx.opts.target_id = strtoul(optarg, &endptr, 10); if (endptr != optarg + strlen(optarg)) err(1, "gcli: error: Cannot parse issue/PR number"); } break; case 'y': always_yes = true; break; + case 'R': { + char *endptr = NULL; + reply_to_id = strtoul(optarg, &endptr, 10); + if (endptr != optarg + strlen(optarg)) + err(1, "gcli: error: cannot parse comment id"); + } break; default: usage(); return EXIT_FAILURE; @@ -239,20 +278,28 @@ subcommand_comment(int argc, char *argv[]) argc -= optind; argv += optind; - check_owner_and_repo(&owner, &repo); + check_owner_and_repo(&sctx.opts.owner, &sctx.opts.repo); - if (target_id < 0) { + if (!sctx.opts.target_id) { fprintf(stderr, "gcli: error: missing issue/PR number (use -i/-p)\n"); usage(); return EXIT_FAILURE; } - rc = comment_submit((struct gcli_submit_comment_opts) { - .owner = owner, - .repo = repo, - .target_type = target_type, - .target_id = target_id }, - always_yes); + if (reply_to_id) { + rc = gcli_get_comment(g_clictx, sctx.opts.owner, sctx.opts.repo, + sctx.opts.target_type, sctx.opts.target_id, + reply_to_id, &sctx.reply_comment); + + if (rc < 0) { + errx(1, "gcli: error: failed to fetch comment for reply: %s", + gcli_get_error(g_clictx)); + } + } + + rc = comment_submit(&sctx, always_yes); + + gcli_comment_free(&sctx.reply_comment); if (rc < 0) errx(1, "gcli: error: failed to submit comment: %s", gcli_get_error(g_clictx)); diff --git a/src/comments.c b/src/comments.c index 54c1337b..10206055 100644 --- a/src/comments.c +++ b/src/comments.c @@ -56,11 +56,12 @@ gcli_comments_free(struct gcli_comment_list *const list) } int -gcli_get_issue_comment(struct gcli_ctx *ctx, char const *const owner, - char const *repo, gcli_id const issue_id, - gcli_id const comment_id, struct gcli_comment *const out) +gcli_get_comment(struct gcli_ctx *ctx, char const *owner, char const *repo, + enum comment_target_type target_type, gcli_id target_id, + gcli_id comment_id, struct gcli_comment *out) { - gcli_null_check_call(get_issue_comment, ctx, owner, repo, issue_id, comment_id, out); + gcli_null_check_call(get_comment, ctx, owner, repo, target_type, target_id, + comment_id, out); } int diff --git a/src/forges.c b/src/forges.c index b1194ba6..8685aca5 100644 --- a/src/forges.c +++ b/src/forges.c @@ -178,7 +178,7 @@ static struct gcli_forge_descriptor const gitlab_forge_descriptor = { /* Comments */ - .get_issue_comment = gitlab_get_issue_comment, + .get_comment = gitlab_get_comment, .get_issue_comments = gitlab_get_issue_comments, .get_pull_comments = gitlab_get_mr_comments, .perform_submit_comment = gitlab_perform_submit_comment, diff --git a/src/gitlab/comments.c b/src/gitlab/comments.c index 403d4dee..612980e6 100644 --- a/src/gitlab/comments.c +++ b/src/gitlab/comments.c @@ -172,20 +172,28 @@ gitlab_fetch_comment(struct gcli_ctx *ctx, char const *const url, } int -gitlab_get_issue_comment(struct gcli_ctx *ctx, char const *const owner, - char const *const repo, gcli_id const issue_id, - gcli_id const comment_id, - struct gcli_comment *const out) +gitlab_get_comment(struct gcli_ctx *ctx, char const *const owner, char const *const repo, + enum comment_target_type const target_type, gcli_id const target_id, + gcli_id const comment_id, struct gcli_comment *const out) { - char *url, *e_owner, *e_repo; + char *url, *e_owner, *e_repo, *target_str; int rc; e_owner = gcli_urlencode(owner); e_repo = gcli_urlencode(repo); - url = sn_asprintf("%s/projects/%s%%2F%s/issues/%"PRIid"/notes/%"PRIid, - gcli_get_apibase(ctx), e_owner, e_repo, issue_id, - comment_id); + switch (target_type) { + case ISSUE_COMMENT: + target_str = "issues"; + break; + case PR_COMMENT: + target_str = "merge_requests"; + break; + } + + url = sn_asprintf("%s/projects/%s%%2F%s/%s/%"PRIid"/notes/%"PRIid, + gcli_get_apibase(ctx), e_owner, e_repo, target_str, + target_id, comment_id); free(e_owner); free(e_repo); From 9033182419725c53d2a617eb3cd5ab6eac27bffc Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:08 +0200 Subject: [PATCH 07/50] github: implement fetching a single comment This allows the -R option in the subcommand to work for GitHub too. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/github/comments.h | 4 +++ src/forges.c | 1 + src/github/comments.c | 49 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/include/gcli/github/comments.h b/include/gcli/github/comments.h index 2325b663..0ce2c50e 100644 --- a/include/gcli/github/comments.h +++ b/include/gcli/github/comments.h @@ -46,4 +46,8 @@ int github_get_comments(struct gcli_ctx *ctx, char const *owner, char const *rep int github_fetch_comments(struct gcli_ctx *ctx, char *url, struct gcli_comment_list *const out); +int github_get_comment(struct gcli_ctx *ctx, char const *repo, char const *owner, + enum comment_target_type target_type, gcli_id target_id, + gcli_id comment_id, struct gcli_comment *out); + #endif /* GITHUB_COMMENTS_H */ diff --git a/src/forges.c b/src/forges.c index 8685aca5..c3d40f59 100644 --- a/src/forges.c +++ b/src/forges.c @@ -79,6 +79,7 @@ static struct gcli_forge_descriptor const github_forge_descriptor = { /* Comments */ + .get_comment = github_get_comment, .get_issue_comments = github_get_comments, .get_pull_comments = github_get_comments, .perform_submit_comment = github_perform_submit_comment, diff --git a/src/github/comments.c b/src/github/comments.c index a17865bb..a2d04899 100644 --- a/src/github/comments.c +++ b/src/github/comments.c @@ -103,3 +103,52 @@ github_get_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, return github_fetch_comments(ctx, url, out); } + +static int +github_fetch_comment(struct gcli_ctx *ctx, char const *const url, + struct gcli_comment *const out) +{ + struct gcli_fetch_buffer buffer = {0}; + struct json_stream stream = {0}; + int rc = 0; + + rc = gcli_fetch(ctx, url, NULL, &buffer); + if (rc < 0) + return rc; + + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_github_comment(ctx, &stream, out); + json_close(&stream); + + gcli_fetch_buffer_free(&buffer); + + return rc; +} + +int +github_get_comment(struct gcli_ctx *ctx, char const *owner, char const *repo, + enum comment_target_type target_type, gcli_id target_id, + gcli_id comment_id, struct gcli_comment *out) +{ + char *e_owner = NULL, *e_repo = NULL, *url = NULL; + int rc = 0; + + (void) target_type; /* target type and id ignored as pull requests are issues on GitHub */ + (void) target_id; + + e_owner = gcli_urlencode(owner); + e_repo = gcli_urlencode(repo); + + url = sn_asprintf("%s/repos/%s/%s/issues/comments/%"PRIid, + gcli_get_apibase(ctx), e_owner, e_repo, + comment_id); + + free(e_owner); + free(e_repo); + + rc = github_fetch_comment(ctx, url, out); + + free(url); + + return rc; +} From 105ba046142ae704c3ab330ddcc9755bbd83bce3 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:09 +0200 Subject: [PATCH 08/50] gitea: implement fetching a single comment With this we can reply easily to comments on Gitea (comment -R option). Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/gitea/comments.h | 5 +++++ src/forges.c | 1 + src/gitea/comments.c | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/gcli/gitea/comments.h b/include/gcli/gitea/comments.h index 56ec3367..fa64c5ee 100644 --- a/include/gcli/gitea/comments.h +++ b/include/gcli/gitea/comments.h @@ -37,6 +37,11 @@ #include #include +int gitea_get_comment(struct gcli_ctx *ctx, char const *owner, + char const *repo, enum comment_target_type target_type, + gcli_id target_id, gcli_id comment_id, + struct gcli_comment *out); + int gitea_get_comments(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, struct gcli_comment_list *out); diff --git a/src/forges.c b/src/forges.c index c3d40f59..2b113b99 100644 --- a/src/forges.c +++ b/src/forges.c @@ -275,6 +275,7 @@ static struct gcli_forge_descriptor const gitea_forge_descriptor = { /* Comments */ + .get_comment = gitea_get_comment, .get_issue_comments = gitea_get_comments, .get_pull_comments = gitea_get_comments, .perform_submit_comment = gitea_perform_submit_comment, diff --git a/src/gitea/comments.c b/src/gitea/comments.c index ecd02287..0bf8393d 100644 --- a/src/gitea/comments.c +++ b/src/gitea/comments.c @@ -43,3 +43,13 @@ gitea_perform_submit_comment(struct gcli_ctx *ctx, { return github_perform_submit_comment(ctx, opts); } + +int +gitea_get_comment(struct gcli_ctx *ctx, char const *const owner, + char const *const repo, enum comment_target_type target_type, + gcli_id const target_id, gcli_id const comment_id, + struct gcli_comment *out) +{ + return github_get_comment(ctx, owner, repo, target_type, target_id, + comment_id, out); +} From e63ec1ea86c80cd24860340bd1189d04e4e1a41f Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:10 +0200 Subject: [PATCH 09/50] bugzilla: implement fetching a single bug comment This allows comment responses for Bugzilla with the newly added -R flag. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/bugzilla/bugs-parser.h | 4 ++++ include/gcli/bugzilla/bugs.h | 4 ++++ src/bugzilla/bugs-parser.c | 23 ++++++++++++++++++ src/bugzilla/bugs.c | 37 +++++++++++++++++++++++++++++ src/forges.c | 1 + templates/bugzilla/bugs.t | 4 ++++ 6 files changed, 73 insertions(+) diff --git a/include/gcli/bugzilla/bugs-parser.h b/include/gcli/bugzilla/bugs-parser.h index 1da4bbef..baabd64c 100644 --- a/include/gcli/bugzilla/bugs-parser.h +++ b/include/gcli/bugzilla/bugs-parser.h @@ -65,4 +65,8 @@ int parse_bugzilla_attachment_content_only_first(struct gcli_ctx *ctx, struct json_stream *stream, struct gcli_attachment *out); +int parse_bugzilla_single_comments_array_only_first(struct gcli_ctx *ctx, + struct json_stream *stream, + struct gcli_comment *out); + #endif /* GCLI_BUGZILLA_BUGS_PARSER_H */ diff --git a/include/gcli/bugzilla/bugs.h b/include/gcli/bugzilla/bugs.h index 879e3660..5ddf531f 100644 --- a/include/gcli/bugzilla/bugs.h +++ b/include/gcli/bugzilla/bugs.h @@ -51,6 +51,10 @@ int bugzilla_bug_get_comments(struct gcli_ctx *const ctx, char const *const component, gcli_id const bug_id, struct gcli_comment_list *out); +int bugzilla_bug_get_comment(struct gcli_ctx *ctx, char const *product, char const *component, + enum comment_target_type target_type, gcli_id target_id, + gcli_id comment_id, struct gcli_comment *out); + int bugzilla_bug_get_attachments(struct gcli_ctx *ctx, char const *const product, char const *const component, gcli_id const bug_id, diff --git a/src/bugzilla/bugs-parser.c b/src/bugzilla/bugs-parser.c index 4391202e..833180df 100644 --- a/src/bugzilla/bugs-parser.c +++ b/src/bugzilla/bugs-parser.c @@ -188,3 +188,26 @@ parse_bugzilla_attachment_content_only_first(struct gcli_ctx *ctx, return rc; } + +int +parse_bugzilla_single_comments_array_only_first(struct gcli_ctx *ctx, + struct json_stream *stream, + struct gcli_comment *out) +{ + enum json_type next = JSON_NULL; + int rc = 0; + + if ((next = json_next(stream)) != JSON_OBJECT) + return gcli_error(ctx, "expected bugzilla comments dictionary"); + + while ((next = json_next(stream)) == JSON_STRING) { + rc = parse_bugzilla_comment(ctx, stream, out); + if (rc < 0) + return rc; + } + + if (next != JSON_OBJECT_END) + return gcli_error(ctx, "unclosed bugzilla comments dictionary"); + + return rc; +} diff --git a/src/bugzilla/bugs.c b/src/bugzilla/bugs.c index d354c737..43f3d77e 100644 --- a/src/bugzilla/bugs.c +++ b/src/bugzilla/bugs.c @@ -140,6 +140,43 @@ bugzilla_bug_get_comments(struct gcli_ctx *const ctx, char const *const product, return rc; } +int +bugzilla_bug_get_comment(struct gcli_ctx *const ctx, char const *const product, + char const *const component, + enum comment_target_type const target_type, + gcli_id const bug_id, gcli_id const comment_id, + struct gcli_comment *const out) +{ + char *url = NULL; + int rc = 0; + struct gcli_fetch_buffer buffer = {0}; + struct json_stream stream = {0}; + + (void) product; + (void) component; + (void) target_type; + (void) bug_id; + + url = sn_asprintf("%s/rest/bug/comment/%"PRIid"?include_fields=_all", + gcli_get_apibase(ctx), comment_id); + + rc = gcli_fetch(ctx, url, NULL, &buffer); + if (rc < 0) + goto error_fetch; + + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_bugzilla_single_comment(ctx, &stream, out); + json_close(&stream); + + gcli_fetch_buffer_free(&buffer); + +error_fetch: + free(url); + + return rc; + +} + static int bugzilla_bug_get_op(struct gcli_ctx *ctx, gcli_id const bug_id, char **out) { diff --git a/src/forges.c b/src/forges.c index 2b113b99..31ffd110 100644 --- a/src/forges.c +++ b/src/forges.c @@ -374,6 +374,7 @@ bugzilla_forge_descriptor = .search_issues = bugzilla_get_bugs, .get_issue_summary = bugzilla_get_bug, .get_issue_comments = bugzilla_bug_get_comments, + .get_comment = bugzilla_bug_get_comment, .get_issue_attachments = bugzilla_bug_get_attachments, .perform_submit_issue = bugzilla_bug_submit, .issue_quirks = GCLI_ISSUE_QUIRKS_COMMENTS diff --git a/templates/bugzilla/bugs.t b/templates/bugzilla/bugs.t index d10891d7..8983c0d1 100644 --- a/templates/bugzilla/bugs.t +++ b/templates/bugzilla/bugs.t @@ -80,3 +80,7 @@ object of struct gcli_attachment with parser bugzilla_bug_creation_result is object of gcli_id select "id" as id; + +parser bugzilla_single_comment is +object of struct gcli_comment with + ("comments" => use parse_bugzilla_single_comments_array_only_first); From ee4cfdabc5be6aca11ec2c6fbbd1e88474deab8a Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:11 +0200 Subject: [PATCH 10/50] gcli-comment(1): update for new -R option This adds the new -R option to the manual page documentation added in: 5da1e5a (comments: add routine for fetching a specific issue comment, 2024-06-30) Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- docs/gcli-comment.1.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/gcli-comment.1.in b/docs/gcli-comment.1.in index a724a583..a670a59f 100644 --- a/docs/gcli-comment.1.in +++ b/docs/gcli-comment.1.in @@ -10,6 +10,7 @@ .Op Fl y .Op Fl o Ar owner Fl r Ar repo .Op Fl i Ar issue | Fl p Ar PR +.Op Fl R Ar comment .Sh DESCRIPTION .Nm can be used to add comments in the discussion under issues and pull @@ -45,6 +46,12 @@ Create the comment under issue .It Fl p , -pull Ar pr Create the comment under PR .Ar #pr . +.It Fl R , -in-reply-to Ar comment +Create a reply to the comment with ID +.Ar comment . +This will put the refenced comment prefixed with +.Dq "> " +into the response file for you to edit. .El .Sh EXAMPLES Comment under PR #11 in the upstream repository: From 58c1eb2bb0c938b796c187b36b6519895035d02d Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 2 Jul 2024 22:53:12 +0200 Subject: [PATCH 11/50] changelog: add entry for new -R option in comments subcommand Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 6c8f306a..15db4ac7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,9 @@ This changelog does not follow semantic versioning. ### Added +- Added a `-R` flag to the comment subcommand that allows you to + reply to a comment with the given ID. + ### Fixed ### Changed From 7c2132d1aaa8a7d4d918e24644bc305699b35ddd Mon Sep 17 00:00:00 2001 From: Gavin-John Noonan Date: Sat, 6 Jul 2024 21:10:28 +0100 Subject: [PATCH 12/50] ci: teach sr.ht to use our new make build sytem de67a099 ("Fix the CI for the new build system") updated the gitlab ci code, however forgot to update sr.ht runners. Signed-off-by: Gavin-John Noonan --- .builds/alpine.yml | 18 ++++-------------- .builds/debian-stable.yml | 16 +++++----------- .builds/freebsd.yml | 14 +++----------- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index d67fa09b..93df95b3 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -2,33 +2,23 @@ image: alpine/edge packages: - libcurl - gcc - - autoconf - - automake - - libtool - make - pkgconf - musl-dev - curl-dev - flex - bison - - xz - - gzip - - bzip2 - libbsd-dev - kyua - atf-dev + - libedit-dev sources: - https://git.sr.ht/~herrhotzenplotz/gcli tasks: - build: | cd gcli - ./autogen.sh - { - CFLAGS='-std=c99 -pedantic -Wall -Wextra -Werror' \ - CPPFLAGS='-D_XOPEN_SOURCE=600' \ - ./configure --disable-silent-rules || (cat config.log && exit 42) - } - make -j + ./configure --debug + make -j all - check: | cd gcli - make -j distcheck + make -j check diff --git a/.builds/debian-stable.yml b/.builds/debian-stable.yml index e3baf612..9f9fd3ef 100644 --- a/.builds/debian-stable.yml +++ b/.builds/debian-stable.yml @@ -1,14 +1,12 @@ image: debian/stable packages: - build-essential + - libssl-dev - libcurl4-openssl-dev - pkgconf - - autotools-dev - bison - flex - make - - autoconf - - automake - libtool - libbsd-dev - libatf-dev kyua @@ -17,13 +15,9 @@ sources: tasks: - build: | cd gcli - ./autogen.sh - { - CFLAGS='-std=c99 -pedantic -Wall -Wextra -Werror' \ - CPPFLAGS='-D_XOPEN_SOURCE=600' \ - ./configure --disable-silent-rules || (cat config.log && exit 42) - } - make + ./configure --debug + make -j 4 all - check: | cd gcli - make distcheck + ./configure --debug + make -j 4 all diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index e85d65cf..74e801e7 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -1,8 +1,6 @@ image: freebsd/14.x packages: - atf - - autoconf - - automake - ca_root_nss - curl - kyua @@ -10,7 +8,6 @@ packages: - libssh2 - libtool - libunistring - - m4 - pkg - pkgconf - readline @@ -19,15 +16,10 @@ sources: tasks: - build: | cd gcli - ./autogen.sh - { - CFLAGS='-std=c99 -pedantic -Wall -Wextra -Wno-misleading-indentation -Werror' \ - CPPFLAGS='-D_XOPEN_SOURCE=600' \ - LEX=flex YACC=byacc \ - ./configure --disable-silent-rules || (cat config.log && exit 42) - } - make -j 4 + ./configure --debug + make -j 4 all - check: | cd gcli + ./configure --debug make -j 4 distcheck From d741223b8f0032d5ca429cd369ef3a5a10e38689 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 15 Jul 2024 21:41:17 +0000 Subject: [PATCH 13/50] configure: fix tr(1) calls tr takes two lists of characters, not two regular expressions as I mistakenly wrote. This might fix a bug encountered on macOS. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 7faaf71d..fa912131 100755 --- a/configure +++ b/configure @@ -35,7 +35,7 @@ rel_srcdir="$(dirname ${0})" srcdir="$(${REALPATH} ${rel_srcdir})" tolower() { - tr '[[:upper:]]' '[[:lower:]]' + tr '[:upper:]' '[:lower:]' } die() { From fd5b92bbdea6ebe953faec01e632f2bf4170b60e Mon Sep 17 00:00:00 2001 From: Gavin-John Noonan Date: Thu, 18 Jul 2024 18:25:34 +0100 Subject: [PATCH 14/50] cmdconfig: simplify checkyes Rather than having to lowercase each character during the check, we now lowercase the whole string first, then check if it is correct. We don't have many variations of what we consider to be yes. Signed-off-by: Gavin-John Noonan Signed-off-by: Nico Sonack --- src/cmd/cmdconfig.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c index d3b7307c..25e88d42 100644 --- a/src/cmd/cmdconfig.c +++ b/src/cmd/cmdconfig.c @@ -435,20 +435,20 @@ ensure_config(struct gcli_ctx *ctx) static int checkyes(char const *const tmp) { - static char const *const yeses[] = { "1", "y", "Y" }; + size_t tmplen = strlen(tmp) + 1; + char *tmp_lower = malloc(tmplen); - if (strlen(tmp) == 3) { - if (tolower(tmp[0]) == 'y' && tolower(tmp[1]) == 'e' && - tolower(tmp[2]) == 's') - return 1; - } + strncpy(tmp_lower, tmp, tmplen); - for (size_t i = 0; i < ARRAY_SIZE(yeses); ++i) { - if (strcmp(yeses[i], tmp) == 0) - return 1; + for (size_t i = 0; i < tmplen - 1; ++i) { + tmp_lower[i] = tolower(tmp_lower[i]); } - return 0; + int is_yes = strcmp(tmp_lower, "1") == 0 || + strcmp(tmp_lower, "yes") == 0; + + free(tmp_lower); + return is_yes; } /* readenv: Read values of environment variables and pre-populate the From 94faf405f96f6175aa43e0503fbbf72b00a452c6 Mon Sep 17 00:00:00 2001 From: Gavin-John Noonan Date: Thu, 18 Jul 2024 18:25:35 +0100 Subject: [PATCH 15/50] cmdconfig: teach checkyes that true means yes Currently when parsing config and checking for a truthy value we only allow a variation of the word "yes" (e.g: yEs, YeS, etc) or the numeral 1, but not actually the word true. Here we teach the checkyes function to also check for a variation of the word true (e.g: true, True, TrUe, etc) Signed-off-by: Gavin-John Noonan Signed-off-by: Nico Sonack --- src/cmd/cmdconfig.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c index 25e88d42..6e764c89 100644 --- a/src/cmd/cmdconfig.c +++ b/src/cmd/cmdconfig.c @@ -445,7 +445,8 @@ checkyes(char const *const tmp) } int is_yes = strcmp(tmp_lower, "1") == 0 || - strcmp(tmp_lower, "yes") == 0; + strcmp(tmp_lower, "yes") == 0 || + strcmp(tmp_lower, "true") == 0; free(tmp_lower); return is_yes; From 0348167ca1d2d06ad918a5f0572e1ba374bc5664 Mon Sep 17 00:00:00 2001 From: Gavin-John Noonan Date: Thu, 18 Jul 2024 18:25:36 +0100 Subject: [PATCH 16/50] cmdconfig: rename checkyes for consistency Throughout the codebase we generally use snake_case for functions. Signed-off-by: Gavin-John Noonan Signed-off-by: Nico Sonack --- src/cmd/cmdconfig.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c index 6e764c89..a48ec08b 100644 --- a/src/cmd/cmdconfig.c +++ b/src/cmd/cmdconfig.c @@ -433,7 +433,7 @@ ensure_config(struct gcli_ctx *ctx) /** Check input for a value that indicates yes/true */ static int -checkyes(char const *const tmp) +check_yes(char const *const tmp) { size_t tmplen = strlen(tmp) + 1; char *tmp_lower = malloc(tmplen); @@ -474,13 +474,13 @@ readenv(struct gcli_config *cfg) * violate the definition to get expected and sane behaviour. */ tmp = getenv("NO_COLOR"); if (tmp && tmp[0] != '\0') - cfg->colours_disabled = checkyes(tmp); + cfg->colours_disabled = check_yes(tmp); if ((tmp = getenv("GCLI_NOSPINNER"))) - cfg->no_spinner = checkyes(tmp); + cfg->no_spinner = check_yes(tmp); if ((tmp = getenv("GCLI_ENABLE_EXPERIMENTAL"))) - cfg->enable_experimental = checkyes(tmp); + cfg->enable_experimental = check_yes(tmp); } int @@ -1011,7 +1011,7 @@ gcli_config_display_progress_spinner(struct gcli_ctx *ctx) if (sn_sv_null(cfg_entry)) return 1; - if (checkyes(sn_sv_to_cstr(cfg_entry))) + if (check_yes(sn_sv_to_cstr(cfg_entry))) return 0; return 1; @@ -1032,5 +1032,5 @@ gcli_config_enable_experimental(struct gcli_ctx *ctx) if (sn_sv_null(cfg_entry)) return false; - return checkyes(sn_sv_to_cstr(cfg_entry)); + return check_yes(sn_sv_to_cstr(cfg_entry)); } From 4f4796baf5eb9c12801579b4192bcc2947d4aeda Mon Sep 17 00:00:00 2001 From: Alexey Ugnichev Date: Mon, 29 Jul 2024 13:34:44 +0200 Subject: [PATCH 17/50] configure: fix find_program not causing main process to exit Nico: I removed the additional should_fail logic from find_program that is pretty much unused. Instead we check the returned sub-shell exit code manually for programs that are required and exit with an appropriate error message if the program wasn't found. Issue-tracker: https://github.com/herrhotzenplotz/gcli/issues/131 Fixes: https://github.com/herrhotzenplotz/gcli/issues/131 Signed-off-by: Nico Sonack --- configure | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/configure b/configure index fa912131..13b9be05 100755 --- a/configure +++ b/configure @@ -9,28 +9,27 @@ PACKAGE_DATE="UNRELEASED" PACKAGE_STRING="gcli $PACKAGE_VERSION" PACKAGE_BUGREPORT="https://lists.sr.ht/~herrhotzenplotz/gcli-discuss" PACKAGE_URL="https://sr.ht/~herrhotzenplotz/gcli" -echo "Configuring gcli $PACKAGE_VERSION" >&2 find_program() { varname=$1 shift - should_fail=$1 - shift printf "Checking for $varname ..." >&2 for x in $*; do if (command -v $x >/dev/null 2>&1) && [ -x $(command -v $x) ]; then printf " $x\n" >&2 echo $x - return + exit 0 fi done printf " not found\n" >&2 - [ $should_fail -eq 1 ] && exit 1 + exit 1 } -REALPATH=${REALPATH:-$(find_program realpath 1 realpath grealpath)} +REALPATH=${REALPATH:-$(find_program realpath realpath grealpath)} +[ $? -eq 0 ] || die "error: need realpath" # exit code of find_program() + rel_srcdir="$(dirname ${0})" srcdir="$(${REALPATH} ${rel_srcdir})" @@ -93,11 +92,11 @@ compiler_target() { find_package() { printf "Checking for $1 ..." >&2 if ! $PKG_CONFIG --exists $1; then - if [ $3 -eq 0 ]; then + if [ "${3}" = optional ]; then printf " not found\n" export ${2}_FOUND=0 return - else + else # $3 = required die "not found" fi fi @@ -239,14 +238,16 @@ fi ############################################################################ # LIBRARIES ############################################################################ -PKG_CONFIG=${PKG_CONFIG:-$(find_program pkg-config 1 pkg-config pkgconf)} -find_package libcurl LIBCURL 1 -find_package atf-c LIBATFC 0 -find_package libcrypto LIBCRYPTO 1 +PKG_CONFIG=${PKG_CONFIG:-$(find_program pkg-config pkg-config pkgconf)} +[ $? -eq 0 ] || die "error: need pkg-config or pkgconf" + +find_package libcurl LIBCURL required +find_package atf-c LIBATFC optional +find_package libcrypto LIBCRYPTO required # Look for libedit if not disabled if [ $ENABLE_LIBEDIT -eq 1 ]; then - find_package libedit LIBEDIT 0 + find_package libedit LIBEDIT optional else LIBEDIT_FOUND=0 fi @@ -256,7 +257,7 @@ fi if [ $LIBEDIT_FOUND -eq 1 ]; then LIBREADLINE_FOUND=0 elif [ $ENABLE_LIBREADLINE -eq 1 ]; then - find_package readline LIBREADLINE 0 + find_package readline LIBREADLINE optional else LIBREADLINE_FOUND=0 fi @@ -267,9 +268,10 @@ fi AR=${AR:-ar} RANLIB=${RANLIB:-ranlib} RM=${RM:-rm} -KYUA=${KYUA:-$(find_program kyua 0 kyua)} -CCACHE=${CCACHE:-$(find_program ccache 0 ccache)} -INSTALL=${INSTALL:-$(find_program install 1 install ginstall)} +KYUA=${KYUA:-$(find_program kyua kyua)} +CCACHE=${CCACHE:-$(find_program ccache ccache)} +INSTALL=${INSTALL:-$(find_program install install ginstall)} +[ $? -eq 0 ] || die "error: need install program" ################################################################### # Configure Makefile @@ -332,7 +334,7 @@ cat > config.h < Date: Tue, 20 Feb 2024 13:54:23 +0100 Subject: [PATCH 18/50] Change line-height of pre tags for readability The code snippets used to be crammed together in an ugly fashion. Increase the line-height such that it becomes more readable and pleasing to the eye. Signed-off-by: Nico Sonack --- docs/website/tutorial/top.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/tutorial/top.html b/docs/website/tutorial/top.html index daec21b4..1a9ac68a 100644 --- a/docs/website/tutorial/top.html +++ b/docs/website/tutorial/top.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; From 8072915a7e07742677c639b5bc953e06f71e5dd8 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 20 Feb 2024 14:43:28 +0100 Subject: [PATCH 19/50] Make repository reference verbatim Signed-off-by: Nico Sonack --- docs/website/tutorial/02-First-Steps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/tutorial/02-First-Steps.md b/docs/website/tutorial/02-First-Steps.md index d7f6d997..cf888bd8 100644 --- a/docs/website/tutorial/02-First-Steps.md +++ b/docs/website/tutorial/02-First-Steps.md @@ -3,7 +3,7 @@ ## Listing issues Let's start off by listing some issues - here for the curl project -which is hosted on GitHub under curl/curl. To list issues for it one +which is hosted on GitHub under `curl/curl`. To list issues for it one would run: $ gcli -t github issues -o curl -r curl From 2feb08100b9af81f22bb375eeb9da4f290043754 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 20 Feb 2024 15:11:19 +0100 Subject: [PATCH 20/50] Add a new page to the tutorial This page will eventually contain a few instructionals for Bugzilla, Gitea and Gitlab. Signed-off-by: Nico Sonack --- docs/website/tutorial/toc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/website/tutorial/toc b/docs/website/tutorial/toc index 30f89b2f..1764ae4a 100644 --- a/docs/website/tutorial/toc +++ b/docs/website/tutorial/toc @@ -4,3 +4,4 @@ 04-Account-Setup.html Setting up an account 05-Creating-an-issue.html Creating an issue 06-Commenting.html Interacting and commenting +07-Other-forges.html Other forges (Gitea, Gitlab, Bugzilla) From 79cf0039af0c9e9f6f83a502e5f34046a38fc461 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 20 Feb 2024 15:12:05 +0100 Subject: [PATCH 21/50] Add Bugzilla instructions Signed-off-by: Nico Sonack --- docs/website/tutorial/07-Other-forges.md | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/website/tutorial/07-Other-forges.md diff --git a/docs/website/tutorial/07-Other-forges.md b/docs/website/tutorial/07-Other-forges.md new file mode 100644 index 00000000..28d617f5 --- /dev/null +++ b/docs/website/tutorial/07-Other-forges.md @@ -0,0 +1,92 @@ +# Other forges and bugtrackers + +gcli is capable of not only interacting with Github. It also currently supports: + +- GitLab +- Gitea +- Bugzilla + +## Bugzilla + +### Notes + +Bugzilla is commonly used as a bug tracker in various large open-source +projects such as FreeBSD, the Linux Kernel, Mozilla and Gentoo. + +### Searching + +Suppose you want to search for bug reports containing `sparc` in +the Gentoo Bugzilla. + +In this case you need to configure an account that points at the +correct URL in `$HOME/.config/gcli/config` by adding: + + gentoo { + forge-type=bugzilla + apibase=https://bugs.gentoo.org/ + } + +Now you can search the Gentoo Bugs: + + $ gcli -a gentoo issues sparc + NUMBER NOTES STATE TITLE + 924443 0 UNCONFIRMED Add keyword ~sparc for app-misc/fastfetch + 924430 0 RESOLVED media-libs/assimp-5.3.1 fails tests on sparc + 924215 0 CONFIRMED dev-libs/libbson dev-libs/mongo-c-driver: alpha arm ia64 mips ppc ppc64 s390 sparc keyword req + 924191 0 CONFIRMED media-libs/exempi: unaligned access causes dev-python/python-xmp-toolkit-2.0.2 to fails tests on sparc (test_file_to_dict (test.test_core_unit.UtilsTestCase.test_file_to_dict) ... Bus error) + 924180 0 CONFIRMED dev-python/psycopg-3.1.17[native-extensions] fails tests on sparc: tests/test_copy_async.py::test_read_rows[asyncio-names-1] Fatal Python error: Bus error + 924031 0 IN_PROGRESS sys-apps/bfs: ~arm ~arm64 ~ppc ~ppc64 ~sparc keywording + 923968 0 CONFIRMED dev-python/pyarrow-15.0.0 fails to configure on sparc: CMake Error at cmake_modules/SetupCxxFlags.cmake:42 (message): Unknown system processor + 921245 0 CONFIRMED media-video/rav1e-0.6.6 fails to compile on sparc: Assertion `DT.dominates(RHead, LHead) && "No dominance between recurrences used by one SCEV?"' failed. + 920956 0 CONFIRMED dev-python/pygame-2.5.2: pygame.tests.font_test SIGBUS on sparc + 920737 0 CONFIRMED sparc64-solaris Prefix no longer supported + + + +### Issue details + +Furthermore we can look at single issues: + + $ gcli -a gentoo issues -i 920737 all comments + NUMBER : 920737 + TITLE : sparc64-solaris Prefix no longer supported + CREATED : 2023-12-26T19:20:58Z + PRODUCT : Gentoo Linux + COMPONENT : Profiles + AUTHOR : Tom Williams + STATE : CONFIRMED + LABELS : none + ASSIGNEES : prefix + + ORIGINAL POST + + Resurrecting a Prefix install on Solaris 11.4 SPARC. It was working rather well + for me; after a hiatus I had hoped to use it again but my first emerge --sync + has removed the profile needed to merge any updates or new packages. + + I note commit 8e006b67e06a19fae10c6059c7fc5ede88834601 in May 2023 removed the + profile and keywording for prefixed installs. + + There is no associated comment. There doesn't seem to be a bug report in regards + to the change (I'm quite sure almost nobody uses it, so probably fair enough) + + Any easy way to restore the profile for now? Eventually Solaris/SPARC and thus + Prefix will be gone anyway, but useful for now. + + Thanks for your continued efforts. + AUTHOR : sam + DATE : 2023-12-26T19:21:33Z + I think at the very least, when removing Prefix support in future, a 'deprecated' + file should be added to the relevant profiles asking if anyone is using + it to step forward. + + AUTHOR : grobian + DATE : 2023-12-26T22:58:12Z + Solaris 11.4 itself is a problem. I doubt you ever had it "working". + + AUTHOR : grobian + DATE : 2023-12-26T22:59:57Z + Linux sparc team is not relevant here + + $ + From ebd4fa345e3e0f6e7f6c1024041a7416d1713230 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sat, 29 Jun 2024 17:52:18 +0200 Subject: [PATCH 22/50] website: reorder list of links - Move the test tarballs to the end - Make tutorial not WIP anymore Signed-off-by: Nico Sonack --- docs/website/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/website/index.html b/docs/website/index.html index d1532141..59c63303 100644 --- a/docs/website/index.html +++ b/docs/website/index.html @@ -62,13 +62,13 @@

General

However, you can go look at

From ab104a1e8c7ec7dbb9cd43148e5b4a4f6ef00d86 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sat, 29 Jun 2024 19:55:18 +0200 Subject: [PATCH 23/50] tutorial: add installation instructions for more OSes Signed-off-by: Nico Sonack --- docs/website/tutorial/01-Installation.html | 12 ++++++++++-- docs/website/tutorial/01-Installation.md | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/docs/website/tutorial/01-Installation.html b/docs/website/tutorial/01-Installation.html index e60433c9..d92c4554 100644 --- a/docs/website/tutorial/01-Installation.html +++ b/docs/website/tutorial/01-Installation.html @@ -43,9 +43,17 @@

Through package manager

following command:

# pkg install gcli
 
+

On NetBSD you can run:

+
# pkgin install gcli
+
+

On Ubuntu, Debian, Devuan and the like you can run:

+
# apt install gcli
+
+

On ArchLinux you can either use the AUR manually or use your favourite AUR helper:

+
# yay -S gcli
+

Compile the source code

-

Other operating systems currently require manual compilation and -installation.

+

Other operating systems may currently require manual compilation and installation.

Windows NT Notes

It is entirely possible to build gcli on Windows using MSYS2. Please follow their instructions on diff --git a/docs/website/tutorial/01-Installation.md b/docs/website/tutorial/01-Installation.md index ed6dcd18..249ecb09 100644 --- a/docs/website/tutorial/01-Installation.md +++ b/docs/website/tutorial/01-Installation.md @@ -7,10 +7,21 @@ following command: # pkg install gcli +On NetBSD you can run: + + # pkgin install gcli + +On Ubuntu, Debian, Devuan and the like you can run: + + # apt install gcli + +On ArchLinux you can either use the AUR manually or use your favourite AUR helper: + + # yay -S gcli + ## Compile the source code -Other operating systems currently require manual compilation and -installation. +Other operating systems may currently require manual compilation and installation. ### Windows NT Notes From 2031a1cd1dd1211289cbb43ed1c39cec48e37905 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sat, 29 Jun 2024 20:15:35 +0200 Subject: [PATCH 24/50] tutorial: add new page about creating pull requests Signed-off-by: Nico Sonack --- docs/website/tutorial/01-Installation.html | 2 +- docs/website/tutorial/02-First-Steps.html | 4 +- .../tutorial/03-Find-Documentation.html | 2 +- docs/website/tutorial/04-Account-Setup.html | 2 +- .../tutorial/05-Creating-an-issue.html | 2 +- docs/website/tutorial/06-Commenting.html | 4 +- .../tutorial/07-Creating-a-pull-request.html | 91 ++++++++++++ .../tutorial/07-Creating-a-pull-request.md | 56 ++++++++ docs/website/tutorial/08-Other-forges.html | 129 ++++++++++++++++++ ...{07-Other-forges.md => 08-Other-forges.md} | 0 docs/website/tutorial/index.html | 4 +- docs/website/tutorial/toc | 3 +- 12 files changed, 290 insertions(+), 9 deletions(-) create mode 100644 docs/website/tutorial/07-Creating-a-pull-request.html create mode 100644 docs/website/tutorial/07-Creating-a-pull-request.md create mode 100644 docs/website/tutorial/08-Other-forges.html rename docs/website/tutorial/{07-Other-forges.md => 08-Other-forges.md} (100%) diff --git a/docs/website/tutorial/01-Installation.html b/docs/website/tutorial/01-Installation.html index d92c4554..d10c06ba 100644 --- a/docs/website/tutorial/01-Installation.html +++ b/docs/website/tutorial/01-Installation.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; diff --git a/docs/website/tutorial/02-First-Steps.html b/docs/website/tutorial/02-First-Steps.html index 3df2173d..375f9945 100644 --- a/docs/website/tutorial/02-First-Steps.html +++ b/docs/website/tutorial/02-First-Steps.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; @@ -41,7 +41,7 @@

First steps

Listing issues

Let's start off by listing some issues - here for the curl project -which is hosted on GitHub under curl/curl. To list issues for it one +which is hosted on GitHub under curl/curl. To list issues for it one would run:

$ gcli -t github issues -o curl -r curl
 
diff --git a/docs/website/tutorial/03-Find-Documentation.html b/docs/website/tutorial/03-Find-Documentation.html index 34265683..b7662a89 100644 --- a/docs/website/tutorial/03-Find-Documentation.html +++ b/docs/website/tutorial/03-Find-Documentation.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; diff --git a/docs/website/tutorial/04-Account-Setup.html b/docs/website/tutorial/04-Account-Setup.html index 8b64f627..43b98c4c 100644 --- a/docs/website/tutorial/04-Account-Setup.html +++ b/docs/website/tutorial/04-Account-Setup.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; diff --git a/docs/website/tutorial/05-Creating-an-issue.html b/docs/website/tutorial/05-Creating-an-issue.html index db7246cc..3e17b005 100644 --- a/docs/website/tutorial/05-Creating-an-issue.html +++ b/docs/website/tutorial/05-Creating-an-issue.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; diff --git a/docs/website/tutorial/06-Commenting.html b/docs/website/tutorial/06-Commenting.html index ef2a611f..832483da 100644 --- a/docs/website/tutorial/06-Commenting.html +++ b/docs/website/tutorial/06-Commenting.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; @@ -35,6 +35,7 @@

Commenting

@@ -65,6 +66,7 @@

Commenting on pull requests

diff --git a/docs/website/tutorial/07-Creating-a-pull-request.html b/docs/website/tutorial/07-Creating-a-pull-request.html new file mode 100644 index 00000000..9b407fe9 --- /dev/null +++ b/docs/website/tutorial/07-Creating-a-pull-request.html @@ -0,0 +1,91 @@ + + + + + GCLI Tutorial | Creating a Pull Request + + + + + + + + + +
+

Creating a pull request

+

Creating a pull request with gcli is usually as simple as running

+
$ gcli pulls create
+
+

Preparation

+

Suppose you have a git repository forked and cloned:

+
$ git clone git@github.com:contour-terminal/contour
+$ cd contour
+$ gcli forks create --into herrhotzenplotz
+
+

Then you do some work on whatever feature you're planning to submit:

+
$ git checkout -b my-amazing-feature
+<hackhackhack>
+$ git add -p
+$ git commit
+
+

Push your changes

+

You then push your changes to your fork:

+
$ git push origin my-amazing-feature
+
+

Create the pull request

+

Now you can run the command to create the pull request:

+
$ gcli pulls create
+From (owner:branch) [herrhotzenplotz:my-amazing-feature]:
+Owner [contour-terminal]:
+Repository [contour]:
+To Branch [master]:
+Title: Add new amazing feature
+Enable automerge? [yN]:
+
+

Most of the defaults you should be able to simply accept. This +assumes that you have the source branch checked out locally and +remotes are configured appropriately.

+

Otherwise you can just change the defaults by entering the correct +values.

+

Enter original post

+

After you entered all the meta data of the pull request gcli will +drop you into your editor and lets you enter a message for the pull +request.

+

Submit

+

After you saved and exit type y and hit enter to submit the pull +request.

+
+
+ + + diff --git a/docs/website/tutorial/07-Creating-a-pull-request.md b/docs/website/tutorial/07-Creating-a-pull-request.md new file mode 100644 index 00000000..aad0ef9f --- /dev/null +++ b/docs/website/tutorial/07-Creating-a-pull-request.md @@ -0,0 +1,56 @@ +# Creating a pull request + +Creating a pull request with gcli is usually as simple as running + + $ gcli pulls create + +## Preparation + +Suppose you have a git repository forked and cloned: + + $ git clone git@github.com:contour-terminal/contour + $ cd contour + $ gcli forks create --into herrhotzenplotz + +Then you do some work on whatever feature you're planning to submit: + + $ git checkout -b my-amazing-feature + + $ git add -p + $ git commit + +## Push your changes + +You then push your changes to your fork: + + $ git push origin my-amazing-feature + +## Create the pull request + +Now you can run the command to create the pull request: + + $ gcli pulls create + From (owner:branch) [herrhotzenplotz:my-amazing-feature]: + Owner [contour-terminal]: + Repository [contour]: + To Branch [master]: + Title: Add new amazing feature + Enable automerge? [yN]: + +Most of the defaults you should be able to simply accept. This +assumes that you have the source branch checked out locally and +remotes are configured appropriately. + +Otherwise you can just change the defaults by entering the correct +values. + +## Enter original post + +After you entered all the meta data of the pull request gcli will +drop you into your editor and lets you enter a message for the pull +request. + +## Submit + +After you saved and exit type `y` and hit enter to submit the pull +request. diff --git a/docs/website/tutorial/08-Other-forges.html b/docs/website/tutorial/08-Other-forges.html new file mode 100644 index 00000000..8023e556 --- /dev/null +++ b/docs/website/tutorial/08-Other-forges.html @@ -0,0 +1,129 @@ + + + + + GCLI Tutorial | Other forges (Gitea, Gitlab, Bugzilla) + + + + + + + + + +
+

Other forges and bugtrackers

+

gcli is capable of not only interacting with Github. It also currently supports:

+
    +
  • GitLab
  • +
  • Gitea
  • +
  • Bugzilla
  • +
+

Bugzilla

+

Notes

+

Bugzilla is commonly used as a bug tracker in various large open-source +projects such as FreeBSD, the Linux Kernel, Mozilla and Gentoo.

+

Searching

+

Suppose you want to search for bug reports containing sparc in +the Gentoo Bugzilla.

+

In this case you need to configure an account that points at the +correct URL in $HOME/.config/gcli/config by adding:

+
gentoo {
+	forge-type=bugzilla
+	apibase=https://bugs.gentoo.org/
+}
+
+

Now you can search the Gentoo Bugs:

+
$ gcli -a gentoo issues sparc
+NUMBER  NOTES  STATE        TITLE
+924443      0  UNCONFIRMED  Add keyword ~sparc for app-misc/fastfetch
+924430      0  RESOLVED     media-libs/assimp-5.3.1 fails tests on sparc
+924215      0  CONFIRMED    dev-libs/libbson dev-libs/mongo-c-driver: alpha arm ia64 mips ppc ppc64 s390 sparc keyword req
+924191      0  CONFIRMED    media-libs/exempi: unaligned access causes dev-python/python-xmp-toolkit-2.0.2 to fails tests on sparc (test_file_to_dict (test.test_core_unit.UtilsTestCase.test_file_to_dict) ... Bus error)
+924180      0  CONFIRMED    dev-python/psycopg-3.1.17[native-extensions] fails tests on sparc: tests/test_copy_async.py::test_read_rows[asyncio-names-1] Fatal Python error: Bus error
+924031      0  IN_PROGRESS  sys-apps/bfs: ~arm ~arm64 ~ppc ~ppc64 ~sparc keywording
+923968      0  CONFIRMED    dev-python/pyarrow-15.0.0 fails to configure on sparc: CMake Error at cmake_modules/SetupCxxFlags.cmake:42 (message): Unknown system processor
+921245      0  CONFIRMED    media-video/rav1e-0.6.6 fails to compile on sparc: Assertion `DT.dominates(RHead, LHead) && "No dominance between recurrences used by one SCEV?"' failed.
+920956      0  CONFIRMED    dev-python/pygame-2.5.2: pygame.tests.font_test SIGBUS on sparc
+920737      0  CONFIRMED    sparc64-solaris Prefix no longer supported
+
+<snip>
+
+

Issue details

+

Furthermore we can look at single issues:

+
$ gcli -a gentoo issues -i 920737 all comments
+   NUMBER : 920737
+    TITLE : sparc64-solaris Prefix no longer supported
+  CREATED : 2023-12-26T19:20:58Z
+  PRODUCT : Gentoo Linux
+COMPONENT : Profiles
+   AUTHOR : Tom Williams
+    STATE : CONFIRMED
+   LABELS : none
+ASSIGNEES : prefix
+
+ORIGINAL POST
+
+    Resurrecting a Prefix install on Solaris 11.4 SPARC. It was working rather well
+    for me; after a hiatus I had hoped to use it again but my first emerge --sync
+    has removed the profile needed to merge any updates or new packages.
+
+    I note commit 8e006b67e06a19fae10c6059c7fc5ede88834601 in May 2023 removed the
+    profile and keywording for prefixed installs.
+
+    There is no associated comment. There doesn't seem to be a bug report in regards
+    to the change (I'm quite sure almost nobody uses it, so probably fair enough)
+
+    Any easy way to restore the profile for now? Eventually Solaris/SPARC and thus
+    Prefix will be gone anyway, but useful for now.
+
+    Thanks for your continued efforts.
+AUTHOR : sam
+DATE   : 2023-12-26T19:21:33Z
+         I think at the very least, when removing Prefix support in future, a 'deprecated'
+         file should be added to the relevant profiles asking if anyone is using
+         it to step forward.
+
+AUTHOR : grobian
+DATE   : 2023-12-26T22:58:12Z
+         Solaris 11.4 itself is a problem.  I doubt you ever had it "working".
+
+AUTHOR : grobian
+DATE   : 2023-12-26T22:59:57Z
+         Linux sparc team is not relevant here
+
+$
+
+
+
+ + + diff --git a/docs/website/tutorial/07-Other-forges.md b/docs/website/tutorial/08-Other-forges.md similarity index 100% rename from docs/website/tutorial/07-Other-forges.md rename to docs/website/tutorial/08-Other-forges.md diff --git a/docs/website/tutorial/index.html b/docs/website/tutorial/index.html index d602e473..0f79b799 100644 --- a/docs/website/tutorial/index.html +++ b/docs/website/tutorial/index.html @@ -18,7 +18,7 @@ text-shadow: 0 1px 0 #ffffff; max-width: 73%; } pre { - line-height: 1.0; + line-height: 1.2; } a { border-bottom: 1px solid #444444; @@ -44,6 +44,8 @@

Table of contents

  • Setting up an account
  • Creating an issue
  • Interacting and commenting
  • +
  • Creating a Pull Request
  • +
  • Other forges (Gitea, Gitlab, Bugzilla)
  • diff --git a/docs/website/tutorial/toc b/docs/website/tutorial/toc index 1764ae4a..173849d4 100644 --- a/docs/website/tutorial/toc +++ b/docs/website/tutorial/toc @@ -4,4 +4,5 @@ 04-Account-Setup.html Setting up an account 05-Creating-an-issue.html Creating an issue 06-Commenting.html Interacting and commenting -07-Other-forges.html Other forges (Gitea, Gitlab, Bugzilla) +07-Creating-a-pull-request.html Creating a Pull Request +08-Other-forges.html Other forges (Gitea, Gitlab, Bugzilla) From 48d971dfd431be15b5c5a0be0056fce37a2cf116 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sun, 4 Aug 2024 18:26:04 +0200 Subject: [PATCH 25/50] Add some devel scripts and update CSS Signed-off-by: Nico Sonack --- docs/website/build.sh | 29 +++++++++++++++++++++++++++++ docs/website/deploy.sh | 25 ++----------------------- docs/website/devl.sh | 15 +++++++++++++++ docs/website/index.html | 27 +++++++++++++++------------ docs/website/serve.sh | 13 +++++++++++++ docs/website/tutorial/index.html | 19 +++++++++++++++++-- docs/website/tutorial/top.html | 19 +++++++++++++++++-- 7 files changed, 108 insertions(+), 39 deletions(-) create mode 100755 docs/website/build.sh create mode 100755 docs/website/devl.sh create mode 100755 docs/website/serve.sh diff --git a/docs/website/build.sh b/docs/website/build.sh new file mode 100755 index 00000000..ff4e92fe --- /dev/null +++ b/docs/website/build.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# This script builds the website into a temporary directory +# + +cd $(dirname $0) + +# Build the tutorial +( + cd tutorial + ./gen.sh +) + +# Make a dist directory and copy over files +DISTDIR=$(mktemp -d) +mkdir -p ${DISTDIR}/tutorial +mkdir -p ${DISTDIR}/assets + +cp -p index.html ${DISTDIR}/ +cp -p \ + tutorial/0*.html \ + tutorial/index.html \ + ${DISTDIR}/tutorial + +cp -p \ + ../screenshot.png \ + ${DISTDIR}/assets/screenshot.png + +echo "${DISTDIR}" diff --git a/docs/website/deploy.sh b/docs/website/deploy.sh index 1e104b80..88101cb2 100755 --- a/docs/website/deploy.sh +++ b/docs/website/deploy.sh @@ -1,32 +1,11 @@ #!/bin/sh -xe # -# This script builds the tutorial and then creates a tarball that +# This script builds the website and then creates a tarball that # is pulled regularly from the server # cd $(dirname $0) -# Build the tutorial -( - cd tutorial - ./gen.sh -) - -# Make a dist directory and copy over files -DISTDIR=$(mktemp -d) -mkdir -p ${DISTDIR}/tutorial -mkdir -p ${DISTDIR}/assets - -cp -vp index.html ${DISTDIR}/ -cp -vp \ - tutorial/0*.html \ - tutorial/index.html \ - ${DISTDIR}/tutorial - -cp -vp \ - ../screenshot.png \ - ${DISTDIR}/assets/screenshot.png - +DISTDIR=$(./build.sh) tar -c -f - -C ${DISTDIR} \. | xz > website_dist.tar.xz - rm -fr ${DISTDIR} diff --git a/docs/website/devl.sh b/docs/website/devl.sh new file mode 100755 index 00000000..9a8c125d --- /dev/null +++ b/docs/website/devl.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +cd $(dirname $0) + +find . -type f -a \( \ + -name \*.md -o \( \ + -name \*.html -a \ + \! -path ./tutorial/index.html -a \ + \! -path ./tutorial/0\*.html \ + \) \ + -o -name toc \ + -o -name top.html \ + -o -name footer.html \ + \) \ +| entr -rs "echo 'Regenerating ...' && ./serve.sh" diff --git a/docs/website/index.html b/docs/website/index.html index 59c63303..182f12ec 100644 --- a/docs/website/index.html +++ b/docs/website/index.html @@ -52,7 +52,7 @@

    GCLI - A Git Forge CLI

    It allows you to create, inspect and interact with issues, pull- and merge requests, inspect CI and pipelines and much more.
    - Screenshot of gcli

    @@ -61,26 +61,29 @@

    General

    There is not much so far on this page. However, you can go look at

    GCLI is available in various distributions

    - + + + + Packaging status + + + +
    + If you want gcli to be available in your favourite operating system please submit them to the respective packaging tree.
    Please also tell me such that I can link to them here.
    diff --git a/docs/website/serve.sh b/docs/website/serve.sh new file mode 100755 index 00000000..f6450d26 --- /dev/null +++ b/docs/website/serve.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# + +cd $(dirname $0) + +DISTDIR=$(./build.sh) + +cleanup() { + rm -fr ${DISTDIR} +} + +trap cleanup EXIT TERM INT +python3.11 -m http.server -d ${DISTDIR} 8080 diff --git a/docs/website/tutorial/index.html b/docs/website/tutorial/index.html index 0f79b799..51929faa 100644 --- a/docs/website/tutorial/index.html +++ b/docs/website/tutorial/index.html @@ -15,19 +15,34 @@ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.8; - text-shadow: 0 1px 0 #ffffff; max-width: 73%; + text-shadow: 0 1px 0 #ffffff; + max-width: 73%; } + pre { - line-height: 1.2; + line-height: 1.3; + padding: 10px; + font-size: 13px; + background: #303030; + color: #ffffff; + overflow: scroll; + border-radius: 5px; + } + + code { + font-family: monospace; } + a { border-bottom: 1px solid #444444; color: #444444; text-decoration: none; } + a:hover { border-bottom: 0; } + diff --git a/docs/website/tutorial/top.html b/docs/website/tutorial/top.html index 1a9ac68a..03945750 100644 --- a/docs/website/tutorial/top.html +++ b/docs/website/tutorial/top.html @@ -15,19 +15,34 @@ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.8; - text-shadow: 0 1px 0 #ffffff; max-width: 73%; + text-shadow: 0 1px 0 #ffffff; + max-width: 73%; } + pre { - line-height: 1.2; + line-height: 1.3; + padding: 10px; + font-size: 13px; + background: #303030; + color: #ffffff; + overflow: scroll; + border-radius: 5px; + } + + code { + font-family: monospace; } + a { border-bottom: 1px solid #444444; color: #444444; text-decoration: none; } + a:hover { border-bottom: 0; } + From 0cdc6f276375d4d67e086e387a1c1e4f461c9e1d Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Wed, 14 Aug 2024 23:14:05 +0200 Subject: [PATCH 26/50] pipelines: refactor cmd code to adhere with the action pattern This is done for an upcoming change regarding child pipelines. Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/239 Signed-off-by: Nico Sonack --- include/gcli/cmd/pipelines.h | 2 - src/cmd/pipelines.c | 417 +++++++++++++++++++++++------------ 2 files changed, 274 insertions(+), 145 deletions(-) diff --git a/include/gcli/cmd/pipelines.h b/include/gcli/cmd/pipelines.h index cc613b43..675c2ab8 100644 --- a/include/gcli/cmd/pipelines.h +++ b/include/gcli/cmd/pipelines.h @@ -35,7 +35,6 @@ #include void gitlab_print_pipelines(struct gitlab_pipeline_list const *const list); -int gitlab_pipelines(char const *owner, char const *repo, int const count); int gitlab_mr_pipelines(char const *owner, char const *repo, int const mr_id); @@ -44,7 +43,6 @@ int gitlab_pipeline_jobs(char const *owner, char const *repo, long pipeline, void gitlab_print_jobs(struct gitlab_job_list const *const list); void gitlab_print_job_status(struct gitlab_job const *const job); -int gitlab_job_status(char const *owner, char const *repo, long const jid); int subcommand_pipelines(int argc, char *argv[]); diff --git a/src/cmd/pipelines.c b/src/cmd/pipelines.c index ca6473f1..329bb336 100644 --- a/src/cmd/pipelines.c +++ b/src/cmd/pipelines.c @@ -49,15 +49,19 @@ static void usage(void) { fprintf(stderr, "usage: gcli pipelines [-o owner -r repo] [-n number]\n"); - fprintf(stderr, " gcli pipelines [-o owner -r repo] -p pipeline [-n number]\n"); - fprintf(stderr, " gcli pipelines [-o owner -r repo] -j job [-n number] actions...\n"); + fprintf(stderr, " gcli pipelines [-o owner -r repo] -p pipeline pipeline-actions...\n"); + fprintf(stderr, " gcli pipelines [-o owner -r repo] -j job [-n number] job-actions...\n"); fprintf(stderr, "OPTIONS:\n"); fprintf(stderr, " -o owner The repository owner\n"); fprintf(stderr, " -r repo The repository name\n"); - fprintf(stderr, " -p pipeline Fetch jobs of the given pipeline\n"); + fprintf(stderr, " -p pipeline Run actions for the given pipeline\n"); fprintf(stderr, " -j job Run actions for the given job\n"); - fprintf(stderr, " -n number Number of issues to fetch (-1 = everything)\n"); - fprintf(stderr, "ACTIONS:\n"); + fprintf(stderr, " -n number Number of pipelines to fetch (-1 = everything)\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "PIPELINE ACTIONS:\n"); + fprintf(stderr, " jobs Print the list of jobs of this pipeline\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "JOB ACTIONS:\n"); fprintf(stderr, " status Display status information\n"); fprintf(stderr, " artifacts [-o filename] Download a zip archive of the artifacts of the given job\n"); fprintf(stderr, " (default output filename: artifacts.zip)\n"); @@ -117,22 +121,6 @@ gitlab_print_pipelines(struct gitlab_pipeline_list const *const list) gcli_tbl_end(table); } -int -gitlab_pipelines(char const *owner, char const *repo, int const count) -{ - struct gitlab_pipeline_list pipelines = {0}; - int rc = 0; - - rc = gitlab_get_pipelines(g_clictx, owner, repo, count, &pipelines); - if (rc < 0) - return rc; - - gitlab_print_pipelines(&pipelines); - gitlab_pipelines_free(&pipelines); - - return rc; -} - void gitlab_print_jobs(struct gitlab_job_list const *const list) { @@ -170,23 +158,6 @@ gitlab_print_jobs(struct gitlab_job_list const *const list) gcli_tbl_end(table); } -int -gitlab_pipeline_jobs(char const *owner, char const *repo, - long const id, int const count) -{ - struct gitlab_job_list jobs = {0}; - int rc = 0; - - rc = gitlab_get_pipeline_jobs(g_clictx, owner, repo, id, count, &jobs); - if (rc < 0) - return rc; - - gitlab_print_jobs(&jobs); - gitlab_free_jobs(&jobs); - - return rc; -} - void gitlab_print_job_status(struct gitlab_job const *const job) { @@ -210,39 +181,274 @@ gitlab_print_job_status(struct gitlab_job const *const job) gcli_dict_end(printer); } -int -gitlab_job_status(char const *owner, char const *repo, long const jid) +/* Pipeline actions */ +struct pipeline_action_ctx { + char const *const owner; + char const *const repo; + long const pipeline_id; + + int argc; + char **argv; +}; + +static int +action_pipeline_jobs(struct pipeline_action_ctx *ctx) +{ + int rc = 0; + struct gitlab_job_list jobs = {0}; + + rc = gitlab_get_pipeline_jobs(g_clictx, ctx->owner, ctx->repo, + ctx->pipeline_id, -1, &jobs); + + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to get pipeline jobs: %s\n", + gcli_get_error(g_clictx)); + + return EXIT_FAILURE; + } + + gitlab_print_jobs(&jobs); + gitlab_free_jobs(&jobs); + + return EXIT_SUCCESS; +} + +static struct pipeline_action { + char const *const name; + int (*fn)(struct pipeline_action_ctx *ctx); +} const pipeline_actions[] = { + { .name = "jobs", .fn = action_pipeline_jobs }, +}; + +static struct pipeline_action const * +find_pipeline_action(char const *const name) +{ + for (size_t i = 0; i < ARRAY_SIZE(pipeline_actions); ++i) { + if (strcmp(name, pipeline_actions[i].name) == 0) + return &pipeline_actions[i]; + } + + return NULL; +} + +static int +handle_pipeline_actions(char const *const owner, char const *const repo, + long const pipeline_id, int argc, char *argv[]) +{ + struct pipeline_action_ctx ctx = { + .owner = owner, + .repo = repo, + .pipeline_id = pipeline_id, + .argc = argc, + .argv = argv, + }; + + if (!ctx.argc) { + fprintf(stderr, "gcli: error: missing pipeline actions\n"); + usage(); + return EXIT_FAILURE; + } + + while (ctx.argc) { + char const *action_name; + int exit_code; + struct pipeline_action const *action; + + action_name = shift(&ctx.argc, &ctx.argv); + action = find_pipeline_action(action_name); + if (!action) { + fprintf(stderr, "gcli: error: no such pipeline action: %s\n", + action_name); + usage(); + return EXIT_FAILURE; + } + + exit_code = action->fn(&ctx); + if (exit_code) + return exit_code; + } + + return EXIT_SUCCESS; +} + +/* Job related actions */ +struct job_action_ctx { + char const *owner, *repo; + long const job_id; + + int argc; + char **argv; +}; + +static int +action_job_status(struct job_action_ctx *ctx) { struct gitlab_job job = {0}; int rc = 0; - rc = gitlab_get_job(g_clictx, owner, repo, jid, &job); - if (rc < 0) - return rc; + rc = gitlab_get_job(g_clictx, ctx->owner, ctx->repo, ctx->job_id, &job); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to get job status: %s\n", + gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } gitlab_print_job_status(&job); gitlab_free_job(&job); - return rc; + return EXIT_SUCCESS; +} + +static int +action_job_log(struct job_action_ctx *ctx) +{ + int rc = gitlab_job_get_log(g_clictx, ctx->owner, ctx->repo, ctx->job_id, stdout); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to get job log: %s\n", + gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int +action_job_cancel(struct job_action_ctx *ctx) +{ + int rc = gitlab_job_cancel(g_clictx, ctx->owner, ctx->repo, ctx->job_id); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to cancel the job: %s\n", + gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } -/* Wrappers that pass in the context to the library functions */ static int -gitlab_job_log_cb(char const *owner, char const *repo, long const jid) +action_job_retry(struct job_action_ctx *ctx) { - return gitlab_job_get_log(g_clictx, owner, repo, jid, stdout); + int rc = gitlab_job_retry(g_clictx, ctx->owner, ctx->repo, ctx->job_id); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to retry the job: %s\n", + gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } static int -gitlab_job_cancel_cb(char const *owner, char const *repo, long const jid) +action_job_artifacts(struct job_action_ctx *ctx) { - return gitlab_job_cancel(g_clictx, owner, repo, jid); + int rc = 0; + char const *outfile = "artifacts.zip"; + + if (ctx->argc && strcmp(ctx->argv[0], "-o") == 0) { + if (ctx->argc < 2) { + fprintf(stderr, "gcli: error: -o is missing the output filename\n"); + usage(); + return EXIT_FAILURE; + } + + outfile = ctx->argv[1]; + ctx->argc -= 2; + ctx->argv += 2; + } + + rc = gitlab_job_download_artifacts(g_clictx, ctx->owner, ctx->repo, + ctx->job_id, outfile); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to download file: %s\n", + gcli_get_error(g_clictx)); + + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static struct job_action { + char const *const name; /* Name on the cli */ + int (*fn)(struct job_action_ctx *ctx); /* Function to be invoked for this action */ +} const job_actions[] = { + { .name = "log", .fn = action_job_log }, + { .name = "status", .fn = action_job_status }, + { .name = "cancel", .fn = action_job_cancel }, + { .name = "retry", .fn = action_job_retry }, + { .name = "artifacts", .fn = action_job_artifacts }, +}; + +static struct job_action const * +find_job_action(char const *const name) +{ + for (size_t i = 0; i < ARRAY_SIZE(job_actions); ++i) { + if (strcmp(name, job_actions[i].name) == 0) + return &job_actions[i]; + } + + return NULL; } static int -gitlab_job_retry_cb(char const *owner, char const *repo, long const jid) +handle_job_actions(char const *const owner, char const *const repo, + long const job_id, int argc, char *argv[]) { - return gitlab_job_retry(g_clictx, owner, repo, jid); + struct job_action_ctx ctx = { + .owner = owner, + .repo = repo, + .job_id = job_id, + .argc = argc, + .argv = argv, + }; + + /* Check if the user missed out on supplying actions */ + if (ctx.argc == 0) { + fprintf(stderr, "gcli: error: no actions supplied\n"); + usage(); + exit(EXIT_FAILURE); + } + + /* Parse and execute all the actions */ + while (ctx.argc) { + int exit_code = 0; + char const *action_name; + struct job_action const *action; + + action_name = shift(&ctx.argc, &ctx.argv); + action = find_job_action(action_name); + + if (!action) { + fprintf(stderr, "gcli: error: unknown action '%s'\n", action_name); + usage(); + return EXIT_FAILURE; + } + + exit_code = action->fn(&ctx); + if (exit_code) + return exit_code; + } + + return EXIT_SUCCESS; +} + +static int +list_pipelines(char const *const owner, char const *const repo, int max) +{ + struct gitlab_pipeline_list list = {0}; + int rc = 0; + + rc = gitlab_get_pipelines(g_clictx, owner, repo, max, &list); + if (rc < 0) { + fprintf(stderr, "gcli: failed to get pipelines: %s\n", + gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } + + gitlab_print_pipelines(&list); + gitlab_pipelines_free(&list); + + return EXIT_SUCCESS; } int @@ -251,8 +457,8 @@ subcommand_pipelines(int argc, char *argv[]) int ch = 0; char const *owner = NULL, *repo = NULL; int count = 30; - long pid = -1; /* pipeline id */ - long jid = -1; /* job id. these are mutually exclusive. */ + long pipeline_id = -1; /* pipeline id */ + long job_id = -1; /* job id. these are mutually exclusive. */ /* Parse options */ const struct option options[] = { @@ -280,19 +486,19 @@ subcommand_pipelines(int argc, char *argv[]) } break; case 'p': { char *endptr = NULL; - pid = strtol(optarg, &endptr, 10); + pipeline_id = strtol(optarg, &endptr, 10); if (endptr != (optarg + strlen(optarg))) err(1, "gcli: error: cannot parse argument to -p"); - if (pid < 0) { + if (pipeline_id < 0) { errx(1, "gcli: error: pipeline id must be a positive number"); } } break; case 'j': { char *endptr = NULL; - jid = strtol(optarg, &endptr, 10); + job_id = strtol(optarg, &endptr, 10); if (endptr != (optarg + strlen(optarg))) err(1, "gcli: error: cannot parse argument to -j"); - if (jid < 0) { + if (job_id < 0) { errx(1, "gcli: error: job id must be a positive number"); } } break; @@ -306,7 +512,7 @@ subcommand_pipelines(int argc, char *argv[]) argc -= optind; argv += optind; - if (pid > 0 && jid > 0) { + if (pipeline_id > 0 && job_id > 0) { fprintf(stderr, "gcli: error: -p and -j are mutually exclusive\n"); usage(); return EXIT_FAILURE; @@ -320,96 +526,21 @@ subcommand_pipelines(int argc, char *argv[]) errx(1, "gcli: error: The pipelines subcommand only works for GitLab. " "Use gcli -t gitlab ... to force a GitLab remote."); - /* If the user specified a pipeline id, print the jobs of that - * given pipeline */ - if (pid >= 0) { - /* Make sure we are interpreting things correctly */ - if (argc != 0) { - fprintf(stderr, "gcli: error: stray arguments\n"); - usage(); - return EXIT_FAILURE; - } + /* In case a Pipeline ID was specified handle its actions */ + if (pipeline_id >= 0) + return handle_pipeline_actions(owner, repo, pipeline_id, argc, argv); - if (gitlab_pipeline_jobs(owner, repo, pid, count) < 0) { - errx(1, "gcli: error: failed to get pipeline jobs: %s", - gcli_get_error(g_clictx)); - } - return EXIT_SUCCESS; - } + /* A Job ID was specified */ + if (job_id >= 0) + return handle_job_actions(owner, repo, job_id, argc, argv); - /* if the user didn't specify the -j option to list jobs, list the + /* Neither a Job id nor a pipeline ID was specified - list all * pipelines instead */ - if (jid < 0) { - /* Make sure we are interpreting things correctly */ - if (argc != 0) { - fprintf(stderr, "gcli: error: stray arguments\n"); - usage(); - return EXIT_FAILURE; - } - - if (gitlab_pipelines(owner, repo, count) < 0) { - errx(1, "gcli: error: failed to get pipelines: %s", - gcli_get_error(g_clictx)); - } - - return EXIT_SUCCESS; - } - - /* At this point jid contains a (hopefully) valid job id */ - - /* Definition of the action list */ - struct { - char const *name; /* Name on the cli */ - int (*fn)(char const *, char const *, long); /* Function to be invoked for this action */ - } job_actions[] = { - { .name = "log", .fn = gitlab_job_log_cb }, - { .name = "status", .fn = gitlab_job_status }, - { .name = "cancel", .fn = gitlab_job_cancel_cb }, - { .name = "retry", .fn = gitlab_job_retry_cb }, - }; - - /* Check if the user missed out on supplying actions */ - if (argc == 0) { - fprintf(stderr, "gcli: error: no actions supplied\n"); - usage(); - exit(EXIT_FAILURE); - } - -next_action: - while (argc) { - char const *action = shift(&argc, &argv); - - /* Handle the artifacts action separately because it allows a - * -o flag. No other action supports flags. */ - if (strcmp(action, "artifacts") == 0) { - char const *outfile = "artifacts.zip"; - if (argc && strcmp(argv[0], "-o") == 0) { - if (argc < 2) - errx(1, "gcli: error: -o is missing the output filename"); - outfile = argv[1]; - argc -= 2; - argv += 2; - } - if (gitlab_job_download_artifacts(g_clictx, owner, repo, jid, outfile) < 0) { - errx(1, "gcli: error: failed to download file: %s", - gcli_get_error(g_clictx)); - } - goto next_action; - } - - /* Find the action and invoke it */ - for (size_t i = 0; i < ARRAY_SIZE(job_actions); ++i) { - if (strcmp(action, job_actions[i].name) == 0) { - if (job_actions[i].fn(owner, repo, jid) < 0) - errx(1, "gcli: error: failed to perform action '%s'", action); - goto next_action; - } - } - - fprintf(stderr, "gcli: error: unknown action '%s'\n", action); + if (argc != 0) { + fprintf(stderr, "gcli: error: stray arguments\n"); usage(); return EXIT_FAILURE; } - return EXIT_SUCCESS; + return list_pipelines(owner, repo, count); } From 9b880c3cfc8906a5f024faac9808e9e318523de1 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 15 Aug 2024 12:50:46 +0200 Subject: [PATCH 27/50] pipelines: add action for listing pipeline children Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/239 Signed-off-by: Nico Sonack --- include/gcli/gitlab/pipelines.h | 4 ++++ src/cmd/pipelines.c | 25 ++++++++++++++++++++++++- src/gitlab/pipelines.c | 25 +++++++++++++++++++++++++ templates/gitlab/pipelines.t | 7 +++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/include/gcli/gitlab/pipelines.h b/include/gcli/gitlab/pipelines.h index e89e5373..802ac9dd 100644 --- a/include/gcli/gitlab/pipelines.h +++ b/include/gcli/gitlab/pipelines.h @@ -82,6 +82,10 @@ int gitlab_get_pipeline_jobs(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pipeline, int count, struct gitlab_job_list *out); +int gitlab_get_pipeline_children(struct gcli_ctx *ctx, char const *owner, + char const *repo, gcli_id pipeline, int count, + struct gitlab_pipeline_list *out); + void gitlab_free_jobs(struct gitlab_job_list *jobs); void gitlab_free_job(struct gitlab_job *job); diff --git a/src/cmd/pipelines.c b/src/cmd/pipelines.c index 329bb336..641924de 100644 --- a/src/cmd/pipelines.c +++ b/src/cmd/pipelines.c @@ -59,6 +59,7 @@ usage(void) fprintf(stderr, " -n number Number of pipelines to fetch (-1 = everything)\n"); fprintf(stderr, "\n"); fprintf(stderr, "PIPELINE ACTIONS:\n"); + fprintf(stderr, " children Print the list of child pipelines\n"); fprintf(stderr, " jobs Print the list of jobs of this pipeline\n"); fprintf(stderr, "\n"); fprintf(stderr, "JOB ACTIONS:\n"); @@ -213,11 +214,33 @@ action_pipeline_jobs(struct pipeline_action_ctx *ctx) return EXIT_SUCCESS; } +static int +action_pipeline_children(struct pipeline_action_ctx *ctx) +{ + int rc = 0; + struct gitlab_pipeline_list children = {0}; + + rc = gitlab_get_pipeline_children(g_clictx, ctx->owner, ctx->repo, + ctx->pipeline_id, -1, &children); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to get pipeline children: %s\n", + gcli_get_error(g_clictx)); + + return EXIT_FAILURE; + } + + gitlab_print_pipelines(&children); + gitlab_pipelines_free(&children); + + return EXIT_SUCCESS; +} + static struct pipeline_action { char const *const name; int (*fn)(struct pipeline_action_ctx *ctx); } const pipeline_actions[] = { - { .name = "jobs", .fn = action_pipeline_jobs }, + { .name = "jobs", .fn = action_pipeline_jobs }, + { .name = "children", .fn = action_pipeline_children }, }; static struct pipeline_action const * diff --git a/src/gitlab/pipelines.c b/src/gitlab/pipelines.c index 2c0f9fd8..70337ed0 100644 --- a/src/gitlab/pipelines.c +++ b/src/gitlab/pipelines.c @@ -139,6 +139,31 @@ gitlab_get_pipeline_jobs(struct gcli_ctx *ctx, char const *owner, return gcli_fetch_list(ctx, url, &fl); } +int +gitlab_get_pipeline_children(struct gcli_ctx *ctx, char const *owner, + char const *repo, gcli_id pipeline, int count, + struct gitlab_pipeline_list *out) +{ + char *url = NULL, *e_owner = NULL, *e_repo = NULL; + struct gcli_fetch_list_ctx fl = { + .listp = &out->pipelines, + .sizep = &out->pipelines_size, + .max = count, + .parse = (parsefn)(parse_gitlab_pipeline_children), + }; + + e_owner = gcli_urlencode(owner); + e_repo = gcli_urlencode(repo); + + url = sn_asprintf("%s/projects/%s%%2F%s/pipelines/%"PRIid"/bridges", + gcli_get_apibase(ctx), e_owner, e_repo, pipeline); + + free(e_owner); + free(e_repo); + + return gcli_fetch_list(ctx, url, &fl); +} + void gitlab_free_job(struct gitlab_job *const job) { diff --git a/templates/gitlab/pipelines.t b/templates/gitlab/pipelines.t index 2e18ffe4..fdaed634 100644 --- a/templates/gitlab/pipelines.t +++ b/templates/gitlab/pipelines.t @@ -34,3 +34,10 @@ object of struct gitlab_job with parser gitlab_jobs is array of struct gitlab_job use parse_gitlab_job; + +parser gitlab_pipeline_child is +object of struct gitlab_pipeline with + ("pipeline" => use parse_gitlab_pipeline); + +parser gitlab_pipeline_children is +array of struct gitlab_pipeline use parse_gitlab_pipeline_child; From a2d67200895e0f3d976782520591445832d948b9 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 15 Aug 2024 13:25:22 +0200 Subject: [PATCH 28/50] pipelines: Add name field to pipeline This field is only sometimes derived in case we have a child pipeline. The /pipelines endpoint itself doesn't seem to return valid names in any case. Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/239 Signed-off-by: Nico Sonack --- include/gcli/gitlab/pipelines.h | 1 + src/cmd/pipelines.c | 2 ++ templates/gitlab/pipelines.t | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/gcli/gitlab/pipelines.h b/include/gcli/gitlab/pipelines.h index 802ac9dd..ba82901d 100644 --- a/include/gcli/gitlab/pipelines.h +++ b/include/gcli/gitlab/pipelines.h @@ -44,6 +44,7 @@ struct gitlab_pipeline { char *ref; char *sha; char *source; + char *name; }; struct gitlab_pipeline_list { diff --git a/src/cmd/pipelines.c b/src/cmd/pipelines.c index 641924de..f1c62f31 100644 --- a/src/cmd/pipelines.c +++ b/src/cmd/pipelines.c @@ -98,6 +98,7 @@ gitlab_print_pipelines(struct gitlab_pipeline_list const *const list) { .name = "STATUS", .type = GCLI_TBLCOLTYPE_STRING, .flags = GCLI_TBLCOL_STATECOLOURED }, { .name = "CREATED", .type = GCLI_TBLCOLTYPE_STRING, .flags = 0 }, { .name = "UPDATED", .type = GCLI_TBLCOLTYPE_STRING, .flags = 0 }, + { .name = "NAME", .type = GCLI_TBLCOLTYPE_STRING, .flags = 0 }, { .name = "REF", .type = GCLI_TBLCOLTYPE_STRING, .flags = 0 }, }; @@ -116,6 +117,7 @@ gitlab_print_pipelines(struct gitlab_pipeline_list const *const list) list->pipelines[i].status, list->pipelines[i].created_at, list->pipelines[i].updated_at, + list->pipelines[i].name, list->pipelines[i].ref); } diff --git a/templates/gitlab/pipelines.t b/templates/gitlab/pipelines.t index fdaed634..008ecf6c 100644 --- a/templates/gitlab/pipelines.t +++ b/templates/gitlab/pipelines.t @@ -37,7 +37,8 @@ array of struct gitlab_job use parse_gitlab_job; parser gitlab_pipeline_child is object of struct gitlab_pipeline with - ("pipeline" => use parse_gitlab_pipeline); + ("downstream_pipeline" => use parse_gitlab_pipeline, + "name" => name as string); parser gitlab_pipeline_children is array of struct gitlab_pipeline use parse_gitlab_pipeline_child; From 390078a2b406e6659e3d6c7bc99ea7404e23b662 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 15 Aug 2024 13:27:13 +0200 Subject: [PATCH 29/50] pipelines: Add support for grabbing a single pipeline and printing its status Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/239 Signed-off-by: Nico Sonack --- include/gcli/gitlab/pipelines.h | 4 ++++ src/cmd/pipelines.c | 41 +++++++++++++++++++++++++++++++++ src/gitlab/pipelines.c | 33 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/include/gcli/gitlab/pipelines.h b/include/gcli/gitlab/pipelines.h index ba82901d..56e1cd62 100644 --- a/include/gcli/gitlab/pipelines.h +++ b/include/gcli/gitlab/pipelines.h @@ -76,6 +76,10 @@ int gitlab_get_pipelines(struct gcli_ctx *ctx, char const *owner, char const *repo, int max, struct gitlab_pipeline_list *out); +int gitlab_get_pipeline(struct gcli_ctx *ctx, char const *owner, + char const *repo, gcli_id pipeline_id, + struct gitlab_pipeline *out); + void gitlab_pipeline_free(struct gitlab_pipeline *pipeline); void gitlab_pipelines_free(struct gitlab_pipeline_list *list); diff --git a/src/cmd/pipelines.c b/src/cmd/pipelines.c index f1c62f31..f2b57ffb 100644 --- a/src/cmd/pipelines.c +++ b/src/cmd/pipelines.c @@ -184,6 +184,25 @@ gitlab_print_job_status(struct gitlab_job const *const job) gcli_dict_end(printer); } +void +gitlab_print_pipeline(struct gitlab_pipeline const *const pipeline) +{ + gcli_dict printer; + + printer = gcli_dict_begin(); + + gcli_dict_add(printer, "ID", 0, 0, "%"PRIid, pipeline->id); + gcli_dict_add_string(printer, "NAME", 0, 0, pipeline->name ? pipeline->name : "N/A"); + gcli_dict_add_string(printer, "STATUS", GCLI_TBLCOL_STATECOLOURED, 0, pipeline->status); + gcli_dict_add_string(printer, "CREATED", 0, 0, pipeline->created_at); + gcli_dict_add_string(printer, "UPDATED", 0, 0, pipeline->updated_at); + gcli_dict_add_string(printer, "REF", GCLI_TBLCOL_COLOUREXPL, GCLI_COLOR_YELLOW, pipeline->ref); + gcli_dict_add_string(printer, "SHA", GCLI_TBLCOL_COLOUREXPL, GCLI_COLOR_YELLOW, pipeline->sha); + gcli_dict_add_string(printer, "SOURCE", 0, 0, pipeline->source); + + gcli_dict_end(printer); +} + /* Pipeline actions */ struct pipeline_action_ctx { char const *const owner; @@ -194,6 +213,27 @@ struct pipeline_action_ctx { char **argv; }; +static int +action_pipeline_status(struct pipeline_action_ctx *ctx) +{ + int rc = 0; + struct gitlab_pipeline pipeline = {0}; + + rc = gitlab_get_pipeline(g_clictx, ctx->owner, ctx->repo, + ctx->pipeline_id, &pipeline); + if (rc < 0) { + fprintf(stderr, "gcli: error: failed to get pipeline: %s\n", + gcli_get_error(g_clictx)); + + return EXIT_FAILURE; + } + + gitlab_print_pipeline(&pipeline); + gitlab_pipeline_free(&pipeline); + + return EXIT_SUCCESS; +} + static int action_pipeline_jobs(struct pipeline_action_ctx *ctx) { @@ -241,6 +281,7 @@ static struct pipeline_action { char const *const name; int (*fn)(struct pipeline_action_ctx *ctx); } const pipeline_actions[] = { + { .name = "status", .fn = action_pipeline_status }, { .name = "jobs", .fn = action_pipeline_jobs }, { .name = "children", .fn = action_pipeline_children }, }; diff --git a/src/gitlab/pipelines.c b/src/gitlab/pipelines.c index 70337ed0..94a674fd 100644 --- a/src/gitlab/pipelines.c +++ b/src/gitlab/pipelines.c @@ -73,6 +73,39 @@ gitlab_get_pipelines(struct gcli_ctx *ctx, char const *owner, char const *repo, return fetch_pipelines(ctx, url, max, list); } +int +gitlab_get_pipeline(struct gcli_ctx *ctx, char const *owner, + char const *repo, gcli_id pipeline_id, + struct gitlab_pipeline *out) +{ + char *e_owner = gcli_urlencode(owner); + char *e_repo = gcli_urlencode(repo); + char *url = NULL; + int rc = 0; + struct gcli_fetch_buffer buffer = {0}; + struct json_stream stream = {0}; + + url = sn_asprintf("%s/projects/%s%%2F%s/pipelines/%"PRIid, + gcli_get_apibase(ctx), e_owner, e_repo, pipeline_id); + + free(e_owner); + free(e_repo); + + rc = gcli_fetch(ctx, url, NULL, &buffer); + if (rc == 0) { + json_open_buffer(&stream, buffer.data, buffer.length); + + rc = parse_gitlab_pipeline(ctx, &stream, out); + + json_close(&stream); + gcli_fetch_buffer_free(&buffer); + } + + free(url); + + return rc; +} + int gitlab_get_mr_pipelines(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id const mr_id, struct gitlab_pipeline_list *const list) From a957805cf185c513303c9cc7d30cf85b0c8c6967 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 15 Aug 2024 13:27:40 +0200 Subject: [PATCH 30/50] pipelines: Add an 'all' action for printing the entire status overview of a pipeline Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/239 Signed-off-by: Nico Sonack --- src/cmd/pipelines.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/cmd/pipelines.c b/src/cmd/pipelines.c index f2b57ffb..84205084 100644 --- a/src/cmd/pipelines.c +++ b/src/cmd/pipelines.c @@ -59,6 +59,7 @@ usage(void) fprintf(stderr, " -n number Number of pipelines to fetch (-1 = everything)\n"); fprintf(stderr, "\n"); fprintf(stderr, "PIPELINE ACTIONS:\n"); + fprintf(stderr, " all Show status of this pipeline (including jobs and children)\n"); fprintf(stderr, " children Print the list of child pipelines\n"); fprintf(stderr, " jobs Print the list of jobs of this pipeline\n"); fprintf(stderr, "\n"); @@ -277,10 +278,37 @@ action_pipeline_children(struct pipeline_action_ctx *ctx) return EXIT_SUCCESS; } +static int +action_pipeline_all(struct pipeline_action_ctx *ctx) +{ + int exit_code = 0; + + exit_code = action_pipeline_status(ctx); + if (exit_code) + return exit_code; + + fprintf(stdout, "\n"); + + fprintf(stdout, "JOBS\n"); + exit_code = action_pipeline_jobs(ctx); + if (exit_code) + return exit_code; + + fprintf(stdout, "\n"); + + fprintf(stdout, "CHILDREN\n"); + exit_code = action_pipeline_children(ctx); + if (exit_code) + return exit_code; + + return EXIT_SUCCESS; +} + static struct pipeline_action { char const *const name; int (*fn)(struct pipeline_action_ctx *ctx); } const pipeline_actions[] = { + { .name = "all", .fn = action_pipeline_all }, { .name = "status", .fn = action_pipeline_status }, { .name = "jobs", .fn = action_pipeline_jobs }, { .name = "children", .fn = action_pipeline_children }, From ba67b62e39d56b426ebbaf3475171f2f801e482d Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 15 Aug 2024 14:08:44 +0200 Subject: [PATCH 31/50] docs: Update gcli-pipelines(1) manual page This adds documentation for the new actions in the pipelines subcommand. Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/239 Signed-off-by: Nico Sonack --- docs/gcli-pipelines.1.in | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/gcli-pipelines.1.in b/docs/gcli-pipelines.1.in index 8ea8c6ae..f09a839b 100644 --- a/docs/gcli-pipelines.1.in +++ b/docs/gcli-pipelines.1.in @@ -11,11 +11,11 @@ .Nm .Fl p Ar pipeline-id .Op Fl o Ar owner Fl r Ar repo -.Op Fl n Ar n +.Ar pipeline-actions... .Nm .Fl j Ar job-id .Op Fl o Ar owner Fl r Ar repo -.Ar actions... +.Ar job-actions... .Sh DESCRIPTION .Nm is used to display data about the Continuous Integration (CI) service @@ -41,7 +41,8 @@ to -1 will fetch all items. Default: 30. Note that on large repositories fetching all items can take a considerable amount of time and may result in rate limiting by the GitLab API. .It Fl p , -pipeline Ar pipeline-id -List jobs in the given pipeline. +Execute the given actions for the specified +.Ar pipeline-id . .It Fl j , -jobs Ar job execute the given .Ar actions @@ -49,7 +50,7 @@ for the specified .Ar job . .El .Sh ACTIONS -.Ar actions... +.Ar job-actions... may be one or more of the following: .Bl -tag -width artifacts .It Cm log @@ -67,15 +68,34 @@ but it can be overridden by using the .Fl o flag. .El +.Pp +.Pp +.Ar pipeline-actions... +may be one or more of the following: +.Bl -tag -width children +.It Cm all +Print a summary with all information about the pipeline. +.It Cm status +Print a short status summary of the pipeline. +.It Cm jobs +Print a list of jobs running in the pipeline. +.It Cm children +Print a list of child pipelines triggered by this pipeline. +.El .Sh EXAMPLES Print a list of the last 30 pipelines in the current project: .Bd -literal -offset indent $ gcli pipelines .Ed .Pp -List all jobs of pipeline #3316: +Print a summary of pipeline 420: +.Bd -literal -offset indent +$ gcli pipelines -p 420 all +.Ed +.Pp +List only jobs of pipeline #3316: .Bd -literal -offset indent -$ gcli pipelines -p3316 +$ gcli pipelines -p3316 jobs .Ed .Pp Dump the log of Job #423141 in herrhotzenplotz/gcli: From 1d2de97ed7f7d93ce5744a16dca5278dc23a3930 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Wed, 21 Feb 2024 17:54:50 +0100 Subject: [PATCH 32/50] Remove generated HTML This content is generated by scripts and only causes noise in the git history. Fixes: https://gitlab.com/herrhotzenplotz/gcli/issues/234 Signed-off-by: Nico Sonack --- docs/website/tutorial/01-Installation.html | 121 ---------------- docs/website/tutorial/02-First-Steps.html | 123 ----------------- docs/website/tutorial/02-First-Steps.md | 19 +++ .../tutorial/03-Find-Documentation.html | 108 --------------- docs/website/tutorial/04-Account-Setup.html | 92 ------------- .../tutorial/05-Creating-an-issue.html | 102 -------------- docs/website/tutorial/06-Commenting.html | 72 ---------- .../tutorial/07-Creating-a-pull-request.html | 91 ------------ docs/website/tutorial/08-Other-forges.html | 129 ------------------ docs/website/tutorial/index.html | 66 --------- 10 files changed, 19 insertions(+), 904 deletions(-) delete mode 100644 docs/website/tutorial/01-Installation.html delete mode 100644 docs/website/tutorial/02-First-Steps.html delete mode 100644 docs/website/tutorial/03-Find-Documentation.html delete mode 100644 docs/website/tutorial/04-Account-Setup.html delete mode 100644 docs/website/tutorial/05-Creating-an-issue.html delete mode 100644 docs/website/tutorial/06-Commenting.html delete mode 100644 docs/website/tutorial/07-Creating-a-pull-request.html delete mode 100644 docs/website/tutorial/08-Other-forges.html delete mode 100644 docs/website/tutorial/index.html diff --git a/docs/website/tutorial/01-Installation.html b/docs/website/tutorial/01-Installation.html deleted file mode 100644 index d10c06ba..00000000 --- a/docs/website/tutorial/01-Installation.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - GCLI Tutorial | Installation - - - - - - - - - -
    -

    Installing GCLI

    -

    Through package manager

    -

    If you're on FreeBSD you can just install gcli by running the -following command:

    -
    # pkg install gcli
    -
    -

    On NetBSD you can run:

    -
    # pkgin install gcli
    -
    -

    On Ubuntu, Debian, Devuan and the like you can run:

    -
    # apt install gcli
    -
    -

    On ArchLinux you can either use the AUR manually or use your favourite AUR helper:

    -
    # yay -S gcli
    -
    -

    Compile the source code

    -

    Other operating systems may currently require manual compilation and installation.

    -

    Windows NT Notes

    -

    It is entirely possible to build gcli on Windows using -MSYS2. Please follow their instructions on -how to set up a development environment.

    -

    Generic build instructions

    -

    For this purpose go to -https://herrhotzenplotz.de/gcli/releases -and choose the latest release. Then download one of the tarballs.

    -

    For version 1.1.0 this would be:

    -

    https://herrhotzenplotz.de/gcli/releases/gcli-1.1.0/gcli-1.1.0.tar.xz

    -

    Now that you have a link, you can download it, extract it, compile the -code and install it:

    -
    $ mkdir ~/build
    -$ cd ~/build
    -$ curl -4LO https://herrhotzenplotz.de/gcli/releases/gcli-1.1.0/gcli-1.1.0.tar.xz
    -  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
    -                                 Dload  Upload   Total   Spent    Left  Speed
    -100  342k  100  342k    0     0  2739k      0 --:--:-- --:--:-- --:--:-- 2736k
    -$ ls
    -gcli-1.1.0.tar.xz
    -$
    -
    -

    Install the dependencies for building gcli:

    -

    e.g. on Debian systems:

    -
    # apt install libcurl4-openssl-dev pkgconf build-essential
    -
    -

    or on MSYS2:

    -
    $ pacman -S libcurl-devel pkgconf
    -
    -

    Extract the tarball:

    -
    $ tar xf gcli-1.1.0.tar.xz
    -$ cd gcli-1.1.0
    -
    -

    Configure, build and install gcli:

    -
    $ ./configure
    -...
    -$ make
    -...
    -$ make install
    -
    -

    Check that the shell finds gcli:

    -
    $ which gcli
    -/usr/local/bin/gcli
    -$
    -$ gcli version
    -gcli 1.1.0 (amd64-unknown-freebsd13.2)
    -Using libcurl/8.1.2 OpenSSL/1.1.1t zlib/1.2.13 libpsl/0.21.2 (+libidn2/2.3.4) libssh2/1.11.0 nghttp2/1.53.0
    -Using vendored pdjson library
    -Report bugs at https://gitlab.com/herrhotzenplotz/gcli/.
    -Copyright 2021, 2022, 2023 Nico Sonack <nsonack@herrhotzenplotz.de> and contributors.
    -$
    -
    -

    Advanced Windows Environment Setup

    -

    In case you want to use the installed gcli from outside MSYS2 (e.g. -in cmd.exe) you may wish to update the Path environment variable -and add the C:\msys2\usr\bin directory to it. Make sure you have -the library paths set up correctly.

    -
    -
    - - - diff --git a/docs/website/tutorial/02-First-Steps.html b/docs/website/tutorial/02-First-Steps.html deleted file mode 100644 index 375f9945..00000000 --- a/docs/website/tutorial/02-First-Steps.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - GCLI Tutorial | First Steps - - - - - - - - - -
    -

    First steps

    -

    Listing issues

    -

    Let's start off by listing some issues - here for the curl project -which is hosted on GitHub under curl/curl. To list issues for it one -would run:

    -
    $ gcli -t github issues -o curl -r curl
    -
    -

    You will see the list of the 30 most recent open issue tickets. The -command above does the following:

    -
      -
    • invoke gcli
    • -
    • as a global option we switch it into GitHub-Mode
    • -
    • invoke the issues subcommand
    • -
    • operate on the repository owner curl (-o curl)
    • -
    • operate on the repository curl (-r curl)
    • -
    -

    Note that the -t github option goes before the issues subcommand -because it is a global option for gcli that affects how all the -following things like subcommands operate.

    -

    However, now I also want to see closed issues:

    -
    $ gcli -t github issues -o curl -r curl -a
    -
    -

    The -a option will disregard the status of the issue.

    -

    Oh and the screen is a bit cluttered by all these tickets - let's only -fetch the first 10 issues:

    -
    $ gcli -t github issues -o curl -r curl -n10
    -
    -

    Examining issues

    -

    As of now we only produced lists of issues. However, we may also want -to look at the details of an issue such as:

    -
      -
    • the original post
    • -
    • labels
    • -
    • comments
    • -
    • assignees of the issue (that is someone who is working on the bug)
    • -
    -

    Let's get a good summary of issue #11268 in the curl project:

    -
    $ gcli -t github issues -o curl -r curl -i 11268 all
    -
    -

    As you can see most of the options are the same, however now we tell -gcli with the -i 11268 option that we want to work with a single -issue. Then we tell gcli what actions to perform on the issue. Another -important action is comments. Guess what it does:

    -
    $ gcli -t github issues -o curl -r curl -i 11268 comments
    -
    -

    I know a person that likes to post long verbose traces. Let's search -for an issue authored by them on the OpenSSL GitHub page:

    -
    $ gcli -t github issues -o openssl -r openssl -A blastwave -a
    -NUMBER  STATE   TITLE
    - 20379  open    test "80-test_ssl_new.t" fails on Solaris 10 SPARCv9
    - 10547  open    Strict C90 CFLAGS results in sha.h:91 ISO C90 does not support long long
    -  8048  closed  OPENSSL_strnlen SIGSEGV in o_str.c line 76
    -$
    -
    -

    The -A option lets you filter for specific authors.

    -

    Let's look at the issue state of #10547:

    -
    $ gcli -t github issues -o openssl -r openssl -i 10547 status
    -     NAME : 10547
    -    TITLE : Strict C90 CFLAGS results in sha.h:91 ISO C90 does not support long long
    -  CREATED : 2019-12-01T04:35:23Z
    -   AUTHOR : blastwave
    -    STATE : open
    - COMMENTS : 9
    -   LOCKED : no
    -   LABELS : triaged: bug
    -ASSIGNEES : none
    -$
    -
    -

    That's nine comments - let's read the original post and the comments -in our favourite pager less:

    -
    $ gcli -t github issues -o openssl -r openssl -i 10547 op comments | less
    -
    -

    As you can see gcli will accept multiple actions for an issue and -executes them sequentially.

    -
    -
    - - - diff --git a/docs/website/tutorial/02-First-Steps.md b/docs/website/tutorial/02-First-Steps.md index cf888bd8..fc2e0623 100644 --- a/docs/website/tutorial/02-First-Steps.md +++ b/docs/website/tutorial/02-First-Steps.md @@ -32,6 +32,25 @@ fetch the first 10 issues: $ gcli -t github issues -o curl -r curl -n10 +## Searching for issues + +Before reporting a bug or looking for solutions to a problem that +you found in a program you may want to search for issues. + +Let's search for `avast` in `curl/curl`: + + $ gcli -t github issues -o curl -r curl -a avast + NUMBER NOTES STATE TITLE + 11383 9 open Issue with FileZilla server (GnuTLS) and close_notify + 10551 7 closed Unable to use curl 7.87 for SSL connections on Windows 10 Pro + 8848 2 closed CURL SSL certificate problem when AVAST HTTPS scanning enabled + $ + +As you can see searching for something is just a matter of appending +keywords to the `issues` subcommand. You can specify any amount of +search terms - they will all be used in the query. Again, `-a` +ignores the status of the issue such that we also see closed tickets. + ## Examining issues As of now we only produced lists of issues. However, we may also want diff --git a/docs/website/tutorial/03-Find-Documentation.html b/docs/website/tutorial/03-Find-Documentation.html deleted file mode 100644 index b7662a89..00000000 --- a/docs/website/tutorial/03-Find-Documentation.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - - GCLI Tutorial | How to find documentation - - - - - - - - - -
    -

    How to find documentation

    -

    When using gcli one may not always remember all the options and flags -for every subcommand. gcli has lots of integrated help to guide you -through its commands.

    -

    Subcommand help

    -

    You can list all available options for the issues subcommand by doing:

    -
    $ gcli issues --help
    -
    -

    With your current knowledge you can also explore the gcli pulls subcommand.

    -

    General usage

    -

    Run the following command:

    -
    $ gcli --help
    -usage: gcli [options] subcommand
    -
    -OPTIONS:
    -  -a account     Use the configured account instead of inferring it
    -  -r remote      Infer account from the given git remote
    -  -t type        Force the account type:
    -                    - github (default: github.com)
    -                    - gitlab (default: gitlab.com)
    -                    - gitea (default: codeberg.org)
    -  -c             Force colour and text formatting.
    -  -q             Be quiet. (Not implemented yet)
    -
    -  -v             Be verbose.
    -
    -SUBCOMMANDS:
    -  ci             Github CI status info
    -  comment        Comment under issues and PRs
    -  config         Configure forges
    -  forks          Create, delete and list repository forks
    -  gists          Create, fetch and list Github Gists
    -  issues         Manage issues
    -  labels         Manage issue and PR labels
    -  milestones     Milestone handling
    -  pipelines      Gitlab CI management
    -  pulls          Create, view and manage PRs
    -  releases       Manage releases of repositories
    -  repos          Remote Repository management
    -  snippets       Fetch and list Gitlab snippets
    -  status         General user status and notifications
    -  api            Fetch plain JSON info from an API (for debugging purposes)
    -  version        Print version
    -
    -gcli 1.2.0 (amd64-unknown-freebsd13.2)
    -Using libcurl/8.1.2 OpenSSL/1.1.1t zlib/1.2.13 libpsl/0.21.2 (+libidn2/2.3.4) libssh2/1.11.0 nghttp2/1.53.0
    -Using vendored pdjson library
    -Report bugs at https://gitlab.com/herrhotzenplotz/gcli/.
    -Copyright 2021, 2022, 2023 Nico Sonack <nsonack@herrhotzenplotz.de> and contributors.
    -
    -

    This gives you an overview over all the available subcommands. Each -subcommand in turn allows you to get its usage by supplying the ---help option to it.

    -

    Manual pages

    -

    Furthermore I recommend reading into the manual page gcli-issues(1) -and gcli-pulls(1):

    -
    $ man gcli-issues
    -$ man gcli-pulls
    -
    -
    -
    - - - diff --git a/docs/website/tutorial/04-Account-Setup.html b/docs/website/tutorial/04-Account-Setup.html deleted file mode 100644 index 43b98c4c..00000000 --- a/docs/website/tutorial/04-Account-Setup.html +++ /dev/null @@ -1,92 +0,0 @@ - - - - - GCLI Tutorial | Setting up an account - - - - - - - - - -
    -

    Setting up gcli for use with an account

    -

    Creating issues on GitHub requires an account which we need to -generate an authentication token for gcli.

    -

    Log into your GitHub account and click on your account icon in the top -right corner. Then choose the Settings option. Scroll down and -choose Developer settings on the bottom of the left column. Under -Personal access tokens choose Tokens (classic).

    -

    Click on Generate new token (classic).

    -

    Set a useful name such as gcli in the Note field, set the expiration -to No expiration and allow the following Scopes:

    -
      -
    • repo
    • -
    • workflow
    • -
    • admin:public_key
    • -
    • gist
    • -
    -

    Then create the token. It'll be printed in green. Do not share it!

    -

    Now we need to tell gcli about this new token. To do this, create -a configuration file for gcli - on Windows you need to do this from -the MSYS2 Shell:

    -
    $ mkdir -p ${HOME}/.config/gcli
    -$ vi ${HOME}/.config/gcli/config
    -
    -

    Obviously, you can choose any other editor of your choice. Put the -following into this file:

    -
    defaults {
    -    editor=vi
    -    github-default-account=my-github-account
    -}
    -
    -my-github-account {
    -    token=<token-goes-here>
    -    account=<account-name>
    -    forge-type=github
    -}
    -
    -

    Replace the <token-goes-here> with the previously generated token -and the <account> with your account name.

    -

    If you now run

    -
    $ gcli -t github repos
    -
    -

    you should get a list of your repos. If not, check again that you did -all the steps above correctly.

    -
    -
    - - - diff --git a/docs/website/tutorial/05-Creating-an-issue.html b/docs/website/tutorial/05-Creating-an-issue.html deleted file mode 100644 index 3e17b005..00000000 --- a/docs/website/tutorial/05-Creating-an-issue.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - - GCLI Tutorial | Creating an issue - - - - - - - - - -
    -

    Creating an issue

    -

    Note: This assumes you have configured gcli with an account for GitHub already.

    -

    Preparation

    -

    For this case I have a playground repository that you may as well use -for testing with gcli. It is available at -herrhotzenplotz/ghcli-playground.

    -

    To see a list of issues, we can run:

    -
    $ gcli -t github issues -o herrhotzenplotz -r ghcli-playground -a
    -NUMBER  NOTES  STATE   TITLE
    -    13      0  open    yet another issue
    -    12      0  closed  wat
    -    11      0  closed  blaaaaaaaaaaaaaaaaaaaah
    -    10      0  closed  "this is the quoted" issue title? anyone?"
    -     9      0  closed  test
    -     8      0  closed  foobar
    -     7      0  closed  foobar
    -     5      0  closed  test2
    -     4      0  closed  test
    -$
    -
    -

    Invoke gcli

    -

    Let's create a bug report where we complain about things not working:

    -
    $ gcli -t github issues create -o herrhotzenplotz -r ghcli-playground \
    -    "Bug: Doesn't work on my machine"
    -
    -

    The message "Bug: doesn't work on my machine" is the title of the -issue.

    -

    Original Post

    -

    You will see the default editor come up and instruct you to type in a -message. This message is the "original post" or the body of the issue -ticket that you're about to submit. You can use Markdown Syntax:

    -
     I tried building this code on my machine but unfortunately it errors
    - out with the following message:
    -
    - ```console
    - $ make love
    - make: don't know how to make love. Stop
    -
    - make: stopped in /tmp/wat
    - $
    - ```
    -
    - What am I doing wrong?
    -
    - ! ISSUE TITLE : Bug: Doesn't work on my machine
    - ! Enter issue description above.
    - ! All lines starting with '!' will be discarded.
    -
    -

    Submit the issue

    -

    After you save and exit the editor gcli gives you a chance to check -back and finally submit the issue. Type 'y' and hit enter.

    -

    You can check back if the issue was created and also view details -about it as you learned earlier.

    -
    -
    - - - diff --git a/docs/website/tutorial/06-Commenting.html b/docs/website/tutorial/06-Commenting.html deleted file mode 100644 index 832483da..00000000 --- a/docs/website/tutorial/06-Commenting.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - GCLI Tutorial | Interacting and commenting - - - - - - - - - -
    -

    Commenting

    -

    Discussions on GitHub and the like are done through comments. You can -comment on issues and pull requests.

    -

    Reviewing a discussion

    -

    Say you were looking at an issue in curl/curl:

    -
    $ gcli issues -o curl -r curl -i 11461 comments
    -
    -

    Create the comment

    -

    And now you wish to respond to this thread:

    -
    $ gcli comment -o curl -r curl -i 11461
    -
    -

    This will now open the editor and lets you type in your message. -After saving and exiting gcli will ask you to confirm. Type 'y' and -hit enter:

    -
    $ gcli -t github comment -o curl -r curl -i 11461
    -You will be commenting the following in curl/curl #11461:
    -
    -Is this okay? [yN] y
    -$
    -
    -

    Commenting on pull requests

    -

    When you want to comment under a pull request use the -p flag -instead of the -i flag to indicate the PR number.

    -
    -
    - - - diff --git a/docs/website/tutorial/07-Creating-a-pull-request.html b/docs/website/tutorial/07-Creating-a-pull-request.html deleted file mode 100644 index 9b407fe9..00000000 --- a/docs/website/tutorial/07-Creating-a-pull-request.html +++ /dev/null @@ -1,91 +0,0 @@ - - - - - GCLI Tutorial | Creating a Pull Request - - - - - - - - - -
    -

    Creating a pull request

    -

    Creating a pull request with gcli is usually as simple as running

    -
    $ gcli pulls create
    -
    -

    Preparation

    -

    Suppose you have a git repository forked and cloned:

    -
    $ git clone git@github.com:contour-terminal/contour
    -$ cd contour
    -$ gcli forks create --into herrhotzenplotz
    -
    -

    Then you do some work on whatever feature you're planning to submit:

    -
    $ git checkout -b my-amazing-feature
    -<hackhackhack>
    -$ git add -p
    -$ git commit
    -
    -

    Push your changes

    -

    You then push your changes to your fork:

    -
    $ git push origin my-amazing-feature
    -
    -

    Create the pull request

    -

    Now you can run the command to create the pull request:

    -
    $ gcli pulls create
    -From (owner:branch) [herrhotzenplotz:my-amazing-feature]:
    -Owner [contour-terminal]:
    -Repository [contour]:
    -To Branch [master]:
    -Title: Add new amazing feature
    -Enable automerge? [yN]:
    -
    -

    Most of the defaults you should be able to simply accept. This -assumes that you have the source branch checked out locally and -remotes are configured appropriately.

    -

    Otherwise you can just change the defaults by entering the correct -values.

    -

    Enter original post

    -

    After you entered all the meta data of the pull request gcli will -drop you into your editor and lets you enter a message for the pull -request.

    -

    Submit

    -

    After you saved and exit type y and hit enter to submit the pull -request.

    -
    -
    - - - diff --git a/docs/website/tutorial/08-Other-forges.html b/docs/website/tutorial/08-Other-forges.html deleted file mode 100644 index 8023e556..00000000 --- a/docs/website/tutorial/08-Other-forges.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - GCLI Tutorial | Other forges (Gitea, Gitlab, Bugzilla) - - - - - - - - - -
    -

    Other forges and bugtrackers

    -

    gcli is capable of not only interacting with Github. It also currently supports:

    -
      -
    • GitLab
    • -
    • Gitea
    • -
    • Bugzilla
    • -
    -

    Bugzilla

    -

    Notes

    -

    Bugzilla is commonly used as a bug tracker in various large open-source -projects such as FreeBSD, the Linux Kernel, Mozilla and Gentoo.

    -

    Searching

    -

    Suppose you want to search for bug reports containing sparc in -the Gentoo Bugzilla.

    -

    In this case you need to configure an account that points at the -correct URL in $HOME/.config/gcli/config by adding:

    -
    gentoo {
    -	forge-type=bugzilla
    -	apibase=https://bugs.gentoo.org/
    -}
    -
    -

    Now you can search the Gentoo Bugs:

    -
    $ gcli -a gentoo issues sparc
    -NUMBER  NOTES  STATE        TITLE
    -924443      0  UNCONFIRMED  Add keyword ~sparc for app-misc/fastfetch
    -924430      0  RESOLVED     media-libs/assimp-5.3.1 fails tests on sparc
    -924215      0  CONFIRMED    dev-libs/libbson dev-libs/mongo-c-driver: alpha arm ia64 mips ppc ppc64 s390 sparc keyword req
    -924191      0  CONFIRMED    media-libs/exempi: unaligned access causes dev-python/python-xmp-toolkit-2.0.2 to fails tests on sparc (test_file_to_dict (test.test_core_unit.UtilsTestCase.test_file_to_dict) ... Bus error)
    -924180      0  CONFIRMED    dev-python/psycopg-3.1.17[native-extensions] fails tests on sparc: tests/test_copy_async.py::test_read_rows[asyncio-names-1] Fatal Python error: Bus error
    -924031      0  IN_PROGRESS  sys-apps/bfs: ~arm ~arm64 ~ppc ~ppc64 ~sparc keywording
    -923968      0  CONFIRMED    dev-python/pyarrow-15.0.0 fails to configure on sparc: CMake Error at cmake_modules/SetupCxxFlags.cmake:42 (message): Unknown system processor
    -921245      0  CONFIRMED    media-video/rav1e-0.6.6 fails to compile on sparc: Assertion `DT.dominates(RHead, LHead) && "No dominance between recurrences used by one SCEV?"' failed.
    -920956      0  CONFIRMED    dev-python/pygame-2.5.2: pygame.tests.font_test SIGBUS on sparc
    -920737      0  CONFIRMED    sparc64-solaris Prefix no longer supported
    -
    -<snip>
    -
    -

    Issue details

    -

    Furthermore we can look at single issues:

    -
    $ gcli -a gentoo issues -i 920737 all comments
    -   NUMBER : 920737
    -    TITLE : sparc64-solaris Prefix no longer supported
    -  CREATED : 2023-12-26T19:20:58Z
    -  PRODUCT : Gentoo Linux
    -COMPONENT : Profiles
    -   AUTHOR : Tom Williams
    -    STATE : CONFIRMED
    -   LABELS : none
    -ASSIGNEES : prefix
    -
    -ORIGINAL POST
    -
    -    Resurrecting a Prefix install on Solaris 11.4 SPARC. It was working rather well
    -    for me; after a hiatus I had hoped to use it again but my first emerge --sync
    -    has removed the profile needed to merge any updates or new packages.
    -
    -    I note commit 8e006b67e06a19fae10c6059c7fc5ede88834601 in May 2023 removed the
    -    profile and keywording for prefixed installs.
    -
    -    There is no associated comment. There doesn't seem to be a bug report in regards
    -    to the change (I'm quite sure almost nobody uses it, so probably fair enough)
    -
    -    Any easy way to restore the profile for now? Eventually Solaris/SPARC and thus
    -    Prefix will be gone anyway, but useful for now.
    -
    -    Thanks for your continued efforts.
    -AUTHOR : sam
    -DATE   : 2023-12-26T19:21:33Z
    -         I think at the very least, when removing Prefix support in future, a 'deprecated'
    -         file should be added to the relevant profiles asking if anyone is using
    -         it to step forward.
    -
    -AUTHOR : grobian
    -DATE   : 2023-12-26T22:58:12Z
    -         Solaris 11.4 itself is a problem.  I doubt you ever had it "working".
    -
    -AUTHOR : grobian
    -DATE   : 2023-12-26T22:59:57Z
    -         Linux sparc team is not relevant here
    -
    -$
    -
    -
    -
    - - - diff --git a/docs/website/tutorial/index.html b/docs/website/tutorial/index.html deleted file mode 100644 index 51929faa..00000000 --- a/docs/website/tutorial/index.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - GCLI Tutorial | Index - - - - - - - - -

    A GCLI Tutorial

    -

    This document is aimed at those who are new to gcli and want get - started using it.

    - -

    Table of contents

    -
      -
    1. Installation
    2. -
    3. First Steps
    4. -
    5. How to find documentation
    6. -
    7. Setting up an account
    8. -
    9. Creating an issue
    10. -
    11. Interacting and commenting
    12. -
    13. Creating a Pull Request
    14. -
    15. Other forges (Gitea, Gitlab, Bugzilla)
    16. -
    - - From e6778c65e2bf940453a0b4cf34d097f9fdccbeb2 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 16 Aug 2024 19:08:32 +0200 Subject: [PATCH 33/50] Add gitignore for docs Signed-off-by: Nico Sonack --- docs/website/.gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/website/.gitignore diff --git a/docs/website/.gitignore b/docs/website/.gitignore new file mode 100644 index 00000000..d2beee95 --- /dev/null +++ b/docs/website/.gitignore @@ -0,0 +1,9 @@ +tutorial/01-Installation.html +tutorial/02-First-Steps.html +tutorial/03-Find-Documentation.html +tutorial/04-Account-Setup.html +tutorial/05-Creating-an-issue.html +tutorial/06-Commenting.html +tutorial/07-Creating-a-pull-request.html +tutorial/08-Other-forges.html +tutorial/index.html From dea88294d2255e53dafe73d3a730fb7555b69edf Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 16 Aug 2024 19:28:09 +0200 Subject: [PATCH 34/50] docs: Install manual pages into the correct directory I accidentially installed the manual pages into the wrong directory Thanks for pointing this out! Reported-by: Bence Ferdinandy Tested-by: Bence Ferdinandy Fixes: https://lists.sr.ht/~herrhotzenplotz/gcli-discuss/%3CD3EP4TA0POIO.1VV22EXIMO9ZH@ferdinandy.com%3E Signed-off-by: Nico Sonack --- Makefile.in | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 49cab5d5..1fca867d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -396,5 +396,8 @@ install-auto: manpages gcli $(INSTALL) -d $(BINDIR) $(INSTALL) -m 0755 gcli $(BINDIR) $(INSTALL) -d $(MANDIR) + $(INSTALL) -d $(MANDIR)/man5 + $(INSTALL) -d $(MANDIR)/man1 # TODO: compress manual pages - $(INSTALL) -m 644 $(MANPAGES) $(MANDIR) + echo $(MANPAGES) | grep '\.1$$' | while read -r PAGE; do $(INSTALL) -m 644 $$PAGE $(MANDIR)/man1; done + echo $(MANPAGES) | grep '\.5$$' | while read -r PAGE; do $(INSTALL) -m 644 $$PAGE $(MANDIR)/man5; done From c4bb36d50412d9463386ca80c11822b8d7a36c3f Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 19 Aug 2024 10:42:41 +0200 Subject: [PATCH 35/50] Fix compilation with IBM XL C The statement always evaluated to false since the type enum is unsigned and would have never been negative. Due to the fact that we compile with -qhalt=w this resulted in an error (-Wtautological-compare). Signed-off-by: Nico Sonack --- src/status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/status.c b/src/status.c index 921e4a0f..a24e4aac 100644 --- a/src/status.c +++ b/src/status.c @@ -79,7 +79,7 @@ notification_target_type_strings[MAX_GCLI_NOTIFICATION_TARGET] = { char const * gcli_notification_target_type_str(enum gcli_notification_target_type type) { - if (type < 0 || type > MAX_GCLI_NOTIFICATION_TARGET) + if (type > MAX_GCLI_NOTIFICATION_TARGET) return NULL; return notification_target_type_strings[type]; From a8e4d0eefc765e3766f7c354c81b4557b73f5a21 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 19 Aug 2024 11:03:42 +0200 Subject: [PATCH 36/50] Update Changelog Signed-off-by: Nico Sonack --- Changelog.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Changelog.md b/Changelog.md index 15db4ac7..7793dd81 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,8 +11,27 @@ This changelog does not follow semantic versioning. ### Fixed +- In various configuration places and environment variables where + boolean values are accepted you can now specify `true` as a truthy + value. + + Submitted by: Gavin-John Noonan + +- The configure script now exits gracefully whenever a required + program couldn't be found. + + Reported by: Alexey Ugnichev + ### Changed +- The pipelines subcommand has been refactored to accept actions + for pipelines. This allows cases where a pipeline triggers child + pipelines to be handled properly. + + See `gcli-pipelines(1)` for documentation. + + Reported by: Bence Ferdinandy + ### Removed ## 2.4.0 (2024-June-28) From aaa5a237de60df0b66e9f4ae12c1c0fbdf927b98 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 19 Aug 2024 22:09:28 +0200 Subject: [PATCH 37/50] issues: Rewrite github_issue_add_labels to use gcli_jsongen This also fixes the bug where the label contains a double-quote would generate invalid JSON. Fixes: https://gitlab.com/herrhotzenplotz/gcli/issues/235 Signed-off-by: Nico Sonack --- Changelog.md | 3 +++ src/github/issues.c | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7793dd81..40b634ca 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,9 @@ This changelog does not follow semantic versioning. Reported by: Alexey Ugnichev +- A bug genereting invalid JSON when adding labels to a GitHub issue + was fixed. + ### Changed - The pipelines subcommand has been refactored to accept actions diff --git a/src/github/issues.c b/src/github/issues.c index 7d17e609..32a05c4b 100644 --- a/src/github/issues.c +++ b/src/github/issues.c @@ -436,24 +436,35 @@ github_issue_add_labels(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id const issue, char const *const labels[], size_t const labels_size) { - char *url = NULL; char *data = NULL; - char *list = NULL; + char *url = NULL; int rc = 0; + struct gcli_jsongen gen = {0}; assert(labels_size > 0); url = sn_asprintf("%s/repos/%s/%s/issues/%"PRIid"/labels", gcli_get_apibase(ctx), owner, repo, issue); - list = sn_join_with(labels, labels_size, "\",\""); - data = sn_asprintf("{ \"labels\": [\"%s\"]}", list); + gcli_jsongen_init(&gen); + gcli_jsongen_begin_object(&gen); + { + gcli_jsongen_objmember(&gen, "labels"); + gcli_jsongen_begin_array(&gen); + for (size_t i = 0; i < labels_size; ++i) { + gcli_jsongen_string(&gen, labels[i]); + } + gcli_jsongen_end_array(&gen); + } + gcli_jsongen_end_object(&gen); + + data = gcli_jsongen_to_string(&gen); + gcli_jsongen_free(&gen); rc = gcli_fetch_with_method(ctx, "POST", url, data, NULL, NULL); free(url); free(data); - free(list); return rc; } From 7b00b5728b496425b29643f42b01b887632764f3 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 16 Aug 2024 21:17:46 +0200 Subject: [PATCH 38/50] Bump required C Language Standard to C11 We have made this decision after thinking and discussing. Reasons are: - C99 is now 25 years old and one can safely assume that any modern system supports it - C11 is now 13 years old and all platforms that we have tested so far have been supporting C11 for years now. This also applies to all the compilers we have been using to build gcli so far. - Compatibility with more optional libraries I plan to integrate the lowdown library for Markdown rendering. It requires C11. I have previously encountered such a case too. - More modern language features: We can now use anonymous unions and static assertions. - gcli is a tool for a modern development workflow: Thus we can assume a reasonably modern system - where C11 is supported. If you have a system where these changes cause trouble and heart ache or you wish to complain about this decision please do so on our mailing list or IRC. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- HACKING.md | 10 +++++----- Makefile.in | 10 +++++----- README.md | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/HACKING.md b/HACKING.md index bade1822..8ad37f63 100644 --- a/HACKING.md +++ b/HACKING.md @@ -211,14 +211,14 @@ Please use the BSD Style conventions for formatting your code. This means: I know we're not using punchcards anymore, however it makes the code way more readable. -- Use C99 +- Use C11 - Please don't use C11 or even more modern features. Reason being that + Please don't use C17 or even more modern features. Reason being that I want gcli to be portable to older platforms where either no modern compilers are available or where we have to rely on old gcc versions - and/or buggy vendor compilers. Notable forbidden features are - `_Static_assert` and anonymous unions. If you use the compiler flags - I mentioned above you should get notified by the compiler. + and/or buggy vendor compilers. This also means that GNU extensions + are forbidden. If you use the compiler flags I mentioned above + you should get notified by the compiler. There is a `.editorconfig` included in the source code that should automatically provide you with all needed diff --git a/Makefile.in b/Makefile.in index 1fca867d..f594ce97 100644 --- a/Makefile.in +++ b/Makefile.in @@ -31,9 +31,9 @@ COPTFLAGS_xlc_release= -qpic -qoptimize=4 COPTFLAGS= $(COPTFLAGS_$(CCOM)_$(OPTIMISE)) COPTFLAGS_FOR_BUILD= $(COPTFLAGS_$(CCOM_FOR_BUILD)_$(OPTIMISE)) -CSTDFLAGS_gcc= -std=c99 -pedantic -CSTDFLAGS_clang= -std=c99 -pedantic -CSTDFLAGS_xlc= -qlanglvl=stdc99 +CSTDFLAGS_gcc= -std=c11 -pedantic +CSTDFLAGS_clang= -std=c11 -pedantic +CSTDFLAGS_xlc= -qlanglvl=stdc11 CSTDFLAGS= $(CSTDFLAGS_$(CCOM)) CSTDFLAGS_FOR_BUILD= $(CSTDFLAGS_$(CCOM_FOR_BUILD)) @@ -47,10 +47,10 @@ CFLAGS_FOR_BUILD= $(CSTDFLAGS_FOR_BUILD) $(COPTFLAGS_FOR_BUILD) -I@SRCDIR@/inclu ############## CPPFLAGS ###################### ENV_CPPFLAGS= @ENV_CPPFLAGS@ -CPPFLAGS= -DHAVE_CONFIG_H=1 -DHAVE_SYS_QUEUE_H=1 -DHAVE_GETOPT_H=1 -D_XOPEN_SOURCE=600 $(ENV_CPPFLAGS) +CPPFLAGS= -DHAVE_CONFIG_H=1 -DHAVE_SYS_QUEUE_H=1 -DHAVE_GETOPT_H=1 -D_XOPEN_SOURCE=700 $(ENV_CPPFLAGS) ENV_CPPFLAGS_FOR_BUILD= @ENV_CPPFLAGS_FOR_BUILD@ -CPPFLAGS_FOR_BUILD= -DHAVE_CONFIG_H -DYY_NO_UNPUT -DYY_NO_INPUT -D_XOPEN_SOURCE=600 $(ENV_CPPFLAGS_FOR_BUILD) +CPPFLAGS_FOR_BUILD= -DHAVE_CONFIG_H -DYY_NO_UNPUT -DYY_NO_INPUT -D_XOPEN_SOURCE=700 $(ENV_CPPFLAGS_FOR_BUILD) # CFLAGS for dependency tracking for various compilers CCDEPFLAGS_gcc= -MD -MF ${@:.o=.d} diff --git a/README.md b/README.md index a3ab3970..98da255c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Required dependencies: - libcurl - yacc (System V yacc, Berkeley Yacc or Bison should suffice) - lex (flex is preferred) -- C99 Compiler and linker +- C11 Compiler and linker - make - pkg-config From e60cdd33d4d0f04a7053cb9c833ebb9af06749ba Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 16 Aug 2024 21:17:47 +0200 Subject: [PATCH 39/50] cmd: Move pretty print to cmd code and remove associated test This is done to later add support for lowdown which needs special handling for disabling colour output. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.in | 1 - include/gcli/cmd/cmd.h | 5 ++ src/cmd/cmd.c | 44 +++++++++++++++++ src/cmd/comment.c | 2 +- src/cmd/issues.c | 2 +- src/cmd/milestones.c | 2 +- src/cmd/pulls.c | 2 +- src/cmd/releases.c | 2 +- src/cmd/status.c | 3 +- tests/Kyuafile | 4 -- tests/pretty-print.c | 106 ----------------------------------------- thirdparty/sn/sn.c | 43 ----------------- thirdparty/sn/sn.h | 3 -- 13 files changed, 55 insertions(+), 164 deletions(-) delete mode 100644 tests/pretty-print.c diff --git a/Makefile.in b/Makefile.in index f594ce97..9b9a248f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -326,7 +326,6 @@ TEST_PROGRAMS = \ tests/gitea-parse \ tests/bugzilla-parse \ tests/url-encode \ - tests/pretty-print \ tests/jsongen \ tests/base64 \ tests/difftests diff --git a/include/gcli/cmd/cmd.h b/include/gcli/cmd/cmd.h index 1767bb11..4b76d544 100644 --- a/include/gcli/cmd/cmd.h +++ b/include/gcli/cmd/cmd.h @@ -34,6 +34,8 @@ #include #endif +#include + #include #include @@ -65,4 +67,7 @@ void delete_repo(bool always_yes, const char *owner, const char *repo); /* List of subcommand entry points */ int subcommand_api(int argc, char *argv[]); +void gcli_pretty_print(char const *input, int indent, int maxlinelen, + FILE *stream); + #endif /* GCLI_CMD_CMD_H */ diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index ed2e723c..d9f4220a 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -150,3 +151,46 @@ delete_repo(bool always_yes, const char *owner, const char *repo) if (gcli_repo_delete(g_clictx, owner, repo) < 0) errx(1, "gcli: error: failed to delete repo"); } + +static int +word_length(const char *x) +{ + int l = 0; + + while (*x && !isspace(*x++)) + l++; + return l; +} + +void +gcli_pretty_print(const char *input, int indent, int maxlinelen, FILE *out) +{ + const char *it = input; + + if (!it) + return; + + while (*it) { + int linelength = indent; + fprintf(out, "%*.*s", indent, indent, ""); + + do { + int w = word_length(it) + 1; + + if (it[w - 1] == '\n') { + fprintf(out, "%.*s", w - 1, it); + it += w; + break; + } else if (it[w - 1] == '\0') { + w -= 1; + } + + fprintf(out, "%.*s", w, it); + it += w; + linelength += w; + + } while (*it && (linelength < maxlinelen)); + + fputc('\n', out); + } +} diff --git a/src/cmd/comment.c b/src/cmd/comment.c index 9a1b63e4..564b948c 100644 --- a/src/cmd/comment.c +++ b/src/cmd/comment.c @@ -200,7 +200,7 @@ gcli_print_comment_list(struct gcli_comment_list const *const list) gcli_setbold(), list->comments[i].author, gcli_resetbold(), list->comments[i].date, list->comments[i].id); - pretty_print(list->comments[i].body, 9, 80, stdout); + gcli_pretty_print(list->comments[i].body, 9, 80, stdout); putchar('\n'); } } diff --git a/src/cmd/issues.c b/src/cmd/issues.c index 606226d5..6f27faf5 100644 --- a/src/cmd/issues.c +++ b/src/cmd/issues.c @@ -208,7 +208,7 @@ void gcli_issue_print_op(struct gcli_issue const *const it) { if (it->body) - pretty_print(it->body, 4, 80, stdout); + gcli_pretty_print(it->body, 4, 80, stdout); } static void diff --git a/src/cmd/milestones.c b/src/cmd/milestones.c index 43d2a167..defa7b64 100644 --- a/src/cmd/milestones.c +++ b/src/cmd/milestones.c @@ -128,7 +128,7 @@ gcli_print_milestone(struct gcli_milestone const *const milestone) if (milestone->description && strlen(milestone->description)) { printf("\nDESCRIPTION:\n"); - pretty_print(milestone->description, 4, 80, stdout); + gcli_pretty_print(milestone->description, 4, 80, stdout); } } diff --git a/src/cmd/pulls.c b/src/cmd/pulls.c index 041d7dc4..a3ff300f 100644 --- a/src/cmd/pulls.c +++ b/src/cmd/pulls.c @@ -250,7 +250,7 @@ void gcli_pull_print_op(struct gcli_pull const *const pull) { if (pull->body) - pretty_print(pull->body, 4, 80, stdout); + gcli_pretty_print(pull->body, 4, 80, stdout); } static void diff --git a/src/cmd/releases.c b/src/cmd/releases.c index 6572c1ab..f79aab85 100644 --- a/src/cmd/releases.c +++ b/src/cmd/releases.c @@ -96,7 +96,7 @@ gcli_print_release(enum gcli_output_flags const flags, /* body */ if (it->body) { putchar('\n'); - pretty_print(it->body, 13, 80, stdout); + gcli_pretty_print(it->body, 13, 80, stdout); } putchar('\n'); diff --git a/src/cmd/status.c b/src/cmd/status.c index c3797c7e..2f044d71 100644 --- a/src/cmd/status.c +++ b/src/cmd/status.c @@ -91,7 +91,7 @@ gcli_print_notifications(struct gcli_notification_list const *const list) printf("\n"); - pretty_print(list->notifications[i].title, 4, 80, stdout); + gcli_pretty_print(list->notifications[i].title, 4, 80, stdout); putchar('\n'); } } @@ -174,4 +174,3 @@ subcommand_status(int argc, char *argv[]) return EXIT_SUCCESS; } - diff --git a/tests/Kyuafile b/tests/Kyuafile index 77ad1658..7cc95bb8 100644 --- a/tests/Kyuafile +++ b/tests/Kyuafile @@ -30,10 +30,6 @@ atf_test_program{ name = 'url-encode' } -atf_test_program{ - name = 'pretty-print' -} - atf_test_program{ name = 'jsongen' } diff --git a/tests/pretty-print.c b/tests/pretty-print.c deleted file mode 100644 index c5379853..00000000 --- a/tests/pretty-print.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2023 Nico Sonack - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -#include -#include -#include - -#include - -static char * -test_string(char const *const input) -{ - FILE *f; - long length; - char *buf; - static char const *const fname = "/tmp/gcli_pretty_print_test"; - - f = fopen(fname, "w"); - pretty_print(input, 4, 80, f); - - length = ftell(f); - fflush(f); - fclose(f); - - f = fopen(fname, "r"); - buf = malloc(length + 1); - fread(buf, 1, length, f); - buf[length] = '\0'; - - fclose(f); - unlink(fname); - - return buf; -} - -ATF_TC_WITHOUT_HEAD(simple_one_line); -ATF_TC_BODY(simple_one_line, tc) -{ - char *formatted = test_string(""); - ATF_CHECK_STREQ(formatted, " \n"); -} - -ATF_TC_WITHOUT_HEAD(long_line_doesnt_break); -ATF_TC_BODY(long_line_doesnt_break, tc) -{ - char const *const input = - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789"; - char *formatted = test_string(input); - char const expected[] = - " 0123456789012345678901234567890123456789012345678901234567890123456789012345" - "6789012345678901234567890123456789012345678901234567890123456789\n"; - - ATF_CHECK_STREQ(formatted, expected); -} - -ATF_TC_WITHOUT_HEAD(line_overflow_break); -ATF_TC_BODY(line_overflow_break, tc) -{ - char const *const input = - "0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 " - "0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789"; - char *formatted = test_string(input); - char const expected[] = - " 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 \n" - " 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 \n" - " 0123456789 0123456789\n"; - ATF_CHECK_STREQ(formatted, expected); -} - -ATF_TP_ADD_TCS(tp) -{ - ATF_TP_ADD_TC(tp, simple_one_line); - ATF_TP_ADD_TC(tp, long_line_doesnt_break); - ATF_TP_ADD_TC(tp, line_overflow_break); - - return atf_no_error(); -} diff --git a/thirdparty/sn/sn.c b/thirdparty/sn/sn.c index 8eb9785f..66b6e4d6 100644 --- a/thirdparty/sn/sn.c +++ b/thirdparty/sn/sn.c @@ -158,49 +158,6 @@ sn_asprintf(const char *fmt, ...) return result; } -static int -word_length(const char *x) -{ - int l = 0; - - while (*x && !isspace(*x++)) - l++; - return l; -} - -void -pretty_print(const char *input, int indent, int maxlinelen, FILE *out) -{ - const char *it = input; - - if (!it) - return; - - while (*it) { - int linelength = indent; - fprintf(out, "%*.*s", indent, indent, ""); - - do { - int w = word_length(it) + 1; - - if (it[w - 1] == '\n') { - fprintf(out, "%.*s", w - 1, it); - it += w; - break; - } else if (it[w - 1] == '\0') { - w -= 1; - } - - fprintf(out, "%.*s", w, it); - it += w; - linelength += w; - - } while (*it && (linelength < maxlinelen)); - - fputc('\n', out); - } -} - int sn_mmap_file(const char *path, void **buffer) { diff --git a/thirdparty/sn/sn.h b/thirdparty/sn/sn.h index 1b501c5c..7dc1631c 100644 --- a/thirdparty/sn/sn.h +++ b/thirdparty/sn/sn.h @@ -105,9 +105,6 @@ char *sn_asprintf(const char *fmt, ...) PRINTF_FORMAT(1, 2); // modifies the underlying string char *sn_strip_suffix(char *it, const char *suffix); -/* pretty functions */ -void pretty_print(const char *input, int indent, int maxlinelen, FILE *out); - /* io file mapping */ int sn_mmap_file(const char *path, void **buffer); int sn_read_file(char const *path, char **buffer); From 143d3151e7cea0d50ead12050c5bcf1881719ac8 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 16 Aug 2024 21:17:48 +0200 Subject: [PATCH 40/50] cmd: Add optional support for rendering Markdown with lowdown Whenever lowdown is detected and enabled this will use lowdown to render Markdown. Colour output can be disabled. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.in | 26 +++++++++++++++++------- configure | 24 ++++++++++++++++++++++ src/cmd/cmd.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/Makefile.in b/Makefile.in index 9b9a248f..756271ed 100644 --- a/Makefile.in +++ b/Makefile.in @@ -70,19 +70,28 @@ LIBATFC_LIBS= @LIBATFC_LIBS@ LIBEDIT_FOUND= @LIBEDIT_FOUND@ LIBEDIT_CFLAGS_1= @LIBEDIT_CFLAGS@ LIBEDIT_LIBS_1= @LIBEDIT_LIBS@ -LIBEDIT_CFLAGS= ${LIBEDIT_CFLAGS_$(LIBEDIT_FOUND)} -LIBEDIT_LIBS= ${LIBEDIT_LIBS_$(LIBEDIT_FOUND)} +LIBEDIT_CFLAGS= $(LIBEDIT_CFLAGS_$(LIBEDIT_FOUND)) +LIBEDIT_LIBS= $(LIBEDIT_LIBS_$(LIBEDIT_FOUND)) LIBEDIT_CPPFLAGS_1= -DHAVE_LIBEDIT=1 -LIBEDIT_CPPFLAGS= ${LIBEDIT_CPPFLAGS_$(LIBEDIT_FOUND)} +LIBEDIT_CPPFLAGS= $(LIBEDIT_CPPFLAGS_$(LIBEDIT_FOUND)) ############## READLINE ######################################### LIBREADLINE_FOUND= @LIBREADLINE_FOUND@ LIBREADLINE_CFLAGS_1= @LIBREADLINE_CFLAGS@ LIBREADLINE_LIBS_1= @LIBREADLINE_LIBS@ -LIBREADLINE_CFLAGS= ${LIBREADLINE_CFLAGS_$(LIBREADLINE_FOUND)} -LIBREADLINE_LIBS= ${LIBREADLINE_LIBS_$(LIBREADLINE_FOUND)} +LIBREADLINE_CFLAGS= $(LIBREADLINE_CFLAGS_$(LIBREADLINE_FOUND)) +LIBREADLINE_LIBS= $(LIBREADLINE_LIBS_$(LIBREADLINE_FOUND)) LIBREADLINE_CPPFLAGS_1= -DHAVE_LIBREADLINE=1 -LIBREADLINE_CPPFLAGS= ${LIBREADLINE_CPPFLAGS_$(LIBREADLINE_FOUND)} +LIBREADLINE_CPPFLAGS= $(LIBREADLINE_CPPFLAGS_$(LIBREADLINE_FOUND)) + +############## LOWDOWN ######################################### +LIBLOWDOWN_FOUND= @LIBLOWDOWN_FOUND@ +LIBLOWDOWN_CFLAGS_1= @LIBLOWDOWN_CFLAGS@ +LIBLOWDOWN_LIBS_1= @LIBLOWDOWN_LIBS@ +LIBLOWDOWN_CFLAGS= $(LIBLOWDOWN_CFLAGS_$(LIBLOWDOWN_FOUND)) +LIBLOWDOWN_LIBS= $(LIBLOWDOWN_LIBS_$(LIBLOWDOWN_FOUND)) +LIBLOWDOWN_CPPFLAGS_1= -DHAVE_LIBLOWDOWN=1 +LIBLOWDOWN_CPPFLAGS= $(LIBLOWDOWN_CPPFLAGS_$(LIBLOWDOWN_FOUND)) ############## LIBCRYPTO ######################################## LIBCRYPTO_CFLAGS= @LIBCRYPTO_CFLAGS@ @@ -282,7 +291,8 @@ $(TEMPLATE_HEADERS) $(TEMPLATE_SRCS): pgen gcli: libgcli.a $(GCLI_OBJS) $(CCACHE) $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o gcli \ $(GCLI_OBJS) libgcli.a $(LIBCURL_LIBS) $(LIBEDIT_LIBS) \ - $(LIBREADLINE_LIBS) $(LIBCRYPTO_LIBS) + $(LIBREADLINE_LIBS) $(LIBCRYPTO_LIBS) \ + $(LIBLOWDOWN_LIBS) libgcli.a: $(LIBGCLI_OBJS) $(AR) -rc libgcli.a $(LIBGCLI_OBJS) @@ -295,6 +305,7 @@ $(LIBGCLI_OBJS): $(TEMPLATE_HEADERS) $(TEMPLATE_SRCS) @mkdir -p $$(dirname $@) $(CCACHE) $(CC) $(CFLAGS) $(CCDEPFLAGS) $(CPPFLAGS) \ $(LIBCURL_CFLAGS) $(LIBCRYPTO_CFLAGS) \ + $(LIBLOWDOWN_CFLAGS) $(LIBLOWDOWN_CPPFLAGS) \ -DIN_LIBGCLI=1 -c -o $@ $< .c.gcli.o: @@ -303,6 +314,7 @@ $(LIBGCLI_OBJS): $(TEMPLATE_HEADERS) $(TEMPLATE_SRCS) $(LIBEDIT_CPPFLAGS) $(LIBREADLINE_CPPFLAGS) \ $(LIBCURL_CFLAGS) $(LIBEDIT_CFLAGS) \ $(LIBREADLINE_CFLAGS) $(LIBCRYPTO_CFLAGS) \ + $(LIBLOWDOWN_CFLAGS) $(LIBLOWDOWN_CPPFLAGS) \ -c -o $@ $< .c.pgen.o: diff --git a/configure b/configure index 13b9be05..a894fbcd 100755 --- a/configure +++ b/configure @@ -148,6 +148,7 @@ PREFIX=/usr/local OPTIMISE= ENABLE_LIBEDIT=1 ENABLE_LIBREADLINE=1 +ENABLE_LIBLOWDOWN=1 # Parse flags while [ $# -gt 0 ]; do @@ -176,6 +177,14 @@ while [ $# -gt 0 ]; do ENABLE_LIBREADLINE=0 shift ;; + --enable-liblowdown) + ENABLE_LIBLOWDOWN=1 + shift + ;; + --disable-liblowdown) + ENABLE_LIBLOWDOWN=0 + shift + ;; --debug) OPTIMISE=debug shift @@ -262,6 +271,13 @@ else LIBREADLINE_FOUND=0 fi +# Lowdown +if [ $ENABLE_LIBLOWDOWN -eq 1 ]; then + find_package lowdown LIBLOWDOWN optional +else + LIBLOWDOWN_FOUND=0 +fi + ################################################################### # TESTS and other programs ################################################################### @@ -299,6 +315,9 @@ sed \ -e "s|@LIBREADLINE_FOUND@|$LIBREADLINE_FOUND|g" \ -e "s|@LIBREADLINE_CFLAGS@|$LIBREADLINE_CFLAGS|g" \ -e "s|@LIBREADLINE_LIBS@|$LIBREADLINE_LIBS|g" \ + -e "s|@LIBLOWDOWN_FOUND@|$LIBLOWDOWN_FOUND|g" \ + -e "s|@LIBLOWDOWN_CFLAGS@|$LIBLOWDOWN_CFLAGS|g" \ + -e "s|@LIBLOWDOWN_LIBS@|$LIBLOWDOWN_LIBS|g" \ -e "s|@CONFIGURE_CMD_ARGS@|$CONFIGURE_CMD_ARGS|g" \ -e "s|@CC@|$CC|g" \ -e "s|@CCOM@|$CCOM|g" \ @@ -356,5 +375,10 @@ echo " Using libreadline:" echo " LIBREADLINE_CFLAGS: ${LIBREADLINE_CFLAGS}" echo " LIBREADLINE_LIBS: ${LIBREADLINE_LIBS}" fi +if [ $LIBLOWDOWN_FOUND -eq 1 ]; then +echo " Using lowdown:" +echo " LIBLOWDOWN_CFLAGS: ${LIBLOWDOWN_CFLAGS}" +echo " LIBLOWDOWN_LIBS: ${LIBLOWDOWN_LIBS}" +fi echo "" echo "Configuration done. You may now run make." diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index d9f4220a..4b1c0e11 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -40,6 +40,13 @@ #include +#ifdef HAVE_LIBLOWDOWN +#include + +#include +#include +#endif + void copyright(void) { @@ -152,6 +159,53 @@ delete_repo(bool always_yes, const char *owner, const char *repo) errx(1, "gcli: error: failed to delete repo"); } +#ifdef HAVE_LIBLOWDOWN +void +gcli_pretty_print(char const *input, int indent, int maxlinelen, FILE *stream) +{ + size_t input_size; + struct lowdown_buf *out; + struct lowdown_doc *doc; + struct lowdown_node *n; + struct lowdown_opts opts = {0}; + void *rndr; + + input_size = strlen(input); + + if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) + err(1, NULL); + + opts.feat |= LOWDOWN_FENCED|LOWDOWN_TASKLIST|LOWDOWN_TABLES; + if (!gcli_config_have_colours(g_clictx)) + opts.oflags |= (LOWDOWN_TERM_NOANSI|LOWDOWN_TERM_NOCOLOUR); + + opts.vmargin = 1; + opts.hmargin = indent - 4; /* somehow there's always 4 spaces being emitted by lowdown */ + opts.cols = maxlinelen; + + if ((doc = lowdown_doc_new(&opts)) == NULL) + err(1, NULL); + + if ((n = lowdown_doc_parse(doc, NULL, input, input_size, NULL)) == NULL) + err(1, NULL); + + if ((out = lowdown_buf_new(256)) == NULL) + err(1, NULL); + + if ((rndr = lowdown_term_new(&opts)) == NULL) + err(1, NULL); + + if (!lowdown_term_rndr(out, rndr, n)) + err(1, NULL); + + fwrite(out->data, 1, out->size, stream); + + lowdown_term_free(rndr); + lowdown_buf_free(out); + lowdown_node_free(n); + lowdown_doc_free(doc); +} +#else static int word_length(const char *x) { @@ -194,3 +248,4 @@ gcli_pretty_print(const char *input, int indent, int maxlinelen, FILE *out) fputc('\n', out); } } +#endif From 5bb9ac4faf93c2986754c5f65c6ab3a87abb3b9a Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 19 Aug 2024 20:47:43 +0200 Subject: [PATCH 41/50] Fix compilation with GCC 14 gcc 14 introduced a new silly warning flag -Wcalloc-transposed-args that is enabled by default. Fix by swapping all the arguments to calloc causing trouble by this new warning. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- src/cmd/cmdconfig.c | 8 ++++---- src/cmd/gcli.c | 2 +- src/cmd/pull_reviews.c | 2 +- src/cmd/table.c | 8 ++++---- src/diffutil.c | 8 ++++---- src/gcli.c | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c index a48ec08b..12cb2a56 100644 --- a/src/cmd/cmdconfig.c +++ b/src/cmd/cmdconfig.c @@ -236,7 +236,7 @@ init_local_config(struct gcli_ctx *ctx) sn_sv value = sn_sv_trim(line); - struct gcli_config_entry *entry = calloc(sizeof(*entry), 1); + struct gcli_config_entry *entry = calloc(1, sizeof(*entry)); TAILQ_INSERT_TAIL(&dgcli->entries, entry, next); entry->key = key; @@ -289,7 +289,7 @@ static void parse_section_entry(struct config_parser *input, struct gcli_config_section *section) { - struct gcli_config_entry *entry = calloc(sizeof(*entry), 1); + struct gcli_config_entry *entry = calloc(1, sizeof(*entry)); TAILQ_INSERT_TAIL(§ion->entries, entry, next); sn_sv key = sn_sv_chop_until(&input->buffer, '='); @@ -343,7 +343,7 @@ parse_config_section(struct gcli_config *cfg, { struct gcli_config_section *section = NULL; - section = calloc(sizeof(*section), 1); + section = calloc(1, sizeof(*section)); TAILQ_INSERT_TAIL(&cfg->sections, section, next); section->title = parse_section_title(input); @@ -486,7 +486,7 @@ readenv(struct gcli_config *cfg) int gcli_config_init_ctx(struct gcli_ctx *ctx) { - struct cmd_ctx *cctx = calloc(sizeof(*cctx), 1); + struct cmd_ctx *cctx = calloc(1, sizeof(*cctx)); gcli_set_userdata(ctx, cctx); cctx->config.sections = diff --git a/src/cmd/gcli.c b/src/cmd/gcli.c index 89422172..2411b136 100644 --- a/src/cmd/gcli.c +++ b/src/cmd/gcli.c @@ -325,7 +325,7 @@ install_aliases(void) static void setup_subcommand_table(void) { - subcommands = calloc(sizeof(*subcommands), ARRAY_SIZE(default_subcommands)); + subcommands = calloc(ARRAY_SIZE(default_subcommands), sizeof(*subcommands)); memcpy(subcommands, default_subcommands, sizeof(default_subcommands)); subcommands_size = ARRAY_SIZE(default_subcommands); } diff --git a/src/cmd/pull_reviews.c b/src/cmd/pull_reviews.c index d4bd0af5..49dbe722 100644 --- a/src/cmd/pull_reviews.c +++ b/src/cmd/pull_reviews.c @@ -145,7 +145,7 @@ process_series_prelude(char *prelude, struct gcli_pull_create_review_details *de char *meta = calloc(line_len - gcli_pref_len + 1, 1); memcpy(meta, bol + gcli_pref_len, line_len - gcli_pref_len); - ml = calloc(sizeof(*ml), 1); + ml = calloc(1, sizeof(*ml)); ml->entry = meta; TAILQ_INSERT_TAIL(&details->meta_lines, ml, next); diff --git a/src/cmd/table.c b/src/cmd/table.c index 4227a373..0701e780 100644 --- a/src/cmd/table.c +++ b/src/cmd/table.c @@ -81,12 +81,12 @@ gcli_tbl_begin(struct gcli_tblcoldef const *const cols, size_t const cols_size) struct gcli_tbl *tbl; /* Allocate the structure and fill in the handle */ - tbl = calloc(sizeof(*tbl), 1); + tbl = calloc(1, sizeof(*tbl)); if (!tbl) return NULL; /* Reserve memory for the column sizes */ - tbl->col_widths = calloc(sizeof(*tbl->col_widths), cols_size); + tbl->col_widths = calloc(cols_size, sizeof(*tbl->col_widths)); if (!tbl->col_widths) { free(tbl); return NULL; @@ -194,7 +194,7 @@ gcli_tbl_add_row(gcli_tbl _table, ...) struct gcli_tbl *table = (struct gcli_tbl *)(_table); /* reserve array of cells */ - row.cells = calloc(sizeof(*row.cells), table->cols_size); + row.cells = calloc(table->cols_size, sizeof(*row.cells)); if (!row.cells) return -1; @@ -357,7 +357,7 @@ struct gcli_dict { gcli_dict gcli_dict_begin(void) { - return calloc(sizeof(struct gcli_dict), 1); + return calloc(1, sizeof(struct gcli_dict)); } static int diff --git a/src/diffutil.c b/src/diffutil.c index e9b7b1b6..96c10b72 100644 --- a/src/diffutil.c +++ b/src/diffutil.c @@ -127,7 +127,7 @@ gcli_parse_patch(struct gcli_diff_parser *parser, struct gcli_patch *out) /* TODO cleanup */ while (parser->hd[0] == 'd') { - struct gcli_diff *d = calloc(sizeof(*d), 1); + struct gcli_diff *d = calloc(1, sizeof(*d)); if (gcli_parse_diff(parser, d) < 0) return -1; @@ -620,7 +620,7 @@ gcli_parse_diff(struct gcli_diff_parser *parser, struct gcli_diff *out) parser->diff_line_offset = 0; TAILQ_INIT(&out->hunks); while (parser->hd[0] == '@') { - struct gcli_diff_hunk *hunk = calloc(sizeof(*hunk), 1); + struct gcli_diff_hunk *hunk = calloc(1, sizeof(*hunk)); if (parse_hunk_range_info(parser, hunk) < 0) { free(hunk); @@ -674,7 +674,7 @@ gcli_parse_patch_series(struct gcli_diff_parser *parser, return -1; while (parser->hd[0] != '\0') { - struct gcli_patch *p = calloc(sizeof(*p), 1); + struct gcli_patch *p = calloc(1, sizeof(*p)); TAILQ_INSERT_TAIL(&series->patches, p, next); if (gcli_parse_patch(parser, p) < 0) @@ -777,7 +777,7 @@ static struct gcli_diff_comment * make_comment(struct comment_read_ctx *ctx, char *text, struct hunk_line_info const *line_info, int diff_line_offset) { - struct gcli_diff_comment *comment = calloc(sizeof(*comment), 1); + struct gcli_diff_comment *comment = calloc(1, sizeof(*comment)); comment->after.filename = strdup(ctx->diff->file_b); comment->after.start_row = line_info->patched_line; comment->after.end_row = line_info->patched_line; diff --git a/src/gcli.c b/src/gcli.c index c956c057..8036860a 100644 --- a/src/gcli.c +++ b/src/gcli.c @@ -39,7 +39,7 @@ gcli_init(struct gcli_ctx **ctx, char *(*get_token)(struct gcli_ctx *), char *(*get_apibase)(struct gcli_ctx *)) { - *ctx = calloc(sizeof (struct gcli_ctx), 1); + *ctx = calloc(1, sizeof (struct gcli_ctx)); if (!(*ctx)) return strerror(errno); From b5e413ee8ace8f70075f9a847a0a89b97ab48140 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 20 Aug 2024 01:37:16 +0200 Subject: [PATCH 42/50] Try to fix Debian CI Bullshit Signed-off-by: Nico Sonack --- .gitlab-ci.yml | 2 +- configure | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 55bfcf50..33530fa3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,7 +37,7 @@ dist: debian-amd64: stage: testing - image: debian:bullseye + image: debian:bookworm script: - apt-get update - apt-get install -y --no-install-recommends build-essential libcurl4-openssl-dev pkgconf bison flex make libbsd-dev libatf-dev kyua libreadline-dev libssl-dev diff --git a/configure b/configure index a894fbcd..70da3ccf 100755 --- a/configure +++ b/configure @@ -100,7 +100,11 @@ find_package() { die "not found" fi fi - export ${2}_CFLAGS="$($PKG_CONFIG --cflags $1)" + + # readline and possibly other dependencies in Debian (yikes) + # return -D_XOPEN_SOURCE which breaks builds. Rip it out - + # we know what we're doing... ha! + export ${2}_CFLAGS="$($PKG_CONFIG --cflags $1 | sed 's|-D_XOPEN_SOURCE=[[:digit:]]*||g')" export ${2}_LIBS="$($PKG_CONFIG --libs $1)" export ${2}_FOUND=1 printf " found\n" >&2 From d2f0943655fb074b8a7d3d6573a93508dcae3d12 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sun, 25 Aug 2024 03:53:03 +0200 Subject: [PATCH 43/50] Makefile: Fix dependencies of test programs We used to get rebuilds of all test programs when one test was touched. Because we want portable Makefiles we need to specify the dependencies this way - there isn't really a good way to do this without resorting to unportable pattern matching. Be verbose instead. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.in | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 756271ed..954cb198 100644 --- a/Makefile.in +++ b/Makefile.in @@ -331,6 +331,7 @@ clean-auto: $(TEST_PROGRAMS) \ $(MANPAGES) +###### TEST SUITE ##################################### TEST_PROGRAMS = \ tests/json-escape \ tests/github-parse \ @@ -342,7 +343,17 @@ TEST_PROGRAMS = \ tests/base64 \ tests/difftests -$(TEST_PROGRAMS): libgcli.a $(TEST_PROGRAMS:=.tests.o) +tests/json-escape: tests/json-escape.tests.o +tests/github-parse: tests/github-parse.tests.o +tests/gitlab-parse: tests/gitlab-parse.tests.o +tests/gitea-parse: tests/gitea-parse.tests.o +tests/bugzilla-parse: tests/bugzilla-parse.tests.o +tests/url-encode: tests/url-encode.tests.o +tests/jsongen: tests/jsongen.tests.o +tests/base64: tests/base64.tests.o +tests/difftests: tests/difftests.tests.o + +$(TEST_PROGRAMS): libgcli.a $(CCACHE) $(CC) $(CFLAGS) $(CCDEPFLAGS) $(CPPFLAGS) \ $(LIBATFC_CFLAGS) $(LIBCURL_CFLAGS) $(LIBCRYPTO_CFLAGS) \ $(LDFLAGS) -o $@ $(@:=.tests.o) libgcli.a \ From 05dfbcd4cdde3c82e44ef3d455322697a6b67006 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sun, 25 Aug 2024 03:53:04 +0200 Subject: [PATCH 44/50] diffutil: Fix buggy hunk range info parser The following header would cause issues: @@ -1 +1 @@ Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- src/diffutil.c | 3 ++- tests/difftests.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/diffutil.c b/src/diffutil.c index 96c10b72..fc370298 100644 --- a/src/diffutil.c +++ b/src/diffutil.c @@ -349,8 +349,9 @@ parse_hunk_range_info(struct gcli_diff_parser *parser, if (read_number(&line, 10, &out->range_r_start) < 0) return -1; - char const delim_r = *line.start++; + char const delim_r = *line.start; if (delim_r == ',') { + line.start += 1; if (read_number(&line, 10, &out->range_r_length) < 0) return -1; } else if (delim_r != ' ') { diff --git a/tests/difftests.c b/tests/difftests.c index ff126e0e..65ef5366 100644 --- a/tests/difftests.c +++ b/tests/difftests.c @@ -1022,6 +1022,28 @@ ATF_TC_BODY(bug_patch_series_fail_get_comments, tc) ATF_REQUIRE(gcli_patch_series_get_comments(&series, &comments) == 0); } +ATF_TC_WITHOUT_HEAD(bug_short_hunk_range); +ATF_TC_BODY(bug_short_hunk_range, tc) +{ + struct gcli_diff_parser parser = {0}; + struct gcli_patch patch = {0}; + + char const input[] = + "diff --git a/foo b/foo\n" + "index 30a503cf..05d233eb 100644\n" + "--- a/foo\n" + "+++ b/foo\n" + "@@ -1 +1 @@\n" + "-wat\n" + "+banana\n"; + + ATF_REQUIRE(gcli_diff_parser_from_buffer(input, sizeof input, "input", &parser) == 0); + ATF_REQUIRE(gcli_parse_patch(&parser, &patch) == 0); + + gcli_free_patch(&patch); + gcli_free_diff_parser(&parser); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, free_patch_cleans_up_properly); @@ -1046,6 +1068,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, new_and_old_with_both_deletions_and_additions); ATF_TP_ADD_TC(tp, patch_for_git_object_format_version_1); ATF_TP_ADD_TC(tp, bug_patch_series_fail_get_comments); + ATF_TP_ADD_TC(tp, bug_short_hunk_range); return atf_no_error(); } From 5c0b4b65fc01814e7f372f64840b05d844045be0 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sun, 25 Aug 2024 03:53:05 +0200 Subject: [PATCH 45/50] diffutil: Handle diff meta lines (starting with '\') properly We used to get errors parsing diffs when the file diffed was missing a trailing newline. This gave a diff line like: \ No newline at end of file This caused a hiccup in the parser which made the line a comment at the end of the diff which makes no sense. Also add a test case for this specific bug. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- src/diffutil.c | 3 +- tests/difftests.c | 37 +++++++++++++++++++ .../stuff_with_no_newline_in_diff.diff | 11 ++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/samples/stuff_with_no_newline_in_diff.diff diff --git a/src/diffutil.c b/src/diffutil.c index fc370298..dd57579e 100644 --- a/src/diffutil.c +++ b/src/diffutil.c @@ -523,7 +523,7 @@ read_hunk_body(struct gcli_diff_parser *parser, struct gcli_diff_hunk *hunk) /* If it is a comment, don't count this line into the absolute diff * offset of the hunk */ if (line.start[0] == ' ' || line.start[0] == '+' || - line.start[0] == '-') + line.start[0] == '-' || line.start[0] == '\\') { parser->diff_line_offset += 1; } @@ -925,6 +925,7 @@ gcli_hunk_get_comments(struct gcli_diff const *diff, case '+': case ' ': case '-': + case '\\': ctx.diff_line_offset += 1; ctx.last_line_is_new = hd == '+'; diff --git a/tests/difftests.c b/tests/difftests.c index 65ef5366..d7bf9ca3 100644 --- a/tests/difftests.c +++ b/tests/difftests.c @@ -1044,6 +1044,42 @@ ATF_TC_BODY(bug_short_hunk_range, tc) gcli_free_diff_parser(&parser); } +ATF_TC_WITHOUT_HEAD(bug_no_newline_at_end_of_file); +ATF_TC_BODY(bug_no_newline_at_end_of_file, tc) +{ + struct gcli_patch patch = {0}; + struct gcli_diff_parser parser = {0}; + struct gcli_diff_comments comments = {0}; + struct gcli_diff_comment *comment = NULL; + + char const *const fname = "stuff_with_no_newline_in_diff.diff"; + + FILE *inf = open_sample(fname); + ATF_REQUIRE(gcli_diff_parser_from_file(inf, fname, &parser) == 0); + ATF_REQUIRE(gcli_parse_patch(&parser, &patch) == 0); + + TAILQ_INIT(&comments); + ATF_REQUIRE(gcli_patch_get_comments(&patch, &comments) == 0); + + comment = TAILQ_FIRST(&comments); + ATF_REQUIRE(comment); + + ATF_CHECK(comment->before.start_row == 1); + ATF_CHECK(comment->before.end_row == 1); + + ATF_CHECK(comment->after.start_row == 1); + ATF_CHECK(comment->after.end_row == 1); + + ATF_CHECK_STREQ(comment->comment, "This is a comment\n"); + ATF_CHECK_STREQ(comment->diff_text, + "-this is a test file\n" + "+this is a test file\n" + "\\ No newline at end of file\n"); + + gcli_free_patch(&patch); + gcli_free_diff_parser(&parser); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, free_patch_cleans_up_properly); @@ -1069,6 +1105,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, patch_for_git_object_format_version_1); ATF_TP_ADD_TC(tp, bug_patch_series_fail_get_comments); ATF_TP_ADD_TC(tp, bug_short_hunk_range); + ATF_TP_ADD_TC(tp, bug_no_newline_at_end_of_file); return atf_no_error(); } diff --git a/tests/samples/stuff_with_no_newline_in_diff.diff b/tests/samples/stuff_with_no_newline_in_diff.diff new file mode 100644 index 00000000..7f05b111 --- /dev/null +++ b/tests/samples/stuff_with_no_newline_in_diff.diff @@ -0,0 +1,11 @@ +diff --git a/test.txt b/test.txt +index 493021b..68a4528 100644 +--- a/test.txt ++++ b/test.txt +@@ -1 +1 @@ +> This is a comment +{ +-this is a test file ++this is a test file +\ No newline at end of file +} From 42313738f2b09c21eb50b902423e1a0e9f8eaf25 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 22 Aug 2024 16:52:51 +0200 Subject: [PATCH 46/50] Makefile.in: fix makefile installation BSD Make removes newlines in macros. The call to read was not producing the expected result for this reason. Change it to a shell for-loop which will split on words. Fixes: dea88294 (docs: Install manual pages into the correct directory, 2024-08-16) Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.in | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 954cb198..63ddb8a1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -421,5 +421,16 @@ install-auto: manpages gcli $(INSTALL) -d $(MANDIR)/man5 $(INSTALL) -d $(MANDIR)/man1 # TODO: compress manual pages - echo $(MANPAGES) | grep '\.1$$' | while read -r PAGE; do $(INSTALL) -m 644 $$PAGE $(MANDIR)/man1; done - echo $(MANPAGES) | grep '\.5$$' | while read -r PAGE; do $(INSTALL) -m 644 $$PAGE $(MANDIR)/man5; done + for PAGE in $(MANPAGES); do \ + case $$PAGE in \ + *.1) \ + $(INSTALL) -m 644 $$PAGE $(MANDIR)/man1 \ + ;; \ + *.5) \ + $(INSTALL) -m 644 $$PAGE $(MANDIR)/man5 \ + ;; \ + *) \ + echo error installing man page $$PAGE >&2 \ + ;; \ + esac \ + done From bf418c16d05595834ddfa4d47a752dc0dd26f28f Mon Sep 17 00:00:00 2001 From: Gavin-John Noonan Date: Sun, 25 Aug 2024 17:43:59 +0100 Subject: [PATCH 47/50] ci: update build targets We no longer offer a `distcheck` target in the Makefile, also our check run in ci for debian was calling the incorrect target Signed-off-by: Gavin-John Noonan --- .builds/debian-stable.yml | 2 +- .builds/freebsd.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.builds/debian-stable.yml b/.builds/debian-stable.yml index 9f9fd3ef..3dea098f 100644 --- a/.builds/debian-stable.yml +++ b/.builds/debian-stable.yml @@ -20,4 +20,4 @@ tasks: - check: | cd gcli ./configure --debug - make -j 4 all + make -j 4 check diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 74e801e7..30181e02 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -22,4 +22,4 @@ tasks: - check: | cd gcli ./configure --debug - make -j 4 distcheck + make -j 4 check From 14d537fc45adb939b4bfd7562ed783048195e9fd Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 26 Aug 2024 14:57:04 +0200 Subject: [PATCH 48/50] Makefile: ccache-ify building the libgcli.a static library The calls to both ar(1) and ranlib(1) were both missing the ccache prefix for speeding up the builds. Add the missing calls. Signed-off-by: Nico Sonack --- Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.in b/Makefile.in index 63ddb8a1..9647e461 100644 --- a/Makefile.in +++ b/Makefile.in @@ -295,8 +295,8 @@ gcli: libgcli.a $(GCLI_OBJS) $(LIBLOWDOWN_LIBS) libgcli.a: $(LIBGCLI_OBJS) - $(AR) -rc libgcli.a $(LIBGCLI_OBJS) - $(RANLIB) libgcli.a + $(CCACHE) $(AR) -rc libgcli.a $(LIBGCLI_OBJS) + $(CCACHE) $(RANLIB) libgcli.a $(LIBGCLI_OBJS): $(TEMPLATE_HEADERS) $(TEMPLATE_SRCS) From 71f6ec7375803d5aa1b2b89c14e28f92b2e48b1c Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 22 Aug 2024 11:06:01 +0200 Subject: [PATCH 49/50] review: automagically create cache directory This fixes the ENOENT when the gcli review cache directory doesn't exist and the review action is used for the first time. Reported-by: Bence Ferdinandy Fixes: https://gitlab.com/herrhotzenplotz/gcli/issues/237 Signed-off-by: Nico Sonack --- Changelog.md | 6 ++++ src/cmd/pull_reviews.c | 65 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 40b634ca..5998c07f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -25,6 +25,12 @@ This changelog does not follow semantic versioning. - A bug genereting invalid JSON when adding labels to a GitHub issue was fixed. +- The reviews cache directory is now automatically created if it + doesn't exist avoiding a 'No such file or directory' error when + invoking the review action for the first time. + + Reported by: Bence Ferdinandy + ### Changed - The pipelines subcommand has been refactored to accept actions diff --git a/src/cmd/pull_reviews.c b/src/cmd/pull_reviews.c index 49dbe722..5bae884f 100644 --- a/src/cmd/pull_reviews.c +++ b/src/cmd/pull_reviews.c @@ -29,9 +29,11 @@ #include +#include +#include #include #include -#include +#include #include #include @@ -59,6 +61,65 @@ djb2(unsigned char const *str) return hash; } +static int +do_mkdir(char const *const path) +{ + struct stat sb = {0}; + + if (stat(path, &sb) < 0) + return mkdir(path, 0750); + + if (S_ISDIR(sb.st_mode)) + return 0; + + errno = ENOTDIR; + return -1; +} + +static int +mkdir_p(char const *const dirname) +{ + char *d = NULL, *hd = NULL; + size_t l; + + l = strlen(dirname); + d = hd = strdup(dirname); + if (*d == '/') + d++; + + while (*d) { + char *p = strchr(d, '/'); + + if (!p || !(*p)) + break; + + *p = '\0'; + if (do_mkdir(hd) < 0) + return -1; + + *p = '/'; + d = p+1; + } + + if (dirname[l-1] != '/') { + if (do_mkdir(hd) < 0) + return -1; + } + + free(hd); + + return 0; +} + +static void +ensure_cache_dir_exists(void) +{ + char *dir = get_review_file_cache_dir(); + if (mkdir_p(dir) < 0) + err(1, "gcli: error: could not create cache directory"); + + free(dir); +} static char * make_review_diff_file_name(char const *const owner, char const *const repo, @@ -264,6 +325,8 @@ do_review_session(char const *owner, char const *repo, gcli_id const pull_id) .diff_path = get_review_diff_file_name(owner, repo, pull_id), }; + ensure_cache_dir_exists(); + TAILQ_INIT(&ctx.details.comments); edit_diff(&ctx); From 1f583c04c8f2f1cfb481ca71c80ef1b803f9fb52 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 26 Aug 2024 15:09:53 +0200 Subject: [PATCH 50/50] Changelog: update for new patchsets Signed-off-by: Nico Sonack --- Changelog.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Changelog.md b/Changelog.md index 5998c07f..2901652c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,6 +31,21 @@ This changelog does not follow semantic versioning. Reported by: Bence Ferdinandy +- A few bugs in the patch parser have been fixed: + + - Under rare conditions hunk ranges were incorrectly parsed + - Parser errors when a diff included lines starting with a backslash + (e.g. when there is no newline at the end of file) were fixed + + Reported by: Bence Ferdinandy + +- The installation location of the manual pages of gcli has been + fixed. The latest release accidentially installed manual pages to + `${DESTDIR}${PREFIX}/share/man` instead of + `${DESTDIR}${PREFIX}/share/man/manX`. + + Reported by: Bence Ferdinandy + ### Changed - The pipelines subcommand has been refactored to accept actions