Skip to content

fix: correct checksum file path in validation (cd .. instead of cd ..… #19

fix: correct checksum file path in validation (cd .. instead of cd ..…

fix: correct checksum file path in validation (cd .. instead of cd ..… #19

Workflow file for this run

name: Release
on:
push:
tags:
- 'v*' # Trigger on version tags like v1.0.0
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., v1.0.0)'
required: true
type: string
env:
CARGO_TERM_COLOR: always
jobs:
# Security scanning before building
# Note: Duplicates security.yml checks to gate releases
security-check:
runs-on: ubicloud-standard-8
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Audit npm dependencies
run: npm audit --audit-level=moderate
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo-audit
uses: actions/cache@v4
with:
path: ~/.cargo/bin/cargo-audit
key: ${{ runner.os }}-cargo-audit-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Audit Rust dependencies
run: cd src-tauri && cargo audit
release-macos:
needs: security-check
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin,x86_64-apple-darwin
- name: Install dependencies
run: npm ci
- name: Import Apple Certificate
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import certificate
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
security import certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
# Set the keychain as default search list
security list-keychain -d user -s $KEYCHAIN_PATH login.keychain
# Allow codesign to access keychain
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Clean up certificate file
rm certificate.p12
echo "✅ Certificate imported successfully"
# Verify certificate is available
echo "📋 Available signing identities:"
security find-identity -v -p codesigning $KEYCHAIN_PATH
- name: Build Tauri app (Universal Binary)
env:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: npm run tauri build -- --target universal-apple-darwin
- name: Notarize macOS app
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
run: |
# Find the DMG file
DMG_PATH=$(find src-tauri/target/universal-apple-darwin/release/bundle/dmg -name "*.dmg" | head -n 1)
if [ -z "$DMG_PATH" ]; then
echo "❌ Error: DMG file not found in expected location"
echo "Expected path: src-tauri/target/universal-apple-darwin/release/bundle/dmg/"
echo "Available files:"
find src-tauri/target/universal-apple-darwin/release/bundle -type f || true
exit 1
fi
echo "📦 Found DMG: $DMG_PATH"
# Verify DMG is valid before notarization
echo "🔍 Validating DMG integrity..."
if ! hdiutil verify "$DMG_PATH"; then
echo "❌ Error: DMG file is corrupted or invalid"
exit 1
fi
echo "✅ DMG integrity verified"
# Verify code signing before notarization
echo "🔍 Verifying code signature..."
if ! codesign -dv --verbose=4 "$DMG_PATH" 2>&1; then
echo "❌ Error: DMG is not properly code signed"
exit 1
fi
echo "✅ Code signature verified"
echo "🔐 Submitting for notarization..."
# Submit for notarization with retry logic
SUBMISSION_OUTPUT=$(mktemp)
MAX_RETRIES=3
RETRY_COUNT=0
SUCCESS=false
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if [ $RETRY_COUNT -gt 0 ]; then
echo "⏳ Retry attempt $RETRY_COUNT of $((MAX_RETRIES - 1))..."
sleep 30
fi
if xcrun notarytool submit "$DMG_PATH" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_PASSWORD" \
--wait \
--timeout 30m 2>&1 | tee "$SUBMISSION_OUTPUT"; then
SUCCESS=true
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "⚠️ Notarization attempt $RETRY_COUNT failed"
done
if [ "$SUCCESS" = false ]; then
echo "❌ Notarization failed after $MAX_RETRIES attempts"
echo "Final submission output:"
cat "$SUBMISSION_OUTPUT"
# Try to extract submission ID for logs
SUBMISSION_ID=$(grep -oE '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}' "$SUBMISSION_OUTPUT" | head -n 1 || true)
if [ -n "$SUBMISSION_ID" ]; then
echo "📋 Fetching notarization logs for submission: $SUBMISSION_ID"
xcrun notarytool log "$SUBMISSION_ID" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_PASSWORD" || true
fi
rm -f "$SUBMISSION_OUTPUT"
exit 1
fi
rm -f "$SUBMISSION_OUTPUT"
echo "✅ Notarization complete!"
# Staple the notarization ticket
echo "📎 Stapling notarization ticket..."
if ! xcrun stapler staple "$DMG_PATH"; then
echo "❌ Error: Failed to staple notarization ticket"
echo "This may indicate the notarization didn't complete successfully"
exit 1
fi
echo "✅ Stapling complete!"
# Verify notarization with Gatekeeper
echo "🔍 Verifying notarization with Gatekeeper..."
if ! spctl -a -vvv -t install "$DMG_PATH" 2>&1; then
echo "❌ Error: Gatekeeper verification failed"
echo "The app may not open on user systems"
exit 1
fi
echo "✅ Gatekeeper verification passed!"
# Final validation
echo "🔍 Final validation checks..."
echo "Staple status:"
xcrun stapler validate "$DMG_PATH" || {
echo "⚠️ Warning: Staple validation failed"
echo "The app is notarized but stapling may need to be checked"
}
echo "✅ All notarization checks passed!"
- name: Generate checksums
run: |
cd src-tauri/target/universal-apple-darwin/release/bundle/dmg
# Verify DMG exists before generating checksums
if [ ! -f *.dmg ]; then
echo "❌ Error: No DMG file found for checksum generation"
exit 1
fi
shasum -a 256 *.dmg > checksums-macos.txt
# Verify checksum file was created and is not empty
if [ ! -s checksums-macos.txt ]; then
echo "❌ Error: Checksum file is empty or was not created"
exit 1
fi
echo "✅ Checksums generated:"
cat checksums-macos.txt
- name: Validate artifacts before upload
run: |
DMG_PATH=$(find src-tauri/target/universal-apple-darwin/release/bundle/dmg -name "*.dmg" | head -n 1)
echo "🔍 Final artifact validation..."
# Verify file exists and has content
if [ ! -f "$DMG_PATH" ]; then
echo "❌ Error: DMG file not found"
exit 1
fi
FILE_SIZE=$(stat -f%z "$DMG_PATH" 2>/dev/null || stat -c%s "$DMG_PATH" 2>/dev/null)
if [ "$FILE_SIZE" -lt 1048576 ]; then
echo "❌ Error: DMG file is suspiciously small ($FILE_SIZE bytes)"
exit 1
fi
echo "✅ DMG size: $(echo "$FILE_SIZE" | awk '{printf "%.2f MB", $1/1024/1024}')"
# Verify notarization is still valid
echo "🔍 Re-verifying notarization before upload..."
if ! xcrun stapler validate "$DMG_PATH" >/dev/null 2>&1; then
echo "⚠️ Warning: Staple validation check failed"
fi
echo "✅ All artifact validations passed!"
- name: Upload macOS artifacts
uses: actions/upload-artifact@v4
with:
name: macos-dmg
path: |
src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg
src-tauri/target/universal-apple-darwin/release/bundle/dmg/checksums-macos.txt
- name: Cleanup keychain
if: always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true
release-linux:
needs: security-check
runs-on: ubicloud-standard-8
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
- name: Install dependencies
run: npm ci
- name: Build Tauri app
run: npm run tauri build
- name: Generate checksums
run: |
cd src-tauri/target/release/bundle/deb
# Verify DEB files exist
if ! ls *.deb 1> /dev/null 2>&1; then
echo "❌ Error: No DEB files found for checksum generation"
exit 1
fi
echo "📦 Generating checksums..."
shasum -a 256 *.deb > ../checksums-linux.txt
# Verify checksum file was created and is not empty
if [ ! -s ../checksums-linux.txt ]; then
echo "❌ Error: Checksum file is empty or was not created"
exit 1
fi
echo "✅ Checksums generated:"
cat ../checksums-linux.txt
- name: Validate Linux artifacts before upload
run: |
echo "🔍 Validating Linux artifacts..."
# Debug: Show what find actually returns
echo "📋 Searching for DEB files..."
find src-tauri/target/release/bundle/deb -type f -name "*.deb" -print || {
echo "❌ Error running find command"
echo "Contents of bundle directory:"
ls -laR src-tauri/target/release/bundle/ || true
exit 1
}
# Check DEB files using a more robust approach
cd src-tauri/target/release/bundle/deb
shopt -s nullglob
DEB_FILES=(*.deb)
if [ ${#DEB_FILES[@]} -eq 0 ]; then
echo "❌ Error: No DEB files found"
echo "Contents of deb directory:"
ls -laR . || true
exit 1
fi
echo "📦 Found ${#DEB_FILES[@]} DEB file(s)"
for deb in "${DEB_FILES[@]}"; do
FILE_SIZE=$(stat -c%s "$deb")
SIZE_MB=$(awk "BEGIN {printf \"%.2f\", $FILE_SIZE/1048576}")
echo "✅ $deb: ${SIZE_MB} MB"
if [ "$FILE_SIZE" -lt 1048576 ]; then
echo "❌ Error: DEB file is suspiciously small ($FILE_SIZE bytes)"
exit 1
fi
done
# Check checksum file (go back to bundle directory, not release)
cd ..
CHECKSUM_FILE="checksums-linux.txt"
if [ ! -f "$CHECKSUM_FILE" ]; then
echo "❌ Error: Checksum file not found at: $(pwd)/$CHECKSUM_FILE"
echo "Contents of current directory:"
ls -la . || true
exit 1
fi
CHECKSUM_SIZE=$(stat -c%s "$CHECKSUM_FILE")
if [ "$CHECKSUM_SIZE" -eq 0 ]; then
echo "❌ Error: Checksum file is empty"
exit 1
fi
echo "✅ Checksum file: $CHECKSUM_SIZE bytes"
echo "✅ All Linux artifact validations passed!"
- name: Upload Linux artifacts
uses: actions/upload-artifact@v4
with:
name: linux-packages
path: |
src-tauri/target/release/bundle/deb/*.deb
src-tauri/target/release/bundle/checksums-linux.txt
release-windows:
needs: security-check
runs-on: windows-2022-16-cores
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: npm ci
- name: Build Tauri app
run: npm run tauri build
- name: Generate checksums
shell: pwsh
run: |
cd src-tauri/target/release/bundle/msi
# Verify MSI files exist
$msiFiles = Get-ChildItem -Filter *.msi
if ($msiFiles.Count -eq 0) {
Write-Host "❌ Error: No MSI files found for checksum generation"
exit 1
}
Write-Host "📦 Found $($msiFiles.Count) MSI file(s)"
# Generate checksums
$msiFiles | ForEach-Object {
$hash = Get-FileHash $_.FullName -Algorithm SHA256
"$($hash.Hash) $($_.Name)"
} | Out-File -FilePath ..\checksums-windows.txt -Encoding UTF8
# Verify checksum file was created and is not empty
$checksumFile = "..\checksums-windows.txt"
if (-not (Test-Path $checksumFile)) {
Write-Host "❌ Error: Checksum file was not created"
exit 1
}
$fileSize = (Get-Item $checksumFile).Length
if ($fileSize -eq 0) {
Write-Host "❌ Error: Checksum file is empty"
exit 1
}
Write-Host "✅ Checksums generated ($fileSize bytes):"
Get-Content $checksumFile
- name: Validate Windows artifacts before upload
shell: pwsh
run: |
$msiPath = "src-tauri/target/release/bundle/msi"
$checksumPath = "src-tauri/target/release/bundle/checksums-windows.txt"
Write-Host "🔍 Validating Windows artifacts..."
# Check MSI files
$msiFiles = Get-ChildItem -Path $msiPath -Filter *.msi
if ($msiFiles.Count -eq 0) {
Write-Host "❌ Error: No MSI files found"
exit 1
}
foreach ($msi in $msiFiles) {
$sizeMB = [math]::Round($msi.Length / 1MB, 2)
Write-Host "✅ $($msi.Name): $sizeMB MB"
if ($msi.Length -lt 1MB) {
Write-Host "❌ Error: MSI file is suspiciously small"
exit 1
}
}
# Check checksum file
if (-not (Test-Path $checksumPath)) {
Write-Host "❌ Error: Checksum file not found"
exit 1
}
$checksumSize = (Get-Item $checksumPath).Length
if ($checksumSize -eq 0) {
Write-Host "❌ Error: Checksum file is empty"
exit 1
}
Write-Host "✅ Checksum file: $checksumSize bytes"
Write-Host "✅ All Windows artifact validations passed!"
- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows-installers
path: |
src-tauri/target/release/bundle/msi/*.msi
src-tauri/target/release/bundle/checksums-windows.txt
create-release:
needs: [release-macos, release-linux, release-windows]
runs-on: ubicloud-standard-8
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Display structure of downloaded files
run: ls -R artifacts
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/macos-dmg/*.dmg
artifacts/macos-dmg/*.txt
artifacts/linux-packages/deb/*.deb
artifacts/linux-packages/*.txt
artifacts/windows-installers/msi/*.msi
artifacts/windows-installers/*.txt
draft: false
prerelease: false
generate_release_notes: true
body: |
## Installation
### macOS
1. Download the `.dmg` file
2. Open the DMG and drag OpenObserve Kide to Applications
3. Right-click and select "Open" on first launch (required for notarized apps)
4. Verify signature: `codesign -dv --verbose=4 "/Applications/OpenObserve Kide.app"`
### Linux
- **Debian/Ubuntu**: Download and install the `.deb` package
```bash
sudo dpkg -i openobserve-kide_*.deb
```
### Windows
- Download and run the `.msi` installer
## Verification
All releases include SHA-256 checksums for each platform. Verify your download:
```bash
# macOS/Linux
shasum -a 256 -c checksums-macos.txt
shasum -a 256 -c checksums-linux.txt
# Windows (PowerShell)
Get-Content checksums-windows.txt
Get-FileHash <downloaded-file> -Algorithm SHA256
```
## Security
- macOS app is code-signed and notarized by Apple
- All builds undergo security scanning (TruffleHog, npm audit, cargo audit)
- All builds are reproducible from source
- See [SECURITY.md](https://github.com/${{ github.repository }}/blob/main/SECURITY.md)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}