|
|
|
@ -37,26 +37,28 @@
|
|
|
|
|
#include <ctime>
|
|
|
|
|
#include <numeric>
|
|
|
|
|
#include <semaphore>
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <shared_mutex>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
|
|
#include <git2.h>
|
|
|
|
|
|
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
|
|
// Define the semaphore with a limit of 5 concurrent jobs
|
|
|
|
|
// Define the semaphore with a limit of 5 concurrent package processing tasks
|
|
|
|
|
static std::counting_semaphore<5> semaphore(5);
|
|
|
|
|
|
|
|
|
|
// RAII helper class for semaphore management
|
|
|
|
|
struct SemaphoreGuard {
|
|
|
|
|
std::counting_semaphore<5>& sem;
|
|
|
|
|
// Mutex to protect access to the repo_mutexes map
|
|
|
|
|
static std::mutex repo_map_mutex;
|
|
|
|
|
|
|
|
|
|
SemaphoreGuard(std::counting_semaphore<5>& s) : sem(s) {
|
|
|
|
|
sem.acquire(); // Acquire a slot
|
|
|
|
|
}
|
|
|
|
|
// Map to hold mutexes for each repository path
|
|
|
|
|
static std::unordered_map<fs::path, std::mutex> repo_mutexes;
|
|
|
|
|
|
|
|
|
|
~SemaphoreGuard() {
|
|
|
|
|
sem.release(); // Release the slot when out of scope
|
|
|
|
|
// Helper function to get the mutex for a given repository path
|
|
|
|
|
static std::mutex& get_repo_mutex(const fs::path& repo_path) {
|
|
|
|
|
std::lock_guard<std::mutex> lock(repo_map_mutex);
|
|
|
|
|
return repo_mutexes[repo_path];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const std::string BASE_DIR = "/srv/lubuntu-ci/repos";
|
|
|
|
|
static const std::string DEBFULLNAME = "Lugito";
|
|
|
|
@ -367,167 +369,7 @@ static std::vector<std::string> get_exclusions(const fs::path &packaging) {
|
|
|
|
|
return exclusions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
// Parse command-line arguments first to set verbosity
|
|
|
|
|
std::string prog_name = fs::path(argv[0]).filename().string();
|
|
|
|
|
for(int i = 1; i < argc; i++) {
|
|
|
|
|
std::string arg = argv[i];
|
|
|
|
|
if(arg == "--help" || arg == "-h") {
|
|
|
|
|
print_help(prog_name);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if(arg == "--verbose" || arg == "-v") {
|
|
|
|
|
verbose = true;
|
|
|
|
|
// Remove the verbose flag from argc and argv for further processing
|
|
|
|
|
// Shift the arguments left
|
|
|
|
|
for(int j = i; j < argc - 1; j++) {
|
|
|
|
|
argv[j] = argv[j+1];
|
|
|
|
|
}
|
|
|
|
|
argc--;
|
|
|
|
|
i--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_info("Script started.");
|
|
|
|
|
fs::create_directories(LOG_DIR);
|
|
|
|
|
log_info("Ensured log directory exists: " + LOG_DIR);
|
|
|
|
|
fs::create_directories(OUTPUT_DIR);
|
|
|
|
|
log_info("Ensured output directory exists: " + OUTPUT_DIR);
|
|
|
|
|
|
|
|
|
|
auto now = std::time(nullptr);
|
|
|
|
|
std::tm tm;
|
|
|
|
|
gmtime_r(&now, &tm);
|
|
|
|
|
char buf_time[20];
|
|
|
|
|
std::strftime(buf_time, sizeof(buf_time), "%Y%m%dT%H%M%S", &tm);
|
|
|
|
|
std::string current_time = buf_time;
|
|
|
|
|
|
|
|
|
|
std::string uuid_part = current_time.substr(0,10); // Adjusted to match timestamp length
|
|
|
|
|
BASE_LINTIAN_DIR = BASE_OUTPUT_DIR + "/.lintian.tmp." + uuid_part;
|
|
|
|
|
fs::create_directories(BASE_LINTIAN_DIR);
|
|
|
|
|
log_info("Created Lintian temporary directory: " + BASE_LINTIAN_DIR);
|
|
|
|
|
|
|
|
|
|
fs::path log_file = fs::path(LOG_DIR) / (current_time + ".log");
|
|
|
|
|
log_info("Opening log file: " + log_file.string());
|
|
|
|
|
log_file_stream.open(log_file);
|
|
|
|
|
if(!log_file_stream.is_open()) {
|
|
|
|
|
std::cerr << "[ERROR] Unable to open log file.\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
log_info("Log file opened successfully.");
|
|
|
|
|
|
|
|
|
|
bool skip_dput = false;
|
|
|
|
|
bool skip_cleanup = false;
|
|
|
|
|
std::string config_path;
|
|
|
|
|
for(int i = 1; i < argc; i++) {
|
|
|
|
|
std::string arg = argv[i];
|
|
|
|
|
log_info("Processing argument: " + arg);
|
|
|
|
|
if(arg == "--skip-dput") {
|
|
|
|
|
skip_dput = true;
|
|
|
|
|
log_info("Flag set: --skip-dput");
|
|
|
|
|
} else if(arg == "--skip-cleanup") {
|
|
|
|
|
skip_cleanup = true;
|
|
|
|
|
log_info("Flag set: --skip-cleanup");
|
|
|
|
|
} else if(arg.rfind("--urgency-level=", 0) == 0) {
|
|
|
|
|
urgency_level_override = arg.substr(std::string("--urgency-level=").size());
|
|
|
|
|
log_info("Urgency level overridden to: " + urgency_level_override);
|
|
|
|
|
} else if(arg.rfind("--workers=", 0) == 0) {
|
|
|
|
|
worker_count = std::stoi(arg.substr(std::string("--workers=").size()));
|
|
|
|
|
if(worker_count < 1) worker_count = 1;
|
|
|
|
|
log_info("Worker count set to: " + std::to_string(worker_count));
|
|
|
|
|
} else if(config_path.empty()) {
|
|
|
|
|
config_path = arg;
|
|
|
|
|
log_info("Config path set to: " + config_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(config_path.empty()) {
|
|
|
|
|
log_error("No config file specified.");
|
|
|
|
|
print_help(prog_name);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setenv("DEBFULLNAME", DEBFULLNAME.c_str(), 1);
|
|
|
|
|
log_info("Set DEBFULLNAME to: " + DEBFULLNAME);
|
|
|
|
|
setenv("DEBEMAIL", DEBEMAIL.c_str(), 1);
|
|
|
|
|
log_info("Set DEBEMAIL to: " + DEBEMAIL);
|
|
|
|
|
|
|
|
|
|
YAML::Node config;
|
|
|
|
|
try {
|
|
|
|
|
config = load_config(config_path);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
log_error(std::string("Error loading config file: ") + e.what());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto packages = config["packages"];
|
|
|
|
|
auto releases = config["releases"];
|
|
|
|
|
log_info("Loaded " + std::to_string(packages.size()) + " packages and " + std::to_string(releases.size()) + " releases from config.");
|
|
|
|
|
|
|
|
|
|
fs::current_path(BASE_DIR);
|
|
|
|
|
log_info("Set current working directory to BASE_DIR: " + BASE_DIR);
|
|
|
|
|
|
|
|
|
|
auto get_packaging_branch = [&](const YAML::Node &pkg) -> std::optional<std::string> {
|
|
|
|
|
if(pkg["packaging_branch"] && pkg["packaging_branch"].IsScalar()) {
|
|
|
|
|
std::string branch = pkg["packaging_branch"].as<std::string>();
|
|
|
|
|
log_info("Packaging branch for package: " + branch);
|
|
|
|
|
return branch;
|
|
|
|
|
} else if (releases.size() > 0) {
|
|
|
|
|
std::string branch = "ubuntu/" + releases[0].as<std::string>();
|
|
|
|
|
log_info("Default packaging branch set to: " + branch);
|
|
|
|
|
return branch;
|
|
|
|
|
}
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto parse_version = [&](const fs::path &changelog_path) -> std::string {
|
|
|
|
|
log_info("Parsing version from changelog: " + changelog_path.string());
|
|
|
|
|
std::ifstream f(changelog_path);
|
|
|
|
|
if(!f) {
|
|
|
|
|
log_error("Changelog not found: " + changelog_path.string());
|
|
|
|
|
throw std::runtime_error("Changelog not found: " + changelog_path.string());
|
|
|
|
|
}
|
|
|
|
|
std::string first_line;
|
|
|
|
|
std::getline(f, first_line);
|
|
|
|
|
size_t start = first_line.find('(');
|
|
|
|
|
size_t end = first_line.find(')');
|
|
|
|
|
if(start == std::string::npos || end == std::string::npos) {
|
|
|
|
|
log_error("Invalid changelog format in " + changelog_path.string());
|
|
|
|
|
throw std::runtime_error("Invalid changelog format");
|
|
|
|
|
}
|
|
|
|
|
std::string version_match = first_line.substr(start + 1, end - (start + 1));
|
|
|
|
|
log_verbose("Extracted version: " + version_match);
|
|
|
|
|
std::string epoch;
|
|
|
|
|
std::string upstream_version = version_match;
|
|
|
|
|
if(auto pos = version_match.find(':'); pos != std::string::npos) {
|
|
|
|
|
epoch = version_match.substr(0, pos);
|
|
|
|
|
upstream_version = version_match.substr(pos + 1);
|
|
|
|
|
log_verbose("Parsed epoch: " + epoch + ", upstream version: " + upstream_version);
|
|
|
|
|
}
|
|
|
|
|
if(auto pos = upstream_version.find('-'); pos != std::string::npos) {
|
|
|
|
|
upstream_version = upstream_version.substr(0, pos);
|
|
|
|
|
log_verbose("Trimmed upstream version: " + upstream_version);
|
|
|
|
|
}
|
|
|
|
|
std::regex git_regex("(\\+git[0-9]+)?(~[a-z]+)?$");
|
|
|
|
|
upstream_version = std::regex_replace(upstream_version, git_regex, "");
|
|
|
|
|
log_verbose("Upstream version after regex: " + upstream_version);
|
|
|
|
|
auto t = std::time(nullptr);
|
|
|
|
|
std::tm tm_utc;
|
|
|
|
|
gmtime_r(&t, &tm_utc);
|
|
|
|
|
char buf_version[20];
|
|
|
|
|
std::strftime(buf_version, sizeof(buf_version), "%Y%m%d%H%M", &tm_utc);
|
|
|
|
|
std::string current_date = buf_version;
|
|
|
|
|
std::string version;
|
|
|
|
|
if(!epoch.empty()) {
|
|
|
|
|
version = epoch + ":" + upstream_version + "+git" + current_date;
|
|
|
|
|
} else {
|
|
|
|
|
version = upstream_version + "+git" + current_date;
|
|
|
|
|
}
|
|
|
|
|
log_info("Final parsed version: " + version);
|
|
|
|
|
return version;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto run_source_lintian = [&](const std::string &name, const fs::path &source_path) {
|
|
|
|
|
static void run_source_lintian(const std::string &name, const fs::path &source_path) {
|
|
|
|
|
log_info("Running Lintian for package: " + name);
|
|
|
|
|
fs::path temp_file = fs::temp_directory_path() / ("lintian_suppress_" + name + ".txt");
|
|
|
|
|
{
|
|
|
|
@ -569,9 +411,9 @@ int main(int argc, char** argv) {
|
|
|
|
|
log_error("Failed to run Lintian for package: " + name);
|
|
|
|
|
}
|
|
|
|
|
log_verbose("Completed Lintian run for package: " + name);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto dput_source = [&](const std::string &name, const std::string &upload_target, const std::vector<std::string> &changes_files, const std::vector<std::string> &devel_changes_files) {
|
|
|
|
|
static void dput_source(const std::string &name, const std::string &upload_target, const std::vector<std::string> &changes_files, const std::vector<std::string> &devel_changes_files) {
|
|
|
|
|
log_info("Uploading changes for package: " + name + " to " + upload_target);
|
|
|
|
|
if(!changes_files.empty()) {
|
|
|
|
|
std::string hr_changes;
|
|
|
|
@ -589,13 +431,14 @@ int main(int argc, char** argv) {
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
log_error("Failed to upload changes for package: " + name);
|
|
|
|
|
// Errors are already logged in run_command_silent_on_success
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log_warning("No changes files to upload for package: " + name);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto update_changelog = [&](const fs::path &packaging_dir, const std::string &release, const std::string &version_with_epoch) {
|
|
|
|
|
static void update_changelog(const fs::path &packaging_dir, const std::string &release, const std::string &version_with_epoch) {
|
|
|
|
|
std::string name = packaging_dir.filename().string();
|
|
|
|
|
log_info("Updating changelog for " + name + " to version " + version_with_epoch + "-0ubuntu1~ppa1");
|
|
|
|
|
try {
|
|
|
|
@ -611,12 +454,9 @@ int main(int argc, char** argv) {
|
|
|
|
|
};
|
|
|
|
|
run_command_silent_on_success(cmd, packaging_dir);
|
|
|
|
|
log_info("Changelog updated for " + name);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto build_package = [&](const fs::path &packaging_dir, const std::map<std::string, std::string> &env_vars, bool large) -> std::string {
|
|
|
|
|
// Acquire semaphore for all operations within this function
|
|
|
|
|
SemaphoreGuard guard(semaphore);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::string build_package(const fs::path &packaging_dir, const std::map<std::string, std::string> &env_vars, bool large) {
|
|
|
|
|
std::string name = packaging_dir.filename().string();
|
|
|
|
|
log_info("Building source package for " + name);
|
|
|
|
|
fs::path temp_dir;
|
|
|
|
@ -699,27 +539,74 @@ int main(int argc, char** argv) {
|
|
|
|
|
}
|
|
|
|
|
log_info("Built package successfully, changes file: " + changes_file);
|
|
|
|
|
return changes_file;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto process_package = [&](const YAML::Node &pkg) {
|
|
|
|
|
static void process_package(const YAML::Node &pkg, const YAML::Node &releases) {
|
|
|
|
|
// Acquire semaphore to limit concurrent package processing
|
|
|
|
|
semaphore.acquire();
|
|
|
|
|
try {
|
|
|
|
|
std::string name = pkg["name"] ? pkg["name"].as<std::string>() : "";
|
|
|
|
|
std::string upload_target = pkg["upload_target"] ? pkg["upload_target"].as<std::string>() : "ppa:lubuntu-ci/unstable-ci-proposed";
|
|
|
|
|
if(name.empty()) {
|
|
|
|
|
log_warning("Skipping package due to missing name.");
|
|
|
|
|
semaphore.release();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
log_info("Processing package: " + name);
|
|
|
|
|
fs::path packaging_destination = fs::path(BASE_DIR) / name;
|
|
|
|
|
fs::path changelog_path = packaging_destination / "debian" / "changelog";
|
|
|
|
|
std::string version = parse_version(changelog_path);
|
|
|
|
|
std::string version = ""; // To be parsed after locking repos
|
|
|
|
|
|
|
|
|
|
// Determine repository paths
|
|
|
|
|
std::string upstream_url = pkg["upstream_url"] ? pkg["upstream_url"].as<std::string>() : ("https://github.com/lxqt/" + name + ".git");
|
|
|
|
|
fs::path upstream_destination = fs::path(BASE_DIR) / ("upstream-" + name);
|
|
|
|
|
std::optional<std::string> packaging_branch = std::nullopt;
|
|
|
|
|
if(pkg["packaging_branch"] && pkg["packaging_branch"].IsScalar()) {
|
|
|
|
|
packaging_branch = pkg["packaging_branch"].as<std::string>();
|
|
|
|
|
} else if (releases.size() > 0) {
|
|
|
|
|
packaging_branch = "ubuntu/" + releases[0].as<std::string>();
|
|
|
|
|
}
|
|
|
|
|
std::string packaging_url = pkg["packaging_url"] ? pkg["packaging_url"].as<std::string>() : ("https://git.lubuntu.me/Lubuntu/" + name + "-packaging.git");
|
|
|
|
|
fs::path packaging_repo = packaging_destination;
|
|
|
|
|
|
|
|
|
|
// Acquire mutexes for Git repositories
|
|
|
|
|
std::mutex& upstream_mutex = get_repo_mutex(upstream_destination);
|
|
|
|
|
std::mutex& packaging_mutex = get_repo_mutex(packaging_repo);
|
|
|
|
|
|
|
|
|
|
// Lock both mutexes without deadlock using std::scoped_lock
|
|
|
|
|
std::scoped_lock lock(upstream_mutex, packaging_mutex);
|
|
|
|
|
|
|
|
|
|
// Perform Git operations and tarball processing
|
|
|
|
|
git_fetch_and_checkout(upstream_destination, upstream_url, std::nullopt);
|
|
|
|
|
git_fetch_and_checkout(packaging_repo, packaging_url, packaging_branch);
|
|
|
|
|
try {
|
|
|
|
|
log_info("Updating maintainer for package: " + name);
|
|
|
|
|
update_maintainer((packaging_destination / "debian").string(), false);
|
|
|
|
|
log_info("Maintainer updated for package: " + name);
|
|
|
|
|
} catch(std::exception &e) {
|
|
|
|
|
log_warning("update_maintainer error for " + name + ": " + std::string(e.what()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto exclusions = get_exclusions(packaging_destination);
|
|
|
|
|
log_info("Creating tarball for package: " + name);
|
|
|
|
|
create_tarball(name, upstream_destination, exclusions);
|
|
|
|
|
log_info("Tarball created for package: " + name);
|
|
|
|
|
|
|
|
|
|
// Parse version after creating tarball
|
|
|
|
|
version = parse_version(changelog_path);
|
|
|
|
|
|
|
|
|
|
// Determine if package is large
|
|
|
|
|
bool large = pkg["large"] ? pkg["large"].as<bool>() : false;
|
|
|
|
|
if(large) {
|
|
|
|
|
log_info("Package " + name + " is marked as large.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, std::map<std::string, std::string>>> built_changes;
|
|
|
|
|
// Prepare environment variables for building
|
|
|
|
|
std::map<std::string, std::string> env_map;
|
|
|
|
|
env_map["DEBFULLNAME"] = DEBFULLNAME;
|
|
|
|
|
env_map["DEBEMAIL"] = DEBEMAIL;
|
|
|
|
|
|
|
|
|
|
// Update environment variables based on version
|
|
|
|
|
std::string epoch;
|
|
|
|
|
std::string version_no_epoch = version;
|
|
|
|
|
if(auto pos = version.find(':'); pos != std::string::npos) {
|
|
|
|
@ -727,147 +614,175 @@ int main(int argc, char** argv) {
|
|
|
|
|
version_no_epoch = version.substr(pos + 1);
|
|
|
|
|
log_verbose("Package " + name + " has epoch: " + epoch);
|
|
|
|
|
}
|
|
|
|
|
env_map["VERSION"] = version_no_epoch;
|
|
|
|
|
|
|
|
|
|
// Build packages for all releases sequentially
|
|
|
|
|
std::vector<std::string> changes_files;
|
|
|
|
|
std::vector<std::string> devel_changes_files;
|
|
|
|
|
for(auto rel : releases) {
|
|
|
|
|
std::string release = rel.as<std::string>();
|
|
|
|
|
log_info("Building " + name + " for release: " + release);
|
|
|
|
|
|
|
|
|
|
std::string release_version_no_epoch = version_no_epoch + "~" + release;
|
|
|
|
|
fs::path tarball_source = fs::path(BASE_DIR) / (name + "_MAIN.orig.tar.gz");
|
|
|
|
|
fs::path tarball_dest = fs::path(BASE_DIR) / (name + "_" + release_version_no_epoch + ".orig.tar.gz");
|
|
|
|
|
std::error_code ec;
|
|
|
|
|
fs::copy_file(tarball_source, tarball_dest, fs::copy_options::overwrite_existing, ec);
|
|
|
|
|
if(ec) {
|
|
|
|
|
log_error("Failed to copy tarball for " + name + " to " + tarball_dest.string());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
log_verbose("Copied tarball to " + tarball_dest.string());
|
|
|
|
|
|
|
|
|
|
std::string version_for_dch = epoch.empty() ? release_version_no_epoch : (epoch + ":" + release_version_no_epoch);
|
|
|
|
|
log_verbose("Version for dch: " + version_for_dch);
|
|
|
|
|
env_map["UPLOAD_TARGET"] = upload_target;
|
|
|
|
|
|
|
|
|
|
std::map<std::string, std::string> env_map;
|
|
|
|
|
env_map["DEBFULLNAME"] = DEBFULLNAME;
|
|
|
|
|
env_map["DEBEMAIL"] = DEBEMAIL;
|
|
|
|
|
// Update changelog
|
|
|
|
|
update_changelog(packaging_destination, release, version_for_dch);
|
|
|
|
|
|
|
|
|
|
// Set environment variables
|
|
|
|
|
env_map["VERSION"] = release_version_no_epoch;
|
|
|
|
|
env_map["UPLOAD_TARGET"] = upload_target;
|
|
|
|
|
|
|
|
|
|
// Build package
|
|
|
|
|
try {
|
|
|
|
|
update_changelog(packaging_destination, release, version_for_dch);
|
|
|
|
|
std::string changes_file = build_package(packaging_destination, env_map, large);
|
|
|
|
|
if(!changes_file.empty()) {
|
|
|
|
|
built_changes.emplace_back(changes_file, env_map);
|
|
|
|
|
changes_files.push_back(changes_file);
|
|
|
|
|
// Determine if this changes file is for the first release (devel)
|
|
|
|
|
if(rel == releases[0]) {
|
|
|
|
|
devel_changes_files.push_back(changes_file);
|
|
|
|
|
} else {
|
|
|
|
|
devel_changes_files.push_back("");
|
|
|
|
|
}
|
|
|
|
|
} catch(std::exception &e) {
|
|
|
|
|
log_error("Error processing package '" + name + "' for release '" + release + "': " + std::string(e.what()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs::remove(tarball_dest, ec);
|
|
|
|
|
if(ec) {
|
|
|
|
|
log_warning("Failed to remove tarball: " + tarball_dest.string());
|
|
|
|
|
} else {
|
|
|
|
|
log_verbose("Removed tarball: " + tarball_dest.string());
|
|
|
|
|
} catch(std::exception &e) {
|
|
|
|
|
log_error("Error building package '" + name + "' for release '" + release + "': " + std::string(e.what()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> changes_files;
|
|
|
|
|
for(auto &bc : built_changes) {
|
|
|
|
|
fs::path cf(bc.first);
|
|
|
|
|
changes_files.push_back(cf.filename().string());
|
|
|
|
|
}
|
|
|
|
|
// After building, remove the main orig tarball
|
|
|
|
|
fs::path main_tarball = fs::path(BASE_DIR) / (name + "_MAIN.orig.tar.gz");
|
|
|
|
|
fs::remove(main_tarball);
|
|
|
|
|
log_verbose("Removed main orig tarball for package: " + name);
|
|
|
|
|
|
|
|
|
|
// Release the semaphore before launching dput
|
|
|
|
|
semaphore.release();
|
|
|
|
|
|
|
|
|
|
std::unordered_set<std::string> devel_changes_files;
|
|
|
|
|
if(releases.size() > 0) {
|
|
|
|
|
std::string first_release = releases[0].as<std::string>();
|
|
|
|
|
for(auto &f : changes_files) {
|
|
|
|
|
if(f.find("~" + first_release) != std::string::npos) {
|
|
|
|
|
devel_changes_files.insert((fs::path(OUTPUT_DIR) / f).string());
|
|
|
|
|
// Handle dput upload asynchronously
|
|
|
|
|
if(!changes_files.empty() && !upload_target.empty()) {
|
|
|
|
|
std::vector<std::string> dput_changes = changes_files;
|
|
|
|
|
std::vector<std::string> dput_devel_changes = devel_changes_files;
|
|
|
|
|
std::async(std::launch::async, [name, upload_target, dput_changes, dput_devel_changes]() {
|
|
|
|
|
dput_source(name, upload_target, dput_changes, dput_devel_changes);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
devel_changes_files.insert(std::string());
|
|
|
|
|
}
|
|
|
|
|
log_warning("No changes files to upload for package: " + name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(built_changes.empty()) {
|
|
|
|
|
log_warning("No built changes files for package: " + name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(getenv("DEBFULLNAME") == nullptr) {
|
|
|
|
|
setenv("DEBFULLNAME", DEBFULLNAME.c_str(), 1);
|
|
|
|
|
log_info("Set DEBFULLNAME environment variable.");
|
|
|
|
|
static void prepare_package(const YAML::Node &pkg, const YAML::Node &releases) {
|
|
|
|
|
try {
|
|
|
|
|
process_package(pkg, releases);
|
|
|
|
|
} catch(const std::exception &e) {
|
|
|
|
|
log_error(std::string("Exception in processing package: ") + e.what());
|
|
|
|
|
}
|
|
|
|
|
if(getenv("DEBEMAIL") == nullptr) {
|
|
|
|
|
setenv("DEBEMAIL", DEBEMAIL.c_str(), 1);
|
|
|
|
|
log_info("Set DEBEMAIL environment variable.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(skip_dput) {
|
|
|
|
|
log_info("Skipping dput upload for package: " + name);
|
|
|
|
|
for(auto &file : devel_changes_files) {
|
|
|
|
|
if(!file.empty()) {
|
|
|
|
|
run_source_lintian(name, file);
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
|
// Parse command-line arguments first to set verbosity
|
|
|
|
|
std::string prog_name = fs::path(argv[0]).filename().string();
|
|
|
|
|
for(int i = 1; i < argc; i++) {
|
|
|
|
|
std::string arg = argv[i];
|
|
|
|
|
if(arg == "--help" || arg == "-h") {
|
|
|
|
|
print_help(prog_name);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if(arg == "--verbose" || arg == "-v") {
|
|
|
|
|
verbose = true;
|
|
|
|
|
// Remove the verbose flag from argc and argv for further processing
|
|
|
|
|
// Shift the arguments left
|
|
|
|
|
for(int j = i; j < argc - 1; j++) {
|
|
|
|
|
argv[j] = argv[j+1];
|
|
|
|
|
}
|
|
|
|
|
argc--;
|
|
|
|
|
i--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
std::string real_upload_target = built_changes[0].second.at("UPLOAD_TARGET");
|
|
|
|
|
dput_source(name, real_upload_target, changes_files, std::vector<std::string>(devel_changes_files.begin(), devel_changes_files.end()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs::remove(fs::path(BASE_DIR) / (name + "_MAIN.orig.tar.gz"));
|
|
|
|
|
log_verbose("Removed main orig tarball for package: " + name);
|
|
|
|
|
};
|
|
|
|
|
log_info("Script started.");
|
|
|
|
|
fs::create_directories(LOG_DIR);
|
|
|
|
|
log_info("Ensured log directory exists: " + LOG_DIR);
|
|
|
|
|
fs::create_directories(OUTPUT_DIR);
|
|
|
|
|
log_info("Ensured output directory exists: " + OUTPUT_DIR);
|
|
|
|
|
|
|
|
|
|
auto prepare_package = [&](const YAML::Node &pkg) {
|
|
|
|
|
std::string name = pkg["name"] ? pkg["name"].as<std::string>() : "";
|
|
|
|
|
if(name.empty()) {
|
|
|
|
|
log_warning("Skipping package due to missing name.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
log_info("Preparing package: " + name);
|
|
|
|
|
auto now = std::time(nullptr);
|
|
|
|
|
std::tm tm;
|
|
|
|
|
gmtime_r(&now, &tm);
|
|
|
|
|
char buf_time[20];
|
|
|
|
|
std::strftime(buf_time, sizeof(buf_time), "%Y%m%dT%H%M%S", &tm);
|
|
|
|
|
std::string current_time = buf_time;
|
|
|
|
|
|
|
|
|
|
std::string upstream_url = pkg["upstream_url"] ? pkg["upstream_url"].as<std::string>() : ("https://github.com/lxqt/" + name + ".git");
|
|
|
|
|
log_verbose("Upstream URL: " + upstream_url);
|
|
|
|
|
fs::path upstream_destination = fs::path(BASE_DIR) / ("upstream-" + name);
|
|
|
|
|
std::optional<std::string> packaging_branch = get_packaging_branch(pkg);
|
|
|
|
|
std::string packaging_url = pkg["packaging_url"] ? pkg["packaging_url"].as<std::string>() : ("https://git.lubuntu.me/Lubuntu/" + name + "-packaging.git");
|
|
|
|
|
log_verbose("Packaging URL: " + packaging_url);
|
|
|
|
|
fs::path packaging_destination = fs::path(BASE_DIR) / name;
|
|
|
|
|
std::string uuid_part = current_time.substr(0,10); // Adjusted to match timestamp length
|
|
|
|
|
BASE_LINTIAN_DIR = BASE_OUTPUT_DIR + "/.lintian.tmp." + uuid_part;
|
|
|
|
|
fs::create_directories(BASE_LINTIAN_DIR);
|
|
|
|
|
log_info("Created Lintian temporary directory: " + BASE_LINTIAN_DIR);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
git_fetch_and_checkout(upstream_destination, upstream_url, std::nullopt);
|
|
|
|
|
} catch(...) {
|
|
|
|
|
log_error("Failed to prepare upstream repo for " + name);
|
|
|
|
|
return;
|
|
|
|
|
fs::path log_file = fs::path(LOG_DIR) / (current_time + ".log");
|
|
|
|
|
log_info("Opening log file: " + log_file.string());
|
|
|
|
|
log_file_stream.open(log_file);
|
|
|
|
|
if(!log_file_stream.is_open()) {
|
|
|
|
|
std::cerr << "[ERROR] Unable to open log file.\n";
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
log_info("Log file opened successfully.");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
git_fetch_and_checkout(packaging_destination, packaging_url, packaging_branch);
|
|
|
|
|
} catch(...) {
|
|
|
|
|
log_error("Failed to prepare packaging repo for " + name);
|
|
|
|
|
return;
|
|
|
|
|
bool skip_dput = false;
|
|
|
|
|
bool skip_cleanup = false;
|
|
|
|
|
std::string config_path;
|
|
|
|
|
for(int i = 1; i < argc; i++) {
|
|
|
|
|
std::string arg = argv[i];
|
|
|
|
|
log_info("Processing argument: " + arg);
|
|
|
|
|
if(arg == "--skip-dput") {
|
|
|
|
|
skip_dput = true;
|
|
|
|
|
log_info("Flag set: --skip-dput");
|
|
|
|
|
} else if(arg == "--skip-cleanup") {
|
|
|
|
|
skip_cleanup = true;
|
|
|
|
|
log_info("Flag set: --skip-cleanup");
|
|
|
|
|
} else if(arg.rfind("--urgency-level=", 0) == 0) {
|
|
|
|
|
urgency_level_override = arg.substr(std::string("--urgency-level=").size());
|
|
|
|
|
log_info("Urgency level overridden to: " + urgency_level_override);
|
|
|
|
|
} else if(arg.rfind("--workers=", 0) == 0) {
|
|
|
|
|
worker_count = std::stoi(arg.substr(std::string("--workers=").size()));
|
|
|
|
|
if(worker_count < 1) worker_count = 1;
|
|
|
|
|
log_info("Worker count set to: " + std::to_string(worker_count));
|
|
|
|
|
} else if(config_path.empty()) {
|
|
|
|
|
config_path = arg;
|
|
|
|
|
log_info("Config path set to: " + config_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(config_path.empty()) {
|
|
|
|
|
log_error("No config file specified.");
|
|
|
|
|
print_help(prog_name);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setenv("DEBFULLNAME", DEBFULLNAME.c_str(), 1);
|
|
|
|
|
log_info("Set DEBFULLNAME to: " + DEBFULLNAME);
|
|
|
|
|
setenv("DEBEMAIL", DEBEMAIL.c_str(), 1);
|
|
|
|
|
log_info("Set DEBEMAIL to: " + DEBEMAIL);
|
|
|
|
|
|
|
|
|
|
YAML::Node config;
|
|
|
|
|
try {
|
|
|
|
|
log_info("Updating maintainer for package: " + name);
|
|
|
|
|
update_maintainer((packaging_destination / "debian").string(), false);
|
|
|
|
|
log_info("Maintainer updated for package: " + name);
|
|
|
|
|
config = load_config(config_path);
|
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
|
log_warning("update_maintainer error for " + name + ": " + std::string(e.what()));
|
|
|
|
|
log_error(std::string("Error loading config file: ") + e.what());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto exclusions = get_exclusions(packaging_destination);
|
|
|
|
|
log_info("Creating tarball for package: " + name);
|
|
|
|
|
create_tarball(name, upstream_destination, exclusions);
|
|
|
|
|
log_info("Tarball created for package: " + name);
|
|
|
|
|
auto packages = config["packages"];
|
|
|
|
|
auto releases = config["releases"];
|
|
|
|
|
log_info("Loaded " + std::to_string(packages.size()) + " packages and " + std::to_string(releases.size()) + " releases from config.");
|
|
|
|
|
|
|
|
|
|
process_package(pkg);
|
|
|
|
|
};
|
|
|
|
|
fs::current_path(BASE_DIR);
|
|
|
|
|
log_info("Set current working directory to BASE_DIR: " + BASE_DIR);
|
|
|
|
|
|
|
|
|
|
std::vector<std::future<void>> futures;
|
|
|
|
|
log_info("Starting package preparation for " + std::to_string(packages.size()) + " packages.");
|
|
|
|
|
for(auto pkg : packages) {
|
|
|
|
|
futures.push_back(std::async(std::launch::async, prepare_package, pkg));
|
|
|
|
|
futures.emplace_back(std::async(std::launch::async, prepare_package, pkg, releases));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(auto &fut : futures) {
|
|
|
|
|