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
117 changes: 97 additions & 20 deletions .github/workflows/release_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ concurrency:
cancel-in-progress: true

env:
FEDORA_IMAGE: fedora:44 # pinned for reproducible packaging; bump deliberately
# Container image pins live in packaging/images.env (shared with
# packaging/test-local.sh) and are sourced by the steps that need them.
# Debian's builder also runs a full cargo build inside debian:12 in
# packaging/test-local.sh.

jobs:
build:
Comment on lines 27 to 28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security · medium
This job declares no permissions key, so it inherits the default (potentially broad) token permissions, while release_windows.yml was updated to set permissions: contents: read. Since this job only builds and uploads artifacts, restrict it the same way for least-privilege consistency.

Suggestion:

Suggested change
jobs:
build:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read

runs-on: ubuntu-22.04
timeout-minutes: 180
permissions:
contents: read
permissions:
contents: read
Comment on lines +31 to 34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · high
This job declares permissions: twice. Duplicate YAML keys are invalid — GitHub's YAML parser may reject the workflow outright (breaking the whole release pipeline) or silently keep only one value depending on the parser version. Keep a single permissions: contents: read block.

Suggestion:

Suggested change
permissions:
contents: read
permissions:
contents: read
permissions:
contents: read

strategy:
Expand All @@ -36,6 +41,7 @@ jobs:
with:
submodules: recursive
fetch-depth: 0 # git describe needs history for the dispatch fallback version
persist-credentials: false # the checkout is only built, never pushed

# Register qemu binfmt handlers so `docker run --platform linux/arm64`
# works on the arm64 matrix leg (needed for the debian:12 deb builder).
Expand All @@ -52,6 +58,12 @@ jobs:
if [ -z "$VERSION" ]; then
VERSION="0.0.0-ci"
fi
# Validate the grammar up front, so a non-semver tag fails here with a clear message
# instead of deep inside a packaging script (the same grammar the builders enforce).
if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "derived version '$VERSION' is not X.Y.Z[-suffix]; tag a semver release" >&2
exit 1
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "Version extracted: $VERSION"

Expand Down Expand Up @@ -121,72 +133,125 @@ jobs:
# runs the deb builder inside an arm64 debian:12 container. The same
# scripts run locally via packaging/test-local.sh.
- name: Create Debian packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
ARCH=${{ matrix.arch }}
if [ "$ARCH" = "x86_64" ]; then DEBARCH=amd64; else DEBARCH=arm64; fi
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/deb/build.sh \
--package node --version "${{ steps.get_version.outputs.VERSION }}" \
--package node --version "$VERSION" \
--debarch $DEBARCH \
--binaries-dir /work/target/$ARCH-unknown-linux-gnu/release \
--out /work/dist
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/deb/build.sh \
--package gui --version "${{ steps.get_version.outputs.VERSION }}" \
--package gui --version "$VERSION" \
--debarch $DEBARCH \
--gui-binary /work/target/$ARCH-unknown-linux-gnu/release/node-gui \
--repo-root /work --out /work/dist

# Arch-matched container (qemu on the arm64 leg): native strip works and
# the binaries run under emulation so help2man generates real man pages.
- name: Create RPM packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work ${{ env.FEDORA_IMAGE }} \
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/rpm/build.sh \
--package node --rpmarch $ARCH \
--version "${{ steps.get_version.outputs.VERSION }}" \
--version "$VERSION" \
--binaries-dir /work/target/$ARCH-unknown-linux-gnu/release \
--out /work/dist
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work ${{ env.FEDORA_IMAGE }} \
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/rpm/build.sh \
--package gui --rpmarch $ARCH \
--version "${{ steps.get_version.outputs.VERSION }}" \
--version "$VERSION" \
--gui-binary /work/target/$ARCH-unknown-linux-gnu/release/node-gui \
--repo-root /work --out /work/dist

# Install smoke tests in fresh containers (same images as production use)
- name: Smoke test Debian packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
ARCH=${{ matrix.arch }}
if [ "$ARCH" = "x86_64" ]; then DEBARCH=amd64; else DEBARCH=arm64; fi
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/checks/smoke-deb.sh dist/Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${DEBARCH}.deb mintlayer-node node
packaging/checks/smoke-deb.sh dist/Mintlayer_Node_linux_${VERSION}_${DEBARCH}.deb mintlayer-node node
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work debian:12 \
packaging/checks/smoke-deb.sh dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${DEBARCH}.deb mintlayer-node-gui gui
packaging/checks/smoke-deb.sh dist/Mintlayer_Node_GUI_linux_${VERSION}_${DEBARCH}.deb mintlayer-node-gui gui

- name: Smoke test RPM packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
ARCH=${{ matrix.arch }}
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work ${{ env.FEDORA_IMAGE }} \
packaging/checks/smoke-rpm.sh dist/Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${ARCH}.rpm mintlayer-node node
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work ${{ env.FEDORA_IMAGE }} \
packaging/checks/smoke-rpm.sh dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${ARCH}.rpm mintlayer-node-gui gui
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/checks/smoke-rpm.sh dist/Mintlayer_Node_linux_${VERSION}_${ARCH}.rpm mintlayer-node node
docker run --rm --platform linux/$ARCH -v "$PWD":/work -w /work $FEDORA_IMAGE \
packaging/checks/smoke-rpm.sh dist/Mintlayer_Node_GUI_linux_${VERSION}_${ARCH}.rpm mintlayer-node-gui gui

# Arch images are amd64-only: the x86_64 leg is arch-matched (native
# strip, ldd-based dependencies, help2man man pages), the arm64 leg
# repackages cross-target with stubs — same as the local rpm legs.
- name: Create Arch packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/arch/build.sh \
Comment on lines +204 to +207

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
On the arm64 matrix leg this step runs an amd64-only container ($ARCH_IMAGE) without --platform. That requires an amd64 binfmt handler on the arm64 runner, but the setup step above only documents/registers handlers so --platform linux/arm64 works on the x86_64 leg. If the setup action doesn't register the amd64 (qemu-x86_64) handler on arm64 runners, this step fails on the arm64 leg. Either register the needed handler explicitly or document that the setup action covers all architectures.

--package node --arch "$ARCH" \
--version "$VERSION" \
--binaries-dir /work/target/$ARCH-unknown-linux-gnu/release \
--out /work/dist
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/arch/build.sh \
--package gui --arch "$ARCH" \
--version "$VERSION" \
--gui-binary /work/target/$ARCH-unknown-linux-gnu/release/node-gui \
--repo-root /work --out /work/dist

# x86_64 leg: arch-matched container (native strip, ldd-based
# dependencies, help2man man pages), so the package installs natively.
# aarch64 leg: the same amd64-only image installs the foreign-arch
# package with IgnoreArch and runs the binaries through the qemu binfmt
# handlers registered above — the same emulation the deb/rpm legs use
# for their arm64 smoke tests.
- name: Smoke test Arch packages
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
source packaging/images.env
ARCH=${{ matrix.arch }}
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/checks/smoke-arch.sh dist/Mintlayer_Node_linux_${VERSION}_${ARCH}.pkg.tar.zst mintlayer-node node "$ARCH"
docker run --rm -v "$PWD":/work -w /work $ARCH_IMAGE \
packaging/checks/smoke-arch.sh dist/Mintlayer_Node_GUI_linux_${VERSION}_${ARCH}.pkg.tar.zst mintlayer-node-gui gui "$ARCH"

- name: Package Mintlayer Node (without GUI) as tar.gz
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
BINARY_LIST: ${{ inputs.binary_list }}
run: |
mkdir -p Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}
IFS=',' read -ra BINARIES <<< "${{ inputs.binary_list }}"
mkdir -p Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}
IFS=',' read -ra BINARIES <<< "$BINARY_LIST"
for binary in "${BINARIES[@]}"; do
cp target/${{ matrix.arch }}-unknown-linux-gnu/release/$binary Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}/mintlayer-$binary
cp target/${{ matrix.arch }}-unknown-linux-gnu/release/$binary Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}/mintlayer-$binary
done
tar -czvf Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.tar.gz Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}
tar -czvf Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}.tar.gz Mintlayer_Node_linux_${VERSION}_${{ matrix.arch }}

- name: Package Mintlayer Node GUI as tar.gz
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
mkdir -p Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}
cp target/${{ matrix.arch }}-unknown-linux-gnu/release/node-gui Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}/mintlayer-node-gui
tar -czvf Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.tar.gz Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}
mkdir -p Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}
cp target/${{ matrix.arch }}-unknown-linux-gnu/release/node-gui Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}/mintlayer-node-gui
tar -czvf Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}.tar.gz Mintlayer_Node_GUI_linux_${VERSION}_${{ matrix.arch }}

- name: Upload Node DEB Artifact (without GUI)
uses: actions/upload-artifact@v4
Expand All @@ -212,6 +277,18 @@ jobs:
name: Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_rpm
path: dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.rpm

- name: Upload Node PKG Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_pkg
path: dist/Mintlayer_Node_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.pkg.tar.zst

- name: Upload GUI PKG Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}_pkg
path: dist/Mintlayer_Node_GUI_linux_${{ steps.get_version.outputs.VERSION }}_${{ matrix.arch }}.pkg.tar.zst

- name: List tar.gz files
run: |
echo "Matching tar.gz files:"
Expand Down
71 changes: 54 additions & 17 deletions .github/workflows/release_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ on:
binary_list:
required: true
type: string
workflow_dispatch:
inputs:
binary_list:
description: 'Binaries bundled into the plain zip artifacts (the NSIS installers always ship the full set)'
required: false
type: string
default: 'api-blockchain-scanner-daemon,api-web-server,dns-server,node-daemon,wallet-address-generator,wallet-cli,wallet-rpc-daemon'

jobs:
build:
runs-on: windows-latest
# Note: bounds the whole job, and in particular the installer smoke test below (every
# installed binary is executed with a per-process timeout as well; this is the backstop).
timeout-minutes: 120
permissions:
contents: read
env:
# With the default CARGO_HOME the job will fail due to Windows path length limit when
# checking out the trezor firmware repo. E.g. one of the paths looks like this:
Expand All @@ -19,11 +31,26 @@ jobs:
- uses: actions/checkout@v5
with:
submodules: recursive
fetch-depth: 0 # git describe needs history for the dispatch fallback version
persist-credentials: false # the checkout is only built, never pushed

- name: Extract version from tag
id: get_version
run: |
$VERSION = $env:GITHUB_REF -replace 'refs/tags/', '' -replace '^v', ''
if ($VERSION -eq $env:GITHUB_REF) {
$VERSION = git describe --tags --abbrev=0 2>$null
$VERSION = $VERSION -replace '^v', ''
Comment on lines +42 to +43

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
The fallback version from git describe --tags --abbrev=0 is not validated against any grammar before being exported. A non-semver tag (e.g. v1.2 or a date-based tag) yields VERSION like 1.2, which makes create-nsis-installers.ps1 throw mid-release ("invalid version"), failing the workflow at the packaging step instead of being caught here. Validate or normalize the version at extraction time so failures surface early with a clear message.

Suggestion:

Suggested change
$VERSION = git describe --tags --abbrev=0 2>$null
$VERSION = $VERSION -replace '^v', ''
$VERSION = git describe --tags --abbrev=0 2>$null
$VERSION = $VERSION -replace '^v', ''
if ($VERSION -notmatch '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$') {
throw "derived version '$VERSION' is not X.Y.Z[-suffix]; tag a semver release"
}

}
Comment on lines +41 to +44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
On workflow_dispatch there is no tag: the regex replace leaves GITHUB_REF (refs/heads/) untouched, so the fallback runs git describe --tags --abbrev=0, which returns the newest tag anywhere in the fetched history — not the version of the code being dispatched. A manual run from an older branch produces installers stamped with an unrelated, newer version (and the semver check then passes). Consider deriving the version from git describe --tags --exact-match first and falling back to a commit-based dev version (e.g. 0.0.0-<short-sha>) instead of the nearest tag.

Suggestion:

Suggested change
if ($VERSION -eq $env:GITHUB_REF) {
$VERSION = git describe --tags --abbrev=0 2>$null
$VERSION = $VERSION -replace '^v', ''
}
if ($VERSION -eq $env:GITHUB_REF) {
$VERSION = git describe --tags --exact-match 2>$null
$VERSION = $VERSION -replace '^v', ''
if ([string]::IsNullOrEmpty($VERSION)) {
$VERSION = "0.0.0-$(git rev-parse --short HEAD)"
}
}

if ([string]::IsNullOrEmpty($VERSION)) {
$VERSION = "0.0.0-ci"
}
# Validate the grammar up front, so a non-semver tag fails here with a clear message
# instead of deep inside a packaging script (the same grammar the NSIS tooling and the
# Linux builders enforce).
if ($VERSION -notmatch '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$') {
throw "derived version '$VERSION' is not X.Y.Z[-suffix]; tag a semver release"
}
echo "VERSION=$VERSION" >> $env:GITHUB_OUTPUT
echo "Version extracted: $VERSION"
shell: pwsh
Expand All @@ -36,12 +63,17 @@ jobs:
- name: Build Mintlayer Node and GUI
run: cargo build --release --locked --features trezor,ledger

# Note: the version (and the workflow input) are passed through environment
# variables instead of direct ${{ }} interpolation into the run blocks, so
# that a crafted tag/ref/input cannot inject shell into the steps.
- name: Package Mintlayer Node
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
BINARY_LIST: ${{ inputs.binary_list }}
run: |
$VERSION = "${{ steps.get_version.outputs.VERSION }}"
$DEST = "Mintlayer_Node_win_${VERSION}"
$DEST = "Mintlayer_Node_win_$env:VERSION"
New-Item -ItemType Directory -Path $DEST
$binary_list = "${{ inputs.binary_list }}" -split ',' | Where-Object { $_ -ne "node-gui" }
$binary_list = $env:BINARY_LIST -split ',' | Where-Object { $_ -ne "node-gui" }
foreach ($binary in $binary_list) {
$binary = $binary.Trim()
if (Test-Path "target\release\$binary.exe") {
Expand All @@ -63,26 +95,25 @@ jobs:
run: .\build-tools\win\create-license.ps1
shell: pwsh

- name: Create NSIS Installer Script
run: .\build-tools\win\create-nsis-script.ps1 -Version "${{ steps.get_version.outputs.VERSION }}"
- name: Create NSIS installers (node + GUI)
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: .\build-tools\win\create-nsis-installers.ps1 -Version $env:VERSION
shell: pwsh

- name: Build NSIS Installer
- name: Smoke test installers (silent install/uninstall)
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
$makensisPath = (Get-Command makensis.exe).Source
Write-Host "Using makensis at: $makensisPath"
& $makensisPath installer.nsi
shell: pwsh

- name: Display NSIS Script
run: |
Get-Content -Path installer.nsi
.\build-tools\win\smoke-install.ps1 -Installer "Mintlayer_Node_win_${env:VERSION}_Setup.exe" -AppName "Mintlayer Node" -Kind node -Version $env:VERSION
.\build-tools\win\smoke-install.ps1 -Installer "Mintlayer_Node_GUI_win_${env:VERSION}_Setup.exe" -AppName "Mintlayer Node GUI" -Kind gui -Version $env:VERSION
shell: pwsh

- name: Package Mintlayer Node GUI
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
$VERSION = "${{ steps.get_version.outputs.VERSION }}"
$DEST = "Mintlayer_Node_GUI_win_${VERSION}"
$DEST = "Mintlayer_Node_GUI_win_$env:VERSION"
New-Item -ItemType Directory -Path $DEST
Copy-Item "target\release\node-gui.exe" -Destination $DEST
Compress-Archive -Path $DEST -DestinationPath "${DEST}.zip"
Expand All @@ -100,7 +131,13 @@ jobs:
name: Mintlayer_Node_GUI_win_${{ steps.get_version.outputs.VERSION }}
path: Mintlayer_Node_GUI_win_${{ steps.get_version.outputs.VERSION }}.zip

- name: Upload NSIS Installer Artifact
- name: Upload Node NSIS Installer Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_win_${{ steps.get_version.outputs.VERSION }}_Setup
path: Mintlayer_Node_win_${{ steps.get_version.outputs.VERSION }}_Setup.exe

- name: Upload GUI NSIS Installer Artifact
uses: actions/upload-artifact@v4
with:
name: Mintlayer_Node_GUI_win_${{ steps.get_version.outputs.VERSION }}_Setup
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,9 @@ build-tools/block-data-plots/output
packaging/dist/
/dist/
target-debian/

# Rendered NSIS scripts and installers (build-tools/win/create-nsis-installers.ps1)
/installer-node.nsi
/installer-gui.nsi
/Mintlayer_Node_win_*_Setup.exe
/Mintlayer_Node_GUI_win_*_Setup.exe
Loading
Loading