Compare commits

..

3 Commits

Author SHA1 Message Date
3d50ea4686 implement local reporter mode 2024-02-26 17:07:27 +02:00
63a2fc791a feat: add ability to disable debug packages distribution
The feature is implemented as supplying !debug option to makepkg when
generating package list. In this case debug packages still will be
built, however, they will not be added to the repository
2024-02-20 14:47:58 +02:00
da7aead06f
feat: add abillity to check broken dependencies (#122)
* implement elf dynamic linking check

* load local database too in pacman wrapper
2024-02-13 11:35:38 +02:00
34 changed files with 2643 additions and 2729 deletions

View File

@ -12,7 +12,7 @@ pacman -Syu --noconfirm
# main dependencies # main dependencies
pacman -Sy --noconfirm devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo python-systemd sudo pacman -Sy --noconfirm devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo python-systemd sudo
# make dependencies # make dependencies
pacman -Sy --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel pacman -Sy --noconfirm python-build python-flit python-installer python-tox python-wheel
# optional dependencies # optional dependencies
if [[ -z $MINIMAL_INSTALL ]]; then if [[ -z $MINIMAL_INSTALL ]]; then
# VCS support # VCS support
@ -36,9 +36,6 @@ sudo -u nobody -- makepkg --packagelist | grep -v -- -debug- | pacman -U --nocon
# create machine-id which is required by build tools # create machine-id which is required by build tools
systemd-machine-id-setup systemd-machine-id-setup
# remove unused dependencies
pacman -Qdtq | pacman -Rscn --noconfirm -
# initial setup command as root # initial setup command as root
[[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080") [[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080")
ahriman -a x86_64 -r "github" service-setup --packager "ahriman bot <ahriman@example.com>" "${WEB_ARGS[@]}" ahriman -a x86_64 -r "github" service-setup --packager "ahriman bot <ahriman@example.com>" "${WEB_ARGS[@]}"
@ -53,7 +50,7 @@ if [[ -z $MINIMAL_INSTALL ]]; then
WEB_PID=$! WEB_PID=$!
fi fi
# add the first package # add the first package
sudo -u ahriman -- ahriman --log-handler console package-add --now ahriman sudo -u ahriman -- ahriman package-add --now ahriman
# check if package was actually installed # check if package was actually installed
test -n "$(find "/var/lib/ahriman/repository/github/x86_64" -name "ahriman*pkg*")" test -n "$(find "/var/lib/ahriman/repository/github/x86_64" -name "ahriman*pkg*")"
# run package check # run package check

View File

@ -3,7 +3,7 @@ version: 2
build: build:
os: ubuntu-20.04 os: ubuntu-20.04
tools: tools:
python: "3.12" python: "3.11"
python: python:
install: install:

View File

@ -33,9 +33,9 @@ COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies ## install package dependencies
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size ## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
RUN pacman -Sy --noconfirm --asdeps devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo && \ RUN pacman -Sy --noconfirm --asdeps devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo && \
pacman -Sy --noconfirm --asdeps base-devel python-build python-flit python-installer python-wheel && \ pacman -Sy --noconfirm --asdeps python-build python-flit python-installer python-wheel && \
pacman -Sy --noconfirm --asdeps breezy git mercurial python-aiohttp python-boto3 python-cryptography python-jinja python-requests-unixsocket python-systemd rsync subversion && \ pacman -Sy --noconfirm --asdeps breezy git mercurial python-aiohttp python-boto3 python-cryptography python-jinja python-requests-unixsocket python-systemd rsync subversion && \
runuser -u build -- install-aur-package python-aioauth-client python-webargs python-aiohttp-apispec-git python-aiohttp-cors \ runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-apispec-git python-aiohttp-cors \
python-aiohttp-jinja2 python-aiohttp-session python-aiohttp-security python-aiohttp-jinja2 python-aiohttp-session python-aiohttp-security
## FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container ## FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container

View File

@ -6,7 +6,7 @@ for PACKAGE in "$@"; do
BUILD_DIR="$(mktemp -d)" BUILD_DIR="$(mktemp -d)"
git clone https://aur.archlinux.org/"$PACKAGE".git "$BUILD_DIR" git clone https://aur.archlinux.org/"$PACKAGE".git "$BUILD_DIR"
cd "$BUILD_DIR" cd "$BUILD_DIR"
makepkg --nocheck --noconfirm --install --rmdeps --syncdeps makepkg --noconfirm --install --rmdeps --syncdeps
cd / cd /
rm -r "$BUILD_DIR" rm -r "$BUILD_DIR"
done done

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -1,7 +1,7 @@
# Maintainer: Evgeniy Alekseev # Maintainer: Evgeniy Alekseev
pkgname='ahriman' pkgname='ahriman'
pkgver=2.13.6 pkgver=2.13.4
pkgrel=1 pkgrel=1
pkgdesc="ArcH linux ReposItory MANager" pkgdesc="ArcH linux ReposItory MANager"
arch=('any') arch=('any')

View File

@ -38,6 +38,10 @@
<script> <script>
const keyImportModal = $("#key-import-modal"); const keyImportModal = $("#key-import-modal");
const keyImportForm = $("#key-import-form"); const keyImportForm = $("#key-import-form");
keyImportModal.on("hidden.bs.modal", () => {
keyImportBodyInput.text("");
keyImportForm.trigger("reset");
});
const keyImportBodyInput = $("#key-import-body-input"); const keyImportBodyInput = $("#key-import-body-input");
const keyImportCopyButton = $("#key-import-copy-button"); const keyImportCopyButton = $("#key-import-copy-button");
@ -86,11 +90,4 @@
}); });
} }
} }
$(() => {
keyImportModal.on("hidden.bs.modal", () => {
keyImportBodyInput.text("");
keyImportForm.trigger("reset");
});
});
</script> </script>

