Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ jobs:
artifact: linux
binary: dscpkg
test_args: -race
- os: ubuntu-24.04-arm
artifact: linux-arm64
binary: dscpkg
test_args: -race
- os: windows-latest
artifact: windows
binary: dscpkg.exe
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ jobs:
with:
name: dscpkg-release-linux
path: dist/linux
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: dscpkg-release-linux-arm64
path: dist/linux-arm64
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: dscpkg-release-windows
Expand Down
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,21 +205,21 @@ go build -trimpath -o dist/dscpkg ./cmd/dscpkg
```

The GitHub Actions workflow runs formatting, vetting, tests (with race detection on
Linux and macOS), and builds on Linux, Windows, and macOS. It uploads one executable artifact
for each runner operating system. Go build caching is enabled through
`actions/setup-go`.

CI also builds and inspects release archives and native installers. Windows MSI,
Ubuntu DEB, and containerized Fedora RPM tests cover fresh installation,
reinstallation/repair, upgrade replacement using isolated fixtures, uninstall,
and preservation of downloaded resources and caches. Windows checks system PATH
registration/removal and rejects downgrades. Installer tests run only on
Linux and macOS), and builds on Linux AMD64/ARM64, Windows, and macOS. It uploads
one executable artifact for each runner operating system and architecture.
Go build caching is enabled through `actions/setup-go`.

CI also builds and inspects release archives and native installers. Windows MSI
tests and Linux AMD64/ARM64 Ubuntu DEB and containerized Fedora RPM tests cover
fresh installation, reinstallation/repair, upgrade replacement using isolated
fixtures, uninstall, and preservation of downloaded resources and caches.
Windows checks system PATH registration/removal and rejects downgrades. Installer tests run only on
disposable GitHub-hosted runners, never on a developer workstation.

## Release downloads and installation

Releases include Linux AMD64 and Windows AMD64 archives, a macOS archive for the
native architecture of the macOS CI runner, Windows MSI, Linux DEB/RPM, and
Releases include Linux AMD64/ARM64 and Windows AMD64 archives, a macOS archive for the
native architecture of the macOS CI runner, Windows MSI, Linux AMD64/ARM64 DEB/RPM, and
`SHA256SUMS`. macOS filenames use Go's `darwin` platform name and the actual
`amd64` or `arm64` build architecture. Extract an archive and put its `bin`
directory on PATH, or use the appropriate native installer:
Expand All @@ -230,6 +230,9 @@ sudo apt install ./dscpkg_1.2.3_amd64.deb
sudo dnf install ./dscpkg-1.2.3-1.x86_64.rpm
```

On ARM64 Linux, use `dscpkg_1.2.3_arm64.deb` or
`dscpkg-1.2.3-1.aarch64.rpm` instead.

On Windows, run `dscpkg-v1.2.3-windows-amd64.msi` as administrator, or use:

```powershell
Expand Down Expand Up @@ -272,9 +275,9 @@ go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0
# Ensure Go's bin directory is on PATH. Go may download nFPM's required toolchain.
```

On each native platform, build one executable with CGO disabled and pass that
same binary to the packager. The output directory must be empty. For example,
in PowerShell on Windows:
On each native platform (including native AMD64 or ARM64 Linux), build one
executable with CGO disabled and pass that same binary to the packager. The output
directory must be empty. For example, in PowerShell on Windows:

