diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..234ae3e --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,153 @@ +name: Build and release + +on: + push: + branches: + - main + tags: + - "v*" + pull_request: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Build Windows installer + runs-on: windows-latest + outputs: + version: ${{ steps.version.outputs.number }} + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up .NET 10 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + + - name: Determine version + id: version + shell: pwsh + run: | + [xml] $project = Get-Content OneNoteExporter.csproj + $projectVersion = $project.Project.PropertyGroup.Version | Select-Object -First 1 + + if (-not $projectVersion) { + throw "No was found in OneNoteExporter.csproj." + } + + if ($env:GITHUB_REF_TYPE -eq 'tag') { + if ($env:GITHUB_REF_NAME -notmatch '^v(?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$') { + throw "Release tags must use semantic versioning, for example v1.2.0." + } + + $version = $Matches.version + if ($version -ne $projectVersion) { + throw "Tag version $version does not match project version $projectVersion." + } + } + else { + $version = $projectVersion + } + + "number=$version" >> $env:GITHUB_OUTPUT + Write-Host "Building version $version" + + - name: Restore dependencies + run: dotnet restore OneNoteExporter.csproj --runtime win-x64 + + - name: Publish self-contained application + shell: pwsh + env: + APP_VERSION: ${{ steps.version.outputs.number }} + run: | + dotnet publish OneNoteExporter.csproj ` + --configuration Release ` + --runtime win-x64 ` + --self-contained true ` + --no-restore ` + --output build ` + -p:PublishSingleFile=false ` + "-p:Version=$env:APP_VERSION" + + - name: Compile installer + shell: pwsh + env: + APP_VERSION: ${{ steps.version.outputs.number }} + run: | + $isccCandidates = @( + 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe', + 'C:\Program Files\Inno Setup 6\ISCC.exe' + ) + $iscc = $isccCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 + + if (-not $iscc) { + choco install innosetup --yes --no-progress + $iscc = $isccCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 + } + + if (-not $iscc) { + throw 'Inno Setup compiler was not found after installation.' + } + + & $iscc "/DMyAppVersion=$env:APP_VERSION" innosetup.iss + if ($LASTEXITCODE -ne 0) { + throw "Inno Setup failed with exit code $LASTEXITCODE." + } + + - name: Create SHA-256 checksum + shell: pwsh + env: + APP_VERSION: ${{ steps.version.outputs.number }} + run: | + $installer = Get-Item "artifacts\OneNoteBackupExporter_Setup_$env:APP_VERSION.exe" + $hash = (Get-FileHash $installer.FullName -Algorithm SHA256).Hash.ToLowerInvariant() + "$hash $($installer.Name)" | Set-Content "$($installer.FullName).sha256" -Encoding ascii + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: OneNoteBackupExporter-${{ steps.version.outputs.number }} + path: | + artifacts/OneNoteBackupExporter_Setup_${{ steps.version.outputs.number }}.exe + artifacts/OneNoteBackupExporter_Setup_${{ steps.version.outputs.number }}.exe.sha256 + if-no-files-found: error + retention-days: 14 + + release: + name: Publish GitHub release + if: startsWith(github.ref, 'refs/tags/v') + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Download installer artifact + uses: actions/download-artifact@v4 + with: + name: OneNoteBackupExporter-${{ needs.build.outputs.version }} + path: artifacts + + - name: Create release and upload installer + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + run: | + set -euo pipefail + assets=(artifacts/*) + + if gh release view "$TAG" >/dev/null 2>&1; then + gh release upload "$TAG" "${assets[@]}" --clobber + else + gh release create "$TAG" "${assets[@]}" \ + --verify-tag \ + --title "OneNote Backup Exporter $TAG" \ + --generate-notes + fi diff --git a/.gitignore b/.gitignore index 6a246d0..bef2432 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ bin/ obj/ build/ +artifacts/ # Setup-EXE (wird als GitHub Release Asset hochgeladen) *.exe diff --git a/CLAUDE.md b/CLAUDE.md index 05cfdee..661ad2a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,7 +22,7 @@ OneNote Desktop ``` OneNoteExporterC#/ -├── OneNoteExporter.csproj Target: net10.0-windows, UseWPF, PlatformTarget=x86 +├── OneNoteExporter.csproj Target: net10.0-windows, UseWPF, PlatformTarget=x64 ├── OneNoteExporter.sln ├── App.xaml Global styles (colors, buttons, inputs) ├── App.xaml.cs Global exception handler @@ -48,14 +48,14 @@ dotnet build # Run (debug) dotnet run -# Publish self-contained x86 -dotnet publish -c Release -r win-x86 --self-contained true +# Publish self-contained x64 +dotnet publish -c Release -r win-x64 --self-contained true ``` ## Critical Technical Details ### COM Compatibility -- **`PlatformTarget=x86` is mandatory** – most OneNote Desktop installations are 32-bit. Changing to x64 or AnyCPU will cause "Class not registered" COM errors. +- **`PlatformTarget=x64` is mandatory** – this build targets current 64-bit OneNote Desktop installations. Changing the platform can cause "Class not registered" COM errors. - **NuGet package:** `Interop.Microsoft.Office.Interop.OneNote` v1.1.0.2 (NOT `Microsoft.Office.Interop.OneNote`) - **Do NOT add `true`** – causes namespace conflict with the COM `Application` class diff --git a/README.md b/README.md index 61fbf22..e551a9e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,24 @@ Talks directly to OneNote Desktop via the COM AP. ## Download -Go to [Releases](../../releases/latest) and download `OneNoteBackupExporter_Setup_1.0.0.exe`. Run the installer, done. +Go to [Releases](../../releases/latest) and download the latest +`OneNoteBackupExporter_Setup_.exe`. Run the installer, done. A matching +`.sha256` file is included so the download can be verified. + +## Automated builds and releases + +GitHub Actions builds a self-contained Windows x64 application and compiles the Inno Setup +installer for every pull request and every push to `main`. The installer and its SHA-256 +checksum are available as workflow artifacts for 14 days. + +To publish a release: + +1. Update `` in `OneNoteExporter.csproj` and commit the change. +2. Create and push a matching tag, for example `v1.2.0`. +3. GitHub Actions builds the installer, creates the GitHub release with generated notes, and + uploads the installer plus checksum automatically. + +The tag and project versions must match; otherwise the release build fails without publishing. ## Requirements diff --git a/build.ps1 b/build.ps1 index 7bf42b6..1dec800 100644 --- a/build.ps1 +++ b/build.ps1 @@ -9,7 +9,15 @@ $ErrorActionPreference = 'Stop' $ProjectRoot = $PSScriptRoot $BuildDir = Join-Path $ProjectRoot 'build' +$ArtifactDir = Join-Path $ProjectRoot 'artifacts' $PublishDir = Join-Path $ProjectRoot 'bin\Release\net10.0-windows\win-x64\publish' +$ProjectFile = Join-Path $ProjectRoot 'OneNoteExporter.csproj' +[xml]$ProjectXml = Get-Content $ProjectFile +$Version = $ProjectXml.Project.PropertyGroup.Version | Select-Object -First 1 + +if (-not $Version) { + throw 'Keine in OneNoteExporter.csproj gefunden.' +} # ── 1. build-Ordner leeren / anlegen ───────────────────────────────────────── Write-Host ">> Bereite build-Ordner vor..." -ForegroundColor Cyan @@ -20,11 +28,12 @@ New-Item -ItemType Directory -Path $BuildDir | Out-Null # ── 2. dotnet publish (Release, self-contained, x64) ───────────────────────── Write-Host ">> Starte dotnet publish..." -ForegroundColor Cyan -dotnet publish "$ProjectRoot\OneNoteExporter.csproj" ` +dotnet publish $ProjectFile ` -c Release ` -r win-x64 ` --self-contained true ` - -p:PublishSingleFile=false + -p:PublishSingleFile=false ` + "-p:Version=$Version" if ($LASTEXITCODE -ne 0) { Write-Host "FEHLER: dotnet publish fehlgeschlagen (Exit-Code $LASTEXITCODE)." -ForegroundColor Red @@ -49,22 +58,22 @@ $IsssPaths = @( $Iscc = $IsssPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 if (-not $Iscc) { - Write-Host "WARNUNG: ISCC.exe nicht gefunden – Installer wird übersprungen." -ForegroundColor Yellow + Write-Host "WARNUNG: ISCC.exe nicht gefunden - Installer wird übersprungen." -ForegroundColor Yellow Write-Host " -> Inno Setup installieren: https://jrsoftware.org/isdl.php" -ForegroundColor Yellow } elseif (-not (Test-Path $IssFile)) { - Write-Host "WARNUNG: innosetup.iss nicht gefunden – Installer wird übersprungen." -ForegroundColor Yellow + Write-Host "WARNUNG: innosetup.iss nicht gefunden - Installer wird übersprungen." -ForegroundColor Yellow } else { Write-Host ">> Starte Inno Setup: $Iscc" -ForegroundColor Cyan - & $Iscc $IssFile + & $Iscc "/DMyAppVersion=$Version" $IssFile if ($LASTEXITCODE -ne 0) { Write-Host "FEHLER: Inno Setup fehlgeschlagen (Exit-Code $LASTEXITCODE)." -ForegroundColor Red exit $LASTEXITCODE } # Setup-EXE ausgeben - $SetupExe = Get-ChildItem -Path $ProjectRoot -Filter 'OneNoteBackupExporter_Setup_*.exe' | + $SetupExe = Get-ChildItem -Path $ArtifactDir -Filter 'OneNoteBackupExporter_Setup_*.exe' | Sort-Object LastWriteTime -Descending | Select-Object -First 1 if ($SetupExe) { diff --git a/innosetup.iss b/innosetup.iss index 2a8e3a8..204f00f 100644 --- a/innosetup.iss +++ b/innosetup.iss @@ -1,9 +1,12 @@ #define MyAppName "OneNoteBackupExporter" -#define MyAppVersion "1.1.0" +#ifndef MyAppVersion + #define MyAppVersion "1.1.0" +#endif #define MyAppPublisher "JLI Software" -#define MyAppURL "https://github.com/vikingjunior12/OneNoteBackupExporter" +#define MyAppURL "https://github.com/jli-software/OneNoteBackupExporter" #define MyAppExeName "OneNoteExporter.exe" -#define MyBuildDir "C:\Users\JLi1\Nextcloud\C#_pwsh\OneNoteExporterC#\build" +#define MyBuildDir SourcePath + "build" +#define MyOutputDir SourcePath + "artifacts" [Setup] AppId={{BBAE42F6-2F7E-474E-9A2D-C771DB475E3F} @@ -18,7 +21,7 @@ UninstallDisplayIcon={app}\{#MyAppExeName} ArchitecturesAllowed=x64compatible ArchitecturesInstallIn64BitMode=x64compatible DisableProgramGroupPage=yes -OutputDir=C:\Users\JLi1\Nextcloud\C#_pwsh\OneNoteExporterC#\ +OutputDir={#MyOutputDir} OutputBaseFilename=OneNoteBackupExporter_Setup_{#MyAppVersion} SolidCompression=yes WizardStyle=modern @@ -30,7 +33,7 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] -; Gesamter Build-Ordner (self-contained, x86) +; Self-contained x64 publish output Source: "{#MyBuildDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs [Icons] diff --git a/release.sh b/release.sh old mode 100644 new mode 100755 index 4615fd4..abc01ce --- a/release.sh +++ b/release.sh @@ -1,59 +1,40 @@ -# release.sh – GitHub Release erstellen und Setup-EXE hochladen -# Voraussetzungen: git, gh (GitHub CLI), authentifiziert via `gh auth login` +#!/usr/bin/env bash +# release.sh – passenden Release-Tag erstellen und zu GitHub pushen +# GitHub Actions baut und veröffentlicht Installer und Prüfsumme automatisch. set -euo pipefail -VERSION="1.1.0" +if [[ $# -ne 1 ]]; then + echo "Verwendung: $0 (zum Beispiel: $0 1.2.0)" + exit 1 +fi + +VERSION="${1#v}" TAG="v${VERSION}" -SETUP_EXE="OneNoteBackupExporter_Setup_${VERSION}.exe" -REPO="vikingjunior12/OneNoteBackupExporter" -# ── Prüfungen ──────────────────────────────────────────────────────────────── -if ! command -v gh &>/dev/null; then - echo "FEHLER: 'gh' (GitHub CLI) ist nicht installiert." - echo " -> pacman -S github-cli" +if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]; then + echo "FEHLER: Ungültige Version: $VERSION" + exit 1 +fi + +PROJECT_VERSION=$(sed -n 's:.*\([^<]*\).*:\1:p' OneNoteExporter.csproj | head -n 1) +if [[ "$VERSION" != "$PROJECT_VERSION" ]]; then + echo "FEHLER: Version $VERSION stimmt nicht mit der Projektversion $PROJECT_VERSION überein." exit 1 fi -if [[ ! -f "$SETUP_EXE" ]]; then - echo "FEHLER: Setup-Datei nicht gefunden: $SETUP_EXE" - echo " -> Erst build.ps1 + Inno Setup ausführen" +if [[ -n "$(git status --porcelain)" ]]; then + echo "FEHLER: Das Arbeitsverzeichnis enthält nicht committete Änderungen." exit 1 fi -# ── Tag erstellen (falls noch nicht vorhanden) ──────────────────────────────── -if git rev-parse "$TAG" &>/dev/null; then - echo ">> Tag $TAG existiert bereits, wird übersprungen." -else - echo ">> Erstelle Tag $TAG ..." - git tag -a "$TAG" -m "Release $TAG" - git push origin "$TAG" +git fetch origin --tags +if git rev-parse --verify --quiet "refs/tags/$TAG" >/dev/null; then + echo "FEHLER: Tag $TAG existiert bereits." + exit 1 fi -# ── GitHub Release erstellen + EXE hochladen ───────────────────────────────── -echo ">> Erstelle GitHub Release $TAG ..." -gh release create "$TAG" "$SETUP_EXE" \ - --repo "$REPO" \ - --title "OneNoteBackupExporter $TAG" \ - --notes "## OneNoteBackupExporter $TAG - -### New Features - -- **Section export**: Notebooks can now be expanded to show individual sections — select and export only the sections you need -- Sections from nested section groups are fully supported (shown as \`📂 Group / 📄 Section\`) -- Encrypted and recycle-bin sections are automatically skipped -- Mixed export: combine whole-notebook exports and section-level exports in a single run -- Sections are saved into a notebook-named subfolder to keep the output organised (e.g. \`MyNotebook/Section1.onepkg\`) -- Export button text dynamically reflects the selection (e.g. \`Export 1 notebook(s) + 3 section(s)\`) - -### Installation -1. Download \`$SETUP_EXE\` -2. Run the installer -3. OneNote Desktop must be installed and opened at least once - -### System Requirements -- Windows 10/11 x64 -- Microsoft OneNote Desktop (Microsoft 365 or 2019/2021)" - -echo "" -echo "Release erfolgreich: https://github.com/${REPO}/releases/tag/${TAG}" +git tag -a "$TAG" -m "Release $TAG" +git push origin "$TAG" + +echo "Tag $TAG wurde gepusht. GitHub Actions erstellt jetzt das Release."