Compare commits

...

88 Commits

Author SHA1 Message Date
Robin Candau
4c6e9c037b Merge branch 'exit_on_nvchecker_cmd_source' into 'master'
fix(version): Fail if the 'cmd' source is used in .nvchecker.toml

See merge request archlinux/devtools!295
2025-08-11 01:04:48 +02:00
Aaron Liu
3f0ebbc6d2 fix(license): add .gitignore to REUSE defaults
36 packages use this while 26 use *.pam and 21 use *.logrotate. Seems
anecdotally common enough to add this here.
2025-08-08 14:13:32 +02:00
Jakub Klinkovský
fc56ebedf3 fix(completion): fix bash completion for the license subcommand
Signed-off-by: Jakub Klinkovský <lahwaacz@archlinux.org>
2025-08-05 17:48:12 +02:00
Christian Heusel
01757e6904 fix(commitpkg): Quiet git ls-files output
So far all files in `needsversioning=(...)` have been printed to the
command line if they were found, which is not useful, especially now
that we have more files present there.

It makes sense however to keep the standard error output, as this gives
a actionable suggestion what one should to to fix the issue:

    > error: pathspec 'PKGBUILD' did not match any file(s) known to git
    > Did you forget to 'git add'?

Fixes #281

Signed-off-by: Christian Heusel <christian@heusel.eu>
2025-08-01 11:26:57 +02:00
Daniel M. Capella
c5fe8ff3e6 feat(license): Extend matches for sysusers/tmpfiles configs
Eg. to match:
- sysusers.conf
- $pkgname.sysusers
- $pkgname.sysusers.conf
2025-07-28 23:38:32 -04:00
Christian Heusel
ad7dd50bf3 chore(release): version v1.4.0 2025-07-25 09:04:45 +02:00
Jakub Klinkovský
5a381835e8 feat(config): set default build flags for Fortran
This implements RFC 54: https://rfc.archlinux.page/0054-fortran-flags/
2025-07-25 08:54:49 +02:00
Sven-Hendrik Haase
b8c475b3f4 feat(commitpkg): Integrate with pkgctl license
This will check for license compliance before committing.
2025-07-22 20:27:54 +02:00
Sven-Hendrik Haase
74cd46f092 feat(license): Add subcommand to check and setup licenses
This implements a part of RFC52. The new pkgctl license check subcommand calls reuse lint on
the provided directories while pkgctl license setup tries to make packages compliant with RFC40
by adding a LICENSE file and by generating a REUSE.toml.

Component: pkgctl license
2025-07-22 20:27:46 +02:00
Bert Peters
40f31f98a3 fix(autocomplete): don't add extra = for message 2025-05-12 22:49:22 +02:00
Jan Alexander Steffens (heftig)
c6f5d72708 fix(common): Fix diffoscope looking at remote debug info
readelf will pull in remote debug info if allowed to. This is not really
what we expect for diffoscope diffs, especially when our server only has
debug info for one side of the diff.
2025-04-23 01:13:46 +02:00
Jakub Klinkovský
b4a5e5dbd9 fix(build): sync command description with the man page
The original wording sounded like `pkgctl build` by default updates the
checksums whenever `pkgver` is changed in PKGBUILD.
2025-04-18 15:56:51 +02:00
Levente Polyak
4926d9d8c5 chore(release): version v1.3.2 2025-02-25 22:05:15 +01:00
Levente Polyak
7165e0d73e chore(conf): add whitespace to RUSTFLAGS option for unified style 2025-02-25 21:51:37 +01:00
Levente Polyak
8776dd39e8 chore(makerepropkg): unify indention style of the file 2025-02-25 21:32:14 +01:00
Levente Polyak
fb4bf96d24 feat(makerepropkg): support conf.d makepkg config files from buildtool
Previously we have only copied the passed makepkg.conf file into the
chroot, which misses build flags for additional language specific files
that makepkg supports. Fix this by extracting all conf.d makepkg config
files from the detected devtools archive.

Component: makerepropkg
Co-authored-by: Christian Heusel <christian@heusel.eu>
2025-02-25 21:32:06 +01:00
Levente Polyak
96eff02801 feat(arch-nspawn): support conf.d makepkg config files
Previously we have only copied the passed makepkg.conf file into the
chroot, which misses build flags for additional language specific files
that makepkg supports. Fix this by also copying all config files that
match the `<file>.d/*.conf` glob.

Fixes #244

Component: arch-nspawn
Suggested-by: Rein Fernhout (Levitating) <me@levitati.ng>
Co-authored-by: Christian Heusel <christian@heusel.eu>
2025-02-25 21:20:31 +01:00
Levente Polyak
79c3162112 feat(config): provide vendored language specific makepkg.conf files
Vendor all language related makepkg.conf files which are also shipped by
makepkg itself. This makes sure we always have full control over the
build flags inside devtools and overlay any by the vendored config we
maintain in devtools.

Component: archbuild
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2025-02-15 11:43:14 +01:00
Levente Polyak
43cd68d73e feat(archbuild): automatically recreate chroot on version mismatch
For our own archbuild script which is used for package building from a
template chroot, automatically handle the case where the template root
is out of date. Check the version and enable the clean flag by default
in case a mismatch is detected.

Component: archbuild
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2025-02-15 11:43:14 +01:00
Levente Polyak
5c1948a357 chore(archroot): bump version to ensure fresh roots after config changes
We are currently facing reproducible builds issues as the
makepkg.conf.d/rust.conf file in the root chroot was leading to pacnew
files, which means the chroot did not use configs as expected from a
clean state. Work around this problem by bumping the chroot version and
ensure we get fresh chroots with expected configs

Component: archroot
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2025-02-15 11:24:31 +01:00
Bert Peters
acd6bda3ed Don't add extra "=" to repo completion 2025-02-01 15:45:36 +01:00
Christian Heusel
8af7a50c03 chore(release): version v1.3.1 2025-01-06 10:11:07 +01:00
Christian Heusel
bed2b5db28 fix(gitlab): prevent division by 0 for missing total pages
As it turns out the Gitlab api is not guaranteed to return the
x-total-pages header for larger query result which previously resulted
in a division by zero for pkgctl search as the utlity function assumed
that this value would always be set to a positive integer.

Fixes #255

