Compare commits

..

No commits in common. "master" and "fix-caching" have entirely different histories.

48 changed files with 116 additions and 326 deletions

2
3rdparty/lmdb-safe vendored

@ -1 +1 @@
Subproject commit 71b613bc7c43d37b444e5531241c355e23de925c
Subproject commit d8ca70bca18b8a31e88b31f981a0e9fb120ac8a6

View File

@ -9,7 +9,7 @@ set(META_APP_DESCRIPTION "Repository manager and package builder for Arch Linux"
set(META_APP_CATEGORIES "System;Utility;Network;FileTransfer")
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 4)
set(META_VERSION_PATCH 3)
set(META_VERSION_EXACT_SONAME ON)
#set(META_CXX_STANDARD 20)
set(META_CXX_STANDARD "any")

View File

@ -43,16 +43,6 @@ Further ideas (not implemented yet):
* bundle an application with its dependencies similar to `linuxdeployqt`
and `windeployqt`
## Important caveats
* So far releases are not regularly tagged. It is probably best to build the latest version
from Git as needed.
* So far also no security review of the code has been done and at the same time the code
base contains lots of rather low-level code (instead of relying on frameworks), many
self-written parsers and exposes a web service. So use it at your own risk (like most
other open source projects, but here I'd like to highlight it specifically).
* 32-bit architectures are not supported (as the database needs a huge address space). Of
course you can build packages *for* 32-bit architectures.
## Setup server
An example config files can be found in this repository, see the `srv/doc`
directory. The example config is also installed so one can easily get started.
@ -118,7 +108,7 @@ local_mirror = file://$local_db_path
```
The server obviously needs write permissions to add packages to repositories. In my
setup I just added it as group and set permissions accordingly:
setup I've just add it as group and set permissions accordingly:
```
sudo chown -R martchus:buildservice-git $local_db_path
@ -212,7 +202,7 @@ location /buildservice/api/ {
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ ^(?!/buildservice/api/)/buildservice/(.*)$ {
alias /usr/share/buildservice-git/web/static/$1;
alias /run/media/devel/projects/c++/cmake/auto-makepkg/buildservice/static/$1;
}
```
@ -232,14 +222,14 @@ keyserver hkp://keys.gnupg.net
#### Notes
* Adding the pacman keyring is actually not very useful because we need to check signatures
of any upstream project and not just arch devs.
* If "auto-key-retrieve" does not work, use `gpg --recv-key <KEYID>` as a workaround.
* If "auto-key-retrieve" does not work, use `gpg --recv-key <KEYID>` as a workaround
* Also see http://allanmcrae.com/2015/01/two-pgp-keyrings-for-package-management-in-arch-linux/
### ccache configuration
To use ccache one can set the ccache directory in the config (`ccache_dir`) and install the
package `ccache` (and `mingw-w64-ccache` for MinGW packages) into the chroot. Make sure the user
you start the server with has permissions to read and write there. Otherwise the resulting
configuration errors can be confusing. Internally the server is mounting that directory as
configure errors can be confusing. Internally the server is mounting that directory like
described in [the wiki](https://wiki.archlinux.org/index.php/Ccache#makechrootpkg).
If you want to use the existing `ccache` directory owned by your current user, you could do
@ -396,14 +386,13 @@ and CSS files are bundled. For development the full checkout might be useful
```
cd srv/static/node_modules
npm install @xterm/xterm @xterm/addon-search
npm install xterm
npm install --save xterm-addon-search
```
## Copyright notice and license
Copyright © 2017-2024 Marius Kittler
Copyright © 2017-2023 Marius Kittler
All code is licensed under [GPL-2-or-later](LICENSE). This does *not* apply to
code contained in Git repositories included as Git submodule (which contain
their own README and licensing information). This does also *not* apply to
code in the directories under `srv/static/node_modules` (which contain 3rdparty
code that has its own licensing information).
their own README and licensing information).

View File

@ -3,11 +3,11 @@ url = http://127.0.0.1:8090
user = martchus
[instance/server]
url = https://martchus.dyn.f3l.de/buildservice
url = https://martchus.no-ip.biz/buildservice
user = martchus
[instance/bogus]
url = http://martchus.dyn.f3l.de/foo
url = http://martchus.no-ip.biz/foo
user = martchus
[user/martchus]

View File

@ -42,6 +42,9 @@ set(META_PROJECT_TYPE library)
set(META_PROJECT_VARNAME LIBPKG)
set(META_APP_NAME "Unofficial Arch Linux package library")
set(META_APP_DESCRIPTION "C++ library to parse Arch Linux packages and databases")
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 1)
# find c++utilities
set(CONFIGURATION_PACKAGE_SUFFIX

View File

@ -321,7 +321,7 @@ void Database::allPackages(const PackageVisitorMove &visitor)
}
}
void Database::allPackagesBase(const PackageVisitorBase &visitor)
void Database::allPackages(const PackageVisitorBase &visitor)
{
auto txn = m_storage->packages.getROTransaction();
for (auto i = txn.begin<std::shared_ptr, PackageBase>(); i != txn.end(); ++i) {
@ -493,9 +493,6 @@ StorageID Database::findBasePackageWithID(const std::string &packageName, Packag
void Database::removePackage(const std::string &packageName)
{
if (packageName.empty()) {
return;
}
const auto lock = std::unique_lock(m_storage->updateMutex);
auto txn = m_storage->packages.getRWTransaction();
const auto [packageID, package] = m_storage->packageCache.retrieve(*m_storage, &txn, packageName);
@ -894,34 +891,6 @@ PackageSpec LibPkg::PackageUpdater::findPackageWithID(const std::string &package
return m_database.m_storage->packageCache.retrieve(*m_database.m_storage, &m_d->packagesTxn, packageName);
}
/*!
* \brief Begins updating the existing \a package with the specified \a packageID.
* \remarks
* - Do not use this function when PackageUpdate has been constructed with clear=true.
* - Call this method before modifying \a package. Then modify the package. Then call endUpdate().
*/
void PackageUpdater::beginUpdate(StorageID packageID, const std::shared_ptr<Package> &package)
{
m_d->update(packageID, true, package);
}
/*!
* \brief Ends updating the existing \a package with the specified \a packageID.
* \remarks
* - Do not use this function when PackageUpdate has been constructed with clear=true.
* - Call this method after callsing beginUpdate() and modifying \a package.
*/
void PackageUpdater::endUpdate(StorageID packageID, const std::shared_ptr<Package> &package)
{
const auto &storage = m_database.m_storage;
storage->packageCache.store(*m_database.m_storage, m_d->packagesTxn, packageID, package);
m_d->update(packageID, false, package);
}
/*!
* \brief Updates the specified \a package. The \a package may or may not exist.
* \remarks If the package exists then provides/deps from the existing package are taken over if appropriate.
*/
StorageID PackageUpdater::update(const std::shared_ptr<Package> &package)
{
const auto &storage = m_database.m_storage;

View File

@ -120,8 +120,6 @@ struct LIBPKG_EXPORT PackageUpdater {
~PackageUpdater();
PackageSpec findPackageWithID(const std::string &packageName);
void beginUpdate(StorageID packageID, const std::shared_ptr<Package> &package);
void endUpdate(StorageID packageID, const std::shared_ptr<Package> &package);
StorageID update(const std::shared_ptr<Package> &package);
bool insertFromDatabaseFile(const std::string &databaseFilePath);
void commit();
@ -173,7 +171,7 @@ struct LIBPKG_EXPORT Database : public ReflectiveRapidJSON::JsonSerializable<Dat
static bool isFileRelevant(const char *filePath, const char *fileName, mode_t);
std::vector<std::shared_ptr<Package>> findPackages(const std::function<bool(const Database &, const Package &)> &pred);
void allPackages(const PackageVisitorMove &visitor);
void allPackagesBase(const PackageVisitorBase &visitor);
void allPackages(const PackageVisitorBase &visitor);
void allPackagesByName(const PackageVisitorByName &visitor);
void allPackagesByName(const PackageVisitorByNameBase &visitor);
std::size_t packageCount() const;

View File

@ -367,26 +367,17 @@ void PackageBase::clear()
description.clear();
}
/*!
* \brief Returns whether addDepsAndProvidesFromOtherPackage() would take over the information (without force).
*/
bool Package::canDepsAndProvidesFromOtherPackage(const Package &otherPackage) const
{
return !((otherPackage.origin != PackageOrigin::PackageContents && otherPackage.origin != PackageOrigin::CustomSource)
|| version != otherPackage.version
|| !(!packageInfo || buildDate.isNull() || (otherPackage.packageInfo && buildDate == otherPackage.buildDate)));
}
/*!
* \brief Takes over deps/provides from \a otherPackage if appropriate.
* \remarks
* The information is not taken over if \a otherPackage does not match the current instance (only
* version and build date are considered) or if otherPackage has no info from package contents. Use
* the \a force parameter to avoid these checks.
*/
bool Package::addDepsAndProvidesFromOtherPackage(const Package &otherPackage, bool force)
{
if (&otherPackage == this || (!force && !canDepsAndProvidesFromOtherPackage(otherPackage))) {
if (&otherPackage == this) {
return false;
}
// skip if otherPackage does not match the current instance (only version and build date are considered) or if otherPackage has no info from package contents
if (!force
&& ((otherPackage.origin != PackageOrigin::PackageContents && otherPackage.origin != PackageOrigin::CustomSource)
|| version != otherPackage.version
|| !(!packageInfo || buildDate.isNull() || (otherPackage.packageInfo && buildDate == otherPackage.buildDate)))) {
return false;
}

View File

@ -415,7 +415,6 @@ struct LIBPKG_EXPORT Package : public PackageBase,
std::string_view directoryPath, const ArchiveFile &file, std::set<std::string> &dllsReferencedByImportLibs);
void addDepsAndProvidesFromContents(const FileMap &contents);
std::vector<std::string> processDllsReferencedByImportLibs(std::set<std::string> &&dllsReferencedByImportLibs);
bool canDepsAndProvidesFromOtherPackage(const Package &otherPackage) const;
bool addDepsAndProvidesFromOtherPackage(const Package &otherPackage, bool force = false);
bool isArchAny() const;
std::vector<std::string> validate() const;

View File

@ -1,3 +1,6 @@
// ignore warning about "return result_type{ storageEntry.id, storageEntry.ref.relatedStorage };"
#pragma GCC diagnostic ignored "-Wnull-dereference"
#include "./storageprivate.h"
#include <c++utilities/conversion/stringbuilder.h>
@ -126,11 +129,6 @@ auto StorageCache<StorageEntriesType, StorageType, SpecType>::retrieve(Storage &
return retrieve(storage, nullptr, entryName);
}
/*!
* \brief Stores the specified \a entry.
* \remarks The entry may exist or may not exist. A lookup for an existing entry is done to take over
* deps/provides from the existing entry if it makes sense.
*/
template <typename StorageEntriesType, typename StorageType, typename SpecType>
auto StorageCache<StorageEntriesType, StorageType, SpecType>::store(Storage &storage, RWTxn &txn, const std::shared_ptr<Entry> &entry) -> StoreResult
{
@ -151,7 +149,6 @@ auto StorageCache<StorageEntriesType, StorageType, SpecType>::store(Storage &sto
entry->addDepsAndProvidesFromOtherPackage(*res.oldEntry);
}
lock.unlock();
// check for package in storage
if (!res.oldEntry) {
res.oldEntry = std::make_shared<Entry>();
@ -161,10 +158,8 @@ auto StorageCache<StorageEntriesType, StorageType, SpecType>::store(Storage &sto
res.oldEntry.reset();
}
}
// update package in storage
res.id = txn.put(*entry, res.id);
// update cache entry
lock = std::unique_lock(m_mutex);
if (cacheEntry) {
@ -174,31 +169,10 @@ auto StorageCache<StorageEntriesType, StorageType, SpecType>::store(Storage &sto
}
cacheEntry->entry = entry;
lock.unlock();
res.updated = true;
return res;
}
/*!
* \brief Stores the specified \a entry with the specified \a storageID.
* \remarks This is used to update an existing entry with a known ID.
*/
template <typename StorageEntriesType, typename StorageType, typename SpecType>
auto StorageCache<StorageEntriesType, StorageType, SpecType>::store(
Storage &storage, RWTxn &txn, StorageID storageID, const std::shared_ptr<Entry> &entry) -> void
{
// update package in storage
const auto id = txn.put(*entry, storageID);
// update cache entry
using CacheEntry = typename Entries::StorageEntry;
using CacheRef = typename Entries::Ref;
const auto ref = CacheRef(storage, entry);
const auto lock = std::unique_lock(m_mutex);
const auto cacheEntry = &m_entries.insert(CacheEntry(ref, id));
cacheEntry->entry = entry;
}
template <typename StorageEntriesType, typename StorageType, typename SpecType>
bool StorageCache<StorageEntriesType, StorageType, SpecType>::invalidate(Storage &storage, const std::string &entryName)
{

View File

@ -152,7 +152,6 @@ template <typename StorageEntriesType, typename StorageType, typename SpecType>
SpecType retrieve(Storage &storage, RWTxn *, const std::string &entryName);
SpecType retrieve(Storage &storage, const std::string &entryName);
StoreResult store(Storage &storage, RWTxn &txn, const std::shared_ptr<Entry> &entry);
void store(Storage &storage, RWTxn &txn, StorageID storageID, const std::shared_ptr<Entry> &entry);
bool invalidate(Storage &storage, const std::string &entryName);
bool invalidateCacheOnly(Storage &storage, const std::string &entryName);
void clear(Storage &storage);

View File

@ -4,7 +4,6 @@
#ifndef LIBPKG_GLOBAL
#define LIBPKG_GLOBAL
#include "libpkg-definitions.h"
#include <c++utilities/application/global.h>
#ifdef LIBPKG_STATIC

View File

@ -1041,9 +1041,8 @@ bool PackageNameData::isVcsPackage() const
PackageNameData PackageNameData::decompose(std::string_view packageName)
{
static constexpr auto targetPrefixBrackets = 2;
static const auto packageNameRegex
= std::regex("((lib32|mingw-w64(-(ucrt|clang-x86_64|clang-aarch64))?|android-aarch64|android-x86-64|android-x86|android-armv7a-eabi|arm-none-eabi|aarch64-linux-"
= std::regex("((lib32|mingw-w64|android-aarch64|android-x86-64|android-x86|android-armv7a-eabi|arm-none-eabi|aarch64-linux-"
"gnu|static-compat|riscv64-linux|avr|psp)-)?(.*?)((-(cvs|svn|hg|darcs|bzr|git|custom|compat|static|qt\\d+|doc|cli|gui))*)");
auto data = PackageNameData{};
auto match = std::cmatch{};
@ -1055,8 +1054,8 @@ PackageNameData PackageNameData::decompose(std::string_view packageName)
regexMatch.first + offset, static_cast<std::size_t>(regexMatch.length() - static_cast<std::cmatch::difference_type>(offset)));
};
data.targetPrefix = matchToStringView(match[2]);
data.actualName = matchToStringView(match[3 + targetPrefixBrackets]);
data.vcsSuffix = match[4 + targetPrefixBrackets].length() ? matchToStringView(match[4 + targetPrefixBrackets], 1) : std::string_view{};
data.actualName = matchToStringView(match[3]);
data.vcsSuffix = match[4].length() ? matchToStringView(match[4], 1) : std::string_view{};
return data;
}

View File

@ -74,7 +74,7 @@ void walkThroughArchiveInternal(struct archive *ar, const string &archiveName, c
}
if (directoryHandler(std::string_view(filePath, dirEnd))) {
goto free;
return;
}
continue;
}
@ -101,7 +101,7 @@ void walkThroughArchiveInternal(struct archive *ar, const string &archiveName, c
if (entryType == AE_IFLNK) {
if (fileHandler(std::string_view(filePath, static_cast<string::size_type>(dirEnd - filePath)),
ArchiveFile(fileName, std::string(archive_entry_symlink_utf8(entry)), ArchiveFileType::Link, creationTime, modificationTime))) {
goto free;
return;
}
continue;
}
@ -128,12 +128,11 @@ void walkThroughArchiveInternal(struct archive *ar, const string &archiveName, c
// move it to results
if (fileHandler(std::string_view(filePath, static_cast<string::size_type>(dirEnd - filePath)),
ArchiveFile(fileName, std::move(fileContent), ArchiveFileType::Regular, creationTime, modificationTime))) {
goto free;
return;
}
}
// free resources used by libarchive
free:
archive_entry_free(entry);
int returnCode = archive_read_free(ar);
if (returnCode != ARCHIVE_OK) {

View File

@ -1,8 +1,5 @@
#include "../data/config.h"
#include "resources/config.h"
#include <c++utilities/application/commandlineutils.h>
#include <c++utilities/conversion/stringbuilder.h>
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/io/misc.h>
@ -40,7 +37,6 @@ class DataTests : public TestFixture {
CPPUNIT_TEST(testAddingDepsAndProvidesFromOtherPackage);
CPPUNIT_TEST(testDependencyExport);
CPPUNIT_TEST(testPackageUpdater);
CPPUNIT_TEST(stresstestPackageUpdater);
CPPUNIT_TEST(testMisc);
CPPUNIT_TEST_SUITE_END();
@ -65,7 +61,6 @@ public:
void testAddingDepsAndProvidesFromOtherPackage();
void testDependencyExport();
void testPackageUpdater();
void stresstestPackageUpdater();
void testMisc();
private:
@ -233,10 +228,7 @@ void DataTests::testPackageSearch()
{
setupPackages();
auto pkgs = m_config.findPackages(std::string_view());
CPPUNIT_ASSERT_EQUAL_MESSAGE("no results for package with empty name", 0_st, pkgs.size());
pkgs = m_config.findPackages("foo"sv);
auto pkgs = m_config.findPackages("foo"sv);
CPPUNIT_ASSERT_EQUAL(2_st, pkgs.size());
CPPUNIT_ASSERT_EQUAL_MESSAGE("package from first db returned first, cached object returned", m_pkg1, pkgs.front().pkg);
CPPUNIT_ASSERT_EQUAL_MESSAGE("package from first db returned second, cached object returned", m_pkg3, pkgs.back().pkg);
@ -572,68 +564,6 @@ void DataTests::testPackageUpdater()
CPPUNIT_ASSERT_EQUAL("zlib"s, newPkg->name);
}
void DataTests::stresstestPackageUpdater()
{
if (!CppUtilities::isEnvVariableSet(PROJECT_VARNAME_UPPER "_ENABLE_STRESS_TESTS").value_or(false)) {
return;
}
m_dbFile = workingCopyPath("stresstest-data.db", WorkingCopyMode::Cleanup);
m_config.initStorage(m_dbFile.data());
auto dbFile = testFilePath("stresstest/ownstuff-protected.files.tar.xz");
auto dbFileOld = testFilePath("stresstest/ownstuff-protected.files.tar.xz.old");
auto *const db = m_config.findOrCreateDatabase("ownstuff-protected"sv, "x86_64"sv);
auto basePackageFromIndex = PackageBase();
static constexpr auto iterations = 1000;
static const auto packageToModify = "android-sdk"s;
static const auto newLibdepends = std::set<std::string>{"foo", "bar", "baz"};
static const auto newLibprovides = std::set<std::string>{"a", "b", "c"};
for (auto i = 0; i != iterations; ++i) {
if (i % (iterations / 100) == 0) {
std::cerr << "\rRunning stress test: " << (i * 100 / iterations) << " %";
}
auto updater = LibPkg::PackageUpdater(*db, true);
updater.insertFromDatabaseFile(i % 2 == 0 ? dbFileOld : dbFile);
updater.commit();
CPPUNIT_ASSERT_MESSAGE("packages present", db->packageCount() > 0);
auto updater2 = LibPkg::PackageUpdater(*db);
auto [existingPackageID, existingPackage] = updater2.findPackageWithID(packageToModify);
CPPUNIT_ASSERT(existingPackage != nullptr);
CPPUNIT_ASSERT_EQUAL(packageToModify, existingPackage->name);
CPPUNIT_ASSERT(existingPackage->libdepends.empty());
CPPUNIT_ASSERT(existingPackage->libprovides.empty());
updater2.beginUpdate(existingPackageID, existingPackage);
existingPackage->libdepends = newLibdepends;
existingPackage->libprovides = newLibprovides;
updater2.endUpdate(existingPackageID, existingPackage);
updater2.commit();
db->allPackagesBase([db, &basePackageFromIndex](StorageID packageID, std::shared_ptr<PackageBase> &&basePackage) {
CPPUNIT_ASSERT_MESSAGE("package ID valid", packageID);
CPPUNIT_ASSERT_MESSAGE("base package returned", basePackage);
CPPUNIT_ASSERT_MESSAGE("name assigned", !basePackage->name.empty());
CPPUNIT_ASSERT_MESSAGE("version assigned", !basePackage->version.empty());
basePackageFromIndex.clear();
const auto packageIDViaIndex = db->findBasePackageWithID(basePackage->name, basePackageFromIndex);
CPPUNIT_ASSERT_MESSAGE("package ID via index valid", packageIDViaIndex);
CPPUNIT_ASSERT_EQUAL_MESSAGE("index valid, ID matches", packageID, packageIDViaIndex);
CPPUNIT_ASSERT_EQUAL_MESSAGE("index valid, name matches", basePackage->name, basePackageFromIndex.name);
return false;
});
auto updatedPackage = db->findPackage(packageToModify);
CPPUNIT_ASSERT(updatedPackage != nullptr);
CPPUNIT_ASSERT_EQUAL(packageToModify, updatedPackage->name);
CPPUNIT_ASSERT_EQUAL(newLibdepends, updatedPackage->libdepends);
CPPUNIT_ASSERT_EQUAL(newLibprovides, updatedPackage->libprovides);
}
std::cerr << "Running stress test: done\n";
}
void DataTests::testMisc()
{
CPPUNIT_ASSERT_EQUAL("123.4"s, PackageVersion::trimPackageVersion("123.4"s));

View File

@ -128,10 +128,6 @@ void ParserTests::testParsingPackageName()
CPPUNIT_ASSERT_EQUAL("gcc"sv, mingwGCC.actualName);
CPPUNIT_ASSERT_EQUAL("mingw-w64"sv, mingwGCC.targetPrefix);
CPPUNIT_ASSERT_EQUAL(""sv, mingwGCC.vcsSuffix);
const auto mingwUcrtCppWinrt = PackageNameData::decompose("mingw-w64-ucrt-cppwinrt");
CPPUNIT_ASSERT_EQUAL("cppwinrt"sv, mingwUcrtCppWinrt.actualName);
CPPUNIT_ASSERT_EQUAL("mingw-w64-ucrt"sv, mingwUcrtCppWinrt.targetPrefix);
CPPUNIT_ASSERT_EQUAL(""sv, mingwUcrtCppWinrt.vcsSuffix);
const auto staticCompatQt6 = PackageNameData::decompose("static-compat-qt6-base");
CPPUNIT_ASSERT_EQUAL("qt6-base"sv, staticCompatQt6.actualName);
CPPUNIT_ASSERT_EQUAL("static-compat"sv, staticCompatQt6.targetPrefix);

View File

@ -70,6 +70,9 @@ set(META_PROJECT_VARNAME LIBREPOMGR)
set(META_APP_AUTHOR "Martchus")
set(META_APP_NAME "Unofficial Arch Linux repository management library")
set(META_APP_DESCRIPTION "Library for managing custom Arch Linux repositories")
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 1)
set(META_VERSION_CACHE 11)
set(LINK_TESTS_AGAINST_APP_TARGET ON)
@ -165,14 +168,12 @@ add_reflection_generator_invocation(
ERROR_RESILIENT)
# disable Boost's support for concepts to avoid compile errors
# ~~~
# /usr/include/boost/asio/async_result.hpp:70:20: error: concept cannot have associated constraints
# BOOST_ASIO_CONCEPT completion_handler_for =
# ^
# /usr/include/boost/asio/async_result.hpp:492:20: error: concept cannot have associated constraints
# BOOST_ASIO_CONCEPT completion_token_for = requires(T&& t)
# ^
# ~~~
list(APPEND META_PUBLIC_COMPILE_DEFINITIONS BOOST_ASIO_DISABLE_CONCEPTS)
# include modules to apply configuration

View File

@ -7,7 +7,6 @@
#include <c++utilities/io/ansiescapecodes.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
namespace LibRepoMgr {
@ -40,19 +39,16 @@ template <> inline void convertValue(const std::multimap<std::string, std::strin
}
}
static constexpr char toUpper(const char c)
{
return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;
}
void ServiceSetup::Authentication::applyConfig(const std::string &userName, const std::multimap<std::string, std::string> &multimap)
{
auto &user = users[userName];
convertValue(multimap, "password_sha512", user.passwordSha512);
convertValue(multimap, "permissions", user.permissions);
for (auto &c : user.passwordSha512) {
c = toUpper(c);
}
}
static constexpr char toLower(const char c)
{
return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
}
UserAuth ServiceSetup::Authentication::authenticate(std::string_view authorizationHeader) const
@ -87,26 +83,22 @@ UserAuth ServiceSetup::Authentication::authenticate(std::string_view authorizati
if (user == users.cend()) {
return auth;
}
constexpr auto sha512HexSize = SHA512_DIGEST_LENGTH * 2;
constexpr auto sha512HexSize = 128;
if (user->second.passwordSha512.size() != sha512HexSize) {
return auth;
}
// hash password
auto hash = std::array<unsigned char, SHA512_DIGEST_LENGTH>();
SHA512(reinterpret_cast<const unsigned char *>(password.data()), password.size(), hash.data());
// convert hash to string (hexadecimal)
auto hashHex = std::array<char, sha512HexSize>();
auto hashHexIter = hashHex.begin();
for (const auto hashNumber : hash) {
*(hashHexIter++) = static_cast<char>(CppUtilities::digitToChar((hashNumber / 16) % 16));
*(hashHexIter++) = static_cast<char>(CppUtilities::digitToChar(hashNumber % 16));
}
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512(reinterpret_cast<const unsigned char *>(password.data()), password.size(), hash);
// check whether password hash matches
if (CRYPTO_memcmp(user->second.passwordSha512.data(), hashHex.data(), sha512HexSize) != 0) {
return auth;
auto i = user->second.passwordSha512.cbegin();
for (unsigned char hashNumber : hash) {
const auto digits = CppUtilities::numberToString(hashNumber, 16);
if ((toLower(*(i++)) != toLower(digits.size() < 2 ? '0' : digits.front())) || (toLower(*(i++)) != toLower(digits.back()))) {
return auth;
}
}
// return the user's permissions

View File

@ -192,17 +192,11 @@ BuildActionMetaInfo::BuildActionMetaInfo()
.param = "fetch-official-pkgbuilds",
},
BuildActionFlagMetaInfo{
.id = static_cast<BuildActionFlagType>(PrepareBuildFlags::UseContainer),
.id = static_cast<BuildActionFlagType>(ConductBuildFlags::UseContainer),
.name = "Use container",
.desc = "Uses `makecontainerpkg` instead of using `makepkg` when printing source info; eliminates the need to having pacman on the host by using docker/podman instead",
.param = "use-container",
},
BuildActionFlagMetaInfo{
.id = static_cast<BuildActionFlagType>(PrepareBuildFlags::AurOnly),
.name = "AUR-only",
.desc = "Consider it an error when a PKGBUILD was found locally and thus was not downloaded from the AUR",
.param = "aur-only",
},
},
.settings = {
BuildActionSettingMetaInfo{

View File

@ -88,7 +88,6 @@ enum class PrepareBuildFlags : BuildActionFlagType {
PullingInFurtherDependenciesUnexpected = (1 << 5),
FetchOfficialPackageSources = (1 << 6),
UseContainer = (1 << 7),
AurOnly = (1 << 8),
};
enum class ConductBuildFlags : BuildActionFlagType {
None,

View File

@ -519,8 +519,7 @@ private:
bool m_pullingInFurtherDependenciesUnexpected = false;
bool m_pulledInFurtherDependencies = false;
bool m_fetchOfficialSources = false;
bool m_useContainer = false;
bool m_aurOnly = false;
bool m_useContainer;
};
struct LIBREPOMGR_EXPORT BatchProcessingSession : public MultiSession<std::string> {

View File

@ -1142,7 +1142,7 @@ void ConductBuild::invokeMakechrootpkgStep3(std::shared_ptr<BuildProcessSession>
locks.emplace_back(std::move(chrootLock));
auto lock = lockToRead();
auto &packageProgress = m_buildProgress.progressByPackage[packageName];
processSession->launch(boost::process::start_dir(packageProgress.buildDirectory), m_makeChrootPkgPath, sudoArgs, makechrootpkgFlags, "-Y",
processSession->launch(boost::process::start_dir(packageProgress.buildDirectory), m_makeChrootPkgPath, sudoArgs, makechrootpkgFlags, "-C",
m_globalPackageCacheDir, "-r", chrootDir, "-l", packageProgress.chrootUser, packageProgress.makechrootpkgFlags, "--", makepkgFlags,
packageProgress.makepkgFlags, boost::process::std_in < boost::asio::buffer(m_sudoPassword));
lock.unlock();

View File

@ -78,7 +78,6 @@ void PrepareBuild::run()
m_pullingInFurtherDependenciesUnexpected = flags & PrepareBuildFlags::PullingInFurtherDependenciesUnexpected;
m_fetchOfficialSources = flags & PrepareBuildFlags::FetchOfficialPackageSources;
m_useContainer = flags & PrepareBuildFlags::UseContainer;
m_aurOnly = flags & PrepareBuildFlags::AurOnly;
if (m_forceBumpPackageVersion && m_keepPkgRelAndEpoch) {
reportError("Can not force-bump pkgrel and keeping it at the same time.");
return;
@ -891,22 +890,14 @@ void PrepareBuild::computeDependencies(WebClient::AurSnapshotQuerySession::Conta
}
// check for errors
auto failedPackages = std::set<std::string>();
auto localPackages = std::set<std::string>();
auto errorMessage = std::string();
set<string> failedPackages;
for (const auto &buildData : m_buildDataByPackage) {
if (!buildData.second.error.empty()) {
failedPackages.emplace(buildData.first);
} else if (m_aurOnly && !buildData.second.originalSourceDirectory.empty()) {
localPackages.emplace(buildData.first);
}
}
if (!failedPackages.empty()) {
errorMessage = "Unable to retrieve the following packages (see result data for details): " + joinStrings(failedPackages, " ");
} else if (!localPackages.empty()) {
errorMessage = "The following packages have a local override but the AUR-only flag was set: " + joinStrings(localPackages, " ");
}
if (!errorMessage.empty()) {
auto errorMessage = "Unable to retrieve the following packages (see result data for details): " + joinStrings(failedPackages, " ");
m_buildAction->appendOutput(Phrases::ErrorMessage, errorMessage, '\n');
auto resultData = makeResultData(std::move(errorMessage));
auto buildActionWriteLock = m_setup.building.lockToWrite();

View File

@ -364,7 +364,7 @@ void ReloadLibraryDependencies::loadPackageInfoFromContents()
m_buildAction->appendOutput(Phrases::SuccessMessage, "Adding parsed information to databases ...\n");
std::size_t counter = 0;
for (DatabaseToConsider &relevantDb : m_relevantPackagesByDatabase) {
auto lock = m_setup.config.lockToRead();
auto configWritelock = m_setup.config.lockToRead();
auto *const db = m_setup.config.findDatabase(relevantDb.name, relevantDb.arch);
if (!db) {
continue; // the whole database has been removed while we were loading package contents
@ -377,19 +377,19 @@ void ReloadLibraryDependencies::loadPackageInfoFromContents()
}
// find the package in the database again
const auto [packageID, existingPackage] = updater.findPackageWithID(package.info.name);
// skip if the package has been removed meanwhile if it it does no longer match what's in the database
if (!existingPackage || !existingPackage->canDepsAndProvidesFromOtherPackage(package.info)) {
continue;
if (!existingPackage) {
continue; // the package has been removed while we were loading package contents
}
// add the dependencies/provides to the existing package
updater.beginUpdate(packageID, existingPackage);
existingPackage->addDepsAndProvidesFromOtherPackage(package.info, true);
if (!existingPackage->addDepsAndProvidesFromOtherPackage(package.info)) {
continue; // the package does no longer match what's in the database
}
// update timestamp so we can skip this package on the next run
if (existingPackage->timestamp < package.lastModified) {
existingPackage->timestamp = package.lastModified;
}
// add the new dependencies on database-level
updater.endUpdate(packageID, existingPackage);
updater.update(existingPackage);
++counter;
}
updater.commit();

View File

@ -337,7 +337,7 @@ void MovePackages::run()
});
// remove package from database file of source repo
m_setup.locks.acquireToWrite(m_buildAction->log(), std::move(m_sourceDatabaseLockName),
m_setup.locks.acquireToWrite(m_buildAction->log(), std::move(m_destinationDatabaseLockName),
[this, buildAction = m_buildAction,
repoRemoveProcess = m_buildAction->makeBuildProcess("repo-remove", m_workingDirectory + "/repo-remove.log",
std::bind(&MovePackages::handleRepoRemoveResult, this, processSession, std::placeholders::_1, std::placeholders::_2))](

View File

@ -4,7 +4,6 @@
#ifndef LIBREPOMGR_GLOBAL
#define LIBREPOMGR_GLOBAL
#include "librepomgr-definitions.h"
#include <c++utilities/application/global.h>
#ifdef LIBREPOMGR_STATIC

View File

@ -27,8 +27,8 @@ VCSCLIENTS=('bzr::bzr'
'hg::mercurial'
'svn::subversion')
MARTCHUS_GIT_URL_PREFIX='git+https://martchus.dyn.f3l.de/gitea/Martchus/'
MARTCHUS_GIT_URL_PREFIX_PRIVATE='git+ssh://gitea@martchus.dyn.f3l.de/Martchus'
MARTCHUS_GIT_URL_PREFIX='git+https://martchus.no-ip.biz/gitea/Martchus/'
MARTCHUS_GIT_URL_PREFIX_PRIVATE='git+ssh://gitea@martchus.no-ip.biz/Martchus'
#########################################################################
# ARCHITECTURE, COMPILE FLAGS

View File

@ -314,7 +314,6 @@ void BuildActionsTests::testParsingInfoFromPkgFiles()
auto &fooDb = config.databases[0];
auto &barDb = config.databases[1];
const auto harfbuzz = LibPkg::Package::fromPkgFileName("mingw-w64-harfbuzz-1.4.2-1-any.pkg.tar.xz");
harfbuzz->libprovides = { "harfbuzzlibrary.so" };
const auto harfbuzzID = fooDb.updatePackage(harfbuzz);
const auto syncthingtray = LibPkg::Package::fromPkgFileName("syncthingtray-0.6.2-1-x86_64.pkg.tar.xz");
const auto syncthingtrayID = fooDb.updatePackage(syncthingtray);
@ -323,14 +322,6 @@ void BuildActionsTests::testParsingInfoFromPkgFiles()
barDb.updatePackage(cmake);
CPPUNIT_ASSERT_EQUAL_MESSAGE("origin", LibPkg::PackageOrigin::PackageFileName, cmake->origin);
barDb.localPkgDir = directory(testFilePath("repo/bar/cmake-3.8.2-1-x86_64.pkg.tar.xz"));
auto harfbuzzLibraryPresent = false;
fooDb.providingPackages("harfbuzzlibrary.so", false, [&](LibPkg::StorageID id, const std::shared_ptr<LibPkg::Package> &package) {
CPPUNIT_ASSERT_EQUAL(harfbuzzID, id);
CPPUNIT_ASSERT_EQUAL(harfbuzz->name, package->name);
harfbuzzLibraryPresent = true;
return true;
});
CPPUNIT_ASSERT_MESSAGE("harfbuzz found via \"harfbuzzlibrary.so\" before reload", harfbuzzLibraryPresent);
auto buildAction = std::make_shared<BuildAction>(0, &m_setup);
auto reloadLibDependencies = ReloadLibraryDependencies(m_setup, buildAction);
@ -354,11 +345,6 @@ void BuildActionsTests::testParsingInfoFromPkgFiles()
CPPUNIT_ASSERT_EQUAL(1_st, pkgsProvidingLibSyncthingConnector.size());
CPPUNIT_ASSERT_EQUAL(syncthingtray->name, pkgsProvidingLibSyncthingConnector.front().pkg->name);
CPPUNIT_ASSERT_EQUAL(syncthingtrayID, pkgsProvidingLibSyncthingConnector.front().id);
fooDb.providingPackages("harfbuzzlibrary.so", false, [&](LibPkg::StorageID, const std::shared_ptr<LibPkg::Package> &) {
CPPUNIT_FAIL("harfbuzz still found via \"harfbuzzlibrary.so\" after reload");
return true;
});
}
/*!
@ -585,7 +571,7 @@ void BuildActionsTests::testConductingBuild()
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"no staging needed: updpkgsums log", "fake updatepkgsums: \n"s, readFile("building/build-data/conduct-build-test/boost/pkg/updpkgsums.log"));
TESTUTILS_ASSERT_LIKE("no staging needed: build log",
"fake makechrootpkg: -c -u -Y .*building/test-cache-dir/x86_64 -r .*chroot-dir/arch-x86_64 -l buildservice --\n"s,
"fake makechrootpkg: -c -u -C .*building/test-cache-dir/x86_64 -r .*chroot-dir/arch-x86_64 -l buildservice --\n"s,
readFile("building/build-data/conduct-build-test/boost/pkg/build.log"));
TESTUTILS_ASSERT_LIKE("no staging needed: repo-add log",
"fake repo-add: boost.db.tar.zst boost(-libs)?-1\\.73\\.0-1-x86_64.pkg.tar.zst boost(-libs)?-1\\.73\\.0-1-x86_64.pkg.tar.zst\n"s,

View File

@ -13,6 +13,9 @@ set(META_PROJECT_VARNAME REPO_CLEAN)
set(META_APP_NAME "Package finder")
set(META_APP_AUTHOR "Martchus")
set(META_APP_DESCRIPTION "Tool to find the package containing a certain file")
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 1)
# find c++utilities
set(CONFIGURATION_PACKAGE_SUFFIX

View File

@ -13,6 +13,9 @@ set(META_PROJECT_VARNAME REPO_CLEAN)
set(META_APP_NAME "Package parser")
set(META_APP_AUTHOR "Martchus")
set(META_APP_DESCRIPTION "Tool to parse an Arch Linux package printing the results as JSON")
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 1)
# find c++utilities
set(CONFIGURATION_PACKAGE_SUFFIX

View File

@ -20,10 +20,10 @@ set(JAVA_SCRIPT_FILES
static/js/terminal.js
static/js/utils.js
static/js/main.js
static/node_modules/@xterm/xterm/lib/xterm.js
static/node_modules/@xterm/addon-search/lib/addon-search.js)
static/node_modules/xterm/lib/xterm.js
static/node_modules/xterm-addon-search/lib/xterm-addon-search.js)
set(CSS_FILES static/css/basics.css static/css/genericrendering.css static/css/layout.css static/css/log.css
static/css/specifics.css static/node_modules/@xterm/xterm/css/xterm.css)
static/css/specifics.css static/node_modules/xterm/css/xterm.css)
set(IMG_FILES
static/img/favicon.svg
static/img/logo.svg
@ -61,6 +61,9 @@ set(META_APP_NAME "Repo manager and package builder for Arch Linux")
set(META_APP_AUTHOR "Martchus")
set(META_APP_DESCRIPTION
"Daemon and web application for building Arch Linux packages and managing custom Arch Linux repositories")
set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 0)
set(META_VERSION_PATCH 1)
# include CMake modules from own project directory
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "${CMAKE_MODULE_PATH}")

View File

@ -10,5 +10,5 @@ location /buildservice/api/ {
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ ^(?!/buildservice/api/)/buildservice/(.*)$ {
alias /run/media/devel/projects/c++/cmake/auto-makepkg/srv/static/$1;
alias /run/media/devel/projects/c++/cmake/auto-makepkg/buildservice/static/$1;
}

View File

@ -30,7 +30,7 @@
#package-search-db {
height: 100px !important;
}
#package-search-form table input[type="text"], input[type="search"], #package-search-form table select {
#package-search-form table input[type="text"], #package-search-form table select {
width: 100%;
}
#package-search-form table td, #package-search-form table th {

View File

@ -10,10 +10,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<link rel="icon" href="img/favicon.svg" type="image/svg+xml" />
<!-- include xterm.js -->
<link rel="stylesheet" type="text/css" href="node_modules/@xterm/xterm/css/xterm.css" />
<link rel="stylesheet" type="text/css" href="node_modules/xterm/css/xterm.css" />
<script type="text/javascript">window.exports = {} // hack for xtermjs exports as it does not support ES6 modules yet</script>
<script src="node_modules/@xterm/xterm/lib/xterm.js"></script>
<script src="node_modules/@xterm/addon-search/lib/addon-search.js"></script>
<script src="node_modules/xterm/lib/xterm.js"></script>
<script src="node_modules/xterm-addon-search/lib/xterm-addon-search.js"></script>
<!-- include custom styles and scripts -->
<link rel="stylesheet" type="text/css" href="css/basics.css" />
<link rel="stylesheet" type="text/css" href="css/layout.css" />
@ -65,7 +65,7 @@
<table class="form-row">
<tr>
<th>Package name:</th>
<td><input type="search" name="name" placeholder="Package name" /></td>
<td><input type="text" name="name" placeholder="Package name" /></td>
<th rowspan="3">Database:</th>
<th rowspan="3">
<select id="package-search-db-arch-filter" class="arch-filter">
@ -216,7 +216,6 @@
</div>
<div class="form-row">
<label for="build-action-package-names">Package names:</label>
<input type="button" value="Clear" style="float: right;" onclick="this.form.elements['package-names'].value = ''" />
<br />
<textarea name="package-names" id="build-action-package-names" style="box-sizing: border-box; width: 100%; height: 50px;"></textarea>
</div>
@ -245,8 +244,6 @@
onchange="this.form['start-after-id'].disabled = !this.checked" />
<label for="build-action-start-after">After</label>
<input type="text" name="start-after-id" value="" placeholder="build action ID" disabled="disabled" />
<input type="button" name="start-after-latest" value="After latest"
title="Start after the first action displayed on the current page of the build actions table" />
</fieldset>
</div>
</div>

View File

@ -38,8 +38,6 @@ export function queryRoute(method, path, callback, type)
return window.alert('Authentication failed. Try again.');
default:
try {
// avoid showing HTML code from gateway
ajaxRequest.responseTextDisplay = ajaxRequest.status >= 500 && ajaxRequest.status < 600 ? 'internal server error' : ajaxRequest.responseText;
return callback(this, status === 200);
} catch (e) {
window.alert('Unable to process server response: ' + e);
@ -90,7 +88,7 @@ function makeFormQueryParameter(form)
continue; // if we disable a form element or hide it via CSS we also don't want to submit its data
}
const type = formElement.type;
if ((type === 'button') || ((type === 'checkbox' || type === 'radio') && !formElement.checked)) {
if ((type === 'checkbox' || type === 'radio') && !formElement.checked) {
continue;
}
const name = formElement.name;

View File

@ -34,17 +34,6 @@ export function initBuildActionsForm()
handleBuildActionPresetChange();
};
// allow selecting to start after the latest build action
const buildActionsFormElements = buildActionsForm.elements;
buildActionsFormElements['start-after-latest'].onclick = function() {
const condElement = buildActionsFormElements['start-condition'];
const idElement = buildActionsFormElements['start-after-id'];
const id = document.getElementById('build-actions-list')?.getElementsByTagName('table')[0]?.tBodies[0]?.getElementsByTagName('tr')[0]?.dataset.id;
condElement.value = 'after';
idElement.disabled = false;
idElement.value = id || '';
};
queryBuildActions();
handleBuildActionTypeChange();
buildActionsForm.dataset.initialized = true;
@ -261,20 +250,19 @@ function renderBuildActionActions(actionValue, buildAction, detailsTable)
function showBuildActions(ajaxRequest)
{
if (!window.globalInfo && ajaxRequest.status === 200) {
if (!window.globalInfo) {
window.functionsPostponedUntilGlobalInfo.push(showBuildActions.bind(this, ...arguments));
return;
}
const buildActionsList = Utils.getAndEmptyElement('build-actions-list');
if (ajaxRequest.status !== 200) {
buildActionsList.appendChild(document.createTextNode('Unable to load build actions: ' + ajaxRequest.responseTextDisplay));
buildActionsList.appendChild(document.createTextNode(' '));
buildActionsList.appendChild(document.createTextNode('Unable to load build actions: ' + ajaxRequest.responseText));
buildActionsList.appendChild(CustomRendering.renderReloadButton(queryBuildActions));
return;
}
const responseJson = JSON.parse(ajaxRequest.responseText);
if (!Array.isArray(responseJson)) {
buildActionsList.appendChild(document.createTextNode('Unable to load build actions: response is no array '));
buildActionsList.appendChild(document.createTextNode('Unable to load build actions: response is no array'));
buildActionsList.appendChild(CustomRendering.renderReloadButton(queryBuildActions));
return;
}

View File

@ -18,10 +18,11 @@ function handleGlobalStatusUpdate(ajaxRequest)
{
const globalStatus = Utils.getAndEmptyElement('global-status');
let responseText = ajaxRequest.responseText;
if (ajaxRequest.status === 500) {
responseText = 'internal server error';
}
if (ajaxRequest.status !== 200) {
globalStatus.appendChild(document.createTextNode('unable to load global status: ' + ajaxRequest.responseTextDisplay));
globalStatus.appendChild(document.createTextNode(' '));
globalStatus.appendChild(CustomRendering.renderReloadButton(queryGlobalStatus));
globalStatus.appendChild(document.createTextNode('unable to load global status: ' + responseText));
return;
}
const responseJson = JSON.parse(responseText);

View File

@ -156,7 +156,7 @@ export function showPackageDetails(ajaxRequest, row)
const packageID = makePackageID(row);
const packageDetailsContainer = Utils.getAndEmptyElement('package-details-container');
if (ajaxRequest.status !== 200) {
packageDetailsContainer.appendChild(document.createTextNode('unable query package details: ' + ajaxRequest.responseTextDisplay));
packageDetailsContainer.appendChild(document.createTextNode('unable query package details: ' + ajaxRequest.responseText));
return;
}
const responseJson = JSON.parse(ajaxRequest.responseText);

View File

@ -108,7 +108,7 @@ function showPackageSearchResults(ajaxRequest)
{
const packageSearchResults = Utils.getAndEmptyElement('package-search-results');
if (ajaxRequest.status !== 200) {
packageSearchResults.appendChild(document.createTextNode('unable search for packages: ' + ajaxRequest.responseTextDisplay));
packageSearchResults.appendChild(document.createTextNode('unable search for packages: ' + ajaxRequest.responseText));
return;
}
const responseJson = JSON.parse(ajaxRequest.responseText);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -140,7 +140,7 @@
cursor: crosshair;
}
.xterm .xterm-accessibility:not(.debug),
.xterm .xterm-accessibility,
.xterm .xterm-message {
position: absolute;
left: 0;
@ -152,15 +152,6 @@
pointer-events: none;
}
.xterm .xterm-accessibility-tree:not(.debug) *::selection {
color: transparent;
}
.xterm .xterm-accessibility-tree {
user-select: text;
white-space: pre;
}
.xterm .live-region {
position: absolute;
left: -9999px;

2
srv/static/node_modules/xterm/lib/xterm.js generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -5,22 +5,22 @@
"packages": {
"": {
"dependencies": {
"@xterm/addon-search": "^0.14.0",
"@xterm/xterm": "^5.4.0"
"xterm": "^5.0.0",
"xterm-addon-search": "^0.10.0"
}
},
"node_modules/@xterm/addon-search": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.14.0.tgz",
"integrity": "sha512-gyKIjC1c2bqxBevPmWlMWRsHqiufUgl3HjN3OYim6YPClqNRUlTab7l8aW8i3W83XzU9q0gmAfIOe4KDmo0GfQ==",
"node_modules/xterm": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz",
"integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg=="
},
"node_modules/xterm-addon-search": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/xterm-addon-search/-/xterm-addon-search-0.10.0.tgz",
"integrity": "sha512-l+kjDxNDQbkniU5OUo9BHknxUEPZGM0OFpVpc2sMmrb97S0FKJVJO4wAZPJvSGVJ8ZEG6KuDyzXluvnb08t71Q==",
"peerDependencies": {
"@xterm/xterm": "^5.0.0"
"xterm": "^5.0.0"
}
},
"node_modules/@xterm/xterm": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
}
}
}

View File

@ -1,6 +1,6 @@
{
"dependencies": {
"@xterm/addon-search": "^0.14.0",
"@xterm/xterm": "^5.4.0"
"xterm": "^5.0.0",
"xterm-addon-search": "^0.10.0"
}
}