Skip to content

Merge pull request #5 from TheMysteriousStranger90/develop #25

Merge pull request #5 from TheMysteriousStranger90/develop

Merge pull request #5 from TheMysteriousStranger90/develop #25

Workflow file for this run

name: Build, Test & Release
on:
push:
branches: [ "master", "develop" ]
tags:
- 'v*.*.*'
pull_request:
branches: [ "master", "develop" ]
workflow_dispatch:
env:
DOTNET_VERSION: '9.0.x'
SOLUTION_NAME: 'VoiceRecorder.sln'
PROJECT_PATH: 'VoiceRecorder/VoiceRecorder.csproj'
ARTIFACT_NAME: 'AzioVoiceRecorder'
EXE_NAME: 'AzioVoiceRecorder.exe'
jobs:
# ============================================
# Job 1: Code Quality Checks
# ============================================
code-quality:
name: Code Quality & Linting
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_NAME }}
- name: Fix code formatting
run: dotnet format ${{ env.SOLUTION_NAME }} --verbosity diagnostic
- name: Check code formatting
run: dotnet format ${{ env.SOLUTION_NAME }} --verify-no-changes --verbosity diagnostic
- name: Run Code Analysis
run: |
dotnet build ${{ env.SOLUTION_NAME }} `
--configuration Release `
--no-restore `
/p:TreatWarningsAsErrors=true
# ============================================
# Job 2: Build & Test
# ============================================
build-and-test:
name: Build & Test (${{ matrix.configuration }})
runs-on: windows-latest
needs: code-quality
strategy:
fail-fast: false
matrix:
configuration: [Debug, Release]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_NAME }}
- name: Build
run: |
dotnet build ${{ env.SOLUTION_NAME }} `
--configuration ${{ matrix.configuration }} `
--no-restore `
/p:ContinuousIntegrationBuild=true `
/p:AssemblyName=AzioVoiceRecorder
- name: Run Unit Tests
if: matrix.configuration == 'Release'
run: |
dotnet test ${{ env.SOLUTION_NAME }} `
--configuration ${{ matrix.configuration }} `
--no-build `
--verbosity normal `
--logger "trx;LogFileName=test-results.trx" `
--results-directory ./TestResults
continue-on-error: true
- name: Upload Test Results
if: always() && matrix.configuration == 'Release'
uses: actions/upload-artifact@v4
with:
name: test-results
path: TestResults/**/*.trx
retention-days: 7
- name: Upload Build Artifacts (${{ matrix.configuration }})
uses: actions/upload-artifact@v4
with:
name: Build-Artifacts-${{ matrix.configuration }}
path: VoiceRecorder/bin/${{ matrix.configuration }}
retention-days: 7
# ============================================
# Job 3: Publish Self-Contained (win-x64) - Hybrid Approach
# ============================================
publish:
name: Publish Self-Contained (win-x64)
runs-on: windows-latest
needs: build-and-test
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_NAME }}
- name: Get version from tag or use default
id: version
run: |
if ("${{ github.ref }}" -match "refs/tags/v(.*)") {
$version = $matches[1]
} else {
$version = "1.3.1-dev.${{ github.run_number }}"
}
echo "version=$version" >> $env:GITHUB_OUTPUT
echo "Version: $version"
- name: Publish Self-Contained (win-x64) - Hybrid with ReadyToRun
run: |
dotnet publish ${{ env.PROJECT_PATH }} `
--configuration Release `
--runtime win-x64 `
--self-contained true `
--output ./publish/win-x64 `
/p:PublishSingleFile=true `
/p:PublishTrimmed=false `
/p:PublishReadyToRun=true `
/p:PublishReadyToRunShowWarnings=true `
/p:PublishReadyToRunComposite=true `
/p:EnableCompressionInSingleFile=true `
/p:IncludeNativeLibrariesForSelfExtract=true `
/p:IncludeAllContentForSelfExtract=true `
/p:DebugType=embedded `
/p:DebugSymbols=true `
/p:AssemblyName=AzioVoiceRecorder `
/p:Version=${{ steps.version.outputs.version }}
- name: List published files
run: Get-ChildItem -Path ./publish/win-x64 -Recurse
- name: Get file size
id: filesize
run: |
$exePath = "./publish/win-x64/${{ env.EXE_NAME }}"
if (Test-Path $exePath) {
$sizeBytes = (Get-Item $exePath).Length
$sizeMB = [math]::Round($sizeBytes / 1MB, 2)
echo "size_mb=$sizeMB" >> $env:GITHUB_OUTPUT
echo "Executable size: $sizeMB MB"
} else {
echo "size_mb=Unknown" >> $env:GITHUB_OUTPUT
Write-Warning "Executable not found at: $exePath"
}
- name: Create ZIP archive
run: |
Compress-Archive -Path ./publish/win-x64/* `
-DestinationPath ./${{ env.ARTIFACT_NAME }}-v${{ steps.version.outputs.version }}-win-x64.zip `
-CompressionLevel Optimal
- name: Calculate ZIP hash
id: hash
run: |
$zipPath = "./${{ env.ARTIFACT_NAME }}-v${{ steps.version.outputs.version }}-win-x64.zip"
$hash = Get-FileHash -Path $zipPath -Algorithm SHA256
$zipSizeBytes = (Get-Item $zipPath).Length
$zipSizeMB = [math]::Round($zipSizeBytes / 1MB, 2)
echo "sha256=$($hash.Hash)" >> $env:GITHUB_OUTPUT
echo "zip_size_mb=$zipSizeMB" >> $env:GITHUB_OUTPUT
echo "SHA256: $($hash.Hash)"
echo "ZIP size: $zipSizeMB MB"
- name: Upload Build Artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}-win-x64
path: ${{ env.ARTIFACT_NAME }}-v${{ steps.version.outputs.version }}-win-x64.zip
retention-days: 90
compression-level: 0
- name: Create build info
run: |
$buildInfo = @"
AzioVoice Recorder - Build Information
======================================
Version: ${{ steps.version.outputs.version }}
Configuration: Release (Hybrid - ReadyToRun without Trimming)
Runtime: win-x64 (self-contained)
Executable: ${{ env.EXE_NAME }}
Executable Size: ${{ steps.filesize.outputs.size_mb }} MB
ZIP Size: ${{ steps.hash.outputs.zip_size_mb }} MB
SHA256: ${{ steps.hash.outputs.sha256 }}
Features:
- Single-file executable
- No .NET Runtime required
- ReadyToRun compilation for faster startup
- Full compatibility (no trimming issues)
- Compressed native libraries
- Real-time audio visualization
- Advanced audio filtering (Echo, Flanger, Distortion, Chorus, Compressor)
Build Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss UTC")
Commit: ${{ github.sha }}
Repository: ${{ github.repository }}
"@
$buildInfo | Out-File -FilePath ./publish/win-x64/BUILD_INFO.txt -Encoding utf8
Write-Output $buildInfo
# ============================================
# Job 4: Create GitHub Release (on tags)
# ============================================
release:
name: Create GitHub Release
runs-on: windows-latest
needs: publish
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}-win-x64
path: ./artifacts
- name: Get version
id: version
run: |
$version = "${{ github.ref_name }}"
echo "version=$version" >> $env:GITHUB_OUTPUT
- name: Create Release Notes
run: |
$version = "${{ steps.version.outputs.version }}"
$notes = @"
# AzioVoice Recorder $version
## Downloads
**Windows x64 (64-bit Intel/AMD)**
- Self-contained build (no .NET Runtime required)
- Single executable file with ReadyToRun compilation
- Optimized for fast startup without trimming issues
## Installation
1. Download the ``${{ env.ARTIFACT_NAME }}-$version-win-x64.zip`` file below
2. Extract to any folder
3. Run ``${{ env.EXE_NAME }}``
4. (Optional) Grant microphone permissions in Windows Privacy Settings
## Features
- **Real-time audio recording** with live waveform visualization
- **Built-in audio filters**: Echo, Flanger, Distortion, Chorus, Compressor
- **Device selection** for recording from multiple microphones
- **File explorer** with playback controls and audio management
- **Modern Avalonia UI** with Fluent theme and smooth animations
- **Configurable audio settings** (sample rate, bit depth, channels)
## Requirements
- **OS**: Windows 10 version 1809 or later / Windows 11
- **Architecture**: x64 (64-bit)
- **.NET Runtime**: Not required (self-contained)
- **Disk Space**: ~100 MB
- **RAM**: 256 MB minimum, 512 MB recommended
- **Microphone**: Required for recording
## Performance
This build uses **Hybrid approach**:
- ✅ ReadyToRun compilation for 30-40% faster startup
- ✅ No trimming = full compatibility with CSCore and audio libraries
- ✅ Optimized garbage collection (Server GC)
- ✅ Tiered compilation for runtime optimization
- ✅ Embedded debug symbols for better diagnostics
## 🔒 Security
- **SHA256 Checksum**: Available in artifact details and BUILD_INFO.txt
- Verify the hash after download to ensure file integrity
- No telemetry or data collection
- All recordings stored locally
## 🐛 Known Issues
- First-time microphone access may require Windows permissions
---
**Full Changelog**: https://github.com/${{ github.repository }}/compare/v1.3.0...$version
**Support**: For issues or questions, please visit [Issues](https://github.com/${{ github.repository }}/issues)
"@
$notes | Out-File -FilePath release_notes.md -Encoding utf8
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: ./artifacts/*.zip
body_path: release_notes.md
draft: false
prerelease: ${{ contains(github.ref, '-alpha') || contains(github.ref, '-beta') || contains(github.ref, '-rc') }}
generate_release_notes: true
make_latest: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ============================================
# Job 5: Security Scanning
# ============================================
security-scan:
name: Security Scan
runs-on: windows-latest
needs: build-and-test
if: github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore ${{ env.SOLUTION_NAME }}
- name: Check for vulnerable packages
run: |
$output = dotnet list ${{ env.SOLUTION_NAME }} package --vulnerable --include-transitive 2>&1
Write-Output $output
if ($output -match "has the following vulnerable packages") {
Write-Error "❌ Vulnerable packages detected!"
exit 1
} else {
Write-Output "✅ No vulnerable packages found"
}
continue-on-error: true
# ============================================
# Job 6: Dependency Review (PRs only)
# ============================================
dependency-review:
name: Dependency Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: moderate
deny-licenses: GPL-2.0, GPL-3.0