mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Create SquirrelCli project, merge SyncReleases into it, refactor command line args
This commit is contained in:
100
Squirrel.sln
100
Squirrel.sln
@@ -11,8 +11,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Setup", "src\Setup\Setup.vc
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Update", "src\Update\Update.csproj", "{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Update", "src\Update\Update.csproj", "{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyncReleases", "src\SyncReleases\SyncReleases.csproj", "{EB521191-1EBF-4D06-8541-ED192E2EE378}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionLevel", "SolutionLevel", "{ED657D2C-F8A0-4012-A64F-7367D41BE4D2}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionLevel", "SolutionLevel", "{ED657D2C-F8A0-4012-A64F-7367D41BE4D2}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
@@ -26,6 +24,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WriteZipToSetup", "src\Writ
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StubExecutable", "src\StubExecutable\StubExecutable.vcxproj", "{C028DB2A-E7C5-4232-8C22-D5FBA2176136}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StubExecutable", "src\StubExecutable\StubExecutable.vcxproj", "{C028DB2A-E7C5-4232-8C22-D5FBA2176136}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SquirrelCli", "src\SquirrelCli\SquirrelCli.csproj", "{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
CIBuild|Any CPU = CIBuild|Any CPU
|
CIBuild|Any CPU = CIBuild|Any CPU
|
||||||
@@ -244,54 +244,6 @@ Global
|
|||||||
{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x64.Build.0 = Release|Any CPU
|
{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x86.ActiveCfg = Release|Any CPU
|
{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x86.Build.0 = Release|Any CPU
|
{1EEBACBC-6982-4696-BD4E-899ED0AC6CD2}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x64.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.CIBuild|x86.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x64.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Coverage|x86.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Mono Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{EB521191-1EBF-4D06-8541-ED192E2EE378}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Any CPU.ActiveCfg = Debug|Win32
|
{4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Any CPU.ActiveCfg = Debug|Win32
|
||||||
{4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Any CPU.Build.0 = Debug|Win32
|
{4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Any CPU.Build.0 = Debug|Win32
|
||||||
{4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Mixed Platforms.ActiveCfg = Release|Win32
|
{4D3C8B70-075D-48A5-9FF3-EDB87347B136}.CIBuild|Mixed Platforms.ActiveCfg = Release|Win32
|
||||||
@@ -386,6 +338,54 @@ Global
|
|||||||
{C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x64.Build.0 = Release|x64
|
{C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x64.Build.0 = Release|x64
|
||||||
{C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x86.ActiveCfg = Release|Win32
|
{C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x86.ActiveCfg = Release|Win32
|
||||||
{C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x86.Build.0 = Release|Win32
|
{C028DB2A-E7C5-4232-8C22-D5FBA2176136}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|x64.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.CIBuild|x86.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|x64.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Coverage|x86.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Mono Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{19E8EBF5-0277-422F-BF49-C66D9DBA5AA4}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
10
build.ps1
10
build.ps1
@@ -20,11 +20,11 @@ foreach ($Folder in $Folders) {
|
|||||||
|
|
||||||
# Build single-exe packaged projects
|
# Build single-exe packaged projects
|
||||||
dotnet publish -v minimal -c Release "$PSScriptRoot\src\Update\Update.csproj" -o "$Out"
|
dotnet publish -v minimal -c Release "$PSScriptRoot\src\Update\Update.csproj" -o "$Out"
|
||||||
dotnet publish -v minimal -c Release "$PSScriptRoot\src\SyncReleases\SyncReleases.csproj" -o "$Out"
|
dotnet publish -v minimal -c Release "$PSScriptRoot\src\SquirrelCli\SquirrelCli.csproj" -o "$Out"
|
||||||
|
|
||||||
# Copy over all files we need
|
# Copy over all files we need
|
||||||
Move-Item "$Out\Update.exe" -Destination "$Out\Squirrel.exe"
|
# Move-Item "$Out\Update.exe" -Destination "$Out\Squirrel.exe"
|
||||||
Move-Item "$Out\Update.com" -Destination "$Out\Squirrel.com"
|
# Move-Item "$Out\Update.com" -Destination "$Out\Squirrel.com"
|
||||||
|
|
||||||
# Move-Item "$Out\Update.pdb" -Destination "$Out\Squirrel.pdb"
|
# Move-Item "$Out\Update.pdb" -Destination "$Out\Squirrel.pdb"
|
||||||
# New-Item -Path "$Out\lib" -ItemType "directory" | Out-Null
|
# New-Item -Path "$Out\lib" -ItemType "directory" | Out-Null
|
||||||
@@ -38,7 +38,9 @@ Copy-Item "$In\Win32\WriteZipToSetup.pdb" -Destination "$Out"
|
|||||||
|
|
||||||
Copy-Item -Path "$PSScriptRoot\vendor\7zip\*" -Destination "$Out" -Recurse
|
Copy-Item -Path "$PSScriptRoot\vendor\7zip\*" -Destination "$Out" -Recurse
|
||||||
Copy-Item -Path "$PSScriptRoot\vendor\wix\*" -Destination "$Out" -Recurse
|
Copy-Item -Path "$PSScriptRoot\vendor\wix\*" -Destination "$Out" -Recurse
|
||||||
Copy-Item "$PSScriptRoot\.nuget\NuGet.exe" -Destination "$Out"
|
Copy-Item "$PSScriptRoot\vendor\NuGet.exe" -Destination "$Out"
|
||||||
|
Copy-Item "$PSScriptRoot\vendor\rcedit.exe" -Destination "$Out"
|
||||||
|
Copy-Item "$PSScriptRoot\vendor\signtool.exe" -Destination "$Out"
|
||||||
|
|
||||||
Remove-Item "$Out\*.pdb"
|
Remove-Item "$Out\*.pdb"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Squirrel.Update
|
namespace Squirrel.Lib
|
||||||
{
|
{
|
||||||
internal static class AuthenticodeTools
|
internal static class AuthenticodeTools
|
||||||
{
|
{
|
||||||
193
src/Squirrel/Lib/ValidatedOptionSet.cs
Normal file
193
src/Squirrel/Lib/ValidatedOptionSet.cs
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Mono.Options;
|
||||||
|
|
||||||
|
namespace Squirrel.Lib
|
||||||
|
{
|
||||||
|
internal class OptionValidationException : Exception
|
||||||
|
{
|
||||||
|
public OptionValidationException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionValidationException(string propertyName, string message) : base($"Argument '{propertyName}': {message}")
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract class ValidatedOptionSet : OptionSet
|
||||||
|
{
|
||||||
|
protected virtual bool IsNullOrDefault(string propertyName)
|
||||||
|
{
|
||||||
|
var p = this.GetType().GetProperty(propertyName);
|
||||||
|
object argument = p.GetValue(this, null);
|
||||||
|
|
||||||
|
// deal with normal scenarios
|
||||||
|
if (argument == null) return true;
|
||||||
|
|
||||||
|
// deal with non-null nullables
|
||||||
|
Type methodType = argument.GetType();
|
||||||
|
if (Nullable.GetUnderlyingType(methodType) != null) return false;
|
||||||
|
|
||||||
|
// deal with boxed value types
|
||||||
|
Type argumentType = argument.GetType();
|
||||||
|
if (argumentType.IsValueType && argumentType != methodType) {
|
||||||
|
object obj = Activator.CreateInstance(argument.GetType());
|
||||||
|
return obj.Equals(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void IsRequired(params string[] propertyNames)
|
||||||
|
{
|
||||||
|
foreach (var property in propertyNames) {
|
||||||
|
IsRequired(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void IsRequired(string propertyName)
|
||||||
|
{
|
||||||
|
if (IsNullOrDefault(propertyName))
|
||||||
|
throw new OptionValidationException($"Argument '{propertyName}' is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void IsValidFile(string propertyName)
|
||||||
|
{
|
||||||
|
var p = this.GetType().GetProperty(propertyName);
|
||||||
|
var path = p.GetValue(this, null) as string;
|
||||||
|
if (path != null)
|
||||||
|
if (!File.Exists(path))
|
||||||
|
throw new OptionValidationException($"Argument '{propertyName}': Expected file to exist at this location but no file was found");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void IsValidUrl(string propertyName)
|
||||||
|
{
|
||||||
|
var p = this.GetType().GetProperty(propertyName);
|
||||||
|
var val = p.GetValue(this, null) as string;
|
||||||
|
if (val != null)
|
||||||
|
if (!Utility.IsHttpUrl(val))
|
||||||
|
throw new OptionValidationException(propertyName, "Must start with http or https and be a valid URI.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Validate();
|
||||||
|
|
||||||
|
public virtual void WriteOptionDescriptions()
|
||||||
|
{
|
||||||
|
WriteOptionDescriptions(Console.Out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract class CommandAction
|
||||||
|
{
|
||||||
|
public string Command { get; protected set; }
|
||||||
|
public string Description { get; protected set; }
|
||||||
|
public abstract void Execute(IEnumerable<string> args);
|
||||||
|
public abstract void PrintHelp();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CommandAction<T> : CommandAction where T : ValidatedOptionSet, new()
|
||||||
|
{
|
||||||
|
public T Options { get; }
|
||||||
|
public Action<T> Action { get; }
|
||||||
|
|
||||||
|
public CommandAction(string command, string description, T options, Action<T> action)
|
||||||
|
{
|
||||||
|
Command = command;
|
||||||
|
Description = description;
|
||||||
|
Options = options;
|
||||||
|
Action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute(IEnumerable<string> args)
|
||||||
|
{
|
||||||
|
Options.Parse(args);
|
||||||
|
Options.Validate();
|
||||||
|
Action(Options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PrintHelp()
|
||||||
|
{
|
||||||
|
Options.WriteOptionDescriptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CommandSet : List<CommandAction>
|
||||||
|
{
|
||||||
|
//public CommandSet() : base(StringComparer.InvariantCultureIgnoreCase) { }
|
||||||
|
|
||||||
|
public void Add<T>(string command, string description, T options, Action<T> action) where T : ValidatedOptionSet, new()
|
||||||
|
{
|
||||||
|
this.Add(new CommandAction<T>(command, description, options, action));
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Execute(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 0)
|
||||||
|
throw new OptionValidationException("Must specify a command to execute.");
|
||||||
|
|
||||||
|
var combined = String.Join(" ", args);
|
||||||
|
CommandAction cmd = null;
|
||||||
|
|
||||||
|
foreach (var k in this.OrderByDescending(k => k.Command.Length)) {
|
||||||
|
if (combined.StartsWith(k.Command, StringComparison.InvariantCultureIgnoreCase)) {
|
||||||
|
cmd = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd == null)
|
||||||
|
throw new OptionValidationException($"Command was not specified or does not exist.");
|
||||||
|
|
||||||
|
cmd.Execute(combined.Substring(cmd.Command.Length).Split(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void WriteHelp()
|
||||||
|
{
|
||||||
|
var exeName = Path.GetFileName(AssemblyRuntimeInfo.EntryExePath);
|
||||||
|
Console.WriteLine($"Usage: {exeName} [command] [options]");
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.WriteLine("Commands:");
|
||||||
|
|
||||||
|
var array = this.ToArray();
|
||||||
|
for (var i = 0; i < array.Length; i++) {
|
||||||
|
var c = array[i];
|
||||||
|
|
||||||
|
// print command name + desc
|
||||||
|
Console.WriteLine();
|
||||||
|
Utility.ConsoleWriteWithColor(c.Command, ConsoleColor.Blue);
|
||||||
|
if (!String.IsNullOrWhiteSpace(c.Description))
|
||||||
|
Console.Write(": " + c.Description);
|
||||||
|
|
||||||
|
|
||||||
|
//Console.Write(c.Command);
|
||||||
|
//if(String.IsNullOrWhiteSpace(c.Description))
|
||||||
|
// Console.WriteLine();
|
||||||
|
//else
|
||||||
|
// Console.WriteLine(": " + c.Description);
|
||||||
|
|
||||||
|
|
||||||
|
//Console.Write(c.);
|
||||||
|
|
||||||
|
// group similar command parameters together
|
||||||
|
if (i + 1 < array.Length) {
|
||||||
|
if (c.GetType() == array[i + 1].GetType()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
c.PrintHelp();
|
||||||
|
|
||||||
|
//Console.WriteLine();
|
||||||
|
//c.Value.WriteOptionDescriptions();
|
||||||
|
//Console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,3 +6,5 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: InternalsVisibleTo("Squirrel.Tests")]
|
[assembly: InternalsVisibleTo("Squirrel.Tests")]
|
||||||
[assembly: InternalsVisibleTo("Update")]
|
[assembly: InternalsVisibleTo("Update")]
|
||||||
[assembly: InternalsVisibleTo("SyncReleases")]
|
[assembly: InternalsVisibleTo("SyncReleases")]
|
||||||
|
[assembly: InternalsVisibleTo("SquirrelCli")]
|
||||||
|
[assembly: InternalsVisibleTo("Squirrel")]
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<Title>Squirrel</Title>
|
<Title>Squirrel</Title>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>9</LangVersion>
|
<LangVersion>9</LangVersion>
|
||||||
|
<AssemblyName>Squirrel.Lib</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -341,16 +341,24 @@ namespace Squirrel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string FindHelperExecutable(string toFind, IEnumerable<string> additionalDirs = null)
|
public static string FindHelperExecutable(string toFind, IEnumerable<string> additionalDirs = null, bool throwWhenNotFound = false)
|
||||||
{
|
{
|
||||||
|
if (File.Exists(toFind))
|
||||||
|
return Path.GetFullPath(toFind);
|
||||||
|
|
||||||
additionalDirs = additionalDirs ?? Enumerable.Empty<string>();
|
additionalDirs = additionalDirs ?? Enumerable.Empty<string>();
|
||||||
var dirs = (new[] { AppContext.BaseDirectory, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) })
|
var dirs = (new[] { AppContext.BaseDirectory, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) })
|
||||||
.Concat(additionalDirs ?? Enumerable.Empty<string>());
|
.Concat(additionalDirs ?? Enumerable.Empty<string>()).Select(Path.GetFullPath);
|
||||||
|
|
||||||
var exe = @".\" + toFind;
|
var exe = @".\" + toFind;
|
||||||
return dirs
|
var result = dirs
|
||||||
.Select(x => Path.Combine(x, toFind))
|
.Select(x => Path.Combine(x, toFind))
|
||||||
.FirstOrDefault(x => File.Exists(x)) ?? exe;
|
.FirstOrDefault(x => File.Exists(x));
|
||||||
|
|
||||||
|
if (result == null && throwWhenNotFound)
|
||||||
|
throw new Exception($"Could not find helper '{exe}'.");
|
||||||
|
|
||||||
|
return result ?? exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string find7Zip()
|
static string find7Zip()
|
||||||
@@ -668,6 +676,47 @@ namespace Squirrel
|
|||||||
return This.Log().LogIfThrows(LogLevel.Error, message, block);
|
return This.Log().LogIfThrows(LogLevel.Error, message, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void WarnIfThrows(this IFullLogger This, Action block, string message = null)
|
||||||
|
{
|
||||||
|
This.LogIfThrows(LogLevel.Warn, message, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task WarnIfThrows(this IFullLogger This, Func<Task> block, string message = null)
|
||||||
|
{
|
||||||
|
return This.LogIfThrows(LogLevel.Warn, message, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T> WarnIfThrows<T>(this IFullLogger This, Func<Task<T>> block, string message = null)
|
||||||
|
{
|
||||||
|
return This.LogIfThrows(LogLevel.Warn, message, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ErrorIfThrows(this IFullLogger This, Action block, string message = null)
|
||||||
|
{
|
||||||
|
This.LogIfThrows(LogLevel.Error, message, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task ErrorIfThrows(this IFullLogger This, Func<Task> block, string message = null)
|
||||||
|
{
|
||||||
|
return This.LogIfThrows(LogLevel.Error, message, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T> ErrorIfThrows<T>(this IFullLogger This, Func<Task<T>> block, string message = null)
|
||||||
|
{
|
||||||
|
return This.LogIfThrows(LogLevel.Error, message, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConsoleWriteWithColor(string text, ConsoleColor color)
|
||||||
|
{
|
||||||
|
var fc = Console.ForegroundColor;
|
||||||
|
var bc = Console.BackgroundColor;
|
||||||
|
Console.ForegroundColor = color;
|
||||||
|
Console.BackgroundColor = ConsoleColor.Black;
|
||||||
|
Console.Write(text);
|
||||||
|
Console.ForegroundColor = fc;
|
||||||
|
Console.BackgroundColor = bc;
|
||||||
|
}
|
||||||
|
|
||||||
static IFullLogger logger;
|
static IFullLogger logger;
|
||||||
static IFullLogger Log()
|
static IFullLogger Log()
|
||||||
{
|
{
|
||||||
|
|||||||
129
src/SquirrelCli/Options.cs
Normal file
129
src/SquirrelCli/Options.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Squirrel.Lib;
|
||||||
|
|
||||||
|
namespace SquirrelCli
|
||||||
|
{
|
||||||
|
internal abstract class BaseOptions : ValidatedOptionSet
|
||||||
|
{
|
||||||
|
public string releaseDir { get; private set; } = ".\\Releases";
|
||||||
|
public BaseOptions()
|
||||||
|
{
|
||||||
|
Add("r=|releaseDir=", "Release directory containing releasified packages", v => releaseDir = v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ReleasifyOptions : BaseOptions
|
||||||
|
{
|
||||||
|
public string package { get; set; }
|
||||||
|
public string splashImage { get; private set; }
|
||||||
|
public string iconPath { get; private set; }
|
||||||
|
public string signParams { get; private set; }
|
||||||
|
public string framework { get; private set; }
|
||||||
|
public bool noDelta { get; private set; }
|
||||||
|
public string baseUrl { get; private set; }
|
||||||
|
|
||||||
|
public ReleasifyOptions()
|
||||||
|
{
|
||||||
|
Add("p=|package=", "Path to a nuget package to releasify", v => package = v);
|
||||||
|
Add("s=|splashImage=", "Image to be displayed during installation (can be jpg, png, gif, etc)", v => splashImage = v);
|
||||||
|
Add("i=|iconPath=", "Ico file that will be used where possible", v => iconPath = v);
|
||||||
|
Add("n=|signParams=", "Sign the installer via SignTool.exe with the parameters given", v => signParams = v);
|
||||||
|
Add("f=|framework=", "Set the required .NET framework version, e.g. net461", v => framework = v);
|
||||||
|
Add("no-delta", "Don't generate delta packages to save time", v => noDelta = true);
|
||||||
|
Add("b=|baseUrl=", "Provides a base URL to prefix the RELEASES file packages with", v => baseUrl = v, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Validate()
|
||||||
|
{
|
||||||
|
IsValidFile(nameof(iconPath));
|
||||||
|
IsValidFile(nameof(splashImage));
|
||||||
|
IsValidUrl(nameof(baseUrl));
|
||||||
|
IsRequired(nameof(package));
|
||||||
|
IsValidFile(nameof(package));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PackOptions : ReleasifyOptions
|
||||||
|
{
|
||||||
|
public string packName { get; private set; }
|
||||||
|
public string packVersion { get; private set; }
|
||||||
|
public string packAuthors { get; private set; }
|
||||||
|
public string packDirectory { get; private set; }
|
||||||
|
|
||||||
|
public PackOptions()
|
||||||
|
{
|
||||||
|
Add("packName=", "desc", v => packName = v);
|
||||||
|
Add("packVersion=", "desc", v => packVersion = v);
|
||||||
|
Add("packAuthors=", "desc", v => packAuthors = v);
|
||||||
|
Add("packDirectory=", "desc", v => packDirectory = v);
|
||||||
|
|
||||||
|
// remove 'package' argument
|
||||||
|
Remove("package");
|
||||||
|
Remove("p");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Validate()
|
||||||
|
{
|
||||||
|
IsRequired(nameof(packName), nameof(packVersion), nameof(packAuthors), nameof(packDirectory));
|
||||||
|
IsValidFile(nameof(iconPath));
|
||||||
|
IsValidFile(nameof(splashImage));
|
||||||
|
IsValidUrl(nameof(baseUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SyncBackblazeOptions : BaseOptions
|
||||||
|
{
|
||||||
|
public string b2KeyId { get; private set; }
|
||||||
|
public string b2AppKey { get; private set; }
|
||||||
|
public string b2BucketId { get; private set; }
|
||||||
|
|
||||||
|
public SyncBackblazeOptions()
|
||||||
|
{
|
||||||
|
Add("b2BucketId=", "Id or name of the bucket in B2, S3, etc", v => b2BucketId = v);
|
||||||
|
Add("b2keyid=", "B2 Auth Key Id", v => b2KeyId = v);
|
||||||
|
Add("b2key=", "B2 Auth Key", v => b2AppKey = v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Validate()
|
||||||
|
{
|
||||||
|
IsRequired(nameof(b2KeyId), nameof(b2AppKey), nameof(b2BucketId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SyncHttpOptions : BaseOptions
|
||||||
|
{
|
||||||
|
public string url { get; private set; }
|
||||||
|
public string token { get; private set; }
|
||||||
|
|
||||||
|
public SyncHttpOptions()
|
||||||
|
{
|
||||||
|
Add("url=", "Url to the simple http folder where the releases are found", v => url = v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Validate()
|
||||||
|
{
|
||||||
|
IsRequired(nameof(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SyncGithubOptions : BaseOptions
|
||||||
|
{
|
||||||
|
public string repoUrl { get; private set; }
|
||||||
|
public string token { get; private set; }
|
||||||
|
|
||||||
|
public SyncGithubOptions()
|
||||||
|
{
|
||||||
|
Add("repoUrl=", "Url to the github repository (eg. 'https://github.com/myname/myrepo')", v => repoUrl = v);
|
||||||
|
Add("token=", "The oauth token to use as login credentials", v => token = v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Validate()
|
||||||
|
{
|
||||||
|
IsRequired(nameof(repoUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
407
src/SquirrelCli/Program.cs
Normal file
407
src/SquirrelCli/Program.cs
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Mono.Options;
|
||||||
|
using Squirrel;
|
||||||
|
using Squirrel.Json;
|
||||||
|
using Squirrel.Lib;
|
||||||
|
using Squirrel.NuGet;
|
||||||
|
using Squirrel.SimpleSplat;
|
||||||
|
using SquirrelCli.Sources;
|
||||||
|
|
||||||
|
namespace SquirrelCli
|
||||||
|
{
|
||||||
|
class Program : IEnableLogger
|
||||||
|
{
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
//var pg = new Program();
|
||||||
|
var commands = new CommandSet {
|
||||||
|
{ "releasify", "Take an existing nuget package and turn it into a Squirrel release", new ReleasifyOptions(), Releasify },
|
||||||
|
{ "pack", "Creates a nuget package from a folder and releasifies it in a single step", new PackOptions(), Pack },
|
||||||
|
{ "b2-down", "Download recent releases from BackBlaze B2", new SyncBackblazeOptions(), o => new BackblazeRepository(o).DownloadRecentPackages().Wait() },
|
||||||
|
{ "b2-up", "Upload releases to BackBlaze B2", new SyncBackblazeOptions(), o => new BackblazeRepository(o).UploadMissingPackages().Wait() },
|
||||||
|
{ "http-down", "Download recent releases from an HTTP source", new SyncHttpOptions(), o => new SimpleWebRepository(o).DownloadRecentPackages().Wait() },
|
||||||
|
//{ "http-up", "sync", new SyncHttpOptions(), o => new SimpleWebRepository(o).UploadMissingPackages().Wait() },
|
||||||
|
{ "github-down", "Download recent releases from GitHub", new SyncGithubOptions(), o => new GitHubRepository(o).DownloadRecentPackages().Wait() },
|
||||||
|
//{ "github-up", "sync", new SyncGithubOptions(), o => new GitHubRepository(o).UploadMissingPackages().Wait() },
|
||||||
|
};
|
||||||
|
|
||||||
|
var logger = new ConsoleLogger();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// check for help argument
|
||||||
|
bool help = false;
|
||||||
|
new OptionSet() { { "h|?|help", _ => help = true }, }.Parse(args);
|
||||||
|
if (help) {
|
||||||
|
commands.WriteHelp();
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
// parse cli and run command
|
||||||
|
SquirrelLocator.CurrentMutable.Register(() => logger, typeof(Squirrel.SimpleSplat.ILogger));
|
||||||
|
commands.Execute(args);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Console.WriteLine();
|
||||||
|
logger.Write(ex.ToString(), LogLevel.Error);
|
||||||
|
Console.WriteLine();
|
||||||
|
commands.WriteHelp();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
|
||||||
|
|
||||||
|
static string[] VendorDirs => new string[] {
|
||||||
|
Path.Combine(AssemblyRuntimeInfo.BaseDirectory, "..", "..", "..", "vendor")
|
||||||
|
};
|
||||||
|
static string BootstrapperPath => Utility.FindHelperExecutable("Setup.exe", throwWhenNotFound: true);
|
||||||
|
static string UpdatePath => Utility.FindHelperExecutable("Update.exe", throwWhenNotFound: true);
|
||||||
|
static string NugetPath => Utility.FindHelperExecutable("NuGet.exe", VendorDirs, throwWhenNotFound: true);
|
||||||
|
|
||||||
|
static void Pack(PackOptions options)
|
||||||
|
{
|
||||||
|
using (Utility.WithTempDirectory(out var tmpDir)) {
|
||||||
|
string nuspec = $@"
|
||||||
|
<?xml version=""1.0"" encoding=""utf-8""?>
|
||||||
|
<package>
|
||||||
|
<metadata>
|
||||||
|
<id>{options.packName}</id>
|
||||||
|
<title>{options.packName}</title>
|
||||||
|
<description>{options.packName}</description>
|
||||||
|
<authors>{options.packAuthors}</authors>
|
||||||
|
<version>0</version>
|
||||||
|
</metadata>
|
||||||
|
<files>
|
||||||
|
<file src=""**"" target=""lib\app\"" exclude=""*.pdb;*.nupkg;*.vshost.*""/>
|
||||||
|
</files>
|
||||||
|
</package>
|
||||||
|
".Trim();
|
||||||
|
var nuspecPath = Path.Combine(tmpDir, options.packName + ".nuspec");
|
||||||
|
File.WriteAllText(nuspecPath, nuspec);
|
||||||
|
|
||||||
|
var args = $"pack \"{nuspecPath}\" -BasePath \"{options.packDirectory}\" -OutputDirectory \"{tmpDir}\" -Version {options.packVersion}";
|
||||||
|
|
||||||
|
Log.Info($"Packing '{options.packDirectory}' into nupkg.");
|
||||||
|
var res = Utility.InvokeProcessAsync(NugetPath, args, CancellationToken.None).Result;
|
||||||
|
|
||||||
|
if (res.Item1 != 0)
|
||||||
|
throw new Exception($"Failed nuget pack (exit {res.Item1}): \r\n " + res.Item2);
|
||||||
|
|
||||||
|
var nupkgPath = Directory.EnumerateFiles(tmpDir).Where(f => f.EndsWith(".nupkg")).FirstOrDefault();
|
||||||
|
if (nupkgPath == null)
|
||||||
|
throw new Exception($"Failed to generate nupkg, unspecified error");
|
||||||
|
|
||||||
|
options.package = nupkgPath;
|
||||||
|
Releasify(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Releasify(ReleasifyOptions options)
|
||||||
|
{
|
||||||
|
var targetDir = options.releaseDir ?? Path.Combine(".", "Releases");
|
||||||
|
if (!Directory.Exists(targetDir)) {
|
||||||
|
Directory.CreateDirectory(targetDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameworkVersion = options.framework;
|
||||||
|
var signingOpts = options.signParams;
|
||||||
|
var package = options.package;
|
||||||
|
var baseUrl = options.baseUrl;
|
||||||
|
var generateDeltas = !options.noDelta;
|
||||||
|
var backgroundGif = options.splashImage;
|
||||||
|
var setupIcon = options.iconPath;
|
||||||
|
|
||||||
|
// validate that the provided "frameworkVersion" is supported by Setup.exe
|
||||||
|
if (!String.IsNullOrWhiteSpace(frameworkVersion)) {
|
||||||
|
var chkFrameworkResult = Utility.InvokeProcessAsync(BootstrapperPath, "--checkFramework " + frameworkVersion, CancellationToken.None).Result;
|
||||||
|
if (chkFrameworkResult.Item1 != 0) {
|
||||||
|
throw new ArgumentException($"Unsupported FrameworkVersion: '{frameworkVersion}'. {chkFrameworkResult.Item2}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy input package to target output directory
|
||||||
|
if (!package.EndsWith(".nupkg", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
throw new ArgumentException("package must be packed with nuget and end in '.nupkg'");
|
||||||
|
var di = new DirectoryInfo(targetDir);
|
||||||
|
File.Copy(package, Path.Combine(di.FullName, Path.GetFileName(package)), true);
|
||||||
|
|
||||||
|
var allNuGetFiles = di.EnumerateFiles()
|
||||||
|
.Where(x => x.Name.EndsWith(".nupkg", StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
|
var toProcess = allNuGetFiles.Where(x => !x.Name.Contains("-delta") && !x.Name.Contains("-full"));
|
||||||
|
var processed = new List<string>();
|
||||||
|
|
||||||
|
var releaseFilePath = Path.Combine(di.FullName, "RELEASES");
|
||||||
|
var previousReleases = new List<ReleaseEntry>();
|
||||||
|
if (File.Exists(releaseFilePath)) {
|
||||||
|
previousReleases.AddRange(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in toProcess) {
|
||||||
|
Log.Info("Creating release package: " + file.FullName);
|
||||||
|
|
||||||
|
var rp = new ReleasePackage(file.FullName);
|
||||||
|
rp.CreateReleasePackage(Path.Combine(di.FullName, rp.SuggestedReleaseFileName), contentsPostProcessHook: pkgPath => {
|
||||||
|
|
||||||
|
// create sub executable for all exe's in this package (except Squirrel!)
|
||||||
|
new DirectoryInfo(pkgPath).GetAllFilesRecursively()
|
||||||
|
.Where(x => x.Name.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Where(x => !x.Name.Contains("squirrel.exe", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Where(x => Utility.IsFileTopLevelInPackage(x.FullName, pkgPath))
|
||||||
|
.Where(x => Utility.ExecutableUsesWin32Subsystem(x.FullName))
|
||||||
|
.ForEachAsync(x => createExecutableStubForExe(x.FullName))
|
||||||
|
.Wait();
|
||||||
|
|
||||||
|
// copy myself into the package so Squirrel can also be updated
|
||||||
|
// how we find the lib dir is a huge hack here, but 'ReleasePackage' verifies there can only be one of these so it should be fine.
|
||||||
|
var re = new Regex(@"lib[\\\/][^\\\/]*[\\\/]?", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||||
|
var libDir = Directory
|
||||||
|
.EnumerateDirectories(pkgPath, "*", SearchOption.AllDirectories)
|
||||||
|
.Where(d => re.IsMatch(d))
|
||||||
|
.OrderBy(d => d.Length)
|
||||||
|
.FirstOrDefault();
|
||||||
|
File.Copy(UpdatePath, Path.Combine(libDir, "Squirrel.exe"));
|
||||||
|
|
||||||
|
// sign all exe's in this package
|
||||||
|
if (signingOpts == null) return;
|
||||||
|
new DirectoryInfo(pkgPath).GetAllFilesRecursively()
|
||||||
|
.Where(x => Utility.FileIsLikelyPEImage(x.Name))
|
||||||
|
.ForEachAsync(async x => {
|
||||||
|
if (isPEFileSigned(x.FullName)) {
|
||||||
|
Log.Info("{0} is already signed, skipping", x.FullName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Info("About to sign {0}", x.FullName);
|
||||||
|
await signPEFile(x.FullName, signingOpts);
|
||||||
|
}, 1)
|
||||||
|
.Wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
processed.Add(rp.ReleasePackageFile);
|
||||||
|
|
||||||
|
var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir);
|
||||||
|
if (prev != null && generateDeltas) {
|
||||||
|
var deltaBuilder = new DeltaPackageBuilder(null);
|
||||||
|
|
||||||
|
var dp = deltaBuilder.CreateDeltaPackage(prev, rp,
|
||||||
|
Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta")));
|
||||||
|
processed.Insert(0, dp.InputPackageFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var file in toProcess) { File.Delete(file.FullName); }
|
||||||
|
|
||||||
|
var newReleaseEntries = processed
|
||||||
|
.Select(packageFilename => ReleaseEntry.GenerateFromFile(packageFilename, baseUrl))
|
||||||
|
.ToList();
|
||||||
|
var distinctPreviousReleases = previousReleases
|
||||||
|
.Where(x => !newReleaseEntries.Select(e => e.Version).Contains(x.Version));
|
||||||
|
var releaseEntries = distinctPreviousReleases.Concat(newReleaseEntries).ToList();
|
||||||
|
|
||||||
|
ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath);
|
||||||
|
|
||||||
|
var targetSetupExe = Path.Combine(di.FullName, "Setup.exe");
|
||||||
|
var newestFullRelease = Squirrel.EnumerableExtensions.MaxBy(releaseEntries, x => x.Version).Where(x => !x.IsDelta).First();
|
||||||
|
|
||||||
|
File.Copy(BootstrapperPath, targetSetupExe, true);
|
||||||
|
var zipPath = createSetupEmbeddedZip(Path.Combine(di.FullName, newestFullRelease.Filename), di.FullName, signingOpts, setupIcon).Result;
|
||||||
|
|
||||||
|
var writeZipToSetup = Utility.FindHelperExecutable("WriteZipToSetup.exe");
|
||||||
|
|
||||||
|
try {
|
||||||
|
string arguments = $"\"{targetSetupExe}\" \"{zipPath}\"";
|
||||||
|
if (!String.IsNullOrWhiteSpace(frameworkVersion)) {
|
||||||
|
arguments += $" --set-required-framework \"{frameworkVersion}\"";
|
||||||
|
}
|
||||||
|
if (!String.IsNullOrWhiteSpace(backgroundGif)) {
|
||||||
|
arguments += $" --set-splash \"{Path.GetFullPath(backgroundGif)}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = Utility.InvokeProcessAsync(writeZipToSetup, arguments, CancellationToken.None).Result;
|
||||||
|
if (result.Item1 != 0) throw new Exception("Failed to write Zip to Setup.exe!\n\n" + result.Item2);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.ErrorException("Failed to update Setup.exe with new Zip file", ex);
|
||||||
|
throw;
|
||||||
|
} finally {
|
||||||
|
File.Delete(zipPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utility.Retry(() =>
|
||||||
|
setPEVersionInfoAndIcon(targetSetupExe, new ZipPackage(package), setupIcon).Wait());
|
||||||
|
|
||||||
|
if (signingOpts != null) {
|
||||||
|
signPEFile(targetSetupExe, signingOpts).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (generateMsi) {
|
||||||
|
// createMsiPackage(targetSetupExe, new ZipPackage(package), packageAs64Bit).Wait();
|
||||||
|
|
||||||
|
// if (signingOpts != null) {
|
||||||
|
// signPEFile(targetSetupExe.Replace(".exe", ".msi"), signingOpts).Wait();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task<string> createSetupEmbeddedZip(string fullPackage, string releasesDir, string signingOpts, string setupIcon)
|
||||||
|
{
|
||||||
|
string tempPath;
|
||||||
|
|
||||||
|
Log.Info("Building embedded zip file for Setup.exe");
|
||||||
|
using (Utility.WithTempDirectory(out tempPath, null)) {
|
||||||
|
Log.ErrorIfThrows(() => {
|
||||||
|
File.Copy(UpdatePath, Path.Combine(tempPath, "Update.exe"));
|
||||||
|
File.Copy(fullPackage, Path.Combine(tempPath, Path.GetFileName(fullPackage)));
|
||||||
|
}, "Failed to write package files to temp dir: " + tempPath);
|
||||||
|
|
||||||
|
if (!String.IsNullOrWhiteSpace(setupIcon)) {
|
||||||
|
Log.ErrorIfThrows(() => {
|
||||||
|
File.Copy(setupIcon, Path.Combine(tempPath, "setupIcon.ico"));
|
||||||
|
}, "Failed to write icon to temp dir: " + tempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var releases = new[] { ReleaseEntry.GenerateFromFile(fullPackage) };
|
||||||
|
ReleaseEntry.WriteReleaseFile(releases, Path.Combine(tempPath, "RELEASES"));
|
||||||
|
|
||||||
|
var target = Path.GetTempFileName();
|
||||||
|
File.Delete(target);
|
||||||
|
|
||||||
|
// Sign Update.exe so that virus scanners don't think we're
|
||||||
|
// pulling one over on them
|
||||||
|
if (signingOpts != null) {
|
||||||
|
var di = new DirectoryInfo(tempPath);
|
||||||
|
|
||||||
|
var files = di.EnumerateFiles()
|
||||||
|
.Where(x => x.Name.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
.Select(x => x.FullName);
|
||||||
|
|
||||||
|
await files.ForEachAsync(x => signPEFile(x, signingOpts));
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.ErrorIfThrows(() =>
|
||||||
|
ZipFile.CreateFromDirectory(tempPath, target, CompressionLevel.Optimal, false),
|
||||||
|
"Failed to create Zip file from directory: " + tempPath);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task signPEFile(string exePath, string signingOpts)
|
||||||
|
{
|
||||||
|
// Try to find SignTool.exe
|
||||||
|
var exe = @".\signtool.exe";
|
||||||
|
if (!File.Exists(exe)) {
|
||||||
|
exe = Path.Combine(AssemblyRuntimeInfo.BaseDirectory, "signtool.exe");
|
||||||
|
|
||||||
|
// Run down PATH and hope for the best
|
||||||
|
if (!File.Exists(exe)) exe = "signtool.exe";
|
||||||
|
}
|
||||||
|
|
||||||
|
var processResult = await Utility.InvokeProcessAsync(exe,
|
||||||
|
String.Format("sign {0} \"{1}\"", signingOpts, exePath), CancellationToken.None);
|
||||||
|
|
||||||
|
if (processResult.Item1 != 0) {
|
||||||
|
var optsWithPasswordHidden = new Regex(@"/p\s+\w+").Replace(signingOpts, "/p ********");
|
||||||
|
var msg = String.Format("Failed to sign, command invoked was: '{0} sign {1} {2}'",
|
||||||
|
exe, optsWithPasswordHidden, exePath);
|
||||||
|
|
||||||
|
throw new Exception(msg);
|
||||||
|
} else {
|
||||||
|
Console.WriteLine(processResult.Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static bool isPEFileSigned(string path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return AuthenticodeTools.IsTrusted(path);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.ErrorException("Failed to determine signing status for " + path, ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task createExecutableStubForExe(string fullName)
|
||||||
|
{
|
||||||
|
var exe = Utility.FindHelperExecutable(@"StubExecutable.exe");
|
||||||
|
|
||||||
|
var target = Path.Combine(
|
||||||
|
Path.GetDirectoryName(fullName),
|
||||||
|
Path.GetFileNameWithoutExtension(fullName) + "_ExecutionStub.exe");
|
||||||
|
|
||||||
|
await Utility.CopyToAsync(exe, target);
|
||||||
|
|
||||||
|
await Utility.InvokeProcessAsync(
|
||||||
|
Utility.FindHelperExecutable("WriteZipToSetup.exe"),
|
||||||
|
String.Format("--copy-stub-resources \"{0}\" \"{1}\"", fullName, target),
|
||||||
|
CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task setPEVersionInfoAndIcon(string exePath, IPackage package, string iconPath = null)
|
||||||
|
{
|
||||||
|
var realExePath = Path.GetFullPath(exePath);
|
||||||
|
var company = String.Join(",", package.Authors);
|
||||||
|
var verStrings = new Dictionary<string, string>() {
|
||||||
|
{ "CompanyName", company },
|
||||||
|
{ "LegalCopyright", package.Copyright ?? "Copyright © " + DateTime.Now.Year.ToString() + " " + company },
|
||||||
|
{ "FileDescription", package.Summary ?? package.Description ?? "Installer for " + package.Id },
|
||||||
|
{ "ProductName", package.Description ?? package.Summary ?? package.Id },
|
||||||
|
};
|
||||||
|
|
||||||
|
var args = verStrings.Aggregate(new StringBuilder("\"" + realExePath + "\""), (acc, x) => { acc.AppendFormat(" --set-version-string \"{0}\" \"{1}\"", x.Key, x.Value); return acc; });
|
||||||
|
args.AppendFormat(" --set-file-version {0} --set-product-version {0}", package.Version.ToString());
|
||||||
|
if (iconPath != null) {
|
||||||
|
args.AppendFormat(" --set-icon \"{0}\"", Path.GetFullPath(iconPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find rcedit.exe
|
||||||
|
string exe = Utility.FindHelperExecutable("rcedit.exe");
|
||||||
|
|
||||||
|
var processResult = await Utility.InvokeProcessAsync(exe, args.ToString(), CancellationToken.None);
|
||||||
|
|
||||||
|
if (processResult.Item1 != 0) {
|
||||||
|
var msg = String.Format(
|
||||||
|
"Failed to modify resources, command invoked was: '{0} {1}'\n\nOutput was:\n{2}",
|
||||||
|
exe, args, processResult.Item2);
|
||||||
|
|
||||||
|
throw new Exception(msg);
|
||||||
|
} else {
|
||||||
|
Console.WriteLine(processResult.Item2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConsoleLogger : Squirrel.SimpleSplat.ILogger
|
||||||
|
{
|
||||||
|
readonly object gate = 42;
|
||||||
|
public LogLevel Level { get; set; } = LogLevel.Info;
|
||||||
|
public void Write(string message, LogLevel logLevel)
|
||||||
|
{
|
||||||
|
if (logLevel < Level) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (gate) {
|
||||||
|
string lvl = logLevel.ToString().Substring(0, 4).ToUpper();
|
||||||
|
if (logLevel == LogLevel.Error || logLevel == LogLevel.Fatal) {
|
||||||
|
Utility.ConsoleWriteWithColor($"[{lvl}] {message}\r\n", ConsoleColor.Red);
|
||||||
|
} else if (logLevel == LogLevel.Warn) {
|
||||||
|
Utility.ConsoleWriteWithColor($"[{lvl}] {message}\r\n", ConsoleColor.Yellow);
|
||||||
|
} else {
|
||||||
|
Console.WriteLine($"[{lvl}] {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/SquirrelCli/SquirrelCli.csproj
Normal file
33
src/SquirrelCli/SquirrelCli.csproj
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
|
<CompressionInSingleFile>true</CompressionInSingleFile>
|
||||||
|
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
|
||||||
|
<ApplicationIcon>squirrel.ico</ApplicationIcon>
|
||||||
|
<AssemblyName>Squirrel</AssemblyName>
|
||||||
|
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="squirrel.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Octokit" Version="0.50.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="System.IO" Version="4.3.0" />
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
|
||||||
|
<PackageReference Include="B2Net" Version="0.7.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -8,17 +8,19 @@ using B2Net;
|
|||||||
using B2Net.Models;
|
using B2Net.Models;
|
||||||
using Squirrel;
|
using Squirrel;
|
||||||
|
|
||||||
namespace Squirrel.SyncReleases.Sources
|
namespace SquirrelCli.Sources
|
||||||
{
|
{
|
||||||
internal class BackblazeRepository : IPackageRepository
|
internal class BackblazeRepository : IPackageRepository
|
||||||
{
|
{
|
||||||
private B2StorageProvider _b2;
|
private B2StorageProvider _b2;
|
||||||
public BackblazeRepository(string keyId, string appKey, string bucketId)
|
private DirectoryInfo releasesDir;
|
||||||
|
public BackblazeRepository(SyncBackblazeOptions options)
|
||||||
{
|
{
|
||||||
_b2 = new B2StorageProvider(keyId, appKey, bucketId);
|
_b2 = new B2StorageProvider(options.b2KeyId, options.b2AppKey, options.b2BucketId);
|
||||||
|
releasesDir = new DirectoryInfo(options.releaseDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DownloadRecentPackages(DirectoryInfo releasesDir)
|
public async Task DownloadRecentPackages()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Downloading RELEASES");
|
Console.WriteLine("Downloading RELEASES");
|
||||||
var releasesBytes = await _b2.DownloadFile("RELEASES");
|
var releasesBytes = await _b2.DownloadFile("RELEASES");
|
||||||
@@ -45,7 +47,7 @@ namespace Squirrel.SyncReleases.Sources
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UploadMissingPackages(DirectoryInfo releasesDir)
|
public async Task UploadMissingPackages()
|
||||||
{
|
{
|
||||||
foreach (var f in releasesDir.GetFiles()) {
|
foreach (var f in releasesDir.GetFiles()) {
|
||||||
await _b2.UploadFile(File.ReadAllBytes(f.FullName), f.Name);
|
await _b2.UploadFile(File.ReadAllBytes(f.FullName), f.Name);
|
||||||
26
src/SquirrelCli/Sync/GitHubRepository.cs
Normal file
26
src/SquirrelCli/Sync/GitHubRepository.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SquirrelCli.Sources
|
||||||
|
{
|
||||||
|
internal class GitHubRepository : IPackageRepository
|
||||||
|
{
|
||||||
|
private SyncGithubOptions _options;
|
||||||
|
|
||||||
|
public GitHubRepository(SyncGithubOptions options)
|
||||||
|
{
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DownloadRecentPackages()
|
||||||
|
{
|
||||||
|
return SyncImplementations.SyncFromGitHub(_options.repoUrl, _options.token, new DirectoryInfo(_options.releaseDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task UploadMissingPackages()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/SquirrelCli/Sync/IPackageRepository.cs
Normal file
11
src/SquirrelCli/Sync/IPackageRepository.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SquirrelCli.Sources
|
||||||
|
{
|
||||||
|
internal interface IPackageRepository
|
||||||
|
{
|
||||||
|
public Task DownloadRecentPackages();
|
||||||
|
public Task UploadMissingPackages();
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/SquirrelCli/Sync/SimpleWebRepository.cs
Normal file
27
src/SquirrelCli/Sync/SimpleWebRepository.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SquirrelCli.Sources
|
||||||
|
{
|
||||||
|
|
||||||
|
internal class SimpleWebRepository : IPackageRepository
|
||||||
|
{
|
||||||
|
private readonly SyncHttpOptions options;
|
||||||
|
|
||||||
|
public SimpleWebRepository(SyncHttpOptions options)
|
||||||
|
{
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DownloadRecentPackages()
|
||||||
|
{
|
||||||
|
return SyncImplementations.SyncRemoteReleases(new Uri(options.url), new DirectoryInfo(options.releaseDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task UploadMissingPackages()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ using Octokit;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
namespace Squirrel.SyncReleases.Sources
|
namespace SquirrelCli.Sources
|
||||||
{
|
{
|
||||||
internal class SyncImplementations
|
internal class SyncImplementations
|
||||||
{
|
{
|
||||||
@@ -151,6 +151,7 @@ namespace Squirrel.SyncReleases.Sources
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable SYSLIB0014 // Type or member is obsolete
|
||||||
class NotBrokenWebClient : WebClient
|
class NotBrokenWebClient : WebClient
|
||||||
{
|
{
|
||||||
protected override WebRequest GetWebRequest(Uri address)
|
protected override WebRequest GetWebRequest(Uri address)
|
||||||
@@ -164,4 +165,5 @@ namespace Squirrel.SyncReleases.Sources
|
|||||||
return hwr;
|
return hwr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#pragma warning restore SYSLIB0014 // Type or member is obsolete
|
||||||
}
|
}
|
||||||
45
src/SquirrelCli/app.manifest
Normal file
45
src/SquirrelCli/app.manifest
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.2.0.0" name="Squirrel.app"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
|
||||||
|
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</assembly>
|
||||||
BIN
src/SquirrelCli/squirrel.ico
Normal file
BIN
src/SquirrelCli/squirrel.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
@@ -1,150 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Mono.Options;
|
|
||||||
using Octokit;
|
|
||||||
using Squirrel.SimpleSplat;
|
|
||||||
using Squirrel;
|
|
||||||
using Squirrel.Json;
|
|
||||||
using Squirrel.SyncReleases.Sources;
|
|
||||||
|
|
||||||
namespace Squirrel.SyncReleases
|
|
||||||
{
|
|
||||||
class Program : IEnableLogger
|
|
||||||
{
|
|
||||||
static OptionSet opts;
|
|
||||||
|
|
||||||
public static int Main(string[] args)
|
|
||||||
{
|
|
||||||
var pg = new Program();
|
|
||||||
try {
|
|
||||||
return pg.main(args).GetAwaiter().GetResult();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Console.Error.WriteLine(ex);
|
|
||||||
Console.Error.WriteLine("> SyncReleases.exe -h for help");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<int> main(string[] args)
|
|
||||||
{
|
|
||||||
using (var logger = new SetupLogLogger(false) { Level = Squirrel.SimpleSplat.LogLevel.Info }) {
|
|
||||||
Squirrel.SimpleSplat.SquirrelLocator.CurrentMutable.Register(() => logger, typeof(Squirrel.SimpleSplat.ILogger));
|
|
||||||
|
|
||||||
var releaseDir = default(string);
|
|
||||||
var repoUrl = default(string);
|
|
||||||
var token = default(string);
|
|
||||||
var provider = default(string);
|
|
||||||
var upload = default(bool);
|
|
||||||
var showHelp = default(bool);
|
|
||||||
|
|
||||||
var b2KeyId = default(string);
|
|
||||||
var b2AppKey = default(string);
|
|
||||||
var bucketId = default(string);
|
|
||||||
|
|
||||||
opts = new OptionSet() {
|
|
||||||
"Usage: SyncReleases.exe [OPTS]",
|
|
||||||
"Utility to download from or upload packages to a remote package release repository",
|
|
||||||
"Can be used to fully automate the distribution of new releases from CI or other scripts",
|
|
||||||
"",
|
|
||||||
"Options:",
|
|
||||||
{ "h|?|help", "Display help and exit", v => showHelp = true },
|
|
||||||
{ "r=|releaseDir=", "Path to the local release directory to sync with remote", v => releaseDir = v},
|
|
||||||
{ "u=|url=", "A GitHub repository url, or a remote web url", v => repoUrl = v},
|
|
||||||
{ "t=|token=", "The OAuth token to use as login credentials", v => token = v},
|
|
||||||
{ "p=|provider=", "Specify the release repository type, can be 'github', 'web', or 'b2'", v => provider = v },
|
|
||||||
{ "upload", "Upload releases in the releaseDir to the remote repository", v => upload = true },
|
|
||||||
{ "bucketId=", "Id or name of the bucket in B2, S3, etc", v => bucketId = v },
|
|
||||||
{ "b2keyid=", "B2 Auth Key Id", v => b2KeyId = v },
|
|
||||||
{ "b2key=", "B2 Auth Key", v => b2AppKey = v },
|
|
||||||
};
|
|
||||||
|
|
||||||
opts.Parse(args);
|
|
||||||
|
|
||||||
if (showHelp) {
|
|
||||||
ShowHelp();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (String.IsNullOrWhiteSpace(provider)) throw new ArgumentNullException(nameof(provider));
|
|
||||||
if (String.IsNullOrWhiteSpace(releaseDir)) throw new ArgumentNullException(nameof(releaseDir));
|
|
||||||
var releaseDirectoryInfo = new DirectoryInfo(releaseDir ?? Path.Combine(".", "Releases"));
|
|
||||||
if (!releaseDirectoryInfo.Exists) releaseDirectoryInfo.Create();
|
|
||||||
|
|
||||||
IPackageRepository repository;
|
|
||||||
|
|
||||||
if (provider.Equals("github", StringComparison.OrdinalIgnoreCase)) {
|
|
||||||
if (String.IsNullOrWhiteSpace(repoUrl)) throw new ArgumentNullException(nameof(repoUrl));
|
|
||||||
if (String.IsNullOrWhiteSpace(token)) throw new ArgumentNullException(nameof(repoUrl));
|
|
||||||
repository = new GitHubRepository(repoUrl, token);
|
|
||||||
} else if (provider.Equals("web", StringComparison.OrdinalIgnoreCase)) {
|
|
||||||
if (String.IsNullOrWhiteSpace(repoUrl)) throw new ArgumentNullException(nameof(repoUrl));
|
|
||||||
repository = new SimpleWebRepository(new Uri(repoUrl));
|
|
||||||
} else if (provider.Equals("b2", StringComparison.OrdinalIgnoreCase)) {
|
|
||||||
if (String.IsNullOrWhiteSpace(b2KeyId)) throw new ArgumentNullException(nameof(b2KeyId));
|
|
||||||
if (String.IsNullOrWhiteSpace(b2AppKey)) throw new ArgumentNullException(nameof(b2AppKey));
|
|
||||||
if (String.IsNullOrWhiteSpace(bucketId)) throw new ArgumentNullException(nameof(bucketId));
|
|
||||||
repository = new BackblazeRepository(b2KeyId, b2AppKey, bucketId);
|
|
||||||
} else {
|
|
||||||
throw new Exception("Release provider missing or invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
var mode = upload ? "Uploading" : "Downloading";
|
|
||||||
Console.WriteLine(mode + " using provider " + repository.GetType().Name);
|
|
||||||
|
|
||||||
if (upload) {
|
|
||||||
await repository.UploadMissingPackages(releaseDirectoryInfo);
|
|
||||||
} else {
|
|
||||||
await repository.DownloadRecentPackages(releaseDirectoryInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowHelp()
|
|
||||||
{
|
|
||||||
opts.WriteOptionDescriptions(Console.Out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SetupLogLogger : Squirrel.SimpleSplat.ILogger, IDisposable
|
|
||||||
{
|
|
||||||
StreamWriter inner;
|
|
||||||
readonly object gate = 42;
|
|
||||||
public Squirrel.SimpleSplat.LogLevel Level { get; set; }
|
|
||||||
|
|
||||||
public SetupLogLogger(bool saveInTemp)
|
|
||||||
{
|
|
||||||
var dir = saveInTemp ?
|
|
||||||
Path.GetTempPath() :
|
|
||||||
AppContext.BaseDirectory;
|
|
||||||
|
|
||||||
var file = Path.Combine(dir, "SquirrelSetup.log");
|
|
||||||
if (File.Exists(file)) File.Delete(file);
|
|
||||||
|
|
||||||
inner = new StreamWriter(file, false, Encoding.UTF8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(string message, LogLevel logLevel)
|
|
||||||
{
|
|
||||||
if (logLevel < Level) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (gate) inner.WriteLine(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
lock (gate) inner.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Squirrel.SyncReleases.Sources
|
|
||||||
{
|
|
||||||
internal class GitHubRepository : IPackageRepository
|
|
||||||
{
|
|
||||||
public string RepoUrl { get; }
|
|
||||||
public string Token { get; }
|
|
||||||
|
|
||||||
public GitHubRepository(string repoUrl, string token)
|
|
||||||
{
|
|
||||||
RepoUrl = repoUrl;
|
|
||||||
Token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DownloadRecentPackages(DirectoryInfo releasesDir)
|
|
||||||
{
|
|
||||||
return SyncImplementations.SyncFromGitHub(RepoUrl, Token, releasesDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task UploadMissingPackages(DirectoryInfo releasesDir)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Squirrel.SyncReleases.Sources
|
|
||||||
{
|
|
||||||
internal interface IPackageRepository
|
|
||||||
{
|
|
||||||
public Task DownloadRecentPackages(DirectoryInfo releasesDir);
|
|
||||||
public Task UploadMissingPackages(DirectoryInfo releasesDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Squirrel.SyncReleases.Sources
|
|
||||||
{
|
|
||||||
|
|
||||||
internal class SimpleWebRepository : IPackageRepository
|
|
||||||
{
|
|
||||||
public Uri TargetUri { get; }
|
|
||||||
|
|
||||||
public SimpleWebRepository(Uri targetUri)
|
|
||||||
{
|
|
||||||
TargetUri = targetUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DownloadRecentPackages(DirectoryInfo releasesDir)
|
|
||||||
{
|
|
||||||
return SyncImplementations.SyncRemoteReleases(TargetUri, releasesDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task UploadMissingPackages(DirectoryInfo releasesDir)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<LangVersion>9</LangVersion>
|
|
||||||
<RootNamespace>Squirrel.SyncReleases</RootNamespace>
|
|
||||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
|
||||||
<SelfContained>true</SelfContained>
|
|
||||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
|
|
||||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Octokit" Version="0.50.0" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
||||||
<PackageReference Include="System.IO" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
|
||||||
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
|
|
||||||
<PackageReference Include="B2Net" Version="0.7.5" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -13,6 +13,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Squirrel.NuGet;
|
using Squirrel.NuGet;
|
||||||
|
using Squirrel.Lib;
|
||||||
|
|
||||||
namespace Squirrel.Update
|
namespace Squirrel.Update
|
||||||
{
|
{
|
||||||
|
|||||||
Binary file not shown.
@@ -22,12 +22,6 @@
|
|||||||
<ApplicationIcon>update.ico</ApplicationIcon>
|
<ApplicationIcon>update.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="rcedit.exe" CopyToOutputDirectory="PreserveNewest" />
|
|
||||||
<None Include="signtool.exe" CopyToOutputDirectory="PreserveNewest" />
|
|
||||||
<None Include="update.com" CopyToOutputDirectory="PreserveNewest" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
0
.nuget/NuGet.exe → vendor/NuGet.exe
vendored
0
.nuget/NuGet.exe → vendor/NuGet.exe
vendored
Reference in New Issue
Block a user