Hot Reload Prebuilt SDK #126
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: Hot Reload Prebuilt SDK | |
| # Manually-dispatched: builds the F#-hot-reload compiler + the F#-aware SDK on each OS, syncs the | |
| # compiler into the SDK redist, and publishes the runnable SDK as a GitHub Release asset. Lives on | |
| # the fork's default branch so it is dispatchable, but it builds the feature branches (hot-reload-v2 | |
| # and fsharp-hotreload-watch-v2) and never touches the review PR. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to publish the binaries to" | |
| required: true | |
| default: "hot-reload-preview" | |
| # Auto-refresh: on a schedule, rebuild ONLY if hot-reload-v2 or fsharp-hotreload-watch-v2 moved | |
| # since the last publish. Cheap when nothing changed (just the `check` job runs, ~30s). Tune the | |
| # cadence by editing the cron below. | |
| schedule: | |
| - cron: "0 */3 * * *" | |
| # Instant trigger you can fire from anywhere (e.g. after pushing): | |
| # gh api repos/NatElkins/fsharp/dispatches -f event_type=rebuild-binaries | |
| repository_dispatch: | |
| types: [rebuild-binaries] | |
| permissions: | |
| contents: write | |
| jobs: | |
| # Gate: build only when the source branches actually changed (always builds on manual/dispatch). | |
| check: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| changed: ${{ steps.c.outputs.changed }} | |
| combo: ${{ steps.c.outputs.combo }} | |
| tag: ${{ steps.c.outputs.tag }} | |
| steps: | |
| - id: c | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| tag="${{ github.event.inputs.tag }}" | |
| [ -z "$tag" ] && tag="hot-reload-preview" | |
| fsha=$(git ls-remote https://github.com/NatElkins/fsharp.git hot-reload-v2 | cut -f1) | |
| ssha=$(git ls-remote https://github.com/NatElkins/sdk.git fsharp-hotreload-watch-v2 | cut -f1) | |
| combo="${fsha:0:12}-${ssha:0:12}" | |
| last=$(gh release view "$tag" --repo "$GITHUB_REPOSITORY" --json body --jq '.body' 2>/dev/null \ | |
| | grep -oE 'built-from: [a-f0-9-]+' | awk '{print $2}' || true) | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "combo=$combo" >> "$GITHUB_OUTPUT" | |
| if [ "${{ github.event_name }}" = "schedule" ] && [ "$combo" = "$last" ]; then | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| echo "No source changes since last publish ($combo); skipping build." | |
| else | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| echo "Building. combo=$combo last=${last:-none} event=${{ github.event_name }}" | |
| fi | |
| build: | |
| needs: check | |
| if: needs.check.outputs.changed == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { os: ubuntu-latest, rid: linux-x64 } | |
| - { os: macos-latest, rid: osx-arm64 } | |
| - { os: windows-latest, rid: win-x64 } | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Free disk space (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL | |
| df -h / | |
| - name: Free disk space (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| sudo rm -rf /Users/runner/hostedtoolcache || true | |
| df -h / | |
| - name: Enable git long paths (Windows) | |
| if: runner.os == 'Windows' | |
| shell: bash | |
| run: git config --system core.longpaths true | |
| - name: Checkout F# compiler (hot-reload-v2) | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: NatElkins/fsharp | |
| ref: hot-reload-v2 | |
| path: fsharp | |
| - name: Checkout SDK (fsharp-hotreload-watch-v2) | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: NatElkins/sdk | |
| ref: fsharp-hotreload-watch-v2 | |
| path: sdk | |
| - name: Guard against checked-in toolchain paths | |
| shell: bash | |
| # A dev-machine .dotnet symlink once got committed to hot-reload-v2 and broke the | |
| # toolchain bootstrap on every CI checkout (dangling symlink, so writing | |
| # .dotnet/dotnet-install.sh failed in a way curl reports as a network error). | |
| # Fail fast with a clear message if that ever happens again. | |
| run: | | |
| set -euo pipefail | |
| for repo in fsharp sdk; do | |
| entry="$GITHUB_WORKSPACE/$repo/.dotnet" | |
| if [ -L "$entry" ] || { [ -e "$entry" ] && [ ! -d "$entry" ]; }; then | |
| echo "::error::$repo/.dotnet exists in the checkout as a non-directory (committed symlink or file). Remove it from the branch." | |
| ls -la "$entry" | |
| exit 1 | |
| fi | |
| done | |
| - name: Build F# compiler (unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| working-directory: fsharp | |
| run: ./build.sh -c Debug | |
| - name: Build F# compiler (Windows) | |
| if: runner.os == 'Windows' | |
| shell: cmd | |
| working-directory: fsharp | |
| run: build.cmd -c Debug | |
| - name: Prune F# artifacts to save disk (keep only the compiler service) | |
| shell: bash | |
| # The only F# outputs the redist needs are FSharp.Compiler.Service.dll + FSharp.Core.dll | |
| # (synced in below). Drop everything else so the SDK build has room on the smaller runners. | |
| run: | | |
| cd "$GITHUB_WORKSPACE/fsharp/artifacts" | |
| rm -rf obj || true | |
| find bin -mindepth 1 -maxdepth 1 ! -name FSharp.Compiler.Service ! -name FSharp.Core -exec rm -rf {} + || true | |
| df -h / 2>/dev/null | tail -1 || true | |
| # The default build compiles the repo's test projects, several of which do not build on this | |
| # merged branch (MSTEST0071, missing xUnit Fact, etc.). So: (1) run the full build to RESTORE | |
| # the whole repo (its restore phase runs before the build phase, which we let fail at the | |
| # broken tests); (2) then build ONLY the redist project, which compiles no test projects and | |
| # now has its transitive deps restored. | |
| - name: Build SDK redist (unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| working-directory: sdk | |
| run: | | |
| ./build.sh -c Debug /p:RunAnalyzers=false || true | |
| ./build.sh --build --projects "$(pwd)/src/Layout/redist/redist.csproj" -c Debug /p:RunAnalyzers=false | |
| - name: Build SDK redist (Windows) | |
| if: runner.os == 'Windows' | |
| shell: cmd | |
| working-directory: sdk | |
| # eng/build.ps1 has no -projects switch, so after the full restore (build.cmd, which fails at | |
| # the broken tests) build the redist directly with the bootstrapped dotnet. | |
| run: | | |
| call build.cmd -c Debug /p:RunAnalyzers=false | |
| .dotnet\dotnet build src\Layout\redist\redist.csproj -c Debug /p:RunAnalyzers=false | |
| - name: Sync hot-reload FCS into the redist | |
| shell: bash | |
| run: | | |
| fcs="$GITHUB_WORKSPACE/fsharp/artifacts/bin/FSharp.Compiler.Service/Debug/netstandard2.0" | |
| for d in "$GITHUB_WORKSPACE"/sdk/artifacts/bin/redist/Debug/dotnet/sdk/*/FSharp; do | |
| cp -f "$fcs/FSharp.Compiler.Service.dll" "$d/" | |
| cp -f "$fcs/FSharp.Core.dll" "$d/" | |
| echo "synced FCS -> $d" | |
| done | |
| - name: Package redist | |
| shell: bash | |
| run: tar -czf "fsharp-hotreload-sdk-${{ matrix.rid }}.tar.gz" -C "$GITHUB_WORKSPACE/sdk/artifacts/bin/redist/Debug" dotnet | |
| - name: Publish to GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.check.outputs.tag }} | |
| files: fsharp-hotreload-sdk-${{ matrix.rid }}.tar.gz | |
| finalize: | |
| needs: [check, build] | |
| runs-on: ubuntu-latest | |
| steps: | |
| # The release is refreshed in place on a fixed tag, so the page header keeps the | |
| # original publish date; a visible build timestamp in the body is the freshness signal. | |
| - name: Compute build timestamp | |
| id: stamp | |
| run: echo "date=$(date -u '+%Y-%m-%d %H:%M UTC')" >> "$GITHUB_OUTPUT" | |
| - name: Stamp GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.check.outputs.tag }} | |
| name: F# hot reload preview SDK | |
| body: | | |
| Prebuilt .NET SDK with F# hot reload (the F#-aware `dotnet watch`). | |
| **Last built: ${{ steps.stamp.outputs.date }}** (the binaries below are replaced in place; ignore the release date in the header) | |
| Unpack the archive for your platform and use the `dotnet` inside it: | |
| tar -xzf fsharp-hotreload-sdk-<rid>.tar.gz | |
| export PATH="$PWD/dotnet:$PATH" # Windows: add the dotnet folder to PATH | |
| dotnet watch run # in an F# project that opts in | |
| See [docs/hot-reload-quickstart.md](https://github.com/NatElkins/fsharp/blob/hot-reload-v2/docs/hot-reload-quickstart.md) (step 4 onward) for the opt-in flag and what to try editing. | |
| built-from: ${{ needs.check.outputs.combo }} |