Skip to content

Commit 7b23246

Browse files
committed
prepare v1.0.0 release pipeline
1 parent 750cb44 commit 7b23246

3 files changed

Lines changed: 193 additions & 58 deletions

File tree

.github/workflows/release.yml

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Release
22

3-
# Builds self-contained binaries for every supported platform and publishes
3+
# Builds self-contained binaries for every platform and publishes
44
# them to a GitHub Release. Trigger by pushing a version tag:
55
#
66
# git tag v1.0.0 && git push origin v1.0.0
@@ -13,7 +13,6 @@ on:
1313
push:
1414
tags:
1515
- "v*"
16-
workflow_dispatch:
1716

1817
permissions:
1918
contents: write
@@ -26,90 +25,141 @@ jobs:
2625
fail-fast: false
2726
matrix:
2827
include:
29-
- os: macos-14
30-
target: aarch64-apple-darwin
31-
# NOTE: Intel macOS (x86_64-apple-darwin) is temporarily omitted —
32-
# GitHub's macos-13 Intel runners are being deprecated and queue
33-
# unpredictably. Intel users can build from source for now; a
34-
# cross-compiled Intel binary will be added in a follow-up.
35-
- os: ubuntu-22.04
28+
- os: macos-latest
29+
target: x86_64-apple-darwin
30+
- os: ubuntu-latest
3631
target: x86_64-unknown-linux-gnu
37-
- os: ubuntu-22.04-arm
38-
target: aarch64-unknown-linux-gnu
39-
- os: windows-2022
32+
- os: windows-latest
4033
target: x86_64-pc-windows-gnu
4134

4235
steps:
4336
- uses: actions/checkout@v4
4437

45-
# ── macOS: static-link Homebrew OpenSSL ──────────────────────
46-
- name: Build (macOS)
47-
if: runner.os == 'macOS'
48-
run: |
49-
brew install openssl@3
50-
make STATIC_SSL=1 OPENSSL_DIR="$(brew --prefix openssl@3)"
51-
./build/qs version
52-
53-
# ── Linux: static-link distro OpenSSL ────────────────────────
54-
- name: Build (Linux)
38+
# ── Dependencies ─────────────────────────────────────────────
39+
- name: Install OpenSSL (Linux)
5540
if: runner.os == 'Linux'
5641
run: |
5742
sudo apt-get update
5843
sudo apt-get install -y libssl-dev
59-
make SSL_LIBS="-l:libssl.a -l:libcrypto.a"
60-
./build/qs version
6144
62-
# ── Windows: MSYS2 / UCRT64, static-link OpenSSL ─────────────
63-
- name: Set up MSYS2
45+
- name: Install OpenSSL (macOS)
46+
if: runner.os == 'macOS'
47+
run: brew install openssl@3
48+
49+
- name: Setup MSYS2 (Windows)
6450
if: runner.os == 'Windows'
6551
uses: msys2/setup-msys2@v2
6652
with:
67-
msystem: UCRT64
53+
msystem: MINGW64
6854
update: true
6955
install: >-
56+
mingw-w64-x86_64-gcc
57+
mingw-w64-x86_64-openssl
58+
mingw-w64-x86_64-pkgconf
7059
make
71-
mingw-w64-ucrt-x86_64-gcc
72-
mingw-w64-ucrt-x86_64-openssl
60+
61+
# ── Build (statically linked) ───────────────────────────────
62+
- name: Build (macOS)
63+
if: runner.os == 'macOS'
64+
run: make STATIC_SSL=1 OPENSSL_DIR="$(brew --prefix openssl@3)"
65+
66+
- name: Build (Linux)
67+
if: runner.os == 'Linux'
68+
run: make SSL_LIBS="-l:libssl.a -l:libcrypto.a"
7369

7470
- name: Build (Windows)
7571
if: runner.os == 'Windows'
7672
shell: msys2 {0}
73+
run: make SSL_LIBS="/mingw64/lib/libssl.a /mingw64/lib/libcrypto.a"
74+
75+
# ── Verify ───────────────────────────────────────────────────
76+
- name: Verify binary (Linux / macOS)
77+
if: runner.os != 'Windows'
78+
run: ./build/qs version
79+
80+
- name: Verify binary (Windows)
81+
if: runner.os == 'Windows'
82+
shell: msys2 {0}
83+
run: ./build/qs.exe version
84+
85+
- name: Check binary dependencies (Linux)
86+
if: runner.os == 'Linux'
7787
run: |
78-
make SSL_LIBS="/ucrt64/lib/libssl.a /ucrt64/lib/libcrypto.a"
79-
./build/qs.exe version
88+
echo "=== Dynamic library dependencies ==="
89+
ldd build/qs
90+
echo ""
91+
echo "=== Checking for libssl/libcrypto ==="
92+
if ldd build/qs | grep -qi 'ssl\|crypto'; then
93+
echo "WARNING: binary dynamically links OpenSSL"
94+
exit 1
95+
else
96+
echo "OK: binary is self-contained (no OpenSSL dynamic deps)"
97+
fi
98+
99+
- name: Check binary dependencies (macOS)
100+
if: runner.os == 'macOS'
101+
run: |
102+
echo "=== Dynamic library dependencies ==="
103+
otool -L build/qs
104+
echo ""
105+
echo "=== Checking for OpenSSL dylib ==="
106+
if otool -L build/qs | grep -qi 'openssl\|libssl\|libcrypto'; then
107+
echo "WARNING: binary dynamically links OpenSSL"
108+
exit 1
109+
else
110+
echo "OK: binary is self-contained (no OpenSSL dynamic deps)"
111+
fi
112+
113+
- name: Check binary dependencies (Windows)
114+
if: runner.os == 'Windows'
115+
shell: msys2 {0}
116+
run: |
117+
echo "=== Dynamic library dependencies ==="
118+
objdump -p build/qs.exe | grep "DLL Name" || true
119+
echo ""
120+
echo "=== Checking for OpenSSL DLLs ==="
121+
if objdump -p build/qs.exe | grep -qi 'ssl\|crypto'; then
122+
echo "WARNING: binary dynamically links OpenSSL"
123+
exit 1
124+
else
125+
echo "OK: binary is self-contained (no OpenSSL dynamic deps)"
126+
fi
80127
81128
# ── Package ──────────────────────────────────────────────────
82-
- name: Package
129+
- name: Create release archive
83130
shell: bash
84131
run: |
85132
set -eu
133+
86134
VERSION="${GITHUB_REF_NAME#v}"
87-
case "$VERSION" in refs/*|"") VERSION="dev" ;; esac
88135
PKG="quantoscript-${VERSION}-${{ matrix.target }}"
89-
mkdir -p "dist/$PKG/bin" "dist/$PKG/stdlib" "dist/$PKG/examples"
90136
91-
if [ "${{ runner.os }}" = "Windows" ]; then BIN="build/qs.exe"; else BIN="build/qs"; fi
92-
cp "$BIN" "dist/$PKG/bin/"
93-
cp stdlib/*.qs "dist/$PKG/stdlib/"
94-
cp examples/*.qs "dist/$PKG/examples/"
95-
cp README.md CHANGELOG.md LICENSE "dist/$PKG/"
137+
# Assemble package tree
138+
mkdir -p "dist/$PKG/bin"
139+
if [ "${{ runner.os }}" = "Windows" ]; then
140+
cp build/qs.exe "dist/$PKG/bin/qs.exe"
141+
else
142+
cp build/qs "dist/$PKG/bin/qs"
143+
fi
144+
cp -R stdlib "dist/$PKG/stdlib"
145+
cp -R examples "dist/$PKG/examples"
146+
cp LICENSE README.md "dist/$PKG/"
96147
148+
# Create archive
97149
cd dist
98150
if [ "${{ runner.os }}" = "Windows" ]; then
99151
7z a "${PKG}.zip" "$PKG" >/dev/null
100-
certutil -hashfile "${PKG}.zip" SHA256 | sed -n '2p' | tr -d ' \r' > "${PKG}.zip.sha256.tmp"
101-
echo "$(cat ${PKG}.zip.sha256.tmp) ${PKG}.zip" > "${PKG}.zip.sha256"
102-
rm -f "${PKG}.zip.sha256.tmp"
103152
else
104153
tar czf "${PKG}.tar.gz" "$PKG"
105-
if command -v sha256sum >/dev/null 2>&1; then
106-
sha256sum "${PKG}.tar.gz" > "${PKG}.tar.gz.sha256"
107-
else
108-
shasum -a 256 "${PKG}.tar.gz" > "${PKG}.tar.gz.sha256"
109-
fi
110154
fi
155+
156+
# Generate SHA256 checksum (sha256sum is available on all runners)
157+
ASSET=$(ls "${PKG}.tar.gz" "${PKG}.zip" 2>/dev/null | head -1)
158+
sha256sum "$ASSET" > "${ASSET}.sha256"
159+
echo "=== Packaged ==="
111160
ls -la
112161
162+
# ── Upload artifacts ─────────────────────────────────────────
113163
- name: Upload build artifacts
114164
uses: actions/upload-artifact@v4
115165
with:
@@ -124,24 +174,24 @@ jobs:
124174
name: Publish release
125175
needs: build
126176
runs-on: ubuntu-latest
127-
# Publish even if some platform legs failed, so a release still ships with
128-
# whatever binaries built successfully. Only runs for tag pushes.
129-
if: always() && startsWith(github.ref, 'refs/tags/')
177+
if: startsWith(github.ref, 'refs/tags/')
130178
steps:
131179
- name: Download all artifacts
132180
uses: actions/download-artifact@v4
133181
with:
134182
path: dist
135183
merge-multiple: true
136184

137-
- name: Combine checksums
185+
- name: List release assets
138186
run: |
139-
cd dist
140-
cat *.sha256 > SHA256SUMS.txt 2>/dev/null || true
141-
echo "=== artifacts ==="
142-
ls -la
143-
echo "=== SHA256SUMS.txt ==="
144-
cat SHA256SUMS.txt 2>/dev/null || echo "(none)"
187+
echo "=== All release assets ==="
188+
ls -la dist/
189+
echo ""
190+
echo "=== SHA256 checksums ==="
191+
for f in dist/*.sha256; do
192+
echo "--- $f ---"
193+
cat "$f"
194+
done
145195
146196
- name: Fail if nothing built
147197
run: |
@@ -157,4 +207,4 @@ jobs:
157207
files: |
158208
dist/*.tar.gz
159209
dist/*.zip
160-
dist/SHA256SUMS.txt
210+
dist/*.sha256

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Build artifacts
22
build/
3+
dist/
34
cmake-build-*/
45

