2019-07-05 00:36:10 +02:00
using System ;
using System.Collections.Generic ;
using Newtonsoft.Json ;
using System.IO ;
using System.Linq ;
using LibGit2Sharp ;
using System.Diagnostics ;
namespace repomgr
{
public class RepoMgr
{
public RepoMgr ( string buildpath )
{
_buildpath = Path . GetFullPath ( buildpath ) ;
_pkgpath = Path . Combine ( _buildpath , "pkg" ) ;
}
private readonly string _buildpath ;
private readonly string _pkgpath ;
private Repository _repo ;
public void Init ( string repopath , string reponame )
{
_repo = new Repository ( repopath , reponame ) ;
if ( ! Directory . Exists ( repopath ) )
Directory . CreateDirectory ( repopath ) ;
WriteIndex ( ) ;
Console . WriteLine ( "Initialized." ) ;
}
public void Add ( string package )
{
Package pkg ;
if ( Directory . Exists ( Path . Combine ( _pkgpath , package ) ) )
{
if ( _repo . Packages . Find ( p = > p . Name . Equals ( package ) ) ! = null )
{
Console . WriteLine ( "Package already exists." ) ;
return ;
}
if ( ! CheckPackage ( package ) )
{
throw new Exception ( "Package directory exists but is invalid (PKGBUILD or .git missing)" ) ;
}
pkg = new Package ( package ) ;
}
else
{
if ( _repo . Packages . Find ( p = > p . Name . Equals ( package ) ) ! = null )
_repo . Packages . RemoveAll ( p = > p . Name . Equals ( package ) ) ;
try
{
var refcount = LibGit2Sharp . Repository . ListRemoteReferences ( $"https://aur.archlinux.org/{package}.git" ) . Count ( ) ;
if ( refcount < 1 )
{
throw new Exception ( "git clone failed: Package doesn't exist in remote" ) ;
}
LibGit2Sharp . Repository . Clone ( $"https://aur.archlinux.org/{package}.git" ,
Path . Combine ( _pkgpath , package ) ) ;
}
catch ( Exception e )
{
throw new Exception ( $"Error during clone: {e}" ) ;
}
pkg = new Package ( package ) ;
}
Console . WriteLine ( $"Adding package {package}..." ) ;
_repo . Packages . Add ( pkg ) ;
WriteIndex ( ) ;
}
private void Build ( Package package , bool force = false )
{
if ( UpdateAvailable ( package ) )
UpdatePackage ( package ) ;
if ( File . ReadAllText ( Path . Combine ( _pkgpath , package . Name , "PKGBUILD" ) ) . Contains ( "pkgver()" ) )
Shell . Exec ( "makepkg -os --noconfirm" , Path . Combine ( _pkgpath , package . Name ) ) ;
2019-07-05 15:29:03 +02:00
package . CurrentVersion = Shell . ExecR ( "source PKGBUILD; echo \"$pkgver-$pkgrel\"" , Path . Combine ( _pkgpath , package . Name ) ) ;
2019-07-05 00:36:10 +02:00
WriteIndex ( ) ;
2019-07-05 15:29:03 +02:00
if ( force )
Shell . Exec ( "makepkg -Ccsf --sign --noconfirm 2>&1| tee buildlog.txt" , Path . Combine ( _pkgpath , package . Name ) ) ;
else if ( package . CurrentVersion ! = package . RepoVersion )
Shell . Exec ( "makepkg -Ccs --sign --noconfirm 2>&1| tee buildlog.txt" , Path . Combine ( _pkgpath , package . Name ) ) ;
2019-07-05 00:36:10 +02:00
else return ;
var resultingPackages = Directory
2019-07-05 15:29:03 +02:00
. GetFiles ( Path . Combine ( _pkgpath , package . Name ) , "*.pkg.tar*" ) ;
2019-07-05 00:36:10 +02:00
if ( resultingPackages . Length < 1 )
{
package . LastBuildSucceeded = false ;
throw new Exception ( "makepkg didn't build any package" ) ;
}
package . LastBuildSucceeded = true ;
2019-07-05 15:29:03 +02:00
if ( package . PkgFiles . Any ( ) )
{
foreach ( var pkgFile in package . PkgFiles )
if ( File . Exists ( Path . Combine ( _repo . Path , pkgFile ) ) )
File . Delete ( Path . Combine ( _repo . Path , pkgFile ) ) ;
package . PkgFiles . Clear ( ) ;
}
2019-07-05 00:36:10 +02:00
foreach ( var resultingPackage in resultingPackages . Where ( p = > p . EndsWith ( ".sig" ) ) )
{
File . Copy ( resultingPackage , Path . Combine ( _repo . Path , Path . GetFileName ( resultingPackage ) ) , true ) ;
File . Delete ( resultingPackage ) ;
2019-07-05 15:29:03 +02:00
package . PkgFiles . Add ( Path . GetFileName ( resultingPackage ) ) ;
2019-07-05 00:36:10 +02:00
}
foreach ( var resultingPackage in resultingPackages . Where ( p = > ! p . EndsWith ( ".sig" ) ) )
{
File . Copy ( resultingPackage , Path . Combine ( _repo . Path , Path . GetFileName ( resultingPackage ) ) , true ) ;
File . Delete ( resultingPackage ) ;
2019-07-05 15:29:03 +02:00
package . PkgFiles . Add ( Path . GetFileName ( resultingPackage ) ) ;
2019-07-05 00:36:10 +02:00
Shell . Exec ( $"repo-add --remove --sign {_repo.Name}.db.tar.gz {Path.GetFileName(resultingPackage)} 2>&1| tee -a {Path.Combine(_buildpath, " repolog . txt ")}" , _repo . Path ) ;
}
package . RepoVersion = package . CurrentVersion ;
WriteIndex ( ) ;
}
private void Remove ( Package package )
{
var packageDir = Path . Combine ( _pkgpath , package . Name ) ;
if ( Directory . Exists ( packageDir ) )
Directory . Delete ( packageDir , true ) ;
Shell . Exec ( $"repo-remove --sign {_repo.Name}.db.tar.gz {package.Name} 2>&1| tee -a {Path.Combine(_buildpath, " repolog . txt ")}" , _repo . Path ) ;
2019-07-05 15:29:03 +02:00
if ( package . PkgFiles . Any ( ) )
{
foreach ( var pkgFile in package . PkgFiles )
if ( File . Exists ( Path . Combine ( _repo . Path , pkgFile ) ) )
File . Delete ( Path . Combine ( _repo . Path , pkgFile ) ) ;
package . PkgFiles . Clear ( ) ;
}
2019-07-05 00:36:10 +02:00
_repo . Packages . Remove ( package ) ;
WriteIndex ( ) ;
}
public void List ( )
{
Console . WriteLine ( Shell . Yellow ( $"{Shell.Bold(_repo.Name)} ({_repo.Packages.Count} packages):" ) ) ;
2019-07-05 15:29:03 +02:00
foreach ( var package in _repo . Packages . OrderBy ( p = > p . Name ) )
2019-07-05 00:36:10 +02:00
{
var line = $"{Shell.Bold(package.Name)}" ;
if ( package . RepoVersion ! = package . CurrentVersion )
line + = $" ({Shell.Red(package.RepoVersion)} {Shell.Gray(" - > ")} {Shell.Yellow(package.CurrentVersion)})" ;
else
line + = $" ({Shell.Green(package.RepoVersion)})" ;
if ( package . LastBuildSucceeded )
line + = $" [{Shell.Green(Shell.Bold(" BUILD PASSING "))}]" ;
else
line + = $" [{Shell.Red(Shell.Bold(" BUILD FAILING "))}]" ;
Console . WriteLine ( line ) ;
}
}
// util stuff
private Package GetPackage ( string package )
{
var pkg = _repo . Packages . FirstOrDefault ( p = > p . Name . Equals ( package ) ) ;
if ( pkg ! = null ) return pkg ;
throw new Exception ( "Package not found." ) ;
}
// git fetch, returns true if differences between origin/master and master found
private bool UpdateAvailable ( Package package )
{
var repo = new LibGit2Sharp . Repository ( Path . Combine ( _pkgpath , package . Name ) ) ;
var remote = repo . Network . Remotes [ "origin" ] ;
var refSpecs = remote . FetchRefSpecs . Select ( x = > x . Specification ) ;
Commands . Fetch ( repo , remote . Name , refSpecs , null , null ) ;
return repo . Diff . Compare < TreeChanges > ( repo . Branches [ "origin/master" ] . Tip . Tree ,
DiffTargets . Index | DiffTargets . WorkingDirectory ) . Any ( ) ;
}
// resets master to origin/master
private void UpdatePackage ( Package package )
{
var repo = new LibGit2Sharp . Repository ( Path . Combine ( _pkgpath , package . Name ) ) ;
var originMaster = repo . Branches [ "origin/master" ] ;
repo . Reset ( ResetMode . Hard , originMaster . Tip ) ;
}
public void ReadIndex ( )
{
try
{
_repo = JsonConvert . DeserializeObject < Repository > (
File . ReadAllText ( Path . Combine ( _buildpath , "repomgr.index.json" ) ) ) ;
}
catch ( IOException )
{
throw new Exception ( "configuration file not found or wrong permissions. Did you run repomgr init?" ) ;
}
catch ( JsonException )
{
throw new Exception ( "configuration corrupt. Please check manually or re-init repo." ) ;
}
}
private void WriteIndex ( )
{
try
{
var json = JsonConvert . SerializeObject ( _repo ) ;
if ( ! Directory . Exists ( _buildpath ) )
Directory . CreateDirectory ( _buildpath ) ;
File . WriteAllText ( Path . Combine ( _buildpath , "repomgr.index.json" ) , json ) ;
}
catch ( IOException )
{
throw new Exception ( "Unable to write configuratoin. Check permissions." ) ;
}
}
private bool CheckPackage ( string package )
{
return Directory . Exists ( Path . Combine ( _pkgpath , package , ".git" ) ) & &
File . Exists ( Path . Combine ( _pkgpath , package , "PKGBUILD" ) ) ;
}
public void Build ( string package , bool force = false )
{
if ( ! CheckPackage ( package ) ) return ;
var iPackage = GetPackage ( package ) ;
try
{
Build ( iPackage , force ) ;
}
catch ( Exception )
{
iPackage . LastBuildSucceeded = false ;
throw ;
}
}
public void BuildAll ( )
{
foreach ( var package in _repo . Packages )
{
if ( ! CheckPackage ( package . Name ) ) return ;
try
{
Build ( package ) ;
}
catch ( Exception )
{
package . LastBuildSucceeded = false ;
}
}
}
public void Remove ( string package )
{
if ( CheckPackage ( package ) )
Remove ( GetPackage ( package ) ) ;
}
}
public class Repository
{
public Repository ( string path , string name )
{
Path = path ;
Name = name ;
Packages = new List < Package > ( ) ;
}
public readonly string Path ;
public readonly string Name ;
public readonly List < Package > Packages ;
}
public class Package
{
public Package ( string name )
{
Name = name ;
}
public readonly string Name ;
public string CurrentVersion = "never-updated" ;
public string RepoVersion = "nA" ;
public bool LastBuildSucceeded = true ;
2019-07-05 15:29:03 +02:00
public readonly List < string > PkgFiles = new List < string > ( ) ;
2019-07-05 00:36:10 +02:00
}
internal static class Shell
{
public static void Exec ( string cmd , string workingDirectory )
{
var escapedArgs = cmd . Replace ( "\"" , "\\\"" ) ;
var process = new Process ( )
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash" ,
Arguments = $"-c \" { escapedArgs } \ "" ,
WorkingDirectory = workingDirectory ,
RedirectStandardOutput = false ,
RedirectStandardError = false ,
UseShellExecute = false ,
CreateNoWindow = true ,
}
} ;
process . Start ( ) ;
process . WaitForExit ( ) ;
}
2019-07-05 15:29:03 +02:00
public static string ExecR ( string cmd , string workingDirectory )
{
var escapedArgs = cmd . Replace ( "\"" , "\\\"" ) ;
var process = new Process ( )
{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash" ,
Arguments = $"-c \" { escapedArgs } \ "" ,
WorkingDirectory = workingDirectory ,
RedirectStandardOutput = true ,
RedirectStandardError = true ,
UseShellExecute = false ,
CreateNoWindow = true ,
}
} ;
process . Start ( ) ;
var stdout = process . StandardOutput . ReadToEnd ( ) ;
process . WaitForExit ( ) ;
return stdout . Trim ( ) ;
}
2019-07-05 00:36:10 +02:00
public static string Bold ( string s )
{
return $"\x1B[1m{s}\x1B[21m" ;
}
public static string Red ( string s )
{
return $"\x1b[31m{s}\x1b[39m" ;
}
public static string Yellow ( string s )
{
return $"\x1b[33m{s}\x1b[39m" ;
}
public static string Green ( string s )
{
return $"\x1b[32m{s}\x1b[39m" ;
}
public static string Gray ( string s )
{
return $"\x1b[90m{s}\x1b[39m" ;
}
}
}