Link: https://gitlab.com/gitlab-org/gitlab/-/issues/436373
Component: gitlab.sh
Signed-off-by: Christian Heusel <christian@heusel.eu>
2025-01-05 20:19:33 +01:00
Robin Candau
cfb99fd2d8 fix(version): Fail if the 'cmd' source is used in .nvchecker.toml
The [cmd](https://nvchecker.readthedocs.io/en/latest/usage.html#find-with-a-command) source allows nvchecker to use a shell command line to get versions. Using this source within `.nvchecker.toml` would result in `pkgctl version {check,upgrade}` to run arbitrary commands which isn't desirable, as it can lead to various issues (e.g. missing packages / dependencies to run said commands or even executing malicious commands in hypothetical worst case scenarios)
2025-01-04 12:25:32 +01:00
Levente Polyak
47d5ea1e89 fix(version): Disable Git user configs to avoid side-effects
The nvchecker upstream version checks are expected to work as is on any
machines without the need of manual Git user configuration. However,
certain user configuration may have a side-effect on version checks.
Subsequently we try to avoid this situation by always disabling Git
config locations.

Component: pkgctl version check
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2025-01-03 23:35:59 +01:00
Jakub Klinkovský
8df81ecd7c fix: declare local arrays before using mapfile 2025-01-03 19:04:01 +01:00
Jakub Klinkovský
1101de9fb9 fix(offload-build): download logs even when the build fails
Fixes #260
2024-12-14 07:34:34 +01:00
Christian Heusel
d5e1c5fae3 fix: Display issue comments in chronological order
Fixes #259

Fixes: 0df36df ("feat(issue): add subcommand to list group and project issues")
Component: pkgctl issue view
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-12-13 21:33:51 +01:00
Zhou Qiankang
e8ab01d662 fix(checkpkg): add a default value for terminal width
* Ensures availability when $COLUMNS is not present

Signed-off-by: Zhou Qiankang <wszqkzqk@qq.com>
2024-12-08 23:51:12 +08:00
Christian Heusel
7d9c2e0648 chore(release): version v1.3.0 2024-12-04 22:37:59 +01:00
Christian Heusel
8bcbca830e fix(util): disable landlocking in fakeroot
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-12-04 18:15:13 +01:00
T.J. Townsend
68eb498347 chore: sync pacman configs with package defaults 2024-12-04 18:15:13 +01:00
Robin Candau
23f1314733 feat(release): Add a warning if nvchecker integration is not set
Hints to run `pkgctl version setup --help` to see how to set it up if needed.

Closes https://gitlab.archlinux.org/archlinux/devtools/-/issues/236
2024-12-03 22:32:59 +01:00
Levente Polyak
98b079f047 chore(doc): remove superfluous trailing whitespaces from pkgctl man 2024-12-03 22:19:58 +01:00
Levente Polyak
a319b0b852 feat(issue): add subcommand to edit an issue
The pkgctl issue edit command is used to modify an existing issue in Arch Linux
packaging projects. This command allows users to update the issue's title,
description, and various attributes, ensuring that the issue information
remains accurate and up-to-date. It also provides a streamlined facility
for bug wranglers to categorize and prioritize issues efficiently.

By default, the command operates within the current directory, but users can
specify a different package base if needed.

In case of a failed run, the command can automatically recover to ensure that
the editing process is completed without losing any data.

Component: pkgctl issue edit
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:19:51 +01:00
Levente Polyak
a1e443856d feat(issue): add subcommand to create a new issue
The create command is used to create a new issue for an Arch Linux package.
This command is suitable for reporting bugs, regressions, feature requests, or
any other issues related to a package. It provides a flexible way to document
and track new issues within the project's issue tracking system.

By default, the command operates within the current directory, but users can
specify a different package base if needed.

Users can provide a title for the issue directly through the command line.
The command allows setting various labels and attributes for the issue, such as
confidentiality, priority, scope, severity, and status.

In case of a failed run, the command can automatically recover to ensure that
the issue creation process is completed without losing any data.

Component: pkgctl issue create
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:19:36 +01:00
Levente Polyak
dfb65e95e3 feat(issue): add subcommand to move issues between projects
The move command allows users to transfer an issue from one project to another
within the Arch Linux packaging group. This is useful when an issue is
identified to be more relevant or better handled in a different project.

By default, the command operates within the current directory, but users can
specify a different package base from which to move the issue.

Users must specify the issue ID (IID) and the destination package to which the
issue should be moved. A comment message explaining the reason for the move can
be provided directly through the command line.

Component: pkgctl issue move
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:19:22 +01:00
Levente Polyak
4e7ec8b37f feat(issue): add subcommand to reopen issues
The reopen command is used to reopen a previously closed issue in Arch Linux
packaging projects. This command is useful when an issue needs to be revisited
or additional work is required after it was initially closed.

By default, the command operates within the current directory, but users can
specify a different package base if needed.

Users can provide a message directly through the command line to explain the
reason for reopening the issue.

Component: pkgctl issue reopen
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:19:07 +01:00
Levente Polyak
292920ac7e feat(issue): add subcommand to close issues
This command is used to close an issue in Arch Linux packaging projects. It
finalizes the issue by marking it as resolved and optionally providing a reason
for its closure.

By default, the command operates within the current directory, but users have
the option to specify a different package base.

Users can provide a message directly through the command line to explain the
reason for closing the issue. Additionally, a specific resolution label can be
set to categorize the closure reason, with the default label being "completed."

Component: pkgctl issue close
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:18:52 +01:00
Levente Polyak
dde6539971 feat(issue): add subcommand to comment on issues
This command allows users to add comments to an issue in Arch Linux packaging
projects. This command is useful for providing feedback, updates, or any
additional information related to an issue directly within the project's issue
tracking system.

By default, the command interacts with the current directory, but users can
specify a different package base if needed.

Component: pkgctl issue comment
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:18:38 +01:00
Levente Polyak
8803e5a57a feat(issue): add subcommand to view issue details and comments
This command is designed to display detailed information about a specific issue
in Arch Linux packaging projects. It gathers and pretty prints all relevant
data about the issue, providing a comprehensive view that includes the issue's
description, status as well as labels and creation date.

By default, the command operates within the current directory, but users have
the option to specify a different package base. Additionally, users can choose
to view the issue in a web browser for a more interactive experience.

Component: pkgctl issue view
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:18:23 +01:00
Levente Polyak
0df36dfa52 feat(issue): add subcommand to list group and project issues
The pkgctl issue list command is used to list issues associated with a specific
packaging project or the entire packaging subgroup in Arch Linux. This command
facilitates efficient issue management by allowing users to list and filter
issues based on various criteria.

Results can also be displayed directly in a web browser for easier navigation
and review.

Component: pkgctl issue list
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:17:39 +01:00
Levente Polyak
b9fe8ee947 chore(gitlab): move project name lookup to gitlab library functions
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-12-03 22:13:14 +01:00
Jakub Klinkovský
af56897f76 feat(offload-build): create or reuse a shared SSH socket with ControlMaster=auto 2024-11-28 15:06:29 +01:00
Jakub Klinkovský
99c6c26a1c fix(offload-build): add end-of-options token (--) to all ssh and rsync commands 2024-11-28 15:06:29 +01:00
Jakub Klinkovský
00f97fcd3d fix(offload-build): setup TEMPDIR in WORKDIR and fix trap override 2024-11-28 15:06:29 +01:00
Jakub Klinkovský
effe511952 fix(offload-build): fix quoting in sshopts and rsyncopts 2024-11-28 15:06:29 +01:00
Jakub Klinkovský
1cd213b2f5 fix(offload-build): ungolfing remote command execution
Instead of passing the command as one complex string to ssh, we create
an SSH master connection and use its control socket in multiple simpler
commands. The same socket is passed also to rsync to transfer the srcpkg
to the remote and to download the build artifacts.

Previously, the srcpkg was passed via stdin to ssh, which prevented
`pkgctl build --offload --inspect` from working. This change frees stdin
for proper remote ptty allocation.

However, it seems that ssh commands with and without the `-t` flag
cannot be multiplexed on a single connection, so there are technically
two SSH connections active for the offload-build execution.
2024-11-28 15:06:29 +01:00
Levente Polyak
b88dec322c feat(version): graceful config for packages without remote sources
Setup a blank config for packages without remote sources. This is
helpful so other commands like `pkgctl version check` operate gracefully
as well as we have easy way to find packages that miss nvchecker
config.

This must only be used for cases without an upstream, please reach out
to the developer team for guidance regarding upstream sources that are
hard to configure.

Component: pkgctl version setup
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-11-26 19:01:56 +01:00
Andreas Schleifer
e2ab07caff feat(version): add json output option to version check command
This allows for tools and data visualization to interface against pkgctl
with a machine readable output.

Fixes #237

Component: pkgctl version check
Signed-off-by: Andreas Schleifer <segaja@archlinux.org>
Co-authored-by: Levente Polyak <anthraxx@archlinux.org>
2024-11-23 17:53:12 +01:00
Celeste Liu
5c0f8d37d5 fix(arch-nspawn): add --timezone=off to avoid pollute build environment
From systemd-nspawn(1),

	--timezone=
		Configures how /etc/localtime inside of the container (i.e. local timezone synchronization from host to container) shall be handled. Takes one of "off", "copy", "bind", "symlink", "delete" or "auto". If set to "off" the /etc/localtime file in the container
		is left as it is included in the image, and neither modified nor bind mounted over. If set to "copy" the /etc/localtime file of the host is copied into the container. Similarly, if "bind" is used, the file is bind mounted from the host into the container. If
		set to "symlink", a symlink is created pointing from /etc/localtime in the container to the timezone file in the container that matches the timezone setting on the host. If set to "delete", the file in the container is deleted, should it exist. If set to
		"auto" and the /etc/localtime file of the host is a symlink, then "symlink" mode is used, and "copy" otherwise, except if the image is read-only in which case "bind" is used instead. Defaults to "auto".

		Added in version 239.

After this commit, we need to recreate all build environment to clean up
pollution already existed.

resolve #250

Signed-off-by: Celeste Liu <CoelacanthusHex@gmail.com>
2024-11-09 20:26:28 +08:00
Christian Heusel
e1401ce41c fix: disable confirmation when dropping packages
Since commit 1d433f6 ("feat(db): confirm list of all packages that will be removed") packages need confirmation by default when being dropped from the db. If we make it to the DB drop phase the package already is pushed to the AUR, so it is safe to remove from the database and not removing it would create a somewhat broken state, so we assume that the package should unconditionally be removed from the db.

Component: pkgctl aur drop-from-repo
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-09-29 12:40:22 +02:00
Orhun Parmaksız
8612b41a20 fix: update the personal access token URL
PAT settings are now under `user_settings` instead of `profile`

Component: pkgctl auth login
2024-09-25 13:20:46 +02:00
Jaroslav Lichtblau
fbb661645b doc: fix the example in the help text
Without the "--release" flag the example command fails with
==> ERROR: cannot use --message without --release.

Component: pkgctl build
2024-09-10 10:59:18 +02:00
Christian Heusel
f1dc2e18f7 fix: remove duplicate error message
It seems like nvchecker emits two log entries for errors:

    $ nvchecker --logger json -c .nvchecker.toml
    {"logger_name": "nvchecker.core", "name": "curl", "event": "token not given but it is required", "level": "error"}
    {"logger_name": "nvchecker.core", "name": "curl", "error": "token not given but it is required", "event": "no-result", "level": "error"}

This leads to a double error message as described in the related issue,
which we fix by narrowing the selector to filter for the error entry.

Fixes #235

Component: pkgctl version check
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-09-08 22:38:19 +02:00
Christian Heusel
c9d821448b fix(makechrootpkg): improve btrfs sanity checks
If the chroot was created in a way where it resides on a BTRFS file
system but "$copydir/root" is not a snapshot an error like the following
would be emitted:

  $ makechrootpkg -r ~/chroot
  ==> Synchronizing chroot copy [/home/chris/chroot/root] -> [chris]...ERROR: Not a Btrfs subvolume: Invalid argument
  ==> ERROR: Unable to create subvolume /home/chris/chroot/chris

Fix this by adding an additional check, which detects if the folder is
actually the root of a BTRFS snapshot before attempting to clone it.

Related to https://gitlab.archlinux.org/archlinux/devtools/-/merge_requests/259

Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-09-08 22:33:45 +02:00
Fox2Code
a620250535 doc: specify default chroot folder for pkgctl-build 2024-08-21 11:26:33 +02:00
Jaroslav Lichtblau
27eebe383d doc: fix the example command in the help text
the '--pkgver' argument is not space-separated but instead specified
with an equals sign.

Component: pkgctl build
2024-06-26 21:00:19 +02:00
Jakub Klinkovský
d6d416b653 feat(checkpkg): enhance diff command for comparing file lists
Replace `sdiff` with `diff` (also from `diffutils`) with the following
parameters:

- `--side-by-side` for the `sdiff`-like output
- `--suppress-common-lines` for the `sdiff -s` behavior
- `--width="$COLUMNS"` to use the full terminal width (long lines are
  still truncated but it is definitely better than the default width of
  130 chars)
- `--color=auto` just because 😉
2024-06-26 07:40:12 +02:00
Chih-Hsuan Yen
9ff63503b9 fix(pkgctl): make sure git signing uses PGP
Component: pkgctl repo configure
2024-06-19 17:37:18 +08:00
Christian Heusel
3f02a1a24c chore(release): version v1.2.1 2024-06-18 14:14:22 +02:00
Christian Heusel
1df0979da6 fix(common): guard the WORKDIR environment var
This avoids the unwanted removal of the folder if someone has already
pre-defined the variable.

Fixes #219

Suggested-by: Levente Polyak <anthraxx@archlinux.org>
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-06-15 14:55:18 +02:00
Christian Heusel
c1a3ed224b misc: add a editorconfig to devtools
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-06-11 22:04:58 +02:00
David Runge
144f9a871e fix(version): Ignore warnings when nvchecker ignores invalid versions
Since version 2.15.1 nvchecker emits a warning for version strings that
it consideres invalid (e.g. in the case of PyPI).
These warning messages get in the way (the first version emitted via a
warning is used as version to compare against) of retrieving the latest
version of an upstream and therefore we ignore them.

Component: pkgctl version check
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-06-09 14:36:47 +02:00
Christian Heusel
44328cd9a3 test: fix the testcases so they pass again
The expected output for these tests has now changed since git smash
(which we test against) has changed their version scheme.

Component: pkgctl version setup
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-06-08 17:35:46 +02:00
Christian Heusel
0e2b16b0ac fix: disable systemd-nspawn terminal coloring
Systemd 256 introduces functionality which colors the terminal
background on systemd-nspawn invocations which makes the pkgctl output
look weird.

Disable this bevaviour for pkgctl, so it stays active for arch-nspawn
(for now).

Component: pkgctl
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-05-22 12:33:09 +02:00
Maharshi Basu
6f0a8c345d doc: add manpage info for pkgctl, pkgctl-db(1) & pkgctl-build
Changes made:
- add description in doc/man/pkgctl.1.asciidoc
- add description in doc/man/pkgctl-db(1).asciidoc
- add manpage for pkgctl-db(1) in doc/man/pkgctl-db(1).asciidoc

Fixes #231

Signed-off-by: Maharshi Basu <basumaharshi10@gmail.com>
2024-05-18 20:36:09 +02:00
依云
5f19853414 feat: support bind mounting a tmpfs
A .cache can be bind mounted into the container to save the caches for
reuse, but sometimes we want to exclude specific caches (because they
don't work well, e.g. bazel's).

Component: makechrootpkg
2024-05-18 19:39:18 +02:00
Frédéric Pierret (fepitre)
2b62ac660d feat(mkarchroot): allow to specify file destination inside chroot
Co-authored-by: Christian Heusel <christian@heusel.eu>
2024-05-18 13:53:39 +02:00
Jan Alexander Steffens (heftig)
7cb72699f6 fix: consider all git repos inside the chroot safe
Git 2.45.1 expanded its security checks to deny cloning even local repos
that are owned by another user. Previously, this just affected network
filesystems.

On our buildserver, this prevents makepkg from cloning repos from our
shared srcdest into the srcdir, if these repos were created by another
packager.

To disable this check, set `safe.directory` to `*`. This looks like a
glob, but is really just a special value. The only other option would be
to add each Git repository in srcdest to the configuration.

Component: makechrootpkg
2024-05-18 00:15:06 +02:00
Levente Polyak
d1790c295a fix(version): escape pkgbase in nvchecker toml
This fixes issues with packages containing plus signs, that need to be
escaped in toml as well as the extended grep regex.

Component: pkgctl version check
2024-05-10 21:22:23 +02:00
Levente Polyak
12a0d0c7b5 chore(release): version v1.2.0 2024-05-01 16:38:19 +02:00
Jakub Klinkovský
952f483574 feat(offload-build): fetch logs after building from the remote server
Since logs from offloaded builds are collected in a temporary directory
on the remote server, it is rather difficult/error-prone to get to them,
because the path changes in each rebuild. Fetching logs from the server
into $LOGDEST makes it easier to investigate them and also brings the
behavior of offload-build closer to archbuild.

Log files are always downloaded, even for failed builds.

Component: offload-build
Signed-off-by: Jakub Klinkovský <lahwaacz@archlinux.org>
Co-authored-by: Levente Polyak <anthraxx@archlinux.org>
2024-04-30 23:30:47 +02:00
Christian Heusel
c484a55cde fix(version): dont die if no PKGBUILD is found
So far the commands would stop execution if one of the target
directories did not contain a PKGBUILD instead of just reporting failure
for that directory. Fix this by replacing the 'die' calls with setting
the error for the spinner facility.

Component: pkgctl version check
Component: pkgctl version upgrade
Signed-off-by: Christian Heusel <christian@heusel.eu>
2024-04-29 19:04:46 +02:00
Robin Candau
a46b2d4fb7 feat(repo): add repo clean command to remove untracked files
This introduces the `pkgctl repo clean` command which removes every
untracked files from local package repositories (via `git clean`).

The usage is as simple as `pkgctl repo clean [OPTION] [PATH]` (where
"[PATH]" can be equal to a wildcard "*").

Component: pkgctl repo clean
2024-04-28 19:42:02 +02:00
Felix Yan
e828111ff7 feat(arch-nspawn): add support for pacman CacheServer
pacman 6.1 added support for CacheServer which allows us to download
packages faster and reduce the burden on tier0 server.

They won't be used to fetch databases so it should be pretty safe in the
repository consistency context.

Component: arch-nspawn
2024-04-28 17:06:46 +02:00
Levente Polyak
35b417d226 fix(db): check valid target repo on db remove
Return an error and abort operation when an invalid target repo is
passed.

Component: pkgctl db remove
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-04-28 15:56:00 +02:00
Levente Polyak
1d433f600e feat(db): confirm list of all packages that will be removed
Sometimes it isn't obvious which set of packages are removed from a
split package when the pkgbase matches also a subset of a pkgbase. This
can happen for example with bootstrapping packages, when the intention
is to just remove a partial part of the bootstrap pkgbase.

To make the intention more explicit, list all to be removed packages and
await for confirmation.

Component: pkgctl db remove
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-04-28 15:55:53 +02:00
Levente Polyak
7b553afcb2 feat(db): add partial split package option to db remove
By default passing a pkgbase removes all split packages, debug packages
as well as entries from the state repo for all existing architectures.

When using the `--partial` option it may most likely lead to undesired
effects by leaving debug packages behind as well as dangling entries in
the state repository. However, for specific use cases its required to
get rid of old split package parts.

Fixes #218

Component: pkgctl db remove
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-04-27 18:38:12 +02:00
sfan5
01614c6817 feat(arch-nspawn): set console mode to autopipe
systemd-nspawn sets up a pts in the container by default which build
scripts might use to ask for interactive input.  This is only okay if
the host side is also a TTY.  The 'autopipe' mode deals with this
situation appropriately.
2024-04-27 14:52:13 +02:00
Christian Heusel
dced77d23d fix(completion): fix erroneous completion variables
The architecture definition of the variable was using invalid bash
syntax and was previously unused:

    $ _binary_arch=${DEVTOOLS_VALID_ARCHES[*]:0:-1}
    bash: -1: substring expression < 0

We therefore fix the definition of the variable and use it for the
autocompletion of the offload-build command.

Furthermore fix wrongly named architecture variables that have been
missed from previous refactoring.

Fixes #222

Component: completion
Fixes: f961e2e ("completion: implemented structured declarative bash completions")
Fixes: 4173e0a ("chore: refactor variable names in valid-{tags,repos}.sh")
Signed-off-by: Christian Heusel <christian@heusel.eu>
Co-authored-by: Levente Polyak <anthraxx@archlinux.org>
2024-04-24 22:55:18 +02:00
Levente Polyak
5780ba0e38 fix(release): ensure we release split package uploads as one batch
Use a central location in common.sh to define the default rsync options.
Switch to use batched uploads by targeting a partial directory which is
not taken into account by db update. Instead, once all packages that
belong to a split package group are uploaded into the .partial
directory, all artifacts are moved in one batch into the staging
directory of the repo server. This reduced the window of opportunity for
a partial release significantly to a tiny window.

Component: pkgctl release
2024-04-23 22:48:55 +02:00
Levente Polyak
9a5181db5b feat(pkgctl): add internal update checksums to better control output
This allows us to have more control over the output and status logs.
Using this method we are able to avoid cluttering the version upgrade
subcommand while downloading sources for updating the checksums.

Having this internally will also allow us in the future to have smart
checksums updating by only trying to change the checksums of sources
that have actually changed, for example when adjusting a patch file we
should avoid trying to overwrite the archive checksums unintentionally.

Component: pkgctl version upgrade
2024-04-23 20:23:49 +02:00
Robin Candau
55c2ca1312 feat(version): let upgrade subcommand also update checksums
This commit aims to make 'pkgctl version upgrade' also update checksums
in addition of bumping the pkgver and reseting the pkgrel.

Component: pkgctl version upgrade
2024-04-23 20:23:42 +02:00
Vekhir --
ccee85b2ab fix: conflicting packages when using -I
Originally fixed in b7893a2ca8, that approach created another bug when packages have several providers. In that case, pacman expects a number, so "yes" is not a valid answer, leading to an infinite loop.

Using the undocumented, but stable option "--ask=4" allows to selectively change the default answer for conflicts, therefore properly fixing the underlying issue.
See also: https://gitlab.archlinux.org/pacman/pacman/-/issues/60
Fixes #163

Component: makechrootpkg
2024-04-16 23:50:12 +02:00
Levente Polyak
05bea3e922 test: add basic bats setup and test cases for version setup
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-03-23 23:57:46 +01:00
Levente Polyak
de0ea3b2f8 fix(version): do not prompt for input on terminal during check
In certain scenarios, like when an URL is checked that asks for
credentials or other kind of input, the process blocks and waits for
stdin input. This isn't particularly useful during scripted execution
and in our cases also just an indicator for like a none existing GitHub
repository.

To avoid this situation, disable git terminal prompts.

Component: pkgctl version check
Signed-off-by: Levente Polyak <anthraxx@archlinux.org>
2024-03-23 23:57:45 +01:00
Jelle van der Waa
81f5e7b3b3 feat(version): add command to automatically detect and setup nvchecker
Introduce a new version subcommand `setup` which does a best effort to
generate the most minimal required .nvchecker.toml file for specific
sources.

It supports a wide range of common sources like:
- Git, GitHub, GitLab, Hackage, NPM, PyPI, RubyGems, CPAN, crates.io

The creation logic is based on matching a domain for a source which is
something predictable and then simply passes an array of the url parts
for every source creator to extract the useful bits out of the url
array.

Component: pkgctl version setup
Co-authored-by: Levente Polyak <anthraxx@archlinux.org>
Signed-off-by: Jelle van der Waa <jelle@archlinux.org>
2024-03-23 23:57:45 +01:00
158 changed files with 7192 additions and 243 deletions

15
.editorconfig Normal file
View File

@@ -0,0 +1,15 @@
# EditorConfig configuration for devtools
# https://editorconfig.org
# Top-most EditorConfig file
root = true
# Unix-style newlines without trailing whitespaces, but with a newline
# ending every file, utf-8 charset, set indent to tabs
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = tab
tab_width = 4

View File

@@ -12,10 +12,33 @@ build:
- make PREFIX=/usr
- make PREFIX=/usr DESTDIR=build install
check:
lint:
stage: test
needs: []
script:
- pacman -Syu --noconfirm m4 make openssh subversion rsync arch-install-scripts git bzr mercurial diffutils asciidoctor shellcheck
- make check || true
- SHELLCHECK_OPTS="-S error" make check
test:
stage: test
needs: []
script:
- pacman -Syu --noconfirm m4 make openssh subversion rsync arch-install-scripts git bzr mercurial diffutils coreutils asciidoctor shellcheck nvchecker bats
- make test BATS_EXTRA_ARGS='--formatter junit'
artifacts:
reports:
junit: build/coverage/bats-report.xml
coverage:
stage: test
needs: []
script:
- pacman -Syu --noconfirm m4 make openssh subversion rsync arch-install-scripts git bzr mercurial diffutils coreutils asciidoctor shellcheck nvchecker bats kcov jq
- make coverage
coverage: '/Percent covered\s+\d+\.\d+/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: build/coverage/bats.*/cobertura.xml

View File

@@ -1,6 +1,6 @@
SHELL=/bin/bash
SHELL=/bin/bash -o pipefail
V=1.1.1
V=1.4.0
BUILDTOOLVER ?= $(V)
PREFIX = /usr/local
@@ -19,6 +19,7 @@ PACMAN_CONFIGS=$(wildcard config/pacman/*)
GIT_CONFIGS = $(wildcard config/git/*)
SETARCH_ALIASES = $(wildcard config/setarch-aliases.d/*)
MANS = $(addprefix $(BUILDDIR)/,$(patsubst %.asciidoc,%,$(wildcard doc/man/*.asciidoc)))
DATA_FILES = $(wildcard data/*)
COMMITPKG_LINKS = \
core-testingpkg \
@@ -52,7 +53,14 @@ ARCHBUILD_LINKS = \
COMPLETIONS = $(addprefix $(BUILDDIR)/,$(patsubst %.in,%,$(wildcard contrib/completion/*/*)))
all: binprogs library conf completion man
CASES ?= test/case
JOBS ?= $(shell nproc)
BATS_EXTRA_ARGS ?=
BATS_ARGS ?= --jobs $(JOBS) $(BATS_EXTRA_ARGS) --verbose-run
COVERAGE_DIR ?= $(BUILDDIR)/coverage
all: binprogs library conf completion man data
binprogs: $(BINPROGS)
library: $(LIBRARY)
completion: $(COMPLETIONS)
@@ -99,12 +107,16 @@ $(BUILDDIR)/doc/man/%: doc/man/%.asciidoc doc/man/include/footer.asciidoc
conf:
@install -d $(BUILDDIR)/makepkg.conf.d
@cp -a $(MAKEPKG_CONFIGS) $(BUILDDIR)/makepkg.conf.d
@cp -ra $(MAKEPKG_CONFIGS) $(BUILDDIR)/makepkg.conf.d
@install -d $(BUILDDIR)/pacman.conf.d
@cp -a $(PACMAN_CONFIGS) $(BUILDDIR)/pacman.conf.d
@install -d $(BUILDDIR)/git.conf.d
@cp -a $(GIT_CONFIGS) $(BUILDDIR)/git.conf.d
data:
@install -d $(BUILDDIR)/data
@cp -ra $(DATA_FILES) $(BUILDDIR)/data
clean:
rm -rf $(BUILDDIR)
@@ -115,9 +127,11 @@ install: all
install -dm0755 $(DESTDIR)$(DATADIR)/pacman.conf.d
install -m0755 ${BINPROGS} $(DESTDIR)$(PREFIX)/bin
install -dm0755 $(DESTDIR)$(DATADIR)/lib
install -dm0755 $(DESTDIR)$(DATADIR)/data
cp -ra $(BUILDDIR)/lib/* $(DESTDIR)$(DATADIR)/lib
cp -a $(BUILDDIR)/git.conf.d -t $(DESTDIR)$(DATADIR)
for conf in $(notdir $(MAKEPKG_CONFIGS)); do install -Dm0644 $(BUILDDIR)/makepkg.conf.d/$$conf $(DESTDIR)$(DATADIR)/makepkg.conf.d/$${conf##*/}; done
cp -ra $(BUILDDIR)/makepkg.conf.d -t $(DESTDIR)$(DATADIR)
cp -ra $(BUILDDIR)/data -t $(DESTDIR)$(DATADIR)
for conf in $(notdir $(PACMAN_CONFIGS)); do install -Dm0644 $(BUILDDIR)/pacman.conf.d/$$conf $(DESTDIR)$(DATADIR)/pacman.conf.d/$${conf##*/}; done
for a in ${SETARCH_ALIASES}; do install -m0644 $$a -t $(DESTDIR)$(DATADIR)/setarch-aliases.d; done
for l in ${COMMITPKG_LINKS}; do ln -sf commitpkg $(DESTDIR)$(PREFIX)/bin/$$l; done
@@ -135,7 +149,8 @@ uninstall:
for f in $(notdir $(LIBRARY)); do rm -f $(DESTDIR)$(DATADIR)/lib/$$f; done
rm -rf $(DESTDIR)$(DATADIR)/lib
rm -rf $(DESTDIR)$(DATADIR)/git.conf.d
for conf in $(notdir $(MAKEPKG_CONFIGS)); do rm -f $(DESTDIR)$(DATADIR)/makepkg.conf.d/$${conf##*/}; done
rm -rf $(DESTDIR)$(DATADIR)/makepkg.conf.d
rm -rf $(DESTDIR)$(DATADIR)/data
for conf in $(notdir $(PACMAN_CONFIGS)); do rm -f $(DESTDIR)$(DATADIR)/pacman.conf.d/$${conf##*/}; done
for f in $(notdir $(SETARCH_ALIASES)); do rm -f $(DESTDIR)$(DATADIR)/setarch-aliases.d/$$f; done
for l in ${COMMITPKG_LINKS}; do rm -f $(DESTDIR)$(PREFIX)/bin/$$l; done
@@ -147,7 +162,6 @@ uninstall:
for manfile in $(notdir $(MANS)); do rm -f $(DESTDIR)$(MANDIR)/man$${manfile##*.}/$${manfile}; done;
rmdir --ignore-fail-on-non-empty \
$(DESTDIR)$(DATADIR)/setarch-aliases.d \
$(DESTDIR)$(DATADIR)/makepkg.conf.d \
$(DESTDIR)$(DATADIR)/pacman.conf.d \
$(DESTDIR)$(DATADIR)
@@ -168,8 +182,17 @@ dist:
git archive --format=tar --prefix=devtools-$(V)/ v$(V) | gzip > devtools-$(V).tar.gz
gpg --detach-sign --use-agent devtools-$(V).tar.gz
test: binprogs library conf completion man
@mkdir -p $(COVERAGE_DIR)
bats $(BATS_ARGS) $(CASES) | tee $(COVERAGE_DIR)/bats-report.xml
coverage: binprogs library conf completion man
kcov --include-path=src $(COVERAGE_DIR) bats $(BATS_ARGS) $(CASES)
jq -r '. | ["Percent covered", .percent_covered], ["Covered lines", .covered_lines], ["Total lines", .total_lines], ["Percent low", .percent_low], ["Percent high", .percent_high] | @tsv' \
$(COVERAGE_DIR)/bats.*/coverage.json
check: $(BINPROGS_SRC) $(LIBRARY_SRC) contrib/completion/bash/devtools.in config/makepkg/x86_64.conf contrib/makepkg/PKGBUILD.proto
shellcheck $^
.PHONY: all binprogs library completion conf man clean install uninstall tag dist upload check
.PHONY: all binprogs library completion conf man data clean install uninstall tag dist upload test coverage check
.DELETE_ON_ERROR:

View File

@@ -71,9 +71,12 @@ Component: pkgctl db remove
- coreutils
- curl
- diffutils
- expac
- fakeroot
- findutils
- glow
- grep
- gum
- jq
- ncurses
- openssh
@@ -91,13 +94,14 @@ Component: pkgctl db remove
- bat (pretty printing)
- nvchecker (version checking)
- pacman-contrib (--update-checksums options for pkgctl build)
- reuse (license compliance)
### Development Dependencies
- asciidoctor
- make
- shellcheck
- bats
## License

View File

@@ -2,6 +2,7 @@
/src
/*/
!/keys/
!/LICENSES/
/*.log
/*.tar.*

View File

@@ -0,0 +1,22 @@
#!/hint/bash
# shellcheck disable=2034
#
# /etc/makepkg.conf.d/fortran.conf
#
#########################################################################
# FORTRAN LANGUAGE SUPPORT
#########################################################################
# Flags used for the Fortran compiler, similar in spirit to CFLAGS. Read
# linkman:gfortran[1] for more details on the available flags.
FFLAGS="-march=x86-64 -mtune=generic -O2 -pipe -fno-plt \
-Wp,-D_FORTIFY_SOURCE=3 -fstack-clash-protection -fcf-protection \
-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"
FCFLAGS="$FFLAGS"
# Additional compiler flags appended to `FFLAGS` and `FCFLAGS` for use in debugging. Usually
# this would include: ``-g''. Read linkman:gfortran[1] for more details on the wide
# variety of compiler flags available.
DEBUG_FFLAGS="-g"

View File

@@ -0,0 +1,19 @@
#!/hint/bash
# shellcheck disable=2034
#
# /etc/makepkg.conf.d/rust.conf
#
#########################################################################
# RUST LANGUAGE SUPPORT
#########################################################################
# Flags used for the Rust compiler, similar in spirit to CFLAGS. Read
# linkman:rustc[1] for more details on the available flags.
RUSTFLAGS="-C force-frame-pointers=yes"
# Additional compiler flags appended to `RUSTFLAGS` for use in debugging.
# Usually this would include: ``-C debuginfo=2''. Read linkman:rustc[1] for
# more details on the available flags.
DEBUG_RUSTFLAGS="-C debuginfo=2"

View File

@@ -48,13 +48,11 @@ CXXFLAGS="$CFLAGS -Wp,-D_GLIBCXX_ASSERTIONS"
LDFLAGS="-Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now \
-Wl,-z,pack-relative-relocs"
LTOFLAGS="-flto=auto"
RUSTFLAGS="-Cforce-frame-pointers=yes"
#-- Make Flags: change this for DistCC/SMP systems
#MAKEFLAGS="-j2"
#-- Debugging flags
DEBUG_CFLAGS="-g"
DEBUG_CXXFLAGS="$DEBUG_CFLAGS"
DEBUG_RUSTFLAGS="-C debuginfo=2"
#########################################################################
# BUILD ENVIRONMENT
@@ -83,7 +81,7 @@ BUILDENV=(!distcc color !ccache check !sign)
# These are default values for the options=() settings
#########################################################################
#
# Makepkg defaults: OPTIONS=(!strip docs libtool staticlibs emptydirs !zipman !purge !debug !lto)
# Makepkg defaults: OPTIONS=(!strip docs libtool staticlibs emptydirs !zipman !purge !debug !lto !autodeps)
# A negated option will do the opposite of the comments below.
#
#-- strip: Strip symbols from binaries/libraries
@@ -95,6 +93,7 @@ BUILDENV=(!distcc color !ccache check !sign)
#-- purge: Remove files specified by PURGE_TARGETS
#-- debug: Add debugging flags as specified in DEBUG_* variables
#-- lto: Add compile flags for building with link time optimization
#-- autodeps: Automatically add depends/provides
#
OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge debug lto)
@@ -114,6 +113,8 @@ DOC_DIRS=(usr/{,local/}{,share/}{doc,gtk-doc} opt/*/{doc,gtk-doc})
PURGE_TARGETS=(usr/{,share}/info/dir .packlist *.pod)
#-- Directory to store source code in for debug packages
DBGSRCDIR="/usr/src/debug"
#-- Prefix and directories for library autodeps
LIB_DIRS=('lib:usr/lib' 'lib32:usr/lib32')
#########################################################################
# PACKAGE OUTPUT

View File

@@ -0,0 +1 @@
../conf.d/fortran.conf

View File

@@ -0,0 +1 @@
../conf.d/rust.conf

View File

@@ -48,13 +48,11 @@ CXXFLAGS="$CFLAGS -Wp,-D_GLIBCXX_ASSERTIONS"
LDFLAGS="-Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now \
-Wl,-z,pack-relative-relocs"
LTOFLAGS="-flto=auto"
RUSTFLAGS="-Cforce-frame-pointers=yes"
#-- Make Flags: change this for DistCC/SMP systems
#MAKEFLAGS="-j2"
#-- Debugging flags
DEBUG_CFLAGS="-g"
DEBUG_CXXFLAGS="$DEBUG_CFLAGS"
DEBUG_RUSTFLAGS="-C debuginfo=2"
#########################################################################
# BUILD ENVIRONMENT
@@ -83,7 +81,7 @@ BUILDENV=(!distcc color !ccache check !sign)
# These are default values for the options=() settings
#########################################################################
#
# Makepkg defaults: OPTIONS=(!strip docs libtool staticlibs emptydirs !zipman !purge !debug !lto)
# Makepkg defaults: OPTIONS=(!strip docs libtool staticlibs emptydirs !zipman !purge !debug !lto !autodeps)
# A negated option will do the opposite of the comments below.
#
#-- strip: Strip symbols from binaries/libraries
@@ -95,6 +93,7 @@ BUILDENV=(!distcc color !ccache check !sign)
#-- purge: Remove files specified by PURGE_TARGETS
#-- debug: Add debugging flags as specified in DEBUG_* variables
#-- lto: Add compile flags for building with link time optimization
#-- autodeps: Automatically add depends/provides
#
OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge debug lto)
@@ -114,6 +113,8 @@ DOC_DIRS=(usr/{,local/}{,share/}{doc,gtk-doc} opt/*/{doc,gtk-doc})
PURGE_TARGETS=(usr/{,share}/info/dir .packlist *.pod)
#-- Directory to store source code in for debug packages
DBGSRCDIR="/usr/src/debug"
#-- Prefix and directories for library autodeps
LIB_DIRS=('lib:usr/lib' 'lib32:usr/lib32')
#########################################################################
# PACKAGE OUTPUT

View File

@@ -0,0 +1 @@
../conf.d/fortran.conf

View File

@@ -0,0 +1 @@
../conf.d/rust.conf

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

View File

@@ -36,6 +36,8 @@ NoProgressBar
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.

114
config/pacman/universe.conf Normal file
View File

@@ -0,0 +1,114 @@
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
#RootDir = /
#DBPath = /var/lib/pacman/
#CacheDir = /var/cache/pacman/pkg/
#LogFile = /var/log/pacman.log
#GPGDir = /etc/pacman.d/gnupg/
#HookDir = /etc/pacman.d/hooks/
HoldPkg = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
#CleanMethod = KeepInstalled
Architecture = auto
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg =
#IgnoreGroup =
#NoUpgrade =
#NoExtract =
# Misc options
#UseSyslog
#Color
NoProgressBar
# We cannot check disk space from within a chroot environment
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
#RemoteFileSigLevel = Required
# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.
#
# REPOSITORIES
# - can be defined here or included from another file
# - pacman will search repositories in the order defined here
# - local/custom mirrors can be added here or in separate files
# - repositories listed first will take precedence when packages
# have identical names, regardless of version number
# - URLs will have $repo replaced by the name of the current repo
# - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
# [repo-name]
# Server = ServerName
# Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#
# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.
[gnome-unstable]
Include = /etc/pacman.d/mirrorlist
[kde-unstable]
Include = /etc/pacman.d/mirrorlist
[core-staging]
Include = /etc/pacman.d/mirrorlist
[core-testing]
Include = /etc/pacman.d/mirrorlist
[core]
Include = /etc/pacman.d/mirrorlist
[extra-staging]
Include = /etc/pacman.d/mirrorlist
[extra-testing]
Include = /etc/pacman.d/mirrorlist
[extra]
Include = /etc/pacman.d/mirrorlist
# If you want to run 32 bit applications on your x86_64 system,
# enable the multilib repositories as required here.
[multilib-staging]
Include = /etc/pacman.d/mirrorlist
[multilib-testing]
Include = /etc/pacman.d/mirrorlist
[multilib]
Include = /etc/pacman.d/mirrorlist
# An example of a custom package repository. See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs

View File

@@ -0,0 +1,85 @@
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
#RootDir = /
#DBPath = /var/lib/pacman/
#CacheDir = /var/cache/pacman/pkg/
#LogFile = /var/log/pacman.log
#GPGDir = /etc/pacman.d/gnupg/
#HookDir = /etc/pacman.d/hooks/
HoldPkg = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
#CleanMethod = KeepInstalled
Architecture = auto
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg =
#IgnoreGroup =
#NoUpgrade =
#NoExtract =
# Misc options
#UseSyslog
#Color
NoProgressBar
# We cannot check disk space from within a chroot environment
#CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
#RemoteFileSigLevel = Required
# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.
#
# REPOSITORIES
# - can be defined here or included from another file
# - pacman will search repositories in the order defined here
# - local/custom mirrors can be added here or in separate files
# - repositories listed first will take precedence when packages
# have identical names, regardless of version number
# - URLs will have $repo replaced by the name of the current repo
# - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
# [repo-name]
# Server = ServerName
# Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#
# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.
[gnome-unstable]
Include = /etc/pacman.d/mirrorlist
[kde-unstable]
Include = /etc/pacman.d/mirrorlist
# An example of a custom package repository. See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs

View File

@@ -13,8 +13,11 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-inspect.sh
# shellcheck source=src/lib/valid-search.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-search.sh
# shellcheck source=src/lib/valid-version.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-version.sh
# shellcheck source=src/lib/valid-issue.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-issue.sh
_binary_arch=${DEVTOOLS_VALID_ARCHES[*]:0:-1}
_colors=(never always auto)
@@ -28,6 +31,7 @@ _makechrootpkg_args=(
-I
-l
-n
-t
-T
-U
-x
@@ -37,6 +41,7 @@ _makechrootpkg_args_D_opts() { _filedir -d; }
_makechrootpkg_args_r_opts() { _filedir -d; }
_makechrootpkg_args_I_opts() { _filedir '*.pkg.tar.*'; }
_makechrootpkg_args_l_opts() { _filedir -d; }
_makechrootpkg_args_t_opts() { _filedir -d; }
_makechrootpkg_args_U_opts() { :; }
_makechrootpkg_args_x_opts() { _devtools_completions_inspect; }
_makechrootpkg() { __devtools_complete _makechrootpkg; }
@@ -62,12 +67,14 @@ _mkarchroot_args=(
-C
-M
-c
-f
-h
)
_mkarchroot_args_U_opts() { _filedir '*.pkg.tar.*'; }
_mkarchroot_args_C_opts() { _filedir '*.conf'; }
_mkarchroot_args_M_opts() { _filedir '*.conf'; }
_mkarchroot_args_c_opts() { _filedir -d; }
_mkarchroot_args_f_opts() { _filedir -d; }
_mkarchroot_opts() {
local args
args=$(__pkgctl_word_count_after_subcommand)
@@ -128,7 +135,7 @@ _offload_build_args=(
)
_offload_build_args__repo_opts() { _devtools_completions_build_repo; }
_offload_build_args_r_opts() { _offload_build_args__repo_opts; }
_offload_build_args__arch_opts() { _devtools_completions_arch; }
_offload_build_args__arch_opts() { _devtools_completions_binary_arch; }
_offload_build_args_a_opts() { _offload_build_args__arch_opts; }
_offload_build_args__server_opts() { :; }
_offload_build_args_s_opts() { _offload_build_args__server_opts; }
@@ -142,6 +149,8 @@ _pkgctl_cmds=(
build
db
diff
issue
license
release
repo
search
@@ -196,7 +205,7 @@ _pkgctl_build_args=(
-h --help
)
_pkgctl_build_args__arch_opts() { _devtools_completions_arch; }
_pkgctl_build_args__arch_opts() { _devtools_completions_binary_arch; }
_pkgctl_build_args__repo_opts() { _devtools_completions_repo; }
_pkgctl_build_args__worker_opts() { :; }
_pkgctl_build_args_w_opts() { _pkgctl_build_args__worker_opts; }
@@ -241,9 +250,13 @@ _pkgctl_db_move_opts() {
_pkgctl_db_remove_args=(
--partial
--noconfirm
-a --arch
-h --help
)
_pkgctl_db_remove_args__arch_opts() { _devtools_completions_binary_arch; }
_pkgctl_db_remove_args_a_opts() { _pkgctl_db_remove_args__arch_opts; }
_pkgctl_db_remove_opts() {
local subcommand args
subcommand=(db remove)
@@ -289,6 +302,7 @@ _pkgctl_aur_drop_from_repo_opts() { _filedir -d; }
_pkgctl_repo_cmds=(
clean
clone
configure
create
@@ -314,6 +328,12 @@ _pkgctl_repo_clone_args__jobs_opts() { :; }
_pkgctl_repo_clone_args_j_opts() { _pkgctl_repo_clone_args__jobs_opts; }
_pkgctl_repo_clone_opts() { _devtools_completions_all_packages; }
_pkgctl_repo_clean_args=(
-i --interactive
-n --dry-run
-h --help
)
_pkgctl_repo_clean_opts() { _filedir -d; }
_pkgctl_repo_configure_args=(
--protocol
@@ -348,19 +368,56 @@ _pkgctl_repo_switch_opts() {
fi
}
_pkgctl_license_cmds=(
check
setup
)
_pkgctl_license_check_args=(
-h --help
)
_pkgctl_license_check_opts() { _filedir -d; }
_pkgctl_license_setup_args=(
--no-check
-f --force
-h --help
)
_pkgctl_license_setup_opts() { _filedir -d; }
_pkgctl_version_cmds=(
check
setup
upgrade
)
_pkgctl_version_check_args=(
-v --verbose
-h --help
--json
-F --format
)
_pkgctl_version_check_opts() { _filedir -d; }
_pkgctl_version_check_args__format_opts() { _devtools_completions_version_output_format; }
_pkgctl_version_check_args_F_opts() { _devtools_completions_version_output_format; }
_pkgctl_version_setup_args=(
--prefer-platform-api
--url
--no-check
--no-upstream
-f --force
-h --help
)
_pkgctl_version_setup_opts() { _filedir -d; }
_pkgctl_version_setup_args__url_opts() { :; }
_pkgctl_version_upgrade_args=(
--no-update-checksums
-v --verbose
-h --help
)
@@ -409,6 +466,185 @@ _pkgctl_diff_args__pool_opts() { _filedir -d; }
_pkgctl_diff_args_P_opts() { _pkgctl_diff_args__pool_opts; }
_pkgctl_diff_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_cmds=(
close
comment
create
edit
list
move
reopen
view
)
_pkgctl_issue_args=(
-h --help
)
_pkgctl_issue_close_args=(
-p --package
-m --message
-e --edit
-r --resolution
-h --help
)
_pkgctl_issue_close_opts() { :; }
_pkgctl_issue_close_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_close_args_p_opts() { _pkgctl_issue_close_args__package_opts; }
_pkgctl_issue_close_args__message_opts() { :; }
_pkgctl_issue_close_args_m_opts() { _pkgctl_issue_close_args__message_opts; }
_pkgctl_issue_close_args__resolution_opts() { _devtools_completions_issue_resolution; }
_pkgctl_issue_close_args_r_opts() { _pkgctl_issue_close_args__resolution_opts; }
_pkgctl_issue_comment_args=(
-p --package
-m --message
-e --edit
-h --help
)
_pkgctl_issue_comment_opts() { :; }
_pkgctl_issue_comment_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_comment_args_p_opts() { _pkgctl_issue_comment_args__package_opts; }
_pkgctl_issue_comment_args__message_opts() { :; }
_pkgctl_issue_comment_args_m_opts() { _pkgctl_issue_comment_args__message_opts; }
_pkgctl_issue_create_args=(
-p --package
-t --title
-F --file
-e --edit
-w --web
--recover
--confidentiality
--priority
--scope
--severity
--status
-h --help
)
_pkgctl_issue_create_opts() { :; }
_pkgctl_issue_create_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_create_args_p_opts() { _pkgctl_issue_create_args__package_opts; }
_pkgctl_issue_create_args__title_opts() { :; }
_pkgctl_issue_create_args_t_opts() { _pkgctl_issue_create_args__title_opts; }
_pkgctl_issue_create_args__confidentiality_opts() { _devtools_completions_issue_confidentiality; }
_pkgctl_issue_create_args__priority_opts() { _devtools_completions_issue_priority; }
_pkgctl_issue_create_args__scope_opts() { _devtools_completions_issue_scope; }
_pkgctl_issue_create_args__severity_opts() { _devtools_completions_issue_severity; }
_pkgctl_issue_create_args__status_opts() { _devtools_completions_issue_status; }
_pkgctl_issue_edit_args=(
-p --package
-t --title
-e --edit
--recover
--confidentiality
--priority
--resolution
--scope
--severity
--status
-h --help
)
_pkgctl_issue_edit_opts() { :; }
_pkgctl_issue_edit_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_edit_args_p_opts() { _pkgctl_issue_edit_args__package_opts; }
_pkgctl_issue_edit_args__title_opts() { :; }
_pkgctl_issue_edit_args_t_opts() { _pkgctl_issue_edit_args__title_opts; }
_pkgctl_issue_edit_args__confidentiality_opts() { _devtools_completions_issue_confidentiality; }
_pkgctl_issue_edit_args__priority_opts() { _devtools_completions_issue_priority; }
_pkgctl_issue_edit_args__resolution_opts() { _devtools_completions_issue_resolution; }
_pkgctl_issue_edit_args__scope_opts() { _devtools_completions_issue_scope; }
_pkgctl_issue_edit_args__severity_opts() { _devtools_completions_issue_severity; }
_pkgctl_issue_edit_args__status_opts() { _devtools_completions_issue_status; }
_pkgctl_issue_list_args=(
-g --group
-w --web
-A --all
-c --closed
-U --unconfirmed
--search
--in
-l --label
--confidentiality
--priority
--resolution
--scope
--severity
--status
--assignee
--assigned-to-me
--author
--created-by-me
-h --help
)
_pkgctl_issue_list_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_list_args__search_opts() { :; }
_pkgctl_issue_list_args__in_opts() { _devtools_completions_issue_search_location; }
_pkgctl_issue_list_args__label_opts() { :; }
_pkgctl_issue_list_args_l_opts() { _pkgctl_issue_list_args__label_opts; }
_pkgctl_issue_list_args__confidentiality_opts() { _devtools_completions_issue_confidentiality; }
_pkgctl_issue_list_args__priority_opts() { _devtools_completions_issue_priority; }
_pkgctl_issue_list_args__resolution_opts() { _devtools_completions_issue_resolution; }
_pkgctl_issue_list_args__scope_opts() { _devtools_completions_issue_scope; }
_pkgctl_issue_list_args__severity_opts() { _devtools_completions_issue_severity; }
_pkgctl_issue_list_args__status_opts() { _devtools_completions_issue_status; }
_pkgctl_issue_list_args__assignee_opts() { :; }
_pkgctl_issue_list_args__author_opts() { :; }
_pkgctl_issue_move_args=(
-p --package
-m --message
-e --edit
-h --help
)
_pkgctl_issue_move_opts() {
local subcommand args
subcommand=(repo switch)
args=$(__pkgctl_word_count_after_subcommand "${subcommand[@]}")
if (( args == 0 )); then
:
elif (( args >= 1 )); then
_devtools_completions_all_packages
fi
}
_pkgctl_issue_move_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_move_args_p_opts() { _pkgctl_issue_move_args__package_opts; }
_pkgctl_issue_move_args__message_opts() { :; }
_pkgctl_issue_move_args_m_opts() { _pkgctl_issue_move_args__message_opts; }
_pkgctl_issue_reopen_args=(
-p --package
-m --message
-e --edit
-h --help
)
_pkgctl_issue_reopen_opts() { :; }
_pkgctl_issue_reopen_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_reopen_args_p_opts() { _pkgctl_issue_reopen_args__package_opts; }
_pkgctl_issue_reopen_args__message_opts() { :; }
_pkgctl_issue_reopen_args_m_opts() { _pkgctl_issue_reopen_args__message_opts; }
_pkgctl_issue_view_args=(
-p --package
-c --comments
-w --web
-h --help
)
_pkgctl_issue_view_opts() { :; }
_pkgctl_issue_view_args__package_opts() { _devtools_completions_all_packages; }
_pkgctl_issue_view_args_p_opts() { _pkgctl_issue_view_args__package_opts; }
_pkgctl_version_args=(
-h --help
@@ -421,6 +657,9 @@ _devtools_completions_color() {
_devtools_completions_arch() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ARCHES[*]}" -- "$cur")
}
_devtools_completions_binary_arch() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_BINARY_ARCHES[*]}" -- "$cur")
}
_devtools_completions_repo() {
local optional=${1:-}
mapfile -t COMPREPLY < <(compgen -W "${optional} ${DEVTOOLS_VALID_REPOS[*]}" -- "$cur")
@@ -440,6 +679,30 @@ _devtools_completions_inspect() {
_devtools_completions_search_format() {
mapfile -t COMPREPLY < <(compgen -W "${valid_search_output_format[*]}" -- "$cur")
}
_devtools_completions_version_output_format() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_VERSION_OUTPUT_FORMAT[*]}" -- "$cur")
}
_devtools_completions_issue_severity() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_SEVERITY[*]}" -- "$cur")
}
_devtools_completions_issue_status() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_STATUS[*]}" -- "$cur")
}
_devtools_completions_issue_scope() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_SCOPE[*]}" -- "$cur")
}
_devtools_completions_issue_search_location() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_SEARCH_LOCATION[*]}" -- "$cur")
}
_devtools_completions_issue_resolution() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_RESOLUTION[*]}" -- "$cur")
}
_devtools_completions_issue_priority() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_PRIORITY[*]}" -- "$cur")
}
_devtools_completions_issue_confidentiality() {
mapfile -t COMPREPLY < <(compgen -W "${DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[*]}" -- "$cur")
}
__devtools_complete() {
local service=$1

View File

@@ -13,8 +13,11 @@ source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-inspect.sh
# shellcheck source=src/lib/valid-search.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-search.sh
# shellcheck source=src/lib/valid-version.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-version.sh
# shellcheck source=src/lib/valid-issue.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-issue.sh
_binary_arch=${DEVTOOLS_VALID_ARCHES[*]:0:-1}
_colors=(never always auto)
_archbuild_args=(
@@ -41,8 +44,8 @@ _pkgctl_auth_status_args=(
)
_pkgctl_build_args=(
"--arch=[Specify architectures to build for (disables auto-detection)]:arch:($_arch[*])"
"--repo=[Specify a target repository (disables auto-detection)]:repo:($DEVTOOLS_VALID_REPOS[*])"
"--arch[Specify architectures to build for (disables auto-detection)]:arch:($DEVTOOLS_VALID_BINARY_ARCHES[*])"
"--repo[Specify a target repository (disables auto-detection)]:repo:($DEVTOOLS_VALID_REPOS[*])"
'(-s --staging)'{-s,--staging}'[Build against the staging counterpart of the auto-detected repo]'
'(-t --testing)'{-t,--testing}'[Build against the testing counterpart of the auto-detected repo]'
'(-o --offload)'{-o,--offload}'[Build on a remote server and transfer artifacts afterwards]'
@@ -58,7 +61,7 @@ _pkgctl_build_args=(
'--update-checksums[Force computation and update of the checksums (disables auto-detection)]'
'(-e --edit)'{-e,--edit}'[Edit the PKGBUILD before building]'
'(-r --release)'{-r,--release}'[Automatically commit, tag and release after building]'
'(-m --message=)'{-m,--message=}"[Use the given <msg> as the commit message]:message:"
'(-m --message)'{-m,--message}"[Use the given <msg> as the commit message]:message:"
'(-u --db-update)'{-u,--db-update}'[Automatically update the pacman database as last action]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'
@@ -79,7 +82,9 @@ _pkgctl_db_move_args=(
)
_pkgctl_db_remove_args=(
'(-a --arch=)'{-a,--arch=}"[Override the architecture (disables auto-detection)]:arch:($_arch[*])"
'--partial[Remove only partial pkgnames from a split package]'
'--noconfirm[Bypass any confirmation messages, should only be used with caution]'
'(-a --arch)'{-a,--arch}"[Override the architecture (disables auto-detection)]:arch:($DEVTOOLS_VALID_BINARY_ARCHES[*])"
'(-h --help)'{-h,--help}'[Display usage]'
"1:repo:($DEVTOOLS_VALID_REPOS[*])"
'*:pkgbase:_devtools_completions_all_packages'
@@ -89,9 +94,115 @@ _pkgctl_db_update_args=(
'(-h --help)'{-h,--help}'[Display usage]'
)
_pkgctl_issue_cmds=(
"pkgctl issue command"
"close[Close an issue]"
"comment[Comment on an issue]"
"create[Create a new issue]"
"edit[Edit and modify an issue]"
"list[List project or group issues]"
"move[Move an issue to another project]"
"reopen[Reopen a closed issue]"
"view[Display information about an issue]"
)
_pkgctl_issue_close_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-m --message)'{-m,--message}'[Use the provided message as the comment]:message:'
'(-e --edit)'{-e,--edit}'[Edit the comment using an editor]'
'(-r --resolution)'{-r,--resolution}"[Set a specific resolution label]:resolution:($DEVTOOLS_VALID_ISSUE_RESOLUTION[*])"
'(-h --help)'{-h,--help}'[Display usage]'
"1:issue_iid:"
)
_pkgctl_issue_comment_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-m --message)'{-m,--message}'[Use the provided message as the comment]:message:'
'(-e --edit)'{-e,--edit}'[Edit the comment using an editor]'
'(-h --help)'{-h,--help}'[Display usage]'
"1:issue_iid:"
)
_pkgctl_issue_create_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-t --title)'{-t,--title}'[Use the provided title for the issue]:title:'
'(-F --file)'{-F,--file}'[Take issue description from <file>]:file:_files'
'(-e --edit)'{-e,--edit}'[Edit the issue title and description using an editor]'
'(-w --web)'{-w,--web}'[Continue issue creation with the web interface]'
"--recover[Automatically recover from a failed run]"
"--confidentiality[Set the issue confidentiality]:confidential:($DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[*])"
"--priority[Set the priority label]:priority:($DEVTOOLS_VALID_ISSUE_PRIORITY[*])"
"--scope[Set the scope label]:scope:($DEVTOOLS_VALID_ISSUE_SCOPE[*])"
"--severity[Set the severity label]:severity:($DEVTOOLS_VALID_ISSUE_SEVERITY[*])"
"--status[Set the status label]:status:($DEVTOOLS_VALID_ISSUE_STATUS[*])"
'(-h --help)'{-h,--help}'[Display usage]'
)
_pkgctl_issue_edit_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-t --title)'{-t,--title}'[Use the provided title for the issue]:title:'
'(-e --edit)'{-e,--edit}'[Edit the issue title and description using an editor]'
"--recover[Automatically recover from a failed run]"
"--confidentiality[Set the issue confidentiality]:confidential:($DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[*])"
"--priority[Set the priority label]:priority:($DEVTOOLS_VALID_ISSUE_PRIORITY[*])"
"--resolution[Set the resolution label]:resolution:($DEVTOOLS_VALID_ISSUE_RESOLUTION[*])"
"--scope[Set the scope label]:scope:($DEVTOOLS_VALID_ISSUE_SCOPE[*])"
"--severity[Set the severity label]:severity:($DEVTOOLS_VALID_ISSUE_SEVERITY[*])"
"--status[Set the status label]:status:($DEVTOOLS_VALID_ISSUE_STATUS[*])"
'(-h --help)'{-h,--help}'[Display usage]'
"1:issue_iid:"
)
_pkgctl_issue_list_args=(
'(-g --group)'{-g,--group}'[Get issues from the whole packaging subgroup]'
'(-w --web)'{-w,--web}'[View results in a browser]'
'(-A --all)'{-A,--all}'[Get all issues including closed]'
'(-c --closed)'{-c,--closed}'[Get only closed issues]'
'(-U --unconfirmed)'{-U,--unconfirmed}'[Shorthand to filter by unconfirmed status label]'
'--search[Search in the fields defined by --in]:search:'
"--in[Search in title or description]:location:($DEVTOOLS_VALID_ISSUE_SEARCH_LOCATION[*])"
"--confidentiality[Filter by confidentiality]:confidential:($DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[*])"
"--priority[Shorthand to filter by priority label]:priority:($DEVTOOLS_VALID_ISSUE_PRIORITY[*])"
"--resolution[Shorthand to filter by resolution label]:resolution:($DEVTOOLS_VALID_ISSUE_RESOLUTION[*])"
"--scope[Shorthand to filter by scope label]:scope:($DEVTOOLS_VALID_ISSUE_SCOPE[*])"
"--severity[Shorthand to filter by severity label]:severity:($DEVTOOLS_VALID_ISSUE_SEVERITY[*])"
"--status[Shorthand to filter by status label]:status:($DEVTOOLS_VALID_ISSUE_STATUS[*])"
'--assignee[Filter issues assigned to the given username]:username:'
'--assigned-to-me[Shorthand to filter issues assigned to you]'
'--author[Filter issues authored by the given username]:username:'
'--created-by-me[Shorthand to filter issues created by you]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:pkgbase:_devtools_completions_all_packages'
)
_pkgctl_issue_move_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-m --message)'{-m,--message}'[Use the provided message as the comment]:message:'
'(-e --edit)'{-e,--edit}'[Edit the comment using an editor]'
'(-h --help)'{-h,--help}'[Display usage]'
"1:issue_iid:"
'1:pkgbase:_devtools_completions_all_packages'
)
_pkgctl_issue_reopen_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-m --message)'{-m,--message}'[Use the provided message as the comment]:message:'
'(-e --edit)'{-e,--edit}'[Edit the comment using an editor]'
'(-h --help)'{-h,--help}'[Display usage]'
"1:issue_iid:"
)
_pkgctl_issue_view_args=(
'(-p --package)'{-p,--package}'[Interact with <pkgbase> instead of the current directory]:pkgbase:_devtools_completions_all_packages'
'(-c --comments)'{-c,--comments}'[Show issue comments and activities]'
'(-w --web)'{-w,--web}'[View results in a browser]'
'(-h --help)'{-h,--help}'[Display usage]'
"1:issue_iid:"
)
_pkgctl_release_args=(
'(-m --message=)'{-m,--message=}"[Use the given <msg> as the commit message]:message:"
'(-r --repo=)'{-r,--repo=}"[Specify a target repository for new packages]:repo:($DEVTOOLS_VALID_REPOS[*])"
'(-m --message)'{-m,--message}"[Use the given <msg> as the commit message]:message:"
'(-r --repo)'{-r,--repo}"[Specify a target repository for new packages]:repo:($DEVTOOLS_VALID_REPOS[*])"
'(-s --staging)'{-s,--staging}'[Release to the staging counterpart of the auto-detected repo]'
'(-t --testing)'{-t,--testing}'[Release to the testing counterpart of the auto-detected repo]'
'(-u --db-update)'{-u,--db-update}'[Automatically update the pacman database after uploading]'
@@ -113,6 +224,7 @@ _pkgctl_aur_drop_from_repo_args=(
_pkgctl_repo_cmds=(
"pkgctl repo command"
"clean[Remove untracked files from the working tree]"
"clone[Clone a package repository]"
"configure[Configure a clone according to distro specs]"
"create[Create a new GitLab package repository]"
@@ -127,6 +239,13 @@ _pkgctl_repo_switch_args=(
'*:git_dir:_files -/'
)
_pkgctl_repo_clean_args=(
'(-i --interactive)'{-i,--interactive}'[Show what would be done and clean files interactively]'
'(-n --dry-run)'{-n,--dry-run}"[Don't remove anything, just show what would be done]"
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'
)
_pkgctl_repo_clone_args=(
'(-m --maintainer=)'{-m,--maintainer=}'[Clone all packages of the named maintainer]:maintainer:'
'--protocol[Clone the repository over https]:proto:(https)'
@@ -213,6 +332,7 @@ _makechrootpkg_args=(
'-c[Clean the chroot before building]'
'*-d[Bind directory into build chroot as read-write]:bind_dir_rw:_files -/'
'*-D[Bind directory into build chroot as read-only]:bind_dir_ro:_files -/'
'*-t[Mount a tmpfs at directory]:tmpfs_dir:_files -/'
'-u[Update the working copy of the chroot before building]'
'-r[The chroot dir to use]:chroot_dir:_files -/'
'*-I[Install a package into the working copy]:target:_files -g "*.pkg.tar.*(.)"'
@@ -228,6 +348,7 @@ _mkarchroot_args=(
'-C[Location of a pacman config file]:pacman_config:_files -g "*.conf(.)"'
'-M[Location of a makepkg config file]:makepkg_config:_files -g "*.conf(.)"'
'-c[Set pacman cache]:pacman_cache:_files -/'
'-f[Copy src file from the host to the chroot]:target:_files -/'
'-h[Display usage]'
'1:working_dir:_files -/'
'*:packages:_devtools_completions_all_packages'
@@ -250,7 +371,7 @@ _sogrep_args=(
_offload_build_args=(
'(-r --repo)'{-r,--repo}'[Build against a specific repository]:repo:($DEVTOOLS_VALID_BUILDREPOS[*])'
'(-a --arch)'{-a,--arch}'[Build against a specific architecture]:arch:(${_binary_arch[*]})'
'(-a --arch)'{-a,--arch}'[Build against a specific architecture]:arch:(${DEVTOOLS_VALID_BINARY_ARCHES[*]})'
'(-s --server)'{-s,--server}'[Offload to a specific Build server]:server:'
'(-h --help)'{-h,--help}'[Display usage]'
)
@@ -277,6 +398,8 @@ _pkgctl_cmds=(
"build[Build packages inside a clean chroot]"
"db[Pacman database modification for package update, move etc]"
"diff[Compare package files using different modes]"
"issue[Work with GitLab packaging issues]"
"license[Check and manage package license compliance]"
"release[Release step to commit, tag and upload build artifacts]"
"repo[Manage Git packaging repositories and their configuration]"
"search[Search for an expression across the GitLab packaging group]"
@@ -288,19 +411,51 @@ _pkgctl_args=(
'(-h --help)'{-h,--help}'[Display usage]'
)
_pkgctl_license_cmds=(
"pkgctl license command"
"check[Checks package licensing compliance using REUSE]"
"setup[Automatically detect and setup a basic REUSE config]"
)
_pkgctl_license_check_args=(
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'
)
_pkgctl_license_setup_args=(
'(-f --force)'{-f,--force}'[Overwrite existing REUSE config]'
'--no-check[Do not run license check after setup]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'
)
_pkgctl_version_cmds=(
"pkgctl version command"
"check[Compares local package versions against upstream versions]"
"setup[Automatically detect and setup a basic nvchecker config]"
"upgrade[Adjust the PKGBUILD to match the latest upstream version]"
)
_pkgctl_version_check_args=(
'(-v --verbose)'{-v,--verbose}'[Display results including up-to-date versions]'
'(-h --help)'{-h,--help}'[Display usage]'
'(-v --verbose)'{-v,--verbose}'[Display all results including up-to-date versions]'
'--json[Enable printing results in JSON]'
'(-F --format)'{-F,--format}"[Controls the output format of the results]:format:($DEVTOOLS_VALID_VERSION_OUTPUT_FORMAT[*])"
'*:git_dir:_files -/'
)
_pkgctl_version_setup_args=(
'(-f --force)'{-f,--force}'[Do not prompt before overwriting]'
'--prefer-platform-api[Prefer platform specific GitHub/GitLab API for complex cases]'
'--url[Derive check target from URL instead of source array]:url:'
'--no-check[Do not run version check after setup]'
'--no-upstream[Setup a blank config for packages without upstream sources]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'
)
_pkgctl_version_upgrade_args=(
'--no-update-checksums[Disable computation and update of the checksums]'
'(-v --verbose)'{-v,--verbose}'[Display results including up-to-date versions]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'

12
data/LICENSE Normal file
View File

@@ -0,0 +1,12 @@
Copyright Arch Linux Contributors
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -23,13 +23,15 @@ Options
Location of a pacman config file
*-M* <file>::
Location of a makepkg config file
Location of a makepkg config file. Specific additions (e.g. build flags for
additional languages) can be placed in '<file>.d/*.conf'.
*-c* <dir>::
Set pacman cache, if no directory is specified the passed pacman.conf's cachedir is used with a fallback to '/etc/pacman.conf'
*-f* <file>::
Copy file from the host to the chroot
*-f* <src>[:<dst>]::
Copy file from the host to the chroot.
If 'dst' is not provided, it defaults to 'src' inside of the chroot.
*-s*::
Do not run setarch

View File

@@ -45,6 +45,9 @@ Options
*-D* <dir>::
Bind directory into build chroot as read-only
*-t* <dir>[:opts]::
Mount a tmpfs at 'dir'. See the '--tmpfs' argument in systemd-nspawn(1) for more details.
*-u*::
Update the working copy of the chroot before building
This is useful for rebuilds without dirtying the pristine
@@ -76,5 +79,9 @@ Options
*-x* <when>::
Inspect chroot after build, possible modes are 'never' (default), 'always' or 'failure'
See Also
--------
systemd-nspawn(1)
include::include/footer.asciidoc[]

View File

@@ -49,7 +49,8 @@ Options
Set the pacman cache directory.
*-M* <file>::
Location of a makepkg config file.
Location of a makepkg config file. Specific additions (e.g. build flags for
additional languages) can be placed in '<file>.d/*.conf'.
*-l* <chroot>::
The directory name to use as the chroot namespace

View File

@@ -32,8 +32,9 @@ Options
*-c* <dir>::
Set pacman cache.
*-f* <file>::
*-f* <src>[:<dst>]::
Copy file from the host to the chroot.
If 'dst' is not provided, it defaults to 'src' inside of the chroot.
*-s*::
Do not run setarch.

View File

@@ -12,7 +12,10 @@ pkgctl build [OPTIONS] [PATH...]
Description
-----------
TODO
Build packages in clean chroot environment, offering various options
and functionalities to customize the package building process.
By default, chroot environments are located in '/var/lib/archbuild/'.
Build Options
-------------

View File

@@ -12,13 +12,27 @@ pkgctl db remove [OPTIONS] [REPO] [PKGBASE]...
Description
-----------
Remove packages from pacman repositories.
Remove packages from pacman repositories. By default passing a pkgbase removes
all split packages, debug packages as well as entries from the state repo for
all existing architectures.
Beware when using the `--partial` option, as it may most likely lead to
undesired effects by leaving debug packages behind as well as dangling entries
in the state repository.
Options
-------
*--partial*::
Remove only partial pkgnames from a split package. This leaves debug
packages behind and pkgbase entries in the state repo.
*-a, --arch* 'ARCH'::
Override the architecture (disables auto-detection)
Remove only one specific architecture (disables auto-detection).
By default all architectures are removed when this option is not used.
*--noconfirm*::
Bypass any confirmation messages, should only be used with caution.
*-h, --help*::
Show a help text

View File

@@ -0,0 +1,43 @@
pkgctl-db(1)
============
NAME
----
pkgctl-db - Pacman database modification utility for package updates, moves, and more.
SYNOPSIS
--------
pkgctl db [OPTIONS] [SUBCOMMAND]
DESCRIPTION
-----------
Managing the Pacman database and facilitate the modification of packages and their metadata
within the database
OPTIONS
-------
*-h, --help*::
Display usage information and available options.
Subcommands
-----------
pkgctl db update::
Update the binary repository as final release step
pkgctl db move::
Move packages between binary repositories
pkgctl db remove::
Remove packages from binary repositories
SEE ALSO
--------
pkgctl-db-update(1)
pkgctl-db-move(1)
pkgctl-db-remove(1)
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,47 @@
pkgctl-issue-close(1)
=====================
Name
----
pkgctl-issue-close - Close an issue
Synopsis
--------
pkgctl issue close [OPTIONS] [IID]
Description
-----------
This command is used to close an issue in Arch Linux packaging projects. It
finalizes the issue by marking it as resolved and optionally providing a reason
for its closure.
To edit an issue, users must specify the issue ID (IID). By default, the
command operates within the current directory, but users have the option to
specify a different package base.
Users can provide a message directly through the command line to explain the
reason for closing the issue. For more detailed or precise reasons, users can
opt to edit the closure message using a text editor before submitting it.
Additionally, a specific resolution label can be set to categorize the closure
reason, with the default label being "completed."
Options
-------
*-p, --package* 'PKGBASE'::
Interact with `PKGBASE` instead of the current directory
*-m, --message* 'MSG'::
Use the provided message as the reason for closing
*-e, --edit*::
Edit the reason for closing using an editor
*-r, --resolution* 'REASON'::
Set a specific resolution label (default: completed)
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,43 @@
pkgctl-issue-comment(1)
=======================
Name
----
pkgctl-issue-comment - Comment on an issue
Synopsis
--------
pkgctl issue comment [OPTIONS] [IID]
Description
-----------
This command allows users to add comments to an issue in Arch Linux packaging
projects. This command is useful for providing feedback, updates, or any
additional information related to an issue directly within the project's issue
tracking system.
By default, the command interacts with the current directory, but users can
specify a different package base if needed.
Users can provide a comment message directly through the command line, ensuring
quick and efficient communication. Additionally, for more detailed or formatted
comments, users have the option to edit their comment using a text editor
before submitting it.
Options
-------
*-p, --package PKGBASE*::
Interact with `PKGBASE` instead of the current directory
*-m, --message MSG*::
Use the provided message as the comment
*-e, --edit*::
Edit the comment using an editor
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,77 @@
pkgctl-issue-create(1)
======================
Name
----
pkgctl-issue-create - Create a new issue
Synopsis
--------
pkgctl issue create [OPTIONS]
Description
-----------
The create command is used to create a new issue for an Arch Linux package.
This command is suitable for reporting bugs, regressions, feature requests, or
any other issues related to a package. It provides a flexible way to document
and track new issues within the project's issue tracking system.
By default, the command operates within the current directory, but users can
specify a different package base if needed.
Users can provide a title for the issue directly through the command line. The
issue description can be supplied from a file or edited using a text editor.
Alternatively, users can opt to continue the issue creation process using the
web interface for a more interactive experience.
The command allows setting various labels and attributes for the issue, such as
confidentiality, priority, scope, severity, and status. These options help
categorize and prioritize the issue appropriately within the tracking system.
In case of a failed run, the command can automatically recover to ensure that
the issue creation process is completed without losing any data.
This command is essential for maintainers, contributors, and users who need to
report new issues related to Arch Linux packages.
Options
-------
*-p, --package* 'PKGBASE'::
Interact with `PKGBASE` instead of the current directory
*-t, --title* 'TITLE'::
Use the provided title for the issue
*-F, --file* 'FILE'::
Take issue description from <file>
*-e, --edit*::
Edit the issue description using an editor
*-w, --web*::
Continue issue creation with the web interface
*--recover*::
Automatically recover from a failed run
*--confidentiality* 'TYPE'::
Set the issue confidentiality
*--priority* 'PRIORITY'::
Set the priority label
*--scope* 'SCOPE'::
Set the scope label
*--severity* 'SEVERITY'::
Set the severity label
*--status* 'STATUS'::
Set the status label
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,75 @@
pkgctl-issue-edit(1)
====================
Name
----
pkgctl-issue-edit - Edit and modify an issue
Synopsis
--------
pkgctl issue edit [OPTIONS] [IID]
Description
-----------
The pkgctl issue edit command is used to modify an existing issue in Arch Linux
packaging projects. This command allows users to update the issue's title,
description, and various attributes, ensuring that the issue information
remains accurate and up-to-date. It also provides a streamlined facility
for bug wranglers to categorize and prioritize issues efficiently.
To edit an issue, users must specify the issue ID (IID). By default, the
command operates within the current directory, but users can specify a
different package base if needed.
The command allows for direct updates to the issue title and description. For
more extensive changes, users can edit these details using a text editor. The
command provides various options to set or update labels and attributes such as
confidentiality, priority, resolution, scope, severity, and status. These
options help maintain clear and organized issue management.
In case of a failed run, the command can automatically recover to ensure that
the editing process is completed without losing any data.
This command is particularly useful for maintainers and contributors who need
to update the details of an issue to reflect new information or changes in
status. It ensures that all issue details are accurately maintained,
facilitating efficient tracking and resolution.
Options
-------
*-p, --package* 'PKGBASE'::
Interact with `PKGBASE` instead of the current directory
*-t, --title* 'TITLE'::
Use the provided title for the issue
*-e, --edit*::
Edit the issue title and description using an editor
*--recover*::
Automatically recover from a failed run
*--confidentiality* 'TYPE'::
Set the issue confidentiality
*--priority* 'PRIORITY'::
Set the priority label
*--resolution* 'REASON'::
Set the resolution label
*--scope* 'SCOPE'::
Set the scope label
*--severity* 'SEVERITY'::
Set the severity label
*--status* 'STATUS'::
Set the status label
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,100 @@
pkgctl-issue-list(1)
====================
Name
----
pkgctl-issue-list - List project or group issues
Synopsis
--------
pkgctl issue list [OPTIONS] [PKGBASE]
Description
-----------
The pkgctl issue list command is used to list issues associated with a specific
packaging project or the entire packaging subgroup in Arch Linux. This command
facilitates efficient issue management by allowing users to list and filter
issues based on various criteria.
Results can also be displayed directly in a web browser for easier navigation
and review.
The command offers filtering options to refine the results. Users can include
closed issues, filter exclusively for unconfirmed issues, or focus on issues
with specific labels such as priority, confidentiality, resolution, scope,
severity, and status.
Additionally, users can search within issue titles or descriptions and filter
issues by the assignee or author. There are also convenient shortcuts to filter
issues assigned to or created by the current user.
This command is particularly useful for package maintainers and contributors in
the Arch Linux community who need to track and manage issues efficiently. It
provides a comprehensive view of the project's or group's issue landscape,
enabling maintainers to address and prioritize issues effectively.
Options
-------
*-g, --group*::
Get issues from the whole packaging subgroup
*-w, --web*::
View results in a browser
*-h, --help*::
Show a help text
Filter Options
--------------
*-A, --all*::
Get all issues including closed
*-c, --closed*::
Get only closed issues
*-U, --unconfirmed*::
Shorthand to filter by unconfirmed status label
*--search* 'SEARCH'::
Search <string> in the fields defined by --in
*--in* 'LOCATION'::
Search in title or description (default: all)
*-l, --label* 'NAME'::
Filter issue by label <name>
*--confidentiality* 'TYPE'::
Filter by confidentiality
*--priority* 'PRIORITY'::
Shorthand to filter by priority label
*--resolution* 'REASON'::
Shorthand to filter by resolution label
*--scope* 'SCOPE'::
Shorthand to filter by scope label
*--severity* 'SEVERITY'::
Shorthand to filter by severity label
*--status* 'STATUS'::
Shorthand to filter by status label
*--assignee* 'USERNAME'::
Filter issues assigned to the given username
*--assigned-to-me*::
Shorthand to filter issues assigned to you
*--author* 'USERNAME'::
Filter issues authored by the given username
*--created-by-me*::
Shorthand to filter issues created by you
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,43 @@
pkgctl-issue-move(1)
====================
Name
----
pkgctl-issue-move - Move an issue to another project
Synopsis
--------
pkgctl issue move [OPTIONS] [IID] [DESTINATION_PACKAGE]
Description
-----------
The move command allows users to transfer an issue from one project to another
within the Arch Linux packaging group. This is useful when an issue is
identified to be more relevant or better handled in a different project.
By default, the command operates within the current directory, but users can
specify a different package base from which to move the issue.
Users must specify the issue ID (IID) and the destination package to which the
issue should be moved. A comment message explaining the reason for the move can
be provided directly through the command line. For more detailed explanations
or additional context, users have the option to edit the move comment using a
text editor before submitting it.
Options
-------
*-p, --package* 'PKGBASE'::
Move from `PKGBASE` instead of the current directory
*-m, --message* 'MSG'::
Use the provided message as the comment
*-e, --edit*::
Edit the comment using an editor
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,43 @@
pkgctl-issue-reopen(1)
======================
Name
----
pkgctl-issue-reopen - Reopen a closed issue
Synopsis
--------
pkgctl issue reopen [OPTIONS] [IID]
Description
-----------
The reopen command is used to reopen a previously closed issue in Arch Linux
packaging projects. This command is useful when an issue needs to be revisited
or additional work is required after it was initially closed.
To edit an issue, users must specify the issue ID (IID). By default, the
command operates within the current directory, but users can specify a
different package base if needed.
Users can provide a message directly through the command line to explain the
reason for reopening the issue. For more detailed explanations or to provide
additional context, users have the option to edit the reopening comment using a
text editor before submitting it.
Options
-------
*-p, --package* 'PKGBASE'::
Interact with `PKGBASE` instead of the current directory
*-m, --message* 'MSG'::
Use the provided message as the comment
*-e, --edit*::
Edit the comment using an editor
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,43 @@
pkgctl-issue-view(1)
====================
Name
----
pkgctl-issue-view - Display information about an issue
Synopsis
--------
pkgctl issue view [OPTIONS]
Description
-----------
This command is designed to display detailed information about a specific issue
in Arch Linux packaging projects. It gathers and pretty prints all relevant
data about the issue, providing a comprehensive view that includes the issue's
description, status as well as labels and creation date.
By default, the command operates within the current directory, but users have
the option to specify a different package base. Additionally, users can choose
to view the issue in a web browser for a more interactive experience.
For those requiring deeper insights, the command can also display all comments
and activities related to the issue, providing a full historical context and
ongoing discussions.
Options
-------
*-p, --package* 'PKGBASE'::
Interact with `PKGBASE` instead of the current directory
*-c, --comments*::
Show issue comments and activities
*-w, --web*::
Open issue in a browser
*-h, --help*::
Show a help text
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,62 @@
pkgctl-issue(1)
===============
Name
----
pkgctl-issue - Work with GitLab packaging issues
Synopsis
--------
pkgctl issue [SUBCOMMAND] [OPTIONS]
Description
-----------
Work with GitLab packaging issues.
Options
-------
*-h, --help*::
Show a help text
Subcommands
-----------
pkgctl issue close::
Close an issue
pkgctl issue comment::
Comment on an issue
pkgctl issue create::
Create a new issue
pkgctl issue edit::
Edit and modify an issue
pkgctl issue list::
List project or group issues
pkgctl issue move::
Move an issue to another project
pkgctl issue reopen::
Reopen a closed issue
pkgctl issue view::
Display information about an issue
See Also
--------
pkgctl-issue-close(1)
pkgctl-issue-comment(1)
pkgctl-issue-create(1)
pkgctl-issue-edit(1)
pkgctl-issue-list(1)
pkgctl-issue-move(1)
pkgctl-issue-reopen(1)
pkgctl-issue-view(1)
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,54 @@
pkgctl-license-check(1)
=======================
Name
----
pkgctl-license-check - Checks package licensing compliance using REUSE
Synopsis
--------
pkgctl license check [OPTIONS] [PKGBASE...]
Description
-----------
Checks package licensing compliance using REUSE and also verifies whether
a LICENSE file with the expected Arch Linux-specific 0BSD license text exists.
Configuration
-------------
Uses reuse(1) and a `REUSE.toml` file located alongside the PKGBUILD(5). Refer
to the configuration section in pkgctl-license(1).
If no `PKGBASE` is specified, the command defaults to using the current working
directory.
Options
-------
*-h, --help*::
Show a help text
Exit Codes
----------
On exit, return one of the following codes:
*0*::
Normal exit condition, all checked packages are compliant
*1*::
Unknown cause of failure
*2*::
Normal exit condition, but some packages are not compliant
See Also
--------
pkgctl-license(1)
reuse(1)
PKGBUILD(5)
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,55 @@
pkgctl-license-setup(1)
=======================
Name
----
pkgctl-license-setup - Automatically detect and setup a basic REUSE
configuration
Synopsis
--------
pkgctl license setup [OPTIONS] [PKGBASE...]
Description
-----------
This subcommand automates the creation of the Arch Linux 0BSD package license
file as well as a basic reuse(1) configuration by applying simple heuristics.
It comes in especially handy when initially setting up licensing for a package
without the need to manually write a `REUSE.toml` file.
If any `.patch` files are detected and the PKGBUILD(5) has only a single entry
in the `license=()` array, this subcommand assumes the patches are licensed
under that license and generates annotations for them.
In case there are no patches, no additional annotations are generated.
Manual annotations are necessary in case the subcommand can't generate a
configuration that accounts for all files. In this case, `reuse lint` will fail
with a descriptive error of which files are missing an annotation.
If no `PKGBASE` is specified, the command defaults to using the current working
directory.
Options
-------
*-f, --force*::
Overwrite existing reuse(1) configuration
*--no-check*::
Do not run pkgctl-license-check(1) after setup
*-h, --help*::
Show a help text
See Also
--------
pkgctl-license(1)
pkgctl-license-check(1)
reuse(1)
PKGBUILD(5)
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,54 @@
pkgctl-license(1)
=================
Name
----
pkgctl-license - Check and manage package license compliance
Synopsis
--------
pkgctl license [OPTIONS] [SUBCOMMAND]
Description
-----------
Commands related to package licenses, including checks for compliance.
Uses reuse(1) and a `REUSE.toml` file located alongside the PKGBUILD(5).
Configuration
-------------
The `REUSE.toml` file must contain annotations for all regular files expected
to be present in an Arch Linux package repository.
Use pkgctl-license-setup(1) to automatically detect and setup a basic REUSE
config file based on the files in the package repository.
For detailed information on the various configuration options available for the
`REUSE.toml` file, refer to the REUSE Specification (https://reuse.software/spec).
Options
-------
*-h, --help*::
Show a help text
Subcommands
-----------
pkgctl license check::
Checks package licensing compliance using REUSE
pkgctl license setup::
Automatically detect and setup a basic REUSE config
See Also
--------
pkgctl-license-check(1)
pkgctl-license-setup(1)
reuse(1)
PKGBUILD(5)
include::include/footer.asciidoc[]

View File

@@ -0,0 +1,40 @@
pkgctl-repo-clean(1)
====================
Name
----
pkgctl-repo-clean - Remove untracked files from the working tree
Synopsis
--------
pkgctl repo clean [OPTION] [PATH]...
Description
-----------
Cleans the working tree by recursively removing files that are not under
version control, starting from the current directory.
Files unknown to Git as well as ignored files are removed. This can, for
example, be useful to remove all build products.
Options
-------
*-i, --interactive*::
Show what would be done and clean files interactively
*-n, --dry-run*::
Don't actually remove anything, just show what would be done
*-h, --help*::
Show a help text
See Also
--------
git-clean(1)
include::include/footer.asciidoc[]

View File

@@ -32,6 +32,9 @@ Options
Subcommands
-----------
pkgctl repo clean::
Remove untracked files from the working tree
pkgctl repo clone::
Clone a package repository
@@ -50,6 +53,7 @@ pkgctl repo web::
See Also
--------
pkgctl-repo-clean(1)
pkgctl-repo-clone(1)
pkgctl-repo-configure(1)
pkgctl-repo-create(1)

View File

@@ -34,12 +34,25 @@ PKGBUILD. Refer to the configuration section in pkgctl-version(1).
Options
-------
*-v, --verbose*::
Display results including up-to-date versions
*-h, --help*::
Show a help text
Filter Options
--------------
*-v, --verbose*::
Display all results including up-to-date versions
Output Options
--------------
*--json*::
Enable printing in JSON; Shorthand for `'--format json'`
*-F, --format* 'FORMAT'::
Controls the output format of the results; `FORMAT` is `'pretty'`,
or `'json'` (default `pretty`)
Exit Codes
----------

View File

@@ -0,0 +1,125 @@
pkgctl-version-setup(1)
=======================
Name
----
pkgctl-version-setup - Automatically detect and setup a basic nvchecker config
Synopsis
--------
pkgctl version setup [OPTIONS] [PKGBASE...]
Description
-----------
This subcommand automates the creation of a basic nvchecker(1) configuration
file by analyzing the source array specified in the PKGBUILD(1) file of a
package. This command intelligently detects various platforms and APIs (e.g.,
GitHub, GitLab, PyPI) used by the package sources and generates a corresponding
`.nvchecker.toml` configuration based on its best guess.
This is particularly useful for initializing nvchecker(1) settings for a
package without manually crafting the `.nvchecker.toml` file. It simplifies the
process of setting up version checks, especially when transitioning a package's
monitoring from one source platform to another or starting version checks for a
new package.
If no `PKGBASE` is specified, the command defaults to using the current working
directory.
To obtain a list of supported sources and their expected URL formats, please
consult the sources section.
Options
-------
*-f, --force*::
Overwrite existing nvchecker(1) configuration
*--prefer-platform-api*::
Prefer platform specific GitHub/GitLab API over git for complex cases
*--url* 'URL'::
Derive check target from the given URL instead of the source array entries
*--no-check*::
Do not run pkgctl-version-check(1) after setup
*--no-upstream*::
Setup a blank config for packages without upstream sources, like meta
packages. This must only be used for cases without an upstream, please
reach out to the developer team for guidance regarding upstream sources
that are hard to configure.
*-h, --help*::
Show a help text
Sources
-------
Here are the currently supported platforms and sources, along with examples of
URL formats that enable their automatic detection as specific source types:
*Git*::
* https://github.com/example/project
* https://gitlab.com/example/group/project
* git://git.foobar.org/example
* git+https://git.foobar.org/example
*GitHub*::
* https://github.com/example/project
* https://github.com/example/project/archive/v1.0/project-v1.0.tar.gz
*GitLab*::
* https://gitlab.com/example/group/project
* https://gitlab.archlinux.org/archlinux/devtools.git
* https://gitlab.archlinux.org/archlinux/devtools/-/releases/v1.1.0/downloads/devtools-v1.1.0.tar.gz
*Hackage*::
* https://hackage.haskell.org/package/xmonad
* https://hackage.haskell.org/package/xmonad-0.18.0/xmonad-0.18.0.tar.gz
* https://hackage.haskell.org/packages/archive/xmonad/0.18.0/xmonad-0.18.0.tar.gz
*NPM*::
* https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz
* https://www.npmjs.com/package/node-gyp
*PyPI*::
* https://pypi.io/packages/source/p/pyflakes
* https://pypi.org/packages/source/b/bleach
* https://files.pythonhosted.org/packages/source/p/pyflakes
* https://pypi.org/project/SQLAlchemy/
*RubyGems*::
* https://rubygems.org/downloads/diff-lcs-1.5.1.gem
* https://rubygems.org/gems/diff-lcs
*CPAN*::
* https://search.cpan.org/CPAN/authors/id/C/CO/COSIMO/Locale-PO-0.27.tar.gz
* https://cpan.metacpan.org/authors/id/C/CO/COSIMO/Locale-PO-0.27.tar.gz
*crates.io*::
* https://static.crates.io/crates/shotgun/shotgun-1.0.crate
* https://crates.io/api/v1/crates/shotgun/1.0/download
* https://crates.io/crates/git-smash
Examples
--------
*pkgctl version setup*::
Detects the source from the current directory's PKGBUILD(1) and
sets up a basic `.nvchecker.toml`.
*pkgctl version setup --url https://github.com/example/project*::
Generates an `.nvchecker.toml` for the current PKGBUILD(1) but
overrides the source URL with the specified GitHub project.
See Also
--------
pkgctl-version(1)
pkgctl-version-check(1)
nvchecker(1)
PKGBUILD(5)
include::include/footer.asciidoc[]

View File

@@ -18,7 +18,7 @@ upstream versions.
Upon execution, it automatically adjusts the PKGBUILD file, ensuring that the
pkgver field is set to match the latest version available from the upstream
source. In addition to updating the pkgver, this command also resets the pkgrel
to 1.
to 1 and updates checksums.
Outputs a summary of upgraded packages, up-to-date packages, and any check
failures.
@@ -35,6 +35,9 @@ PKGBUILD. Refer to the configuration section in pkgctl-version(1).
Options
-------
*--no-update-checksums*::
Disable computation and update of the checksums
*-v, --verbose*::
Display results including up-to-date versions

View File

@@ -26,6 +26,9 @@ package's pkgbase. The pkgbase section within the `.nvchecker.toml` file
specifies the source and method for checking the latest version of the
corresponding package.
Use pkgctl-version-setup(1) to automatically detect and setup a basic nvchecker
config based on the source array of the package PKGBUILD.
For detailed information on the various configuration options available for the
`.nvchecker.toml` file, refer to the configuration files section in
nvchecker(1). This documentation provides insights into the possible
@@ -48,6 +51,9 @@ Subcommands
pkgctl version check::
Compares local package versions against upstream
pkgctl version setup::
Automatically detect and setup a basic nvchecker config
pkgctl version upgrade::
Adjust the PKGBUILD to match the latest upstream version
@@ -55,6 +61,7 @@ See Also
--------
pkgctl-version-check(1)
pkgctl-version-setup(1)
pkgctl-version-upgrade(1)
include::include/footer.asciidoc[]

View File

@@ -12,7 +12,12 @@ pkgctl [SUBCOMMAND] [OPTIONS]
Description
-----------
TODO
Command-line utility serving as a unified interface for multiple development tools.
This tool aims to simplify and optimize interactions with devtools by offering
various subcommands for executing tasks related to package management, repository management,
version control, among others.
Utilizing pkgctl enables users to efficiently administer their development workflows.
Options
-------
@@ -41,6 +46,9 @@ pkgctl db::
pkgctl diff::
Compare package files using different modes
pkgctl issue::
Work with GitLab packaging issues
pkgctl release::
Release step to commit, tag and upload build artifacts
@@ -61,6 +69,7 @@ pkgctl-auth(1)
pkgctl-build(1)
pkgctl-db(1)
pkgctl-diff(1)
pkgctl-issue(1)
pkgctl-release(1)
pkgctl-repo(1)
pkgctl-search(1)

View File

@@ -22,12 +22,13 @@ usage() {
echo "A wrapper around systemd-nspawn. Provides support for pacman."
echo
echo ' options:'
echo ' -C <file> Location of a pacman config file'
echo ' -M <file> Location of a makepkg config file'
echo ' -c <dir> Set pacman cache'
echo ' -f <file> Copy file from the host to the chroot'
echo ' -s Do not run setarch'
echo ' -h This message'
echo ' -C <file> Location of a pacman config file'
echo ' -M <file> Location of a makepkg config file'
echo ' -c <dir> Set pacman cache'
echo ' -f <src>[:<dst>] Copy src file from the host to the chroot.'
echo ' If dst file is not provided, it defaults to src'
echo ' -s Do not run setarch'
echo ' -h This message'
exit 1
}
@@ -63,6 +64,8 @@ nspawn_args=(
--slice="devtools-$(systemd-escape "${SUDO_USER:-$USER}")"
--machine="arch-nspawn-$$"
--as-pid2
--console=autopipe
--timezone=off
)
if (( ${#cache_dirs[@]} == 0 )); then
@@ -71,6 +74,7 @@ fi
# shellcheck disable=2016
host_mirrors=($(pacman-conf --repo extra Server 2> /dev/null | sed -r 's#(.*/)extra/os/.*#\1$repo/os/$arch#'))
host_cachemirrors=($(pacman-conf --repo extra CacheServer 2> /dev/null | sed -r 's#(.*/)extra/os/.*#\1$repo/os/$arch#'))
for host_mirror in "${host_mirrors[@]}"; do
if [[ $host_mirror == *file://* ]]; then
@@ -105,13 +109,23 @@ copy_hostconf () {
printf 'Server = %s\n' "${host_mirrors[@]}" >"$working_dir/etc/pacman.d/mirrorlist"
[[ -n $host_cachemirrors ]] && printf 'CacheServer = %s\n' "${host_cachemirrors[@]}" >>"$working_dir/etc/pacman.d/mirrorlist"
[[ -n $pac_conf ]] && cp "$pac_conf" "$working_dir/etc/pacman.conf"
[[ -n $makepkg_conf ]] && cp "$makepkg_conf" "$working_dir/etc/makepkg.conf"
if [[ -n $makepkg_conf ]]; then
cp "$makepkg_conf" "$working_dir/etc/makepkg.conf"
if [[ -d "${makepkg_conf}.d" ]] && is_globfile "${makepkg_conf}.d"/*.conf; then
mkdir --parents "$working_dir/etc/makepkg.conf.d/"
cp "${makepkg_conf}.d/"*.conf "$working_dir/etc/makepkg.conf.d/"
fi
fi
local file
for file in "${files[@]}"; do
mkdir -p "$(dirname "$working_dir$file")"
cp -T "$file" "$working_dir$file"
src="${file%%:*}"
dst="${file#*:}"
mkdir -p "$(dirname "$working_dir$dst")"
cp -T "$src" "$working_dir$dst"
done
sed -r "s|^#?\\s*CacheDir.+|CacheDir = ${cache_dirs[*]}|g" -i "$working_dir/etc/pacman.conf"

View File

@@ -79,6 +79,13 @@ check_root SOURCE_DATE_EPOCH,SRCDEST,SRCPKGDEST,PKGDEST,LOGDEST,MAKEFLAGS,PACKAG
# Pass all arguments after -- right to makepkg
makechrootpkg_args+=("${@:$OPTIND}")
# Automatically recreate the root chroot if a version mismatch is detected
CURRENT_CHROOT_VERSION=$(cat "${chroots}/${repo}-${arch}/root/.arch-chroot")
if [[ -f "${chroots}/${repo}-${arch}/root/.arch-chroot" ]] && [[ "$CURRENT_CHROOT_VERSION" != "$CHROOT_VERSION" ]]; then
warning "Recreating chroot '%s' (%s) as it is not at version %s" "${chroots}/${repo}-${arch}/root" "$CURRENT_CHROOT_VERSION" "$CHROOT_VERSION"
clean_first=true
fi
if ${clean_first} || [[ ! -d "${chroots}/${repo}-${arch}" ]]; then
msg "Creating chroot for [%s] (%s)..." "${repo}" "${arch}"

View File

@@ -140,7 +140,7 @@ for _pkgname in "${pkgname[@]}"; do
bsdtar tf "$TEMPDIR/$oldpkg" | sort > "$TEMPDIR/filelist-$_pkgname-old"
bsdtar tf "$pkgfile" | sort > "$TEMPDIR/filelist-$_pkgname"
sdiff -s "$TEMPDIR/filelist-$_pkgname-old" "$TEMPDIR/filelist-$_pkgname"
diff --side-by-side --suppress-common-lines --width="${COLUMNS:-130}" --color=auto "$TEMPDIR/filelist-$_pkgname-old" "$TEMPDIR/filelist-$_pkgname"
find-libprovides "$TEMPDIR/$oldpkg" 2>/dev/null | sort > "$TEMPDIR/libraries-$_pkgname-old"
find-libprovides "$pkgfile" 2>/dev/null | sort > "$TEMPDIR/libraries-$_pkgname"

View File

@@ -120,8 +120,20 @@ if (( ${#validpgpkeys[@]} != 0 )); then
git add --force -- keys/pgp/*
fi
needsversioning=()
if [[ ! -e REUSE.toml || ! -e LICENSE || ! -d LICENSES ]]; then
# TODO: Make this a hard failure in the future after packagers have had
# some time to add licenses to all packages.
warning "package doesn't have proper licensing information, set it up using:"
msg2 'pkgctl license setup'
else
pkgctl license check
needsversioning+=(REUSE.toml LICENSE LICENSES/*)
fi
# find files which should be under source control
needsversioning=(PKGBUILD)
needsversioning+=(PKGBUILD)
for s in "${source[@]}"; do
[[ $s != *://* ]] && needsversioning+=("$s")
done
@@ -143,7 +155,7 @@ if (( ${#needsversioning[*]} )); then
if [[ ! -f "${file}" ]]; then
continue
fi
if ! git ls-files --error-unmatch "$file"; then
if ! git ls-files --error-unmatch "$file" >/dev/null; then
die "%s is not under version control" "$file"
fi
done
@@ -151,7 +163,7 @@ fi
server=${PACKAGING_REPO_RELEASE_HOST}
rsyncopts=(-e ssh -p '--chmod=ug=rw,o=r' -c -h -L --progress --partial -y)
rsyncopts=("${RSYNC_OPTS[@]}" --perms --chmod='ug=rw,o=r')
archreleaseopts=()
while getopts ':l:a:s:f' flag; do
case $flag in

View File

@@ -14,6 +14,8 @@ set -o pipefail
archweb_query_all_packages() {
local -a pkgbases
[[ -z ${WORKDIR:-} ]] && setup_workdir
stat_busy "Query all released packages"
@@ -36,6 +38,7 @@ archweb_query_all_packages() {
archweb_query_maintainer_packages() {
local maintainer=$1
local -a pkgbases
[[ -z ${WORKDIR:-} ]] && setup_workdir

View File

@@ -8,8 +8,12 @@ DEVTOOLS_INCLUDE_API_GITLAB_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/cache.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/cache.sh
# shellcheck source=src/lib/config.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/config.sh
# shellcheck source=src/lib/valid-issue.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-issue.sh
set -e
@@ -115,11 +119,13 @@ gitlab_api_call_paged() {
local next_page=1
local total_pages=1
local known_total_pages=1
local percentage=100
while [[ -n "${next_page}" ]]; do
percentage=$(( 100 * next_page / total_pages ))
printf "📡 Querying GitLab: %s/%s [%s] %%spinner%%" \
"${BOLD}${next_page}" "${total_pages}" "${percentage}%${ALL_OFF}" \
"${BOLD}${next_page}" "${known_total_pages}" "${percentage}%${ALL_OFF}" \
> "${tmp_file}"
mv "${tmp_file}" "${status_file}"
@@ -144,6 +150,15 @@ gitlab_api_call_paged() {
next_page=$(grep "x-next-page" "${header}" | tr -d '\r' | awk '{ print $2 }')
total_pages=$(grep "x-total-pages" "${header}" | tr -d '\r' | awk '{ print $2 }')
# The api is not guaranteed to return x-total-pages for larger query results
# https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23931
# https://gitlab.com/gitlab-org/gitlab/-/issues/436373
if (( total_pages == 0 )); then
total_pages=${next_page}
known_total_pages="?"
else
known_total_pages=${total_pages}
fi
done
jq --slurp add "${api_workdir}"/result.* > "${outfile}"
@@ -234,6 +249,101 @@ gitlab_api_get_project_name_mapping() {
return 0
}
gitlab_lookup_project_names() {
local status_file=$1; shift
local project_ids=("$@")
local graphql_lookup_batch=200
local project_name_cache_file tmp_file from length percentage
local project_slice query projects mapping_output
# collect project ids whose name needs to be looked up
project_name_cache_file=$(get_cache_file gitlab/project_id_to_name)
lock 11 "${project_name_cache_file}" "Locking project name cache"
# early exit if there is nothing new to look up
if (( ! ${#project_ids[@]} )); then
cat "${project_name_cache_file}"
# close project name cache lock
lock_close 11
return
fi
# reduce project_ids to uncached entries
mapfile -t project_ids < <(
printf "%s\n" "${project_ids[@]}" | \
grep --invert-match --file <(awk '{ print $1 }' < "${project_name_cache_file}" ))
# look up project names
tmp_file=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api-spinner.tmp.XXXXXXXXXX)
local entries="${#project_ids[@]}"
local until=0
while (( until < entries )); do
from=${until}
until=$(( until + graphql_lookup_batch ))
if (( until > entries )); then
until=${entries}
fi
length=$(( until - from ))
percentage=$(( 100 * until / entries ))
printf "📡 Querying GitLab project names: %s/%s [%s] %%spinner%%" \
"${BOLD}${until}" "${entries}" "${percentage}%${ALL_OFF}" \
> "${tmp_file}"
mv "${tmp_file}" "${status_file}"
project_slice=("${project_ids[@]:${from}:${length}}")
printf -v projects '"gid://gitlab/Project/%s",' "${project_slice[@]}"
query='{
projects(after: "" ids: ['"${projects}"']) {
pageInfo {
startCursor
endCursor
hasNextPage
}
nodes {
id
name
}
}
}'
mapping_output=$(gitlab_api_get_project_name_mapping "${query}")
# update cache
while read -r project_id project_name; do
printf "%s %s\n" "${project_id}" "${project_name}" >> "${project_name_cache_file}"
done < <(jq --raw-output \
'.[] | "\(.id | rindex("/") as $lastSlash | .[$lastSlash+1:]) \(.name)"' \
<<< "${mapping_output}")
done
cat "${project_name_cache_file}"
# close project name cache lock
lock_close 11
}
longest_package_name_from_ids() {
local project_ids=("$@")
local longest=0
# collect project ids whose name needs to be looked up
project_name_cache_file=$(get_cache_file gitlab/project_id_to_name)
lock 11 "${project_name_cache_file}" "Locking project name cache"
# read project_id to name mapping from cache
while read -r project_id project_name; do
if (( ${#project_name} > longest )) && in_array "${project_id}" "${project_ids[@]}"; then
longest="${#project_name}"
fi
done < "${project_name_cache_file}"
# close project name cache lock
lock_close 11
printf "%s" "${longest}"
}
# Convert arbitrary project names to GitLab valid path names.
#
# GitLab has several limitations on project and group names and also maintains
@@ -302,3 +412,492 @@ gitlab_api_search() {
return 0
}
# https://docs.gitlab.com/ee/api/projects.html#get-single-project
gitlab_project() {
local project=$1
local outfile project_path
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
project_path=$(gitlab_project_name_to_path "${project}")
if ! gitlab_api_call "${outfile}" GET "projects/archlinux%2fpackaging%2fpackages%2f${project_path}/"; then
return 1
fi
cat "${outfile}"
return 0
}
# TODO: parallelize
# https://docs.gitlab.com/ee/api/issues.html#list-project-issues
gitlab_projects_issues_list() {
local project=$1
local status_file=$2
local params=${3:-}
local data=${4:-}
local outfile
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
if ! gitlab_api_call_paged "${outfile}" "${status_file}" GET "/projects/archlinux%2fpackaging%2fpackages%2f${project}/issues?${params}" "${data}"; then
return 1
fi
cat "${outfile}"
return 0
}
# TODO: parallelize
# https://docs.gitlab.com/ee/api/issues.html#list-project-issues
gitlab_group_issue_list() {
local group=$1
local status_file=$2
local params=${3:-}
local data=${4:-}
local outfile
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
group=${group//\//%2f}
params=${params//\[/%5B}
params=${params//\]/%5D}
if ! gitlab_api_call_paged "${outfile}" "${status_file}" GET "/groups/${group}/issues?${params}" "${data}"; then
return 1
fi
cat "${outfile}"
}
# https://docs.gitlab.com/ee/api/issues.html#single-project-issue
gitlab_project_issue() {
local pkgbase=$1
local iid=$2
local outfile data path project_path
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
project_path=$(gitlab_project_name_to_path "${pkgbase}")
if ! gitlab_api_call "${outfile}" GET "projects/archlinux%2fpackaging%2fpackages%2f${project_path}/issues/${iid}"; then
return 1
fi
if ! path=$(jq --raw-output --exit-status '.title' < "${outfile}"); then
msg_error " failed to query path: $(cat "${outfile}")"
return 1
fi
cat "${outfile}"
return 0
}
gitlab_project_issue_create() {
local pkgbase=$1
local title=$2
local description=$3
local confidential=$4
shift 4
local labels=("${@}")
local outfile data iid project_path
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
project_path=$(gitlab_project_name_to_path "${pkgbase}")
data=$(jq --null-input \
--arg title "${title}" \
--arg description "${description}" \
--arg confidential "${confidential}" \
--arg labels "$(join_by , "${labels[@]}")" \
'$ARGS.named')
if ! gitlab_api_call "${outfile}" POST "/projects/archlinux%2fpackaging%2fpackages%2f${project_path}/issues" "${data}"; then
return 1
fi
if ! iid=$(jq --raw-output --exit-status '.iid' < "${outfile}"); then
msg_error " failed to query note: $(cat "${outfile}")"
return 1
fi
cat "${outfile}"
return 0
}
# TODO: parallelize
# https://docs.gitlab.com/ee/api/notes.html#list-project-issue-notes
gitlab_project_issue_notes() {
local project=$1
local iid=$2
local status_file=$3
local params=${4:-}
local outfile
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
if ! gitlab_api_call_paged "${outfile}" "${status_file}" GET "/projects/archlinux%2fpackaging%2fpackages%2f${project}/issues/${iid}/notes?${params}"; then
return 1
fi
cat "${outfile}"
return 0
}
# https://docs.gitlab.com/ee/api/issues.html#edit-an-issue
gitlab_project_issue_edit() {
local pkgbase=$1
local iid=$2
local params=$3
local data=${4:-}
local outfile data path project_path
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
project_path=$(gitlab_project_name_to_path "${pkgbase}")
if ! gitlab_api_call "${outfile}" PUT "/projects/archlinux%2fpackaging%2fpackages%2f${project_path}/issues/${iid}?${params}" "${data}"; then
return 1
fi
if ! path=$(jq --raw-output --exit-status '.title' < "${outfile}"); then
msg_error " failed to query path: $(cat "${outfile}")"
return 1
fi
cat "${outfile}"
return 0
}
gitlab_create_project_issue_note() {
local pkgbase=$1
local iid=$2
local body=$3
local outfile data path project_path
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
project_path=$(gitlab_project_name_to_path "${pkgbase}")
data=$(jq --null-input --arg body "${body}" '$ARGS.named')
if ! gitlab_api_call "${outfile}" POST "/projects/archlinux%2fpackaging%2fpackages%2f${project_path}/issues/${iid}/notes" "${data}"; then
return 1
fi
if ! path=$(jq --raw-output --exit-status '.body' < "${outfile}"); then
msg_error " failed to query note: $(cat "${outfile}")"
return 1
fi
cat "${outfile}"
return 0
}
gitlab_project_issue_move() {
local pkgbase=$1
local iid=$2
local to_project_id=$3
local outfile path project_path
[[ -z ${WORKDIR:-} ]] && setup_workdir
outfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api.XXXXXXXXXX)
project_path=$(gitlab_project_name_to_path "${pkgbase}")
if ! gitlab_api_call "${outfile}" POST "/projects/archlinux%2fpackaging%2fpackages%2f${project_path}/issues/${iid}/move?to_project_id=${to_project_id}"; then
return 1
fi
if ! path=$(jq --raw-output --exit-status '.title' < "${outfile}"); then
msg_error " failed to move issue: $(cat "${outfile}")"
return 1
fi
cat "${outfile}"
return 0
}
gitlab_severity_from_labels() {
local labels=("$@")
local severity="unknown"
local label
for label in "${labels[@]}"; do
if [[ ${label} == severity::* ]]; then
severity="${label#*-}"
fi
done
printf "%s" "${severity}"
}
severity_as_gitlab_label() {
local severity=$1
case "${severity}" in
lowest)
printf "severity::5-%s" "${severity}" ;;
low)
printf "severity::4-%s" "${severity}" ;;
medium)
printf "severity::3-%s" "${severity}" ;;
high)
printf "severity::2-%s" "${severity}" ;;
critical)
printf "severity::1-%s" "${severity}" ;;
*)
return 1 ;;
esac
return 0
}
gitlab_priority_from_labels() {
local labels=("$@")
local priority="normal"
local label
for label in "${labels[@]}"; do
if [[ ${label} == priority::* ]]; then
priority="${label#*-}"
fi
done
printf "%s" "${priority}"
}
priority_as_gitlab_label() {
local priority=$1
case "${priority}" in
low)
printf "priority::4-%s" "${priority}" ;;
normal)
printf "priority::3-%s" "${priority}" ;;
high)
printf "priority::2-%s" "${priority}" ;;
urgent)
printf "priority::1-%s" "${priority}" ;;
*)
return 1 ;;
esac
return 0
}
gitlab_scope_from_labels() {
local labels=("$@")
local scope="unknown"
local label
for label in "${labels[@]}"; do
if [[ ${label} == scope::* ]]; then
scope="${label#*::}"
fi
done
printf "%s" "${scope}"
}
scope_as_gitlab_label() {
local scope=$1
if ! in_array "${scope}" "${DEVTOOLS_VALID_ISSUE_SCOPE[@]}"; then
return 1
fi
printf "scope::%s" "${scope}"
}
gitlab_scope_short() {
local scope=$1
case "${scope}" in
regression)
scope=regress ;;
enhancement)
scope=enhance ;;
documentation)
scope=doc ;;
reproducibility)
scope=repro ;;
out-of-date)
scope=ood ;;
esac
printf "%s" "${scope}"
}
gitlab_scope_color() {
local scope=$1
local color="${GRAY}"
case "${scope}" in
bug)
color="${DARK_RED}" ;;
feature)
color="${DARK_BLUE}" ;;
security)
color="${RED}" ;;
question)
color="${PURPLE}" ;;
regression)
color="${DARK_RED}" ;;
enhancement)
color="${DARK_BLUE}" ;;
documentation)
color="${ALL_OFF}" ;;
reproducibility)
color="${DARK_GREEN}" ;;
out-of-date)
color="${DARK_YELLOW}" ;;
esac
printf "%s" "${color}"
}
status_as_gitlab_label() {
local status=$1
if ! in_array "${status}" "${DEVTOOLS_VALID_ISSUE_STATUS[@]}"; then
return 1
fi
printf "status::%s" "${status}"
return 0
}
gitlab_issue_state_display() {
local state=$1
if [[ ${state} == opened ]]; then
state=open
fi
printf "%s" "${state}"
}
gitlab_issue_status_from_labels() {
local labels=("$@")
local status=unconfirmed
local label
for label in "${labels[@]}"; do
if [[ ${label} == status::* ]]; then
status="${label#*::}"
fi
done
printf "%s" "${status}"
}
gitlab_issue_status_short() {
local status=$1
if [[ ${status} == waiting-* ]]; then
status=waiting
fi
printf "%s" "${status}"
}
gitlab_issue_status_color() {
local status=$1
local color="${GRAY}"
case "${status}" in
confirmed)
color="${GREEN}" ;;
in-progress)
color="${YELLOW}" ;;
in-review)
color="${PURPLE}" ;;
on-hold|unconfirmed)
color="${GRAY}" ;;
waiting-input|waiting-upstream)
color="${DARK_BLUE}" ;;
esac
printf "%s" "${color}"
}
resolution_as_gitlab_label() {
local resolution=$1
if ! in_array "${resolution}" "${DEVTOOLS_VALID_ISSUE_RESOLUTION[@]}"; then
return 1
fi
printf "resolution::%s" "${resolution}"
}
gitlab_resolution_from_labels() {
local labels=("$@")
local label
for label in "${labels[@]}"; do
if [[ ${label} == resolution::* ]]; then
printf "%s" "${label#*::}"
return 0
fi
done
return 1
}
gitlab_resolution_color() {
local resolution=$1
local color=""
case "${resolution}" in
cant-reproduce)
color="${DARK_YELLOW}" ;;
completed)
color="${GREEN}" ;;
duplicate)
color="${GRAY}" ;;
invalid)
color="${DARK_YELLOW}" ;;
not-a-bug)
color="${GRAY}" ;;
upstream)
color="${PURPLE}" ;;
wont-fix)
color="${DARK_BLUE}" ;;
esac
printf "%s" "${color}"
}
gitlab_severity_color() {
local severity=$1
local color="${PURPLE}"
case "${severity}" in
lowest)
color="${DARK_GREEN}" ;;
low)
color="${GREEN}" ;;
medium)
color="${YELLOW}" ;;
high)
color="${RED}" ;;
critical)
color="${RED}${UNDERLINE}" ;;
esac
printf "%s" "${color}"
}
gitlab_priority_color() {
local priority=$1
local color="${PURPLE}"
case "${priority}" in
low)
color="${DARK_GREEN}" ;;
normal)
color="${GREEN}" ;;
high)
color="${YELLOW}" ;;
urgent)
color="${RED}" ;;
esac
printf "%s" "${color}"
}
gitlab_issue_state_color() {
local state=$1
local state_color="${DARK_GREEN}"
if [[ ${state} == closed ]]; then
state_color="${DARK_RED}"
fi
printf "%s" "${state_color}"
}

View File

@@ -4,7 +4,7 @@
:
# shellcheck disable=2034
CHROOT_VERSION='v5'
CHROOT_VERSION='v6'
##
# usage : check_root $keepenv

View File

@@ -165,7 +165,7 @@ pkgctl_aur_drop_from_repo() {
warning 'Did not find %s in any repository, please delete manually' "${pkgbase}"
else
msg2 " repo: ${pkgrepo}"
pkgctl_db_remove "${pkgrepo}" "${pkgbase}"
pkgctl_db_remove --noconfirm "${pkgrepo}" "${pkgbase}"
fi
popd >/dev/null

View File

@@ -63,7 +63,7 @@ pkgctl_auth_login() {
esac
done
personal_access_token_url="https://${GITLAB_HOST}/-/profile/personal_access_tokens?name=pkgctl+token&scopes=api,write_repository"
personal_access_token_url="https://${GITLAB_HOST}/-/user_settings/personal_access_tokens?name=pkgctl+token&scopes=api,write_repository"
cat <<- _EOF_
Logging into ${BOLD}${GITLAB_HOST}${ALL_OFF}

View File

@@ -42,10 +42,10 @@ pkgctl_build_usage() {
Build packages inside a clean chroot
When a new pkgver is set using the appropriate PKGBUILD options the
checksums are automatically updated.
Build packages in clean chroot environment, offering various options
and functionalities to customize the package building process.
TODO
By default, chroot environments are located in /var/lib/archbuild/.
BUILD OPTIONS
--arch ARCH Specify architectures to build for (disables auto-detection)
@@ -79,8 +79,8 @@ pkgctl_build_usage() {
EXAMPLES
$ ${COMMAND}
$ ${COMMAND} --rebuild --staging --message 'libyay 0.42 rebuild' libfoo libbar
$ ${COMMAND} --pkgver 1.42 --release --db-update
$ ${COMMAND} --rebuild --staging --release --message 'libyay 0.42 rebuild' libfoo libbar
$ ${COMMAND} --pkgver=1.42 --release --db-update
_EOF_
}
@@ -312,7 +312,7 @@ pkgctl_build() {
# Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then
update_pacman_repo_cache
update_pacman_repo_cache multilib
# Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}"
@@ -437,10 +437,11 @@ pkgctl_build() {
stat_done
fi
# update checksums if any sources are declared
if (( UPDATE_CHECKSUMS )) && (( ${#source[@]} >= 1 )); then
updpkgsums
if ! result=$(pkgbuild_update_checksums /dev/stderr); then
die "${result}"
fi
fi
# re-source the PKGBUILD if it changed

View File

@@ -15,6 +15,12 @@ $DEVTOOLS_INCLUDE_COMMON_SH
# Avoid any encoding problems
export LANG=C.UTF-8
# Avoid systemd trying to color the terminal on systemd-nspawn
export SYSTEMD_TINT_BACKGROUND=no
# Avoid diffoscope looking at remote debug info through readelf
unset DEBUGINFOD_URLS
# Set buildtool properties
export BUILDTOOL=devtools
export BUILDTOOLVER=@buildtoolver@
@@ -31,6 +37,27 @@ export PACKAGING_REPO_RELEASE_HOST=repos.archlinux.org
export PKGBASE_MAINTAINER_URL=https://archlinux.org/packages/pkgbase-maintainer
export AUR_URL_SSH=aur@aur.archlinux.org
# Create or reuse a shared SSH control socket with ControlMaster=auto. The
# connection is initialized on the first use and persisted for some time, so
# multiple invokations of devtools can share it.
# shellcheck disable=SC2016
export SSH_OPTS=(
-o ControlMaster=auto
-o ControlPersist=60s
-o ControlPath='${XDG_RUNTIME_DIR}/devtools-%r@%h:%p'
)
export RSYNC_OPTS=(
--rsh="ssh ${SSH_OPTS[*]}"
--checksum
--copy-links
--human-readable
--progress
--partial
--partial-dir=.partial
--delay-updates
)
# ensure TERM is set with a fallback to dumb
export TERM=${TERM:-dumb}
@@ -40,15 +67,23 @@ if [[ -t 2 && "$TERM" != dumb ]] || [[ ${DEVTOOLS_COLOR} == always ]]; then
if tput setaf 0 &>/dev/null; then
PURPLE="$(tput setaf 5)"
DARK_GREEN="$(tput setaf 2)"
DARK_RED="$(tput setaf 1)"
DARK_BLUE="$(tput setaf 4)"
DARK_YELLOW="$(tput setaf 3)"
UNDERLINE="$(tput smul)"
GRAY=$(tput setaf 242)
else
PURPLE="\e[35m"
DARK_GREEN="\e[32m"
DARK_RED="\e[31m"
DARK_BLUE="\e[34m"
DARK_YELLOW="\e[33m"
UNDERLINE="\e[4m"
GRAY=""
fi
else
# shellcheck disable=2034
declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' PURPLE='' DARK_GREEN='' UNDERLINE=''
declare -gr ALL_OFF='' BOLD='' BLUE='' GREEN='' RED='' YELLOW='' PURPLE='' DARK_RED='' DARK_GREEN='' DARK_BLUE='' DARK_YELLOW='' UNDERLINE='' GRAY=''
fi
stat_busy() {
@@ -106,6 +141,8 @@ print_workdir_error() {
}
_setup_workdir=false
# Ensure that there is no outside value for WORKDIR leaking in
unset WORKDIR
setup_workdir() {
[[ -z ${WORKDIR:-} ]] && WORKDIR=$(mktemp -d --tmpdir "${0##*/}.XXXXXXXXXX")
_setup_workdir=true
@@ -342,3 +379,65 @@ is_debug_package() {
pkgdesc="$(getpkgdesc "${pkgfile}")"
[[ ${pkgdesc} == "Detached debugging symbols for "* && ${pkgbase}-debug = "${pkgname}" ]]
}
# Proxy function to check if a file exists. Using [[ -f ... ]] directly is not
# always wanted because we might want to expand bash globs first. This way we
# can pass unquoted globs to is_globfile() and have them expanded as function
# arguments before being checked.
is_globfile() {
[[ -f $1 ]]
}
join_by() {
local IFS=" "
local sep=$1
local split
shift
split=$(printf "%s" "$*")
echo "${split//${IFS}/"${sep}"}"
}
trim_string() {
local max_length=$1
local string=$2
if (( ${#string} > max_length )); then
# Subtract 3 from max_length to accommodate "..."
max_length=$((max_length - 3))
string="${string:0:max_length}..."
fi
printf "%s" "${string}"
}
relative_date_unit() {
local target_date=$1
local now diff value units unit names
target_date=$(date -d "$1" +%s)
now=$(date +%s)
diff=$((now - target_date))
local names=(year month week day hour minute second)
declare -A units=(
[year]=$((60 * 60 * 24 * 365))
[month]=$((60 * 60 * 24 * 30))
[week]=$((60 * 60 * 24 * 7))
[day]=$((60 * 60 * 24))
[hour]=$((60 * 60))
[minute]=60
[second]=1
)
for unit in "${names[@]}"; do
local value=$((diff / ${units[${unit}]}))
if (( value > 1 )); then
printf "%s %ss" "${value}" "${unit}"
return
elif (( value == 1 )); then
printf "%s %s" "${value}" "${unit}"
return
fi
done
printf "1 second"
}

View File

@@ -8,6 +8,12 @@ DEVTOOLS_INCLUDE_DB_REMOVE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/util/pacman.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/pacman.sh
# shellcheck source=src/lib/util/term.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
# shellcheck source=src/lib/valid-repos.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh
set -e
@@ -17,10 +23,19 @@ pkgctl_db_remove_usage() {
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [REPO] [PKGBASE]...
Remove packages from binary repositories.
Remove packages from pacman repositories. By default passing a pkgbase removes
all split packages, debug packages as well as entries from the state repo for
all existing architectures.
Beware when using the --partial option, as it may most likely lead to
undesired effects by leaving debug packages behind as well as dangling entries
in the state repository.
OPTIONS
-a, --arch Override the architecture (disables auto-detection)
-a, --arch Remove only one specific architecture (disables auto-detection)
--partial Remove only partial pkgnames from a split package. This leaves
debug packages behind and pkgbase entries in the state repo.
--noconfirm Bypass any confirmation messages, should only be used with caution
-h, --help Show this help text
EXAMPLES
@@ -31,8 +46,13 @@ _EOF_
pkgctl_db_remove() {
local REPO=""
local ARCH=any
local PKGBASES=()
local pkgnames=()
local partial=0
local confirm=1
local dbscripts_options=()
local lookup_repo=multilib
local pkgname
# option checking
while (( $# )); do
@@ -41,11 +61,20 @@ pkgctl_db_remove() {
pkgctl_db_remove_usage
exit 0
;;
--partial)
partial=1
dbscripts_options+=(--partial)
shift
;;
-a|--arch)
(( $# <= 1 )) && die "missing argument for %s" "$1"
ARCH=$2
dbscripts_options+=(--arch "$2")
shift 2
;;
--noconfirm)
confirm=0
shift
;;
-*)
die "invalid argument: %s" "$1"
;;
@@ -63,7 +92,62 @@ pkgctl_db_remove() {
REPO=$1
shift
PKGBASES+=("$@")
pkgnames=("${PKGBASES[@]}")
# check if the target repo is valid
if ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}"
fi
# update pacman cache to query all pkgnames
if (( ! partial )); then
case ${REPO} in
*-unstable)
update_pacman_repo_cache unstable
;;
*-staging)
update_pacman_repo_cache multilib-staging
;;
*-testing)
update_pacman_repo_cache multilib-testing
;;
*)
update_pacman_repo_cache multilib
;;
esac
# fetch the pkgnames of all pkgbase as present in the repo
mapfile -t pkgnames < <(get_pkgnames_from_repo_pkgbase "${REPO}" "${PKGBASES[@]}")
echo
if (( ! ${#pkgnames[@]} )); then
error "Packages not found in %s" "${REPO}"
exit 1
fi
fi
# print list of packages
printf "%sRemoving packages from %s:%s\n" "${RED}" "${REPO}" "${ALL_OFF}"
for pkgname in "${pkgnames[@]}"; do
printf "• %s\n" "${pkgname}"
done
# print explenation about partial removal
if (( partial )); then
echo
msg_warn "${YELLOW}Removing only partial pkgnames from a split package.${ALL_OFF}"
msg_warn "${YELLOW}This leaves debug packages and pkgbase entries in the state repo!${ALL_OFF}"
fi
# ask for confirmation
if (( confirm )); then
echo
if ! prompt "${GREEN}${BOLD}?${ALL_OFF} Are you sure this is correct?"; then
exit 1
fi
fi
echo
# shellcheck disable=SC2029
ssh "${PACKAGING_REPO_RELEASE_HOST}" db-remove "${REPO}" "${ARCH}" "${PKGBASES[@]}"
ssh "${PACKAGING_REPO_RELEASE_HOST}" db-remove "${dbscripts_options[@]}" "${REPO}" "${PKGBASES[@]}"
}

194
src/lib/issue/close.sh Normal file
View File

@@ -0,0 +1,194 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_CLOSE_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_CLOSE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
set -eo pipefail
pkgctl_issue_close_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [IID]
This command is used to close an issue in Arch Linux packaging projects. It
finalizes the issue by marking it as resolved and optionally providing a reason
for its closure.
By default, the command operates within the current directory, but users have
the option to specify a different package base.
Users can provide a message directly through the command line to explain the
reason for closing the issue. Additionally, a specific resolution label can be
set to categorize the closure reason, with the default label being "completed."
OPTIONS
-p, --package PKGBASE Interact with <pkgbase> instead of the current directory
-m, --message MSG Use the provided message as the reason for closing
-e, --edit Edit the reason for closing using an editor
-r, --resolution REASON Set a specific resolution label (default: completed)
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} 42
$ ${COMMAND} --edit --package linux 42
_EOF_
}
pkgctl_issue_close() {
if (( $# < 1 )); then
pkgctl_issue_close_usage
exit 0
fi
local iid=""
local pkgbase=""
local message=""
local edit=0
local labels=()
local resolution="completed"
local issue note
local params="state_event=close"
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_close_usage
exit 0
;;
-m|--message)
(( $# <= 1 )) && die "missing argument for %s" "$1"
message=$2
shift 2
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-e|--edit)
edit=1
shift
;;
--resolution)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(resolution_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
params+="&add_labels=${label}"
shift 2
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
iid=$1
shift
;;
esac
done
if [[ -z ${iid} ]]; then
die "missing issue iid argument"
fi
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
# spawn editor
if (( edit )); then
msgfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-note.XXXXXXXXXX.md)
printf "%s\n" "${message}" >> "${msgfile}"
if [[ -n $VISUAL ]]; then
$VISUAL "${msgfile}" || die
elif [[ -n $EDITOR ]]; then
$EDITOR "${msgfile}" || die
else
die "No usable editor found (tried \$VISUAL, \$EDITOR)."
fi
message=$(cat "${msgfile}")
fi
# comment on issue
if [[ -n ${message} ]]; then
if ! note=$(gitlab_create_project_issue_note "${pkgbase}" "${iid}" "${message}"); then
msg_error "Failed to comment on issue ${BOLD}#${iid}${ALL_OFF}"
exit 1
fi
msg_success "Commented on issue ${BOLD}#${iid}${ALL_OFF}"
fi
# close issue
if ! issue=$(gitlab_project_issue_edit "${pkgbase}" "${iid}" "${params}"); then
msg_error "Failed to close issue ${BOLD}#${iid}${ALL_OFF}"
exit 1
fi
msg_success "Closed issue ${BOLD}#${iid}${ALL_OFF}"
echo
{ read -r iid; read -r title; read -r state; read -r created_at; read -r author; } < <(
jq --raw-output ".iid, .title, .state, .created_at, .author.username" <<< "${issue}"
)
mapfile -t labels < <(
jq --raw-output ".labels[]" <<< "${issue}"
)
severity="$(gitlab_severity_from_labels "${labels[@]}")"
severity_color="$(gitlab_severity_color "${severity}")"
created_at=$(relative_date_unit "${created_at}")
state_color="$(gitlab_issue_state_color "${state}")"
state="$(gitlab_issue_state_display "${state}")"
status="$(gitlab_issue_status_from_labels "${labels[@]}")"
status_color="$(gitlab_issue_status_color "${status}")"
scope="$(gitlab_scope_from_labels "${labels[@]}")"
scope_color="$(gitlab_scope_color "${scope}")"
scope_label=""
if [[ ${scope} != unknown ]]; then
scope_label="${scope_color}${scope}${ALL_OFF} ${GRAY}${ALL_OFF} "
fi
resolution_label=""
if resolution="$(gitlab_resolution_from_labels "${labels[@]}")"; then
resolution_color="$(gitlab_resolution_color "${resolution}")"
resolution_label="${resolution_color}${resolution}${ALL_OFF} ${GRAY}${ALL_OFF} "
fi
printf "%s%s • %s%sseverity %s • %s • %s%sopened by %s %s ago%s\n" \
"${state_color}${state}${ALL_OFF}" "${GRAY}" "${resolution_label}" "${severity_color}" "${severity}${GRAY}" \
"${status_color}${status}${GRAY}" "${scope_label}" "${GRAY}" "${author}" "${created_at}" "${ALL_OFF}"
printf "%s %s\n" "${BOLD}${title}${ALL_OFF}" "${GRAY}#${iid}${ALL_OFF}"
# show comment
if [[ -n ${note} ]]; then
{ read -r created_at; read -r author; } < <(
jq --raw-output ".created_at, .author.username" <<< "${note}"
)
body=$(jq --raw-output ".body" <<< "${note}")
created_at=$(relative_date_unit "${created_at}")
echo
echo "${BOLD}Comments / Notes${ALL_OFF}"
printf -v spaces '%*s' $(( COLUMNS - 2 )) ''
printf '%s\n\n' "${spaces// /─}"
printf "%s commented%s %s ago%s\n" "${author}" "${GRAY}" "${created_at}" "${ALL_OFF}"
echo "${body}" | glow
fi
}

130
src/lib/issue/comment.sh Normal file
View File

@@ -0,0 +1,130 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_COMMENT_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_COMMENT_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
set -eo pipefail
pkgctl_issue_comment_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [IID]
This command allows users to add comments to an issue in Arch Linux packaging
projects. This command is useful for providing feedback, updates, or any
additional information related to an issue directly within the project's issue
tracking system.
By default, the command interacts with the current directory, but users can
specify a different package base if needed.
OPTIONS
-p, --package PKGBASE Interact with <pkgbase> instead of the current directory
-m, --message MSG Use the provided message as the comment
-e, --edit Edit the comment using an editor
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} --message "I've attached some logs" 42
$ ${COMMAND} --package linux 42
$ ${COMMAND} 42
_EOF_
}
pkgctl_issue_comment() {
if (( $# < 1 )); then
pkgctl_issue_comment_usage
exit 0
fi
local iid=""
local pkgbase=""
local message=""
local edit=0
local note
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_comment_usage
exit 0
;;
-m|--message)
(( $# <= 1 )) && die "missing argument for %s" "$1"
message=$2
shift 2
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-e|--edit)
edit=1
shift
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
iid=$1
shift
;;
esac
done
if [[ -z ${iid} ]]; then
die "missing issue iid argument"
fi
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
# spawn editor
if (( edit )) || [[ -z ${message} ]]; then
msgfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-note.XXXXXXXXXX.md)
printf "%s\n" "${message}" >> "${msgfile}"
if [[ -n $VISUAL ]]; then
$VISUAL "${msgfile}" || die
elif [[ -n $EDITOR ]]; then
$EDITOR "${msgfile}" || die
else
die "No usable editor found (tried \$VISUAL, \$EDITOR)."
fi
message=$(< "${msgfile}")
fi
# comment on issue
if ! note=$(gitlab_create_project_issue_note "${pkgbase}" "${iid}" "${message}"); then
msg_error "Failed to comment on issue ${BOLD}#${iid}${ALL_OFF}"
exit 1
fi
msg_success "Commented on issue ${BOLD}#${iid}${ALL_OFF}"
echo
{ read -r created_at; read -r author; } < <(
jq --raw-output ".created_at, .author.username" <<< "${note}"
)
body=$(jq --raw-output ".body" <<< "${note}")
created_at=$(relative_date_unit "${created_at}")
printf "%s commented%s %s ago%s\n" "${author}" "${GRAY}" "${created_at}" "${ALL_OFF}"
echo "${body}" | glow
}

296
src/lib/issue/create.sh Normal file
View File

@@ -0,0 +1,296 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_CREATE_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_CREATE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
# shellcheck source=src/lib/util/term.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
set -eo pipefail
pkgctl_issue_create_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS]
The create command is used to create a new issue for an Arch Linux package.
This command is suitable for reporting bugs, regressions, feature requests, or
any other issues related to a package. It provides a flexible way to document
and track new issues within the project's issue tracking system.
By default, the command operates within the current directory, but users can
specify a different package base if needed.
Users can provide a title for the issue directly through the command line.
The command allows setting various labels and attributes for the issue, such as
confidentiality, priority, scope, severity, and status.
In case of a failed run, the command can automatically recover to ensure that
the issue creation process is completed without losing any data.
OPTIONS
-p, --package PKGBASE Interact with <pkgbase> instead of the current directory
-t, --title TITLE Use the provided title for the issue
-F, --file FILE Take issue description from <file>
-e, --edit Edit the issue description using an editor
-w, --web Continue issue creation with the web interface
--recover Automatically recover from a failed run
--confidentiality TYPE Set the issue confidentiality
--priority PRIORITY Set the priority label
--scope SCOPE Set the scope label
--severity SEVERITY Set the severity label
--status STATUS Set the status label
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} --package linux --title "some very informative title"
_EOF_
}
pkgctl_issue_create() {
if (( $# < 1 )); then
pkgctl_issue_create_usage
exit 0
fi
local pkgbase=""
local title_placeholder="PLACEHOLDER"
local title="${title_placeholder}"
local description=""
local labels=()
local msgfile=""
local edit=0
local web=0
local recover=0
local confidential=0
local issue_template_url="https://gitlab.archlinux.org/archlinux/packaging/templates/-/raw/master/.gitlab/issue_templates/Default.md"
local issue_template
local recovery_home=${XDG_DATA_HOME:-$HOME/.local/share}/devtools/recovery
local recovery_file
local issue_url
local project_path
local result
local iid
local message
local editor
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_create_usage
exit 0
;;
-t|--title)
(( $# <= 1 )) && die "missing argument for %s" "$1"
title=$2
shift 2
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-F|--file)
(( $# <= 1 )) && die "missing argument for %s" "$1"
msgfile=$2
shift 2
;;
-e|--edit)
edit=1
shift
;;
-w|--web)
web=1
shift
;;
--recover)
recover=1
shift
;;
--confidentiality)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! in_array "$2" "${DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[@]}"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
if [[ $2 == confidential ]]; then
confidential=1
fi
shift 2
;;
--priority)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(priority_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--scope)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(scope_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--severity)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(severity_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--status)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(status_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
*)
die "invalid argument: %s" "$1"
;;
esac
done
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
project_path=$(gitlab_project_name_to_path "${pkgbase}")
recovery_file="${recovery_home}/issue_create_${pkgbase}.md"
# spawn web browser
if (( web )); then
if ! command -v xdg-open &>/dev/null; then
die "The web option requires 'xdg-open'"
fi
issue_url="${GIT_PACKAGING_URL_HTTPS}/${project_path}/-/issues/new"
echo "Opening ${issue_url} in your browser."
xdg-open "${issue_url}"
return
fi
# check existence of recovery file
if [[ -f ${recovery_file} ]]; then
if (( ! recover )); then
msg_warn "Recovery file already exists: ${recovery_file}"
if prompt "${GREEN}${BOLD}?${ALL_OFF} Do you want to recover?"; then
msgfile=${recovery_file}
recover=1
edit=1
fi
fi
fi
# check existence of msgfile
if [[ -n ${msgfile} ]]; then
if [[ ! -f ${msgfile} ]]; then
msg_error "File does not exist: ${msgfile}${ALL_OFF}"
exit 1
fi
else
# prepare msgfile and fetch the issue template
if ! issue_template=$(curl --url "${issue_template_url}" --silent); then
msg_error "Failed to fetch issue template${ALL_OFF}"
exit 1
fi
# populate message file
msgfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-create.XXXXXXXXXX.md)
edit=1
printf "# Title: %s\n\n" "${title}" >> "${msgfile}"
printf "%s\n" "${issue_template}" >> "${msgfile}"
fi
# spawn editor
if (( edit )); then
if [[ -n $VISUAL ]]; then
editor=${VISUAL}
elif [[ -n $EDITOR ]]; then
editor=${EDITOR}
else
die "No usable editor found (tried \$VISUAL, \$EDITOR)."
fi
if ! ${editor} "${msgfile}"; then
message=$(< "${msgfile}")
pkgctl_issue_write_recovery_file "${pkgbase}" "${message}" "${recovery_file}" "${recover}"
fi
fi
# check if the file contains a title
message=$(< "${msgfile}")
description=${message}
if [[ ${message} == "# Title: "* ]]; then
title=$(head --lines 1 <<< "${message}")
title=${title//# Title: /}
description=$(tail --lines +2 <<< "${message}")
if [[ ${description} == $'\n'* ]]; then
description=$(tail --lines +3 <<< "${message}")
fi
fi
# validate title
if [[ ${title} == 'PLACEHOLDER' ]]; then
msg_error "Invalid issue title: ${title}${ALL_OFF}"
pkgctl_issue_write_recovery_file "${pkgbase}" "${message}" "${recovery_file}" "${recover}"
exit 1
fi
# create the issue
if ! result=$(gitlab_project_issue_create "${pkgbase}" "${title}" "${description}" "${confidential}" "${labels[@]}"); then
msg_error "Failed to create issue in ${BOLD}${pkgbase}${ALL_OFF}"
pkgctl_issue_write_recovery_file "${pkgbase}" "${message}" "${recovery_file}" "${recover}"
exit 1
fi
# delete old recovery file if we succeeded
if [[ -f ${recovery_file} ]]; then
rm --force "${recovery_file}"
fi
# read issue iid
{ read -r iid; } < <(
jq --raw-output ".iid" <<< "${result}"
)
issue_url="${GIT_PACKAGING_URL_HTTPS}/${project_path}/-/issues/${iid}"
msg_success "Created new issue ${BOLD}#${iid}${ALL_OFF}"
printf "%sView this issue on GitLab: %s%s\n" "${GRAY}" "${issue_url}" "${ALL_OFF}"
}
pkgctl_issue_write_recovery_file() {
local pkgbase=$1
local message=$2
local recovery_file=$3
local recover=$4
if [[ -f ${recovery_file} ]] && (( ! recover )); then
msg_warn "Recovery file already exists: ${recovery_file}"
if ! prompt "${YELLOW}${BOLD}?${ALL_OFF} Are you sure you want to overwrite it?"; then
return 1
fi
fi
mkdir -p "$(dirname "${recovery_file}")"
printf "%s\n" "${message}" > "${recovery_file}"
printf "Created recovery file: %s\n" "${recovery_file}"
return 0
}

311
src/lib/issue/edit.sh Normal file
View File

@@ -0,0 +1,311 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_EDIT_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_EDIT_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
# shellcheck source=src/lib/util/term.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
set -eo pipefail
pkgctl_issue_edit_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [IID]
The pkgctl issue edit command is used to modify an existing issue in Arch Linux
packaging projects. This command allows users to update the issue's title,
description, and various attributes, ensuring that the issue information
remains accurate and up-to-date. It also provides a streamlined facility
for bug wranglers to categorize and prioritize issues efficiently.
By default, the command operates within the current directory, but users can
specify a different package base if needed.
In case of a failed run, the command can automatically recover to ensure that
the editing process is completed without losing any data.
OPTIONS
-p, --package PKGBASE Interact with <pkgbase> instead of the current directory
-t, --title TITLE Use the provided title for the issue
-e, --edit Edit the issue title and description using an editor
--recover Automatically recover from a failed run
--confidentiality TYPE Set the issue confidentiality
--priority PRIORITY Set the priority label
--resolution REASON Set the resolution label
--scope SCOPE Set the scope label
--severity SEVERITY Set the severity label
--status STATUS Set the status label
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} --package linux --title "some very informative title"
_EOF_
}
pkgctl_issue_edit() {
if (( $# < 1 )); then
pkgctl_issue_edit_usage
exit 0
fi
local pkgbase=""
local title=""
local description=""
local labels=()
local confidential=""
local msgfile=""
local edit=0
local recover=0
local recovery_home=${XDG_DATA_HOME:-$HOME/.local/share}/devtools/recovery
local recovery_file
local issue_url
local project_path
local result
local iid
local message
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_edit_usage
exit 0
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-t|--title)
(( $# <= 1 )) && die "missing argument for %s" "$1"
title=$2
shift 2
;;
-e|--edit)
edit=1
shift
;;
--recover)
recover=1
shift
;;
--confidentiality)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! in_array "$2" "${DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[@]}"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
if [[ $2 == public ]]; then
confidential=false
else
confidential=true
fi
shift 2
;;
--priority)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(priority_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--resolution)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(resolution_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--scope)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(scope_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--severity)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(severity_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--status)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(status_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
iid=$1
shift
;;
esac
done
if [[ -z ${iid} ]]; then
die "missing issue iid argument"
fi
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
project_path=$(gitlab_project_name_to_path "${pkgbase}")
recovery_file="${recovery_home}/issue_edit_${pkgbase}.md"
# load current issue data
if ! result=$(gitlab_project_issue "${pkgbase}" "${iid}"); then
die "Failed to query issue ${pkgbase} #${iid}"
fi
{ read -r current_title; read -r current_confidential; } < <(
jq --raw-output ".title, .confidential" <<< "${result}"
)
current_description=$(jq --raw-output ".description" <<< "${result}")
# check existence of recovery file
if [[ -f ${recovery_file} ]]; then
if (( ! recover )); then
msg_warn "Recovery file already exists: ${recovery_file}"
if prompt "${GREEN}${BOLD}?${ALL_OFF} Do you want to recover?"; then
msgfile=${recovery_file}
recover=1
edit=1
fi
fi
fi
# assign data to msgfile
if [[ -n ${msgfile} ]]; then
# check existence of msgfile
if [[ ! -f ${msgfile} ]]; then
msg_error "File does not exist: ${msgfile}${ALL_OFF}"
exit 1
fi
fi
# spawn editor
if (( edit )); then
if [[ -z ${msgfile} ]]; then
msgfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-create.XXXXXXXXXX.md)
if [[ -n ${title} ]]; then
printf "# Title: %s\n\n" "${title}" >> "${msgfile}"
else
printf "# Title: %s\n\n" "${current_title}" >> "${msgfile}"
fi
printf "%s\n" "${current_description}" >> "${msgfile}"
fi
if [[ -n $VISUAL ]]; then
editor=${VISUAL}
elif [[ -n $EDITOR ]]; then
editor=${EDITOR}
else
die "No usable editor found (tried \$VISUAL, \$EDITOR)."
fi
if ! ${editor} "${msgfile}"; then
message=$(< "${msgfile}")
pkgctl_issue_write_recovery_file "${pkgbase}" "${message}" "${recovery_file}" "${recover}"
return 1
fi
fi
# check if the file contains a title
if [[ -n ${msgfile} ]]; then
message=$(< "${msgfile}")
description=${message}
if [[ ${message} == "# Title: "* ]]; then
title=$(head --lines 1 <<< "${message}")
title=${title//# Title: /}
description=$(tail --lines +2 <<< "${message}")
if [[ ${description} == $'\n'* ]]; then
description=$(tail --lines +3 <<< "${message}")
fi
fi
fi
# prepare changes
data='{}'
if [[ -n ${title} ]] && [[ ${title} != "${current_title}" ]]; then
result=$(jq --null-input \
--arg title "${title}" \
'$ARGS.named')
data=$(jq --slurp '.[0] * .[1]' <(echo "${data}") <(echo "${result}"))
fi
if [[ -n ${description} ]] && [[ ${description} != "${current_description}" ]]; then
result=$(jq --null-input \
--arg description "${description}" \
'$ARGS.named')
data=$(jq --slurp '.[0] * .[1]' <(echo "${data}") <(echo "${result}"))
fi
if [[ -n ${confidential} ]] && [[ ${confidential} != "${current_confidential}" ]]; then
result=$(jq --null-input \
--arg confidential "${confidential}" \
'$ARGS.named')
data=$(jq --slurp '.[0] * .[1]' <(echo "${data}") <(echo "${result}"))
fi
if (( ${#labels[@]} )); then
result=$(jq --null-input \
--arg add_labels "$(join_by , "${labels[@]}")" \
'$ARGS.named')
data=$(jq --slurp '.[0] * .[1]' <(echo "${data}") <(echo "${result}"))
fi
# edit the issue
if ! result=$(gitlab_project_issue_edit "${pkgbase}" "${iid}" "${params}" "${data}"); then
msg_error "Failed to edit issue ${BOLD}${pkgbase}${ALL_OFF} #${iid}"
pkgctl_issue_write_recovery_file "${pkgbase}" "${message}" "${recovery_file}" "${recover}"
exit 1
fi
# delete old recovery file if we succeeded
if [[ -f ${recovery_file} ]]; then
rm --force "${recovery_file}"
fi
issue_url="${GIT_PACKAGING_URL_HTTPS}/${project_path}/-/issues/${iid}"
msg_success "Updated issue ${BOLD}#${iid}${ALL_OFF}"
printf "%sView this issue on GitLab: %s%s\n" "${GRAY}" "${issue_url}" "${ALL_OFF}"
}
pkgctl_issue_write_recovery_file() {
local pkgbase=$1
local message=$2
local recovery_file=$3
if [[ -f ${recovery_file} ]]; then
msg_warn "Recovery file already exists: ${recovery_file}"
if ! prompt "${YELLOW}${BOLD}?${ALL_OFF} Are you sure you want to overwrite it?"; then
return 1
fi
fi
mkdir -p "$(dirname "${recovery_file}")"
printf "%s\n" "${message}" > "${recovery_file}"
printf "Created recovery file: %s\n" "${recovery_file}"
return 0
}

124
src/lib/issue/issue.sh Normal file
View File

@@ -0,0 +1,124 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
set -eo pipefail
pkgctl_issue_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [COMMAND] [OPTIONS]
Work with GitLab packaging issues.
COMMANDS
close Close an issue
comment Comment on an issue
create Create a new issue
edit Edit and modify an issue
list List project or group issues
move Move an issue to another project
reopen Reopen a closed issue
view Display information about an issue
OPTIONS
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} list libfoo libbar
$ ${COMMAND} view 4
_EOF_
}
pkgctl_issue() {
if (( $# < 1 )); then
pkgctl_issue_usage
exit 0
fi
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_usage
exit 0
;;
close)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/close.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/close.sh
pkgctl_issue_close "$@"
exit 0
;;
create)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/create.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/create.sh
pkgctl_issue_create "$@"
exit 0
;;
edit|update)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/edit.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/edit.sh
pkgctl_issue_edit "$@"
exit 0
;;
list)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/list.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/list.sh
pkgctl_issue_list "$@"
exit 0
;;
comment|note)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/comment.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/comment.sh
pkgctl_issue_comment "$@"
exit 0
;;
move)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/move.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/move.sh
pkgctl_issue_move "$@"
exit 0
;;
reopen)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/reopen.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/reopen.sh
pkgctl_issue_reopen "$@"
exit 0
;;
view)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/issue/view.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/issue/view.sh
pkgctl_issue_view "$@"
exit 0
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
die "invalid command: %s" "$1"
;;
esac
done
}

