Skip to content

Update release.yml #218

Update release.yml

Update release.yml #218

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release'
required: true
default: '0.0.0'
push:
branches:
- main
- release-build-testing
permissions:
contents: write
jobs:
create-release:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
release_id: ${{ steps.create-release.outputs.result }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
#deno-version: v2.3.7
cache: true
- name: Get version
id: get_version
run: echo "VERSION=$(deno eval 'import { VERSION } from "./version.ts"; console.log(VERSION);')" >> $GITHUB_OUTPUT
- name: Extract release notes from CHANGELOG
id: extract_changelog
run: |
VERSION=$(deno eval 'import { VERSION } from "./version.ts"; console.log(VERSION);')
# Extract full release section for current version from CHANGELOG.md
RELEASE_SECTION=$(awk "/^## \[$VERSION\]/{flag=1; print; next} /^## \[/{flag=0} flag{print}" CHANGELOG.md)
if [ -z "$RELEASE_SECTION" ]; then
RELEASE_NOTES="See CHANGELOG.md for version $VERSION details."
HAS_BREAKING="false"
CRITICAL_NOTICE=""
else
# Check for breaking changes indicators
if echo "$RELEASE_SECTION" | grep -qi "breaking\|BREAKING"; then
HAS_BREAKING="true"
CRITICAL_NOTICE="🚨 **BREAKING CHANGES DETECTED** - Please backup your projects before upgrading and review the changelog carefully."
else
HAS_BREAKING="false"
CRITICAL_NOTICE=""
fi
# Extract just the changes (skip the version header)
RELEASE_NOTES=$(echo "$RELEASE_SECTION" | tail -n +2)
fi
# Store unescaped versions for release body
echo "RELEASE_NOTES_RAW<<EOF" >> $GITHUB_OUTPUT
echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "CRITICAL_NOTICE_RAW<<EOF" >> $GITHUB_OUTPUT
echo "$CRITICAL_NOTICE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "HAS_BREAKING=$HAS_BREAKING" >> $GITHUB_OUTPUT
- name: Create release
id: create-release
uses: actions/github-script@v6
with:
script: |
const { data } = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: `v${{ steps.get_version.outputs.VERSION }}`,
name: `Beyond Better v${{ steps.get_version.outputs.VERSION }}`,
body: `# Beyond Better application and server components v${{ steps.get_version.outputs.VERSION }}
${{ steps.extract_changelog.outputs.HAS_BREAKING == 'true' && steps.extract_changelog.outputs.CRITICAL_NOTICE_RAW || '' }}
## Installation Instructions:
1. Download the appropriate installer for your system:
Windows:
- MSI installer (recommended): Download the .msi file
- EXE installer: Download the -setup.exe file
- Run the installer and follow the prompts
macOS:
- Universal (Intel & Apple Silicon): Download the universal.dmg
- Open the .dmg file
- Drag the Beyond Better app to your Applications folder
Linux:
- Download the .AppImage file
- Make it executable: chmod +x BB-app-*.AppImage
- Run the AppImage
2. Binary Installation:
- The DUI will automatically download and install the appropriate BB binaries on first run
- For manual installation, download the appropriate binary archive for your system
Note: On Windows, you may need to approve SmartScreen security prompts during first run.
## Changes in this Release:
${{ steps.extract_changelog.outputs.RELEASE_NOTES_RAW }}`,
draft: false,
prerelease: false
})
return data.id
build-binaries:
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: macos-latest
target: x86_64-apple-darwin
- os: macos-latest
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
#deno-version: v2.3.7
cache: true
- name: Building with Deno version
run: |
deno --version
- name: Build CLI for ${{ matrix.target }}
run: |
cd cli
deno task update-deps
deno compile --allow-env --allow-net --allow-read --allow-run --allow-write --target ${{ matrix.target }} --output ../build/bb${{ matrix.target == 'x86_64-pc-windows-msvc' && '.exe' || '' }} src/main.ts
- name: Build API for ${{ matrix.target }}
run: |
cd api
deno task update-deps
deno run --allow-read --allow-run --allow-write scripts/compile.ts --target ${{ matrix.target }} --output ../build/bb-api${{ matrix.target == 'x86_64-pc-windows-msvc' && '.exe' || '' }}
- name: Build BUI for ${{ matrix.target }}
run: |
cd bui
deno task update-deps
deno run -A --unstable-kv src/dev.ts build
deno compile --include src/static --include src/_fresh --include deno.jsonc --target ${{ matrix.target }} --output ../build/bb-bui${{ matrix.target == 'x86_64-pc-windows-msvc' && '.exe' || '' }} -A src/main.ts
- name: Create install script (Unix)
if: matrix.target != 'x86_64-pc-windows-msvc'
run: |
echo '#!/bin/sh' > build/install.sh
echo 'cp bb /usr/local/bin/' >> build/install.sh
echo 'cp bb-api /usr/local/bin/' >> build/install.sh
echo 'cp bb-bui /usr/local/bin/' >> build/install.sh
echo 'chmod +x /usr/local/bin/bb /usr/local/bin/bb-api /usr/local/bin/bb-bui' >> build/install.sh
chmod +x build/install.sh
- name: Copy README and INSTALL
shell: bash
run: |
if [[ "${{ runner.os }}" == "Windows" ]]; then
cp README.md build/README.txt
cp INSTALL.md build/INSTALL.txt
else
cp README.md INSTALL.md build/
fi
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: bb-${{ matrix.target }}
path: build/*
build-dui:
needs: create-release
strategy:
fail-fast: false
matrix:
include:
# macOS universal build
- os: macos-latest
target: "universal-apple-darwin"
arch: "universal"
name: "Universal"
platform: "macos"
# Linux builds
- os: ubuntu-latest
target: "x86_64-unknown-linux-gnu"
arch: "x86_64"
name: "Linux x64"
platform: "linux"
# Windows builds
- os: windows-latest
target: "x86_64-pc-windows-msvc"
arch: "x86_64"
name: "Windows x64"
platform: "windows"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
#deno-version: v2.3.7
cache: true
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform == 'macos' && 'x86_64-apple-darwin,aarch64-apple-darwin' || matrix.target }}
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: './dui/src-tauri -> target'
- name: Get version
id: get_version
run: |
echo "VERSION=$(deno eval 'import { VERSION } from "version.ts"; console.log(VERSION);')" >> $GITHUB_OUTPUT
echo "=== VERSION ==="
echo "$VERSION"
- name: Install Linux dependencies
if: matrix.platform == 'linux'
run: |
sudo apt-get update
sudo apt-get update
sudo apt-get install -y \
build-essential \
curl \
wget \
file \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libjavascriptcoregtk-4.1-dev \
libsoup-3.0-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
- name: Install Windows dependencies
if: matrix.platform == 'windows'
run: |
# Install WiX Toolset
$wixUrl = "https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip"
$wixZip = "$env:TEMP\wix.zip"
Invoke-WebRequest -Uri $wixUrl -OutFile $wixZip
Expand-Archive -Path $wixZip -DestinationPath "$env:TEMP\wix" -Force
echo "$env:TEMP\wix" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
# Set MSI-compatible version
echo "=== VERSION ==="
echo "${{ steps.get_version.outputs.VERSION }}"
# $originalVersion = "${{ steps.get_version.outputs.VERSION }}"
$configPath = "dui\src-tauri\tauri.conf.json"
$config = Get-Content $configPath | ConvertFrom-Json
$originalVersion = $config.version
$msiVersion = $originalVersion -replace '[+-].*', ''
echo "=== Version Debug ==="
echo "Original version: $originalVersion"
echo "MSI version: $msiVersion"
echo "=== Updating config ==="
(Get-Content $configPath) -replace '"version":\s*"[^"]*"', "`"version`": `"$msiVersion`"" | Set-Content $configPath
echo "=== Modified tauri.conf.json ==="
Get-Content $configPath
- name: Install frontend dependencies
run: |
cd dui
deno task build
# Import and setup Apple certificates for macOS signing
- name: Import Apple Developer Certificate
if: matrix.platform == 'macos'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -l build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S "apple-tool:,apple:,codesign:,security:" -s -k "$KEYCHAIN_PASSWORD" build.keychain
security list-keychain -s build.keychain
security find-identity -v -p codesigning build.keychain
- name: Verify Certificate
if: matrix.platform == 'macos'
env:
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application")
#echo "CERT_INFO: $CERT_INFO"
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "CERT_ID: $CERT_ID"
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
echo "Certificate imported."
# Set up Apple API key for notarization
- name: Setup Apple API Key
if: matrix.platform == 'macos'
run: |
echo "${{ secrets.APPLE_API_KEY_VALUE }}" > ${{ secrets.APPLE_API_KEY_PATH }}
chmod 600 ${{ secrets.APPLE_API_KEY_PATH }}
- name: Clear Existing Signatures
if: matrix.platform == 'macos'
run: |
find ./dui/src-tauri/target -name "*.app" -exec codesign --remove-signature "{}" \; || true
find ./dui/src-tauri/target -name "beyond-better" -exec codesign --remove-signature "{}" \; || true
- name: Build DUI (Tauri)
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
MACOSX_DEPLOYMENT_TARGET: "10.13"
# macOS signing environment variables
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
# API Key notarization (preferred method)
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY_PATH: ${{ secrets.APPLE_API_KEY_PATH }}
# Apple ID notarization (fallback method)
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
projectPath: './dui'
releaseId: ${{ needs.create-release.outputs.release_id }}
args: ${{ matrix.platform == 'macos' && '--target universal-apple-darwin' || format('--target {0}', matrix.target) }}
includeUpdaterJson: true # this will include the latest.json file in the release
#releaseDraft: true
#prerelease: false
# Process and upload build artifacts
- name: Process build artifacts
shell: bash
run: |
BUNDLE_DIR="dui/src-tauri/target/${{ matrix.target }}/release/bundle"
echo "Build output directory contents:"
ls -R "$BUNDLE_DIR" || echo "No bundle directory found"
# Create staging directory
mkdir -p staging
# Copy artifacts based on platform
if [[ "${{ matrix.platform }}" == "macos" ]]; then
echo "Processing macOS artifacts..."
if [ -d "$BUNDLE_DIR/dmg" ]; then
cp -v "$BUNDLE_DIR"/dmg/*.dmg staging/ || echo "No DMG files found"
fi
if [ -d "$BUNDLE_DIR/macos" ]; then
cp -rv "$BUNDLE_DIR"/macos/*.app staging/ || echo "No APP files found"
fi
elif [[ "${{ matrix.platform }}" == "linux" ]]; then
echo "Processing Linux artifacts..."
if [ -d "$BUNDLE_DIR/appimage" ]; then
cp -v "$BUNDLE_DIR"/appimage/*.AppImage staging/ || echo "No AppImage files found"
fi
elif [[ "${{ matrix.platform }}" == "windows" ]]; then
echo "Processing Windows artifacts..."
if [ -d "$BUNDLE_DIR/msi" ]; then
cp -v "$BUNDLE_DIR"/msi/*.msi staging/ || echo "No MSI files found"
fi
if [ -d "$BUNDLE_DIR/nsis" ]; then
cp -v "$BUNDLE_DIR"/nsis/*.exe staging/ || echo "No NSIS files found"
fi
fi
echo "\nStaging directory contents:"
ls -la staging/
# Upload artifacts
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dui-${{ matrix.platform }}-${{ matrix.arch }}
path: staging/*
release:
needs: [create-release, build-binaries, build-dui]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
#deno-version: v2.3.7
cache: true
- name: Get version
id: get_version
run: echo "VERSION=$(deno eval 'import { VERSION } from "./version.ts"; console.log(VERSION);')" >> $GITHUB_OUTPUT
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: List downloaded artifacts
run: |
echo "Artifacts for version: v${{ steps.get_version.outputs.VERSION }}"
echo "Current directory: $PWD"
echo "Directory contents:"
ls -la
echo "\nFull directory tree:"
tree .
echo "\nDetailed file listing:"
find . -type f -ls
echo "\nSearching for specific file types:"
echo "DMG files:" && find . -name "*.dmg"
echo "APP bundles:" && find . -name "*.app"
echo "AppImage files:" && find . -name "*.AppImage"
echo "MSI files:" && find . -name "*.msi"
echo "EXE files:" && find . -name "*.exe"
- name: Upload Release Assets
run: |
# Debug: Show downloaded artifacts
echo "Current directory contents:"
ls -la
find . -type f -ls
# Function to handle upload with retry
upload_asset() {
local file="$1"
local attempt=1
local max_attempts=3
while [ $attempt -le $max_attempts ]; do
echo "Uploading $file (attempt $attempt of $max_attempts)"
if gh release upload "v${{ steps.get_version.outputs.VERSION }}" "$file"; then
return 0
fi
attempt=$((attempt + 1))
if [ $attempt -le $max_attempts ]; then
echo "Retrying in 5 seconds..."
sleep 5
fi
done
echo "::error::Failed to upload $file after $max_attempts attempts"
return 1
}
# Upload binary artifacts to the existing release
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu x86_64-pc-windows-msvc x86_64-apple-darwin aarch64-apple-darwin; do
echo "Processing binary target: $target"
cd "bb-$target"
if [ "$target" = "x86_64-pc-windows-msvc" ]; then
zip -r "../bb-$target-v${{ steps.get_version.outputs.VERSION }}.zip" .
else
zip -r "../bb-$target-v${{ steps.get_version.outputs.VERSION }}.zip" .
tar czf "../bb-$target-v${{ steps.get_version.outputs.VERSION }}.tar.gz" .
fi
cd ..
upload_asset "bb-$target-v${{ steps.get_version.outputs.VERSION }}.zip"
if [ "$target" != "x86_64-pc-windows-msvc" ]; then
upload_asset "bb-$target-v${{ steps.get_version.outputs.VERSION }}.tar.gz"
fi
done
# Upload DUI artifacts
for platform_arch in "macos-universal" "linux-x86_64" "windows-x86_64"; do
echo "Processing DUI platform/arch: $platform_arch"
platform="${platform_arch%%-*}"
arch="${platform_arch#*-}"
echo "Checking for DUI artifacts:"
ls -la "dui-${platform}-${arch}" || true
case "$platform_arch" in
"macos-universal")
platform_name="macos"
arch_name="universal" ;;
"linux-x86_64")
platform_name="linux"
arch_name="x64" ;;
"windows-x86_64")
platform_name="windows"
arch_name="x64" ;;
esac
if [ "$platform" = "macos" ]; then
# Upload DMG files
echo "Processing DMG files:"
for f in "dui-${platform}-${arch}"/*.dmg; do
if [ -f "$f" ]; then
echo "Found DMG file: $f"
new_name="BB-app-${{ steps.get_version.outputs.VERSION }}-$platform_name-$arch_name.dmg"
echo "Creating renamed copy: $new_name"
cp "$f" "$new_name"
echo "Attempting upload of $new_name"
upload_asset "$new_name"
fi
done
# Create and upload app archive
echo "Processing .app bundles:"
for app in "dui-${platform}-${arch}"/*.app; do
if [ -d "$app" ]; then
echo "Found app bundle: $app"
new_name="BB-app-${{ steps.get_version.outputs.VERSION }}-$platform_name-$arch_name.app.tar.gz"
echo "Creating archive: $new_name"
tar -czf "$new_name" "$app"
echo "Attempting upload of $new_name"
upload_asset "$new_name"
fi
done
elif [ "$platform" = "linux" ]; then
# Upload AppImage files
echo "Processing AppImage files:"
for f in "dui-${platform}-${arch}"/*.AppImage; do
if [ -f "$f" ]; then
echo "Found AppImage file: $f"
new_name="BB-app-${{ steps.get_version.outputs.VERSION }}-$platform_name-$arch_name.AppImage"
echo "Creating renamed copy: $new_name"
cp "$f" "$new_name"
echo "Attempting upload of $new_name"
upload_asset "$new_name"
fi
done
elif [ "$platform" = "windows" ]; then
# Upload MSI files
echo "Processing MSI files:"
for f in "dui-${platform}-${arch}"/*.msi; do
if [ -f "$f" ]; then
echo "Found MSI file: $f"
new_name="BB-app-${{ steps.get_version.outputs.VERSION }}-$platform_name-$arch_name.msi"
echo "Creating renamed copy: $new_name"
cp "$f" "$new_name"
echo "Attempting upload of $new_name"
upload_asset "$new_name"
fi
done
# Upload NSIS installer
echo "Processing NSIS files:"
for f in "dui-${platform}-${arch}"/*.exe; do
if [[ "$f" == *"-setup.exe" ]]; then
echo "Found NSIS file: $f"
new_name="BB-app-${{ steps.get_version.outputs.VERSION }}-$platform_name-$arch_name-setup.exe"
echo "Creating renamed copy: $new_name"
cp "$f" "$new_name"
echo "Attempting upload of $new_name"
upload_asset "$new_name"
fi
done
fi
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}