Compare commits

...

4 Commits
dev ... rewrite

11 changed files with 343 additions and 331 deletions

3
.gitignore vendored
View File

@ -1,4 +1,3 @@
.vscode/
obj/
bin/
justdoit.sh
bin/

View File

@ -1,57 +0,0 @@
public class Program {
static void Main(string[] args) {
string beforeUpdate() {
Update.copyEverthingBeforeUpdateToBackupLocation();
Update.zipAllContentInBackupLocation("pre-backup.zip");
Update.zipPacmanDatabase();
Console.ForegroundColor = ConsoleColor.Green;
return "pre-backup complete";
}
string postUpdate() {
Update.copyEverthingAfterUpdateToBackupLocation();
Update.zipAllContentInBackupLocation("post-backup.zip");
Update.copyEverthingFromBackupLocationToFinalDestination(args[0]);
Console.ForegroundColor = ConsoleColor.Green;
return "post-backup complete";
}
string testPacmanDatabase() {
Update.zipPacmanDatabase();
Console.ForegroundColor = ConsoleColor.Yellow;
return "Test complete";
}
// Use https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?redirectedfrom=MSDN&view=net-7.0
try {
switch(args[1]) {
case "pre-backup":
beforeUpdate();
Console.ResetColor();
break;
case "post-backup":
postUpdate();
Console.ResetColor();
break;
case "testPacmanDatabase":
testPacmanDatabase();
Console.ResetColor();
break;
default:
Console.WriteLine("Wait! How did you do that?");
break;
}
} catch (Exception e) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
}
}
}

271
Update.cs
View File