View File

@ -36,6 +36,9 @@
<script> <script>
const loginModal = $("#login-modal"); const loginModal = $("#login-modal");
const loginForm = $("#login-form"); const loginForm = $("#login-form");
loginModal.on("hidden.bs.modal", () => {
loginForm.trigger("reset");
});
const loginPasswordInput = $("#login-password"); const loginPasswordInput = $("#login-password");
const loginUsernameInput = $("#login-username"); const loginUsernameInput = $("#login-username");
@ -74,10 +77,4 @@
showHidePasswordButton.addClass("bi-eye"); showHidePasswordButton.addClass("bi-eye");
} }
} }
$(() => {
loginModal.on("hidden.bs.modal", () => {
loginForm.trigger("reset");
});
});
</script> </script>

View File

@ -43,10 +43,41 @@
<script> <script>
const packageAddModal = $("#package-add-modal"); const packageAddModal = $("#package-add-modal");
const packageAddForm = $("#package-add-form"); const packageAddForm = $("#package-add-form");
packageAddModal.on("shown.bs.modal", () => {
$(`#package-add-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
});
packageAddModal.on("hidden.bs.modal", () => {
packageAddVariablesDiv.empty();
packageAddForm.trigger("reset");
});
const packageAddInput = $("#package-add-input"); const packageAddInput = $("#package-add-input");
const packageAddRepositoryInput = $("#package-add-repository-input"); const packageAddRepositoryInput = $("#package-add-repository-input");
const packageAddKnownPackagesList = $("#package-add-known-packages-dlist"); const packageAddKnownPackagesList = $("#package-add-known-packages-dlist");
packageAddInput.keyup(() => {
clearTimeout(packageAddInput.data("timeout"));
packageAddInput.data("timeout", setTimeout($.proxy(() => {
const value = packageAddInput.val();
if (value.length >= 3) {
$.ajax({
url: "/api/v1/service/search",
data: {"for": value},
type: "GET",
dataType: "json",
success: response => {
const options = response.map(pkg => {
const option = document.createElement("option");
option.value = pkg.package;
option.innerText = `${pkg.package} (${pkg.description})`;
return option;
});
packageAddKnownPackagesList.empty().append(options);
},
});
}
}, this), 500));
});
const packageAddVariablesDiv = $("#package-add-variables-div"); const packageAddVariablesDiv = $("#package-add-variables-div");
@ -125,39 +156,4 @@
doPackageAction("/api/v1/service/request", [packages], repository, onSuccess, onFailure, patches); doPackageAction("/api/v1/service/request", [packages], repository, onSuccess, onFailure, patches);
} }
} }
$(() => {
packageAddModal.on("shown.bs.modal", () => {
$(`#package-add-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
});
packageAddModal.on("hidden.bs.modal", () => {
packageAddVariablesDiv.empty();
packageAddForm.trigger("reset");
});
packageAddInput.keyup(() => {
clearTimeout(packageAddInput.data("timeout"));
packageAddInput.data("timeout", setTimeout($.proxy(() => {
const value = packageAddInput.val();
if (value.length >= 3) {
$.ajax({
url: "/api/v1/service/search",
data: {"for": value},
type: "GET",
dataType: "json",
success: response => {
const options = response.map(pkg => {
const option = document.createElement("option");
option.value = pkg.package;
option.innerText = `${pkg.package} (${pkg.description})`;
return option;
});
packageAddKnownPackagesList.empty().append(options);
},
});
}
}, this), 500));
});
});
</script> </script>

