Compare commits

..

11 Commits

Author SHA1 Message Date
0e7ca45b54 implement local reporter mode 2024-05-07 14:54:13 +03:00
1ec1bd05eb feat: add abillity to check broken dependencies (#122)
* implement elf dynamic linking check

* load local database too in pacman wrapper
2024-05-05 22:03:07 +03:00
6d05389639 Release 2.13.6 2024-05-05 21:59:30 +03:00
daf9841717 fix: update integrity checksums for momentjs and daterangepicker 2024-05-05 21:17:30 +03:00
0d243a781a refactor: update code to the latest python (3.12+) 2024-05-05 21:17:30 +03:00
cf2e66a934 fix: remove debug packages together with normal ones (#124) 2024-05-05 21:17:30 +03:00
f01f35238d Release 2.13.5 2024-04-04 13:33:03 +03:00
d30d512eb6 fix: update Repo.init to the latest pacman release 2024-04-04 13:16:05 +03:00
0437f90e5a build: install base-devel package 2024-04-04 13:16:03 +03:00
3cab65855a fix: lazy web component initialization
In some cases (probably slow internet) in place initialization can cause
exception, because elements are not available yet. This commit moves
events initialization to $()
2024-04-04 13:14:17 +03:00
ecfb615f97 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-04-04 13:14:17 +03:00
34 changed files with 2729 additions and 2643 deletions

View File

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

View File

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

View File

@ -33,9 +33,9 @@ COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
## 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 && \
pacman -Sy --noconfirm --asdeps python-build python-flit python-installer python-wheel && \
pacman -Sy --noconfirm --asdeps base-devel 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 && \
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-apispec-git python-aiohttp-cors \
runuser -u build -- install-aur-package python-aioauth-client python-webargs python-aiohttp-apispec-git python-aiohttp-cors \
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

View File

@ -6,7 +6,7 @@ for PACKAGE in "$@"; do
BUILD_DIR="$(mktemp -d)"
git clone https://aur.archlinux.org/"$PACKAGE".git "$BUILD_DIR"
cd "$BUILD_DIR"
makepkg --noconfirm --install --rmdeps --syncdeps
makepkg --nocheck --noconfirm --install --rmdeps --syncdeps
cd /
rm -r "$BUILD_DIR"
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
pkgname='ahriman'
pkgver=2.13.4
pkgver=2.13.6
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')

View File

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

View File

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

View File

@ -43,41 +43,10 @@
<script>
const packageAddModal = $("#package-add-modal");
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 packageAddRepositoryInput = $("#package-add-repository-input");
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");
@ -156,4 +125,39 @@
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>

View File

@ -72,26 +72,6 @@
const packageInfoModal = $("#package-info-modal");
const packageInfoModalHeader = $("#package-info-modal-header");
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 packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
@ -309,4 +289,27 @@
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>

View File

@ -35,11 +35,6 @@
<script>
const packageRebuildModal = $("#package-rebuild-modal");
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 packageRebuildRepositoryInput = $("#package-rebuild-repository-input");
@ -54,4 +49,12 @@
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>

View File

@ -9,46 +9,8 @@
const packageInfoUpdateButton = $("#package-info-update-button");
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");
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 versionBadge = $("#badge-version");
@ -221,8 +183,47 @@
}
$(() => {
table.bootstrapTable({});
$("#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();
});
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();
selectRepository();
});
</script>
</script>

View File