@ -1,271 +0,0 @@
using System.Diagnostics;
using System.IO.Compression;
public class Update {
public static string getHomePath() {
string homePath = string.Empty;
if (Environment.OSVersion.Platform == PlatformID.Unix) {
homePath = Environment.GetEnvironmentVariable("HOME");
return homePath;
} else {
throw new ApplicationException("This script doesn't support your operating system.");
}
}
public static string copyEverthingBeforeUpdateToBackupLocation() {
string targetPath = "/tmp/backup/uncompressed/";
if (File.Exists(targetPath + "pacman-after.txt") && File.Exists(targetPath + "flatpak-after.txt")) {
File.Delete(targetPath + "pacman-after.txt");
File.Delete(targetPath + "flatpak-after.txt");
}
string[] systemFilesToCopy = {"/etc/fstab", "/etc/makepkg.conf"};
List<string> filesToCopy = new List<string>(systemFilesToCopy);
string pacmanPackageListBeforeUpdate = targetPath + "pacman-pre.txt";
filesToCopy.Add(pacmanPackageListBeforeUpdate);
string flatpakListBeforeUpdate = targetPath+ "flatpak-pre.txt";
filesToCopy.Add(flatpakListBeforeUpdate);
if (!Directory.Exists(targetPath)) {
Directory.CreateDirectory(targetPath);
copyEverthingBeforeUpdateToBackupLocation();
} else {
foreach (string file in filesToCopy) {
FileInfo info = new FileInfo(file);
string destFile = Path.Combine(targetPath, info.Name);
File.Copy(info.FullName, destFile, true);
}
}
string copiedFiles = string.Join(", ", filesToCopy);
Console.ForegroundColor = ConsoleColor.Green;
return $"Copied {copiedFiles} to {targetPath}";
}
public static string copyEverthingAfterUpdateToBackupLocation() {
string targetPath = "/tmp/backup/uncompressed/"; // Use /tmp to zip and then move into /backup/compressed/
if (File.Exists(targetPath + "pacman-pre.txt") && File.Exists(targetPath + "flatpak-pre.txt")) {
File.Delete(targetPath + "pacman-pre.txt");
File.Delete(targetPath + "flatpak-pre.txt");
File.Delete(targetPath + "fstab");
File.Delete(targetPath + "makepkg.conf");
}
List<string> filesToCopy = new List<string>();
string pacmanPackageListBeforeUpdate = targetPath + "pacman-after.txt";
filesToCopy.Add(pacmanPackageListBeforeUpdate);
string flatpakListBeforeUpdate = targetPath + "flatpak-after.txt";
filesToCopy.Add(flatpakListBeforeUpdate);
if (!Directory.Exists(targetPath)) {
Directory.CreateDirectory(targetPath);
copyEverthingAfterUpdateToBackupLocation();
} else {
foreach (string file in filesToCopy) {
FileInfo info = new FileInfo(file);
string destFile = Path.Combine(targetPath, info.Name);
File.Copy(info.FullName, destFile, true);
}
}
string copiedFiles = string.Join(", ", filesToCopy);
Console.ForegroundColor = ConsoleColor.Green;
return $"Copied {copiedFiles} to {targetPath}";
}
/// <summary>
/// Method <c>copyEverthingFromBackupLocationToFinalDestination</c> copies everything to second Backup Location which should be a external drive or a network share. Offsite/Second Backup.
/// </summary>
public static string copyEverthingFromBackupLocationToFinalDestination(string finalBackupLocation) {
string targetPath = finalBackupLocation;
if (!Directory.Exists(targetPath)) {
Console.ForegroundColor = ConsoleColor.Red;
return $"Backup location does not exist! Please specify one in the config.";
}
if (targetPath is not null) {
string sourcePath = getHomePath() + "/backup/compressed/";
string[] intermediateBackupLocation = Directory.GetFiles(sourcePath);
if (!Directory.Exists(targetPath)) {
throw new DirectoryNotFoundException("Target directory not found");
} else {
foreach (string file in intermediateBackupLocation) {
FileInfo info = new FileInfo(file);
string destFile = Path.Combine(targetPath, info.Name);
File.Copy(info.FullName, destFile, true);
}
}
Console.ForegroundColor = ConsoleColor.Green;
return $"Copied everything successfully to {targetPath}";
} else {
Console.ForegroundColor = ConsoleColor.Red;
return "You have not configured a backup location!";
}
}
public static bool zipAllContentInBackupLocation(string finalZipName) {
string targetPath = getHomePath() + "/backup/compressed/";
if (!Directory.Exists(targetPath)) {
Directory.CreateDirectory(targetPath);
}
string sourcePath = "/tmp/backup/uncompressed/";
string targetZip = getHomePath() + "/backup/compressed/" + finalZipName;
if (!Directory.Exists("/tmp/backup/")) {
Directory.CreateDirectory("/tmp/backup/");
}
string newFinalZip = "/tmp/backup/" + finalZipName;
File.Delete(newFinalZip); // Delete residual zip's in tmp
ZipFile.CreateFromDirectory(sourcePath, newFinalZip);
if (File.Exists(targetZip)) {
if (!checkForIdenticalFile(targetZip, newFinalZip)) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"{finalZipName} is outdated");
File.Delete(targetZip);
if (File.Exists(newFinalZip)) {
File.Delete(newFinalZip);
zipAllContentInBackupLocation(finalZipName);
} else {
File.Move(newFinalZip, targetZip);
}
} else {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{finalZipName} is up to date");
}
} else {
ZipFile.CreateFromDirectory(sourcePath, targetZip);
}
if (File.Exists(targetZip)) {
return true;
} else {
return false;
}
}
public static bool zipPacmanDatabase() {
string pacmanDatabaseLocation = "/var/lib/pacman/local/";
string oldPacmanDatabaseZip = getHomePath() + "/backup/compressed/pacman-database.zip";
string newPacmanDatabaseZip = "/tmp/backup/compressed/pacman-database.zip";
if (!Directory.Exists("/tmp/backup/compressed/")) {
Directory.CreateDirectory("/tmp/backup/compressed/");
}
try {
if(checkForLckFile("/var/lib/pacman/") == false) { // Only creates the zip if the db.lck doesn't exist
makeDatabaseLock();
if (File.Exists(newPacmanDatabaseZip)) { // Delete residual pacman database in tmp
File.Delete(newPacmanDatabaseZip);
deleteDatabaseLock(); // Delete previous created database lock
} else {
ZipFile.CreateFromDirectory(pacmanDatabaseLocation, newPacmanDatabaseZip); // If no zip exists create one
}
if (File.Exists(oldPacmanDatabaseZip)) {
if (!checkForIdenticalFile(oldPacmanDatabaseZip, newPacmanDatabaseZip)) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Pacman Database is outdated");
File.Delete(oldPacmanDatabaseZip);
File.Move(newPacmanDatabaseZip, oldPacmanDatabaseZip);
} else {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Pacman Database is up to date");
File.Delete(newPacmanDatabaseZip);
}
} else {
if (!File.Exists(newPacmanDatabaseZip)) {
ZipFile.CreateFromDirectory(pacmanDatabaseLocation, newPacmanDatabaseZip); // Create the zip in tmp
}
File.Move(newPacmanDatabaseZip, oldPacmanDatabaseZip);
}
} else {
throw new ApplicationException("db.lck exists. Please try again later.");
}
} catch (Exception e) {
//deleteDatabaseLock(); // Bad practice. Only for debug purpose!
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
}
if (File.Exists(oldPacmanDatabaseZip)) {
deleteDatabaseLock();
return true;
} else {
return false;
}
}
private static bool checkForIdenticalFile(string existingFilePath, string newFilePath) {
byte[] existingFile = File.ReadAllBytes(existingFilePath);
byte[] newFile = File.ReadAllBytes(newFilePath);
if (existingFile.Length == newFile.Length) {
for (int i=0; i < existingFile.Length; i++) {
if (existingFile[i] != newFile[i]) {
return false;
}
}
return true;
}
return false;
}
private static bool checkForLckFile(string folderToCheck) {
if (Directory.GetFiles(folderToCheck, "*.lck").Length == 1) {
return true; // lck file exists
} else {
return false; // lck file doesn't exists
}
}
private static void makeDatabaseLock() {
var psi = new ProcessStartInfo
{
FileName = "/bin/bash",
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = string.Format("-c \"cd /var/lib/pacman/ && sudo touch db.lck && sudo chmod 000 db.lck\"")
};
using (var p = Process.Start(psi))
{
if (p != null)
{
var strOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
}
}
private static void deleteDatabaseLock() {
var psi = new ProcessStartInfo
{
FileName = "/bin/bash",
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = string.Format("-c \"cd /var/lib/pacman/ && sudo rm -f db.lck\"")
};
using (var p = Process.Start(psi))
{
if (p != null)
{
var strOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
}
}
}

31
justfile Normal file
View File

@ -0,0 +1,31 @@
default:
@just --list
project_name := `printf '%s\n' "${PWD##*/}"`
uppercase_project_name := capitalize(project_name)
setup:
@mkdir src
@dotnet new sln --name src/{{project_name}}
@dotnet new classlib -o src/{{uppercase_project_name}}
@dotnet new xunit -o src/{{uppercase_project_name}}.Tests
@dotnet sln add src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj
@dotnet sln add src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj
@dotnet add src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj reference src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj
run:
@dotnet run
build:
@dotnet build src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj
@dotnet build src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj
publish:
@dotnet publish --configuration Release src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj
format:
@dotnet format src/{{uppercase_project_name}}
@dotnet format src/{{uppercase_project_name}}.Tests
test: build
@dotnet test src/{{uppercase_project_name}}.Tests

View File

@ -0,0 +1,10 @@
namespace Update_csharp.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>Update_csharp.Tests</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1 @@
global using Xunit;

View File

@ -0,0 +1,128 @@
public class Program {
static void Main(string[] args) {
var exitCommand = "nothing";
string backupLocation = "/home/rene/";
var pacmanWrapper = "yay";
var backupAmount = 2;
if (args.Length == 0) {
beforeUpdate();
Console.WriteLine(pacmanWrapper);
afterUpdate();
return;
}
string arg = args[0].TrimStart('-');;
if (args[0].StartsWith("--")) {
switch (arg) {
case "help":
Console.WriteLine(help());
break;
case "preview":
// need to execute 'yay -Sy && yay -Qu'
break;
case "version":
Console.WriteLine("version");
break;
case "backup":
beforeUpdate();
break;
default:
return;
}
return;
}
beforeUpdate();
foreach (var option in arg) {
switch (option) {
case 'f':
// flatpak update -u --noninteractive && flatpak list > "$TMP"/after-backup_"$DATE"/flatpak-after.txt
Console.WriteLine("flatpak update");
break;
case 'p':
// "$PACMAN_WRAPPER" && pacman -Qq > "$TMP"/after-backup_"$DATE"/pacman-after.txt
Console.WriteLine(pacmanWrapper);
break;
case 'a':
// 'p' && 'f'
Console.WriteLine("flatpak update && " + pacmanWrapper);
break;
case 'g':
if (args[0].TrimStart('-') == "g") {
Console.WriteLine(pacmanWrapper);
}
exitCommand = "systemctl -i poweroff";
break;
case 'r':
if (args[0].TrimStart('-') == "r") {
Console.WriteLine(pacmanWrapper);
}
exitCommand = "systemctl -i reboot";
break;
default:
help();
return;
}
}
afterUpdate();
void readConfig() {
backupLocation = "/mnt/Hephaistos/update/";
pacmanWrapper = "yay";
backupAmount = 2;
if (!Directory.Exists(backupLocation)) {
Console.WriteLine("update-csharp: Backup location does not exist or cannot be reached");
return;
}
}
//find the amount of backups in the backupLocation and delete the oldest
void deleteOldestBackup() {
Console.WriteLine(backupAmount);
}
void beforeUpdate() {
readConfig();
deleteOldestBackup();
Update.backup("before");
Update.zipPacmanDatabase();
Update.copyToBackupLocation(backupLocation);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("pre-backup complete");
Console.ResetColor();
}
void afterUpdate() {
Update.backup("after");
Update.copyToBackupLocation(backupLocation);
Directory.Delete("/tmp/update", true);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("after-backup complete");
Console.ResetColor();
Console.WriteLine(exitCommand); // execute exitCommand
}
string help() {
string helpMessage = "Usage: update [OPTION]\n";
helpMessage += "\noptions:\n";
helpMessage += "no flag same as -p\n";
helpMessage += "-f updates using flatpak update only\n";
helpMessage += "-p updates using a pacman-wrapper only\n";
helpMessage += "-a updates using flatpak update and a pacman-wrapper\n";
helpMessage += "-g shutdowns the computer afterwards\n";
helpMessage += "-r reboots the computer afterwards\n";
helpMessage += "--help displays this message\n";
helpMessage += "--preview shows a preview of which pkg's can be updated\n";
helpMessage += "--version prints out the version number\n";
helpMessage += "--backup just does the pre-backup without updating";
return helpMessage;
}
}
}

View File

@ -3,14 +3,17 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>Update_csharp</RootNamespace>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>true</PublishTrimmed>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Update-csharp.Tests\Update-csharp.Tests.csproj" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.ZipFile" />
</ItemGroup>

115
src/Update-csharp/Update.cs Normal file
View File

@ -0,0 +1,115 @@
using System.Diagnostics;
using System.IO.Compression;
public class Update {
static string tmpDirectory = "/tmp/update/";
public static void backup(string mode) {
string backupDir;
string pkgList;
string flatpakList;
List<string> filesToCopy = new List<string>();
string zipName;
switch (mode) {
case "before":
backupDir = tmpDirectory + "beforeBackup";
pkgList = backupDir + "pacman-pre.txt";
flatpakList = backupDir + "flatpak-pre.txt";
filesToCopy.Add("/etc/fstab");
filesToCopy.Add("/etc/makepkg.conf");
zipName = tmpDirectory + "beforeBackup.zip";
break;
case "after":
backupDir = tmpDirectory + "afterBackup";
pkgList = backupDir + "pacman-after.txt";
flatpakList = backupDir + "flatpak-after.txt";
zipName = tmpDirectory + "afterBackup.zip";
break;
default:
throw new ArgumentException("No valid mode was given. Valid modes are before and after");
}
if (Directory.Exists(tmpDirectory)) {
Directory.Delete(tmpDirectory, true);
}
Directory.CreateDirectory(backupDir);
foreach (var file in filesToCopy) {
FileInfo info = new FileInfo(file);
string destFile = Path.Combine(backupDir, info.Name);
File.Copy(info.FullName, destFile);
}
ZipFile.CreateFromDirectory(backupDir, zipName);
Directory.Delete(backupDir, true);
}
public static void copyToBackupLocation(string backupLocation) {
foreach (var zip in Directory.GetFiles(tmpDirectory)) {
FileInfo info = new FileInfo(zip);
string destFile = Path.Combine(backupLocation, info.Name);
File.Copy(info.FullName, destFile, true);
}
}
public static void zipPacmanDatabase() {
string pacmanDatabaseLocation = "/var/lib/pacman/local";
string zipName = tmpDirectory + "pacmanDatabase.zip";
if(dbLockExists()) {
throw new ApplicationException("db.lck exists. Please try again later.");
}
try {
lockDatabase();
ZipFile.CreateFromDirectory(pacmanDatabaseLocation, zipName);
} catch (Exception e) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
} finally {
unlockDatabase();
}
}
private static bool dbLockExists() {
if (File.Exists("/var/lib/pacman/db.lck")) {
return true;
}
return false;
}
private static void lockDatabase() {
var psi = new ProcessStartInfo
{
FileName = "/bin/bash",
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = string.Format("-c \"cd /var/lib/pacman/ && sudo touch db.lck && sudo chmod 000 db.lck\"")
};
using (var p = Process.Start(psi))
{
if (p != null)
{
var strOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
}
}
private static void unlockDatabase() {
var psi = new ProcessStartInfo {
FileName = "/bin/bash",
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = string.Format("-c \"cd /var/lib/pacman/ && sudo rm -f db.lck\"")
};
using (var p = Process.Start(psi)) {
if (p != null) {
var strOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
}
}
}

28
src/update-csharp.sln Normal file
View File

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update-csharp", "Update-csharp\Update-csharp.csproj", "{6FE9BE72-7118-4CC5-AC47-20F5E145FA67}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update-csharp.Tests", "Update-csharp.Tests\Update-csharp.Tests.csproj", "{F66860FA-9516-458E-87F3-245AC885AB96}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6FE9BE72-7118-4CC5-AC47-20F5E145FA67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6FE9BE72-7118-4CC5-AC47-20F5E145FA67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6FE9BE72-7118-4CC5-AC47-20F5E145FA67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6FE9BE72-7118-4CC5-AC47-20F5E145FA67}.Release|Any CPU.Build.0 = Release|Any CPU
{F66860FA-9516-458E-87F3-245AC885AB96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F66860FA-9516-458E-87F3-245AC885AB96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F66860FA-9516-458E-87F3-245AC885AB96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F66860FA-9516-458E-87F3-245AC885AB96}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal