2026.05.31 #5
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Publish | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| build-deb: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET 10 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '10.0.x' | |
| - name: Build deb packages | |
| working-directory: sources | |
| run: | | |
| chmod +x build-deb.sh | |
| ./build-deb.sh | |
| - name: Upload deb artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: deb-packages | |
| path: Output/*.deb | |
| - name: Upload debs to GitHub Release | |
| if: github.event_name == 'release' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| for deb in Output/*.deb; do | |
| gh release upload "${{ github.event.release.tag_name }}" "$deb" --clobber | |
| done | |
| build-windows: | |
| runs-on: windows-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| installer_hash: ${{ steps.hash.outputs.hash }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET 10 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '10.0.x' | |
| - name: Get version | |
| id: version | |
| shell: pwsh | |
| run: | | |
| $version = (Get-Content "CHANGELOG.md" -First 1).Substring(2) | |
| echo "version=$version" >> $env:GITHUB_OUTPUT | |
| echo "Version: $version" | |
| - name: Publish win-x64 | |
| shell: pwsh | |
| working-directory: sources | |
| run: | | |
| $v = "${{ steps.version.outputs.version }}" | |
| dotnet publish FtpsServer.slnx "/p:InformationalVersion=$v" "/p:VersionPrefix=$v" "/p:Version=$v" "/p:AssemblyVersion=$v" ` | |
| "--runtime=win-x64" -c Release "/p:PublishDir=../../Output/publish/x64" ` | |
| /p:PublishReadyToRun=false /p:RunAnalyzersDuringBuild=False --self-contained true --property WarningLevel=0 | |
| if ($LASTEXITCODE -ne 0) { exit 1 } | |
| - name: Publish win-arm64 | |
| shell: pwsh | |
| working-directory: sources | |
| run: | | |
| $v = "${{ steps.version.outputs.version }}" | |
| dotnet publish FtpsServer.slnx "/p:InformationalVersion=$v" "/p:VersionPrefix=$v" "/p:Version=$v" "/p:AssemblyVersion=$v" ` | |
| "--runtime=win-arm64" -c Release "/p:PublishDir=../../Output/publish/arm64" ` | |
| /p:PublishReadyToRun=false /p:RunAnalyzersDuringBuild=False --self-contained true --property WarningLevel=0 | |
| if ($LASTEXITCODE -ne 0) { exit 1 } | |
| - name: Prepare binaries README | |
| shell: pwsh | |
| run: Copy-Item "README.md" "Output\publish\README.md" | |
| - name: Install NSIS | |
| shell: pwsh | |
| run: choco install nsis -y | |
| - name: Build NSIS installer | |
| shell: pwsh | |
| run: | | |
| $v = "${{ steps.version.outputs.version }}" | |
| & "C:\Program Files (x86)\NSIS\Bin\makensis.exe" "/DPRODUCT_VERSION=$v" "scripts\setup.nsi" | |
| if ($LASTEXITCODE -ne 0) { exit 1 } | |
| - name: Compute installer SHA256 | |
| id: hash | |
| shell: pwsh | |
| run: | | |
| $v = "${{ steps.version.outputs.version }}" | |
| $hash = (Get-FileHash -Path "Output\FtpsServerWindows_v$v.exe" -Algorithm SHA256).Hash | |
| echo "hash=$hash" >> $env:GITHUB_OUTPUT | |
| echo "Installer SHA256: $hash" | |
| - name: Pack 7z archive | |
| shell: pwsh | |
| run: | | |
| $v = "${{ steps.version.outputs.version }}" | |
| Push-Location "Output\publish" | |
| & "C:\Program Files\7-Zip\7z.exe" a -y "..\FtpsServer_v${v}_win-binaries.7z" * -mx9 -t7z -m0=lzma2 -ms=on -sccUTF-8 -ssw | |
| if ($LASTEXITCODE -ne 0) { exit 1 } | |
| Pop-Location | |
| - name: Clean publish staging | |
| shell: pwsh | |
| run: Remove-Item "Output\publish" -Recurse -Force | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-packages | |
| path: | | |
| Output/*.exe | |
| Output/*.7z | |
| - name: Upload Windows artifacts to GitHub Release | |
| if: github.event_name == 'release' | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| $v = "${{ steps.version.outputs.version }}" | |
| gh release upload "${{ github.event.release.tag_name }}" "Output\FtpsServerWindows_v$v.exe" --clobber | |
| gh release upload "${{ github.event.release.tag_name }}" "Output\FtpsServer_v${v}_win-binaries.7z" --clobber | |
| build-android: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET 10 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '10.0.x' | |
| - name: Install Android workload | |
| run: dotnet workload install android | |
| - name: Get version | |
| id: version | |
| run: | | |
| version=$(head -1 CHANGELOG.md | sed 's/^\xEF\xBB\xBF//' | sed 's/^# //') | |
| echo "version=$version" >> $GITHUB_OUTPUT | |
| echo "Version: $version" | |
| - name: Publish Android | |
| working-directory: sources | |
| run: | | |
| v="${{ steps.version.outputs.version }}" | |
| success=false | |
| for attempt in 1 2 3; do | |
| echo "Attempt $attempt of 3..." | |
| if dotnet publish FtpsServer.Android.slnx \ | |
| "/p:InformationalVersion=$v" \ | |
| "/p:VersionPrefix=$v" \ | |
| "/p:Version=$v" \ | |
| "/p:AssemblyVersion=$v" \ | |
| /p:Configuration=Release \ | |
| /p:PublishReadyToRun=false \ | |
| /p:RunAnalyzersDuringBuild=False \ | |
| --self-contained true \ | |
| --property WarningLevel=0; then | |
| success=true | |
| break | |
| fi | |
| [ $attempt -lt 3 ] && echo "Build failed. Retrying in 2s..." && sleep 2 | |
| done | |
| $success || exit 1 | |
| - name: Stage APK | |
| run: | | |
| v="${{ steps.version.outputs.version }}" | |
| mkdir -p Output | |
| echo "APK files produced:" | |
| find sources/FtpsServerAvalonia/FtpsServerAvalonia.Android/bin -name "*.apk" -type f | |
| apk=$(find sources/FtpsServerAvalonia/FtpsServerAvalonia.Android/bin -name "*.apk" -type f | head -1) | |
| if [ -z "$apk" ]; then | |
| echo "Error: APK not found" | |
| exit 1 | |
| fi | |
| echo "Using: $apk" | |
| cp "$apk" "Output/com.SiarheiKuchuk.FtpsServer-${v}.apk" | |
| - name: Upload Android artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-package | |
| path: Output/*.apk | |
| - name: Upload Android APK to GitHub Release | |
| if: github.event_name == 'release' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| v="${{ steps.version.outputs.version }}" | |
| gh release upload "${{ github.event.release.tag_name }}" "Output/com.SiarheiKuchuk.FtpsServer-${v}.apk" --clobber | |
| update-apt-repo: | |
| needs: build-deb | |
| if: github.event_name == 'release' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download deb artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: deb-packages | |
| path: debs | |
| - name: Import GPG key | |
| run: | | |
| echo "${{ secrets.DEB_GPG_PRIVATE_KEY }}" | gpg --batch --import | |
| GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format long 2>/dev/null \ | |
| | grep "^sec" | head -1 | awk '{print $2}' | cut -d'/' -f2) | |
| echo "GPG_KEY_ID=$GPG_KEY_ID" >> "$GITHUB_ENV" | |
| echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf | |
| gpgconf --kill gpg-agent | |
| - name: Build APT repository | |
| run: | | |
| mkdir -p apt-repo/pool/main | |
| mkdir -p apt-repo/dists/stable/main/binary-amd64 | |
| mkdir -p apt-repo/dists/stable/main/binary-arm64 | |
| cp debs/*.deb apt-repo/pool/main/ | |
| gpg --armor --export "$GPG_KEY_ID" > apt-repo/gpg-key.pub | |
| cd apt-repo | |
| for arch in amd64 arm64; do | |
| dpkg-scanpackages --arch "$arch" pool/ > "dists/stable/main/binary-${arch}/Packages" | |
| gzip -k -f "dists/stable/main/binary-${arch}/Packages" | |
| done | |
| cd dists/stable | |
| apt-ftparchive \ | |
| -o APT::FTPArchive::Release::Origin="FtpsServer" \ | |
| -o APT::FTPArchive::Release::Label="FtpsServer" \ | |
| -o APT::FTPArchive::Release::Suite="stable" \ | |
| -o APT::FTPArchive::Release::Codename="stable" \ | |
| -o APT::FTPArchive::Release::Architectures="amd64 arm64" \ | |
| -o APT::FTPArchive::Release::Components="main" \ | |
| -o APT::FTPArchive::Release::Description="FTPS Server APT Repository" \ | |
| release . > Release | |
| gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.DEB_GPG_PASSPHRASE }}" --default-key "$GPG_KEY_ID" -abs -o Release.gpg Release | |
| gpg --batch --yes --pinentry-mode loopback --passphrase "${{ secrets.DEB_GPG_PASSPHRASE }}" --default-key "$GPG_KEY_ID" --clearsign -o InRelease Release | |
| cd ../.. | |
| - name: Force-push to gh-pages | |
| run: | | |
| cd apt-repo | |
| git init -b gh-pages | |
| git remote add origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add -A | |
| git commit -m "APT repo for ${{ github.event.release.tag_name }}" | |
| git push --force origin gh-pages | |
| update-winget: | |
| needs: build-windows | |
| if: github.event_name == 'release' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: | | |
| sources/tools/winget-pkgs | |
| sources/tools/ResxSorter | |
| sources/FtpsServerWindows/Resources | |
| - name: Setup .NET 10 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '10.0.x' | |
| - name: Generate winget locale files | |
| working-directory: sources/tools/ResxSorter | |
| run: dotnet run | |
| - name: Create winget-pkgs branch | |
| run: | | |
| version="${{ needs.build-windows.outputs.version }}" | |
| hash="${{ needs.build-windows.outputs.installer_hash }}" | |
| release_date=$(echo "$version" | tr '.' '-') | |
| current_year=$(date +%Y) | |
| echo "Version: $version" | |
| echo "Hash: $hash" | |
| echo "Release date: $release_date" | |
| git clone --depth 1 \ | |
| "https://x-access-token:${{ secrets.WINGET_FORK_PAT }}@github.com/drweb86/winget-pkgs.git" \ | |
| winget-fork | |
| cd winget-fork | |
| git checkout -b "ftps-server-${version}" | |
| manifest_dir="manifests/s/SiarheiKuchuk/FtpsServer/${version}" | |
| mkdir -p "$manifest_dir" | |
| sed -e "s/APP_VERSION_STRING/$version/g" \ | |
| -e "s/2001-01-01/$release_date/g" \ | |
| -e "s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/$hash/g" \ | |
| ../sources/tools/winget-pkgs/SiarheiKuchuk.FtpsServer.installer.yaml \ | |
| > "$manifest_dir/SiarheiKuchuk.FtpsServer.installer.yaml" | |
| sed -e "s/APP_VERSION_STRING/$version/g" \ | |
| ../sources/tools/winget-pkgs/SiarheiKuchuk.FtpsServer.yaml \ | |
| > "$manifest_dir/SiarheiKuchuk.FtpsServer.yaml" | |
| for locale_file in ../sources/tools/winget-pkgs/SiarheiKuchuk.FtpsServer.locale.*.yaml; do | |
| filename=$(basename "$locale_file") | |
| sed -e "s/APP_VERSION_STRING/$version/g" \ | |
| -e "s/CURRENT_YEAR/$current_year/g" \ | |
| "$locale_file" > "$manifest_dir/$filename" | |
| done | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add -A | |
| git commit -m "FTPS Server $version" | |
| git push -u origin "ftps-server-${version}" | |
| echo "" | |
| echo "Branch 'ftps-server-${version}' pushed to drweb86/winget-pkgs" | |
| echo "Create PR at: https://github.com/microsoft/winget-pkgs/compare/master...drweb86:winget-pkgs:ftps-server-${version}" |