@ -102,25 +102,6 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<script>
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 pacmanConfCopyButton = $("#copy-btn");
@ -141,6 +122,28 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
function filterListLicenses() {
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>
</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/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-u4eJN1VWrTf/FnYYQJo2kqJyVxEQf5UmWY4iUcNAoLenOEtEuCkfwc5bKvZOWBi5" 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/daterangepicker@3.1.0/daterangepicker.min.js" integrity="sha384-Gn1XZMJEKL3ycoWq97jYAl+FP3vXQYE2ObBgzgcPMKOZdUZdF6ZuyUxbGC2bAnUT" 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
fi
current_action_args_start_index=$(( $word_index + 1 ))
current_action_args_start_index=$(( $word_index + 1 - $pos_only ))
current_action_is_positional=$2
}
@ -623,6 +623,7 @@ _shtab_ahriman() {
local prefix=_shtab_ahriman
local word_index=0
local pos_only=0 # "--" delimeter not encountered yet
_set_parser_defaults
word_index=1
@ -631,26 +632,30 @@ _shtab_ahriman() {
while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}"
if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ $pos_only = 1 || " $this_word " != " -- " ]]; then
if [[ -n $sub_parsers && " ${sub_parsers[@]} " == *" ${this_word} "* ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ " ${current_option_strings[@]} " == *" ${this_word} "* ]]; then
# a new action should be acquired (due to recognised option string or
# no more input expected from current action);
# the next positional action can fill in here
_set_new_action $this_word false
fi
if [[ " ${current_option_strings[@]} " == *" ${this_word} "* ]]; then
# a new action should be acquired (due to recognised option string or
# no more input expected from current action);
# the next positional action can fill in here
_set_new_action $this_word false
fi
if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index >= \
$current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true
if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index - $pos_only >= \
$current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true
fi
else
pos_only=1 # "--" delimeter encountered
fi
let "word_index+=1"
@ -658,7 +663,7 @@ _shtab_ahriman() {
# Generate the completions
if [[ "${completing_word}" == -* ]]; then
if [[ $pos_only = 0 && "${completing_word}" == -* ]]; then
# optional argument started: use option strings
COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
else

View File

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

View File

@ -742,4 +742,12 @@ _shtab_ahriman() {
typeset -A opt_args
_shtab_ahriman "$@"
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 "$@"
fi

View File

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

View File

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

View File

@ -68,7 +68,7 @@ class Repo(LazyLogging):
path(Path): path to archive to add
"""
check_output(
"repo-add", *self.sign_args, "-R", str(self.repo_path), str(path),
"repo-add", *self.sign_args, "--remove", str(self.repo_path), str(path),
exception=BuildError.from_process(path.name),
cwd=self.paths.repository,
logger=self.logger,
@ -78,8 +78,13 @@ class Repo(LazyLogging):
"""
create empty repository database. It just calls add with empty arguments
"""
check_output("repo-add", *self.sign_args, str(self.repo_path),
cwd=self.paths.repository, logger=self.logger, user=self.uid)
# since pacman-6.1.0 repo-add doesn't create empty database in case if no packages supplied
# this code creates empty files instead
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:
"""

View File

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

View File

@ -345,5 +345,5 @@ class UnsafeRunError(RuntimeError):
root_uid(int): ID of the owner of root directory
"""
RuntimeError.__init__(self, f"Current UID {current_uid} differs from root owner {root_uid}. "
f"Note that for the most actions it is unsafe to run application as different user."
f" If you are 100% sure that it must be there try --unsafe option")
f"Note that for the most actions it is unsafe to run application as different user."
f" If you are 100% sure that it must be there try --unsafe option")

View File

@ -106,7 +106,7 @@ class RemoteCall(Report):
"aur": self.update_aur,
"local": self.update_local,
"manual": self.update_manual,
})
})
response_json = response.json()
process_id: str = response_json["process_id"]

View File

@ -119,7 +119,8 @@ class Executor(PackageInfo, Cleaner):
# build package list based on user input
result = Result()
requested = set(packages)
packages = set(packages) # remove duplicates
requested = packages | {f"{package}-debug" for package in packages} # append debug packages
for local in self.packages():
if local.base in packages or all(package in requested for package in local.packages):
packages_to_remove.update({
@ -138,7 +139,7 @@ class Executor(PackageInfo, Cleaner):
# 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
for unknown in requested:
for unknown in packages:
if unknown in packages_to_remove or unknown in bases_to_remove:
continue
bases_to_remove.append(unknown)

View File

@ -183,11 +183,12 @@ post_install() {{
Returns:
str: package() function for PKGBUILD
"""
# somehow autopep thinks that construction inside contains valid python code and reformats it
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}-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"}"
}}"""
}}""" # nopep8
def sources(self) -> dict[str, Callable[[Path], None]]:
"""

View File

@ -162,7 +162,8 @@ class GitHub(Upload, HttpUpload):
Returns:
dict[str, Any] | None: GitHub API release object if release found and None otherwise
"""
url = f"https://api.github.com/repos/{self.github_owner}/{self.github_repository}/releases/tags/{self.github_release_tag}"
url = f"https://api.github.com/repos/{self.github_owner}/{
self.github_repository}/releases/tags/{self.github_release_tag}"
try:
response = self.make_request("GET", url)
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:
return ""
if isinstance(timestamp, (int, float)):
timestamp = datetime.datetime.utcfromtimestamp(timestamp)
timestamp = datetime.datetime.fromtimestamp(timestamp, datetime.UTC)
return timestamp.strftime("%Y-%m-%d %H:%M:%S")
@ -505,7 +505,7 @@ def utcnow() -> datetime.datetime:
Returns:
datetime.datetime: current time in UTC
"""
return datetime.datetime.utcnow()
return datetime.datetime.now(datetime.UTC)
def walk(directory_path: Path) -> Generator[Path, None, None]:

View File

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

View File

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

View File

@ -133,8 +133,8 @@ def aur_package_ahriman() -> AURPackage:
description="ArcH linux ReposItory MANager",
num_votes=0,
popularity=0,
first_submitted=datetime.datetime.utcfromtimestamp(1618008285),
last_modified=datetime.datetime.utcfromtimestamp(1673826351),
first_submitted=datetime.datetime.fromtimestamp(1618008285, datetime.UTC),
last_modified=datetime.datetime.fromtimestamp(1673826351, datetime.UTC),
url_path="/cgit/aur.git/snapshot/ahriman.tar.gz",
url="https://github.com/arcan1s/ahriman",
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",
num_votes=0,
popularity=0.0,
first_submitted=datetime.datetime.utcfromtimestamp(0),
last_modified=datetime.datetime.utcfromtimestamp(1646555990.610),
first_submitted=datetime.datetime.fromtimestamp(0, datetime.UTC),
last_modified=datetime.datetime.fromtimestamp(1646555990.610, datetime.UTC),
url_path="",
url="https://kontact.kde.org",
out_of_date=None,

View File

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

View File

@ -85,7 +85,7 @@ def test_remote_update(remote_call: RemoteCall, mocker: MockerFixture) -> None:
"aur": False,
"local": False,
"manual": True,
})
})
def test_remote_wait(remote_call: RemoteCall, mocker: MockerFixture) -> None:

View File

@ -92,6 +92,28 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
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,
mocker: MockerFixture) -> None:
"""

View File

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