diff --git a/ci/Monitor.Tar/Monitor.Tar.csproj b/ci/Monitor.Tar/Monitor.Tar.csproj
new file mode 100644
index 0000000..238f157
--- /dev/null
+++ b/ci/Monitor.Tar/Monitor.Tar.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
diff --git a/ci/Monitor.Tar/Program.cs b/ci/Monitor.Tar/Program.cs
new file mode 100644
index 0000000..a66e340
--- /dev/null
+++ b/ci/Monitor.Tar/Program.cs
@@ -0,0 +1,38 @@
+using System.Formats.Tar;
+using System.IO.Compression;
+
+if (args.Length < 3)
+{
+ Console.Error.WriteLine("usage: Monitor.Tar ...");
+ return 2;
+}
+
+string sourceDirectory = Path.GetFullPath(args[0]);
+string archivePath = Path.GetFullPath(args[1]);
+HashSet executables = args.Skip(2).Select(path => path.Replace('\\', '/')).ToHashSet(StringComparer.Ordinal);
+UnixFileMode executableMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
+ UnixFileMode.GroupRead | UnixFileMode.GroupExecute |
+ UnixFileMode.OtherRead | UnixFileMode.OtherExecute;
+UnixFileMode regularMode = UnixFileMode.UserRead | UnixFileMode.UserWrite |
+ UnixFileMode.GroupRead | UnixFileMode.OtherRead;
+
+Directory.CreateDirectory(Path.GetDirectoryName(archivePath)!);
+using FileStream fileStream = File.Create(archivePath);
+using GZipStream gzipStream = new(fileStream, CompressionLevel.Optimal, leaveOpen: true);
+using TarWriter writer = new(gzipStream, TarEntryFormat.Pax, leaveOpen: true);
+
+foreach (string filePath in Directory.EnumerateFiles(sourceDirectory, "*", SearchOption.AllDirectories))
+{
+ string relativePath = Path.GetRelativePath(sourceDirectory, filePath).Replace('\\', '/');
+ PaxTarEntry entry = new(TarEntryType.RegularFile, relativePath)
+ {
+ Mode = executables.Contains(relativePath) ? executableMode : regularMode,
+ DataStream = File.OpenRead(filePath)
+ };
+ using (entry.DataStream)
+ {
+ writer.WriteEntry(entry);
+ }
+}
+
+return 0;
diff --git a/ci/package-cross-platform.ps1 b/ci/package-cross-platform.ps1
index dc45eea..ba44f20 100644
--- a/ci/package-cross-platform.ps1
+++ b/ci/package-cross-platform.ps1
@@ -15,31 +15,21 @@ if ($version -ne $projectVersion) {
throw "Tag version $version does not match project version $projectVersion."
}
-Add-Type -AssemblyName System.Formats.Tar
+$tarProject = Join-Path $PSScriptRoot 'Monitor.Tar\Monitor.Tar.csproj'
+$tarTool = Join-Path $PSScriptRoot 'Monitor.Tar\bin\Release\net10.0\Monitor.Tar.dll'
+& dotnet build $tarProject -c Release --nologo
+if ($LASTEXITCODE -ne 0) {
+ throw 'Could not build the cross-platform tar helper.'
+}
$giteaBase = if ($env:GITEA_BASE) { $env:GITEA_BASE.TrimEnd('/') } else { 'http://localhost:3100' }
$netrc = Join-Path $env:TEMP ("monitor-gitea-$([guid]::NewGuid().ToString('N')).netrc")
$giteaHost = ([Uri]$giteaBase).Host
Set-Content -LiteralPath $netrc -Value "machine $giteaHost login justme password $($env:GITEA_TOKEN)" -Encoding ascii -NoNewline
function New-TarGzArchive([string]$SourceDirectory, [string]$ArchivePath, [string[]]$Executables) {
- $tarFile = [IO.File]::Create($ArchivePath)
- $gzip = [IO.Compression.GZipStream]::new($tarFile, [IO.Compression.CompressionLevel]::Optimal, $true)
- $writer = [System.Formats.Tar.TarWriter]::new($gzip, [System.Formats.Tar.TarEntryFormat]::Pax, $true)
- $executableMode = [System.IO.UnixFileMode]::UserRead -bor [System.IO.UnixFileMode]::UserWrite -bor [System.IO.UnixFileMode]::UserExecute -bor [System.IO.UnixFileMode]::GroupRead -bor [System.IO.UnixFileMode]::GroupExecute -bor [System.IO.UnixFileMode]::OtherRead -bor [System.IO.UnixFileMode]::OtherExecute
- $regularMode = [System.IO.UnixFileMode]::UserRead -bor [System.IO.UnixFileMode]::UserWrite -bor [System.IO.UnixFileMode]::GroupRead -bor [System.IO.UnixFileMode]::OtherRead
- try {
- foreach ($file in Get-ChildItem -LiteralPath $SourceDirectory -File -Recurse) {
- $relativePath = [IO.Path]::GetRelativePath($SourceDirectory, $file.FullName).Replace('\', '/')
- $entry = [System.Formats.Tar.PaxTarEntry]::new([System.Formats.Tar.TarEntryType]::RegularFile, $relativePath)
- $entry.Mode = if ($Executables -contains $relativePath) { $executableMode } else { $regularMode }
- $entry.DataStream = [IO.File]::OpenRead($file.FullName)
- try { $writer.WriteEntry($entry) } finally { $entry.DataStream.Dispose() }
- }
- }
- finally {
- $writer.Dispose()
- $gzip.Dispose()
- $tarFile.Dispose()
+ & dotnet $tarTool $SourceDirectory $ArchivePath @Executables
+ if ($LASTEXITCODE -ne 0) {
+ throw "Could not create tar archive $ArchivePath."
}
}