```powershell
$env:CGO_ENABLED = '0'
Expand Down
10 changes: 6 additions & 4 deletions packaging/common.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ function Get-AssetNames {
param([string] $Version, [string] $Platform)
$packageVersion = & "$PSScriptRoot/version.ps1" -Version $Version
switch -Regex -CaseSensitive ($Platform) {
'^linux-amd64$' {
"dscpkg-$Version-linux-amd64.tar.gz"
"dscpkg_${packageVersion}_amd64.deb"
"dscpkg-${packageVersion}-1.x86_64.rpm"
'^linux-(amd64|arm64)$' {
$arch = $Matches[1]
$rpmArch = if ($arch -eq 'arm64') { 'aarch64' } else { 'x86_64' }
"dscpkg-$Version-$Platform.tar.gz"
"dscpkg_${packageVersion}_${arch}.deb"
"dscpkg-${packageVersion}-1.$rpmArch.rpm"
}
'^windows-amd64$' {
"dscpkg-$Version-windows-amd64.zip"
Expand Down
7 changes: 7 additions & 0 deletions packaging/linux/architecture.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

case "$(uname -m)" in
x86_64) export PACKAGE_ARCH=amd64 RPM_ARCH=x86_64 ;;
aarch64|arm64) export PACKAGE_ARCH=arm64 RPM_ARCH=aarch64 ;;
*) echo "Unsupported Linux package architecture: $(uname -m)" >&2; exit 1 ;;
esac
8 changes: 5 additions & 3 deletions packaging/linux/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ trap 'printf "ERROR %s:%s: %s\n" "$0" "$LINENO" "$BASH_COMMAND" >&2' ERR

[[ $# == 3 ]] || { echo "Usage: $0 VERSION BINARY OUTPUT_DIRECTORY" >&2; exit 1; }
root=$(cd "$(dirname "$0")/../.." && pwd)
# shellcheck source=packaging/linux/architecture.sh
source "$root/packaging/linux/architecture.sh"
export VERSION
VERSION=$(pwsh -NoProfile -File "$root/packaging/version.ps1" -Version "$1")
export BINARY_PATH
BINARY_PATH=$(realpath "$2")
output=$(realpath "$3")
cd "$root"
[[ $(go version -m "$BINARY_PATH") == *'GOOS=linux'* ]]
[[ $(go version -m "$BINARY_PATH") == *'GOARCH=amd64'* ]]
[[ $(go version -m "$BINARY_PATH") == *"GOARCH=$PACKAGE_ARCH"* ]]
nfpm package --config packaging/linux/nfpm.yaml --packager deb \
--target "$output/dscpkg_${VERSION}_amd64.deb"
--target "$output/dscpkg_${VERSION}_${PACKAGE_ARCH}.deb"
nfpm package --config packaging/linux/nfpm.yaml --packager rpm \
--target "$output/dscpkg-${VERSION}-1.x86_64.rpm"
--target "$output/dscpkg-${VERSION}-1.$RPM_ARCH.rpm"
10 changes: 6 additions & 4 deletions packaging/linux/inspect.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ trap 'printf "ERROR %s:%s: %s\n" "$0" "$LINENO" "$BASH_COMMAND" >&2' ERR

[[ $# == 3 ]] || { echo "Usage: $0 VERSION BINARY OUTPUT_DIRECTORY" >&2; exit 1; }
root=$(cd "$(dirname "$0")/../.." && pwd)
# shellcheck source=packaging/linux/architecture.sh
source "$root/packaging/linux/architecture.sh"
version=$(pwsh -NoProfile -File "$root/packaging/version.ps1" -Version "$1")
binary=$(realpath "$2")
output=$(realpath "$3")
deb="$output/dscpkg_${version}_amd64.deb"
rpm="$output/dscpkg-${version}-1.x86_64.rpm"
deb="$output/dscpkg_${version}_${PACKAGE_ARCH}.deb"
rpm="$output/dscpkg-${version}-1.$RPM_ARCH.rpm"
[[ $(dpkg-deb -f "$deb" Package) == dscpkg ]]
[[ $(dpkg-deb -f "$deb" Version) == "$version" ]]
[[ $(dpkg-deb -f "$deb" Architecture) == amd64 ]]
[[ $(dpkg-deb -f "$deb" Architecture) == "$PACKAGE_ARCH" ]]
[[ -z $(dpkg-deb -f "$deb" Depends) ]]
[[ $(rpm -qp --qf '%{NAME} %{VERSION} %{RELEASE} %{ARCH}' "$rpm") == "dscpkg $version 1 x86_64" ]]
[[ $(rpm -qp --qf '%{NAME} %{VERSION} %{RELEASE} %{ARCH}' "$rpm") == "dscpkg $version 1 $RPM_ARCH" ]]
[[ -z $(rpm -qp --scripts "$rpm") ]]
rpm --checksig "$rpm"
stage=$(mktemp -d)
Expand Down
2 changes: 1 addition & 1 deletion packaging/linux/nfpm.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: dscpkg
arch: amd64
arch: ${PACKAGE_ARCH}
platform: linux
version: ${VERSION}
maintainer: dscpkg contributors <Bpoe@users.noreply.github.com>
Expand Down
7 changes: 5 additions & 2 deletions packaging/linux/test-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ if [[ $# != 7 || $1 != --disposable || ${GITHUB_ACTIONS:-} != true ||
echo "Usage (disposable GitHub-hosted runner/container only): $0 --disposable deb|rpm VERSION ASSETS BINARY FIXTURES LOGS" >&2
exit 1
fi
root=$(cd "$(dirname "$0")/../.." && pwd)
# shellcheck source=packaging/linux/architecture.sh
source "$root/packaging/linux/architecture.sh"
format=$2
version=${3#v}
assets=$(realpath "$4")
Expand All @@ -33,8 +36,8 @@ for file in "${sentinels[@]}"; do printf 'preserve-me\n' > "$file"; done
installed=false

package_path() {
if [[ $format == deb ]]; then printf '%s/dscpkg_%s_amd64.deb' "$1" "$2"
else printf '%s/dscpkg-%s-1.x86_64.rpm' "$1" "$2"; fi
if [[ $format == deb ]]; then printf '%s/dscpkg_%s_%s.deb' "$1" "$2" "$PACKAGE_ARCH"
else printf '%s/dscpkg-%s-1.%s.rpm' "$1" "$2" "$RPM_ARCH"; fi
}
install_package() {
if [[ $format == deb ]]; then dpkg -i "$1"; else rpm -U --replacepkgs "$1"; fi
Expand Down
3 changes: 2 additions & 1 deletion packaging/release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ if ($Operation -eq 'Preflight') {
if (-not $AssetDirectory) { throw 'Draft creation requires AssetDirectory.' }
$downloads = (Resolve-Path -LiteralPath $AssetDirectory).Path
$directories = @(Get-ChildItem -LiteralPath $downloads -Force)
Assert-Equal (($directories.Name | Sort-Object) -join ',') 'linux,macos,windows' 'Downloaded platform directories'
Assert-Equal (($directories.Name | Sort-Object) -join ',') 'linux,linux-arm64,macos,windows' 'Downloaded platform directories'
foreach ($directory in $directories) {
if (-not $directory.PSIsContainer) { throw "Expected a platform directory: $($directory.Name)" }
}
Expand All @@ -72,6 +72,7 @@ Assert-Equal $macArchives.Count 1 'macOS archive count'
$macPlatform = [regex]::Match($macArchives[0].Name, $macPattern).Groups[1].Value
$platforms = @(
@{ Directory = 'linux'; Platform = 'linux-amd64' },
@{ Directory = 'linux-arm64'; Platform = 'linux-arm64' },
@{ Directory = 'windows'; Platform = 'windows-amd64' },
@{ Directory = 'macos'; Platform = $macPlatform }
)
Expand Down
35 changes: 33 additions & 2 deletions packaging/test-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ $releaseScript = Join-Path $PSScriptRoot 'release.ps1'
$stage = Join-Path ([IO.Path]::GetTempPath()) ("dscpkg-release-test-" + [guid]::NewGuid())
$global:DscpkgReleaseTestState = @{
Commit = ''; TagObject = $null; Releases = '[[]]'; FailAPI = $false
Created = 0; Assets = @(); Arguments = @()
Created = 0; Assets = @(); Arguments = @(); ChecksumLines = @()
}

function global:gh {
Expand Down Expand Up @@ -32,6 +32,9 @@ function global:gh {
$state.Arguments = $Arguments
$state.Assets = @($Arguments | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } |
ForEach-Object { Split-Path $_ -Leaf })
$manifest = @($Arguments | Where-Object { (Split-Path $_ -Leaf) -ceq 'SHA256SUMS' })
Assert-Equal $manifest.Count 1 'Combined checksum manifest count'
$state.ChecksumLines = @(Get-Content -LiteralPath $manifest[0])
return
}
throw "Unexpected gh command: $Arguments"
Expand All @@ -54,6 +57,7 @@ function New-TestAssets {
param([string] $Directory)
foreach ($entry in @(
@{ Name = 'linux'; Platform = 'linux-amd64' },
@{ Name = 'linux-arm64'; Platform = 'linux-arm64' },
@{ Name = 'windows'; Platform = 'windows-amd64' },
@{ Name = 'macos'; Platform = 'darwin-arm64' }
)) {
Expand Down Expand Up @@ -142,7 +146,18 @@ try {
New-TestAssets $downloads
& $releaseScript -Operation Draft @parameters -AssetDirectory $downloads
Assert-Equal $global:DscpkgReleaseTestState.Created 1 'Draft creation count'
Assert-Equal $global:DscpkgReleaseTestState.Assets.Count 7 'Draft asset count'
$expectedAssets = @(
'dscpkg-v1.2.3-linux-amd64.tar.gz', 'dscpkg_1.2.3_amd64.deb', 'dscpkg-1.2.3-1.x86_64.rpm',
'dscpkg-v1.2.3-linux-arm64.tar.gz', 'dscpkg_1.2.3_arm64.deb', 'dscpkg-1.2.3-1.aarch64.rpm',
'dscpkg-v1.2.3-windows-amd64.zip', 'dscpkg-v1.2.3-windows-amd64.msi',
'dscpkg-v1.2.3-darwin-arm64.tar.gz', 'SHA256SUMS'
)
Assert-Equal (($global:DscpkgReleaseTestState.Assets | Sort-Object) -join ',') `
(($expectedAssets | Sort-Object) -join ',') 'Draft assets for both Linux architectures'
$expectedChecksums = @(Get-ChildItem -LiteralPath $downloads -Recurse -File -Filter SHA256SUMS |
ForEach-Object { Get-Content -LiteralPath $_.FullName })
Assert-Equal (($global:DscpkgReleaseTestState.ChecksumLines | Sort-Object) -join "`n") `
(($expectedChecksums | Sort-Object) -join "`n") 'Combined platform checksums'
foreach ($flag in @('--draft', '--verify-tag', '--generate-notes')) {
if ($global:DscpkgReleaseTestState.Arguments -cnotcontains $flag) { throw "Missing release flag: $flag" }
}
Expand All @@ -156,6 +171,22 @@ try {
Assert-Rejected { & $releaseScript -Operation Draft @parameters -AssetDirectory $downloads } 'gh failed'
$global:DscpkgReleaseTestState.FailAPI = $false

$armDirectory = Join-Path $downloads 'linux-arm64'
$savedArmDirectory = Join-Path $stage 'saved-linux-arm64'
Move-Item -LiteralPath $armDirectory -Destination $savedArmDirectory
Assert-Rejected { & $releaseScript -Operation Draft @parameters -AssetDirectory $downloads } 'Downloaded platform directories'
Move-Item -LiteralPath $savedArmDirectory -Destination $armDirectory
foreach ($name in @(Get-AssetNames 'v1.2.3' 'linux-arm64')) {
$armAsset = Join-Path $armDirectory $name
$armBytes = [IO.File]::ReadAllBytes($armAsset)
Add-Content -LiteralPath $armAsset -Value corrupt
Assert-Rejected { & $releaseScript -Operation Draft @parameters -AssetDirectory $downloads } 'Checksum for'
[IO.File]::WriteAllBytes($armAsset, $armBytes)
Remove-Item -LiteralPath $armAsset
Assert-Rejected { & $releaseScript -Operation Draft @parameters -AssetDirectory $downloads } 'Files in'
[IO.File]::WriteAllBytes($armAsset, $armBytes)
}

$extra = Join-Path $downloads 'linux/unexpected.txt'
Set-Content -LiteralPath $extra -Value extra
Assert-Rejected { & $releaseScript -Operation Draft @parameters -AssetDirectory $downloads } 'Files in'
Expand Down
Loading