name: LANCommander Server Build on: workflow_dispatch: workflow_call: inputs: version_semver: description: "Semantic Version" required: true type: string version_tag: description: 'Version Tag' required: true type: string build_dotnet_version: description: 'Build .NET Version' required: true type: string build_runtime: description: 'Build Runtime' required: false type: string default: 'win-x64' build_arch: description: 'Build Architecture' required: false type: string default: 'x64' build_platform: description: 'Build Platform' required: false type: string default: 'Windows' build_configuration: description: 'Build Configuration (Debug/Release)' required: false type: string default: 'Release' permissions: contents: write env: NUGET_PACKAGES: ${{ github.workspace }}/.nuget/package jobs: build: runs-on: ${{ inputs.build_platform == 'Linux' && inputs.build_arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: frabert/replace-string-action@v2 name: Swap Path Backslashes id: swap_path_backslashes with: string: '${{ github.workspace }}' pattern: '\\' replace-with: '/' flags: g # Checkout code - uses: actions/checkout@v3 with: submodules: true # .NET Setup and Caching - name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: ${{ inputs.build_dotnet_version }} - name: Restore dependencies run: dotnet restore --locked-mode # Node.js Setup and Caching - name: Setup Node.js uses: actions/setup-node@v3.8.1 with: node-version: '20' - name: Install Node Packages run: | npm install --prefix ./LANCommander.UI npm install --prefix ./LANCommander.Server - name: Generate PowerShell Completions run: dotnet run --project ./LANCommander.CompletionGenerator/LANCommander.CompletionGenerator.csproj -- ./LANCommander.UI/Components/MonacoCodeEditor/PowerShellCompletions.g.ts - name: Package Frontend run: | npm run package --prefix ./LANCommander.UI npm run package --prefix ./LANCommander.Server # .NET builds - name: Publish Updater and Server run: | # Strip leading 'v' if present RAW_VERSION="${{ inputs.version_tag }}" # e.g. v2.0.0-rc1 SEMVER="${RAW_VERSION#v}" # 2.0.0-rc1 # Numeric part only for Assembly/FileVersion NUMERIC="${SEMVER%%-*}" # 2.0.0 ASSEMBLY_VERSION="${NUMERIC}.0" # 2.0.0.0 echo "SEMVER=$SEMVER" echo "ASSEMBLY_VERSION=$ASSEMBLY_VERSION" dotnet publish "./LANCommander.AutoUpdater/LANCommander.AutoUpdater.csproj" \ -c "${{ inputs.build_configuration }}" \ --self-contained \ --runtime "${{ inputs.build_runtime }}" \ -p:Version="$SEMVER" \ -p:AssemblyVersion="$ASSEMBLY_VERSION" \ -p:FileVersion="$ASSEMBLY_VERSION" \ -p:InformationalVersion="$SEMVER" # Disable NetBeauty on ARM64 runners — nbeauty2 only ships x64 native binaries DISABLE_BEAUTY="False" if [ "${{ inputs.build_arch }}" = "arm64" ] && [ "${{ inputs.build_platform }}" = "Linux" ]; then DISABLE_BEAUTY="True" fi dotnet publish "./LANCommander.Server/LANCommander.Server.csproj" \ -c "${{ inputs.build_configuration }}" \ --self-contained \ --runtime "${{ inputs.build_runtime }}" \ -p:Version="$SEMVER" \ -p:AssemblyVersion="$ASSEMBLY_VERSION" \ -p:FileVersion="$ASSEMBLY_VERSION" \ -p:InformationalVersion="$SEMVER" \ -p:DisableBeauty="$DISABLE_BEAUTY" - name: Bundle and Clean shell: pwsh run: | Copy-Item -Force -Recurse -Verbose LANCommander.AutoUpdater/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/* LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/ # Remove unnecessary files in a single operation $PathsToRemove = @( 'wwwroot/_content/BootstrapBlazor.PdfReader/compat', 'wwwroot/_content/BootstrapBlazor.PdfReader/2.*', 'wwwroot/_content/BootstrapBlazor.PdfReader/build/pdf.sandbox.js', 'wwwroot/_content/BootstrapBlazor.PdfReader/build/*.map', 'wwwroot/_content/BootstrapBlazor.PdfReader/web/*.map', 'wwwroot/_content/AntDesign/less', 'wwwroot/_content/BlazorMonaco/lib/monaco-editor/min-maps', 'wwwroot/Identity/lib/bootstrap', 'LANCommander.ico', 'LANCommanderDark.ico', 'package-lock.json', 'package.json', '*.pdb', 'hostfxr.dll.bak', 'Libraries/locales' ) $BasePath = "LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish" foreach ($path in $PathsToRemove) { Remove-Item -Recurse -Force -ErrorAction Continue "$BasePath/$path" } - name: Bundle macOS .app if: inputs.build_platform == 'macOS' shell: bash run: | set -euo pipefail # Strip leading 'v' and reduce to numeric x.y.z for the bundle version keys. RAW_VERSION="${{ inputs.version_tag }}" SEMVER="${RAW_VERSION#v}" NUMERIC="${SEMVER%%-*}" APP_NAME="LANCommander Server" EXECUTABLE="LANCommander.Server" BUNDLE_ID="app.lancommander.server" ICON_SRC="LANCommander.Server/icon.icns" PUBLISH_DIR="LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish" APP_DIR="${EXECUTABLE}.app" # Lay out the bundle skeleton. mkdir -p "$APP_DIR/Contents/MacOS" "$APP_DIR/Contents/Resources" cp -a "$PUBLISH_DIR/." "$APP_DIR/Contents/MacOS/" cp "$ICON_SRC" "$APP_DIR/Contents/Resources/AppIcon.icns" # Write Info.plist. cat > "$APP_DIR/Contents/Info.plist" < CFBundleName ${APP_NAME} CFBundleDisplayName ${APP_NAME} CFBundleIdentifier ${BUNDLE_ID} CFBundleExecutable ${EXECUTABLE} CFBundleIconFile AppIcon CFBundlePackageType APPL CFBundleInfoDictionaryVersion 6.0 CFBundleShortVersionString ${NUMERIC} CFBundleVersion ${SEMVER} LSMinimumSystemVersion 11.0 NSHighResolutionCapable PLIST # Ensure the entrypoint is executable, then zip while preserving the # permission bits (Compress-Archive drops the exec bit). chmod +x "$APP_DIR/Contents/MacOS/${EXECUTABLE}" zip -ry "LANCommander.Server-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip" "$APP_DIR" - name: Compress Build Output if: inputs.build_platform != 'macOS' shell: pwsh run: | $compress = @{ Path = "LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/*" DestinationPath = "LANCommander.Server-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip" CompressionLevel = "Fastest" } Compress-Archive @compress - name: Upload Artifact uses: actions/upload-artifact@v4 with: path: LANCommander.Server-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip name: LANCommander.Server-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip