edge-install/ReleaseData/ReleaseData.cs
Robert Morrison 60e06b2144
Another bad commit
Lots of changes here...

- Removed code using RecursivExtractor due to bad usage of /tmp
  Note: the code that used RecursiveExtractor may not have been in the
  previous commit
- Created _functioning_ implementation of DebUnpacker
- Restructured Project layout.

TODO:
- Possibly rewrite how the DebUnpacker works to have the file contents
  part of an entry.
- Further restructuring and refactoring to make the code a little
  neater.
- Add code for the final unpack steps Un-XZ -> untar -> write to disk
- Add code to install
- Add code for modified post-install pre-remove etc..
  This is kinda needed to ensure proper system integration of new
  packages.
2023-06-08 04:36:31 +01:00

129 lines
4.5 KiB
C#

// TODO: Refactor to be more like Package code. This is messy
// Substring should be replaced with a .split(": ")
// Also Directly assign to the values instead of using temporary variables.
using Serilog;
namespace EdgeInstaller;
public sealed partial class ReleaseData
{
public DateTime Date { get; private set; }
public List<string> Architectures { get; private set; }
public List<PackageFile> PackageFiles { get; private set; }
public ReleaseData(string Release)
{
Log.Information("Creating new ReleaseData object");
if (Release == "")
{
throw new ArgumentException(
message: "Empty release provided to ReleaseData",
paramName: "Release");
}
// Pull release apart
List<string> ReleaseLines = Release.Split("\n", StringSplitOptions.RemoveEmptyEntries).ToList();
Log.Debug("ReleaseLines = {@releaselines}", ReleaseLines);
Date = DateTime.Parse(
ReleaseLines.Where(
Line => Line.Contains("Date:"))
.First()
.Split(": ")
.Last()
);
// Safely find and add the possible architectures
string ArchitecturesLine =
ReleaseLines.Where(
Line => Line.Contains("Architectures:"))
.FirstOrDefault("");
Log.Debug("Arch line: {archline}", ArchitecturesLine);
if (ArchitecturesLine == "")
{
throw _lineFormatException("architectures");
}
Architectures = ArchitecturesLine
.Split(": ").Last() // We only want items after the label
.Split(" ", StringSplitOptions.RemoveEmptyEntries)
.ToList();
Log.Debug("Extracted architectures {@architectures}", Architectures);
/*
* Package files are grabbed here. It is worth noting that empty files
* are removed (along with the .gz versions) this avoids displaying impossible
* options to the user
*/
// First split the Lines to only get the file data
// NOTE: This is probably the cleanest way to do this.
List<string> SHA256FileData = ReleaseLines.Where((_, index) =>
index > ReleaseLines.IndexOf("SHA256:") &&
index < ReleaseLines.IndexOf("SHA512:")
).ToList();
Log.Debug("SHA256FileData = {@data}", SHA256FileData);
// Now process these lines to get the data we need
List<PackageFile> releasePackageFiles = new();
foreach (var fileData in SHA256FileData)
{
Log.Debug("fileData = {filedata}", fileData);
string[] splitData = fileData.Split(' ', StringSplitOptions.RemoveEmptyEntries);
Log.Debug("splitData = {@splitData}", splitData);
string checksum = splitData[0];
int size = int.Parse(splitData[1]);
string filename = splitData[2];
releasePackageFiles.Add(new PackageFile(filename, checksum, size));
}
// remove any empty files (and the .gz version of them)
// NOTE: Empty gz files still have a size due to headers
// WARN: The `.ToList` is essential to ensure we COPY the list and not use references to the original items
var emptyFiles = releasePackageFiles
.Where(package => package.size == 0)
.ToList();
foreach (var file in emptyFiles)
{
releasePackageFiles
.RemoveAll(package =>
package.filename == file.filename ||
package.filename == $"{file.filename}.gz"
);
}
PackageFiles = releasePackageFiles;
// After removing the empty files we need to make sure that we remove the architectures that don't have any files
foreach (var arch in Architectures.ToList())
/*
* WARN: `.ToList` is used again to copy the list so we can iterate and operate
* on the list at the same time. This could be unsafe, but in this scenario
* should never cause any issues.
*/
{
if (PackageFiles.Where(file => file.filename.Contains(arch)).Count() == 0)
{
Architectures.Remove(arch);
}
}
}
private static FormatException _lineFormatException(string field)
{
return new FormatException($"Release does not contain a valid {field} line");
}
}
public record PackageFile(string filename, string Checksum, int size);