#!/usr/bin/php
<?php

/******************************************************************************
*
* pushrepo
* part of lfs-ryco
*
* The MIT License (MIT)
*
* Copyright 2020-2023 by Ryan Coe <bluemrp9@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
******************************************************************************/

declare(strict_types=1);

if (@file_exists('./vendor/_autoload.php')) {
    require_once('./vendor/_autoload.php');
} else {
    require_once((isset($_SERVER['LFS_ROOTFS']) ? $_SERVER['LFS_ROOTFS'] : '').'/usr/share/php/vendor/_autoload.php');
}

use LFS\LFS;
use LFS\Repo;
use Ryco\Error\Exception;
use Ryco\Key\Key;
use Ryco\Package\RPM;
use Ryco\Utils\ArrayHelper;
use Ryco\Utils\FindFiles;
use Ryco\Utils\Process;
use Ryco\Utils\System;
use Ryco\Log;

LFS::_init();
LFS::timer_start();

$prefix = LFS::get_prefix();
$repo_file = $prefix.'/rpmbuild/REPO/'.LFS::REPO_FILE;
$rpms_dir = $prefix.'/rpmbuild/RPMS';
$srpms_dir = $prefix.'/rpmbuild/SRPMS';
$tools_dir = $prefix.'/rpmbuild/TOOLS';
$repo_dir = $prefix.'/rpmbuild/REPO';
$repo_rpm_dir = $repo_dir.'/rpm';
$repo_key_dir = $repo_dir.'/keys';
$repo_ini_file = $prefix.'/rpmbuild/TOOLS/repo.ini';

// Print name and version
Log::info(LFS::DISTRO.' pushrepo 2025.06.22');

// Read repo file
$repo = Repo::createFromRepoFile($repo_file);

// Read repo settings into config
$config = $repo->getConfig($repo_ini_file);
$len1 = ArrayHelper::arrayMaxStrLength(RPM::getSupportedArch())+5;
Log::info(sprintf('%s : %s', str_pad('Repo Name', $len1), $repo->getName()));
Log::info(sprintf('%s : %d', str_pad('Serial', $len1), $repo->getSerial()));
Log::info(sprintf('%s : %s', str_pad('Generated', $len1), date('Y-m-d H:i:s', $repo->getGenerated())));

// Generate files list
Log::info('Generating file list');
$keys = $repo->getKeys();
$files = [];
$found = true;
foreach ($keys as $key) {
    $pkg = $repo->getPackage($key);
    if ($pkg->arch === 'source') {
        $filename = 'SRPMS/'.$pkg->srpm_name;
    } else {
        $filename = 'RPMS/'.$pkg->arch.'/'.$pkg->rpm_name;
    }
    if (!in_array($filename, $files, true)) {
        $_filename = $prefix.'/rpmbuild/'.$filename;
        if (!System::fileExists($_filename)) {
            Log::error("File not found '{$_filename}'");
            $found = false;
        }
        $files[] = $filename;
    }
}
if (!$found) {
    exit(1);
}
sort($files);

// Create repo directory
try {
    System::recursiveMkdir($repo_rpm_dir);
    System::recursiveMkdir($repo_key_dir);
} catch (Exception $e) {
    Log::error($e->getMessage());
    exit(1);
}

// Get public keys from keychain
Log::info('Importing keys from keychain');
try {
    Key::fromKeyring($prefix.'/.gnupg');
} catch (Exception $e) {
    Log::error($e->getMessage());
    exit(1);
}

// Get list of gpg keys from repo
$keys = $repo->getGPGKeys();

// Export keys
foreach ($keys as $key) {
    $key_file = $repo_key_dir.'/rpm-gpg-key-'.$key;
    $_key_file = basename($key_file);
    if (!System::fileExists($key_file)) {
        Log::info("Exporting gpg key {$key}");
        try {
            $k = Key::getInstance($key);
            System::atomicWriteFile($key_file, $k->getPubkey(), 0644);
            Log::info("Wrote key file '{$_key_file}'");
        } catch (Exception $e) {
            Log::error($e->getMessage());
            exit(1);
        }
    } else {
        Log::debug("Skipping '{$_key_file}'");
    }
}

// Create temporary file with names
$tmp_file = System::getTempFile('pushrepo');
try {
    System::writeFile($tmp_file, implode("\n", $files));
} catch (Exception $e) {
    Log::error($e->getMessage());
    System::unlinkFile($tmp_file);
    exit(1);
}

// Rsync rpm files to rpm directory
Log::info('Syncing files in local repo');
$exec = [
    'rsync',
    '--exclude .gitkeep',
    '--archive',
    '--files-from='.$tmp_file,
    '--no-relative',
    $prefix.'/rpmbuild',
    $repo_rpm_dir,
];
$proc = Process::fromArray(['exec'=>$exec])->start()->wait();
$retval = $proc->getExitCode();
System::unlinkFile($tmp_file);
if ($retval !== 0) {
    Log::error('Failed to rsync files to local repo');
    Log::error($proc->getOutputArray('STDERR'));
    exit(1);
}

// Check for deleted files
foreach ($files as &$file) {
    $file = basename($file);
}
unset($file);
$find_files = FindFiles::newInstance();
$local_files = $find_files->setPath($repo_rpm_dir)->setMaxDepth(1)->findFiles()->getFileKeys();
foreach ($local_files as $_local) {
    if (!in_array(basename($_local), $files, true)) {
        $__local = $find_files->getFile($_local);
        Log::debug("Removing {$_local}");
        System::unlinkFile($__local->realpath);
    }
}

// Rsync files to remote directory
Log::info('Syncing files to remote repo');
$remote_user = $config->getString('repo.remote_user');
$remote_host = $config->getString('repo.remote_host');
$remote_dir = $config->getString('repo.remote_dir');
$repo_name = $config->getString('repo.name');
$exec = [
    'rsync',
    '--exclude .gitkeep',
    '--archive',
    '--verbose',
    '--delete',
    '--delete-after',
    '--stats',
    $repo_dir.'/',
    $remote_user.'@'.$remote_host.':'.$remote_dir.'/'.$repo_name.'/',
];
$proc = Process::fromArray(['exec'=>$exec])->start()->wait();
$retval = $proc->getExitCode();
if ($retval !== 0) {
    Log::error($proc->getOutputArray('STDERR'));
    Log::error('Failed to rsync files to remote directory');
    exit(1);
}
Log::debug($proc->getOutputArray('STDOUT'));
exit(0);
