From d9bd9feda44bbbe52d0ab512e3673d3a1dfdfafc Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 5 Feb 2024 18:28:57 +0100 Subject: [PATCH 01/32] Update version for development Signed-off-by: Nico Sonack --- Changelog.md | 10 ++++++++++ configure.ac | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1f1cb8f2..8ebc621d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,16 @@ This changelog does not follow semantic versioning. +## UNRELEASED + +### Added + +### Fixed + +### Changed + +### Removed + ## 2.2.0 (2024-Feb-05) ### Added diff --git a/configure.ac b/configure.ac index c683c784..a0ff3b98 100644 --- a/configure.ac +++ b/configure.ac @@ -3,14 +3,14 @@ AC_PREREQ([2.69]) AC_INIT([gcli], - [2.2.0], + [2.3.0-devel], [~herrhotzenplotz/gcli-discuss@lists.sr.ht], [gcli], [https://herrhotzenplotz.de/gcli]) AM_INIT_AUTOMAKE([1.0 foreign subdir-objects dist-bzip2 dist-xz -Wall]) dnl Release Date. -PACKAGE_DATE="2024-Feb-05" +PACKAGE_DATE="UNRELEASED" AC_SUBST([PACKAGE_DATE]) dnl Silent by default. From ff3d8c130c94b32074aae91588d741d217ec672e Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 6 Feb 2024 16:37:41 +0100 Subject: [PATCH 02/32] docs: Update README and Website for addition of AUR package gcli has been added to the AUR: https://aur.archlinux.org/packages/gcli Update the Website and the README accordingly. Issue-tracker: https://github.com/herrhotzenplotz/gcli/issues/124 Reported-by: Werenter Signed-off-by: Nico Sonack --- README.md | 3 +++ docs/website/index.html | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb8773a5..6365c878 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ consider using the link above. There are official packages available: - [FreeBSD](https://freshports.org/devel/gcli) +- [Debian Testing](https://packages.debian.org/trixie/gcli) +- [ArchLinux AUR](https://aur.archlinux.org/packages/gcli) +- [NixPkgs Unstable](https://search.nixos.org/packages?channel=unstable&show=gcli&from=0&size=50&sort=relevance&type=packages&query=gcli) ### Dependencies diff --git a/docs/website/index.html b/docs/website/index.html index 70289881..00373821 100644 --- a/docs/website/index.html +++ b/docs/website/index.html @@ -72,11 +72,12 @@

General

-

GCLI is available or coming to various distributions

+

GCLI is available in various distributions

