Files
termscp/.github/workflows/release.yml
T
Christian Visintin afbc74113f
Deploy docs to GitHub Pages / deploy (push) Has been cancelled
Site / build-site (push) Has been cancelled
Install.sh / build (macos-latest) (push) Has been cancelled
Install.sh / build (ubuntu-latest) (push) Has been cancelled
CI / toolchain (push) Has been cancelled
CI / fmt (push) Has been cancelled
CI / install-scripts (push) Has been cancelled
CI / crates-ubuntu-latest (push) Has been cancelled
CI / crates-windows-latest (push) Has been cancelled
CI / doc (push) Has been cancelled
CI / deny (push) Has been cancelled
CI / crates-macos-latest (push) Has been cancelled
ci: migrate project automation to Just (#442)
* ci: migrate project automation to Just

Centralize build, test, release, dependency, hook, and website commands in Just recipes. Pin workflow tooling, use the repository toolchain, and run the complete validation set in CI.

* ci: codex being codex

* docs: update CLAUDE.md for just task runner migration

Reflect the switch to just recipes for build/test/clippy/fmt, note
dprint replacing raw rustfmt, and add a cross-platform code requirement.

* fix: resolve clippy warnings breaking CI on ubuntu and windows

Use clone() instead of implicit to_string() on already-owned String
values, gate the windows-only unused make_file_at import behind
cfg(posix), and fix unused mut / manual assign-op in the windows-only
localhost test.

* fix: fmt
2026-08-28 19:29:58 +02:00

467 lines
15 KiB
YAML

name: Release
on:
workflow_dispatch:
inputs:
version:
description: "Version to release, e.g. 1.1.0 (no leading v)"
required: true
type: string
dry_run:
description: "Dry run: build & compute everything, push/publish nothing"
required: true
type: boolean
default: true
permissions:
contents: read
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ inputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
token: ${{ secrets.RELEASE_PAT }}
persist-credentials: true
fetch-depth: 0
fetch-tags: true
- name: Validate version
env:
VERSION: ${{ inputs.version }}
run: |
if [[ ! "$VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
echo "invalid release version: $VERSION (expected MAJOR.MINOR.PATCH)" >&2
exit 2
fi
- name: Configure git identity
run: |
git config user.name "veeso"
git config user.email "christian.visintin@veeso.dev"
- name: Install git-cliff
uses: taiki-e/install-action@37f7c5781271959fb65b6b35224e28652ff2b63d # v2.87.0
with:
tool: git-cliff
- name: Install just
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- name: Bump version
env:
VERSION: ${{ inputs.version }}
run: dist/release/bump_version.sh "$VERSION" "$(date +%F)"
- name: Generate CHANGELOG
env:
VERSION: ${{ inputs.version }}
run: just changelog "$VERSION"
- name: Generate release notes
env:
VERSION: ${{ inputs.version }}
run: git-cliff --unreleased --tag "v$VERSION" --strip header -o RELEASE_NOTES.md
- name: Upload release notes
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-notes
path: RELEASE_NOTES.md
retention-days: 1
if-no-files-found: error
- name: Show diff (dry run)
if: ${{ inputs.dry_run }}
run: git --no-pager diff
- name: Commit & push version bump
if: ${{ !inputs.dry_run }}
env:
VERSION: ${{ inputs.version }}
run: |
rm -f RELEASE_NOTES.md
git add -A
git commit -m "chore: release v$VERSION"
git push origin HEAD:main
build:
needs: prepare
name: build-${{ matrix.target }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
kind: linux
deb_suffix: amd64
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
kind: linux
deb_suffix: arm64
- target: aarch64-apple-darwin
os: macos-latest
kind: macos
features: "--features smb-vendored"
- target: x86_64-apple-darwin
os: macos-latest
kind: macos
features: "--no-default-features --features keyring"
- target: x86_64-pc-windows-msvc
os: windows-latest
kind: windows
- target: aarch64-pc-windows-msvc
os: windows-11-arm
kind: windows
runs-on: ${{ matrix.os }}
env:
VERSION: ${{ needs.prepare.outputs.version }}
TARGET: ${{ matrix.target }}
FEATURES: ${{ matrix.features }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.dry_run && github.sha || 'main' }}
persist-credentials: false
fetch-depth: 0
fetch-tags: true
- name: Install just
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- name: Install Rust target
if: matrix.kind != 'windows'
run: rustup target add "$TARGET"
- name: Install Rust target
if: matrix.kind == 'windows'
run: rustup target add "$env:TARGET"
# ---- Linux: native per-arch build (x86_64 on ubuntu-latest, aarch64 on ubuntu-24.04-arm) ----
- name: Install dependencies (Linux)
if: matrix.kind == 'linux'
run: |
sudo apt-get update
sudo apt-get install -y \
make \
libgit2-dev \
build-essential \
pkg-config \
libbsd-dev \
libcap-dev \
libcups2-dev \
libgnutls28-dev \
libicu-dev \
libjansson-dev \
libkeyutils-dev \
libldap2-dev \
zlib1g-dev \
libpam0g-dev \
libacl1-dev \
libarchive-dev \
flex \
bison \
libntirpc-dev \
libtracker-sparql-3.0-dev \
libglib2.0-dev \
libdbus-1-dev \
libsasl2-dev \
libunistring-dev \
cpanminus
sudo cpanm Parse::Yapp::Driver
cargo install cargo-deb
- name: Build (Linux)
if: matrix.kind == 'linux'
run: just build_release "$TARGET" "--features smb-vendored"
- name: Build deb (Linux)
if: matrix.kind == 'linux'
run: just package_deb "$TARGET"
# ---- macOS ----
- name: Install deps (macOS)
if: matrix.kind == 'macos'
run: |
brew update
brew install bison cpanminus cups flex gettext gmp gnutls icu4c jansson \
libarchive libbsd libunistring libgit2 libtirpc openldap pkg-config zlib
for p in bison cups flex gettext gmp gnutls icu4c jansson libarchive \
libbsd libgit2 libtirpc libunistring openldap zlib; do brew link --force "$p"; done
cpanm Parse::Yapp::Driver
- name: Build (macOS)
if: matrix.kind == 'macos'
run: just build_release "$TARGET" "$FEATURES"
# ---- Windows ----
- name: Build (Windows)
if: matrix.kind == 'windows'
run: just build_release "$env:TARGET" "--features smb-vendored"
# ---- Package posix (tar.gz) ----
- name: Package (posix)
if: matrix.kind != 'windows'
run: |
mkdir -p artifact
cp "target/$TARGET/release/termscp" artifact/termscp
tar -czf "artifact/termscp-v$VERSION-$TARGET.tar.gz" -C artifact termscp
shasum -a 256 "artifact/termscp-v$VERSION-$TARGET.tar.gz" | awk '{print $1}' > "artifact/$TARGET.sha256"
# ---- Package windows (zip) ----
- name: Package (windows)
if: matrix.kind == 'windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force artifact | Out-Null
Copy-Item "target/$env:TARGET/release/termscp.exe" artifact/termscp.exe
Compress-Archive -Path artifact/termscp.exe -DestinationPath "artifact/termscp-v$env:VERSION-$env:TARGET.zip"
(Get-FileHash "artifact/termscp-v$env:VERSION-$env:TARGET.zip" -Algorithm SHA256).Hash.ToLower() | Out-File -NoNewline "artifact/$env:TARGET.sha256"
- name: Move deb into artifact dir (Linux)
if: matrix.kind == 'linux'
run: cp target/"$TARGET"/debian/*.deb artifact/
- name: Upload build artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: build-${{ matrix.target }}
path: artifact/*
retention-days: 1
if-no-files-found: error
publish-homebrew:
needs: [prepare, build]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- name: Download build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: build-*
path: dl
merge-multiple: true
- name: Checkout homebrew tap
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: veeso/homebrew-termscp
token: ${{ secrets.RELEASE_PAT }}
path: tap
persist-credentials: true
- name: Rewrite formula
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
SHA_MAC_ARM=$(cat dl/aarch64-apple-darwin.sha256)
SHA_MAC_X64=$(cat dl/x86_64-apple-darwin.sha256)
SHA_LIN_ARM=$(cat dl/aarch64-unknown-linux-gnu.sha256)
SHA_LIN_X64=$(cat dl/x86_64-unknown-linux-gnu.sha256)
BASE="https://github.com/veeso/termscp/releases/latest/download"
cat > tap/Formula/termscp.rb <<EOF
class Termscp < Formula
desc "A feature rich terminal file transfer and explorer with support for SCP/SFTP/FTP/S3/Kube/SMB/WebDAV"
homepage "https://termscp.rs/"
license "MIT"
version "$VERSION"
on_macos do
depends_on "bison"
depends_on "cups"
depends_on "flex"
depends_on "gettext"
depends_on "gmp"
depends_on "gnutls"
depends_on "icu4c"
depends_on "jansson"
depends_on "libarchive"
depends_on "libbsd"
depends_on "libgit2"
depends_on "libtirpc"
depends_on "libunistring"
depends_on "openldap"
depends_on "zlib"
on_arm do
url "$BASE/termscp-v$VERSION-aarch64-apple-darwin.tar.gz"
sha256 "$SHA_MAC_ARM"
end
on_intel do
url "$BASE/termscp-v$VERSION-x86_64-apple-darwin.tar.gz"
sha256 "$SHA_MAC_X64"
end
end
on_linux do
depends_on "dbus"
on_arm do
url "$BASE/termscp-v$VERSION-aarch64-unknown-linux-gnu.tar.gz"
sha256 "$SHA_LIN_ARM"
end
on_intel do
url "$BASE/termscp-v$VERSION-x86_64-unknown-linux-gnu.tar.gz"
sha256 "$SHA_LIN_X64"
end
end
def install
bin.install "termscp"
end
end
EOF
- name: Show formula (dry run)
if: ${{ inputs.dry_run }}
run: cat tap/Formula/termscp.rb
- name: Commit & push formula
if: ${{ !inputs.dry_run }}
run: |
cd tap
git config user.name "veeso"
git config user.email "christian.visintin@veeso.dev"
git add Formula/termscp.rb
git commit -m "termscp $VERSION"
git push
release:
needs: [prepare, build]
runs-on: ubuntu-latest
permissions:
contents: write
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
token: ${{ secrets.RELEASE_PAT }}
ref: ${{ inputs.dry_run && github.sha || 'main' }}
persist-credentials: true
- name: Download build artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: build-*
path: dl
merge-multiple: true
- name: Download release notes
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-notes
path: notes
- name: Patch chocolatey checksums & pack
run: |
set -euo pipefail
SHA_WIN_X64=$(cat dl/x86_64-pc-windows-msvc.sha256)
SHA_WIN_ARM=$(cat dl/aarch64-pc-windows-msvc.sha256)
PS=dist/chocolatey/tools/chocolateyinstall.ps1
# arm checksum is the first $checksum line, x64 the second (matches file order)
SHA_WIN_ARM="$SHA_WIN_ARM" SHA_WIN_X64="$SHA_WIN_X64" \
perl -0pi -e 'BEGIN{our $n=0} s/(\$checksum\s*=\s*'"'"')[0-9a-f]*('"'"')/ $n++==0 ? "${1}$ENV{SHA_WIN_ARM}${2}" : "${1}$ENV{SHA_WIN_X64}${2}" /ge' "$PS"
docker run --rm -v "$PWD/dist/chocolatey:/work" -w /work \
chocolatey/choco:latest choco pack --output-directory /work
- name: Assemble release assets
run: |
mkdir -p out
cp dl/*.tar.gz dl/*.deb dl/*.zip out/ 2>/dev/null || true
cp dist/chocolatey/*.nupkg out/
- name: Upload assets artifact (dry run)
if: ${{ inputs.dry_run }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-assets-dryrun
path: out/*
retention-days: 3
- name: Create GitHub release
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
gh release create "v$VERSION" out/* \
--title "v$VERSION" \
--notes-file notes/RELEASE_NOTES.md
publish-crate:
needs: [prepare, release]
if: ${{ !inputs.dry_run }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
persist-credentials: false
fetch-depth: 0
fetch-tags: true
- name: Install just
uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4
- name: Install dependencies (Linux)
run: |
sudo apt-get update
sudo apt-get install -y \
make \
libgit2-dev \
build-essential \
pkg-config \
libbsd-dev \
libcap-dev \
libcups2-dev \
libgnutls28-dev \
libicu-dev \
libjansson-dev \
libkeyutils-dev \
libldap2-dev \
zlib1g-dev \
libpam0g-dev \
libacl1-dev \
libarchive-dev \
flex \
bison \
libntirpc-dev \
libtracker-sparql-3.0-dev \
libglib2.0-dev \
libdbus-1-dev \
libsasl2-dev \
libunistring-dev \
cpanminus
sudo cpanm Parse::Yapp::Driver
- name: Authenticate to crates.io
id: auth
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: just publish_crate
publish-choco:
needs: [prepare, release]
if: ${{ !inputs.dry_run }}
runs-on: windows-latest
env:
VERSION: ${{ needs.prepare.outputs.version }}
steps:
- name: Download nupkg from release
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: gh release download "v$env:VERSION" --repo veeso/termscp --pattern "*.nupkg"
- name: Push to Chocolatey
env:
CHOCO_API_KEY: ${{ secrets.CHOCO_API_KEY }}
run: |
choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
choco push (Get-ChildItem *.nupkg).Name --source https://push.chocolatey.org/