View File

@ -72,6 +72,26 @@
const packageInfoModal = $("#package-info-modal"); const packageInfoModal = $("#package-info-modal");
const packageInfoModalHeader = $("#package-info-modal-header"); const packageInfoModalHeader = $("#package-info-modal-header");
const packageInfo = $("#package-info"); const packageInfo = $("#package-info");
packageInfoModal.on("hidden.bs.modal", () => {
packageInfoAurUrl.empty();
packageInfoDepends.empty();
packageInfoGroups.empty();
packageInfoLicenses.empty();
packageInfoPackager.empty();
packageInfoPackages.empty();
packageInfoUpstreamUrl.empty();
packageInfoVersion.empty();
packageInfoVariablesBlock.attr("hidden", true);
packageInfoVariablesDiv.empty();
packageInfoLogsInput.empty();
packageInfoChangesInput.empty();
packageInfoModal.trigger("reset");
hideInfoControls(true);
});
const packageInfoLogsInput = $("#package-info-logs-input"); const packageInfoLogsInput = $("#package-info-logs-input");
const packageInfoLogsCopyButton = $("#package-info-logs-copy-button"); const packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
@ -289,27 +309,4 @@
if (isPackageBaseSet) packageInfoModal.modal("show"); if (isPackageBaseSet) packageInfoModal.modal("show");
} }
$(() => {
packageInfoModal.on("hidden.bs.modal", () => {
packageInfoAurUrl.empty();
packageInfoDepends.empty();
packageInfoGroups.empty();
packageInfoLicenses.empty();
packageInfoPackager.empty();
packageInfoPackages.empty();
packageInfoUpstreamUrl.empty();
packageInfoVersion.empty();
packageInfoVariablesBlock.attr("hidden", true);
packageInfoVariablesDiv.empty();
packageInfoLogsInput.empty();
packageInfoChangesInput.empty();
packageInfoModal.trigger("reset");
hideInfoControls(true);
});
});
</script> </script>

View File

@ -35,6 +35,11 @@
<script> <script>
const packageRebuildModal = $("#package-rebuild-modal"); const packageRebuildModal = $("#package-rebuild-modal");
const packageRebuildForm = $("#package-rebuild-form"); const packageRebuildForm = $("#package-rebuild-form");
packageRebuildModal.on("shown.bs.modal", () => {
$(`#package-rebuild-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
});
packageRebuildModal.on("hidden.bs.modal", () => { packageRebuildForm.trigger("reset"); });
const packageRebuildDependencyInput = $("#package-rebuild-dependency-input"); const packageRebuildDependencyInput = $("#package-rebuild-dependency-input");
const packageRebuildRepositoryInput = $("#package-rebuild-repository-input"); const packageRebuildRepositoryInput = $("#package-rebuild-repository-input");
@ -49,12 +54,4 @@
doPackageAction("/api/v1/service/rebuild", [packages], repository, onSuccess, onFailure); doPackageAction("/api/v1/service/rebuild", [packages], repository, onSuccess, onFailure);
} }
} }
$(() => {
packageRebuildModal.on("shown.bs.modal", () => {
$(`#package-rebuild-repository-input option[value="${repository.architecture}-${repository.repository}"]`).prop("selected", true);
});
packageRebuildModal.on("hidden.bs.modal", () => { packageRebuildForm.trigger("reset"); });
});
</script> </script>

View File

