From 456d1c0c56ff21b6c7b89b8d8732bb54d64612fc Mon Sep 17 00:00:00 2001 From: Alastair Pitts Date: Thu, 21 Dec 2023 15:16:57 +1100 Subject: [PATCH] Support locally building unsigned Linux packages (#757) --- .nuke/build.schema.json | 5 + build-k8s-docker-local.ps1 | 74 ++++- build/Build.Pack.cs | 31 ++- build/Build.cs | 255 ++++++++++-------- .../unsigned/create-linux-packages.sh | 107 ++++++++ .../packaging-scripts/unsigned/package.sh | 60 +++++ 6 files changed, 395 insertions(+), 137 deletions(-) create mode 100644 linux-packages/packaging-scripts/unsigned/create-linux-packages.sh create mode 100644 linux-packages/packaging-scripts/unsigned/package.sh diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index f8fd5f075..01f5c2cc6 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -81,6 +81,9 @@ "type": "string", "description": "Root directory during build execution" }, + "RuntimeId": { + "type": "string" + }, "signing_certificate_password": { "type": "string", "default": "Secrets must be entered via 'nuke :secrets [profile]'" @@ -112,6 +115,7 @@ "PackLinux", "PackLinuxPackagesLegacy", "PackLinuxTarballs", + "PackLinuxUnsigned", "PackOsx", "PackOsxTarballs", "PackRedHatPackage", @@ -156,6 +160,7 @@ "PackLinux", "PackLinuxPackagesLegacy", "PackLinuxTarballs", + "PackLinuxUnsigned", "PackOsx", "PackOsxTarballs", "PackRedHatPackage", diff --git a/build-k8s-docker-local.ps1 b/build-k8s-docker-local.ps1 index 47f6972c8..fab6111dd 100644 --- a/build-k8s-docker-local.ps1 +++ b/build-k8s-docker-local.ps1 @@ -1,24 +1,74 @@ -param ( - [Parameter(Mandatory=$True)] - [string] - $BuildNumber, - +param ( [Parameter()] [string] $BuildArch = "amd64", - [Parameter()] [string] - $LocalRegistryDomain = "localhost:5500" + $LocalRegistryDomain = "localhost:5500", + + [Parameter()] + [switch] + $NonMinikubeRegistry = $false ) -$env:BUILD_NUMBER=$BuildNumber -$env:BUILD_DATE= Get-Date -Format "yyyy-MM-dd" -$env:BUILD_ARCH=$BuildArch +$runtimeToBuild = "linux-$BuildArch".Replace("amd", "x") + +#First we pack the unsigned builds +& .\build.ps1 -Target "PackLinuxUnsigned" -RuntimeId $runtimeToBuild + +#Now find the latest package version +$package = Get-ChildItem -Path "$PSScriptRoot/_artifacts/deb" -Filter "tentacle_*_$BuildArch.deb" +$packageNameParts = $package.Name -Split "_" +$buildNumber = $packageNameParts[1] + +Write-Output "Using package $($package.Name)" + +$env:BUILD_NUMBER = $buildNumber +$env:BUILD_DATE = Get-Date -Format "yyyy-MM-dd" +$env:BUILD_ARCH = $BuildArch & docker compose -f docker-compose.build.yml -v build --pull octopusdeploy-kubernetes-tentacle-linux -& docker tag "docker.packages.octopushq.com/octopusdeploy/kubernetes-tentacle:$BuildNumber-linux-$BuildArch" "$LocalRegistryDomain/kubernetes-tentacle:$BuildNumber-linux-$BuildArch" +& docker tag "docker.packages.octopushq.com/octopusdeploy/kubernetes-tentacle:$buildNumber-linux-$BuildArch" "$LocalRegistryDomain/kubernetes-tentacle:$buildNumber-linux-$BuildArch" + +if (!$NonMinikubeRegistry) { + $registryPort = ($LocalRegistryDomain -split ":")[-1] + + Write-Output "Setting kubectl context to 'minikube'" + & kubectl config use-context minikube + + Write-Output "Forwarding minikube registry on port $registryPort" + $portForwardProcess = Start-Process kubectl -ArgumentList "port-forward --namespace kube-system service/registry $($registryPort):80" -NoNewWindow -PassThru + + Write-Output "Running network forwarding docker container" + $minikubeIP = & minikube ip + $containerId = & docker run --rm -d --network=host alpine/socat "tcp-listen:$registryPort,reuseaddr,fork" "tcp-connect:host.docker.internal:$registryPort" + + $isRunning = $false + Write-Output "Waiting for network forwarding docker container to be running" + while ($isRunning -ne $true) { + $runningValue = & docker inspect -f "{{.State.Running}}" $containerId + $isRunning = $runningValue -eq "true" + + if ($isRunning -eq $false) { + Start-Sleep -Milliseconds 250 + } + } + Write-Output "Network forwarding docker container running" +} + +$imageName = "$LocalRegistryDomain/kubernetes-tentacle:$buildNumber-linux-$BuildArch" + +Write-Output "Pushing $imageName" + +& docker push $imageName + +Write-Output "Pushed $imageName" -& docker push "$LocalRegistryDomain/kubernetes-tentacle:$BuildNumber-linux-$BuildArch" \ No newline at end of file +if (!$NonMinikubeRegistry) { + Write-Output "Stopping network forwarding docker container" + & docker stop $containerId | Out-Null + Write-Output "Stopping minikube port forwarding" + Stop-Process -Id $portForwardProcess.Id -ErrorAction SilentlyContinue +} \ No newline at end of file diff --git a/build/Build.Pack.cs b/build/Build.Pack.cs index 4d92b919b..1bfdedba8 100644 --- a/build/Build.Pack.cs +++ b/build/Build.Pack.cs @@ -17,6 +17,9 @@ partial class Build { + //We don't sign linux packages when building locally + readonly bool SignLinuxPackages = !IsLocalBuild; + [PublicAPI] Target PackOsxTarballs => _ => _ .Description("Packs the OS/X tarballs containing the published binaries.") @@ -45,12 +48,15 @@ partial class Build .Description("Legacy task until we can split creation of .rpm and .deb packages into their own tasks") .DependsOn(PackLinuxTarballs) .Requires( - () => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SIGN_PRIVATE_KEY")), - () => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SIGN_PASSPHRASE"))) + () => !SignLinuxPackages || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SIGN_PRIVATE_KEY")), + () => !SignLinuxPackages || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SIGN_PASSPHRASE"))) .Executes(() => { const string dockerToolsContainerImage = "docker.packages.octopushq.com/octopusdeploy/tool-containers/tool-linux-packages:latest"; + //this is just to stop messages such as scout vulnerability hints which are reported as errors (but don't actually fail anything) + Environment.SetEnvironmentVariable("DOCKER_CLI_HINTS", "false"); + void CreateLinuxPackages(string runtimeId) { //TODO It's probable that the .deb and .rpm package layouts will be different - and potentially _should already_ be different. @@ -61,6 +67,11 @@ void CreateLinuxPackages(string runtimeId) FileSystemTasks.EnsureExistingDirectory(debBuildDir / "output"); var packagingScriptsDirectory = RootDirectory / "linux-packages" / "packaging-scripts"; + + // if we aren't signing, use the unsigned scripts + if (!SignLinuxPackages) + packagingScriptsDirectory /= "unsigned"; + packagingScriptsDirectory.GlobFiles("*") .ForEach(x => FileSystemTasks.CopyFileToDirectory(x, debBuildDir / "scripts")); @@ -121,6 +132,14 @@ void CreateLinuxPackages(string runtimeId) .DependsOn(PackDebianPackage) .DependsOn(PackRedHatPackage); + [PublicAPI] + Target PackLinuxUnsigned => _ => _ + .Description("Packages all the Linux targets without signing the packages.") + .DependsOn(PackDebianPackage) + .DependsOn(PackRedHatPackage) + .OnlyWhenStatic(() => IsLocalBuild) + .OnlyWhenStatic(() => !SignLinuxPackages); + [PublicAPI] Target PackWindowsZips => _ => _ .Description("Packs the Windows .zip files containing the published binaries.") @@ -159,7 +178,7 @@ void PackWindowsInstallers(MSBuildTargetPlatform platform, AbsolutePath wixNuget var installerDirectory = BuildDirectory / "Installer"; FileSystemTasks.EnsureExistingDirectory(installerDirectory); FileSystemTasks.EnsureCleanDirectory(installerDirectory); - + if (framework != NetCore) { (BuildDirectory / "Tentacle" / framework / "win").GlobFiles("*") @@ -250,7 +269,7 @@ void BuildMsiInstallerForPlatform(MSBuildTargetPlatform platform, AbsolutePath w if (wixNugetInstalledPackage == null) throw new Exception("Failed to find wix nuget package path"); FileSystemTasks.EnsureExistingDirectory(ArtifactsDirectory / "msi"); - + PackWindowsInstallers(MSBuildTargetPlatform.x64, wixNugetInstalledPackage.Directory, NetFramework, "NetFramework"); PackWindowsInstallers(MSBuildTargetPlatform.x86, wixNugetInstalledPackage.Directory, NetFramework, "NetFramework"); @@ -361,7 +380,7 @@ string ConstructRedHatPackageFilename(string packageName, string architecture) FileSystemTasks.CopyFile(BuildDirectory / "Octopus.Tentacle.Upgrader" / NetCore / "win-x86" / "Octopus.Tentacle.Upgrader.exe", workingDirectory / "Octopus.Tentacle.Upgrader-net6.0-win-x86.exe"); FileSystemTasks.CopyFile(BuildDirectory / "Octopus.Tentacle.Upgrader" / NetCore / "win-x64" / "Octopus.Tentacle.Upgrader.exe", workingDirectory / "Octopus.Tentacle.Upgrader-net6.0-win-x64.exe"); - + var octopusTentacleUpgraderDirectory = BuildDirectory / "Octopus.Tentacle.Upgrader" / NetFramework / "win"; octopusTentacleUpgraderDirectory.GlobFiles("*").ForEach(x => FileSystemTasks.CopyFileToDirectory(x, workingDirectory)); FileSystemTasks.CopyFile(ArtifactsDirectory / "deb" / debAmd64PackageFilename, workingDirectory / debAmd64PackageFilename); @@ -371,7 +390,7 @@ string ConstructRedHatPackageFilename(string packageName, string architecture) FileSystemTasks.CopyFile(ArtifactsDirectory / "rpm" / rpmArm32PackageFilename, workingDirectory / rpmArm32PackageFilename); FileSystemTasks.CopyFile(ArtifactsDirectory / "rpm" / rpmx64PackageFilename, workingDirectory / rpmx64PackageFilename); - foreach (var framework in new[] {NetFramework, NetCore}) + foreach (var framework in new[] { NetFramework, NetCore }) { foreach (var runtimeId in RuntimeIds) { diff --git a/build/Build.cs b/build/Build.cs index 89eff9ed5..abd25d8e5 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -1,5 +1,7 @@ // ReSharper disable RedundantUsingDirective + using System; +using System.Collections.Generic; using System.Linq; using System.Xml; using JetBrains.Annotations; @@ -26,15 +28,14 @@ partial class Build : NukeBuild /// - JetBrains Rider https://nuke.build/rider /// - Microsoft VisualStudio https://nuke.build/visualstudio /// - Microsoft VSCode https://nuke.build/vscode - - [Solution(GenerateProjects = true)] readonly Solution Solution = null!; + [Solution(GenerateProjects = true)] + readonly Solution Solution = null!; [Parameter("Branch name for OctoVersion to use to calculate the version number. Can be set via the environment variable OCTOVERSION_CurrentBranch.", Name = "OCTOVERSION_CurrentBranch")] readonly string BranchName = null!; - [Parameter("Whether to auto-detect the branch name - this is okay for a local build, but should not be used under CI.")] - readonly bool AutoDetectBranch = IsLocalBuild; + [Parameter("Whether to auto-detect the branch name - this is okay for a local build, but should not be used under CI.")] readonly bool AutoDetectBranch = IsLocalBuild; [OctoVersion(UpdateBuildNumber = true, BranchParameter = nameof(BranchName), AutoDetectBranchParameter = nameof(AutoDetectBranch), Framework = "net6.0")] @@ -68,6 +69,9 @@ partial class Build : NukeBuild [Parameter(Name = "signing_certificate_path")] public static string SigningCertificatePath = RootDirectory / "certificates" / "OctopusDevelopment.pfx"; [Secret] [Parameter(Name = "signing_certificate_password")] public static string SigningCertificatePassword = "Password01!"; + [Parameter(Name = "RuntimeId")] + public string? SpecificRuntimeId; + readonly AbsolutePath SourceDirectory = RootDirectory / "source"; readonly AbsolutePath ArtifactsDirectory = RootDirectory / "_artifacts"; readonly AbsolutePath BuildDirectory = RootDirectory / "_build"; @@ -76,7 +80,10 @@ partial class Build : NukeBuild const string NetFramework = "net48"; const string NetCore = "net6.0"; - readonly string[] RuntimeIds = { "win", "win-x86", "win-x64", "linux-x64", "linux-musl-x64", "linux-arm64", "linux-arm", "osx-x64", "osx-arm64" }; + + IEnumerable RuntimeIds => SpecificRuntimeId != null + ? new[] { SpecificRuntimeId } + : new[] { "win", "win-x86", "win-x64", "linux-x64", "linux-musl-x64", "linux-arm64", "linux-arm", "osx-x64", "osx-arm64" }; static readonly string Timestamp = DateTime.Now.ToString("yyyyMMddHHmmss"); @@ -91,133 +98,143 @@ partial class Build : NukeBuild : $"{OctoVersionInfo.NuGetVersion}-{Timestamp}"; [PublicAPI] - Target CalculateVersion => _ => _ - .Executes(() => - { - // This is here just so that TeamCity has a target to call. The OctoVersion attribute generates the version for us - }); + Target CalculateVersion => + _ => _ + .Executes(() => + { + // This is here just so that TeamCity has a target to call. The OctoVersion attribute generates the version for us + }); [PublicAPI] - Target Clean => _ => _ - .Executes(() => - { - SourceDirectory.GlobDirectories("**/bin", "**/obj", "**/TestResults").ForEach(DeleteDirectory); - EnsureCleanDirectory(ArtifactsDirectory); - EnsureCleanDirectory(BuildDirectory); - }); + Target Clean => + _ => _ + .Executes(() => + { + SourceDirectory.GlobDirectories("**/bin", "**/obj", "**/TestResults").ForEach(DeleteDirectory); + EnsureCleanDirectory(ArtifactsDirectory); + EnsureCleanDirectory(BuildDirectory); + }); [PublicAPI] - Target Restore => _ => _ - .DependsOn(Clean) - .Executes(() => - { - DotNetRestore(_ => _ - .SetProjectFile(Solution)); - }); + Target Restore => + _ => _ + .DependsOn(Clean) + .Executes(() => + { + DotNetRestore(_ => _ + .SetProjectFile(Solution)); + }); [PublicAPI] - Target BuildWindows => _ => _ - .DependsOn(Restore) - .Executes(() => - { - using var versionInfoFile = ModifyTemplatedVersionAndProductFilesWithValues(); - using var productWxsFile = UpdateMsiProductVersion(); - - RuntimeIds.Where(x => x.StartsWith("win")) - .ForEach(runtimeId => RunBuildFor(runtimeId.Equals("win") ? NetFramework : NetCore, runtimeId)); - - versionInfoFile.Dispose(); - productWxsFile.Dispose(); - - var hardenInstallationDirectoryScript = RootDirectory / "scripts" / "Harden-InstallationDirectory.ps1"; - var directoriesToCopyHardenScriptInto = new [] + Target BuildWindows => + _ => _ + .DependsOn(Restore) + .Executes(() => { - (BuildDirectory / "Tentacle" / NetFramework / "win"), - (BuildDirectory / "Tentacle" / NetCore / "win-x86"), - (BuildDirectory / "Tentacle" / NetCore / "win-x64") - }; - directoriesToCopyHardenScriptInto.ForEach(dir => CopyFileToDirectory(hardenInstallationDirectoryScript, dir, FileExistsPolicy.Overwrite)); - - // Sign any unsigned libraries that Octopus Deploy authors so that they play nicely with security scanning tools. - // Refer: https://octopusdeploy.slack.com/archives/C0K9DNQG5/p1551655877004400 - // Decision re: no signing everything: https://octopusdeploy.slack.com/archives/C0K9DNQG5/p1557938890227100 - var windowsOnlyBuiltFileSpec = BuildDirectory.GlobDirectories("**/win*/**"); - - var filesToSign = windowsOnlyBuiltFileSpec - .SelectMany(x => x.GlobFiles("**/Octo*.exe", "**/Octo*.dll", "**/Tentacle.exe", "**/Tentacle.dll", "**/Halibut.dll", "**/Nuget.*.dll", "**/Nevermore.dll", "**/*.ps1")) - .Where(file => !Signing.HasAuthenticodeSignature(file)) - .ToArray(); - - Signing.Sign(filesToSign); - }); + using var versionInfoFile = ModifyTemplatedVersionAndProductFilesWithValues(); + using var productWxsFile = UpdateMsiProductVersion(); + + RuntimeIds.Where(x => x.StartsWith("win")) + .ForEach(runtimeId => RunBuildFor(runtimeId.Equals("win") ? NetFramework : NetCore, runtimeId)); + + versionInfoFile.Dispose(); + productWxsFile.Dispose(); + + var hardenInstallationDirectoryScript = RootDirectory / "scripts" / "Harden-InstallationDirectory.ps1"; + var directoriesToCopyHardenScriptInto = new[] + { + (BuildDirectory / "Tentacle" / NetFramework / "win"), + (BuildDirectory / "Tentacle" / NetCore / "win-x86"), + (BuildDirectory / "Tentacle" / NetCore / "win-x64") + }; + directoriesToCopyHardenScriptInto.ForEach(dir => CopyFileToDirectory(hardenInstallationDirectoryScript, dir, FileExistsPolicy.Overwrite)); + + // Sign any unsigned libraries that Octopus Deploy authors so that they play nicely with security scanning tools. + // Refer: https://octopusdeploy.slack.com/archives/C0K9DNQG5/p1551655877004400 + // Decision re: no signing everything: https://octopusdeploy.slack.com/archives/C0K9DNQG5/p1557938890227100 + var windowsOnlyBuiltFileSpec = BuildDirectory.GlobDirectories("**/win*/**"); + + var filesToSign = windowsOnlyBuiltFileSpec + .SelectMany(x => x.GlobFiles("**/Octo*.exe", "**/Octo*.dll", "**/Tentacle.exe", "**/Tentacle.dll", "**/Halibut.dll", "**/Nuget.*.dll", "**/Nevermore.dll", "**/*.ps1")) + .Where(file => !Signing.HasAuthenticodeSignature(file)) + .ToArray(); + + Signing.Sign(filesToSign); + }); [PublicAPI] - Target BuildLinux => _ => _ - .DependsOn(Restore) - .Executes(() => - { - using var versionInfoFile = ModifyTemplatedVersionAndProductFilesWithValues(); - using var productWxsFile = UpdateMsiProductVersion(); - - RuntimeIds.Where(x => x.StartsWith("linux-")) - .ForEach(runtimeId => RunBuildFor(NetCore, runtimeId)); - - versionInfoFile.Dispose(); - productWxsFile.Dispose(); - }); + Target BuildLinux => + _ => _ + .DependsOn(Restore) + .Executes(() => + { + using var versionInfoFile = ModifyTemplatedVersionAndProductFilesWithValues(); + using var productWxsFile = UpdateMsiProductVersion(); + + RuntimeIds.Where(x => x.StartsWith("linux-")) + .ForEach(runtimeId => RunBuildFor(NetCore, runtimeId)); + + versionInfoFile.Dispose(); + productWxsFile.Dispose(); + }); [PublicAPI] - Target BuildOsx => _ => _ - .DependsOn(Restore) - .Executes(() => - { - using var versionInfoFile = ModifyTemplatedVersionAndProductFilesWithValues(); - using var productWxsFile = UpdateMsiProductVersion(); - - RuntimeIds.Where(x => x.StartsWith("osx-")) - .ForEach(runtimeId => RunBuildFor(NetCore, runtimeId)); - - versionInfoFile.Dispose(); - productWxsFile.Dispose(); - }); + Target BuildOsx => + _ => _ + .DependsOn(Restore) + .Executes(() => + { + using var versionInfoFile = ModifyTemplatedVersionAndProductFilesWithValues(); + using var productWxsFile = UpdateMsiProductVersion(); + + RuntimeIds.Where(x => x.StartsWith("osx-")) + .ForEach(runtimeId => RunBuildFor(NetCore, runtimeId)); + + versionInfoFile.Dispose(); + productWxsFile.Dispose(); + }); [PublicAPI] - Target BuildAll => _ => _ - .Description("Build all the framework/runtime combinations. Notional task - running this on a single host is possible but cumbersome.") - .DependsOn(BuildWindows) - .DependsOn(BuildLinux) - .DependsOn(BuildOsx); + Target BuildAll => + _ => _ + .Description("Build all the framework/runtime combinations. Notional task - running this on a single host is possible but cumbersome.") + .DependsOn(BuildWindows) + .DependsOn(BuildLinux) + .DependsOn(BuildOsx); [PublicAPI] - Target CopyToLocalPackages => _ => _ - .OnlyWhenStatic(() => IsLocalBuild) - .DependsOn(PackCrossPlatformBundle) - .Description("If not running on a build agent, this step copies the relevant built artifacts to the local packages cache.") - .Executes(() => - { - EnsureExistingDirectory(LocalPackagesDirectory); - CopyFileToDirectory(ArtifactsDirectory / "Chocolatey" / $"OctopusDeploy.Tentacle.{NuGetVersion}.nupkg", LocalPackagesDirectory); - }); + Target CopyToLocalPackages => + _ => _ + .OnlyWhenStatic(() => IsLocalBuild) + .DependsOn(PackCrossPlatformBundle) + .Description("If not running on a build agent, this step copies the relevant built artifacts to the local packages cache.") + .Executes(() => + { + EnsureExistingDirectory(LocalPackagesDirectory); + CopyFileToDirectory(ArtifactsDirectory / "Chocolatey" / $"OctopusDeploy.Tentacle.{NuGetVersion}.nupkg", LocalPackagesDirectory); + }); [PublicAPI] - Target CopyClientAndContractsToLocalPackages => _ => _ - .OnlyWhenStatic(() => IsLocalBuild) - .DependsOn(PackContracts) - .DependsOn(PackClient) - .Description("If not running on a build agent, this step copies the relevant built artifacts to the local packages cache.") - .Executes(() => - { - EnsureExistingDirectory(LocalPackagesDirectory); - CopyFileToDirectory(ArtifactsDirectory / "nuget" / $"Octopus.Tentacle.Contracts.{FullSemVer}.nupkg", LocalPackagesDirectory); - CopyFileToDirectory(ArtifactsDirectory / "nuget" / $"Octopus.Tentacle.Client.{FullSemVer}.nupkg", LocalPackagesDirectory); - }); + Target CopyClientAndContractsToLocalPackages => + _ => _ + .OnlyWhenStatic(() => IsLocalBuild) + .DependsOn(PackContracts) + .DependsOn(PackClient) + .Description("If not running on a build agent, this step copies the relevant built artifacts to the local packages cache.") + .Executes(() => + { + EnsureExistingDirectory(LocalPackagesDirectory); + CopyFileToDirectory(ArtifactsDirectory / "nuget" / $"Octopus.Tentacle.Contracts.{FullSemVer}.nupkg", LocalPackagesDirectory); + CopyFileToDirectory(ArtifactsDirectory / "nuget" / $"Octopus.Tentacle.Client.{FullSemVer}.nupkg", LocalPackagesDirectory); + }); [PublicAPI] - Target Default => _ => _ - .DependsOn(Pack) - .DependsOn(CopyToLocalPackages); + Target Default => + _ => _ + .DependsOn(Pack) + .DependsOn(CopyToLocalPackages); - //Modifies VersionInfo.cs to embed version information into the shipped product. +//Modifies VersionInfo.cs to embed version information into the shipped product. ModifiableFileWithRestoreContentsOnDispose ModifyTemplatedVersionAndProductFilesWithValues() { var versionInfoFilePath = SourceDirectory / "Solution Items" / "VersionInfo.cs"; @@ -233,11 +250,11 @@ ModifiableFileWithRestoreContentsOnDispose ModifyTemplatedVersionAndProductFiles return versionInfoFile; } - //Modifies Product.wxs to embed version information into the shipped product. +//Modifies Product.wxs to embed version information into the shipped product. ModifiableFileWithRestoreContentsOnDispose UpdateMsiProductVersion() { var productWxsFilePath = RootDirectory / "installer" / "Octopus.Tentacle.Installer" / "Product.wxs"; - + var xmlDoc = new XmlDocument(); xmlDoc.Load(productWxsFilePath); @@ -260,7 +277,7 @@ ModifiableFileWithRestoreContentsOnDispose UpdateMsiProductVersion() void RunBuildFor(string framework, string runtimeId) { var configuration = $"Release-{framework}-{runtimeId}"; - + DotNetPublish(p => p .SetProject(SourceDirectory / "Tentacle.sln") .SetConfiguration(configuration) @@ -270,8 +287,8 @@ void RunBuildFor(string framework, string runtimeId) .SetVersion(FullSemVer)); } - // We need to use tar directly, because .NET utilities aren't able to preserve the file permissions - // Importantly, the Tentacle executable needs to be +x in the tar.gz file +// We need to use tar directly, because .NET utilities aren't able to preserve the file permissions +// Importantly, the Tentacle executable needs to be +x in the tar.gz file void TarGZipCompress(AbsolutePath inputDirectory, string fileSpec, AbsolutePath outputDirectory, string outputFile) { DockerTasks.DockerRun(settings => settings @@ -282,5 +299,5 @@ void TarGZipCompress(AbsolutePath inputDirectory, string fileSpec, AbsolutePath .SetArgs("tar", "-C", "/input", "-czvf", $"/output/{outputFile}", fileSpec, "--preserve-permissions")); } - public static int Main () => Execute(x => x.Default); -} + public static int Main() => Execute(x => x.Default); +} \ No newline at end of file diff --git a/linux-packages/packaging-scripts/unsigned/create-linux-packages.sh b/linux-packages/packaging-scripts/unsigned/create-linux-packages.sh new file mode 100644 index 000000000..38b7ee907 --- /dev/null +++ b/linux-packages/packaging-scripts/unsigned/create-linux-packages.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# Package files from INPUT_PATH, with executable permission and a /usr/bin symlink, into .deb and .rpm packages in OUTPUT_PATH. + +# NOTE: This file has been lifted wholesale from +# https://github.com/OctopusDeploy/tool-containers/blob/main/tool-linux-packages/linux/scripts/create-linux-packages.sh +# with almost no modification. It still leeds a lot of re-work. + +SIGN_TIMEOUT_SECONDS=240 # Signing aborts after this long. Healthy operation takes 15-45 seconds +if [[ -z "$VERSION" ]]; then + echo 'This script requires the environment variable VERSION - the version being packaged.' >&2 + exit 1 +fi +if [[ -z "$INPUT_PATH" ]]; then + echo 'This script requires the environment variable INPUT_PATH - the path containing binaries and related files to package.' >&2 + exit 1 +fi +if [[ -z "$COMMAND_FILE" ]]; then + echo 'This script requires the environment variable COMMAND_FILE - the path of a command (relative to INPUT_PATH) to symlink in /usr/bin/.' >&2 + exit 1 +fi +if [[ -z "$INSTALL_PATH" ]]; then + echo 'This script requires the environment variable INSTALL_PATH - the path the packages will install to.' >&2 + exit 1 +fi +if [[ -z "$OUTPUT_PATH" ]]; then + echo 'This script requires the environment variable OUTPUT_PATH - the path where packages should be written.' >&2 + exit 1 +fi +# Set the array FPM_OPTS to supply additional options to fpm. +# Set the array FPM_DEB_OPTS to supply additional options to fpm when building the .deb package. +# Set the array FPM_RPM_OPTS to supply additional options to fpm when building the .rpm package. + +which fpm >/dev/null || { + echo 'This script requires fpm and related tools, and is intended to be run in the container "octopusdeploy/package-linux-docker".' >&2 + exit 1 +} +which gpg1 >/dev/null || { + echo 'This script requires gpg1, and is intended to be run in the container "octopusdeploy/package-linux-docker".' >&2 + exit 1 +} +list_descendent_pids() { + local CHPIDS="$(ps --format pid= --ppid "$1")" + echo -n "$1," + for PID in $CHPIDS; do + list_descendent_pids "$PID" + done +} + + +echo "Preparing files to package." + +# Remove existing packages, fpm doesnt like to overwrite +rm -f *.{deb,rpm} || exit + +# Remove build files +if [[ -d tmp_usr_bin ]]; then + rm -rf tmp_usr_bin || exit +fi + +# Create executable symlink to include in package +mkdir tmp_usr_bin && ln -s "$INSTALL_PATH/$COMMAND_FILE" tmp_usr_bin/ || exit + +# Make sure the command has execute permissions +chmod a+x "$INPUT_PATH/$COMMAND_FILE" || exit + +echo "Creating .deb package." +set -ex +fpm --version "$VERSION" \ + --name "$PACKAGE_NAME" \ + --input-type dir \ + --output-type deb \ + --maintainer '' \ + --vendor 'Octopus Deploy' \ + --url 'https://octopus.com/' \ + --description "$PACKAGE_DESC" \ + --deb-no-default-config-files \ + "${FPM_DEB_OPTS[@]}" \ + "${FPM_OPTS[@]}" \ + "$INPUT_PATH/=$INSTALL_PATH/" \ + tmp_usr_bin/=/usr/bin/ +set +ex + +echo "Creating .rpm package." +set -ex +fpm --version "$VERSION" \ + --name "$PACKAGE_NAME" \ + --input-type dir \ + --output-type rpm \ + --maintainer '' \ + --vendor 'Octopus Deploy' \ + --url 'https://octopus.com/' \ + --description "$PACKAGE_DESC" \ + --verbose \ + --rpm-rpmbuild-define "_build_id_links none" \ + "${FPM_RPM_OPTS[@]}" \ + "${FPM_OPTS[@]}" \ + "$INPUT_PATH/=$INSTALL_PATH/" \ + tmp_usr_bin/=/usr/bin/ +set +ex + +# Remove build files +if [[ -d tmp_usr_bin ]]; then + rm -rf tmp_usr_bin || exit +fi + +# Move to output path +mv -f *.{deb,rpm} "$OUTPUT_PATH" || exit diff --git a/linux-packages/packaging-scripts/unsigned/package.sh b/linux-packages/packaging-scripts/unsigned/package.sh new file mode 100644 index 000000000..1e13407d8 --- /dev/null +++ b/linux-packages/packaging-scripts/unsigned/package.sh @@ -0,0 +1,60 @@ +#!/bin/bash +set -eux + +# Get the directory of the cucrrently-executing script so that we can call its siblings later. +# fpm appears to be sensitive to working directories so we don't want to just cd to anywhere. +SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") + +# Package tentacle from INPUT_PATH, with executable permission into .deb and .rpm packages (with a /usr/bin symlink) +# and a .tar.gz archive, placed in OUTPUT_PATH. + +echo Package versions will be $VERSION +echo Will pack content from $INPUT_PATH +echo Will write artifacts to $OUTPUT_PATH + +find $INPUT_PATH + +# Ensure that all the scripts in the input path have the correct attributes +find $INPUT_PATH -name "*.sh" -type f -exec chmod +x {} \; + +# Ensure that the output path exists +mkdir -p "$OUTPUT_PATH" + +set +ex + +# Prepare arguments to invoke the package-creation script. The script is from our tool container +# and actually creates the packages for us. + +architecture=$1 +if [ $architecture == "linux-arm64" ] ; then + DEB_PACKAGE_ARCHITECTURE="arm64"; + RPM_PACKAGE_ARCHITECTURE="aarch64"; +elif [ $architecture == "linux-arm" ] ; then + DEB_PACKAGE_ARCHITECTURE="armhf" + RPM_PACKAGE_ARCHITECTURE="armv7hl" +elif [ $architecture == "linux-x64" ] ; then + DEB_PACKAGE_ARCHITECTURE="x86_64"; + RPM_PACKAGE_ARCHITECTURE="x86_64"; +elif [ $architecture == "linux-musl-x64" ] ; then + DEB_PACKAGE_ARCHITECTURE="x86_64"; + RPM_PACKAGE_ARCHITECTURE="x86_64"; +fi +COMMAND_FILE=Tentacle +INSTALL_PATH=/opt/octopus/tentacle +PACKAGE_NAME="tentacle" +PACKAGE_DESC='Octopus Tentacle package' +FPM_OPTS=( + --after-install "$INPUT_PATH/after-install.sh" + --before-remove "$INPUT_PATH/before-uninstall.sh" +) +FPM_DEB_OPTS=( + --depends 'libssl1.0.0 | libssl1.0.2 | libssl1.1 | libssl3' + --architecture "$DEB_PACKAGE_ARCHITECTURE" +) +FPM_RPM_OPTS=( + --depends 'openssl-libs' + --architecture "$RPM_PACKAGE_ARCHITECTURE" +) + +# The script has interstitial errors so we can't use set -e here. +source $SCRIPT_DIR/create-linux-packages.sh || exit 1