56
# CMake generated

scripts/package_release.ps1

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# package_release.ps1 — Generate release archives for QuantoScript.
2+
#
3+
# Usage (from project root):
4+
# pwsh -File scripts/package_release.ps1 -Version 1.0.0
5+
#
6+
# Produces (inside dist/):
7+
# quantoscript-1.0.0-x86_64-pc-windows-gnu.zip + .sha256
8+
# quantoscript-1.0.0-x86_64-unknown-linux-gnu.tar.gz + .sha256
9+
# quantoscript-1.0.0-x86_64-apple-darwin.tar.gz + .sha256
10+
#
11+
# Each archive contains exactly one top-level directory:
12+
# quantoscript-<ver>-<target>/{bin/qs[.exe], stdlib/, examples/, LICENSE, README.md}
13+
14+
[CmdletBinding()]
15+
param(
16+
[Parameter(Mandatory)]
17+
[string]$Version
18+
)
19+
20+
$ErrorActionPreference = "Stop"
21+
$Root = Split-Path $PSScriptRoot -Parent # project root
22+
$Dist = Join-Path $Root "dist"
23+
if (Test-Path $Dist) { Remove-Item $Dist -Recurse -Force }
24+
New-Item -ItemType Directory -Force -Path $Dist | Out-Null
25+
26+
$targets = @(
27+
@{ Name = "x86_64-pc-windows-gnu"; Bin = "qs.exe"; Ext = "zip" }
28+
@{ Name = "x86_64-unknown-linux-gnu"; Bin = "qs"; Ext = "tar.gz" }
29+
@{ Name = "x86_64-apple-darwin"; Bin = "qs"; Ext = "tar.gz" }
30+
)
31+
32+
function Make-Checksum {
33+
param([string]$FilePath)
34+
$hash = (Get-FileHash $FilePath -Algorithm SHA256).Hash.ToLower()
35+
$line = "$hash $(Split-Path $FilePath -Leaf)"
36+
Set-Content -Path "$FilePath.sha256" -Value $line -NoNewline
37+
Write-Host " checksum: $(Split-Path $FilePath -Leaf).sha256"
38+
}
39+
40+
foreach ($t in $targets) {
41+
$pkgName = "quantoscript-$Version-$($t.Name)"
42+
$staging = Join-Path $Dist "staging_$($t.Name)"
43+
if (Test-Path $staging) { Remove-Item $staging -Recurse -Force }
44+
45+
$pkgDir = Join-Path $staging $pkgName
46+
$binDir = Join-Path $pkgDir "bin"
47+
New-Item -ItemType Directory -Force -Path $binDir | Out-Null
48+
49+
# Copy binary
50+
$srcBin = Join-Path $Root "build\$($t.Bin)"
51+
if (-not (Test-Path $srcBin)) {
52+
Write-Warning "Binary not found: $srcBin, skipping for $($t.Name)"
53+
} else {
54+
Copy-Item $srcBin (Join-Path $binDir $t.Bin) -Force
55+
}
56+
57+
# Copy stdlib, examples, LICENSE, README.md
58+
Copy-Item (Join-Path $Root "stdlib") (Join-Path $pkgDir "stdlib") -Recurse -Force
59+
Copy-Item (Join-Path $Root "examples") (Join-Path $pkgDir "examples") -Recurse -Force
60+
Copy-Item (Join-Path $Root "LICENSE") (Join-Path $pkgDir "LICENSE") -Force
61+
Copy-Item (Join-Path $Root "README.md")(Join-Path $pkgDir "README.md") -Force
62+
63+
# Create archive
64+
if ($t.Ext -eq "zip") {
65+
$archive = Join-Path $Dist "$pkgName.zip"
66+
Compress-Archive -Path $pkgDir -DestinationPath $archive -Force
67+
Write-Host "Created: $archive"
68+
Make-Checksum $archive
69+
} else {
70+
$archive = Join-Path $Dist "$pkgName.tar.gz"
71+
# tar is available on Windows 10+ and all Linux/macOS
72+
tar -czf $archive -C $staging $pkgName
73+
if ($LASTEXITCODE -ne 0) { throw "tar failed" }
74+
Write-Host "Created: $archive"
75+
Make-Checksum $archive
76+
}
77+
78+
# Cleanup staging
79+
Remove-Item $staging -Recurse -Force
80+
}
81+
82+
Write-Host ""
83+
Write-Host "All archives written to $Dist"
84+
Get-ChildItem $Dist | ForEach-Object { Write-Host " $($_.Name)" }

0 commit comments

Comments
 (0)