@ -9,8 +9,46 @@
const packageInfoUpdateButton = $("#package-info-update-button"); const packageInfoUpdateButton = $("#package-info-update-button");
let repository = null; let repository = null;
$("#repositories a").on("click", (event) => {
const element = event.target;
repository = {
architecture: element.dataset.architecture,
repository: element.dataset.repository,
};
packageUpdateButton.html(`<i class="bi bi-play"></i> update<span class="d-none d-sm-inline"> ${safe(repository.repository)} (${safe(repository.architecture)})</span>`);
$(`#${element.id}`).tab("show");
reload();
});
const table = $("#packages"); const table = $("#packages");
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table", () => {
packageRemoveButton.prop("disabled", !table.bootstrapTable("getSelections").length);
});
table.on("click-row.bs.table", (self, data, row, cell) => {
if (0 === cell || "base" === cell) {
const method = data[0] === true ? "uncheckBy" : "checkBy"; // fck javascript
table.bootstrapTable(method, {field: "id", values: [data.id]});
} else showPackageInfo(data.id);
});
table.on("created-controls.bs.table", () => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
pickerInput.on("apply.daterangepicker", (event, picker) => {
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
table.bootstrapTable("triggerSearch");
});
pickerInput.on("cancel.daterangepicker", () => {
pickerInput.val("");
table.bootstrapTable("triggerSearch");
});
});
const statusBadge = $("#badge-status"); const statusBadge = $("#badge-status");
const versionBadge = $("#badge-version"); const versionBadge = $("#badge-version");
@ -183,46 +221,7 @@
} }
$(() => { $(() => {
$("#repositories a").on("click", event => { table.bootstrapTable({});
const element = event.target;
repository = {
architecture: element.dataset.architecture,
repository: element.dataset.repository,
};
packageUpdateButton.html(`<i class="bi bi-play"></i> update<span class="d-none d-sm-inline"> ${safe(repository.repository)} (${safe(repository.architecture)})</span>`);
$(`#${element.id}`).tab("show");
reload();
});
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table", () => {
packageRemoveButton.prop("disabled", !table.bootstrapTable("getSelections").length);
});
table.on("click-row.bs.table", (self, data, row, cell) => {
if (0 === cell || "base" === cell) {
const method = data[0] === true ? "uncheckBy" : "checkBy"; // fck javascript
table.bootstrapTable(method, {field: "id", values: [data.id]});
} else showPackageInfo(data.id);
});
table.on("created-controls.bs.table", () => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
pickerInput.on("apply.daterangepicker", (event, picker) => {
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
table.bootstrapTable("triggerSearch");
});
pickerInput.on("cancel.daterangepicker", () => {
pickerInput.val("");
table.bootstrapTable("triggerSearch");
});
});
statusBadge.popover(); statusBadge.popover();
selectRepository(); selectRepository();
}); });

View File

@ -102,6 +102,25 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<script> <script>
const table = $("#packages"); const table = $("#packages");
table.on("created-controls.bs.table", () => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
pickerInput.on("apply.daterangepicker", (event, picker) => {
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
table.bootstrapTable("triggerSearch");
});
pickerInput.on("cancel.daterangepicker", () => {
pickerInput.val("");
table.bootstrapTable("triggerSearch");
});
});
const pacmanConf = $("#pacman-conf"); const pacmanConf = $("#pacman-conf");
const pacmanConfCopyButton = $("#copy-btn"); const pacmanConfCopyButton = $("#copy-btn");
@ -122,28 +141,6 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
function filterListLicenses() { function filterListLicenses() {
return extractDataList(table.bootstrapTable("getData"), "licenses"); return extractDataList(table.bootstrapTable("getData"), "licenses");
} }
$(() => {
table.on("created-controls.bs.table", () => {
const pickerInput = $(".bootstrap-table-filter-control-timestamp");
pickerInput.daterangepicker({
autoUpdateInput: false,
locale: {
cancelLabel: "Clear",
},
});
pickerInput.on("apply.daterangepicker", (event, picker) => {
pickerInput.val(`${picker.startDate.format("YYYY-MM-DD")} - ${picker.endDate.format("YYYY-MM-DD")}`);
table.bootstrapTable("triggerSearch");
});
pickerInput.on("cancel.daterangepicker", () => {
pickerInput.val("");
table.bootstrapTable("triggerSearch");
});
});
});
</script> </script>
</body> </body>

View File

@ -1,7 +1,7 @@
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" integrity="sha384-4ZTAzTbfB8H7hkWtXbyNDzDvxirmBT7EmURIvfOJ3Foympc+OD9p+bZNNENaJXgW" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js" integrity="sha384-8hHkOkbWN1TLWwet/jpbJ0zbx3FJDeYJgQ8dX1mRrv/vfCfHCqFSFZYCgaMML3z9" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" integrity="sha384-Gn1XZMJEKL3ycoWq97jYAl+FP3vXQYE2ObBgzgcPMKOZdUZdF6ZuyUxbGC2bAnUT" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.min.js" integrity="sha384-u4eJN1VWrTf/FnYYQJo2kqJyVxEQf5UmWY4iUcNAoLenOEtEuCkfwc5bKvZOWBi5" crossorigin="anonymous" type="application/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.28.0/tableExport.min.js" integrity="sha384-1Rz4Kz/y1rSWw+ZsjTcxB684XgofbO8iizY+UFIzCwFeQ+QUyhBNWBMh/STOyomI" crossorigin="anonymous" type="application/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.28.0/tableExport.min.js" integrity="sha384-1Rz4Kz/y1rSWw+ZsjTcxB684XgofbO8iizY+UFIzCwFeQ+QUyhBNWBMh/STOyomI" crossorigin="anonymous" type="application/javascript"></script>

View File

@ -596,7 +596,7 @@ _set_new_action() {
current_action_nargs=1 current_action_nargs=1
fi fi
current_action_args_start_index=$(( $word_index + 1 - $pos_only )) current_action_args_start_index=$(( $word_index + 1 ))
current_action_is_positional=$2 current_action_is_positional=$2
} }
@ -623,7 +623,6 @@ _shtab_ahriman() {
local prefix=_shtab_ahriman local prefix=_shtab_ahriman
local word_index=0 local word_index=0
local pos_only=0 # "--" delimeter not encountered yet
_set_parser_defaults _set_parser_defaults
word_index=1 word_index=1
@ -632,7 +631,6 @@ _shtab_ahriman() {
while [ $word_index -ne $COMP_CWORD ]; do while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}" local this_word="${COMP_WORDS[$word_index]}"
if [[ $pos_only = 1 || " $this_word " != " -- " ]]; then
if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then
# valid subcommand: add it to the prefix & reset the current action # valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)" prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
@ -649,21 +647,18 @@ _shtab_ahriman() {
if [[ "$current_action_nargs" != "*" ]] && \ if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \ [[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \ [[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index - $pos_only >= \ (( $word_index + 1 - $current_action_args_start_index >= \
$current_action_nargs )); then $current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1" $current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true _set_new_action "pos_${completed_positional_actions}" true
fi fi
else
pos_only=1 # "--" delimeter encountered
fi
let "word_index+=1" let "word_index+=1"
done done
# Generate the completions # Generate the completions
if [[ $pos_only = 0 && "${completing_word}" == -* ]]; then if [[ "${completing_word}" == -* ]]; then
# optional argument started: use option strings # optional argument started: use option strings
COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") ) COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
else else

View File

@ -1,4 +1,4 @@
.TH AHRIMAN "1" "2024\-05\-05" "ahriman" "Generated Python Manual" .TH AHRIMAN "1" "2024\-02\-10" "ahriman" "Generated Python Manual"
.SH NAME .SH NAME
ahriman ahriman
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -742,12 +742,4 @@ _shtab_ahriman() {
typeset -A opt_args typeset -A opt_args
if [[ $zsh_eval_context[-1] == eval ]]; then
# eval/source/. command, register function for later
compdef _shtab_ahriman -N ahriman
else
# autoload from fpath, call function directly
_shtab_ahriman "$@" _shtab_ahriman "$@"
fi

View File

@ -82,7 +82,6 @@ web = [
"aiohttp_security", "aiohttp_security",
"cryptography", "cryptography",
"requests-unixsocket", # required by unix socket support "requests-unixsocket", # required by unix socket support
"setuptools", # required by aiohttp-apispec
] ]
[tool.flit.sdist] [tool.flit.sdist]

View File

@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
__version__ = "2.13.6" __version__ = "2.13.4"

View File

@ -68,7 +68,7 @@ class Repo(LazyLogging):
path(Path): path to archive to add path(Path): path to archive to add
""" """
check_output( check_output(
"repo-add", *self.sign_args, "--remove", str(self.repo_path), str(path), "repo-add", *self.sign_args, "-R", str(self.repo_path), str(path),
exception=BuildError.from_process(path.name), exception=BuildError.from_process(path.name),
cwd=self.paths.repository, cwd=self.paths.repository,
logger=self.logger, logger=self.logger,
@ -78,13 +78,8 @@ class Repo(LazyLogging):
""" """
create empty repository database. It just calls add with empty arguments create empty repository database. It just calls add with empty arguments
""" """
# since pacman-6.1.0 repo-add doesn't create empty database in case if no packages supplied check_output("repo-add", *self.sign_args, str(self.repo_path),
# this code creates empty files instead cwd=self.paths.repository, logger=self.logger, user=self.uid)
if self.repo_path.exists():
return # database is already created, skip this part
self.repo_path.touch(exist_ok=True)
(self.paths.repository / f"{self.name}.db").symlink_to(self.repo_path)
def remove(self, package: str, filename: Path) -> None: def remove(self, package: str, filename: Path) -> None:
""" """

View File

@ -69,8 +69,7 @@ class OAuth(Mapping):
Returns: Returns:
str: login control as html code to insert str: login control as html code to insert
""" """
return f"""<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-{ return f"""<a class="nav-link" href="/api/v1/login" title="login via OAuth2"><i class="bi bi-{self.icon}"></i> login</a>"""
self.icon}"></i> login</a>"""
@staticmethod @staticmethod
def get_provider(name: str) -> type[aioauth_client.OAuth2Client]: def get_provider(name: str) -> type[aioauth_client.OAuth2Client]:

View File

@ -119,8 +119,7 @@ class Executor(PackageInfo, Cleaner):
# build package list based on user input # build package list based on user input
result = Result() result = Result()
packages = set(packages) # remove duplicates requested = set(packages)
requested = packages | {f"{package}-debug" for package in packages} # append debug packages
for local in self.packages(): for local in self.packages():
if local.base in packages or all(package in requested for package in local.packages): if local.base in packages or all(package in requested for package in local.packages):
packages_to_remove.update({ packages_to_remove.update({
@ -139,7 +138,7 @@ class Executor(PackageInfo, Cleaner):
# check for packages which were requested to remove, but weren't found locally # check for packages which were requested to remove, but weren't found locally
# it might happen for example, if there were no success build before # it might happen for example, if there were no success build before
for unknown in packages: for unknown in requested:
if unknown in packages_to_remove or unknown in bases_to_remove: if unknown in packages_to_remove or unknown in bases_to_remove:
continue continue
bases_to_remove.append(unknown) bases_to_remove.append(unknown)

View File

@ -183,12 +183,11 @@ post_install() {{
Returns: Returns:
str: package() function for PKGBUILD str: package() function for PKGBUILD
""" """
# somehow autopep thinks that construction inside contains valid python code and reformats it
return f"""{{ return f"""{{
install -Dm644 "{Path("$srcdir") / f"{self.name}.gpg"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}.gpg"}" install -Dm644 "{Path("$srcdir") / f"{self.name}.gpg"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}.gpg"}"
install -Dm644 "{Path("$srcdir") / f"{self.name}-revoked"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-revoked"}" install -Dm644 "{Path("$srcdir") / f"{self.name}-revoked"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-revoked"}"
install -Dm644 "{Path("$srcdir") / f"{self.name}-trusted"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-trusted"}" install -Dm644 "{Path("$srcdir") / f"{self.name}-trusted"}" "{Path("$pkgdir") / "usr" / "share" / "pacman" / "keyrings" / f"{self.name}-trusted"}"
}}""" # nopep8 }}"""
def sources(self) -> dict[str, Callable[[Path], None]]: def sources(self) -> dict[str, Callable[[Path], None]]:
""" """

View File

@ -162,8 +162,7 @@ class GitHub(Upload, HttpUpload):
Returns: Returns:
dict[str, Any] | None: GitHub API release object if release found and None otherwise dict[str, Any] | None: GitHub API release object if release found and None otherwise
""" """
url = f"https://api.github.com/repos/{self.github_owner}/{ url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases/tags/{self.github_release_tag}"
self.github_repository}/releases/tags/{self.github_release_tag}"
try: try:
response = self.make_request("GET", url) response = self.make_request("GET", url)
release: dict[str, Any] = response.json() release: dict[str, Any] = response.json()

View File

@ -349,7 +349,7 @@ def pretty_datetime(timestamp: datetime.datetime | float | int | None) -> str:
if timestamp is None: if timestamp is None:
return "" return ""
if isinstance(timestamp, (int, float)): if isinstance(timestamp, (int, float)):
timestamp = datetime.datetime.fromtimestamp(timestamp, datetime.UTC) timestamp = datetime.datetime.utcfromtimestamp(timestamp)
return timestamp.strftime("%Y-%m-%d %H:%M:%S") return timestamp.strftime("%Y-%m-%d %H:%M:%S")
@ -505,7 +505,7 @@ def utcnow() -> datetime.datetime:
Returns: Returns:
datetime.datetime: current time in UTC datetime.datetime: current time in UTC
""" """
return datetime.datetime.now(datetime.UTC) return datetime.datetime.utcnow()
def walk(directory_path: Path) -> Generator[Path, None, None]: def walk(directory_path: Path) -> Generator[Path, None, None]:

View File

@ -137,8 +137,8 @@ class AURPackage:
description=package.desc, description=package.desc,
num_votes=0, num_votes=0,
popularity=0.0, popularity=0.0,
first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC), first_submitted=datetime.datetime.utcfromtimestamp(0),
last_modified=datetime.datetime.fromtimestamp(package.builddate, datetime.UTC), last_modified=datetime.datetime.utcfromtimestamp(package.builddate),
url_path="", url_path="",
url=package.url, url=package.url,
out_of_date=None, out_of_date=None,
@ -175,11 +175,13 @@ class AURPackage:
description=dump["pkgdesc"], description=dump["pkgdesc"],
num_votes=0, num_votes=0,
popularity=0.0, popularity=0.0,
first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC), first_submitted=datetime.datetime.utcfromtimestamp(0),
last_modified=datetime.datetime.fromisoformat(dump["last_update"]), last_modified=datetime.datetime.strptime(dump["last_update"], "%Y-%m-%dT%H:%M:%S.%fZ"),
url_path="", url_path="",
url=dump["url"], url=dump["url"],
out_of_date=datetime.datetime.fromisoformat(dump["flag_date"]) if dump.get("flag_date") else None, out_of_date=datetime.datetime.strptime(
dump["flag_date"],
"%Y-%m-%dT%H:%M:%S.%fZ") if dump["flag_date"] is not None else None,
maintainer=next(iter(dump["maintainers"]), None), maintainer=next(iter(dump["maintainers"]), None),
submitter=None, submitter=None,
repository=dump["repo"], repository=dump["repo"],
@ -206,9 +208,9 @@ class AURPackage:
""" """
identity_mapper: Callable[[Any], Any] = lambda value: value identity_mapper: Callable[[Any], Any] = lambda value: value
value_mapper: dict[str, Callable[[Any], Any]] = { value_mapper: dict[str, Callable[[Any], Any]] = {
"out_of_date": lambda value: datetime.datetime.fromtimestamp(value, datetime.UTC) if value is not None else None, "out_of_date": lambda value: datetime.datetime.utcfromtimestamp(value) if value is not None else None,
"first_submitted": lambda value: datetime.datetime.fromtimestamp(value, datetime.UTC), "first_submitted": datetime.datetime.utcfromtimestamp,
"last_modified": lambda value: datetime.datetime.fromtimestamp(value, datetime.UTC), "last_modified": datetime.datetime.utcfromtimestamp,
} }
result: dict[str, Any] = {} result: dict[str, Any] = {}

View File

@ -18,8 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from dataclasses import dataclass from dataclasses import dataclass
from elftools.elf.dynamic import DynamicSection from elftools.elf.dynamic import DynamicSection # type: ignore[import-untyped]
from elftools.elf.elffile import ELFFile from elftools.elf.elffile import ELFFile # type: ignore[import-untyped]
from pathlib import Path from pathlib import Path
from typing import IO from typing import IO
@ -57,19 +57,13 @@ class PackageArchive:
if not PackageArchive.is_elf(binary_file): if not PackageArchive.is_elf(binary_file):
return [] return []
elf_file = ELFFile(binary_file) # type: ignore[no-untyped-call] elf_file = ELFFile(binary_file)
dynamic_section = next( dynamic_section = next(
(section for section in elf_file.iter_sections() # type: ignore[no-untyped-call] (section for section in elf_file.iter_sections() if isinstance(section, DynamicSection)), None)
if isinstance(section, DynamicSection)),
None)
if dynamic_section is None: if dynamic_section is None:
return [] return []
return [ return [tag.needed for tag in dynamic_section.iter_tags() if tag.entry.d_tag == "DT_NEEDED"]
tag.needed
for tag in dynamic_section.iter_tags() # type: ignore[no-untyped-call]
if tag.entry.d_tag == "DT_NEEDED"
]
@staticmethod @staticmethod
def is_elf(content: IO[bytes]) -> bool: def is_elf(content: IO[bytes]) -> bool:

View File

@ -133,8 +133,8 @@ def aur_package_ahriman() -> AURPackage:
description="ArcH linux ReposItory MANager", description="ArcH linux ReposItory MANager",
num_votes=0, num_votes=0,
popularity=0, popularity=0,
first_submitted=datetime.datetime.fromtimestamp(1618008285, datetime.UTC), first_submitted=datetime.datetime.utcfromtimestamp(1618008285),
last_modified=datetime.datetime.fromtimestamp(1673826351, datetime.UTC), last_modified=datetime.datetime.utcfromtimestamp(1673826351),
url_path="/cgit/aur.git/snapshot/ahriman.tar.gz", url_path="/cgit/aur.git/snapshot/ahriman.tar.gz",
url="https://github.com/arcan1s/ahriman", url="https://github.com/arcan1s/ahriman",
out_of_date=None, out_of_date=None,
@ -200,8 +200,8 @@ def aur_package_akonadi() -> AURPackage:
description="PIM layer, which provides an asynchronous API to access all kind of PIM data", description="PIM layer, which provides an asynchronous API to access all kind of PIM data",
num_votes=0, num_votes=0,
popularity=0.0, popularity=0.0,
first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC), first_submitted=datetime.datetime.utcfromtimestamp(0),
last_modified=datetime.datetime.fromtimestamp(1646555990.610, datetime.UTC), last_modified=datetime.datetime.utcfromtimestamp(1646555990.610),
url_path="", url_path="",
url="https://kontact.kde.org", url="https://kontact.kde.org",
out_of_date=None, out_of_date=None,

View File

@ -26,28 +26,13 @@ def test_repo_add(repo: Repo, mocker: MockerFixture) -> None:
def test_repo_init(repo: Repo, mocker: MockerFixture) -> None: def test_repo_init(repo: Repo, mocker: MockerFixture) -> None:
""" """
must create empty database files must call repo-add with empty package list on repo initializing
""" """
mocker.patch("pathlib.Path.exists", return_value=False) check_output_mock = mocker.patch("ahriman.core.alpm.repo.check_output")
touch_mock = mocker.patch("pathlib.Path.touch")
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
repo.init() repo.init()
touch_mock.assert_called_once_with(exist_ok=True) check_output_mock.assert_called_once() # it will be checked later
symlink_mock.assert_called_once_with(repo.repo_path) assert check_output_mock.call_args[0][0] == "repo-add"
def test_repo_init_skip(repo: Repo, mocker: MockerFixture) -> None:
"""
must do not create files if database already exists
"""
mocker.patch("pathlib.Path.exists", return_value=True)
touch_mock = mocker.patch("pathlib.Path.touch")
symlink_mock = mocker.patch("pathlib.Path.symlink_to")
repo.init()
touch_mock.assert_not_called()
symlink_mock.assert_not_called()
def test_repo_remove(repo: Repo, mocker: MockerFixture) -> None: def test_repo_remove(repo: Repo, mocker: MockerFixture) -> None:

View File

@ -92,28 +92,6 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
status_client_mock.assert_called_once_with(package_ahriman.base) status_client_mock.assert_called_once_with(package_ahriman.base)
def test_process_remove_with_debug(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run remove debug packages too
"""
package_ahriman.packages = {
package_ahriman.base: package_ahriman.packages[package_ahriman.base],
f"{package_ahriman.base}-debug": package_ahriman.packages[package_ahriman.base],
}
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear")
mocker.patch("ahriman.core.database.SQLite.package_clear")
mocker.patch("ahriman.core.status.client.Client.package_remove")
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
executor.process_remove([package_ahriman.base])
# must remove via alpm wrapper
repo_remove_mock.assert_has_calls([
MockCall(package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath),
MockCall(f"{package_ahriman.base}-debug", package_ahriman.packages[package_ahriman.base].filepath),
])
def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package, def test_process_remove_base_multiple(executor: Executor, package_python_schedule: Package,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """

View File

@ -6,6 +6,7 @@ from pytest_mock import MockerFixture
from unittest.mock import MagicMock, call as MockCall from unittest.mock import MagicMock, call as MockCall
from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator
from ahriman.core.util import utcnow
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -37,7 +38,7 @@ def test_pkgver(pkgbuild_generator: PkgbuildGenerator, mocker: MockerFixture) ->
must implement default version as current date must implement default version as current date
""" """
mocker.patch("ahriman.core.support.pkgbuild.pkgbuild_generator.utcnow", return_value=datetime.datetime(2002, 3, 11)) mocker.patch("ahriman.core.support.pkgbuild.pkgbuild_generator.utcnow", return_value=datetime.datetime(2002, 3, 11))
assert pkgbuild_generator.pkgver == "20020311" assert pkgbuild_generator.pkgver == utcnow().strftime("20020311")
def test_url(pkgbuild_generator: PkgbuildGenerator) -> None: def test_url(pkgbuild_generator: PkgbuildGenerator) -> None: