Compare commits

..

6 Commits

Author SHA1 Message Date
68d1046dca refine package logging 2025-02-24 17:01:09 +02:00
08640d9108 feat: add dashboard (#139) 2025-02-24 00:10:15 +02:00
65324633b4 feat: add counters to repository stats overview 2025-02-24 00:10:15 +02:00
ed67898012 fix: parse non-utf pkgbuilds as well (#140)
it has been reported that duriing reading pkgbuilds with latin-1 charset
the execption will be raised. Well, it is one more point to rewrite
parser to use own impl instead of shlex and parse raw byte array instead
2025-02-24 00:10:15 +02:00
a1a8dd68e8 type: remove unused ignore directive 2025-02-24 00:10:15 +02:00
a9505386c2 fix: force dry run build on task initialization for VCS packages
Previously if package is VCS and version in PKGBUILD doesn't match to
AUR one, then makepkg will update pkgbuild ignoring all previous pkgrel
patches

With this change during task init dry ryn process is always run for vcs
packages
2025-02-24 00:10:15 +02:00
14 changed files with 673 additions and 61 deletions

View File

@ -95,6 +95,19 @@ class DuplicateRunError(RuntimeError):
self, "Another application instance is run. This error can be suppressed by using --force flag.")
class EncodeError(ValueError):
"""
exception used for bytes encoding errors
"""
def __init__(self, encodings: list[str]) -> None:
"""
Args:
encodings(list[str]): list of encodings tried
"""
ValueError.__init__(self, f"Could not encode bytes by using {encodings}")
class ExitCode(RuntimeError):
"""
special exception which has to be thrown to return non-zero status without error message

View File

@ -24,6 +24,7 @@ from pathlib import Path
from typing import Any, IO, Self
from ahriman.core.alpm.pkgbuild_parser import PkgbuildParser, PkgbuildToken
from ahriman.core.exceptions import EncodeError
from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -33,11 +34,14 @@ class Pkgbuild(Mapping[str, Any]):
model and proxy for PKGBUILD properties
Attributes:
DEFAULT_ENCODINGS(list[str]): (class attribute) list of encoding to be applied on the file content
fields(dict[str, PkgbuildPatch]): PKGBUILD fields
"""
fields: dict[str, PkgbuildPatch]
DEFAULT_ENCODINGS = ["utf8", "latin-1"]
@property
def variables(self) -> dict[str, str]:
"""
@ -54,18 +58,34 @@ class Pkgbuild(Mapping[str, Any]):
}
@classmethod
def from_file(cls, path: Path) -> Self:
def from_file(cls, path: Path, encodings: list[str] | None = None) -> Self:
"""
parse PKGBUILD from the file
Args:
path(Path): path to the PKGBUILD file
encodings(list[str] | None, optional): the encoding of the file (Default value = None)
Returns:
Self: constructed instance of self
Raises:
EncodeError: if encoding is unknown
"""
with path.open(encoding="utf8") as input_file:
return cls.from_io(input_file)
# read raw bytes from file
with path.open("rb") as input_file:
content = input_file.read()
# decode bytes content based on either
encodings = encodings or cls.DEFAULT_ENCODINGS
for encoding in encodings:
try:
io = StringIO(content.decode(encoding))
return cls.from_io(io)
except ValueError:
pass
raise EncodeError(encodings)
@classmethod
def from_io(cls, stream: IO[str]) -> Self:

View File

@ -0,0 +1,8 @@
from ahriman.core.database.migrations.m015_logs_process_id import steps
def test_migration_logs_process_id() -> None:
"""
migration must not be empty
"""
assert steps

View File

@ -14,8 +14,12 @@ def test_logs_insert_remove_version(database: SQLite, package_ahriman: Package,
database.logs_insert(LogRecordId(package_python_schedule.base, "1"), 42.0, "message 3")
database.logs_remove(package_ahriman.base, "1")
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
assert database.logs_get(package_ahriman.base) == [
(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1"),
]
assert database.logs_get(package_python_schedule.base) == [
(LogRecordId(package_python_schedule.base, "1"), 42.0, "message 3"),
]
def test_logs_insert_remove_multi(database: SQLite, package_ahriman: Package) -> None:
@ -28,7 +32,7 @@ def test_logs_insert_remove_multi(database: SQLite, package_ahriman: Package) ->
database.logs_remove(package_ahriman.base, None, RepositoryId("i686", database._repository_id.name))
assert not database.logs_get(package_ahriman.base, repository_id=RepositoryId("i686", database._repository_id.name))
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]
assert database.logs_get(package_ahriman.base) == [(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")]
def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
@ -41,7 +45,9 @@ def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, pac
database.logs_remove(package_ahriman.base, None)
assert not database.logs_get(package_ahriman.base)
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
assert database.logs_get(package_python_schedule.base) == [
(LogRecordId(package_python_schedule.base, "1"), 42.0, "message 3"),
]
def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
@ -50,7 +56,10 @@ def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1"), (43.0, "message 2")]
assert database.logs_get(package_ahriman.base) == [
(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1"),
(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2"),
]
def test_logs_insert_get_pagination(database: SQLite, package_ahriman: Package) -> None:
@ -59,7 +68,9 @@ def test_logs_insert_get_pagination(database: SQLite, package_ahriman: Package)
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
assert database.logs_get(package_ahriman.base, 1, 1) == [(42.0, "message 1")]
assert database.logs_get(package_ahriman.base, 1, 1) == [
(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1"),
]
def test_logs_insert_get_multi(database: SQLite, package_ahriman: Package) -> None:
@ -71,5 +82,40 @@ def test_logs_insert_get_multi(database: SQLite, package_ahriman: Package) -> No
RepositoryId("i686", database._repository_id.name))
assert database.logs_get(package_ahriman.base,
repository_id=RepositoryId("i686", database._repository_id.name)) == [(43.0, "message 2")]
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]
repository_id=RepositoryId("i686", database._repository_id.name)) == [
(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2"),
]
assert database.logs_get(package_ahriman.base) == [
(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1"),
]
def test_logs_rotate_remove_all(database: SQLite, package_ahriman: Package) -> None:
"""
must remove all records when rotating with keep_last_records is 0
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
database.logs_insert(LogRecordId(package_ahriman.base, "2"), 44.0, "message 3")
database.logs_rotate(0)
assert not database.logs_get(package_ahriman.base)
def test_logs_rotate_remove_duplicates(database: SQLite, package_ahriman: Package) -> None:
"""
must remove duplicate records while preserving the most recent one for each package version
"""
database.logs_insert(LogRecordId(package_ahriman.base, "1", "p1"), 42.0, "message 1")
database.logs_insert(LogRecordId(package_ahriman.base, "1", "p2"), 43.0, "message 2")
database.logs_insert(LogRecordId(package_ahriman.base, "1", "p3"), 44.0, "message 3")
database.logs_insert(LogRecordId(package_ahriman.base, "2", "p1"), 45.0, "message 4")
database.logs_rotate(2)
logs = database.logs_get(package_ahriman.base)
assert len(logs) == 2
assert logs == [
(LogRecordId(package_ahriman.base, "1", "p3"), 44.0, "message 3"),
(LogRecordId(package_ahriman.base, "2", "p1"), 45.0, "message 4"),
]

View File

@ -19,12 +19,14 @@ def test_load(configuration: Configuration, mocker: MockerFixture) -> None:
add_mock = mocker.patch("logging.Logger.addHandler")
load_mock = mocker.patch("ahriman.core.status.Client.load")
atexit_mock = mocker.patch("atexit.register")
_, repository_id = configuration.check_loaded()
handler = HttpLogHandler.load(repository_id, configuration, report=False)
assert handler
add_mock.assert_called_once_with(handler)
load_mock.assert_called_once_with(repository_id, configuration, report=False)
atexit_mock.assert_called_once_with(handler.rotate)
def test_load_exist(configuration: Configuration) -> None:
@ -93,3 +95,16 @@ def test_emit_skip(configuration: Configuration, log_record: logging.LogRecord,
handler.emit(log_record)
log_mock.assert_not_called()
def test_rotate(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must rotate logs
"""
rotate_mock = mocker.patch("ahriman.core.status.Client.logs_rotate")
_, repository_id = configuration.check_loaded()
handler = HttpLogHandler(repository_id, configuration, report=False, suppress_errors=False)
handler.rotate()
rotate_mock.assert_called_once_with(handler.keep_last_records)

View File

@ -87,13 +87,3 @@ def test_in_package_context_failed(database: SQLite, package_ahriman: Package, m
raise ValueError()
reset_mock.assert_called_once_with()
def test_suppress_logging(database: SQLite, mocker: MockerFixture) -> None:
"""
must temporary disable log messages
"""
disable_mock = mocker.patch("ahriman.core.log.lazy_logging.logging.disable")
with database.suppress_logging():
pass
disable_mock.assert_has_calls([MockCall(logging.WARNING), MockCall(logging.NOTSET)])

View File

@ -112,6 +112,13 @@ def test_event_get(client: Client) -> None:
client.event_get(None, None)
def test_logs_rotate(client: Client, package_ahriman: Package) -> None:
"""
must do not raise exception on logs rotation call
"""
client.logs_rotate(1)
def test_package_changes_get(client: Client, package_ahriman: Package) -> None:
"""
must raise not implemented on package changes request

View File

@ -34,6 +34,15 @@ def test_event_get(local_client: LocalClient, package_ahriman: Package, mocker:
local_client.repository_id)
def test_logs_rotate(local_client: LocalClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must rotate logs
"""
rotate_mock = mocker.patch("ahriman.core.database.SQLite.logs_rotate")
local_client.logs_rotate(42)
rotate_mock.assert_called_once_with(42, local_client.repository_id)
def test_package_changes_get(local_client: LocalClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must retrieve package changes

View File

@ -5,7 +5,6 @@ from pytest_mock import MockerFixture
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.status.watcher import Watcher
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package
@ -64,38 +63,6 @@ def test_package_get_failed(watcher: Watcher, package_ahriman: Package) -> None:
watcher.package_get(package_ahriman.base)
def test_package_logs_add_new(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must create package logs record for new package
"""
delete_mock = mocker.patch("ahriman.core.status.watcher.Watcher.package_logs_remove", create=True)
insert_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_logs_add")
log_record_id = LogRecordId(package_ahriman.base, watcher._last_log_record_id.version)
assert watcher._last_log_record_id != log_record_id
watcher.package_logs_add(log_record_id, 42.01, "log record")
delete_mock.assert_called_once_with(package_ahriman.base, log_record_id.version)
insert_mock.assert_called_once_with(log_record_id, 42.01, "log record")
assert watcher._last_log_record_id == log_record_id
def test_package_logs_add_update(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must create package logs record for current package
"""
delete_mock = mocker.patch("ahriman.core.status.watcher.Watcher.package_logs_remove", create=True)
insert_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_logs_add")
log_record_id = LogRecordId(package_ahriman.base, watcher._last_log_record_id.version)
watcher._last_log_record_id = log_record_id
watcher.package_logs_add(log_record_id, 42.01, "log record")
delete_mock.assert_not_called()
insert_mock.assert_called_once_with(log_record_id, 42.01, "log record")
def test_package_remove(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must remove package base

View File

@ -257,6 +257,57 @@ def test_event_get_failed_http_error_suppress(web_client: WebClient, mocker: Moc
logging_mock.assert_not_called()
def test_logs_rotate(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must rotate logs
"""
requests_mock = mocker.patch("ahriman.core.status.web_client.WebClient.make_request")
web_client.logs_rotate(42)
requests_mock.assert_called_once_with("DELETE", pytest.helpers.anyvar(str, True),
params=web_client.repository_id.query() + [("keep_last_records", "42")])
def test_logs_rotate_failed(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during logs rotation
"""
mocker.patch("requests.Session.request", side_effect=Exception())
web_client.logs_rotate(42)
def test_logs_rotate_failed_http_error(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during logs rotation
"""
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
web_client.logs_rotate(42)
def test_logs_rotate_failed_suppress(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during logs rotation and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=Exception())
logging_mock = mocker.patch("logging.exception")
web_client.logs_rotate(42)
logging_mock.assert_not_called()
def test_logs_rotate_failed_http_error_suppress(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress HTTP exception happened during logs rotation and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=requests.HTTPError())
logging_mock = mocker.patch("logging.exception")
web_client.logs_rotate(42)
logging_mock.assert_not_called()
def test_package_changes_get(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must get changes
@ -551,6 +602,7 @@ def test_package_logs_add(web_client: WebClient, log_record: logging.LogRecord,
payload = {
"created": log_record.created,
"message": log_record.getMessage(),
"process_id": LogRecordId.process_id,
"version": package_ahriman.version,
}
@ -588,7 +640,12 @@ def test_package_logs_get(web_client: WebClient, package_ahriman: Package, mocke
"""
must get logs
"""
message = {"created": 42.0, "message": "log"}
message = {
"created": 42.0,
"message": "log",
"version": package_ahriman.version,
"process_id": LogRecordId.process_id,
}
response_obj = requests.Response()
response_obj._content = json.dumps([message]).encode("utf8")
response_obj.status_code = 200
@ -598,7 +655,9 @@ def test_package_logs_get(web_client: WebClient, package_ahriman: Package, mocke
result = web_client.package_logs_get(package_ahriman.base, 1, 2)
requests_mock.assert_called_once_with("GET", pytest.helpers.anyvar(str, True),
params=web_client.repository_id.query() + [("limit", "1"), ("offset", "2")])
assert result == [(message["created"], message["message"])]
assert result == [
(LogRecordId(package_ahriman.base, package_ahriman.version), message["created"], message["message"]),
]
def test_package_logs_get_failed(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -471,6 +471,7 @@ def test_walk(resource_path_root: Path) -> None:
resource_path_root / "models" / "package_ahriman_pkgbuild",
resource_path_root / "models" / "package_gcc10_pkgbuild",
resource_path_root / "models" / "package_jellyfin-ffmpeg6-bin_pkgbuild",
resource_path_root / "models" / "package_pacman-static_pkgbuild",
resource_path_root / "models" / "package_python-pytest-loop_pkgbuild",
resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild",
resource_path_root / "models" / "package_vim-youcompleteme-git_pkgbuild",

View File

@ -1,9 +1,11 @@
import pytest
from io import StringIO
from io import BytesIO, StringIO
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.core.exceptions import EncodeError
from ahriman.models.pkgbuild import Pkgbuild
from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -23,13 +25,39 @@ def test_from_file(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> None:
must correctly load from file
"""
open_mock = mocker.patch("pathlib.Path.open")
open_mock.return_value.__enter__.return_value = BytesIO(b"content")
load_mock = mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_io", return_value=pkgbuild_ahriman)
assert Pkgbuild.from_file(Path("local"))
open_mock.assert_called_once_with(encoding="utf8")
open_mock.assert_called_once_with("rb")
load_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_from_file_latin(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> None:
"""
must correctly load from file with latin encoding
"""
open_mock = mocker.patch("pathlib.Path.open")
open_mock.return_value.__enter__.return_value = BytesIO("contént".encode("latin-1"))
load_mock = mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_io", return_value=pkgbuild_ahriman)
assert Pkgbuild.from_file(Path("local"))
open_mock.assert_called_once_with("rb")
load_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_from_file_unknown_encoding(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> None:
"""
must raise exception when encoding is unknown
"""
open_mock = mocker.patch("pathlib.Path.open")
io_mock = open_mock.return_value.__enter__.return_value = MagicMock()
io_mock.read.return_value.decode.side_effect = EncodeError(pkgbuild_ahriman.DEFAULT_ENCODINGS)
with pytest.raises(EncodeError):
assert Pkgbuild.from_file(Path("local"))
def test_from_io(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> None:
"""
must correctly load from io
@ -461,9 +489,7 @@ def test_parse_python_pytest_loop(resource_path_root: Path) -> None:
"pkgbase": "python-pytest-loop",
"_pname": "${pkgbase#python-}",
"_pyname": "${_pname//-/_}",
"pkgname": [
"python-${_pname}",
],
"pkgname": ["python-${_pname}"],
"pkgver": "1.0.13",
"pkgrel": "1",
"pkgdesc": "Pytest plugin for looping test execution.",
@ -487,3 +513,98 @@ def test_parse_python_pytest_loop(resource_path_root: Path) -> None:
"98365f49606d5068f92350f1d2569a5f",
],
}
def test_parse_pacman_static(resource_path_root: Path) -> None:
"""
must parse real PKGBUILDs correctly (pacman-static)
"""
pkgbuild = Pkgbuild.from_file(resource_path_root / "models" / "package_pacman-static_pkgbuild")
values = {key: value.value for key, value in pkgbuild.fields.items() if not value.is_function}
print(values)
assert values == {
"pkgbase": "pacman-static",
"pkgname": "pacman-static",
"pkgver": "7.0.0.r6.gc685ae6",
"_cares_ver": "1.34.4",
"_nghttp2_ver": "1.64.0",
"_curlver": "8.12.1",
"_sslver": "3.4.1",
"_zlibver": "1.3.1",
"_xzver": "5.6.4",
"_bzipver": "1.0.8",
"_zstdver": "1.5.6",
"_libarchive_ver": "3.7.7",
"_gpgerrorver": "1.51",
"_libassuanver": "3.0.0",
"_gpgmever": "1.24.2",
"pkgrel": "15",
"_git_tag": "7.0.0",
"_git_patch_level_commit": "c685ae6412af04cae1eaa5d6bda8c277c7ffb8c8",
"pkgdesc": "Statically-compiled pacman (to fix or install systems without libc)",
"arch": [
"i486",
"i686",
"pentium4",
"x86_64",
"arm",
"armv6h",
"armv7h",
"aarch64"
],
"url": "https://www.archlinux.org/pacman/",
"license": ["GPL-2.0-or-later"],
"depends": ["pacman"],
"makedepends": [
"meson",
"musl",
"kernel-headers-musl",
"git",
],
"options": [
"!emptydirs",
"!lto",
],
"source": [
"git+https://gitlab.archlinux.org/pacman/pacman.git#tag=v${_git_tag}?signed",
"pacman-revertme-makepkg-remove-libdepends-and-libprovides.patch::https://gitlab.archlinux.org/pacman/pacman/-/commit/354a300cd26bb1c7e6551473596be5ecced921de.patch",
],
"validpgpkeys": [
"6645B0A8C7005E78DB1D7864F99FFE0FEAE999BD",
"B8151B117037781095514CA7BBDFFC92306B1121",
],
"sha512sums": [
"44e00c2bc259fe6a85de71f7fd8a43fcfd1b8fb7d920d2267bd5b347e02f1dab736b3d96e31faf7b535480398e2348f7c0b9914e51ca7e12bab2d5b8003926b4",
"1a108c4384b6104e627652488659de0b1ac3330640fc3250f0a283af7c5884daab187c1efc024b2545262da1911d2b0b7b0d5e4e5b68bb98db25a760c9f1fb1a",
"b544196c3b7a55faacd11700d11e2fe4f16a7418282c9abb24a668544a15293580fd1a2cc5f93367c8a17c7ee45335c6d2f5c68a72dd176d516fd033f203eeec",
"3285e14d94bc736d6caddfe7ad7e3c6a6e69d49b079c989bb3e8aba4da62c022e38229d1e691aaa030b7d3bcd89e458d203f260806149a71ad9adb31606eae02",
"SKIP",
"9fcdcceab8bce43e888db79a38c775ff15790a806d3cc5cc96f396a829c6da2383b258481b5642153da14087943f6ef607af0aa3b75df6f41b95c6cd61d835eb",
"SKIP",
"1de6307c587686711f05d1e96731c43526fa3af51e4cd94c06c880954b67f6eb4c7db3177f0ea5937d41bc1f8cadcf5bce75025b5c1a46a469376960f1001c5f",
"SKIP",
"b1873dbb7a49460b007255689102062756972de5cc2d38b12cc9f389b6be412da6797579b1acd3717a8cd2ee118fd9801b94e55f063d4328f050f0876a5eb53c",
"b5887ea77417fae49b6cb1e9fa782d3021f268d5219701d87a092235964f73fa72a31428b630445517f56f2bb69dcbbb24119ef9dbf8b4e40a753369a9f9a16f",
"580677aad97093829090d4b605ac81c50327e74a6c2de0b85dd2e8525553f3ddde17556ea46f8f007f89e435493c9a20bc997d1ef1c1c2c23274528e3c46b94f",
"SKIP",
"e3216eca5fae2c9ce419e698bfbe186903088dad0a579749cb49bcde8f9d4073b98bf1570fe69190a9a41feb2a7c9814498ec9b867527de1c74ff75a1cbdfc17",
"083f5e675d73f3233c7930ebe20425a533feedeaaa9d8cc86831312a6581cefbe6ed0d08d2fa89be81082f2a5abdabca8b3c080bf97218a1bd59dc118a30b9f3",
"SKIP",
"21f9da445afd76acaf3acb22d216c2b584d95e8c68e00f5cb3f6673f2d556dd14a7593344adf8ffd194bba3314387ee0e486d6248f6c935abca2edd8a4cf95ed",
"SKIP",
"4489f615c6a0389577a7d1fd7d3917517bb2fe032abd9a6d87dfdbd165dabcf53f8780645934020bf27517b67a064297475888d5b368176cf06bc22f1e735e2b",
"SKIP",
"7c5c95c1b85bef2d4890c068a5a8ea8a1fe0d8def6ab09e5f34fc2746d8808bbb0fc168e3bd66d52ee5ed799dcf9f258f4125cda98c8384f6411bcad8d8b3139",
"SKIP",
"ad69101d1fceef6cd1dd6d5348f6f2be06912da6b6a7d0fece3ce08cf35054e6953b80ca9c4748554882892faa44e7c54e705cf25bbf2b796cd4ad12b09da185",
"SKIP",
"2524f71f4c2ebc254a1927279be3394e820d0a0c6dec7ef835a862aa08c35756edaa4208bcdc710dd092872b59c200b555b78670372e2830822e278ff1ec4e4a",
"SKIP",
],
"LDFLAGS": "$LDFLAGS -static",
"CC": "musl-gcc -fno-stack-protector",
"CXX": "musl-gcc -fno-stack-protector",
"CFLAGS": "${CFLAGS/-fstack-protector-strong/}",
"CXXFLAGS": "${CXXFLAGS/-fstack-protector-strong/}",
"PKGEXT": ".pkg.tar.xz",
}

View File

@ -0,0 +1,356 @@
# Maintainer: Eli Schwartz <eschwartz@archlinux.org>
# All my PKGBUILDs are managed at https://github.com/eli-schwartz/pkgbuilds
pkgname=pacman-static
pkgver=7.0.0.r6.gc685ae6
_cares_ver=1.34.4
_nghttp2_ver=1.64.0
_curlver=8.12.1
_sslver=3.4.1
_zlibver=1.3.1
_xzver=5.6.4
_bzipver=1.0.8
_zstdver=1.5.6
_libarchive_ver=3.7.7
_gpgerrorver=1.51
_libassuanver=3.0.0
_gpgmever=1.24.2
pkgrel=15
# use annotated tag and patch level commit from release branch (can be empty for no patches)
_git_tag=7.0.0
_git_patch_level_commit=c685ae6412af04cae1eaa5d6bda8c277c7ffb8c8
pkgdesc="Statically-compiled pacman (to fix or install systems without libc)"
arch=('i486' 'i686' 'pentium4' 'x86_64' 'arm' 'armv6h' 'armv7h' 'aarch64')
url="https://www.archlinux.org/pacman/"
license=('GPL-2.0-or-later')
depends=('pacman')
makedepends=('meson' 'musl' 'kernel-headers-musl' 'git')
options=('!emptydirs' '!lto')
# pacman
source=("git+https://gitlab.archlinux.org/pacman/pacman.git#tag=v${_git_tag}?signed"
pacman-revertme-makepkg-remove-libdepends-and-libprovides.patch::https://gitlab.archlinux.org/pacman/pacman/-/commit/354a300cd26bb1c7e6551473596be5ecced921de.patch)
validpgpkeys=('6645B0A8C7005E78DB1D7864F99FFE0FEAE999BD' # Allan McRae <allan@archlinux.org>
'B8151B117037781095514CA7BBDFFC92306B1121') # Andrew Gregory (pacman) <andrew@archlinux.org>
# nghttp2
source+=("https://github.com/nghttp2/nghttp2/releases/download/v$_nghttp2_ver/nghttp2-$_nghttp2_ver.tar.xz")
# c-ares
source+=("https://github.com/c-ares/c-ares/releases/download/v${_cares_ver}/c-ares-${_cares_ver}.tar.gz"{,.asc})
validpgpkeys+=('27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2' # Daniel Stenberg <daniel@haxx.se>
'DA7D64E4C82C6294CB73A20E22E3D13B5411B7CA') # Brad House <brad@brad-house.com>
# curl
source+=("https://curl.haxx.se/download/curl-${_curlver}.tar.gz"{,.asc})
validpgpkeys+=('27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2') # Daniel Stenberg
# openssl
source+=("https://github.com/openssl/openssl/releases/download/openssl-${_sslver}/openssl-${_sslver}.tar.gz"{,.asc}
"ca-dir.patch"
"openssl-3.0.7-no-atomic.patch")
validpgpkeys+=('8657ABB260F056B1E5190839D9C4D26D0E604491'
'7953AC1FBC3DC8B3B292393ED5E9E43F7DF9EE8C'
'A21FAB74B0088AA361152586B8EF1A6BA9DA2D5C'
'EFC0A467D613CB83C7ED6D30D894E2CE8B3D79F5'
'BA5473A2B0587B07FB27CF2D216094DFD0CB81EF')
validpgpkeys+=('8657ABB260F056B1E5190839D9C4D26D0E604491' # Matt Caswell <matt@openssl.org>
'7953AC1FBC3DC8B3B292393ED5E9E43F7DF9EE8C' # Matt Caswell <matt@openssl.org>
'A21FAB74B0088AA361152586B8EF1A6BA9DA2D5C' # Tomá? Mráz <tm@t8m.info>
'EFC0A467D613CB83C7ED6D30D894E2CE8B3D79F5') # OpenSSL security team key
# zlib
source+=("https://zlib.net/zlib-${_zlibver}.tar.gz"{,.asc})
validpgpkeys+=('5ED46A6721D365587791E2AA783FCD8E58BCAFBA') # Mark Adler <madler@alumni.caltech.edu>
# xz
source+=("git+https://github.com/tukaani-project/xz#tag=v${_xzver}")
validpgpkeys+=('3690C240CE51B4670D30AD1C38EE757D69184620') # Lasse Collin <lasse.collin@tukaani.org>
# bzip2
source+=("https://sourceware.org/pub/bzip2/bzip2-${_bzipver}.tar.gz"{,.sig})
validpgpkeys+=('EC3CFE88F6CA0788774F5C1D1AA44BE649DE760A') # Mark Wielaard <mark@klomp.org>
# zstd
source+=("https://github.com/facebook/zstd/releases/download/v${_zstdver}/zstd-${_zstdver}.tar.zst"{,.sig})
validpgpkeys+=('4EF4AC63455FC9F4545D9B7DEF8FE99528B52FFD') # Zstandard Release Signing Key <signing@zstd.net>
# libgpg-error
source+=("https://gnupg.org/ftp/gcrypt/libgpg-error/libgpg-error-${_gpgerrorver}.tar.bz2"{,.sig})
validpgpkeys+=('D8692123C4065DEA5E0F3AB5249B39D24F25E3B6' # Werner Koch
'031EC2536E580D8EA286A9F22071B08A33BD3F06' # NIIBE Yutaka (GnuPG Release Key) <gniibe@fsij.org>
'6DAA6E64A76D2840571B4902528897B826403ADA') # "Werner Koch (dist signing 2020)"
# libassuan
source+=("https://gnupg.org/ftp/gcrypt/libassuan/libassuan-${_libassuanver}.tar.bz2"{,.sig})
# gpgme
source+=("https://www.gnupg.org/ftp/gcrypt/gpgme/gpgme-${_gpgmever}.tar.bz2"{,.sig})
validpgpkeys+=('AC8E115BF73E2D8D47FA9908E98E9B2D19C6C8BD') # Niibe Yutaka (GnuPG Release Key)
# libarchive
source+=("https://github.com/libarchive/libarchive/releases/download/v${_libarchive_ver}/libarchive-${_libarchive_ver}.tar.xz"{,.asc})
validpgpkeys+=('A5A45B12AD92D964B89EEE2DEC560C81CEC2276E' # Martin Matuska <mm@FreeBSD.org>
'DB2C7CF1B4C265FAEF56E3FC5848A18B8F14184B') # Martin Matuska <martin@matuska.org>
sha512sums=('44e00c2bc259fe6a85de71f7fd8a43fcfd1b8fb7d920d2267bd5b347e02f1dab736b3d96e31faf7b535480398e2348f7c0b9914e51ca7e12bab2d5b8003926b4'
'1a108c4384b6104e627652488659de0b1ac3330640fc3250f0a283af7c5884daab187c1efc024b2545262da1911d2b0b7b0d5e4e5b68bb98db25a760c9f1fb1a'
'b544196c3b7a55faacd11700d11e2fe4f16a7418282c9abb24a668544a15293580fd1a2cc5f93367c8a17c7ee45335c6d2f5c68a72dd176d516fd033f203eeec'
'3285e14d94bc736d6caddfe7ad7e3c6a6e69d49b079c989bb3e8aba4da62c022e38229d1e691aaa030b7d3bcd89e458d203f260806149a71ad9adb31606eae02'
'SKIP'
'9fcdcceab8bce43e888db79a38c775ff15790a806d3cc5cc96f396a829c6da2383b258481b5642153da14087943f6ef607af0aa3b75df6f41b95c6cd61d835eb'
'SKIP'
'1de6307c587686711f05d1e96731c43526fa3af51e4cd94c06c880954b67f6eb4c7db3177f0ea5937d41bc1f8cadcf5bce75025b5c1a46a469376960f1001c5f'
'SKIP'
'b1873dbb7a49460b007255689102062756972de5cc2d38b12cc9f389b6be412da6797579b1acd3717a8cd2ee118fd9801b94e55f063d4328f050f0876a5eb53c'
'b5887ea77417fae49b6cb1e9fa782d3021f268d5219701d87a092235964f73fa72a31428b630445517f56f2bb69dcbbb24119ef9dbf8b4e40a753369a9f9a16f'
'580677aad97093829090d4b605ac81c50327e74a6c2de0b85dd2e8525553f3ddde17556ea46f8f007f89e435493c9a20bc997d1ef1c1c2c23274528e3c46b94f'
'SKIP'
'e3216eca5fae2c9ce419e698bfbe186903088dad0a579749cb49bcde8f9d4073b98bf1570fe69190a9a41feb2a7c9814498ec9b867527de1c74ff75a1cbdfc17'
'083f5e675d73f3233c7930ebe20425a533feedeaaa9d8cc86831312a6581cefbe6ed0d08d2fa89be81082f2a5abdabca8b3c080bf97218a1bd59dc118a30b9f3'
'SKIP'
'21f9da445afd76acaf3acb22d216c2b584d95e8c68e00f5cb3f6673f2d556dd14a7593344adf8ffd194bba3314387ee0e486d6248f6c935abca2edd8a4cf95ed'
'SKIP'
'4489f615c6a0389577a7d1fd7d3917517bb2fe032abd9a6d87dfdbd165dabcf53f8780645934020bf27517b67a064297475888d5b368176cf06bc22f1e735e2b'
'SKIP'
'7c5c95c1b85bef2d4890c068a5a8ea8a1fe0d8def6ab09e5f34fc2746d8808bbb0fc168e3bd66d52ee5ed799dcf9f258f4125cda98c8384f6411bcad8d8b3139'
'SKIP'
'ad69101d1fceef6cd1dd6d5348f6f2be06912da6b6a7d0fece3ce08cf35054e6953b80ca9c4748554882892faa44e7c54e705cf25bbf2b796cd4ad12b09da185'
'SKIP'
'2524f71f4c2ebc254a1927279be3394e820d0a0c6dec7ef835a862aa08c35756edaa4208bcdc710dd092872b59c200b555b78670372e2830822e278ff1ec4e4a'
'SKIP')
export LDFLAGS="$LDFLAGS -static"
export CC=musl-gcc
export CXX=musl-gcc
# https://www.openwall.com/lists/musl/2014/11/05/3
# fstack-protector and musl do not get along but only on i686
if [[ $CARCH = i686 || $CARCH = pentium4 || $CARCH = i486 ]]; then
# silly build systems have configure checks or buildtime programs that don't CFLAGS but do do CC
export CC="musl-gcc -fno-stack-protector"
export CXX="musl-gcc -fno-stack-protector"
export CFLAGS="${CFLAGS/-fstack-protector-strong/}"
export CXXFLAGS="${CXXFLAGS/-fstack-protector-strong/}"
fi
# to enable func64 interface in musl for 64-bit file system functions
export CFLAGS+=' -D_LARGEFILE64_SOURCE'
export CXXFLAGS+=' -D_LARGEFILE64_SOURCE'
# keep using xz-compressed packages, because one use of the package is to
# recover on systems with broken zstd support in libarchive
[[ $PKGEXT = .pkg.tar.zst ]] && PKGEXT=.pkg.tar.xz
prepare() {
cd "${srcdir}/pacman"
# apply patch level commits on top of annotated tag for pacman
if [[ -n ${_git_patch_level_commit} ]]; then
if [[ v${_git_tag} != $(git describe --tags --abbrev=0 "${_git_patch_level_commit}") ]] then
error "patch level commit ${_git_patch_level_commit} is not a descendant of v${_git_tag}"
exit 1
fi
git rebase "${_git_patch_level_commit}"
fi
# handle local pacman patches
local -a patches
patches=($(printf '%s\n' "${source[@]}" | grep 'pacman-.*.patch'))
patches=("${patches[@]%%::*}")
patches=("${patches[@]##*/}")
if (( ${#patches[@]} != 0 )); then
for patch in "${patches[@]}"; do
if [[ $patch =~ revertme-* ]]; then
msg2 "Reverting patch $patch..."
patch -RNp1 < "../$patch"
else
msg2 "Applying patch $patch..."
patch -Np1 < "../$patch"
fi
done
fi
# openssl
cd "${srcdir}"/openssl-${_sslver}
patch -Np1 -i "${srcdir}/ca-dir.patch"
case ${CARCH} in
arm|armv6h|armv7h)
# special patch to omit -latomic when installing pkgconfig files
msg2 "Applying openssl patch openssl-3.0.7-no-atomic.patch..."
patch -Np1 -i "${srcdir}/openssl-3.0.7-no-atomic.patch"
esac
}
build() {
export PKG_CONFIG_PATH="${srcdir}"/temp/usr/lib/pkgconfig
export PATH="${srcdir}/temp/usr/bin:${PATH}"
# openssl
cd "${srcdir}"/openssl-${_sslver}
case ${CARCH} in
x86_64)
openssltarget='linux-x86_64'
optflags='enable-ec_nistp_64_gcc_128'
;;
pentium4)
openssltarget='linux-elf'
optflags=''
;;
i686)
openssltarget='linux-elf'
optflags='no-sse2'
;;
i486)
openssltarget='linux-elf'
optflags='386 no-threads'
;;
arm|armv6h|armv7h)
openssltarget='linux-armv4'
optflags=''
;;
aarch64)
openssltarget='linux-aarch64'
optflags='no-afalgeng'
;;
esac
# mark stack as non-executable: http://bugs.archlinux.org/task/12434
./Configure --prefix="${srcdir}"/temp/usr \
--openssldir=/etc/ssl \
--libdir=lib \
-static \
no-ssl3-method \
${optflags} \
"${openssltarget}" \
"-Wa,--noexecstack ${CPPFLAGS} ${CFLAGS} ${LDFLAGS}"
make build_libs
make install_dev
# xz
cd "${srcdir}"/xz
./autogen.sh --no-po4a --no-doxygen
./configure --prefix="${srcdir}"/temp/usr \
--disable-shared
cd src/liblzma
make
make install
# bzip2
cd "${srcdir}"/bzip2-${_bzipver}
sed -i "s|-O2|${CFLAGS}|g;s|CC=gcc|CC=${CC}|g" Makefile
make libbz2.a
install -Dvm644 bzlib.h "${srcdir}"/temp/usr/include/
install -Dvm644 libbz2.a "${srcdir}"/temp/usr/lib/
cd "${srcdir}"/zstd-${_zstdver}/lib
make libzstd.a
make PREFIX="${srcdir}"/temp/usr install-pc install-static install-includes
# zlib
cd "${srcdir}/"zlib-${_zlibver}
./configure --prefix="${srcdir}"/temp/usr \
--static
make libz.a
make install
# libarchive
cd "${srcdir}"/libarchive-${_libarchive_ver}
CPPFLAGS="-I${srcdir}/temp/usr/include" CFLAGS="-L${srcdir}/temp/usr/lib" \
./configure --prefix="${srcdir}"/temp/usr \
--without-xml2 \
--without-nettle \
--disable-{bsdtar,bsdcat,bsdcpio,bsdunzip} \
--without-expat \
--disable-shared
make
make install-{includeHEADERS,libLTLIBRARIES,pkgconfigDATA,includeHEADERS}
# nghttp2
cd "${srcdir}"/nghttp2-${_nghttp2_ver}
./configure --prefix="${srcdir}"/temp/usr \
--disable-shared \
--disable-examples \
--disable-python-bindings
make -C lib
make -C lib install
# c-ares
# needed for curl, which does not use it in the repos
# but seems to be needed for static builds
cd "${srcdir}"/c-ares-${_cares_ver}
./configure --prefix="${srcdir}"/temp/usr \
--disable-shared
make -C src/lib
make install-pkgconfigDATA
make -C src/lib install
make -C include install
# curl
cd "${srcdir}"/curl-${_curlver}
# c-ares is not detected via pkg-config :(
./configure --prefix="${srcdir}"/temp/usr \
--disable-shared \
--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt \
--disable-{dict,gopher,imap,ldap,ldaps,manual,pop3,rtsp,smb,smtp,telnet,tftp} \
--without-{brotli,libidn2,librtmp,libssh2,libpsl} \
--disable-libcurl-option \
--with-openssl \
--enable-ares="${srcdir}"/temp/usr
make -C lib
make install-pkgconfigDATA
make -C lib install
make -C include install
# libgpg-error
cd "${srcdir}"/libgpg-error-${_gpgerrorver}
./configure --prefix="${srcdir}"/temp/usr \
--disable-shared
make -C src
make -C src install-{binSCRIPTS,libLTLIBRARIES,nodist_includeHEADERS,pkgconfigDATA}
# libassuan
cd "${srcdir}"/libassuan-${_libassuanver}
./configure --prefix="${srcdir}"/temp/usr \
--disable-shared
make -C src
make -C src install-{binSCRIPTS,libLTLIBRARIES,nodist_includeHEADERS,pkgconfigDATA}
# gpgme
cd "${srcdir}"/gpgme-${_gpgmever}
./configure --prefix="${srcdir}"/temp/usr \
--disable-fd-passing \
--disable-shared \
--disable-languages
make -C src
make -C src install-{binSCRIPTS,libLTLIBRARIES,nodist_includeHEADERS,pkgconfigDATA}
# ew libtool
rm "${srcdir}"/temp/usr/lib/lib*.la
# Finally, it's a pacman!
mkdir -p "${srcdir}"/pacman
cd "${srcdir}"/pacman
meson --prefix=/usr \
--includedir=lib/pacman/include \
--libdir=lib/pacman/lib \
--buildtype=plain \
-Dbuildstatic=true \
-Ddefault_library=static \
-Ddoc=disabled \
-Ddoxygen=disabled \
-Dldconfig=/usr/bin/ldconfig \
-Dscriptlet-shell=/usr/bin/bash \
build
meson compile -C build
}
package() {
cd "${srcdir}"/pacman
DESTDIR="${pkgdir}" meson install -C build
rm -rf "${pkgdir}"/usr/share "${pkgdir}"/etc
for exe in "${pkgdir}"/usr/bin/*; do
if [[ -f ${exe} && $(head -c4 "${exe}") = $'\x7fELF' ]]; then
mv "${exe}" "${exe}"-static
else
rm "${exe}"
fi
done
cp -a "${srcdir}"/temp/usr/{bin,include,lib} "${pkgdir}"/usr/lib/pacman/
sed -i "s@${srcdir}/temp/usr@/usr/lib/pacman@g" \
"${pkgdir}"/usr/lib/pacman/lib/pkgconfig/*.pc \
"${pkgdir}"/usr/lib/pacman/bin/*
}