417
src/lib/issue/list.sh Normal file
View File

@@ -0,0 +1,417 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_LIST_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_LIST_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
# shellcheck source=src/lib/util/term.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
set -eo pipefail
pkgctl_issue_list_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [PKGBASE]
The pkgctl issue list command is used to list issues associated with a specific
packaging project or the entire packaging subgroup in Arch Linux. This command
facilitates efficient issue management by allowing users to list and filter
issues based on various criteria.
Results can also be displayed directly in a web browser for easier navigation
and review.
OPTIONS
-g, --group Get issues from the whole packaging subgroup
-w, --web View results in a browser
-h, --help Show this help text
FILTER
-A, --all Get all issues including closed
-c, --closed Get only closed issues
-U, --unconfirmed Shorthand to filter by unconfirmed status label
--search SEARCH Search <string> in the fields defined by --in
--in LOCATION Search in title or description (default: all)
-l, --label NAME Filter issue by label <name>
--confidentiality TYPE Filter by confidentiality
--priority PRIORITY Shorthand to filter by priority label
--resolution REASON Shorthand to filter by resolution label
--scope SCOPE Shorthand to filter by scope label
--severity SEVERITY Shorthand to filter by severity label
--status STATUS Shorthand to filter by status label
--assignee USERNAME Filter issues assigned to the given username
--assigned-to-me Shorthand to filter issues assigned to you
--author USERNAME Filter issues authored by the given username
--created-by-me Shorthand to filter issues created by you
EXAMPLES
$ ${COMMAND} libfoo libbar
$ ${COMMAND} --group --unconfirmed
_EOF_
}
pkgctl_issue_list() {
if (( $# < 1 )) && [[ ! -f PKGBUILD ]]; then
pkgctl_issue_list_usage
exit 0
fi
local paths path project_path params web_params label username issue_url
local group=0
local web=0
local confidential=0
local state=opened
local request_data=""
local search_in="all"
local labels=()
local assignee=
local author=
local scope=all
local confidentiality=
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_list_usage
exit 0
;;
-A|--all)
state=all
shift
;;
-c|--closed)
state=closed
shift
;;
-U|--unconfirmed)
labels+=("$(status_as_gitlab_label unconfirmed)")
shift
;;
-g|--group)
group=1
shift
;;
-w|--web)
web=1
shift
;;
--in)
(( $# <= 1 )) && die "missing argument for %s" "$1"
search_in=$2
shift 2
;;
--search)
(( $# <= 1 )) && die "missing argument for %s" "$1"
request_data="search=$2"
web_params+="&search=$2"
shift 2
;;
-l|--label)
(( $# <= 1 )) && die "missing argument for %s" "$1"
labels+=("$2")
shift 2
;;
--confidentiality)
(( $# <= 1 )) && die "missing argument for %s" "$1"
confidentiality=$2
if ! in_array "${confidentiality}" "${DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY[@]}"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
shift 2
;;
--priority)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(priority_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--resolution)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(resolution_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--scope)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(scope_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--severity)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(severity_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--status)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if ! label="$(status_as_gitlab_label "$2")"; then
die "invalid argument for %s: %s" "$1" "$2"
fi
labels+=("$label")
shift 2
;;
--assignee)
(( $# <= 1 )) && die "missing argument for %s" "$1"
assignee="$2"
shift 2
;;
--assigned-to-me)
scope=assigned_to_me
shift
;;
--author)
(( $# <= 1 )) && die "missing argument for %s" "$1"
author="$2"
shift 2
;;
--created-by-me)
scope=created_by_me
shift
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
paths=("$@")
break
;;
esac
done
if [[ ${search_in} == all ]]; then
search_in="title,description"
else
web_params+="&in=${search_in^^}"
fi
params+="&in=${search_in}"
if [[ ${state} != all ]]; then
params+="&state=${state}"
fi
web_params+="&state=${state}"
if (( ${#labels} )); then
params+="&labels=$(join_by , "${labels[@]}")"
web_params+="&label_name[]=$(join_by "&label_name[]=" "${labels[@]}")"
fi
if [[ -n ${scope} ]]; then
params+="&scope=${scope}"
if (( web )); then
if ! username=$(gitlab_api_get_user); then
exit 1
fi
case "${scope}" in
created_by_me) author=${username} ;;
assigned_to_me) assignee=${username} ;;
esac
fi
fi
if [[ -n ${assignee} ]]; then
params+="&assignee_username=${assignee}"
web_params+="&assignee_username=${assignee}"
fi
if [[ -n ${author} ]]; then
params+="&author_username=${author}"
web_params+="&author_username=${author}"
fi
if [[ -n ${confidentiality} ]]; then
if [[ ${confidentiality} == confidential ]]; then
params+="&confidential=true"
web_params+="&confidential=yes"
else
params+="&confidential=false"
web_params+="&confidential=no"
fi
fi
# check if invoked without any path from within a packaging repo
if (( ${#paths[@]} == 0 )); then
if [[ -f PKGBUILD ]] && (( ! group )); then
paths=("$(realpath --canonicalize-existing .)")
elif (( ! group )); then
pkgctl_issue_list_usage
exit 1
fi
fi
if (( web )) && ! command -v xdg-open &>/dev/null; then
die "The web option requires 'xdg-open'"
fi
local separator=" "
for path in "${paths[@]}"; do
# skip paths from a glob that aren't directories
if [[ -e "${path}" ]] && [[ ! -d "${path}" ]]; then
continue
fi
pkgbase=$(basename "${path}")
project_path=$(gitlab_project_name_to_path "${pkgbase}")
echo "${UNDERLINE}${pkgbase}${ALL_OFF}"
if (( web )); then
issue_url="${GIT_PACKAGING_URL_HTTPS}/${project_path}/-/issues/?${web_params}"
echo "Opening ${issue_url} in your browser."
xdg-open "${issue_url}"
continue
fi
status_dir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
printf "📡 Querying GitLab issues API..." > "${status_dir}/status"
term_spinner_start "${status_dir}"
if ! output=$(gitlab_projects_issues_list "${project_path}" "${status_dir}/status" "${params}" "${request_data}"); then
term_spinner_stop "${status_dir}"
echo
continue
fi
term_spinner_stop "${status_dir}"
issue_count=$(jq --compact-output 'length' <<< "${output}")
if (( issue_count == 0 )); then
echo "No open issues match your search"
echo
continue
else
echo "Showing ${issue_count} issues that match your search"
fi
print_issue_list "${output}"
done
if (( group )); then
if (( web )); then
issue_url="https://${GITLAB_HOST}/groups/${GIT_PACKAGING_NAMESPACE}/-/issues/?${web_params}"
echo "Opening ${issue_url} in your browser."
xdg-open "${issue_url}"
return
fi
status_dir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
printf "📡 Querying GitLab issues API..." > "${status_dir}/status"
term_spinner_start "${status_dir}"
if ! output=$(gitlab_group_issue_list "${GIT_PACKAGING_NAMESPACE_ID}" "${status_dir}/status" "${params}" "${request_data}"); then
term_spinner_stop "${status_dir}"
exit 1
fi
term_spinner_stop "${status_dir}"
print_issue_list "${output}"
fi
}
print_issue_list() {
local output=$1
local limit=${2:-100}
local i=0
local status_dir
local longest_pkgname
# limit results
output=$(jq ".[:${limit}]" <<< "${output}")
mapfile -t project_ids < <(
jq --raw-output '[.[].project_id] | unique[]' <<< "${output}")
status_dir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
printf "📡 Querying GitLab project names..." > "${status_dir}/status"
term_spinner_start "${status_dir}"
# read project_id to name mapping from cache
declare -A project_name_lookup=()
while read -r project_id project_name; do
project_name_lookup[${project_id}]=${project_name}
done < <(gitlab_lookup_project_names "${status_dir}/status" "${project_ids[@]}")
longest_pkgname=$(longest_package_name_from_ids "${project_ids[@]}")
term_spinner_stop "${status_dir}"
result_file=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-list.XXXXXXXXXX)
printf "📡 Collecting issue information %%spinner%%" > "${status_dir}/status"
term_spinner_start "${status_dir}"
local columns="ID,Title,Scope,Status,Severity,Age"
if (( group )); then
columns="ID,Package,Title,Scope,Status,Severity,Age"
fi
# pretty print each result
while read -r result; do
if (( i > limit )); then
break
fi
i=$(( ++i ))
{ read -r project_id; read -r iid; read -r title; read -r state; read -r created_at; read -r confidential; } < <(
jq --raw-output ".project_id, .iid, .title, .state, .created_at, .confidential" <<< "${result}"
)
mapfile -t labels < <(
jq --raw-output ".labels[]" <<< "${result}"
)
pkgbase=${project_name_lookup[${project_id}]}
created_at=$(relative_date_unit "${created_at}")
severity="$(gitlab_severity_from_labels "${labels[@]}")"
severity_color="$(gitlab_severity_color "${severity}")"
state_color="$(gitlab_issue_state_color "${state}")"
state="$(gitlab_issue_state_display "${state}")"
status="$(gitlab_issue_status_from_labels "${labels[@]}")"
status_color="$(gitlab_issue_status_color "${status}")"
status="$(gitlab_issue_status_short "${status}")"
scope="$(gitlab_scope_from_labels "${labels[@]}")"
scope_color="$(gitlab_scope_color "${scope}")"
scope="$(gitlab_scope_short "${scope}")"
title_space=$(( COLUMNS - 7 - 10 - 15 - 12 - 10 ))
if (( group )); then
title_space=$(( title_space - longest_pkgname ))
fi
if [[ ${confidential} == true ]]; then
title_space=$(( title_space - 2 ))
fi
title=$(trim_string "${title_space}" "${title}")
# gum is silly and doesn't allow double quotes
title=${title//\"/}
if [[ ${confidential} == true ]]; then
title="${YELLOW}${PKGCTL_TERM_ICON_CONFIDENTIAL} ${title}${ALL_OFF}"
fi
if (( group )); then
printf "%s\n" "${state_color}#$iid${ALL_OFF}${separator}${BOLD}${pkgbase}${separator}${ALL_OFF}${title}${separator}${scope_color}${scope}${ALL_OFF}${separator}${status_color}${status}${separator}${severity_color}${severity}${ALL_OFF}${separator}${GRAY}${created_at}${ALL_OFF}" \
>> "${result_file}"
else
printf "%s\n" "${state_color}#$iid${ALL_OFF}${separator}${title}${separator}${scope_color}${scope}${ALL_OFF}${separator}${status_color}${status}${separator}${severity_color}${severity}${ALL_OFF}${separator}${GRAY}${created_at}${ALL_OFF}" \
>> "${result_file}"
fi
done < <(jq --compact-output '.[]' <<< "${output}")
term_spinner_stop "${status_dir}"
gum table --print --border="none" --columns="${columns}" \
--separator="${separator}" --file "${result_file}"
}

156
src/lib/issue/move.sh Normal file
View File

@@ -0,0 +1,156 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_MOVE_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_MOVE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/cache.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/cache.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
set -eo pipefail
pkgctl_issue_move_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [IID] [DESTINATION_PACKAGE]
The move command allows users to transfer an issue from one project to another
within the Arch Linux packaging group. This is useful when an issue is
identified to be more relevant or better handled in a different project.
By default, the command operates within the current directory, but users can
specify a different package base from which to move the issue.
Users must specify the issue ID (IID) and the destination package to which the
issue should be moved. A comment message explaining the reason for the move can
be provided directly through the command line.
OPTIONS
-p, --package PKGBASE Move from <pkgbase> instead of the current directory
-m, --message MSG Use the provided message as the comment
-e, --edit Edit the comment using an editor
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} 42 to-bar
$ ${COMMAND} --package from-foo 42 to-bar
_EOF_
}
pkgctl_issue_move() {
if (( $# < 1 )); then
pkgctl_issue_move_usage
exit 0
fi
local iid=""
local pkgbase=""
local message=""
local edit=0
local to_project_name to_project_id project_path issue_url to_iid result
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_move_usage
exit 0
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-m|--message)
(( $# <= 1 )) && die "missing argument for %s" "$1"
message=$2
shift 2
;;
-e|--edit)
edit=1
shift
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
break
;;
esac
done
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
if (( $# < 2 )); then
pkgctl_issue_move_usage
exit 1
fi
iid=$1
to_project_name=$(basename "$2")
# spawn editor
if (( edit )); then
msgfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-note.XXXXXXXXXX.md)
printf "%s\n" "${message}" >> "${msgfile}"
if [[ -n $VISUAL ]]; then
$VISUAL "${msgfile}" || die
elif [[ -n $EDITOR ]]; then
$EDITOR "${msgfile}" || die
else
die "No usable editor found (tried \$VISUAL, \$EDITOR)."
fi
message=$(cat "${msgfile}")
fi
if ! result=$(gitlab_project "${to_project_name}"); then
msg_error "Failed to query target project ${BOLD}${to_project_name}${ALL_OFF}"
exit 1
fi
if ! to_project_id=$(jq --raw-output ".id" <<< "${result}"); then
msg_error "Failed to query project id for ${BOLD}${to_project_name}${ALL_OFF}"
exit 1
fi
# comment on issue
if [[ -n ${message} ]]; then
if ! result=$(gitlab_create_project_issue_note "${pkgbase}" "${iid}" "${message}"); then
msg_error "Failed to comment on issue ${BOLD}#${iid}${ALL_OFF}"
exit 1
fi
msg_success "Commented on issue ${BOLD}#${iid}${ALL_OFF}"
fi
if ! result=$(gitlab_project_issue_move "${pkgbase}" "${iid}" "${to_project_id}"); then
msg_error "Failed to move issue ${BOLD}#${iid}${ALL_OFF} to ${BOLD}${to_project_name}${ALL_OFF}"
exit 1
fi
if ! to_iid=$(jq --raw-output ".iid" <<< "${result}"); then
msg_error "Failed to query issue id for ${BOLD}${to_project_name}${ALL_OFF}"
exit 1
fi
project_path=$(gitlab_project_name_to_path "${to_project_name}")
issue_url="${GIT_PACKAGING_URL_HTTPS}/${project_path}/-/issues/${to_iid}"
msg_success "Moved issue ${BOLD}${pkgbase}${ALL_OFF} ${BOLD}#${iid}${ALL_OFF} to ${BOLD}${to_project_name}${ALL_OFF} ${BOLD}#${to_iid}${ALL_OFF}"
echo
printf "%sView this issue on GitLab: %s%s\n" "${GRAY}" "${issue_url}" "${ALL_OFF}"
}

189
src/lib/issue/reopen.sh Normal file
View File

@@ -0,0 +1,189 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_REOPEN_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_REOPEN_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
set -eo pipefail
pkgctl_issue_reopen_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [IID]
The reopen command is used to reopen a previously closed issue in Arch Linux
packaging projects. This command is useful when an issue needs to be revisited
or additional work is required after it was initially closed.
By default, the command operates within the current directory, but users can
specify a different package base if needed.
Users can provide a message directly through the command line to explain the
reason for reopening the issue.
OPTIONS
-p, --package PKGBASE Interact with <pkgbase> instead of the current directory
-m, --message MSG Use the provided message as the comment
-e, --edit Edit the comment using an editor
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} 42
$ ${COMMAND} --package linux 42
_EOF_
}
pkgctl_issue_reopen() {
if (( $# < 1 )); then
pkgctl_issue_reopen_usage
exit 0
fi
local iid=""
local pkgbase=""
local message=""
local edit=0
local issue note result resolution labels
local params="state_event=reopen"
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_reopen_usage
exit 0
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-m|--message)
(( $# <= 1 )) && die "missing argument for %s" "$1"
message=$2
shift 2
;;
-e|--edit)
edit=1
shift
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
iid=$1
shift
;;
esac
done
if [[ -z ${iid} ]]; then
die "missing issue iid argument"
fi
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
# spawn editor
if (( edit )); then
msgfile=$(mktemp --tmpdir="${WORKDIR}" pkgctl-issue-note.XXXXXXXXXX.md)
printf "%s\n" "${message}" >> "${msgfile}"
if [[ -n $VISUAL ]]; then
$VISUAL "${msgfile}" || die
elif [[ -n $EDITOR ]]; then
$EDITOR "${msgfile}" || die
else
die "No usable editor found (tried \$VISUAL, \$EDITOR)."
fi
message=$(< "${msgfile}")
fi
# query issue details
if ! result=$(gitlab_project_issue "${pkgbase}" "${iid}"); then
die "Failed to fetch issue ${pkgbase} #${iid}"
fi
mapfile -t labels < <(
jq --raw-output ".labels[]" <<< "${result}"
)
if resolution=$(gitlab_resolution_from_labels "${labels[@]}"); then
resolution=$(resolution_as_gitlab_label "${resolution}")
params+="&remove_labels=${resolution}"
fi
# comment on issue
if [[ -n ${message} ]]; then
if ! note=$(gitlab_create_project_issue_note "${pkgbase}" "${iid}" "${message}"); then
msg_error "Failed to comment on issue ${BOLD}#${iid}${ALL_OFF}"
exit 1
fi
msg_success "Commented on issue ${BOLD}#${iid}${ALL_OFF}"
fi
# reopen issue
if ! issue=$(gitlab_project_issue_edit "${pkgbase}" "${iid}" "${params}"); then
msg_error "Failed to reopen issue ${BOLD}#${iid}${ALL_OFF}"
exit 1
fi
msg_success "Reopened issue ${BOLD}#${iid}${ALL_OFF}"
echo
{ read -r iid; read -r title; read -r state; read -r created_at; read -r author; } < <(
jq --raw-output ".iid, .title, .state, .created_at, .author.username" <<< "${issue}"
)
mapfile -t labels < <(
jq --raw-output ".labels[]" <<< "${issue}"
)
severity="$(gitlab_severity_from_labels "${labels[@]}")"
severity_color="$(gitlab_severity_color "${severity}")"
created_at=$(relative_date_unit "${created_at}")
state_color="$(gitlab_issue_state_color "${state}")"
state="$(gitlab_issue_state_display "${state}")"
status="$(gitlab_issue_status_from_labels "${labels[@]}")"
status_color="$(gitlab_issue_status_color "${status}")"
scope="$(gitlab_scope_from_labels "${labels[@]}")"
scope_color="$(gitlab_scope_color "${scope}")"
scope_label=""
if [[ ${scope} != unknown ]]; then
scope_label="${scope_color}${scope}${ALL_OFF} ${GRAY}${ALL_OFF} "
fi
printf "%s%s • %sseverity %s • %s • %s%sopened by %s %s ago%s\n" \
"${state_color}${state}${ALL_OFF}" "${GRAY}" "${severity_color}" "${severity}${GRAY}" \
"${status_color}${status}${GRAY}" "${scope_label}" "${GRAY}" "${author}" "${created_at}" "${ALL_OFF}"
printf "%s %s\n" "${BOLD}${title}${ALL_OFF}" "${GRAY}#${iid}${ALL_OFF}"
# show comment
if [[ -n ${note} ]]; then
{ read -r created_at; read -r author; } < <(
jq --raw-output ".created_at, .author.username" <<< "${note}"
)
body=$(jq --raw-output ".body" <<< "${note}")
created_at=$(relative_date_unit "${created_at}")
echo
echo "${BOLD}Comments / Notes${ALL_OFF}"
printf -v spaces '%*s' $(( COLUMNS - 2 )) ''
printf '%s\n\n' "${spaces// /─}"
printf "%s commented%s %s ago%s\n" "${author}" "${GRAY}" "${created_at}" "${ALL_OFF}"
echo "${body}" | glow
fi
}

209
src/lib/issue/view.sh Normal file
View File

@@ -0,0 +1,209 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_ISSUE_VIEW_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_ISSUE_VIEW_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
# shellcheck source=src/lib/util/term.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
set -eo pipefail
pkgctl_issue_view_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [IID]
This command is designed to display detailed information about a specific issue
in Arch Linux packaging projects. It gathers and pretty prints all relevant
data about the issue, providing a comprehensive view that includes the issue's
description, status as well as labels and creation date.
By default, the command operates within the current directory, but users have
the option to specify a different package base. Additionally, users can choose
to view the issue in a web browser for a more interactive experience.
OPTIONS
-p, --package PKGBASE Interact with <pkgbase> instead of the current directory
-c, --comments Show issue comments and activities
-w, --web Open issue in a browser
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} 4
$ ${COMMAND} --web --package linux 4
_EOF_
}
pkgctl_issue_view() {
if (( $# < 1 )); then
pkgctl_issue_view_usage
exit 0
fi
local web=0
local comments=0
local pkgbase=""
local iid=""
local project_path
# option checking
while (( $# )); do
case $1 in
-h|--help)
pkgctl_issue_view_usage
exit 0
;;
-p|--package)
(( $# <= 1 )) && die "missing argument for %s" "$1"
pkgbase=$2
shift 2
;;
-w|--web)
web=1
shift
;;
-c|--comments)
comments=1
shift
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
iid=$1
shift
;;
esac
done
if [[ -z ${iid} ]]; then
die "missing issue iid argument"
fi
if [[ -z ${pkgbase} ]]; then
if ! [[ -f PKGBUILD ]]; then
die "missing --package option or PKGBUILD in current directory"
fi
pkgbase=$(realpath --canonicalize-existing .)
fi
pkgbase=$(basename "${pkgbase}")
project_path=$(gitlab_project_name_to_path "${pkgbase}")
if ! result=$(gitlab_project_issue "${pkgbase}" "${iid}"); then
die "Failed to view issue ${pkgbase} #${iid}"
fi
{ read -r iid; read -r title; read -r state; read -r created_at; read -r closed_at; read -r author; } < <(
jq --raw-output ".iid, .title, .state, .created_at, .closed_at, .author.username" <<< "${result}"
)
{ read -r upvotes; read -r downvotes; read -r user_notes_count; read -r confidential; } < <(
jq --raw-output ".upvotes, .downvotes, .user_notes_count, .confidential" <<< "${result}"
)
description=$(jq --raw-output ".description" <<< "${result}")
mapfile -t labels < <(
jq --raw-output ".labels[]" <<< "${result}"
)
mapfile -t assignees < <(
jq --raw-output ".assignees[].username" <<< "${result}"
)
if [[ ${closed_at} != null ]]; then
closed_by=$(jq --raw-output ".closed_by.username" <<< "${result}")
fi
issue_url="${GIT_PACKAGING_URL_HTTPS}/${project_path}/-/issues/${iid}"
if (( web )); then
if ! command -v xdg-open &>/dev/null; then
die "The web option requires 'xdg-open'"
fi
echo "Opening ${issue_url} in your browser."
xdg-open "${issue_url}"
return
fi
severity="$(gitlab_severity_from_labels "${labels[@]}")"
severity_color="$(gitlab_severity_color "${severity}")"
created_at=$(relative_date_unit "${created_at}")
state_color="$(gitlab_issue_state_color "${state}")"
state="$(gitlab_issue_state_display "${state}")"
status="$(gitlab_issue_status_from_labels "${labels[@]}")"
status_color="$(gitlab_issue_status_color "${status}")"
scope="$(gitlab_scope_from_labels "${labels[@]}")"
scope_color="$(gitlab_scope_color "${scope}")"
scope_label=""
if [[ ${scope} != unknown ]]; then
scope_label="${scope_color}${scope}${ALL_OFF} ${GRAY}${ALL_OFF} "
fi
resolution_label=""
if resolution="$(gitlab_resolution_from_labels "${labels[@]}")"; then
resolution_color="$(gitlab_resolution_color "${resolution}")"
resolution_label="${resolution_color}${resolution}${ALL_OFF} ${GRAY}${ALL_OFF} "
fi
confidential_label=""
if [[ ${confidential} == true ]]; then
confidential_label="${YELLOW}${PKGCTL_TERM_ICON_CONFIDENTIAL} CONFIDENTIAL${ALL_OFF} ${GRAY}${ALL_OFF} "
fi
printf "%s%s • %s%s%sseverity %s • %s • %s%sopened by %s %s ago%s\n" \
"${state_color}${state}${ALL_OFF}" "${GRAY}" "${confidential_label}" "${resolution_label}" "${severity_color}" "${severity}${ALL_OFF}${GRAY}" \
"${status_color}${status}${ALL_OFF}${GRAY}" "${scope_label}" "${GRAY}" "${author}" "${created_at}" "${ALL_OFF}"
printf "%s %s\n\n" "${BOLD}${title}${ALL_OFF}" "${GRAY}#${iid}${ALL_OFF}"
printf "%s\n" "${description}" | glow
printf "\n\n"
printf "%s%s upvotes • %s downvotes • %s comments%s\n" "${GRAY}" "${upvotes}" "${downvotes}" "${user_notes_count}" "${ALL_OFF}"
printf "%s %s\n" "${BOLD}Labels:${ALL_OFF}" "$(join_by ", " "${labels[@]}")"
printf "%s %s\n" "${BOLD}Assignees:${ALL_OFF}" "$(join_by ", " "${assignees[@]}")"
if [[ ${closed_at} != null ]]; then
closed_at=$(relative_date_unit "${closed_at}")
printf "%s %s %s ago\n" "${BOLD}Closed by:${ALL_OFF}" "${closed_by}" "${closed_at}"
fi
if (( comments )); then
printf "\n\n"
echo "${BOLD}Comments / Notes${ALL_OFF}"
printf -v spaces '%*s' $(( COLUMNS - 2 )) ''
printf '%s\n' "${spaces// /─}"
printf "\n\n"
status_dir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-gitlab-api.XXXXXXXXXX)
printf "📡 Querying GitLab issue notes API..." > "${status_dir}/status"
term_spinner_start "${status_dir}"
if ! output=$(gitlab_project_issue_notes "${project_path}" "${iid}" "${status_dir}/status" "sort=asc&order_by=created_at"); then
term_spinner_stop "${status_dir}"
msg_error "Failed to fetch comments"
exit 1
fi
term_spinner_stop "${status_dir}"
# pretty print each result
while read -r result; do
{ read -r created_at; read -r author; } < <(
jq --raw-output ".created_at, .author.username" <<< "${result}"
)
body=$(jq --raw-output ".body" <<< "${result}")
created_at=$(relative_date_unit "${created_at}")
printf "%s commented%s %s ago%s\n" "${author}" "${GRAY}" "${created_at}" "${ALL_OFF}"
echo "${body}" | glow
echo
done < <(jq --compact-output '.[]' <<< "${output}")
echo "$output" > /tmp/notes.json
fi
echo
printf "%sView this issue on GitLab: %s%s\n" "${GRAY}" "${issue_url}" "${ALL_OFF}"
}

66
src/lib/license.sh Normal file
View File

@@ -0,0 +1,66 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_LICENSE_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_LICENSE_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
set -e
pkgctl_license_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [COMMAND] [OPTIONS]
Check and manage package license compliance.
COMMANDS
check Check package license compliance
setup Automatically detect and setup a basic REUSE config
OPTIONS
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} check libfoo linux libbar
$ ${COMMAND} setup libfoo
_EOF_
}
pkgctl_license() {
if (( $# < 1 )); then
pkgctl_license_usage
exit 0
fi
while (( $# )); do
case $1 in
-h|--help)
pkgctl_license_usage
exit 0
;;
check)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/license/check.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/license/check.sh
pkgctl_license_check "$@"
exit $?
;;
setup)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/license/setup.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/license/setup.sh
pkgctl_license_setup "$@"
exit 0
;;
*)
die "invalid argument: %s" "$1"
;;
esac
done
}

147
src/lib/license/check.sh Normal file
View File

@@ -0,0 +1,147 @@
#!/bin/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
[[ -z ${DEVTOOLS_INCLUDE_LICENSE_CHECK_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_LICENSE_CHECK_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
source /usr/share/makepkg/util/message.sh
set -eo pipefail
readonly PKGCTL_LICENSE_CHECK_EXIT_COMPLIANT=0
export PKGCTL_LICENSE_CHECK_EXIT_COMPLIANT
readonly PKGCTL_LICENSE_CHECK_EXIT_FAILURE=2
export PKGCTL_LICENSE_CHECK_EXIT_FAILURE
pkgctl_license_check_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
Checks package licensing compliance using REUSE and also verifies
whether a LICENSE file with the expected Arch Linux-specific 0BSD
license text exists.
Upon execution, it runs 'reuse lint'.
OPTIONS
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} neovim vim
_EOF_
}
pkgctl_license_check() {
local pkgbases=()
local verbose=0
local license_text
license_text=$(< "${_DEVTOOLS_LIBRARY_DIR}"/data/LICENSE)
local exit_code=${PKGCTL_LICENSE_CHECK_EXIT_COMPLIANT}
while (( $# )); do
case $1 in
-h|--help)
pkgctl_license_check_usage
exit 0
;;
--)
shift
break
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
pkgbases=("$@")
break
;;
esac
done
if ! command -v reuse &>/dev/null; then
die "The \"$_DEVTOOLS_COMMAND\" command requires the 'reuse' CLI tool"
fi
# Check if used without pkgbases in a packaging directory
if (( ${#pkgbases[@]} == 0 )); then
if [[ -f PKGBUILD ]]; then
pkgbases=(".")
else
pkgctl_license_check_usage
exit 1
fi
fi
# enable verbose mode when we only have a single item to check
if (( ${#pkgbases[@]} == 1 )); then
verbose=1
fi
for path in "${pkgbases[@]}"; do
# skip paths that are not directories
if [[ ! -d "${path}" ]]; then
continue
fi
pushd "${path}" >/dev/null
if [[ ! -f PKGBUILD ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} no PKGBUILD found"
return 1
fi
# reset common PKGBUILD variables
unset pkgbase
# shellcheck source=contrib/makepkg/PKGBUILD.proto
if ! . ./PKGBUILD; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} failed to source PKGBUILD"
return 1
fi
pkgbase=${pkgbase:-$pkgname}
if [[ ! -e LICENSE ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} is missing the LICENSE file"
return "${PKGCTL_LICENSE_CHECK_EXIT_FAILURE}"
fi
if [[ ! -L LICENSES/0BSD.txt ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} LICENSES/0BSD should be a symlink to LICENSE but it isn't"
return "${PKGCTL_LICENSE_CHECK_EXIT_FAILURE}"
fi
# Check if the local LICENSE file mismatches our expectations
if [[ $license_text != $(< LICENSE) ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} LICENSE file doesn't have the expected Arch Linux-specific license text"
return "${PKGCTL_LICENSE_CHECK_EXIT_FAILURE}"
fi
# Check for REUSE compliance
if ! reuse lint --json | jq --exit-status '.summary.compliant' &>/dev/null; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} repository is not REUSE compliant"
exit_code=${PKGCTL_LICENSE_CHECK_EXIT_FAILURE}
# re-execute reuse lint for human readable output
if (( verbose )); then
reuse lint
fi
popd >/dev/null
continue
fi
msg_success "${BOLD}${pkgbase}:${ALL_OFF} repository is REUSE compliant"
popd >/dev/null
done
# return status based on results
return "${exit_code}"
}

272
src/lib/license/setup.sh Normal file
View File

@@ -0,0 +1,272 @@
#!/bin/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
[[ -z ${DEVTOOLS_INCLUDE_LICENSE_SETUP_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_LICENSE_SETUP_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/license/check.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/license/check.sh
source /usr/share/makepkg/util/message.sh
set -eo pipefail
shopt -s nullglob
pkgctl_license_setup_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
Automate the creation of a basic REUSE configuration by analyzing the
license array specified in the PKGBUILD file of a package.
If no PKGBASE is specified, the command defaults to using the current
working directory.
OPTIONS
-f, --force Overwrite existing REUSE configuration
-h, --help Show this help text
--no-check Do not run license check after setup
EXAMPLES
$ ${COMMAND} neovim vim
_EOF_
}
pkgctl_license_setup() {
local pkgbases=()
local force=0
local run_check=1
local path exit_code
local checks=()
while (( $# )); do
case $1 in
-h|--help)
pkgctl_license_setup_usage
exit 0
;;
-f|--force)
force=1
shift
;;
--no-check)
run_check=0
shift
;;
--)
shift
break
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
pkgbases=("$@")
break
;;
esac
done
if ! command -v reuse &>/dev/null; then
die "The \"$_DEVTOOLS_COMMAND\" command requires the 'reuse' CLI tool"
fi
# Check if used without pkgbases in a packaging directory
if (( ${#pkgbases[@]} == 0 )); then
if [[ -f PKGBUILD ]]; then
pkgbases=(".")
else
pkgctl_license_setup_usage
exit 1
fi
fi
exit_code=0
for path in "${pkgbases[@]}"; do
# skip paths that are not directories
if [[ ! -d "${path}" ]]; then
continue
fi
pushd "${path}" >/dev/null
if license_setup "${path}" "${force}"; then
checks+=("${path}")
else
exit_code=1
fi
popd >/dev/null
done
# run checks on the setup targets
if (( run_check )) && (( ${#checks[@]} >= 1 )); then
echo
echo 📡 Running checks...
pkgctl_license_check "${checks[@]}" || true
fi
return "$exit_code"
}
license_setup() {
local path=$1
local force=$2
local pkgbase pkgname license
if [[ ! -f PKGBUILD ]]; then
msg_error "${BOLD}${path}:${ALL_OFF} no PKGBUILD found"
return 1
fi
# shellcheck source=contrib/makepkg/PKGBUILD.proto
if ! . ./PKGBUILD; then
msg_error "${BOLD}${path}:${ALL_OFF} failed to source PKGBUILD"
return 1
fi
pkgbase=${pkgbase:-$pkgname}
# setup LICENSE file
if ! license_file_setup "${pkgbase}" "${force}"; then
return 1
fi
# setup REUSE.toml
if ! reuse_setup "${pkgbase}" "${force}" "${license[@]}"; then
return 1
fi
}
is_valid_license() {
local license_name=$1
local supported_licenses
supported_licenses=$(reuse supported-licenses | awk '{print $1}')
if grep --quiet "^${license_name}$" <<< "$supported_licenses"; then
return 0
else
return 1
fi
}
license_file_setup() {
local pkgbase=$1
local force=$2
local license_text
license_text=$(< "${_DEVTOOLS_LIBRARY_DIR}"/data/LICENSE)
# Write LICENSE file, or check if it mismatches
if (( force )) || [[ ! -f LICENSE ]]; then
printf "%s\n" "${license_text}" > LICENSE
msg_success "${BOLD}${pkgbase}:${ALL_OFF} successfully configured LICENSE"
elif [[ -f LICENSE ]]; then
# if there is a license file, check whether it has the text we expect
existing_license="$(< LICENSE)"
if [[ "${license_text}" != "${existing_license}" ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} existing LICENSE file doesn't have expected content, use --force to overwrite"
return 1
fi
fi
# make sure the LICENSE file is found by REUSE
mkdir --parents LICENSES
ln --symbolic --force ../LICENSE LICENSES/0BSD.txt
}
reuse_default_annotations() {
cat << EOF
version = 1
[[annotations]]
path = [
"PKGBUILD",
"README.md",
"keys/**",
".SRCINFO",
".gitignore",
".nvchecker.toml",
"*.install",
"*.sysusers",
"*sysusers.conf",
"*.tmpfiles",
"*tmpfiles.conf",
"*.logrotate",
"*.pam",
"*.service",
"*.socket",
"*.timer",
"*.desktop",
"*.hook",
]
SPDX-FileCopyrightText = "Arch Linux contributors"
SPDX-License-Identifier = "0BSD"
EOF
}
reuse_setup() {
local pkgbase=$1
local force=$2
shift 2
local license=("$@")
# Check if REUSE.toml already exists
if (( ! force )) && [[ -f REUSE.toml ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} REUSE.toml already exists, use --force to overwrite"
return 1
fi
reuse_default_annotations > REUSE.toml
local warning_occurred=0
local patches=(*.patch)
# If there are patches and there's only a single well-known license listed in the package,
# we can generate the annotations for the patches, otherwise we will fail to do so and warn
# the user.
if (( ${#patches} )); then
# If there are multiple licenses, we can't make a good guess about which license the
# patches should have. In case the first element contains a space, we are dealing with
# a complex SPDX license identifier.
if (( ${#license[@]} > 1 )) || [[ ${license[0]} =~ [[:space:]] ]]; then
msg_warn "${BOLD}${pkgbase}:${ALL_OFF} .patch files were found but couldn't automatically guess a suitable license because PKGBUILD has multiple licenses"
patch_annotations "${pkgbase}" "TODO-Choose-a-license" "${patches[@]}" >> REUSE.toml
warning_occurred=1
elif ! is_valid_license "${license[0]}"; then
msg_warn "${BOLD}${pkgbase}:${ALL_OFF} .patch files were found but couldn't automatically guess a suitable license because the PKGBUILD license '${license[0]}' is not a recognized SPDX license"
patch_annotations "${pkgbase}" "TODO-Choose-a-license" "${patches[@]}" >> REUSE.toml
warning_occurred=1
else
patch_annotations "${pkgbase}" "${license[0]}" "${patches[@]}" >> REUSE.toml
fi
fi
if (( warning_occurred )); then
msg_warn "${BOLD}${pkgbase}:${ALL_OFF} configured REUSE but a warning occurred, manually edit and fix REUSE.toml"
return 1
else
reuse download --all
msg_success "${BOLD}${pkgbase}:${ALL_OFF} successfully configured REUSE.toml"
fi
}
patch_annotations() {
local pkgbase=$1
local license_identifier=$2
shift 2
local patches=("$@")
local annotations
annotations+="\n[[annotations]]\n"
annotations+="path = [\n"
for patch in "${patches[@]}"; do
annotations+=" \"$(basename "${patch}")\",\n"
done
annotations+="]\n"
annotations+="SPDX-FileCopyrightText = \"${pkgbase} contributors\"\n"
annotations+="SPDX-License-Identifier = \"${license_identifier}\""
echo -e "${annotations}"
}

View File

@@ -124,7 +124,7 @@ pkgctl_release() {
# Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then
update_pacman_repo_cache
update_pacman_repo_cache multilib
# Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${DEVTOOLS_VALID_REPOS[@]}"; then
die "Invalid repository target: %s" "${REPO}"
@@ -157,6 +157,11 @@ pkgctl_release() {
repo=${REPO}
fi
# output a warning if .nvchecker.toml does not exists
if [[ ! -f ".nvchecker.toml" ]]; then
warning "Nvchecker integration is not set, run 'pkgctl version setup --help' to see how to automate the creation of the '.nvchecker.toml' configuration file"
fi
if (( TESTING )); then
repo="${repo}-testing"
elif (( STAGING )); then

View File

@@ -27,6 +27,7 @@ pkgctl_repo_usage() {
without SSH access using read-only HTTPS.
COMMANDS
clean Remove untracked files from the working tree
clone Clone a package repository
configure Configure a clone according to distro specs
create Create a new GitLab package repository
@@ -37,6 +38,7 @@ pkgctl_repo_usage() {
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} clean --interactive *
$ ${COMMAND} clone libfoo linux libbar
$ ${COMMAND} clone --maintainer mynickname
$ ${COMMAND} configure *
@@ -59,6 +61,14 @@ pkgctl_repo() {
pkgctl_repo_usage
exit 0
;;
clean)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/repo/clean.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/repo/clean.sh
pkgctl_repo_clean "$@"
exit 0
;;
clone)
_DEVTOOLS_COMMAND+=" $1"
shift

114
src/lib/repo/clean.sh Normal file
View File

@@ -0,0 +1,114 @@
#!/bin/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_REPO_CLEAN_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_REPO_CLEAN_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
source /usr/share/makepkg/util/message.sh
set -eo pipefail
pkgctl_repo_clean_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTION] [PATH]...
Cleans the working tree by recursively removing files that are not under
version control, starting from the current directory.
Files unknown to Git as well as ignored files are removed. This can, for
example, be useful to remove all build products.
OPTIONS
-i, --interactive Show what would be done and clean files interactively
-n, --dry-run Don't remove anything, just show what would be done
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} libfoo linux libbar
$ ${COMMAND} --interactive libfoo linux libbar
$ ${COMMAND} --dry-run *
_EOF_
}
pkgctl_repo_clean() {
# options
local git_clean_options=()
local paths
local path pkgbase
while (( $# )); do
case $1 in
-i|--interactive)
git_clean_options+=("$1")
shift
;;
-n|--dry-run)
git_clean_options+=("$1")
shift
;;
-h|--help)
pkgctl_repo_clean_usage
exit 0
;;
--)
shift
break
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
paths=("$@")
break
;;
esac
done
# check if invoked without any path from within a packaging repo
if (( ${#paths[@]} == 0 )); then
paths=(".")
fi
# print message about the work chunk
printf "🗑️ Removing untracked files from %s working trees\n" "${BOLD}${#paths[@]}${ALL_OFF}"
for path in "${paths[@]}"; do
# skip paths that are not directories
if [[ ! -d "${path}" ]]; then
continue
fi
if [[ ! -f "${path}/PKGBUILD" ]]; then
msg_error "Not a package repository: ${path}"
continue
fi
if [[ ! -d "${path}/.git" ]]; then
msg_error "Not a Git repository: ${path}"
continue
fi
pkgbase=$(basename "$(realpath "${path}")")
pkgbase=${pkgbase%.git}
# run dry mode to see if git would clean any files
if [[ ! $(git -C "${path}" clean -x -d --dry-run 2>&1) ]]; then
continue
fi
# git clean untracked files
msg_success "Cleaning ${BOLD}${pkgbase}${ALL_OFF}"
if ! git -C "${path}" clean -x -d --force "${git_clean_options[@]}"; then
msg_error "Failed to remove untracked files"
fi
echo
done
}

View File

@@ -65,6 +65,7 @@ pkgctl_repo_clone() {
local CONFIGURE_OPTIONS=()
local jobs=
jobs=$(nproc)
local -a pkgbases
# variables
local command=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}

View File

@@ -271,6 +271,7 @@ pkgctl_repo_configure() {
if [[ -n $GPGKEY ]]; then
git config commit.gpgsign true
git config user.signingKey "${GPGKEY}"
git config gpg.format openpgp
fi
# set default git exclude

View File

@@ -8,8 +8,6 @@ DEVTOOLS_INCLUDE_SEARCH_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/cache.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/cache.sh
# shellcheck source=src/lib/api/gitlab.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/api/gitlab.sh
# shellcheck source=src/lib/valid-search.sh
@@ -95,10 +93,8 @@ pkgctl_search() {
# variables
local bat_style="header,grid"
local default_filter="-path:keys/pgp/*.asc"
local graphql_lookup_batch=200
local output result query entries from until length
local project_name_cache_file project_name_lookup project_ids project_id project_name project_slice
local mapping_output path startline currentline data line
local output result project_name_lookup project_ids project_id project_name
local path startline currentline data line
while (( $# )); do
case $1 in
@@ -176,68 +172,20 @@ pkgctl_search() {
term_spinner_stop "${status_dir}"
msg_success "Querying GitLab search API"
# collect project ids whose name needs to be looked up
project_name_cache_file=$(get_cache_file gitlab/project_id_to_name)
lock 11 "${project_name_cache_file}" "Locking project name cache"
mapfile -t project_ids < <(
jq --raw-output '[.[].project_id] | unique[]' <<< "${output}" | \
grep --invert-match --file <(awk '{ print $1 }' < "${project_name_cache_file}" ))
jq --raw-output '[.[].project_id] | unique[]' <<< "${output}")
# look up project names
tmp_file=$(mktemp --tmpdir="${WORKDIR}" pkgctl-gitlab-api-spinner.tmp.XXXXXXXXXX)
printf "📡 Querying GitLab project names..." > "${status_dir}/status"
term_spinner_start "${status_dir}"
local entries="${#project_ids[@]}"
local until=0
while (( until < entries )); do
from=${until}
until=$(( until + graphql_lookup_batch ))
if (( until > entries )); then
until=${entries}
fi
length=$(( until - from ))
percentage=$(( 100 * until / entries ))
printf "📡 Querying GitLab project names: %s/%s [%s] %%spinner%%" \
"${BOLD}${until}" "${entries}" "${percentage}%${ALL_OFF}" \
> "${tmp_file}"
mv "${tmp_file}" "${status_dir}/status"
project_slice=("${project_ids[@]:${from}:${length}}")
printf -v projects '"gid://gitlab/Project/%s",' "${project_slice[@]}"
query='{
projects(after: "" ids: ['"${projects}"']) {
pageInfo {
startCursor
endCursor
hasNextPage
}
nodes {
id
name
}
}
}'
mapping_output=$(gitlab_api_get_project_name_mapping "${query}")
# update cache
while read -r project_id project_name; do
printf "%s %s\n" "${project_id}" "${project_name}" >> "${project_name_cache_file}"
done < <(jq --raw-output \
'.[] | "\(.id | rindex("/") as $lastSlash | .[$lastSlash+1:]) \(.name)"' \
<<< "${mapping_output}")
done
term_spinner_stop "${status_dir}"
msg_success "Querying GitLab project names"
# read project_id to name mapping from cache
declare -A project_name_lookup=()
while read -r project_id project_name; do
project_name_lookup[${project_id}]=${project_name}
done < "${project_name_cache_file}"
done < <(gitlab_lookup_project_names "${status_dir}/status" "${project_ids[@]}")
# close project name cache lock
lock_close 11
term_spinner_stop "${status_dir}"
msg_success "Querying GitLab project names"
# output mode JSON
if [[ ${output_format} == json ]]; then

View File

@@ -22,9 +22,11 @@ makepkg_source_package() {
return
fi
(
# shellcheck disable=SC2030 disable=SC2031
export LIBMAKEPKG_LINT_PKGBUILD_SH=1
lint_pkgbuild() { :; }
# shellcheck disable=SC2030 disable=SC2031
export LIBMAKEPKG_SRCINFO_SH=1
write_srcinfo() { print_srcinfo; }
@@ -35,3 +37,21 @@ makepkg_source_package() {
source "$(command -v makepkg)"
)
}
makepkg_generate_integrity() {
if [[ -z ${DEVTOOLS_GENERATE_INTEGRITY} ]]; then
[[ -z ${WORKDIR:-} ]] && setup_workdir
export WORKDIR DEVTOOLS_INCLUDE_COMMON_SH
bash -$- -c "DEVTOOLS_GENERATE_INTEGRITY=1; source '${BASH_SOURCE[0]}' && ${FUNCNAME[0]}"
return
fi
(
# shellcheck disable=SC2030 disable=SC2031
export LIBMAKEPKG_LINT_PKGBUILD_SH=1
lint_pkgbuild() { :; }
set +e -- --geninteg
# shellcheck source=/usr/bin/makepkg
source "$(command -v makepkg)"
)
}

View File

@@ -18,17 +18,22 @@ readonly _DEVTOOLS_MAKEPKG_CONF_DIR=${_DEVTOOLS_LIBRARY_DIR}/makepkg.conf.d
update_pacman_repo_cache() {
local repo=${1:-multilib}
mkdir -p "${_DEVTOOLS_PACMAN_CACHE_DIR}"
msg "Updating pacman database cache"
lock 10 "${_DEVTOOLS_PACMAN_CACHE_DIR}.lock" "Locking pacman database cache"
fakeroot -- pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/multilib.conf" \
fakeroot -- pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/${repo}.conf" \
--dbpath "${_DEVTOOLS_PACMAN_CACHE_DIR}" \
--disable-sandbox \
-Sy
lock_close 10
}
get_pacman_repo_from_pkgbuild() {
local path=${1:-PKGBUILD}
local repo=${2:-multilib}
local -a pkgnames
# shellcheck source=contrib/makepkg/PKGBUILD.proto
mapfile -t pkgnames < <(source "${path}"; printf "%s\n" "${pkgname[@]}")
@@ -40,12 +45,12 @@ get_pacman_repo_from_pkgbuild() {
# update the pacman repo cache if it doesn't exist yet
if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then
update_pacman_repo_cache
update_pacman_repo_cache "${repo}"
fi
slock 10 "${_DEVTOOLS_PACMAN_CACHE_DIR}.lock" "Locking pacman database cache"
# query repo of passed pkgname, specify --nodeps twice to skip all dependency checks
mapfile -t repos < <(pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/multilib.conf" \
mapfile -t repos < <(pacman --config "${_DEVTOOLS_PACMAN_CONF_DIR}/${repo}.conf" \
--dbpath "${_DEVTOOLS_PACMAN_CACHE_DIR}" \
--sync \
--nodeps \
@@ -58,3 +63,31 @@ get_pacman_repo_from_pkgbuild() {
printf "%s" "${repos[0]}"
}
get_pkgnames_from_repo_pkgbase() {
local repo=$1
shift
local pkgbases=("$@")
local -a pkgnames
# update the pacman repo cache if it doesn't exist yet
if [[ ! -d "${_DEVTOOLS_PACMAN_CACHE_DIR}" ]]; then
update_pacman_repo_cache universe
fi
slock 10 "${_DEVTOOLS_PACMAN_CACHE_DIR}.lock" "Locking pacman database cache"
# query pkgnames of passed pkgbase inside a repo
mapfile -t pkgnames < <(expac --config <(sed "s|#DBPath.*|DBPath = $(realpath "${_DEVTOOLS_PACMAN_CACHE_DIR}")|" < "${_DEVTOOLS_PACMAN_CONF_DIR}/universe.conf") \
--sync '%r %e %n' 2>/dev/null \
| sort | awk -v pkgbase="${pkgbases[*]}" \
'BEGIN { split(pkgbase, array); for (item in array) filter[array[item]]=1 } $1=="'"${repo}"'" && $2 in filter {print $3}'
)
lock_close 10
if (( ! ${#pkgnames[@]} )); then
return 1
fi
printf "%s\n" "${pkgnames[@]}"
return 0
}

View File

@@ -6,10 +6,13 @@
DEVTOOLS_INCLUDE_UTIL_PKGBUILD_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/util/makepkg.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/makepkg.sh
source /usr/share/makepkg/util/message.sh
source /usr/share/makepkg/util/schema.sh
set -e
set -eo pipefail
# set the pkgver variable in a PKGBUILD
@@ -41,3 +44,48 @@ pkgbuild_set_pkgrel() {
sed --regexp-extended "s|^(pkgrel=)${pkgrel}$|\1${new_pkgrel}|g" --in-place PKGBUILD
}
pkgbuild_update_checksums() {
local status_file=$1
local builddir newbuildfile sumtypes newsums
[[ -z ${WORKDIR:-} ]] && setup_workdir
builddir=$(mktemp --tmpdir="${WORKDIR}" --directory update-checksums.XXXXXX)
newbuildfile="${builddir}/PKGBUILD"
# generate new integrity checksums
if ! newsums=$(BUILDDIR=${builddir} makepkg_generate_integrity 2>"${status_file}"); then
printf 'Failed to generate new checksums'
return 1
fi
# early exit if no integrity checksums are needed
if [[ -z ${newsums} ]]; then
return 0
fi
# replace the integrity sums and write it to a temporary file
sumtypes=$(IFS='|'; echo "${known_hash_algos[*]}")
if ! awk --assign=sumtypes="${sumtypes}" --assign=newsums="${newsums}" '
$0 ~"^[[:blank:]]*(" sumtypes ")sums(_[^=]+)?\\+?=", $0 ~ "\\)[[:blank:]]*(#.*)?$" {
if (!w) {
print newsums
w++
}
next
}
1
END { if (!w) print newsums }' PKGBUILD > "${newbuildfile}"; then
printf 'Failed to replace the generated checksums'
return 1
fi
# overwrite the original PKGBUILD while preserving permissions
if ! cat -- "${newbuildfile}" > PKGBUILD; then
printf "Failed to write to the PKGBUILD file"
return 1
fi
return 0
}

View File

@@ -7,6 +7,8 @@ DEVTOOLS_INCLUDE_UTIL_TERM_SH=1
set -eo pipefail
readonly PKGCTL_TERM_ICON_CONFIDENTIAL=
export PKGCTL_TERM_ICON_CONFIDENTIAL
readonly PKGCTL_TERM_SPINNER_DOTS=Dots
export PKGCTL_TERM_SPINNER_DOTS
@@ -180,3 +182,19 @@ term_spinner_stop() {
# show the cursor after stopping the spinner
term_cursor_show
}
prompt() {
local message=$1
local answer
read -r -p "${message} (y/N) " answer
case "${answer}" in
y|Y|yes|Yes|YES)
true
;;
*)
false
;;
esac
}

68
src/lib/valid-issue.sh Normal file
View File

@@ -0,0 +1,68 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_SEVERITY=(
lowest
low
medium
high
critical
)
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_PRIORITY=(
low
normal
high
urgent
)
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_STATUS=(
confirmed
in-progress
in-review
on-hold
unconfirmed
waiting-input
waiting-upstream
)
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_SCOPE=(
bug
feature
security
question
regression
enhancement
documentation
reproducibility
out-of-date
)
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_SEARCH_LOCATION=(
title
description
all
)
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_RESOLUTION=(
cant-reproduce
completed
duplicate
invalid
not-a-bug
upstream
wont-fix
)
# shellcheck disable=2034
DEVTOOLS_VALID_ISSUE_CONFIDENTIALITY=(
confidential
public
)

View File

@@ -4,8 +4,13 @@
:
# shellcheck disable=2034
DEVTOOLS_VALID_ARCHES=(
DEVTOOLS_VALID_BINARY_ARCHES=(
x86_64
)
# shellcheck disable=2034
DEVTOOLS_VALID_ARCHES=(
"${DEVTOOLS_VALID_BINARY_ARCHES[@]}"
any
)

10
src/lib/valid-version.sh Normal file
View File

@@ -0,0 +1,10 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
:
# shellcheck disable=2034
DEVTOOLS_VALID_VERSION_OUTPUT_FORMAT=(
pretty
json
)

View File

@@ -19,6 +19,7 @@ pkgctl_version_usage() {
COMMANDS
check Compares local package versions against upstream
setup Automatically detect and setup a basic nvchecker config
upgrade Adjust the PKGBUILD to match the latest upstream version
OPTIONS
@@ -26,6 +27,7 @@ pkgctl_version_usage() {
EXAMPLES
$ ${COMMAND} check libfoo linux libbar
$ ${COMMAND} setup libfoo
_EOF_
}
@@ -57,6 +59,14 @@ pkgctl_version() {
pkgctl_version_upgrade "$@"
exit $?
;;
setup)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/version/setup.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version/setup.sh
pkgctl_version_setup "$@"
exit 0
;;
*)
die "invalid argument: %s" "$1"
;;

View File

@@ -10,6 +10,8 @@ _DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/util/term.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/term.sh
# shellcheck source=src/lib/valid-version.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-version.sh
source /usr/share/makepkg/util/message.sh
@@ -39,8 +41,15 @@ pkgctl_version_check_usage() {
check failures.
OPTIONS
-v, --verbose Display results including up-to-date versions
-h, --help Show this help text
-h, --help Show this help text
FILTER OPTIONS
-v, --verbose Display all results including up-to-date versions
OUTPUT OPTIONS
--json Enable printing in JSON; Shorthand for '--format json'
-F, --format FORMAT Controls the output format of the results;
FORMAT is 'pretty', or 'json' (default: pretty)
EXAMPLES
$ ${COMMAND} neovim vim
@@ -50,9 +59,11 @@ _EOF_
pkgctl_version_check() {
local pkgbases=()
local verbose=0
local output_format=pretty
local path status_file path pkgbase upstream_version result
local json_data=()
local up_to_date=()
local out_of_date=()
local failure=()
@@ -66,6 +77,18 @@ pkgctl_version_check() {
pkgctl_version_check_usage
exit 0
;;
--json)
output_format=json
shift
;;
-F|--format)
(( $# <= 1 )) && die "missing argument for %s" "$1"
output_format="${2}"
if ! in_array "${output_format}" "${DEVTOOLS_VALID_VERSION_OUTPUT_FORMAT[@]}"; then
die "Unknown output format: %s" "${output_format}"
fi
shift 2
;;
-v|--verbose)
verbose=1
shift
@@ -103,9 +126,11 @@ pkgctl_version_check() {
verbose=1
fi
# start a terminal spinner as checking versions takes time
status_dir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-version-check-spinner.XXXXXXXXXX)
term_spinner_start "${status_dir}"
if [[ ${output_format} == pretty ]]; then
# start a terminal spinner as checking versions takes time
status_dir=$(mktemp --tmpdir="${WORKDIR}" --directory pkgctl-version-check-spinner.XXXXXXXXXX)
term_spinner_start "${status_dir}"
fi
for path in "${pkgbases[@]}"; do
# skip paths that are not directories
@@ -114,19 +139,26 @@ pkgctl_version_check() {
fi
pushd "${path}" >/dev/null
if [[ ! -f "PKGBUILD" ]]; then
die "No PKGBUILD found for ${path}"
if [[ ${output_format} == pretty ]]; then
# update the current terminal spinner status
(( ++current_item ))
pkgctl_version_check_spinner \
"${status_dir}" \
"${#up_to_date[@]}" \
"${#out_of_date[@]}" \
"${#failure[@]}" \
"${current_item}" \
"${#pkgbases[@]}"
fi
# update the current terminal spinner status
(( ++current_item ))
pkgctl_version_check_spinner \
"${status_dir}" \
"${#up_to_date[@]}" \
"${#out_of_date[@]}" \
"${#failure[@]}" \
"${current_item}" \
"${#pkgbases[@]}"
if [[ ! -f "PKGBUILD" ]]; then
result="no PKGBUILD found"
pkgbase=$(basename "${path}")
json_data+=("$(build_json_package_version_entry "${pkgbase}" failure "${result}" false null null)")
failure+=("${BOLD}${pkgbase}${ALL_OFF}: ${result}")
popd >/dev/null
continue
fi
# reset common PKGBUILD variables
unset pkgbase pkgname arch source pkgver pkgrel validpgpkeys
@@ -135,27 +167,36 @@ pkgctl_version_check() {
pkgbase=${pkgbase:-$pkgname}
if ! result=$(get_upstream_version); then
result="${BOLD}${pkgbase}${ALL_OFF}: ${result}"
failure+=("${result}")
json_data+=("$(build_json_package_version_entry "${pkgbase}" failure "${result}" false "${pkgver}" null)")
failure+=("${BOLD}${pkgbase}${ALL_OFF}: ${result}")
popd >/dev/null
continue
fi
upstream_version=${result}
if ! result=$(vercmp "${upstream_version}" "${pkgver}"); then
result="${BOLD}${pkgbase}${ALL_OFF}: failed to compare version ${upstream_version} against ${pkgver}"
result="failed to compare version ${upstream_version} against ${pkgver}"
json_data+=("$(build_json_package_version_entry "${pkgbase}" failure "${result}" false "${pkgver}" "${upstream_version}")")
result="${BOLD}${pkgbase}${ALL_OFF}: ${result}"
failure+=("${result}")
popd >/dev/null
continue
fi
if (( result == 0 )); then
if (( verbose )); then
json_data+=("$(build_json_package_version_entry "${pkgbase}" success null false "${pkgver}" "${upstream_version}")")
fi
result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is latest"
up_to_date+=("${result}")
elif (( result < 0 )); then
if (( verbose )); then
json_data+=("$(build_json_package_version_entry "${pkgbase}" warning "local version is newer than upstream" false "${pkgver}" "${upstream_version}")")
fi
result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is newer than ${DARK_GREEN}${upstream_version}${ALL_OFF}"
up_to_date+=("${result}")
elif (( result > 0 )); then
json_data+=("$(build_json_package_version_entry "${pkgbase}" success null true "${pkgver}" "${upstream_version}")")
result="${BOLD}${pkgbase}${ALL_OFF}: upgrade from version ${PURPLE}${pkgver}${ALL_OFF} to ${DARK_GREEN}${upstream_version}${ALL_OFF}"
out_of_date+=("${result}")
fi
@@ -163,8 +204,18 @@ pkgctl_version_check() {
popd >/dev/null
done
# stop the terminal spinner after all checks
term_spinner_stop "${status_dir}"
if [[ ${output_format} == pretty ]]; then
# stop the terminal spinner after all checks
term_spinner_stop "${status_dir}"
fi
# early exit for json output
if [[ ${output_format} == json ]]; then
jq --null-input \
'$ARGS.positional' \
--jsonargs "${json_data[@]}"
return 0
fi
if (( verbose )) && (( ${#up_to_date[@]} > 0 )); then
printf "%sUp-to-date%s\n" "${section_separator}${BOLD}${UNDERLINE}" "${ALL_OFF}"
@@ -205,6 +256,24 @@ pkgctl_version_check() {
return "${exit_code}"
}
build_json_package_version_entry() {
local pkgbase=$1
local status=$2
local message=$3
local is_out_of_date=$4
local local_version=$5
local upstream_version=$6
jq --null-input \
--arg pkgbase "${pkgbase}" \
--arg status "${status}" \
--arg message "${message}" \
--arg local_version "${local_version}" \
--arg upstream_version "${upstream_version}" \
--argjson out_of_date "${is_out_of_date}" \
'$ARGS.named | walk(if type == "string" and (. == "null" or . == "") then null else . end)'
}
get_upstream_version() {
local config=.nvchecker.toml
local output errors upstream_version
@@ -223,8 +292,9 @@ get_upstream_version() {
opts+=(--keyfile "${keyfile}")
fi
if ! output=$(nvchecker --file "${config}" --logger json "${opts[@]}" 2>&1 | \
jq --raw-output 'select(.level != "debug")'); then
if ! output=$(GIT_TERMINAL_PROMPT=0 GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null \
nvchecker --file "${config}" --logger json "${opts[@]}" 2>&1 | \
jq --raw-output 'select((.level != "debug") and (.event != "ignoring invalid version"))'); then
printf "failed to run nvchecker: %s" "${output}"
return 1
fi
@@ -239,6 +309,12 @@ get_upstream_version() {
return 1
fi
# implement none source for packages like meta and virtual packages that are always up to date
if [[ ${upstream_version} == None ]] && grep --quiet --extended-regexp '^source *= *"manual"' "${config}" &>/dev/null; then
printf '%s' "${pkgver:-${upstream_version}}"
return 0
fi
printf "%s" "${upstream_version}"
return 0
}
@@ -264,16 +340,22 @@ nvchecker_check_config() {
done
# check if the config contains a pkgbase section
if [[ -n ${pkgbase} ]] && ! grep --max-count=1 --extended-regexp --quiet "^\\[\"?${pkgbase}\"?\\]" < "${config}"; then
if [[ -n ${pkgbase} ]] && ! grep --max-count=1 --extended-regexp --quiet "^\\[\"?${pkgbase//+/\\+}\"?\\]" < "${config}"; then
printf "missing pkgbase section in %s: %s" "${config}" "${pkgbase}"
return 1
fi
# check if the config contains any section other than pkgbase
if [[ -n ${pkgbase} ]] && property=$(grep --max-count=1 --perl-regexp "^\\[(?!\"?${pkgbase}\"?\\]).+\\]" < "${config}"); then
if [[ -n ${pkgbase} ]] && property=$(grep --max-count=1 --perl-regexp "^\\[(?!\"?${pkgbase//+/\\+}\"?\\]).+\\]" < "${config}"); then
printf "non-pkgbase section not supported in %s: %s" "${config}" "${property}"
return 1
fi
# check if the config is using the 'cmd' source
if grep --extended-regexp --quiet '^\s*source\s*=\s*["'\'']cmd["'\'']\s*$' "${config}"; then
printf "using the 'cmd' source in %s is disallowed" "${config}"
return 1
fi
}
nvchecker_check_error() {
@@ -281,7 +363,7 @@ nvchecker_check_error() {
local errors
if ! errors=$(jq --raw-output --exit-status \
'select(.level == "error") | "\(.event)" + if .error then ": \(.error)" else "" end' \
'select((.level == "error") and (.error != null)) | "\(.event)" + if .error then ": \(.error)" else "" end' \
<<< "${result}"); then
return 0
fi

539
src/lib/version/setup.sh Normal file
View File

@@ -0,0 +1,539 @@
#!/bin/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
[[ -z ${DEVTOOLS_INCLUDE_VERSION_SETUP_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_VERSION_SETUP_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/version/check.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/version/check.sh
source /usr/share/makepkg/util/message.sh
source /usr/share/makepkg/util/source.sh
set -eo pipefail
pkgctl_version_setup_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [PKGBASE]...
Automate the creation of a basic nvchecker configuration file by
analyzing the source array specified in the PKGBUILD file of a package.
If no PKGBASE is specified, the command defaults to using the current
working directory.
OPTIONS
-f, --force Overwrite existing nvchecker configuration
--prefer-platform-api Prefer platform specific GitHub/GitLab API for complex cases
--url URL Derive check target from URL instead of source array
--no-check Do not run version check after setup
--no-upstream Setup a blank config for packages without upstream sources
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} neovim vim
_EOF_
}
pkgctl_version_setup() {
local pkgbases=()
local override_url=
local run_check=1
local force=0
local prefer_platform_api=0
local no_upstream=0
local path ret
local checks=()
while (( $# )); do
case $1 in
-h|--help)
pkgctl_version_setup_usage
exit 0
;;
-f|--force)
force=1
shift
;;
--prefer-platform-api)
prefer_platform_api=1
shift
;;
--url)
(( $# <= 1 )) && die "missing argument for %s" "$1"
override_url=$2
shift 2
;;
--no-check)
run_check=0
shift
;;
--no-upstream)
no_upstream=1
shift
;;
--)
shift
break
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
pkgbases=("$@")
break
;;
esac
done
# Check if used without pkgbases in a packaging directory
if (( ${#pkgbases[@]} == 0 )); then
if [[ -f PKGBUILD ]]; then
pkgbases=(".")
else
pkgctl_version_setup_usage
exit 1
fi
fi
ret=0
for path in "${pkgbases[@]}"; do
# skip paths that are not directories
if [[ ! -d "${path}" ]]; then
continue
fi
pushd "${path}" >/dev/null
if nvchecker_setup "${path}" "${force}" "${prefer_platform_api}" "${override_url}" "${no_upstream}"; then
checks+=("${path}")
else
ret=1
fi
popd >/dev/null
done
# run checks on the setup targets
if (( run_check )) && (( ${#checks[@]} >= 1 )); then
echo
pkgctl_version_check --verbose "${checks[@]}" || true
fi
return $ret
}
nvchecker_setup() {
local path=$1
local force=$2
local prefer_platform_api=$3
local override_url=$4
local no_upstream=$5
local pkgbase pkgname source source_url proto domain url_parts section body
if [[ ! -f PKGBUILD ]]; then
msg_error "${BOLD}${path}:${ALL_OFF} no PKGBUILD found"
return 1
fi
unset body pkgbase pkgname source url
# shellcheck source=contrib/makepkg/PKGBUILD.proto
if ! . ./PKGBUILD; then
msg_error "${BOLD}${path}:${ALL_OFF} failed to source PKGBUILD"
return 1
fi
pkgbase=${pkgbase:-$pkgname}
# try to guess from url as last try
if [[ -n ${url} ]]; then
source+=("${url}")
fi
# handle overwrite of existing config
if [[ -f .nvchecker.toml ]] && (( ! force )); then
msg_warn "${BOLD}${pkgbase}:${ALL_OFF} nvchecker already configured"
return 1
fi
# override the source array with a passed URL
if [[ -n ${override_url} ]]; then
source=("${override_url}")
fi
# skip empty source array
if (( ${#source[@]} == 0 )) && (( ! no_upstream )); then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} PKGBUILD has no source array"
return 1
fi
for source_url in "${source[@]}"; do
# Strips out filename::http for example
source_url=$(get_url "${source_url}")
# discard query fragments
source_url=${source_url%\?*}
source_url=${source_url%#*}
# skip patches
if [[ ${source_url} == *.patch ]]; then
continue
fi
# skip signatures
if [[ ${source_url} == *.asc ]] || [[ ${source_url} == *.sig ]]; then
continue
fi
# skip local files
if [[ ${source_url} != *://* ]]; then
continue
fi
# split URL segments while avoiding empty element after protocol and newline at the end
mapfile -td / url_parts <<< "${source_url/:\/\//\/}/"
unset "url_parts[-1]"
# extract protocol and domain to select the configuration type
proto=${url_parts[0]}
domain=${url_parts[1]}
case "${domain}" in
gitlab.*)
if (( prefer_platform_api )); then
body=$(nvchecker_setup_gitlab "${url_parts[@]}")
else
body=$(nvchecker_setup_git "${url_parts[@]}")
fi
break
;;
github.com)
if (( prefer_platform_api )); then
body=$(nvchecker_setup_github "${url_parts[@]}")
else
body=$(nvchecker_setup_git "${url_parts[@]}")
fi
break
;;
codeberg.org)
body=$(nvchecker_setup_git "${url_parts[@]}")
break
;;
pypi.org|pypi.io|files.pythonhosted.org)
body=$(nvchecker_setup_pypi "${url_parts[@]}")
break
;;
hackage.haskell.org)
body=$(nvchecker_setup_hackage "${url_parts[@]}")
break
;;
registry.npmjs.org|npmjs.com|www.npmjs.com)
body=$(nvchecker_setup_npm "${url_parts[@]}")
break
;;
rubygems.org)
body=$(nvchecker_setup_rubygems "${url_parts[@]}")
break
;;
*.cpan.org|*.mcpan.org|*.metacpan.org)
body=$(nvchecker_setup_cpan "${url_parts[@]}")
break
;;
crates.io|*.crates.io)
body=$(nvchecker_setup_crates_io "${url_parts[@]}")
break
;;
*)
if [[ ${proto} == git ]] || [[ ${proto} == git+https ]]; then
body=$(nvchecker_setup_git "${url_parts[@]}")
fi
;;
esac
done
if (( no_upstream )); then
body='source = "manual"'
fi
if [[ -z "${body}" ]]; then
msg_error "${BOLD}${pkgbase}:${ALL_OFF} unable to automatically setup nvchecker"
return 1
fi
# escape the section if it contains toml subsection chars
section="${pkgbase}"
if [[ ${section} == *.* ]] || [[ ${section} == *+* ]]; then
section="\"${section}\""
fi
msg_success "${BOLD}${pkgbase}:${ALL_OFF} successfully configured nvchecker"
cat > .nvchecker.toml << EOF
[${section}]
${body}
EOF
}
get_git_url_from_parts() {
local url_parts=("$@")
local proto=${url_parts[0]#*+}
local domain=${url_parts[1]}
local url
url="${proto}://$(join_by / "${url_parts[@]:1}")"
case "${domain}" in
gitlab.*)
url=${url%/-/*/*}
[[ ${url} != *.git ]] && url+=.git
;;
github.com|codeberg.org)
url="${proto}://$(join_by / "${url_parts[@]:1:3}")"
[[ ${url} != *.git ]] && url+=.git
;;
esac
printf '%s' "${url}"
}
# PyPI
#
# As Arch python packages don't necessarily match the pypi name, when the
# provided source url comes from pypi.io or pypi.org try to extract the package
# name from the (predictable) tarball download url for example:
#
# https://pypi.io/packages/source/p/pyflakes/pyflakes-3.1.0.tar.gz
# https://pypi.io/packages/source/p/pyflakes
# https://pypi.org/packages/source/b/bleach
# https://files.pythonhosted.org/packages/source/p/pyflakes
# https://pypi.org/project/SQLAlchemy/
nvchecker_setup_pypi() {
local url_parts=("$@")
local pypi
if [[ ${url_parts[2]} == packages ]]; then
pypi=${url_parts[5]}
elif [[ ${url_parts[2]} == project ]]; then
pypi=${url_parts[3]}
else
return 1
fi
cat << EOF
source = "pypi"
pypi = "${pypi}"
EOF
}
# Git
#
# Set up a generic Git source, while removing the proto specific part from makepkg
#
# git+https://github.com/prometheus/prometheus.git
# https://git.foobar.com/some/path/group/project.git
# https://gitlab.com/sub/group/project/-/archive/8.0.0/packages-8.0.0.tar.gz
nvchecker_setup_git() {
local url_parts=("$@")
local url
url=$(get_git_url_from_parts "${url_parts[@]}")
cat << EOF
source = "git"
git = "${url}"
EOF
# best effort check if the tags are prefixed with v
if git_tags_have_version_prefix "${url}"; then
echo 'prefix = "v"'
fi
}
git_tags_have_version_prefix() {
local url=$1
# best effort check if the tags are prefixed with v
if ! grep --max-count=1 --quiet --extended-regex 'refs/tags/v[0-9]+[\.0-9]*$' \
<(GIT_TERMINAL_PROMPT=0 git ls-remote --quiet --tags "${url}" 2>/dev/null); then
return 1
fi
return 0
}
# Github
#
# We want to know the $org/$project name from the url
#
# https://github.com/prometheus/prometheus/archive/v2.49.1.tar.gz
nvchecker_setup_github() {
local url_parts=("$@")
local url project
if ! url=$(get_git_url_from_parts "${url_parts[@]}"); then
return 1
fi
project=${url#*://*/}
project=${project%.git}
cat << EOF
source = "github"
github = "${project}"
use_max_tag = true
EOF
# best effort check if the tags are prefixed with v
if git_tags_have_version_prefix "${url}"; then
echo 'prefix = "v"'
fi
}
# GitLab
#
# We want to know the $org/$project name from the url
#
# git+https://gitlab.com/inkscape/inkscape.git#tag=091e20ef0f204eb40ecde54436e1ef934a03d894
nvchecker_setup_gitlab() {
local url_parts=("$@")
local url project host
if ! url=$(get_git_url_from_parts "${url_parts[@]}"); then
return 1
fi
project=${url#*://*/}
project=${project%.git}
cat << EOF
source = "gitlab"
gitlab = "${project}"
EOF
host=${url#*://}
host=${host%%/*}
if [[ ${host} != gitlab.com ]]; then
echo "host = \"${host}\""
fi
echo "use_max_tag = true"
# best effort check if the tags are prefixed with v
if git_tags_have_version_prefix "${url}"; then
echo 'prefix = "v"'
fi
}
# Hackage
#
# We want to know the project name
#
# https://hackage.haskell.org/package/xmonad
# https://hackage.haskell.org/package/xmonad-0.18.0/xmonad-0.18.0.tar.gz
# https://hackage.haskell.org/packages/archive/digits/0.3.1/digits-0.3.1.tar.gz
nvchecker_setup_hackage() {
local url_parts=("$@")
local hackage
if [[ ${url_parts[2]} == packages ]]; then
hackage=${url_parts[4]}
elif [[ ${url_parts[2]} == package ]] && (( ${#url_parts[@]} == 4 )); then
hackage=${url_parts[3]}
elif [[ ${url_parts[2]} == package ]] && (( ${#url_parts[@]} >= 5 )); then
hackage=${url_parts[3]%-*}
else
return 1
fi
cat << EOF
source = "hackage"
hackage = "${hackage}"
EOF
}
# NPM
#
# We want to know the project name
#
# https://registry.npmjs.org/eslint_d/-/eslint_d-12.1.0.tgz
# https://www.npmjs.com/package/node-gyp
nvchecker_setup_npm() {
local url_parts=("$@")
local npm
if [[ ${url_parts[1]} == registry.npmjs.org ]]; then
npm=${url_parts[2]}
elif [[ ${url_parts[2]} == package ]] && (( ${#url_parts[@]} == 4 )); then
npm=${url_parts[3]}
else
return 1
fi
cat << EOF
source = "npm"
npm = "${npm}"
EOF
}
# RubyGems
#
# We want to know the project name
#
# https://rubygems.org/downloads/polyglot-0.3.5.gem
# https://rubygems.org/gems/diff-lcs
nvchecker_setup_rubygems() {
local url_parts=("$@")
local gem
if [[ ${url_parts[2]} == downloads ]]; then
gem=${url_parts[-1]%-*}
elif [[ ${url_parts[2]} == gems ]]; then
gem=${url_parts[3]}
else
return 1
fi
cat << EOF
source = "gems"
gems = "${gem}"
EOF
}
# CPAN
#
# We want to know the project name
#
# source = https://search.cpan.org/CPAN/authors/id/C/CO/COSIMO/Locale-PO-1.2.3.tar.gz
nvchecker_setup_cpan() {
local url_parts=("$@")
local cpan=${url_parts[-1]}
cpan=${cpan%-*}
cat << EOF
source = "cpan"
cpan = "${cpan}"
EOF
}
# crates.io
#
# We want to know the crate name
#
# https://crates.io/api/v1/crates/${pkgname}/${pkgver}/download
# https://static.crates.io/crates/${pkgname}/$pkgname-$pkgver.crate
# https://crates.io/crates/git-smash
nvchecker_setup_crates_io() {
local url_parts=("$@")
local crate
if [[ ${url_parts[2]} == crates ]]; then
crate=${url_parts[3]}
elif [[ ${url_parts[4]} == crates ]]; then
crate=${url_parts[5]}
else
return 1
fi
for i in "${!url_parts[@]}"; do
if [[ ${url_parts[i]} == crates ]]; then
crate=${url_parts[(( i + 1 ))]}
fi
done
cat << EOF
source = "cratesio"
cratesio = "${crate}"
EOF
}

View File

@@ -30,14 +30,15 @@ pkgctl_version_upgrade_usage() {
Upon execution, it automatically adjusts the PKGBUILD file, ensuring that the
pkgver field is set to match the latest version available from the upstream
source. In addition to updating the pkgver, this command also resets the pkgrel
to 1.
to 1 and updates checksums.
Outputs a summary of upgraded packages, up-to-date packages, and any check
failures.
OPTIONS
-v, --verbose Display results including up-to-date versions
-h, --help Show this help text
--no-update-checksums Disable computation and update of the checksums
-v, --verbose Display results including up-to-date versions
-h, --help Show this help text
EXAMPLES
$ ${COMMAND} neovim vim
@@ -50,6 +51,7 @@ pkgctl_version_upgrade() {
local verbose=0
local exit_code=0
local current_item=0
local update_checksums=1
while (( $# )); do
case $1 in
@@ -57,6 +59,10 @@ pkgctl_version_upgrade() {
pkgctl_version_upgrade_usage
exit 0
;;
--no-update-checksums)
update_checksums=0
shift
;;
-v|--verbose)
verbose=1
shift
@@ -105,19 +111,14 @@ pkgctl_version_upgrade() {
fi
pushd "${path}" >/dev/null
if [[ ! -f "PKGBUILD" ]]; then
die "No PKGBUILD found for ${path}"
fi
# update the current terminal spinner status
(( ++current_item ))
pkgctl_version_upgrade_spinner \
"${status_dir}" \
"${#up_to_date[@]}" \
"${#out_of_date[@]}" \
"${#failure[@]}" \
"${current_item}" \
"${#pkgbases[@]}"
if [[ ! -f "PKGBUILD" ]]; then
result="${BOLD}${path}${ALL_OFF}: no PKGBUILD found"
failure+=("${result}")
popd >/dev/null
continue
fi
# reset common PKGBUILD variables
unset pkgbase pkgname arch source pkgver pkgrel validpgpkeys
@@ -125,6 +126,17 @@ pkgctl_version_upgrade() {
. ./PKGBUILD
pkgbase=${pkgbase:-$pkgname}
# update the current terminal spinner status
pkgctl_version_upgrade_spinner \
"${status_dir}" \
"${#up_to_date[@]}" \
"${#out_of_date[@]}" \
"${#failure[@]}" \
"${current_item}" \
"${#pkgbases[@]}" \
"${pkgbase}" \
"query latest version"
if ! result=$(get_upstream_version); then
result="${BOLD}${pkgbase}${ALL_OFF}: ${result}"
failure+=("${result}")
@@ -153,6 +165,24 @@ pkgctl_version_upgrade() {
# change the PKGBUILD
pkgbuild_set_pkgver "${upstream_version}"
pkgbuild_set_pkgrel 1
# download sources and update the checksums
if (( update_checksums )); then
pkgctl_version_upgrade_spinner \
"${status_dir}" \
"${#up_to_date[@]}" \
"${#out_of_date[@]}" \
"${#failure[@]}" \
"${current_item}" \
"${#pkgbases[@]}" \
"${pkgbase}" \
"updating checksums"
if ! result=$(pkgbuild_update_checksums /dev/null); then
result="${BOLD}${pkgbase}${ALL_OFF}: failed to update checksums for version ${DARK_GREEN}${upstream_version}${ALL_OFF}"
failure+=("${result}")
fi
fi
fi
popd >/dev/null
@@ -231,6 +261,8 @@ pkgctl_version_upgrade_spinner() {
local failure_count=$4
local current=$5
local total=$6
local pkgbase=$7
local message=$8
local percentage=$(( 100 * current / total ))
local tmp_file="${status_dir}/tmp"
@@ -243,8 +275,10 @@ pkgctl_version_upgrade_spinner() {
"${failure_count}" > "${tmp_file}"
# print the progress status
printf "📡 Upgrading: %s/%s [%s] %%spinner%%" \
"${BOLD}${current}" "${total}" "${percentage}%${ALL_OFF}" \
printf "📡 %s: %s\n" \
"${pkgbase}" "${BOLD}${message}${ALL_OFF}" >> "${tmp_file}"
printf "⌛ Upgrading: %s/%s [%s] %%spinner%%" \
"${BOLD}${current}" "${total}" "${percentage}%${ALL_OFF}" \
>> "${tmp_file}"
# swap the status file

Some files were not shown because too many files have changed in this diff Show More