If you want gcli to be available in your favourite operating system please submit them to the respective packaging tree.
From 22db92aca761853d42b0faa8b82edd0c98b45f71 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Wed, 7 Feb 2024 17:14:43 +0100 Subject: [PATCH 03/32] Fix XDG variable name --- docs/gcli.1.in | 8 ++++---- docs/gcli.5.in | 2 +- src/cmd/cmdconfig.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/gcli.1.in b/docs/gcli.1.in index b7a2fc9b..7bc5b112 100644 --- a/docs/gcli.1.in +++ b/docs/gcli.1.in @@ -153,12 +153,12 @@ Operate on the given numeric identifier. Other options specific to the context are documented in the respective man pages. .Sh ENVIRONMENT -.Bl -tag -width XDG_CONFIG_DIR +.Bl -tag -width XDG_CONFIG_HOME .It Ev EDITOR If the gcli config file does not name an editor, .Nm may use this editor. -.It Ev XDG_CONFIG_DIR +.It Ev XDG_CONFIG_HOME There should be a subdirectory called gcli in the directory this environment variable points to where .Nm @@ -183,8 +183,8 @@ escape sequences. See (--colours). .El .Sh FILES -.Bl -tag -width ${XDG_CONFIG_DIR}/gcli/config -compact -.It Pa ${XDG_CONFIG_DIR}/gcli/config +.Bl -tag -width ${XDG_CONFIG_HOME}/gcli/config -compact +.It Pa ${XDG_CONFIG_HOME}/gcli/config The user configuration file for gcli. It contains account definitions as well as sensible default values. See .Xr gcli 5 . diff --git a/docs/gcli.5.in b/docs/gcli.5.in index d6ebdd77..67b8fdae 100644 --- a/docs/gcli.5.in +++ b/docs/gcli.5.in @@ -15,7 +15,7 @@ into the repository and provide these default values to other users as well. .Ss User Configuration File The user configuration file is located in -.Pa ${XDG_CONFIG_DIR}/gcli/config . +.Pa ${XDG_CONFIG_HOME}/gcli/config . On most systems this equal to .Pa ${HOME}/.config/gcli/config . .Pp diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c index e412755e..5b5b7c3b 100644 --- a/src/cmd/cmdconfig.c +++ b/src/cmd/cmdconfig.c @@ -389,11 +389,11 @@ ensure_config(struct gcli_ctx *ctx) cfg->inited = true; - file_path = getenv("XDG_CONFIG_PATH"); + file_path = getenv("XDG_CONFIG_HOME"); if (!file_path) { file_path = getenv("HOME"); if (!file_path) { - warnx("Neither XDG_CONFIG_PATH nor HOME set in env"); + warnx("Neither XDG_CONFIG_HOME nor HOME set in env"); return cfg; } From ff156421d7770b9cbfafad65b41c9a7bc35957c3 Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Wed, 7 Feb 2024 17:33:11 +0100 Subject: [PATCH 04/32] Fix spurious emphasis in gcli.5 --- docs/gcli.5.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/gcli.5.in b/docs/gcli.5.in index 67b8fdae..64f33b8c 100644 --- a/docs/gcli.5.in +++ b/docs/gcli.5.in @@ -103,7 +103,8 @@ It contains a list of key-value pairs. Allowed keys are: Name of a branch that the changes should be merged into by default. Usually this is one of .Em master , -.Em main or +.Em main +or .Em trunk . .It pr.upstream Name of the upstream repository to submit the pull request to by default. From b1187f394bd0704ec440116183edcae951b6f0f1 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Wed, 7 Feb 2024 21:53:30 +0100 Subject: [PATCH 05/32] Update Changelog for fix of XDG_CONFIG_HOME variable Signed-off-by: Nico Sonack --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 8ebc621d..dda30fe8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,10 @@ This changelog does not follow semantic versioning. ### Fixed +- gcli was incorrectly using an environment variable *XDG_CONFIG_DIR*. + This variable has now been fixed to be *XDG_CONFIG_HOME*. + Submitted by: Jakub Wilk + ### Changed ### Removed From 3c057d408afc4d7cdfc919791b1670ce69fcbede Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Thu, 8 Feb 2024 14:06:54 +0100 Subject: [PATCH 06/32] docs: Fix typos --- docs/gcli-comment.1.in | 2 +- docs/gcli-issues.1.in | 4 ++-- docs/gcli.1.in | 4 ++-- docs/pgen.org | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/gcli-comment.1.in b/docs/gcli-comment.1.in index 58b34836..c4e3427b 100644 --- a/docs/gcli-comment.1.in +++ b/docs/gcli-comment.1.in @@ -20,7 +20,7 @@ issues on GitHub and Gitea, making the .Fl i and .Fl p -flags exchangable without changing the overall effect of creating the +flags exchangeable without changing the overall effect of creating the comment. .Nm will open an editor, either specified in your environment through diff --git a/docs/gcli-issues.1.in b/docs/gcli-issues.1.in index d28658ab..261d64b2 100644 --- a/docs/gcli-issues.1.in +++ b/docs/gcli-issues.1.in @@ -50,9 +50,9 @@ option. .It Fl A , Fl -author Ar user Only list issues authored by the given user. .It Fl L , Fl -label Ar label -Filter issues by the given label. This option may only be specfied once. +Filter issues by the given label. This option may only be specified once. .It Fl M , Fl -milestone Ar milestone -Filter issues by the given milestone. This option may only be specfied once. +Filter issues by the given milestone. This option may only be specified once. .It Fl n , -count Ar n Fetch at least .Ar n diff --git a/docs/gcli.1.in b/docs/gcli.1.in index 7bc5b112..09f8835c 100644 --- a/docs/gcli.1.in +++ b/docs/gcli.1.in @@ -28,7 +28,7 @@ command). .Pp The default behaviour of .Nm -can be overriden to accomodate more nuanced use cases. Manual +can be overridden to accommodate more nuanced use cases. Manual overrides must be passed before subcommands and their options. .Sh SUBCOMMANDS Most of these subcommands are documented in dedicated man pages. @@ -104,7 +104,7 @@ unless stdout is not a tty. See This is useful in combination with modern pagers such as .Xr less 1 . .It Fl q , -quiet -Supresses most output of +Suppresses most output of .Nm . .It Fl v , -verbose Be very verbose. This means that warnings about missing config files diff --git a/docs/pgen.org b/docs/pgen.org index d76742b2..02036c70 100644 --- a/docs/pgen.org +++ b/docs/pgen.org @@ -69,7 +69,7 @@ "things" => things as array of thing use parse_thing); #+end_src - This will generate two functions with the follwing signatures: + This will generate two functions with the following signatures: #+begin_src shell :exports results :results output ../pgen -th < Date: Sun, 18 Feb 2024 14:08:20 +0900 Subject: [PATCH 07/32] Build shared library on cygwin --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 9af5865f..2bc9af54 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,7 @@ bin_PROGRAMS = gcli$(EXEEXT) gcli_LDADD = libgcli.la libgcli_la_LIBADD = libpdjson.la libsn.la +libgcli_la_LDFLAGS = -no-undefined dist_man_MANS = \ docs/gcli-api.1 \ From fcbae36e9199744646f6469e36e9326cfe41a255 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 26 Feb 2024 18:02:17 +0100 Subject: [PATCH 08/32] Fix segmentation fault when listing forks Yet another one of the silly _SV changes. Signed-off-by: Nico Sonack --- Changelog.md | 2 ++ src/cmd/forks.c | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 102766e4..b16d1003 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,8 @@ This changelog does not follow semantic versioning. This variable has now been fixed to be *XDG_CONFIG_HOME*. Submitted by: Jakub Wilk +- Fixed a segmentation fault when listing forks + ### Changed ### Removed diff --git a/src/cmd/forks.c b/src/cmd/forks.c index a3cc6d58..f38e2135 100644 --- a/src/cmd/forks.c +++ b/src/cmd/forks.c @@ -68,10 +68,10 @@ gcli_print_forks(enum gcli_output_flags const flags, size_t n; gcli_tbl table; struct gcli_tblcoldef cols[] = { - { .name = "OWNER", .type = GCLI_TBLCOLTYPE_SV, .flags = GCLI_TBLCOL_BOLD }, - { .name = "DATE", .type = GCLI_TBLCOLTYPE_SV, .flags = 0 }, - { .name = "FORKS", .type = GCLI_TBLCOLTYPE_INT, .flags = GCLI_TBLCOL_JUSTIFYR }, - { .name = "FULLNAME", .type = GCLI_TBLCOLTYPE_SV, .flags = 0 }, + { .name = "OWNER", .type = GCLI_TBLCOLTYPE_STRING, .flags = GCLI_TBLCOL_BOLD }, + { .name = "DATE", .type = GCLI_TBLCOLTYPE_STRING, .flags = 0 }, + { .name = "FORKS", .type = GCLI_TBLCOLTYPE_INT, .flags = GCLI_TBLCOL_JUSTIFYR }, + { .name = "FULLNAME", .type = GCLI_TBLCOLTYPE_STRING, .flags = 0 }, }; if (list->forks_size == 0) { From daf90edf7d35ce63604ad73f6f3c109d96032406 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 27 Feb 2024 16:33:31 +0100 Subject: [PATCH 09/32] Remove SV printing for tables and dictionaries This finally removes string-views from the table printer code. That way we can finally avoid segmentation faults due to this confusion. Signed-off-by: Nico Sonack --- include/gcli/cmd/table.h | 1 - src/cmd/table.c | 5 ----- 2 files changed, 6 deletions(-) diff --git a/include/gcli/cmd/table.h b/include/gcli/cmd/table.h index 2e36155d..5c1d9aa5 100644 --- a/include/gcli/cmd/table.h +++ b/include/gcli/cmd/table.h @@ -67,7 +67,6 @@ enum gcli_tblcoltype { GCLI_TBLCOLTYPE_LONG, /* signed long int */ GCLI_TBLCOLTYPE_ID, /* some ID type (uint64_t) */ GCLI_TBLCOLTYPE_STRING, /* C string */ - GCLI_TBLCOLTYPE_SV, /* sn_sv */ GCLI_TBLCOLTYPE_DOUBLE, /* double precision float */ GCLI_TBLCOLTYPE_BOOL, /* yes/no */ }; diff --git a/src/cmd/table.c b/src/cmd/table.c index fa97de8a..4227a373 100644 --- a/src/cmd/table.c +++ b/src/cmd/table.c @@ -159,11 +159,6 @@ tablerow_add_cell(struct gcli_tbl *const table, row->cells[col].text = strdup(it); cell_size = strlen(it); } break; - case GCLI_TBLCOLTYPE_SV: { - sn_sv src = va_arg(*vp, sn_sv); - row->cells[col].text = sn_sv_to_cstr(src); - cell_size = src.length; - } break; case GCLI_TBLCOLTYPE_DOUBLE: { row->cells[col].text = sn_asprintf("%lf", va_arg(*vp, double)); cell_size = strlen(row->cells[col].text); From 2562c81431f407e0d9b4f304e883cd2e6dd0db44 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 29 Feb 2024 17:00:07 +0100 Subject: [PATCH 10/32] Use Gitea-side filter for issues/pull requests in issues search Do not use the Github fetch routine as it introduces useless overhead and it's just doing a fetch_list anyways. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- src/gitea/issues.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/gitea/issues.c b/src/gitea/issues.c index a73c08e4..ac6fe5ce 100644 --- a/src/gitea/issues.c +++ b/src/gitea/issues.c @@ -38,6 +38,8 @@ #include +#include + int gitea_issues_search(struct gcli_ctx *ctx, char const *owner, char const *repo, struct gcli_issue_fetch_details const *details, @@ -46,6 +48,13 @@ gitea_issues_search(struct gcli_ctx *ctx, char const *owner, char const *repo, char *url = NULL, *e_owner = NULL, *e_repo = NULL, *e_author = NULL, *e_label = NULL, *e_milestone = NULL, *e_query = NULL; + struct gcli_fetch_list_ctx fl = { + .listp = &out->issues, + .sizep = &out->issues_size, + .parse = (parsefn)(parse_github_issues), + .max = max, + }; + if (details->milestone) { char *tmp = gcli_urlencode(details->milestone); e_milestone = sn_asprintf("&milestones=%s", tmp); @@ -73,7 +82,7 @@ gitea_issues_search(struct gcli_ctx *ctx, char const *owner, char const *repo, e_owner = gcli_urlencode(owner); e_repo = gcli_urlencode(repo); - url = sn_asprintf("%s/repos/%s/%s/issues?state=%s%s%s%s%s", + url = sn_asprintf("%s/repos/%s/%s/issues?type=issues&state=%s%s%s%s%s", gcli_get_apibase(ctx), e_owner, e_repo, details->all ? "all" : "open", @@ -89,7 +98,7 @@ gitea_issues_search(struct gcli_ctx *ctx, char const *owner, char const *repo, free(e_owner); free(e_repo); - return github_fetch_issues(ctx, url, max, out); + return gcli_fetch_list(ctx, url, &fl); } int From a4c2c846f6f755db604c8b373fb3e26fc63b24b0 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 27 Feb 2024 23:34:32 +0100 Subject: [PATCH 11/32] Rename get_pulls -> search_pulls Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/-/issues/220 Signed-off-by: Nico Sonack --- include/gcli/forges.h | 2 +- include/gcli/pulls.h | 6 +++--- src/cmd/pulls.c | 2 +- src/forges.c | 6 +++--- src/pulls.c | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/gcli/forges.h b/include/gcli/forges.h index a704bb6d..f8fc1d31 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -279,7 +279,7 @@ struct gcli_forge_descriptor { /** * Get a list of PRs/MRs on the given repo */ - int (*get_pulls)( + int (*search_pulls)( struct gcli_ctx *ctx, char const *owner, char const *reponame, diff --git a/include/gcli/pulls.h b/include/gcli/pulls.h index d9724561..97fcb72d 100644 --- a/include/gcli/pulls.h +++ b/include/gcli/pulls.h @@ -124,9 +124,9 @@ struct gcli_pull_checks_list { int forge_type; }; -int gcli_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, - struct gcli_pull_fetch_details const *details, int max, - struct gcli_pull_list *out); +int gcli_search_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *details, int max, + struct gcli_pull_list *out); void gcli_pull_free(struct gcli_pull *it); diff --git a/src/cmd/pulls.c b/src/cmd/pulls.c index 7344f479..5bb9434f 100644 --- a/src/cmd/pulls.c +++ b/src/cmd/pulls.c @@ -661,7 +661,7 @@ subcommand_pulls(int argc, char *argv[]) /* In case no explicit PR number was specified, list all * open PRs and exit */ if (pr < 0) { - if (gcli_get_pulls(g_clictx, owner, repo, &details, n, &pulls) < 0) + if (gcli_search_pulls(g_clictx, owner, repo, &details, n, &pulls) < 0) errx(1, "gcli: error: could not fetch pull requests: %s", gcli_get_error(g_clictx)); diff --git a/src/forges.c b/src/forges.c index 6835a52d..6fe76fb0 100644 --- a/src/forges.c +++ b/src/forges.c @@ -115,7 +115,7 @@ github_forge_descriptor = .get_pull = github_get_pull, .get_pull_checks = github_pull_get_checks, .get_pull_commits = github_get_pull_commits, - .get_pulls = github_get_pulls, + .search_pulls = github_get_pulls, .perform_submit_pull = github_perform_submit_pull, .pull_add_reviewer = github_pull_add_reviewer, .pull_close = github_pull_close, @@ -211,7 +211,7 @@ gitlab_forge_descriptor = .get_pull = gitlab_get_pull, .get_pull_checks = (gcli_get_pull_checks_cb)gitlab_get_mr_pipelines, .get_pull_commits = gitlab_get_pull_commits, - .get_pulls = gitlab_get_mrs, + .search_pulls = gitlab_get_mrs, .perform_submit_pull = gitlab_perform_submit_mr, .pull_add_labels = gitlab_mr_add_labels, .pull_add_reviewer = gitlab_mr_add_reviewer, @@ -303,7 +303,7 @@ gitea_forge_descriptor = .get_pull = gitea_get_pull, .get_pull_checks = gitea_pull_get_checks, /* stub, will always return an error */ .get_pull_commits = gitea_get_pull_commits, - .get_pulls = gitea_get_pulls, + .search_pulls = gitea_get_pulls, .perform_submit_pull = gitea_pull_submit, .pull_add_labels = gitea_issue_add_labels, .pull_add_reviewer = gitea_pull_add_reviewer, diff --git a/src/pulls.c b/src/pulls.c index 373c2e3d..12a8b060 100644 --- a/src/pulls.c +++ b/src/pulls.c @@ -50,11 +50,11 @@ gcli_pulls_free(struct gcli_pull_list *const it) } int -gcli_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, - struct gcli_pull_fetch_details const *const details, int const max, - struct gcli_pull_list *const out) +gcli_search_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *const details, + int const max, struct gcli_pull_list *const out) { - gcli_null_check_call(get_pulls, ctx, owner, repo, details, max, out); + gcli_null_check_call(search_pulls, ctx, owner, repo, details, max, out); } int From 1d99b9cf5d3ecd42b958f398e4d5450df53522c8 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 27 Feb 2024 23:44:30 +0100 Subject: [PATCH 12/32] Add search term to pull request search details struct Signed-off-by: Nico Sonack --- docs/gcli-pulls.1.in | 1 + include/gcli/pulls.h | 9 +++++---- src/cmd/pulls.c | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/gcli-pulls.1.in b/docs/gcli-pulls.1.in index 7b6c44bb..591e0c77 100644 --- a/docs/gcli-pulls.1.in +++ b/docs/gcli-pulls.1.in @@ -13,6 +13,7 @@ .Op Fl s .Op Fl n Ar n .Op Fl o Ar owner Fl r Ar repo +.Op Ar search-terms... .Nm .Fl i Ar pr .Op Fl o Ar owner Fl r Ar repo diff --git a/include/gcli/pulls.h b/include/gcli/pulls.h index 97fcb72d..6867f4a0 100644 --- a/include/gcli/pulls.h +++ b/include/gcli/pulls.h @@ -104,10 +104,11 @@ struct gcli_submit_pull_options { }; struct gcli_pull_fetch_details { - bool all; - char const *author; - char const *label; - char const *milestone; + bool all; /** Ignore status of the pull requests */ + char const *author; /** Author of the pull request or NULL */ + char const *label; /** a label attached to the pull request or NULL */ + char const *milestone; /** a milestone this pull request is a part of or NULL */ + char const *search_term; /** some text to match in the pull request or NULL */ }; /** Generic list of checks ran on a pull request diff --git a/src/cmd/pulls.c b/src/cmd/pulls.c index 5bb9434f..ac554996 100644 --- a/src/cmd/pulls.c +++ b/src/cmd/pulls.c @@ -58,7 +58,7 @@ usage(void) fprintf(stderr, "usage: gcli pulls create [-o owner -r repo] [-f from]\n"); fprintf(stderr, " [-t to] [-d] [-a] [-l label] pull-request-title\n"); fprintf(stderr, " gcli pulls [-o owner -r repo] [-a] [-A author] [-n number]\n"); - fprintf(stderr, " [-L label] [-M milestone] [-s]\n"); + fprintf(stderr, " [-L label] [-M milestone] [-s] [search-terms...]\n"); fprintf(stderr, " gcli pulls [-o owner -r repo] -i pull-id actions...\n"); fprintf(stderr, "OPTIONS:\n"); fprintf(stderr, " -o owner The repository owner\n"); @@ -563,7 +563,7 @@ subcommand_pulls(int argc, char *argv[]) return subcommand_pull_create(argc, argv); } - const struct option options[] = { + struct option const options[] = { { .name = "all", .has_arg = no_argument, .flag = NULL, @@ -661,6 +661,14 @@ subcommand_pulls(int argc, char *argv[]) /* In case no explicit PR number was specified, list all * open PRs and exit */ if (pr < 0) { + char *search_term = NULL; + + /* Trailing arguments indicate a search term */ + if (argc) + search_term = sn_join_with((char const *const *)argv, argc, " "); + + details.search_term = search_term; + if (gcli_search_pulls(g_clictx, owner, repo, &details, n, &pulls) < 0) errx(1, "gcli: error: could not fetch pull requests: %s", gcli_get_error(g_clictx)); @@ -668,6 +676,9 @@ subcommand_pulls(int argc, char *argv[]) gcli_print_pulls(flags, &pulls, n); gcli_pulls_free(&pulls); + free(search_term); + details.search_term = search_term = NULL; + return EXIT_SUCCESS; } From 7d2e0d477ecc455e09dcab3a2e21dfddd7d612d2 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Wed, 28 Feb 2024 00:09:18 +0100 Subject: [PATCH 13/32] Implement pull request search for Github Signed-off-by: Nico Sonack --- include/gcli/github/pulls.h | 6 +-- src/forges.c | 2 +- src/gitea/pulls.c | 2 +- src/github/pulls.c | 73 +++++++++++++++++++++++++++++++++++-- templates/github/pulls.t | 8 +++- 5 files changed, 80 insertions(+), 11 deletions(-) diff --git a/include/gcli/github/pulls.h b/include/gcli/github/pulls.h index 30f07abc..cf5e5191 100644 --- a/include/gcli/github/pulls.h +++ b/include/gcli/github/pulls.h @@ -37,9 +37,9 @@ #include #include -int github_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, - struct gcli_pull_fetch_details const *details, int max, - struct gcli_pull_list *out); +int github_search_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *details, int max, + struct gcli_pull_list *out); int github_pull_get_diff(struct gcli_ctx *ctx, FILE *stream, char const *owner, char const *reponame, gcli_id pr_number); diff --git a/src/forges.c b/src/forges.c index 6fe76fb0..bee197c4 100644 --- a/src/forges.c +++ b/src/forges.c @@ -115,7 +115,7 @@ github_forge_descriptor = .get_pull = github_get_pull, .get_pull_checks = github_pull_get_checks, .get_pull_commits = github_get_pull_commits, - .search_pulls = github_get_pulls, + .search_pulls = github_search_pulls, .perform_submit_pull = github_perform_submit_pull, .pull_add_reviewer = github_pull_add_reviewer, .pull_close = github_pull_close, diff --git a/src/gitea/pulls.c b/src/gitea/pulls.c index 66db5932..f15f488d 100644 --- a/src/gitea/pulls.c +++ b/src/gitea/pulls.c @@ -38,7 +38,7 @@ gitea_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, struct gcli_pull_fetch_details const *const details, int const max, struct gcli_pull_list *const out) { - return github_get_pulls(ctx, owner, repo, details, max, out); + return github_search_pulls(ctx, owner, repo, details, max, out); } int diff --git a/src/github/pulls.c b/src/github/pulls.c index 61ae2342..16b3476f 100644 --- a/src/github/pulls.c +++ b/src/github/pulls.c @@ -107,10 +107,64 @@ github_fetch_pulls(struct gcli_ctx *ctx, char *url, return gcli_fetch_list(ctx, url, &fl); } -int -github_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, - struct gcli_pull_fetch_details const *const details, - int const max, struct gcli_pull_list *const list) +static int +search_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *const details, + int const max, struct gcli_pull_list *const out) +{ + char *url = NULL, *query_string = NULL, *e_query_string = NULL, + *milestone = NULL, *author = NULL, *label = NULL; + int rc = 0; + struct gcli_fetch_buffer buffer = {0}; + struct json_stream stream = {0}; + + (void) max; + + if (details->milestone) + milestone = sn_asprintf("milestone:%s", details->milestone); + + if (details->author) + author = sn_asprintf("author:%s", details->author); + + if (details->label) + label = sn_asprintf("label:%s", details->label); + + query_string = sn_asprintf("repo:%s/%s is:pull-request%s %s %s %s %s", + owner, repo, + details->all ? "" : " is:open", + milestone ? milestone : "", author ? author : "", + label ? label : "", details->search_term); + e_query_string = gcli_urlencode(query_string); + + url = sn_asprintf("%s/search/issues?q=%s", gcli_get_apibase(ctx), + e_query_string); + + free(milestone); + free(author); + free(label); + free(query_string); + free(e_query_string); + + rc = gcli_fetch(ctx, url, NULL, &buffer); + if (rc < 0) + goto error_fetch; + + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_github_pull_search_result(ctx, &stream, out); + + json_close(&stream); + free(buffer.data); + +error_fetch: + free(url); + + return rc; +} + +static int +list_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *const details, + int const max, struct gcli_pull_list *const list) { char *url = NULL; char *e_owner = NULL; @@ -130,6 +184,17 @@ github_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, return github_fetch_pulls(ctx, url, details, max, list); } +int +github_search_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *const details, + int const max, struct gcli_pull_list *const list) +{ + if (details->search_term) + return search_pulls(ctx, owner, repo, details, max, list); + else + return list_pulls(ctx, owner, repo, details, max, list); +} + int github_pull_get_patch(struct gcli_ctx *ctx, FILE *stream, char const *owner, char const *repo, gcli_id const pr_number) diff --git a/templates/github/pulls.t b/templates/github/pulls.t index 8f2e467b..c745c884 100644 --- a/templates/github/pulls.t +++ b/templates/github/pulls.t @@ -63,5 +63,9 @@ object of struct gcli_pull with parser github_pr_merge_message is object of char* select "message" as string; -parser github_pulls is array of struct gcli_pull - use parse_github_pull; +parser github_pulls is +array of struct gcli_pull use parse_github_pull; + +parser github_pull_search_result is +object of struct gcli_pull_list with + ("items" => pulls as array of gcli_pull use parse_github_pull); From dcc1f7d66d018fb1e0d6217a625f8fb50f57fa52 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Wed, 28 Feb 2024 00:16:16 +0100 Subject: [PATCH 14/32] Implement pull request search for Gitlab Signed-off-by: Nico Sonack --- src/gitlab/merge_requests.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/gitlab/merge_requests.c b/src/gitlab/merge_requests.c index d95928be..4b0cb245 100644 --- a/src/gitlab/merge_requests.c +++ b/src/gitlab/merge_requests.c @@ -84,6 +84,7 @@ gitlab_get_mrs(struct gcli_ctx *ctx, char const *owner, char const *repo, char *e_author = NULL; char *e_label = NULL; char *e_milestone = NULL; + char *e_search = NULL; e_owner = gcli_urlencode(owner); e_repo = gcli_urlencode(repo); @@ -109,14 +110,25 @@ gitlab_get_mrs(struct gcli_ctx *ctx, char const *owner, char const *repo, free(tmp); } - url = sn_asprintf("%s/projects/%s%%2F%s/merge_requests%s%s%s%s", + if (details->search_term) { + char *tmp = gcli_urlencode(details->search_term); + bool const need_qmark = details->all && !details->author && + !details->label && !details->milestone; + + e_search = sn_asprintf("%csearch=%s", need_qmark ? '?' : '&', tmp); + free(tmp); + } + + url = sn_asprintf("%s/projects/%s%%2F%s/merge_requests%s%s%s%s%s", gcli_get_apibase(ctx), e_owner, e_repo, details->all ? "" : "?state=opened", e_author ? e_author : "", e_label ? e_label : "", - e_milestone ? e_milestone : ""); + e_milestone ? e_milestone : "", + e_search ? e_search : ""); + free(e_search); free(e_milestone); free(e_label); free(e_author); From d4bdf06814bd7cae6aa4a0136697f3a8edfca01c Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Thu, 29 Feb 2024 16:52:37 +0100 Subject: [PATCH 15/32] Implement pull request search for Gitea Signed-off-by: Nico Sonack --- include/gcli/gitea/pulls.h | 7 +++-- src/forges.c | 2 +- src/gitea/pulls.c | 63 +++++++++++++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/include/gcli/gitea/pulls.h b/include/gcli/gitea/pulls.h index 401575b5..8a74225f 100644 --- a/include/gcli/gitea/pulls.h +++ b/include/gcli/gitea/pulls.h @@ -37,9 +37,10 @@ #include #include -int gitea_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, - struct gcli_pull_fetch_details const *details, int max, - struct gcli_pull_list *out); +int gitea_search_pulls(struct gcli_ctx *ctx, char const *owner, + char const *repo, + struct gcli_pull_fetch_details const *details, + int const max, struct gcli_pull_list *const out); int gitea_get_pull(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pr_number, struct gcli_pull *out); diff --git a/src/forges.c b/src/forges.c index bee197c4..eff67819 100644 --- a/src/forges.c +++ b/src/forges.c @@ -303,7 +303,7 @@ gitea_forge_descriptor = .get_pull = gitea_get_pull, .get_pull_checks = gitea_pull_get_checks, /* stub, will always return an error */ .get_pull_commits = gitea_get_pull_commits, - .search_pulls = gitea_get_pulls, + .search_pulls = gitea_search_pulls, .perform_submit_pull = gitea_pull_submit, .pull_add_labels = gitea_issue_add_labels, .pull_add_reviewer = gitea_pull_add_reviewer, diff --git a/src/gitea/pulls.c b/src/gitea/pulls.c index f15f488d..94bd00e2 100644 --- a/src/gitea/pulls.c +++ b/src/gitea/pulls.c @@ -33,12 +33,67 @@ #include +#include + int -gitea_get_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, - struct gcli_pull_fetch_details const *const details, int const max, - struct gcli_pull_list *const out) +gitea_search_pulls(struct gcli_ctx *ctx, char const *owner, char const *repo, + struct gcli_pull_fetch_details const *details, + int const max, struct gcli_pull_list *const out) { - return github_search_pulls(ctx, owner, repo, details, max, out); + char *url = NULL, *e_owner = NULL, *e_repo = NULL, *e_author = NULL, + *e_label = NULL, *e_milestone = NULL, *e_query = NULL; + + struct gcli_fetch_list_ctx fl = { + .listp = &out->pulls, + .sizep = &out->pulls_size, + .parse = (parsefn)(parse_github_pulls), + .max = max, + }; + + if (details->milestone) { + char *tmp = gcli_urlencode(details->milestone); + e_milestone = sn_asprintf("&milestones=%s", tmp); + free(tmp); + } + + if (details->author) { + char *tmp = gcli_urlencode(details->author); + e_author = sn_asprintf("&created_by=%s", tmp); + free(tmp); + } + + if (details->label) { + char *tmp = gcli_urlencode(details->label); + e_label = sn_asprintf("&labels=%s", tmp); + free(tmp); + } + + if (details->search_term) { + char *tmp = gcli_urlencode(details->search_term); + e_query = sn_asprintf("&q=%s", tmp); + free(tmp); + } + + e_owner = gcli_urlencode(owner); + e_repo = gcli_urlencode(repo); + + url = sn_asprintf("%s/repos/%s/%s/issues?type=pulls&state=%s%s%s%s%s", + gcli_get_apibase(ctx), + e_owner, e_repo, + details->all ? "all" : "open", + e_author ? e_author : "", + e_label ? e_label : "", + e_milestone ? e_milestone : "", + e_query ? e_query : ""); + + free(e_query); + free(e_milestone); + free(e_author); + free(e_label); + free(e_owner); + free(e_repo); + + return gcli_fetch_list(ctx, url, &fl); } int From f05a9525ecbe19d11de7b09d98b50f96398e64ab Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 4 Mar 2024 13:54:06 +0100 Subject: [PATCH 16/32] Update Changelog for pull request search feature Fixes #220 Issue-tracker: https://gitlab.com/herrhotzenplotz/gcli/issues/220 Signed-off-by: Nico Sonack --- Changelog.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Changelog.md b/Changelog.md index b16d1003..ddc63e6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,17 @@ This changelog does not follow semantic versioning. - It is now possible to build gcli against libgcli as a DLL on cygwin. Submitted by: Daisuke Fujimura +- The pulls subcommand now allows searching for pull requests with + a given search term. The search terms can be appended to the + regular pull subcommand for listing PRs: + + ```console + $ gcli pulls -L bug segmentation fault + ``` + + The above will search for pull requests containing »segmentation + fault« and the label »bug«. + ### Fixed - gcli was incorrectly using an environment variable *XDG_CONFIG_DIR*. From 2da05b391cf078d9de8208984407d3b9904c2c7f Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 4 Mar 2024 15:57:35 +0100 Subject: [PATCH 17/32] Avoid copying struct of pointers with pull request creation details Instead of passing a copy of a struct (likely through the stack) pass a pointer to it (allowing call overhead optimisation). Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/forges.h | 2 +- include/gcli/gitea/pulls.h | 2 +- include/gcli/github/pulls.h | 3 ++- include/gcli/gitlab/merge_requests.h | 2 +- include/gcli/pulls.h | 2 +- src/cmd/pulls.c | 10 +++++----- src/gitea/pulls.c | 2 +- src/github/pulls.c | 29 ++++++++++++++-------------- src/gitlab/merge_requests.c | 26 ++++++++++++------------- src/pulls.c | 4 ++-- 10 files changed, 42 insertions(+), 40 deletions(-) diff --git a/include/gcli/forges.h b/include/gcli/forges.h index f8fc1d31..b1dc4f69 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -341,7 +341,7 @@ struct gcli_forge_descriptor { * Submit PR/MR */ int (*perform_submit_pull)( struct gcli_ctx *ctx, - struct gcli_submit_pull_options opts); + struct gcli_submit_pull_options *opts); /** * Get a list of commits in the given PR/MR */ diff --git a/include/gcli/gitea/pulls.h b/include/gcli/gitea/pulls.h index 8a74225f..881f9811 100644 --- a/include/gcli/gitea/pulls.h +++ b/include/gcli/gitea/pulls.h @@ -49,7 +49,7 @@ int gitea_get_pull_commits(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pr_number, struct gcli_commit_list *out); -int gitea_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts); +int gitea_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options *opts); int gitea_pull_merge(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pr_number, enum gcli_merge_flags flags); diff --git a/include/gcli/github/pulls.h b/include/gcli/github/pulls.h index cf5e5191..9ec08e01 100644 --- a/include/gcli/github/pulls.h +++ b/include/gcli/github/pulls.h @@ -61,7 +61,8 @@ int github_pull_reopen(struct gcli_ctx *ctx, char const *owner, int github_pull_close(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pr_number); -int github_perform_submit_pull(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts); +int github_perform_submit_pull(struct gcli_ctx *ctx, + struct gcli_submit_pull_options *opts); int github_get_pull_commits(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pr_number, diff --git a/include/gcli/gitlab/merge_requests.h b/include/gcli/gitlab/merge_requests.h index 889f3ac7..d89d451b 100644 --- a/include/gcli/gitlab/merge_requests.h +++ b/include/gcli/gitlab/merge_requests.h @@ -90,7 +90,7 @@ int gitlab_get_pull_commits(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id mr_number, struct gcli_commit_list *out); -int gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts); +int gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options *opts); int gitlab_mr_add_labels(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id mr_number, diff --git a/include/gcli/pulls.h b/include/gcli/pulls.h index 6867f4a0..6a159651 100644 --- a/include/gcli/pulls.h +++ b/include/gcli/pulls.h @@ -151,7 +151,7 @@ void gcli_commits_free(struct gcli_commit_list *list); int gcli_get_pull(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id pr_number, struct gcli_pull *out); -int gcli_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options); +int gcli_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options *); enum gcli_merge_flags { GCLI_PULL_MERGE_SQUASH = 0x1, /* squash commits when merging */ diff --git a/src/cmd/pulls.c b/src/cmd/pulls.c index ac554996..9353f9ae 100644 --- a/src/cmd/pulls.c +++ b/src/cmd/pulls.c @@ -381,9 +381,9 @@ gcli_pull_get_user_message(struct gcli_submit_pull_options *opts) } static int -create_pull(struct gcli_submit_pull_options opts, int always_yes) +create_pull(struct gcli_submit_pull_options *const opts, int always_yes) { - opts.body = gcli_pull_get_user_message(&opts); + opts->body = gcli_pull_get_user_message(opts); fprintf(stdout, "The following PR will be created:\n" @@ -393,8 +393,8 @@ create_pull(struct gcli_submit_pull_options opts, int always_yes) "HEAD : %s\n" "IN : %s/%s\n" "MESSAGE :\n%s\n", - opts.title, opts.to, opts.from, - opts.owner, opts.repo, opts.body ? opts.body : "No message."); + opts->title, opts->to, opts->from, + opts->owner, opts->repo, opts->body ? opts->body : "No message."); fputc('\n', stdout); @@ -529,7 +529,7 @@ subcommand_pull_create(int argc, char *argv[]) opts.title = argv[0]; - if (create_pull(opts, always_yes) < 0) + if (create_pull(&opts, always_yes) < 0) errx(1, "gcli: error: failed to submit pull request: %s", gcli_get_error(g_clictx)); diff --git a/src/gitea/pulls.c b/src/gitea/pulls.c index 94bd00e2..7489c586 100644 --- a/src/gitea/pulls.c +++ b/src/gitea/pulls.c @@ -112,7 +112,7 @@ gitea_get_pull_commits(struct gcli_ctx *ctx, char const *owner, } int -gitea_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts) +gitea_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options *opts) { warnx("In case the following process errors out, see: " "https://github.com/go-gitea/gitea/issues/20175"); diff --git a/src/github/pulls.c b/src/github/pulls.c index 16b3476f..c271cb47 100644 --- a/src/github/pulls.c +++ b/src/github/pulls.c @@ -405,7 +405,8 @@ github_pull_set_automerge(struct gcli_ctx *const ctx, char const *const node_id) } int -github_perform_submit_pull(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts) +github_perform_submit_pull(struct gcli_ctx *ctx, + struct gcli_submit_pull_options *opts) { char *url = NULL, *payload = NULL, *e_owner = NULL, *e_repo = NULL; struct gcli_fetch_buffer fetch_buffer = {0}; @@ -416,26 +417,26 @@ github_perform_submit_pull(struct gcli_ctx *ctx, struct gcli_submit_pull_options gcli_jsongen_begin_object(&gen); { gcli_jsongen_objmember(&gen, "head"); - gcli_jsongen_string(&gen, opts.from); + gcli_jsongen_string(&gen, opts->from); gcli_jsongen_objmember(&gen, "base"); - gcli_jsongen_string(&gen, opts.to); + gcli_jsongen_string(&gen, opts->to); gcli_jsongen_objmember(&gen, "title"); - gcli_jsongen_string(&gen, opts.title); + gcli_jsongen_string(&gen, opts->title); /* Body is optional and will be NULL if unset */ - if (opts.body) { + if (opts->body) { gcli_jsongen_objmember(&gen, "body"); - gcli_jsongen_string(&gen, opts.body); + gcli_jsongen_string(&gen, opts->body); } } gcli_jsongen_end_object(&gen); payload = gcli_jsongen_to_string(&gen); gcli_jsongen_free(&gen); - e_owner = gcli_urlencode(opts.owner); - e_repo = gcli_urlencode(opts.repo); + e_owner = gcli_urlencode(opts->owner); + e_repo = gcli_urlencode(opts->repo); url = sn_asprintf("%s/repos/%s/%s/pulls", gcli_get_apibase(ctx), e_owner, e_repo); @@ -447,20 +448,20 @@ github_perform_submit_pull(struct gcli_ctx *ctx, struct gcli_submit_pull_options /* Add labels if requested. GitHub doesn't allow us to do this all * with one request. */ - if (rc == 0 && (opts.labels_size || opts.automerge)) { + if (rc == 0 && (opts->labels_size || opts->automerge)) { struct json_stream json = {0}; struct gcli_pull pull = {0}; json_open_buffer(&json, fetch_buffer.data, fetch_buffer.length); parse_github_pull(ctx, &json, &pull); - if (opts.labels_size) { - rc = github_issue_add_labels(ctx, opts.owner, opts.repo, pull.id, - (char const *const *)opts.labels, - opts.labels_size); + if (opts->labels_size) { + rc = github_issue_add_labels(ctx, opts->owner, opts->repo, pull.id, + (char const *const *)opts->labels, + opts->labels_size); } - if (rc == 0 && opts.automerge) { + if (rc == 0 && opts->automerge) { /* pull.id is the global pull request ID */ rc = github_pull_set_automerge(ctx, pull.node_id); } diff --git a/src/gitlab/merge_requests.c b/src/gitlab/merge_requests.c index 4b0cb245..e40feb88 100644 --- a/src/gitlab/merge_requests.c +++ b/src/gitlab/merge_requests.c @@ -515,7 +515,7 @@ gitlab_mr_wait_until_mergeable(struct gcli_ctx *ctx, char const *const e_owner, } int -gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts) +gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options *opts) { /* Note: this doesn't really allow merging into repos with * different names. We need to figure out a way to make this @@ -528,8 +528,8 @@ gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options o struct gcli_jsongen gen = {0}; struct gcli_repo target = {0}; - target_branch = opts.to; - source_owner = strdup(opts.from); + target_branch = opts->to; + source_owner = strdup(opts->from); source_branch = strchr(source_owner, ':'); if (source_branch == NULL) return gcli_error(ctx, "bad merge request source: expected 'owner:branch'"); @@ -537,7 +537,7 @@ gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options o *source_branch++ = '\0'; /* Figure out the project id */ - rc = gitlab_get_repo(ctx, opts.owner, opts.repo, &target); + rc = gitlab_get_repo(ctx, opts->owner, opts->repo, &target); if (rc < 0) return rc; @@ -552,23 +552,23 @@ gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options o gcli_jsongen_string(&gen, target_branch); gcli_jsongen_objmember(&gen, "title"); - gcli_jsongen_string(&gen, opts.title); + gcli_jsongen_string(&gen, opts->title); /* description is optional and will be NULL if unset */ - if (opts.body) { + if (opts->body) { gcli_jsongen_objmember(&gen, "description"); - gcli_jsongen_string(&gen, opts.body); + gcli_jsongen_string(&gen, opts->body); } gcli_jsongen_objmember(&gen, "target_project_id"); gcli_jsongen_number(&gen, target.id); - if (opts.labels_size) { + if (opts->labels_size) { gcli_jsongen_objmember(&gen, "labels"); gcli_jsongen_begin_array(&gen); - for (size_t i = 0; i < opts.labels_size; ++i) - gcli_jsongen_string(&gen, opts.labels[i]); + for (size_t i = 0; i < opts->labels_size; ++i) + gcli_jsongen_string(&gen, opts->labels[i]); gcli_jsongen_end_array(&gen); } } @@ -579,7 +579,7 @@ gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options o /* generate url */ e_owner = gcli_urlencode(source_owner); - e_repo = gcli_urlencode(opts.repo); + e_repo = gcli_urlencode(opts->repo); url = sn_asprintf("%s/projects/%s%%2F%s/merge_requests", gcli_get_apibase(ctx), e_owner, e_repo); @@ -589,7 +589,7 @@ gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options o /* if that succeeded and the user wants automerge, parse the result and * set the automerge flag */ - if (rc == 0 && opts.automerge) { + if (rc == 0 && opts->automerge) { struct json_stream stream = {0}; struct gcli_pull pull = {0}; @@ -604,7 +604,7 @@ gitlab_perform_submit_mr(struct gcli_ctx *ctx, struct gcli_submit_pull_options o if (rc < 0) goto out; - rc = gitlab_mr_set_automerge(ctx, opts.owner, opts.repo, pull.number); + rc = gitlab_mr_set_automerge(ctx, opts->owner, opts->repo, pull.number); out: gcli_pull_free(&pull); diff --git a/src/pulls.c b/src/pulls.c index 12a8b060..867f86b8 100644 --- a/src/pulls.c +++ b/src/pulls.c @@ -143,9 +143,9 @@ gcli_pull_checks_free(struct gcli_pull_checks_list *list) } int -gcli_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options opts) +gcli_pull_submit(struct gcli_ctx *ctx, struct gcli_submit_pull_options *opts) { - if (opts.automerge) { + if (opts->automerge) { int const q = gcli_forge(ctx)->pull_summary_quirks; if (q & GCLI_PRS_QUIRK_AUTOMERGE) return gcli_error(ctx, "forge does not support auto-merge"); From ade7f840560e99f5e10577db6db4ecd5dca63efa Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 4 Mar 2024 22:54:32 +0100 Subject: [PATCH 18/32] Avoid pass-by-value of struct when creating issues This details struct contained lots of pointers which have previously caused a lot of call overhead. Instead pass a pointer. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/bugzilla/bugs.h | 3 ++- include/gcli/forges.h | 2 +- include/gcli/gitea/issues.h | 3 ++- include/gcli/github/issues.h | 2 +- include/gcli/gitlab/issues.h | 2 +- include/gcli/issues.h | 2 +- src/bugzilla/bugs.c | 9 +++++---- src/cmd/issues.c | 14 +++++++------- src/gitea/issues.c | 4 ++-- src/github/issues.c | 13 +++++++------ src/gitlab/issues.c | 13 +++++++------ src/issues.c | 2 +- 12 files changed, 37 insertions(+), 32 deletions(-) diff --git a/include/gcli/bugzilla/bugs.h b/include/gcli/bugzilla/bugs.h index c11440ec..ede1bd91 100644 --- a/include/gcli/bugzilla/bugs.h +++ b/include/gcli/bugzilla/bugs.h @@ -56,7 +56,8 @@ int bugzilla_bug_get_attachments(struct gcli_ctx *ctx, char const *const product gcli_id const bug_id, struct gcli_attachment_list *const out); -int bugzilla_bug_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, +int bugzilla_bug_submit(struct gcli_ctx *ctx, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out); #endif /* GCLI_BUGZILLA_BUGS_H */ diff --git a/include/gcli/forges.h b/include/gcli/forges.h index b1dc4f69..ca50b26a 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -165,7 +165,7 @@ struct gcli_forge_descriptor { * Submit an issue */ int (*perform_submit_issue)( struct gcli_ctx *ctx, - struct gcli_submit_issue_options opts, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out); /** diff --git a/include/gcli/gitea/issues.h b/include/gcli/gitea/issues.h index 12742c49..a6a7aea7 100644 --- a/include/gcli/gitea/issues.h +++ b/include/gcli/gitea/issues.h @@ -44,7 +44,8 @@ int gitea_get_issue_summary(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue_number, struct gcli_issue *out); -int gitea_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, +int gitea_submit_issue(struct gcli_ctx *ctx, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out); int gitea_issue_close(struct gcli_ctx *ctx, char const *owner, char const *repo, diff --git a/include/gcli/github/issues.h b/include/gcli/github/issues.h index 54e5e456..d55ec6f0 100644 --- a/include/gcli/github/issues.h +++ b/include/gcli/github/issues.h @@ -55,7 +55,7 @@ int github_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue_number); int github_perform_submit_issue(struct gcli_ctx *ctx, - struct gcli_submit_issue_options opts, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out); int github_issue_assign(struct gcli_ctx *ctx, char const *owner, diff --git a/include/gcli/gitlab/issues.h b/include/gcli/gitlab/issues.h index bdb1dade..e9349ceb 100644 --- a/include/gcli/gitlab/issues.h +++ b/include/gcli/gitlab/issues.h @@ -59,7 +59,7 @@ int gitlab_issue_assign(struct gcli_ctx *ctx, char const *owner, char const *assignee); int gitlab_perform_submit_issue(struct gcli_ctx *ctx, - struct gcli_submit_issue_options opts, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out); int gitlab_issue_add_labels(struct gcli_ctx *ctx, char const *owner, diff --git a/include/gcli/issues.h b/include/gcli/issues.h index b223279e..baa38ec3 100644 --- a/include/gcli/issues.h +++ b/include/gcli/issues.h @@ -103,7 +103,7 @@ int gcli_issue_close(struct gcli_ctx *ctx, char const *owner, char const *repo, int gcli_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue_number); -int gcli_issue_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options); +int gcli_issue_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options *); int gcli_issue_assign(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue_number, char const *assignee); diff --git a/src/bugzilla/bugs.c b/src/bugzilla/bugs.c index 3820727a..2c877c97 100644 --- a/src/bugzilla/bugs.c +++ b/src/bugzilla/bugs.c @@ -284,13 +284,14 @@ add_extra_options(struct gcli_nvlist const *list, struct gcli_jsongen *gen) } int -bugzilla_bug_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, +bugzilla_bug_submit(struct gcli_ctx *ctx, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out) { char *payload = NULL, *url = NULL; char *token; /* bugzilla wants the api token as a parameter in the url or the json payload */ - char const *product = opts.owner, *component = opts.repo, - *summary = opts.title, *description = opts.body; + char const *product = opts->owner, *component = opts->repo, + *summary = opts->title, *description = opts->body; struct gcli_jsongen gen = {0}; int rc = 0; @@ -338,7 +339,7 @@ bugzilla_bug_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, gcli_jsongen_objmember(&gen, "api_key"); gcli_jsongen_string(&gen, token); - add_extra_options(&opts.extra, &gen); + add_extra_options(&opts->extra, &gen); } gcli_jsongen_end_object(&gen); diff --git a/src/cmd/issues.c b/src/cmd/issues.c index 83203bfc..509d1248 100644 --- a/src/cmd/issues.c +++ b/src/cmd/issues.c @@ -230,11 +230,11 @@ gcli_issue_get_user_message(struct gcli_submit_issue_options *opts) } static int -create_issue(struct gcli_submit_issue_options opts, int always_yes) +create_issue(struct gcli_submit_issue_options *opts, int always_yes) { int rc; - opts.body = gcli_issue_get_user_message(&opts); + opts->body = gcli_issue_get_user_message(opts); printf("The following issue will be created:\n" "\n" @@ -242,8 +242,8 @@ create_issue(struct gcli_submit_issue_options opts, int always_yes) "OWNER : %s\n" "REPO : %s\n" "MESSAGE :\n%s\n", - opts.title, opts.owner, opts.repo, - opts.body ? opts.body : "No message"); + opts->title, opts->owner, opts->repo, + opts->body ? opts->body : "No message"); putchar('\n'); @@ -254,8 +254,8 @@ create_issue(struct gcli_submit_issue_options opts, int always_yes) rc = gcli_issue_submit(g_clictx, opts); - free(opts.body); - free(opts.body); + free(opts->body); + free(opts->body); return rc; } @@ -345,7 +345,7 @@ subcommand_issue_create(int argc, char *argv[]) opts.title = argv[0]; - if (create_issue(opts, always_yes) < 0) + if (create_issue(&opts, always_yes) < 0) errx(1, "gcli: error: failed to submit issue: %s", gcli_get_error(g_clictx)); diff --git a/src/gitea/issues.c b/src/gitea/issues.c index ac6fe5ce..4cabf047 100644 --- a/src/gitea/issues.c +++ b/src/gitea/issues.c @@ -110,10 +110,10 @@ gitea_get_issue_summary(struct gcli_ctx *ctx, char const *owner, } int -gitea_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, +gitea_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *const out) { - return github_perform_submit_issue(ctx,opts, out); + return github_perform_submit_issue(ctx, opts, out); } /* Gitea has closed, Github has close ... go figure */ diff --git a/src/github/issues.c b/src/github/issues.c index f1185aa4..1992e052 100644 --- a/src/github/issues.c +++ b/src/github/issues.c @@ -322,7 +322,8 @@ github_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, } int -github_perform_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, +github_perform_submit_issue(struct gcli_ctx *ctx, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *out) { char *e_owner = NULL, *e_repo = NULL, *payload = NULL, *url = NULL; @@ -334,12 +335,12 @@ github_perform_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_optio gcli_jsongen_begin_object(&gen); { gcli_jsongen_objmember(&gen, "title"); - gcli_jsongen_string(&gen, opts.title); + gcli_jsongen_string(&gen, opts->title); /* Body can be omitted and is NULL in that case */ - if (opts.body) { + if (opts->body) { gcli_jsongen_objmember(&gen, "body"); - gcli_jsongen_string(&gen, opts.body); + gcli_jsongen_string(&gen, opts->body); } } gcli_jsongen_begin_object(&gen); @@ -348,8 +349,8 @@ github_perform_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_optio gcli_jsongen_free(&gen); /* Generate URL */ - e_owner = gcli_urlencode(opts.owner); - e_repo = gcli_urlencode(opts.repo); + e_owner = gcli_urlencode(opts->owner); + e_repo = gcli_urlencode(opts->repo); url = sn_asprintf("%s/repos/%s/%s/issues", gcli_get_apibase(ctx), e_owner, e_repo); diff --git a/src/gitlab/issues.c b/src/gitlab/issues.c index fe6ea433..15ebb22c 100644 --- a/src/gitlab/issues.c +++ b/src/gitlab/issues.c @@ -207,27 +207,28 @@ gitlab_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, } int -gitlab_perform_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts, +gitlab_perform_submit_issue(struct gcli_ctx *ctx, + struct gcli_submit_issue_options *opts, struct gcli_fetch_buffer *const out) { char *e_owner = NULL, *e_repo = NULL, *url = NULL, *payload = 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); gcli_jsongen_init(&gen); gcli_jsongen_begin_object(&gen); { gcli_jsongen_objmember(&gen, "title"); - gcli_jsongen_string(&gen, opts.title); + gcli_jsongen_string(&gen, opts->title); /* The body may be NULL if empty. In this case we can omit the * body / description as it is not required by the API */ - if (opts.body) { + if (opts->body) { gcli_jsongen_objmember(&gen, "description"); - gcli_jsongen_string(&gen, opts.body); + gcli_jsongen_string(&gen, opts->body); } } gcli_jsongen_end_object(&gen); diff --git a/src/issues.c b/src/issues.c index d367fb00..b10bf3d0 100644 --- a/src/issues.c +++ b/src/issues.c @@ -103,7 +103,7 @@ gcli_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, } int -gcli_issue_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options opts) +gcli_issue_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts) { gcli_null_check_call(perform_submit_issue, ctx, opts, NULL); } From 418f8194bb49b599d1686d2498ad8d206499141b Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 4 Mar 2024 22:54:33 +0100 Subject: [PATCH 19/32] Remove return of fetch_buffer from issues api This exposed the internals of the forge handlers and leak internal APIs. This code is probably very old and rotted over time. Remove the return of the fetch buffer and instead return the created issue optionally. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/bugzilla/bugs.h | 2 +- include/gcli/forges.h | 2 +- include/gcli/gitea/issues.h | 2 +- include/gcli/github/issues.h | 2 +- include/gcli/gitlab/issues.h | 2 +- src/bugzilla/bugs.c | 25 +++++++++++++++++++++---- src/gitea/issues.c | 5 +++-- src/github/issues.c | 21 +++++++++++++++++---- src/gitlab/issues.c | 22 +++++++++++++++++----- templates/bugzilla/bugs.t | 3 +++ 10 files changed, 66 insertions(+), 20 deletions(-) diff --git a/include/gcli/bugzilla/bugs.h b/include/gcli/bugzilla/bugs.h index ede1bd91..879e3660 100644 --- a/include/gcli/bugzilla/bugs.h +++ b/include/gcli/bugzilla/bugs.h @@ -58,6 +58,6 @@ int bugzilla_bug_get_attachments(struct gcli_ctx *ctx, char const *const product int bugzilla_bug_submit(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out); + struct gcli_issue *out); #endif /* GCLI_BUGZILLA_BUGS_H */ diff --git a/include/gcli/forges.h b/include/gcli/forges.h index ca50b26a..db19a5b2 100644 --- a/include/gcli/forges.h +++ b/include/gcli/forges.h @@ -166,7 +166,7 @@ struct gcli_forge_descriptor { int (*perform_submit_issue)( struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out); + struct gcli_issue *out); /** * Change the title of an issue */ diff --git a/include/gcli/gitea/issues.h b/include/gcli/gitea/issues.h index a6a7aea7..58a59fa0 100644 --- a/include/gcli/gitea/issues.h +++ b/include/gcli/gitea/issues.h @@ -46,7 +46,7 @@ int gitea_get_issue_summary(struct gcli_ctx *ctx, char const *owner, int gitea_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out); + struct gcli_issue *out); int gitea_issue_close(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue_number); diff --git a/include/gcli/github/issues.h b/include/gcli/github/issues.h index d55ec6f0..c97da2f0 100644 --- a/include/gcli/github/issues.h +++ b/include/gcli/github/issues.h @@ -56,7 +56,7 @@ int github_issue_reopen(struct gcli_ctx *ctx, char const *owner, int github_perform_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out); + struct gcli_issue *out); int github_issue_assign(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue_number, diff --git a/include/gcli/gitlab/issues.h b/include/gcli/gitlab/issues.h index e9349ceb..b9f7b42c 100644 --- a/include/gcli/gitlab/issues.h +++ b/include/gcli/gitlab/issues.h @@ -60,7 +60,7 @@ int gitlab_issue_assign(struct gcli_ctx *ctx, char const *owner, int gitlab_perform_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out); + struct gcli_issue *out); int gitlab_issue_add_labels(struct gcli_ctx *ctx, char const *owner, char const *repo, gcli_id issue, diff --git a/src/bugzilla/bugs.c b/src/bugzilla/bugs.c index 2c877c97..dc28f279 100644 --- a/src/bugzilla/bugs.c +++ b/src/bugzilla/bugs.c @@ -284,15 +284,16 @@ add_extra_options(struct gcli_nvlist const *list, struct gcli_jsongen *gen) } int -bugzilla_bug_submit(struct gcli_ctx *ctx, - struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out) +bugzilla_bug_submit(struct gcli_ctx *const ctx, + struct gcli_submit_issue_options *const opts, + struct gcli_issue *const out) { char *payload = NULL, *url = NULL; char *token; /* bugzilla wants the api token as a parameter in the url or the json payload */ char const *product = opts->owner, *component = opts->repo, *summary = opts->title, *description = opts->body; struct gcli_jsongen gen = {0}; + struct gcli_fetch_buffer buffer = {0}, *_buffer = NULL; int rc = 0; /* prepare data for payload generation */ @@ -348,8 +349,24 @@ bugzilla_bug_submit(struct gcli_ctx *ctx, /* generate url and perform request */ url = sn_asprintf("%s/rest/bug", gcli_get_apibase(ctx)); - rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, out); + if (out) + _buffer = &buffer; + + rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, _buffer); + if (out && rc == 0) { + struct json_stream stream = {0}; + gcli_id id = 0; + + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_bugzilla_bug_creation_result(ctx, &stream, &id); + json_close(&stream); + + if (rc == 0) + rc = bugzilla_get_bug(ctx, NULL, NULL, id, out); + } + + free(buffer.data); free(url); free(payload); diff --git a/src/gitea/issues.c b/src/gitea/issues.c index 4cabf047..c27318d3 100644 --- a/src/gitea/issues.c +++ b/src/gitea/issues.c @@ -110,8 +110,9 @@ gitea_get_issue_summary(struct gcli_ctx *ctx, char const *owner, } int -gitea_submit_issue(struct gcli_ctx *ctx, struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *const out) +gitea_submit_issue(struct gcli_ctx *const ctx, + struct gcli_submit_issue_options *const opts, + struct gcli_issue *const out) { return github_perform_submit_issue(ctx, opts, out); } diff --git a/src/github/issues.c b/src/github/issues.c index 1992e052..ea0d8f5d 100644 --- a/src/github/issues.c +++ b/src/github/issues.c @@ -322,12 +322,13 @@ github_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, } int -github_perform_submit_issue(struct gcli_ctx *ctx, - struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *out) +github_perform_submit_issue(struct gcli_ctx *const ctx, + struct gcli_submit_issue_options *const opts, + struct gcli_issue *const out) { char *e_owner = NULL, *e_repo = NULL, *payload = NULL, *url = NULL; struct gcli_jsongen gen = {0}; + struct gcli_fetch_buffer buffer = {0}, *_buffer = NULL; int rc = 0; /* Generate Payload */ @@ -358,8 +359,20 @@ github_perform_submit_issue(struct gcli_ctx *ctx, free(e_owner); free(e_repo); - rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, out); + /* only read the resulting data if the issue data has been requested */ + if (out) + _buffer = &buffer; + rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, _buffer); + if (out && rc == 0) { + struct json_stream stream = {0}; + + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_github_issue(ctx, &stream, out); + json_close(&stream); + } + + free(buffer.data); free(payload); free(url); diff --git a/src/gitlab/issues.c b/src/gitlab/issues.c index 15ebb22c..1e122e98 100644 --- a/src/gitlab/issues.c +++ b/src/gitlab/issues.c @@ -207,13 +207,14 @@ gitlab_issue_reopen(struct gcli_ctx *ctx, char const *owner, char const *repo, } int -gitlab_perform_submit_issue(struct gcli_ctx *ctx, - struct gcli_submit_issue_options *opts, - struct gcli_fetch_buffer *const out) +gitlab_perform_submit_issue(struct gcli_ctx *const ctx, + struct gcli_submit_issue_options *const opts, + struct gcli_issue *const out) { char *e_owner = NULL, *e_repo = NULL, *url = NULL, *payload = NULL; - struct gcli_jsongen gen = {0}; int rc = 0; + struct gcli_fetch_buffer buffer = {0}, *_buffer = NULL; + struct gcli_jsongen gen = {0}; e_owner = gcli_urlencode(opts->owner); e_repo = gcli_urlencode(opts->repo); @@ -242,8 +243,19 @@ gitlab_perform_submit_issue(struct gcli_ctx *ctx, free(e_owner); free(e_repo); - rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, out); + if (out) + _buffer = &buffer; + + rc = gcli_fetch_with_method(ctx, "POST", url, payload, NULL, _buffer); + if (rc == 0 && out) { + struct json_stream stream = {0}; + json_open_buffer(&stream, buffer.data, buffer.length); + rc = parse_gitlab_issue(ctx, &stream, out); + json_close(&stream); + } + + free(buffer.data); free(payload); free(url); diff --git a/templates/bugzilla/bugs.t b/templates/bugzilla/bugs.t index 02790287..d10891d7 100644 --- a/templates/bugzilla/bugs.t +++ b/templates/bugzilla/bugs.t @@ -77,3 +77,6 @@ array of struct gcli_attachment use parse_bugzilla_bug_attachment; parser bugzilla_attachment_content is object of struct gcli_attachment with ("attachments" => use parse_bugzilla_attachment_content_only_first); + +parser bugzilla_bug_creation_result is +object of gcli_id select "id" as id; From 7be184966464d7a2133eb2f4e37bd3fb48b5bef3 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Fri, 15 Mar 2024 18:03:27 +0100 Subject: [PATCH 20/32] gitlab comments: Fix comments submission error I got the following error while trying to comment on an issue on Gitlab: gcli: error: failed to submit comment: request to https://gitlab.com/api/v4/project/herrhotzenplotz%2Fgcli/issues/224/notes failed with code 404: API error: no error message: failed to parse error response. Please run the gcli query with verbose mode again. This error is because the route is `projects` not `project`. Fix by correcting the typo. Signed-off-by: Nico Sonack --- Changelog.md | 2 ++ src/gitlab/comments.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index ddc63e6d..b540450c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,6 +28,8 @@ This changelog does not follow semantic versioning. - Fixed a segmentation fault when listing forks +- Fixed error when submitting a comment on Gitlab issues + ### Changed ### Removed diff --git a/src/gitlab/comments.c b/src/gitlab/comments.c index 009b684b..add5c91d 100644 --- a/src/gitlab/comments.c +++ b/src/gitlab/comments.c @@ -66,7 +66,7 @@ gitlab_perform_submit_comment(struct gcli_ctx *ctx, struct gcli_submit_comment_o payload = gcli_jsongen_to_string(&gen); gcli_jsongen_free(&gen); - url = sn_asprintf("%s/project/%s%%2F%s/%s/%"PRIid"/notes", + url = sn_asprintf("%s/projects/%s%%2F%s/%s/%"PRIid"/notes", gcli_get_apibase(ctx), e_owner, e_repo, type, opts.target_id); From 8eb6fa3c7007658c62f54062fbfa80d034f759f6 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:13 +0100 Subject: [PATCH 21/32] Make repository detection and inference code return error codes instead of calling exit This is in prepration for the interactive PR/issue creation feature. In that case we want to print suggestions instead of exiting in case we failed to derive a useful remote. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- include/gcli/cmd/cmdconfig.h | 2 +- include/gcli/cmd/gitconfig.h | 6 +++--- src/cmd/cmd.c | 7 +++++-- src/cmd/cmdconfig.c | 24 ++++++++++++++---------- src/cmd/gitconfig.c | 26 ++++++++++++++++---------- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/include/gcli/cmd/cmdconfig.h b/include/gcli/cmd/cmdconfig.h index 26aebee2..b60fbe72 100644 --- a/include/gcli/cmd/cmdconfig.h +++ b/include/gcli/cmd/cmdconfig.h @@ -64,7 +64,7 @@ sn_sv gcli_config_get_base(struct gcli_ctx *ctx); gcli_forge_type gcli_config_get_forge_type(struct gcli_ctx *ctx); sn_sv gcli_config_get_override_default_account(struct gcli_ctx *ctx); bool gcli_config_pr_inhibit_delete_source_branch(struct gcli_ctx *ctx); -void gcli_config_get_repo(struct gcli_ctx *ctx, char const **, char const **); +int gcli_config_get_repo(struct gcli_ctx *ctx, char const **, char const **); int gcli_config_have_colours(struct gcli_ctx *ctx); struct gcli_config_entries const *gcli_config_get_section_entries( struct gcli_ctx *ctx, char const *section_name); diff --git a/include/gcli/cmd/gitconfig.h b/include/gcli/cmd/gitconfig.h index efe13055..0a4edeb6 100644 --- a/include/gcli/cmd/gitconfig.h +++ b/include/gcli/cmd/gitconfig.h @@ -50,8 +50,8 @@ void gcli_gitconfig_add_fork_remote(char const *org, char const *repo); int gcli_gitconfig_get_forgetype(struct gcli_ctx *ctx, char const *remote_name); -int gcli_gitconfig_repo_by_remote(char const *remote_name, - char const **owner, - char const **repo); +int gcli_gitconfig_repo_by_remote(struct gcli_ctx *ctx, char const *const remote_name, + char const **const owner, char const **const repo, + int *const forge); #endif /* GCLI_CMD_GITCONFIG_H */ diff --git a/src/cmd/cmd.c b/src/cmd/cmd.c index 56e7ed90..ed2e723c 100644 --- a/src/cmd/cmd.c +++ b/src/cmd/cmd.c @@ -78,8 +78,11 @@ check_owner_and_repo(const char **owner, const char **repo) if ((*owner == NULL) != (*repo == NULL)) errx(1, "gcli: error: missing either explicit owner or repo"); - if (*owner == NULL) - gcli_config_get_repo(g_clictx, owner, repo); + if (*owner == NULL) { + int rc = gcli_config_get_repo(g_clictx, owner, repo); + if (rc < 0) + errx(1, "gcli: error: %s", gcli_get_error(g_clictx)); + } } /* Parses (and updates) the given argument list into two seperate lists: diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c index 5b5b7c3b..074ca03b 100644 --- a/src/cmd/cmdconfig.c +++ b/src/cmd/cmdconfig.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -896,7 +897,7 @@ gcli_config_get_forge_type(struct gcli_ctx *ctx) return result; } -void +int gcli_config_get_repo(struct gcli_ctx *ctx, char const **const owner, char const **const repo) { @@ -906,30 +907,33 @@ gcli_config_get_repo(struct gcli_ctx *ctx, char const **const owner, cfg = ensure_config(ctx); if (cfg->override_remote) { - int const forge = gcli_gitconfig_repo_by_remote( - cfg->override_remote, owner, repo); + int forge = 0, rc = 0; + + rc = gcli_gitconfig_repo_by_remote(ctx, cfg->override_remote, owner, + repo, &forge); + + if (rc < 0) + return rc; if (forge >= 0) { if ((int)(gcli_config_get_forge_type(ctx)) != forge) - errx(1, "gcli: error: forge types are inconsistent"); + return gcli_error(ctx, "forge types are inconsistent"); } - return; + return 0; } if ((upstream = gcli_config_get_upstream(ctx)).length != 0) { sn_sv const owner_sv = sn_sv_chop_until(&upstream, '/'); - sn_sv const repo_sv = sn_sv_from_parts( - upstream.data + 1, - upstream.length - 1); + sn_sv const repo_sv = sn_sv_from_parts(upstream.data + 1, upstream.length - 1); *owner = sn_sv_to_cstr(owner_sv); *repo = sn_sv_to_cstr(repo_sv); - return; + return 0; } - gcli_gitconfig_repo_by_remote(NULL, owner, repo); + return gcli_gitconfig_repo_by_remote(ctx, NULL, owner, repo, NULL); } int diff --git a/src/cmd/gitconfig.c b/src/cmd/gitconfig.c index f851f0f4..e8cd0161 100644 --- a/src/cmd/gitconfig.c +++ b/src/cmd/gitconfig.c @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -507,29 +508,34 @@ gcli_gitconfig_get_forgetype(struct gcli_ctx *ctx, char const *const remote_name } int -gcli_gitconfig_repo_by_remote( - char const *const remote_name, - char const **const owner, - char const **const repo) +gcli_gitconfig_repo_by_remote(struct gcli_ctx *ctx, char const *const remote, + char const **const owner, char const **const repo, + int *const forge) { gcli_gitconfig_read_gitconfig(); - if (remote_name) { + if (remote) { for (size_t i = 0; i < remotes_size; ++i) { - if (sn_sv_eq_to(remotes[i].name, remote_name)) { + if (sn_sv_eq_to(remotes[i].name, remote)) { *owner = sn_sv_to_cstr(remotes[i].owner); *repo = sn_sv_to_cstr(remotes[i].repo); - return remotes[i].forge_type; + if (forge) + *forge = remotes[i].forge_type; + + return 0; } } - errx(1, "gcli: error: no such remote: %s", remote_name); + return gcli_error(ctx, "no such remote: %s", remote); } if (!remotes_size) - errx(1, "gcli: error: no remotes to auto-detect forge"); + return gcli_error(ctx, "no remotes to auto-detect forge"); *owner = sn_sv_to_cstr(remotes[0].owner); *repo = sn_sv_to_cstr(remotes[0].repo); - return remotes[0].forge_type; + if (forge) + *forge = remotes[0].forge_type; + + return 0; } From a5c68d3b072370d3fc5f54120830ab476a2f5825 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:14 +0100 Subject: [PATCH 22/32] Add a function for interactive prompting This will be used extensively to query input from the user. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.am | 1 + include/gcli/cmd/interactive.h | 39 ++++++++++++++++++ src/cmd/interactive.c | 73 ++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 include/gcli/cmd/interactive.h create mode 100644 src/cmd/interactive.c diff --git a/Makefile.am b/Makefile.am index 2bc9af54..327bcc53 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,6 +83,7 @@ gcli_SOURCES = \ include/gcli/cmd/snippets.h src/cmd/snippets.c \ include/gcli/cmd/status.h src/cmd/status.c \ include/gcli/cmd/table.h src/cmd/table.c \ + include/gcli/cmd/interactive.h src/cmd/interactive.c \ src/cmd/api.c \ src/cmd/gcli.c diff --git a/include/gcli/cmd/interactive.h b/include/gcli/cmd/interactive.h new file mode 100644 index 00000000..99b3d32b --- /dev/null +++ b/include/gcli/cmd/interactive.h @@ -0,0 +1,39 @@ +/* + * Copyright 2024 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. + */ + +#ifndef GCLI_CMD_INTERACTIVE_H +#define GCLI_CMD_INTERACTIVE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +char *gcli_cmd_prompt(char const *const fmt, char const *const deflt, ...); + +#endif /* GCLI_CMD_INTERACTIVE_H */ diff --git a/src/cmd/interactive.c b/src/cmd/interactive.c new file mode 100644 index 00000000..98a399da --- /dev/null +++ b/src/cmd/interactive.c @@ -0,0 +1,73 @@ +/* + * Copyright 2024 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 + +/** Prompt for input with an optional default + * + * This function prompts for user input. The prompt can be specified using a + * format string. An optional default value can be specified. If the default + * value is NULL the user will be repeatedly prompted until the input + * is non-empty. */ +char * +gcli_cmd_prompt(char const *const fmt, char const *const deflt, ...) +{ + char buf[256] = {0}; /* nobody types more than 256 characters, amirite? */ + va_list vp; + +ask_again: + va_start(vp, deflt); + vfprintf(stdout, fmt, vp); + va_end(vp); + + if (deflt) + fprintf(stdout, " [%s]: ", deflt); + else + fprintf(stdout, ": "); + + fflush(stdout); + + fgets(buf, sizeof buf, stdin); + + if (buf[0] == '\n') { + if (deflt) + return strdup(deflt); + + memset(buf, 0, sizeof(buf)); + goto ask_again; + } + + buf[strlen(buf)-1] = '\0'; + + return strdup(buf); +} From 2ebade6ee768332ae03b3564c15f767e5fe7f67b Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:15 +0100 Subject: [PATCH 23/32] Add an interactive version of the pull create subcommand Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- docs/gcli-pulls.1.in | 10 +++++- src/cmd/pulls.c | 76 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/docs/gcli-pulls.1.in b/docs/gcli-pulls.1.in index 591e0c77..6fea8b02 100644 --- a/docs/gcli-pulls.1.in +++ b/docs/gcli-pulls.1.in @@ -24,7 +24,7 @@ .Op Fl t Ar branch .Op Fl f Ar owner:branch .Op Fl y -.Ar "PR title..." +.Op Ar "PR title..." .Sh DESCRIPTION Use .Nm @@ -94,6 +94,9 @@ on the specified Create a new PR in the given or autodetected repository. The editor will come up and ask you to enter the PR message. .Pp +When the title is omitted gcli will interactively prompt the various +options listed below, including the title. +.Pp The following flags can be specified: .Bl -tag -width indent .It Fl o , -owner Ar owner @@ -185,6 +188,11 @@ Print a list of open PRs in the current project: $ gcli pulls .Ed .Pp +Create a new PR and let gcli interactively prompt you for details: +.Bd -literal -offset indent +$ gcli pr create +.Ed +.Pp Create a new PR in the current Project, the head is the currently checked out branch of git. See .Xr git-status 1 diff --git a/src/cmd/pulls.c b/src/cmd/pulls.c index 9353f9ae..b01eafdf 100644 --- a/src/cmd/pulls.c +++ b/src/cmd/pulls.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,7 @@ static void usage(void) { fprintf(stderr, "usage: gcli pulls create [-o owner -r repo] [-f from]\n"); - fprintf(stderr, " [-t to] [-d] [-a] [-l label] pull-request-title\n"); + fprintf(stderr, " [-t to] [-d] [-a] [-l label] [pull-request-title]\n"); fprintf(stderr, " gcli pulls [-o owner -r repo] [-a] [-A author] [-n number]\n"); fprintf(stderr, " [-L label] [-M milestone] [-s] [search-terms...]\n"); fprintf(stderr, " gcli pulls [-o owner -r repo] -i pull-id actions...\n"); @@ -429,6 +430,76 @@ pr_try_derive_head(void) return sn_asprintf("%s:"SV_FMT, account, SV_ARGS(branch)); } +static char * +derive_head(void) +{ + char const *account; + sn_sv branch = {0}; + + if ((account = gcli_config_get_account_name(g_clictx)) == NULL) + return NULL; + + branch = gcli_gitconfig_get_current_branch(); + if (branch.length == 0) + return NULL; + + return sn_asprintf("%s:"SV_FMT, account, SV_ARGS(branch)); +} + +/** Interactive version of the create subcommand */ +static int +subcommand_pull_create_interactive(struct gcli_submit_pull_options *const opts) +{ + char const *deflt_owner = NULL, *deflt_repo = NULL; + int rc = 0; + + gcli_config_get_repo(g_clictx, &deflt_owner, &deflt_repo); + + /* PR Source */ + if (!opts->from) { + char *tmp = NULL; + + tmp = derive_head(); + opts->from = gcli_cmd_prompt("From (owner:branch)", tmp); + free(tmp); + tmp = NULL; + } + + /* PR Target */ + if (!opts->owner) + opts->owner = gcli_cmd_prompt("Owner", deflt_owner); + + if (!opts->repo) + opts->repo = gcli_cmd_prompt("Repository", deflt_repo); + + if (!opts->to) { + char *tmp = NULL; + sn_sv base; + + base = gcli_config_get_base(g_clictx); + if (base.length != 0) + tmp = sn_sv_to_cstr(base); + + opts->to = gcli_cmd_prompt("To Branch", tmp); + + free(tmp); + tmp = NULL; + } + + /* Meta */ + opts->title = gcli_cmd_prompt("Title", NULL); + opts->automerge = sn_yesno("Enable automerge?"); + + /* create_pull is going to pop up the editor */ + rc = create_pull(opts, false); + if (rc < 0) { + fprintf(stderr, "gcli: error: %s\n", gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + static int subcommand_pull_create(int argc, char *argv[]) { @@ -506,6 +577,9 @@ subcommand_pull_create(int argc, char *argv[]) argc -= optind; argv += optind; + if (argc == 0) + return subcommand_pull_create_interactive(&opts); + if (!opts.from) opts.from = pr_try_derive_head(); From 1c630d07e55b8188d7a8f1695e2bf6e0b5418eea Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:16 +0100 Subject: [PATCH 24/32] Add an interactive version of the issue creation subcommand When the user doesn't give an explicit issue title gcli will now switch to interactive mode and prompt the user for details about the issue. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- docs/gcli-issues.1.in | 14 +++++++++++++- src/cmd/issues.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/gcli-issues.1.in b/docs/gcli-issues.1.in index 261d64b2..40706460 100644 --- a/docs/gcli-issues.1.in +++ b/docs/gcli-issues.1.in @@ -22,6 +22,7 @@ .Cm create .Op Fl o Ar owner Fl r Ar repo .Op Fl y +.Op Ar issue-title .Sh DESCRIPTION Use .Nm @@ -75,6 +76,9 @@ for the specified Create a new issue in the given or autodetected repository. The editor will come up and ask you to enter an issue message. .Pp +When the issue title is omitted gcli will interactively prompt you +for all the details to create an issue. +.Pp The following flags can be specified: .Bl -tag -width indent .It Fl i , -in Ar owner/repo @@ -139,7 +143,15 @@ in contour-terminal/contour on GitHub including closed issues: $ gcli -t github issues -o contour-terminal -r contour -a crash .Ed .Pp -Report a new issue in the current project: +Report a new issue in the current project; interactively asking for +details: +.Bd -literal -offset indent +$ gcli issues create +.Ed +.Pp +Report a new issue titled +.Dq summary here +in the current project: .Bd -literal -offset indent $ gcli issues create "summary here" .Ed diff --git a/src/cmd/issues.c b/src/cmd/issues.c index 509d1248..606226d5 100644 --- a/src/cmd/issues.c +++ b/src/cmd/issues.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -49,7 +50,7 @@ static void usage(void) { - fprintf(stderr, "usage: gcli issues create [-o owner -r repo] [-y] title...\n"); + fprintf(stderr, "usage: gcli issues create [-o owner -r repo] [-y] [title...]\n"); fprintf(stderr, " gcli issues [-o owner -r repo] [-a] [-n number] [-A author] [-L label]\n"); fprintf(stderr, " [-M milestone] [-s] [search query...]\n"); fprintf(stderr, " gcli issues [-o owner -r repo] -i issue actions...\n"); @@ -260,6 +261,31 @@ create_issue(struct gcli_submit_issue_options *opts, int always_yes) return rc; } +static int +subcommand_issue_create_interactive(struct gcli_submit_issue_options *const opts) +{ + char const *deflt_owner = NULL, *deflt_repo = NULL; + int rc = 0; + + gcli_config_get_repo(g_clictx, &deflt_owner, &deflt_repo); + + if (!opts->owner) + opts->owner = gcli_cmd_prompt("Owner", deflt_owner); + + if (!opts->repo) + opts->repo = gcli_cmd_prompt("Repository", deflt_repo); + + opts->title = gcli_cmd_prompt("Title", NULL); + + rc = create_issue(opts, false); + if (rc < 0) { + fprintf(stderr, "gcli: error: %s\n", gcli_get_error(g_clictx)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + static int parse_submit_issue_option(struct gcli_submit_issue_options *opts) { @@ -335,6 +361,9 @@ subcommand_issue_create(int argc, char *argv[]) argc -= optind; argv += optind; + if (argc == 0) + return subcommand_issue_create_interactive(&opts); + check_owner_and_repo(&opts.owner, &opts.repo); if (argc != 1) { From 05b42c7253a59daa5013d19de6a2ccc5d946a961 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:17 +0100 Subject: [PATCH 25/32] libedit integration for interactive editing This patch adds support for prompting using libedit which allows for interactive editing of the input. Common key bindings such as C-b, C-f, C-d, M-b, M-f, M-d etc. are available. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- configure.ac | 36 ++++++++++++++ src/cmd/interactive.c | 113 +++++++++++++++++++++++++++++++++--------- 2 files changed, 125 insertions(+), 24 deletions(-) diff --git a/configure.ac b/configure.ac index a0ff3b98..ded27652 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,9 @@ if ! test -z "${CCACHE}"; then CC="${CCACHE} ${CC}" fi +dnl #################################################################################### +dnl LIBCURL +dnl #################################################################################### dnl Go looking for libcurl OPT_LIBCURL=check AC_ARG_WITH([libcurl], @@ -63,6 +66,39 @@ AC_CHECK_HEADER([curl/curl.h],,[AC_MSG_ERROR([Cannot find libcurl headers])]) dnl FIXME find a better way for this dnl AC_CHECK_LIB([curl],[curl_easy_init],,[AC_MSG_ERROR([-lcurl doesn not contain curl_easy_init])]) +dnl #################################################################################### +dnl LIBEDIT +dnl #################################################################################### +dnl Check for libedit +OPT_LIBEDIT=check +AC_ARG_WITH([libedit], + [AS_HELP_STRING([--with-libedit], + [Use libedit at the given prefix for interactive editing])], + OPT_LIBEDIT=$withval, + OPT_LIBEDIT=no +) + +HAVE_LIBEDIT=0 +if test "x$OPT_LIBEDIT" = "xyes" || test "x$OPT_LIBEDIT" = "xcheck"; then + HAVE_LIBEDIT=1 + + PKG_CHECK_MODULES([LIBEDIT], [libedit],,[AC_MSG_ERROR([Could not find libedit])]) + CFLAGS="$LIBEDIT_CFLAGS $CFLAGS" + LDFLAGS="$LIBEDIT_LIBS $LDFLAGS" +elif ! test "x$OPT_LIBEDIT" = "xno"; then + CPPFLAGS="-I$OPT_LIBEDIT/include $CPPFLAGS" + LDFLAGS="-L$OPT_LIBEDIT/lib $LDFLAGS" +fi + +if test $HAVE_LIBEDIT -eq 1; then + AC_CHECK_HEADER([histedit.h],,[AC_MSG_ERROR([Cannot find libedit headers])]) +fi +AC_DEFINE_UNQUOTED([HAVE_LIBEDIT], [$HAVE_LIBEDIT], [Define if we link against libedit]) + +dnl #################################################################################### +dnl TEST SUITE STUFF +dnl #################################################################################### + dnl For the test suite we require libatf-c and Kyua OPT_LIBATFC=check AC_ARG_WITH([libatf-c], diff --git a/src/cmd/interactive.c b/src/cmd/interactive.c index 98a399da..12a5a535 100644 --- a/src/cmd/interactive.c +++ b/src/cmd/interactive.c @@ -30,44 +30,109 @@ #include #include +#include #include #include +#if defined(HAVE_LIBEDIT) +# if HAVE_LIBEDIT +# include +# define USE_EDITLINE 1 +# else +# define USE_EDITLINE 0 +# endif +#else +# define USE_EDITLINE 0 +#endif + +#if USE_EDITLINE +static char * +el_prompt_fn(EditLine *el_ctx) +{ + char *prompt = NULL; + if (el_get(el_ctx, EL_CLIENTDATA, &prompt) < 0) + return strdup(">"); + else + return prompt; +} +#endif + +/* Actual implementation for reading in the line */ +static char * +get_input_line(char *const prompt) +{ +#if USE_EDITLINE + static EditLine *el_ctx = NULL; + char const *txt = NULL; + char *result = NULL; + int len = 0; + + if (!el_ctx) { + el_ctx = el_init("gcli", stdin, stdout, stderr); + el_set(el_ctx, EL_PROMPT, el_prompt_fn); + el_set(el_ctx, EL_EDITOR, "emacs"); + } + + el_set(el_ctx, EL_CLIENTDATA, prompt); + + txt = el_gets(el_ctx, &len); + bool const is_error = txt == NULL || len < 0; + bool const is_empty = len == 1 && txt[0] == '\n'; + if (is_error || is_empty) + return NULL; + + result = strdup(txt); + result[len - 1] = '\0'; + + return result; + +#else + char buf[256] = {0}; /* nobody types more than 256 characters, amirite? */ + + fputs(prompt, stdout); + fflush(stdout); + fgets(buf, sizeof buf, stdin); + + if (buf[0] == '\n') + return NULL; + + buf[strlen(buf)-1] = '\0'; + return strdup(buf); +#endif +} + /** Prompt for input with an optional default * - * This function prompts for user input. The prompt can be specified using a - * format string. An optional default value can be specified. If the default - * value is NULL the user will be repeatedly prompted until the input - * is non-empty. */ + * This function prompts for user input, possibly with editline + * capabilities. The prompt can be specified using a format string. + * An optional default value can be specified. If the default value + * is NULL the user will be repeatedly prompted until the input is + * non-empty. */ char * gcli_cmd_prompt(char const *const fmt, char const *const deflt, ...) { - char buf[256] = {0}; /* nobody types more than 256 characters, amirite? */ va_list vp; + char *result; + char prompt[256] = {0}; + size_t prompt_len; -ask_again: va_start(vp, deflt); - vfprintf(stdout, fmt, vp); + vsnprintf(prompt, sizeof(prompt), fmt, vp); va_end(vp); - if (deflt) - fprintf(stdout, " [%s]: ", deflt); - else - fprintf(stdout, ": "); - - fflush(stdout); - - fgets(buf, sizeof buf, stdin); - - if (buf[0] == '\n') { - if (deflt) - return strdup(deflt); - - memset(buf, 0, sizeof(buf)); - goto ask_again; + prompt_len = strlen(prompt); + if (deflt) { + snprintf(prompt + prompt_len, sizeof(prompt) - prompt_len, " [%s]: ", deflt); + } else { + strncat(prompt, ": ", sizeof(prompt) - prompt_len - 1); } - buf[strlen(buf)-1] = '\0'; + do { + result = get_input_line(prompt); + } while (deflt == NULL && result == NULL); - return strdup(buf); + if (result == NULL) + result = strdup(deflt); + + return result; } From 8a72b618750800f090f857182692de3ae926782e Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:18 +0100 Subject: [PATCH 26/32] readline integration for interactive prompting This patch adds optional support for readline in interactive prompts. When both libedit and readline are available libedit is preferred over readline. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- configure.ac | 39 +++++++++++++++++++++++++++++++++++++++ src/cmd/interactive.c | 24 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/configure.ac b/configure.ac index ded27652..6f261211 100644 --- a/configure.ac +++ b/configure.ac @@ -86,6 +86,8 @@ if test "x$OPT_LIBEDIT" = "xyes" || test "x$OPT_LIBEDIT" = "xcheck"; then CFLAGS="$LIBEDIT_CFLAGS $CFLAGS" LDFLAGS="$LIBEDIT_LIBS $LDFLAGS" elif ! test "x$OPT_LIBEDIT" = "xno"; then + HAVE_LIBEDIT=1 + CPPFLAGS="-I$OPT_LIBEDIT/include $CPPFLAGS" LDFLAGS="-L$OPT_LIBEDIT/lib $LDFLAGS" fi @@ -95,6 +97,43 @@ if test $HAVE_LIBEDIT -eq 1; then fi AC_DEFINE_UNQUOTED([HAVE_LIBEDIT], [$HAVE_LIBEDIT], [Define if we link against libedit]) +dnl #################################################################################### +dnl READLINE (second option) +dnl #################################################################################### +if ! test $HAVE_LIBEDIT -eq 1; then + OPT_READLINE=check + AC_ARG_WITH([readline], + [AS_HELP_STRING([--with-readline], + [Use readline at the given prefix for interactive editing. + When libedit is available this option is a no-op.])], + OPT_READLINE=$withval, + OPT_READLINE=no + ) + + HAVE_READLINE=0 + if test "x$OPT_READLINE" = "xyes" || test "x$OPT_READLINE" = "xcheck"; then + HAVE_READLINE=1 + # FIXME: This is an ugly hack because the readline headers are + # not C99 clean. Clang seems to not like them and causes + # the configure script to fail. + CFLAGS="${CFLAGS} -Wno-strict-prototypes" + + PKG_CHECK_MODULES([READLINE], [readline],,[AC_MSG_ERROR([Could not find readline])]) + CFLAGS="$READLINE_CFLAGS $CFLAGS" + LDFLAGS="$READLINE_LIBS $LDFLAGS" + elif ! test "x$OPT_READLINE" = "xno"; then + HAVE_READLINE=1 + + CPPFLAGS="-I$OPT_READLINE/include $CPPFLAGS" + LDFLAGS="-L$OPT_READLINE/lib $LDFLAGS" + fi + + if test $HAVE_READLINE -eq 1; then + AC_CHECK_HEADER([readline/readline.h],,[AC_MSG_ERROR([Cannot find readline headers])]) + fi + AC_DEFINE_UNQUOTED([HAVE_READLINE], [$HAVE_READLINE], [Define if we link against readline]) +fi + dnl #################################################################################### dnl TEST SUITE STUFF dnl #################################################################################### diff --git a/src/cmd/interactive.c b/src/cmd/interactive.c index 12a5a535..e389522b 100644 --- a/src/cmd/interactive.c +++ b/src/cmd/interactive.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #if defined(HAVE_LIBEDIT) @@ -45,6 +46,17 @@ # define USE_EDITLINE 0 #endif +#if !USE_EDITLINE && defined(HAVE_READLINE) +# if HAVE_READLINE +# include +# define USE_READLINE 1 +# else +# define USE_READLINE 0 +# endif +#else +# define USE_READLINE 0 +#endif + #if USE_EDITLINE static char * el_prompt_fn(EditLine *el_ctx) @@ -86,6 +98,18 @@ get_input_line(char *const prompt) return result; +#elif USE_READLINE + char *result = readline(prompt); + + /* readline() returns an empty string if the input is empty. Our interface + * returns NULL if the input was empty */ + if (result == NULL || result[0] == '\0') { + free(result); + result = NULL; + } + + return result; + #else char buf[256] = {0}; /* nobody types more than 256 characters, amirite? */ From a5581b8faa4b1f4aee8cf304bdb7cb69f64ab1e6 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:28:19 +0100 Subject: [PATCH 27/32] CI: Add interactive library dependencies to pipeline rules This is merely for testing readline and libedit integration on the respective platforms. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3115065e..4378d813 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,9 +8,9 @@ alpine-amd64: - linux image: alpine:3.17 script: - - apk add libcurl gcc autoconf automake libtool make pkgconf musl-dev curl-dev flex bison xz gzip bzip2 libbsd-dev kyua atf-dev + - apk add libcurl gcc autoconf automake libtool make pkgconf musl-dev curl-dev flex bison xz gzip bzip2 libbsd-dev kyua atf-dev libedit-dev - ./autogen.sh - - ./configure CFLAGS='-std=c99 -pedantic -Wall -Wextra -Werror' CPPFLAGS='-D_XOPEN_SOURCE=600' --disable-silent-rules || (cat config.log && exit 42) + - ./configure CFLAGS='-std=c99 -pedantic -Wall -Wextra -Werror' CPPFLAGS='-D_XOPEN_SOURCE=600' --disable-silent-rules --with-libedit || (cat config.log && exit 42) - make -j - make -j distcheck @@ -49,8 +49,8 @@ debian-amd64: image: debian:bullseye script: - apt-get update - - apt-get install -y --no-install-recommends build-essential libcurl4-openssl-dev pkgconf autotools-dev bison flex make autoconf automake libtool libbsd-dev libatf-dev kyua + - apt-get install -y --no-install-recommends build-essential libcurl4-openssl-dev pkgconf autotools-dev bison flex make autoconf automake libtool libbsd-dev libatf-dev kyua libreadline-dev - ./autogen.sh - - ./configure CFLAGS='-std=c99 -pedantic -Wall -Wextra -Werror -Wno-misleading-indentation' CPPFLAGS='-D_XOPEN_SOURCE=600' --disable-silent-rules || (cat config.log && exit 42) + - ./configure CFLAGS='-std=c99 -pedantic -Wall -Wextra -Werror -Wno-misleading-indentation' CPPFLAGS='-D_XOPEN_SOURCE=600' --disable-silent-rules --with-readline || (cat config.log && exit 42) - make -j - make -j distcheck From cc38bab1381e944c8cb17f9ac49eed4ea9ea1154 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 12 Mar 2024 23:33:41 +0100 Subject: [PATCH 28/32] configure.ac: Improve help message The configure help message did not include optional paths for the --with-* options. Include them in the help output. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 6f261211..c778acbf 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ dnl ############################################################################ dnl Go looking for libcurl OPT_LIBCURL=check AC_ARG_WITH([libcurl], -[AS_HELP_STRING([--with-libcurl], +[AS_HELP_STRING([--with-libcurl[[=DIR]]], [Give an alternate path to libcurl.])], OPT_LIBCURL=$withval ) @@ -72,7 +72,7 @@ dnl ############################################################################ dnl Check for libedit OPT_LIBEDIT=check AC_ARG_WITH([libedit], - [AS_HELP_STRING([--with-libedit], + [AS_HELP_STRING([--with-libedit[[=DIR]]], [Use libedit at the given prefix for interactive editing])], OPT_LIBEDIT=$withval, OPT_LIBEDIT=no @@ -103,7 +103,7 @@ dnl ############################################################################ if ! test $HAVE_LIBEDIT -eq 1; then OPT_READLINE=check AC_ARG_WITH([readline], - [AS_HELP_STRING([--with-readline], + [AS_HELP_STRING([--with-readline[[=DIR]]], [Use readline at the given prefix for interactive editing. When libedit is available this option is a no-op.])], OPT_READLINE=$withval, @@ -141,7 +141,7 @@ dnl ############################################################################ dnl For the test suite we require libatf-c and Kyua OPT_LIBATFC=check AC_ARG_WITH([libatf-c], -[AS_HELP_STRING([--with-libatf-c], +[AS_HELP_STRING([--with-libatf-c[[=DIR]]], [Give an alternate path to libatf-c.])], OPT_LIBATFC=$withval ) From da145e0d9c5665776475e4f7ec8309d22365dfb2 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Sun, 17 Mar 2024 11:11:32 +0100 Subject: [PATCH 29/32] autotools: fix errorneous linkage of gcli to libatf-c When building gcli with tests the main gcli binary errorneously ended up being linked to libatf-c. Fix this by not including LIBATFC_LIBS in the main LDFLAGS. Fixes: https://gitlab.com/herrhotzenplotz/gcli/-/issues/225 Reported-by: Robert Clausecker Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.am | 45 ++++++++++++++++++++++++++++++++++++--------- configure.ac | 27 ++++++++++++++++++--------- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/Makefile.am b/Makefile.am index 327bcc53..057353bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -247,57 +247,84 @@ do_test: $(check_PROGRAMS) tests/Kyuafile tests_json_escape_SOURCES = \ tests/json-escape.c +tests_json_escape_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_json_escape_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_github_parse_tests_SOURCES = \ tests/github-parse-tests.c +tests_github_parse_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_github_parse_tests_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_gitlab_parse_tests_SOURCES = \ tests/gitlab-parse-tests.c +tests_gitlab_parse_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_gitlab_parse_tests_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_gitea_parse_tests_SOURCES = \ tests/gitea-parse-tests.c +tests_gitea_parse_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_gitea_parse_tests_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_bugzilla_parse_tests_SOURCES = \ tests/bugzilla-parse-tests.c +tests_bugzilla_parse_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_bugzilla_parse_tests_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_base64_tests_SOURCES = \ tests/base64-tests.c +tests_base64_tests_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_base64_tests_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_url_encode_SOURCES = \ tests/url-encode.c +tests_url_encode_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_url_encode_LDADD = \ libgcli.la libpdjson.la libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_pretty_print_test_SOURCES = \ tests/pretty_print_test.c +tests_pretty_print_test_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_pretty_print_test_LDADD = \ libsn.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) tests_test_jsongen_SOURCES = \ tests/test-jsongen.c +tests_test_jsongen_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBATFC_CFLAGS) tests_test_jsongen_LDADD = \ libgcli.la \ - $(LIBATFC_LDFLAGS) + $(LIBATFC_LIBS) EXTRA_DIST += tests/samples/github_simple_comment.json \ tests/samples/github_simple_fork.json \ diff --git a/configure.ac b/configure.ac index c778acbf..38b7912c 100644 --- a/configure.ac +++ b/configure.ac @@ -150,23 +150,32 @@ HAVE_ATFC=no if test "x$OPT_LIBATFC" = "xcheck" || test "x$OPT_LIBATFC" = "xyes" then PKG_CHECK_MODULES([LIBATFC], [atf-c], [HAVE_ATFC=yes],[HAVE_ATFC=no]) - CFLAGS="$LIBATFC_CFLAGS $CFLAGS" - LDFLAGS="$LIBATFC_LIBS $LDFLAGS" elif test "x$OPT_LIBATFC" = "xno" then HAVE_ATFC=no else - CPPFLAGS="-I$OPT_LIBATFC/include $CPPFLAGS" - LDFLAGS="-L$OPT_LIBATFC/lib $LDFLAGS" + LIBATFC_CFLAGS="-I$OPT_LIBATFC/include" + LIBATFC_LIBS="-L$OPT_LIBATFC/lib" HAVE_ATFC=yes fi -AS_IF([test "x$HAVE_ATFC" = "xyes"], - [AC_CHECK_HEADER([atf-c.h],,[AC_MSG_ERROR([Cannot find libatf-c headers])]) - HAVE_ATFC=yes]) +if test "x$HAVE_ATFC" = "xyes"; then + AC_SUBST([LIBATFC_CFLAGS]) + AC_SUBST([LIBATFC_LIBS]) -dnl FIXME find a better way to do this -dnl AC_CHECK_LIB([atf-c],[atf_no_error],,[AC_MSG_ERROR([-latf-c doesn not contain atf_no_error])]) + _OLD_CFLAGS="$CFLAGS" + _OLD_LDFLAGS="$LDFLAGS" + + CFLAGS="$CFLAGS $LIBATFC_CFLAGS" + LDFLAGS="$LDFLAGS $LIBATFC_LIBS" + + AC_CHECK_HEADER([atf-c.h],,[AC_MSG_ERROR([Cannot find libatf-c headers])]) + + CFLAGS="$_OLD_CFLAGS" + LDFLAGS="$_OLD_LDFLAGS" + + HAVE_ATFC=yes +fi AC_CHECK_PROG([KYUA], [kyua], [kyua]) AC_CHECK_PROGS([REALPATH], [realpath grealpath], []) From 83e4798a14955390247499604215a6e4349f40a6 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Tue, 19 Mar 2024 23:18:44 +0100 Subject: [PATCH 30/32] Update Website for now pkgsrc port and change Debian URL Change the Debian URL to trixie as this is the current testing distribution. Signed-off-by: Nico Sonack --- docs/website/index.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/website/index.html b/docs/website/index.html index 00373821..8db6ebab 100644 --- a/docs/website/index.html +++ b/docs/website/index.html @@ -75,9 +75,10 @@

General

GCLI is available in various distributions

If you want gcli to be available in your favourite operating system please submit them to the respective packaging tree.
From e3bdf56d52827d112c8fac8c43cbd49731ef68ea Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 18 Mar 2024 20:41:55 +0100 Subject: [PATCH 31/32] pgen: Remove libsn and manually compile against sn.c A linker error occurred while testing on Haiku/x86_64 where errx was undefined. Use our portability code for errx. The reason why I removed libsn is that I don't want to have libsn as a pre-make-all dependency. Also it is impossible to build sn.c with and without libtool. Signed-off-by: Nico Sonack Signed-off-by: Gavin-John Noonan --- Makefile.am | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Makefile.am b/Makefile.am index 057353bc..2c09679e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,6 +13,7 @@ AM_CFLAGS = -Wno-gnu-zero-variadic-macro-arguments noinst_PROGRAMS = pgen$(EXEEXT) pgen_SOURCES = \ include/gcli/pgen.h \ + thirdparty/sn/sn.c \ src/pgen/dump_c.c \ src/pgen/dump_h.c \ src/pgen/dump_plain.c \ @@ -20,13 +21,13 @@ pgen_SOURCES = \ src/pgen/lexer.l lib_LTLIBRARIES = libgcli.la -noinst_LTLIBRARIES = libsn.la libpdjson.la +noinst_LTLIBRARIES = libpdjson.la # For testing puproses I'll reenable parallel builds. If it breaks again, uncomment. # .NOTPARALLEL: pgen$(EXEEXT) $(builddir)/src/pgen/parser.c $(builddir)/src/pgen/parser.h $(builddir)/src/pgen/lexer.c src/pgen/lexer.c: src/pgen/parser.h -libgcli_la_DEPENDENCIES = pgen$(EXEEXT) libpdjson.la libsn.la +libgcli_la_DEPENDENCIES = pgen$(EXEEXT) libpdjson.la $(BUILT_SOURCES): pgen$(EXEEXT) @@ -40,7 +41,7 @@ SUFFIXES = .t bin_PROGRAMS = gcli$(EXEEXT) gcli_LDADD = libgcli.la -libgcli_la_LIBADD = libpdjson.la libsn.la +libgcli_la_LIBADD = libpdjson.la libgcli_la_LDFLAGS = -no-undefined dist_man_MANS = \ @@ -87,10 +88,6 @@ gcli_SOURCES = \ src/cmd/api.c \ src/cmd/gcli.c -libsn_la_SOURCES = \ - thirdparty/sn/sn.c \ - thirdparty/sn/sn.h - libpdjson_la_SOURCES = \ thirdparty/pdjson/pdjson.c \ thirdparty/pdjson/pdjson.h @@ -202,7 +199,8 @@ libgcli_la_SOURCES = \ src/bugzilla/bugs.c include/gcli/bugzilla/bugs.h \ src/bugzilla/bugs-parser.c include/gcli/bugzilla/bugs-parser.h \ src/bugzilla/config.c include/gcli/bugzilla/config.h \ - $(TEMPLATES) + $(TEMPLATES) \ + thirdparty/sn/sn.c thirdparty/sn/sn.h libgcli_la_CPPFLAGS = \ $(AM_CPPFLAGS) \ @@ -251,7 +249,7 @@ tests_json_escape_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_json_escape_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_github_parse_tests_SOURCES = \ @@ -260,7 +258,7 @@ tests_github_parse_tests_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_github_parse_tests_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_gitlab_parse_tests_SOURCES = \ @@ -269,7 +267,7 @@ tests_gitlab_parse_tests_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_gitlab_parse_tests_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_gitea_parse_tests_SOURCES = \ @@ -278,7 +276,7 @@ tests_gitea_parse_tests_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_gitea_parse_tests_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_bugzilla_parse_tests_SOURCES = \ @@ -287,7 +285,7 @@ tests_bugzilla_parse_tests_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_bugzilla_parse_tests_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_base64_tests_SOURCES = \ @@ -296,7 +294,7 @@ tests_base64_tests_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_base64_tests_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_url_encode_SOURCES = \ @@ -305,16 +303,16 @@ tests_url_encode_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_url_encode_LDADD = \ - libgcli.la libpdjson.la libsn.la \ + libgcli.la libpdjson.la \ $(LIBATFC_LIBS) tests_pretty_print_test_SOURCES = \ - tests/pretty_print_test.c + tests/pretty_print_test.c \ + thirdparty/sn/sn.c thirdparty/sn/sn.h tests_pretty_print_test_CFLAGS = \ $(AM_CFLAGS) \ $(LIBATFC_CFLAGS) tests_pretty_print_test_LDADD = \ - libsn.la \ $(LIBATFC_LIBS) tests_test_jsongen_SOURCES = \ From 0ba81c89e28aa7d543202467ad4b0b071b36c860 Mon Sep 17 00:00:00 2001 From: Nico Sonack Date: Mon, 25 Mar 2024 09:44:55 +0100 Subject: [PATCH 32/32] Update changelog Signed-off-by: Nico Sonack --- Changelog.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Changelog.md b/Changelog.md index b540450c..6ed83ed5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,25 @@ This changelog does not follow semantic versioning. The above will search for pull requests containing »segmentation fault« and the label »bug«. +- An interactive mode for creating both PRs and issues has been added. + You can now interactively create pull requests and issues by omitting their title: + + ```console + $ gcli issues create + Owner [herrhotzenplotz]: + Repository [gcli]: + Title: foo + The following issue will be created: + + TITLE : foo + OWNER : herrhotzenplotz + REPO : gcli + MESSAGE : + No message + + Do you want to continue? [yN] + ``` + ### Fixed - gcli was incorrectly using an environment variable *XDG_CONFIG_DIR*. @@ -30,6 +49,9 @@ This changelog does not follow semantic versioning. - Fixed error when submitting a comment on Gitlab issues +- The build on Haiku has been fixed. GCLI can now be compiled and + used on this platform. + ### Changed ### Removed