diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 172978e258..f49d95e8a0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "fsdocs-tool": { - "version": "22.0.0", + "version": "23.0.0-alpha.1", "commands": [ "fsdocs" ], diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cdb39771cd..d4018e05e1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,39 +10,17 @@ on: - main workflow_dispatch: -env: - FSHARP_DIR: fsharp - jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Checkout fsharp main - uses: actions/checkout@v6 - with: - repository: dotnet/fsharp - path: ${{ env.FSHARP_DIR }} - ref: main - - name: Setup .NET for FSharp + - name: Setup .NET uses: actions/setup-dotnet@v5 with: - global-json-file: ${{ env.FSHARP_DIR }}/global.json - - name: Restore tools - run: dotnet tool restore - - name: Restore FSharp.Compiler.Service.fsproj - run: dotnet restore FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj - - name: Build FCS - run: dotnet build FSharp.Compiler.Service.sln - working-directory: ${{ env.FSHARP_DIR }} - - - name: Run fsdocs - env: - # allow roll forward to latest major version - this would happen for us if we invoked the fsdocs tool instead of invoking the binary directly - DOTNET_ROLL_FORWARD: "LatestMajor" - # need previews because .NET 8 is what's being used at runtime - DOTNET_ROLL_FORWARD_TO_PRERELEASE: "1" - run: dotnet fsdocs build --eval --sourcefolder fsharp --input fsharp/docs + dotnet-version: 10.0.x + - name: Build docs + run: dotnet fsi build.fsx - name: Deploy uses: peaceiris/actions-gh-pages@v4 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 388d39e52a..c1344546cf 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -5,37 +5,14 @@ on: branches: - '**' -env: - FSHARP_DIR: fsharp - jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Checkout fsharp main - uses: actions/checkout@v6 - with: - repository: dotnet/fsharp - path: ${{ env.FSHARP_DIR }} - ref: main - - name: Setup .NET for FSharp + - name: Setup .NET uses: actions/setup-dotnet@v5 with: - global-json-file: ${{ env.FSHARP_DIR }}/global.json - - name: Restore tools - run: dotnet tool restore - - name: Restore FSharp.Compiler.Service.fsproj - run: dotnet restore FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj - - - name: Build FCS - run: dotnet build FSharp.Compiler.Service.sln - working-directory: ${{ env.FSHARP_DIR }} - - - name: Run fsdocs - env: - # allow roll forward to latest major version - this would happen for us if we invoked the fsdocs tool instead of invoking the binary directly - DOTNET_ROLL_FORWARD: "LatestMajor" - # need previews because .NET 8 is what's being used at runtime - DOTNET_ROLL_FORWARD_TO_PRERELEASE: "1" - run: dotnet fsdocs build --eval --sourcefolder fsharp --input fsharp/docs + dotnet-version: 10.0.x + - name: Build docs + run: dotnet fsi build.fsx diff --git a/README.md b/README.md index cd27934674..56dfed92fe 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ signature files (`*.fsi`) in the FSharp.Compiler.Service implementation. * Fork and clone https://github.com/dotnet/fsharp locally, see below -* Contribute to [src/fsharp directory](https://github.com/dotnet/fsharp/tree/master/docs) and [src/fsharp directory](https://github.com/dotnet/fsharp/tree/master/src/fsharp) and submit work to `main` branch of [dotnet/fsharp](https://github.com/dotnet/fsharp) +* Contribute to [docs directory](https://github.com/dotnet/fsharp/tree/main/docs) and [src/Compiler directory](https://github.com/dotnet/fsharp/tree/main/src/Compiler) and submit work to `main` branch of [dotnet/fsharp](https://github.com/dotnet/fsharp) * Once accepted your work will be published through a rebuild here. A rebuild is triggered daily at 17:30 UTC or you can trigger it yourself by submitting a dummy change. @@ -17,33 +17,28 @@ The docs are generated by using `fsdocs` tool from FSharp.Formatting. ## Build steps -Eventually the build will just be +Everything is driven by `build.fsx`, a [Fun.Build](https://github.com/slaveOftime/Fun.Build) script. +You need a .NET 10 SDK to run it. The default pipeline does the following: - dotnet tool restore - dotnet restore FSharp.Compiler.Service - dotnet fsdocs build +1. Clones [dotnet/fsharp](https://github.com/dotnet/fsharp) (shallow, `main` branch) into the git-ignored `fsharp` folder, unless it already exists. +2. Builds `FSharp.Compiler.Service.slnx` with the SDK that `fsharp/global.json` pins, using dotnet/fsharp's own `eng/common/dotnet.sh` bootstrap so that SDK does not need to be on your PATH. +3. Restores the `fsdocs` tool and the `FSharp.Compiler.Service` project in this repo. +4. Runs `fsdocs build` against `fsharp/docs` and writes the site to `output`. -For now, we make a fresh build of FSharp.Compiler.Service. + dotnet fsi build.fsx - (start in fsharp-compiler-docs) - dotnet restore FSharp.Compiler.Service - dotnet tool restore +To pick up new upstream changes, delete the `fsharp` folder and run the pipeline again. - (make fsharp-compiler-docs/fsharp) - git clone https://github.com/dotnet/fsharp --depth 1 -b main +Once a build has run, iterate on the docs with live reload: - (build fsharp-compiler-docs/fsharp) - pushd fsharp - dotnet build src/Compiler/FSharp.Compiler.Service.fsproj /p:BUILDING_USING_DOTNET=true - popd + dotnet fsi build.fsx -- -p Watch -Then do iterative development using: +Anything after the pipeline name is passed on to `fsdocs watch`, for example `-- -p Watch --nolaunch --port 8080`. - (from fsharp-compiler-docs) - dotnet fsdocs watch --sourcefolder fsharp --input fsharp/docs +Run `dotnet fsi build.fsx -- -h` to list the pipelines. ## CI Pipeline -This repo is published via GitHub Actions. On each push to main, the docs are built, and the outputs (which are written to the `output` directory by fsdocs) are pushed to the `gh-pages` branch. This repo is configured to host using GitHub Pages from this branch. +This repo is published via GitHub Actions. On each push to main, `build.fsx` runs, and the outputs (which are written to the `output` directory by fsdocs) are pushed to the `gh-pages` branch. This repo is configured to host using GitHub Pages from this branch. diff --git a/build.fsx b/build.fsx new file mode 100755 index 0000000000..bc30be5158 --- /dev/null +++ b/build.fsx @@ -0,0 +1,88 @@ +#!/usr/bin/env -S dotnet fsi -- +#r "nuget: Fun.Build, 1.1.18" + +open System +open System.IO +open Fun.Build + +let root = __SOURCE_DIRECTORY__ + +/// A shallow clone of dotnet/fsharp main. It is git-ignored; the docs are generated from its +/// `docs` folder and from the FSharp.Compiler.Service it builds. +let fsharpDir = Path.Combine(root, "fsharp") + +/// The SDK bootstrap script dotnet/fsharp ships. It runs its arguments with the exact SDK that +/// `fsharp/global.json` asks for, installing it under `fsharp/.dotnet` first when this machine +/// does not have it. Building through it means nobody has to keep a preview SDK on their PATH. +let upstreamDotnet = + if OperatingSystem.IsWindows() then + let script = Path.Combine(fsharpDir, "eng", "common", "dotnet.cmd") + $"cmd /c \"{script}\"" + else + Path.Combine(fsharpDir, "eng", "common", "dotnet.sh") + +let cloneStage = + stage "clone fsharp" { + run (fun ctx -> + async { + if Directory.Exists fsharpDir then + printfn "fsharp/ already exists, not cloning. Delete it to start from a fresh checkout." + return Ok() + else + return! ctx.RunCommand "git clone https://github.com/dotnet/fsharp --depth 1 -b main fsharp" + }) + } + +let buildFcsStage = + stage "build FSharp.Compiler.Service" { + workingDir fsharpDir + run $"{upstreamDotnet} build FSharp.Compiler.Service.slnx" + } + +let restoreStage = + stage "restore" { + run "dotnet tool restore" + run "dotnet restore FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj" + } + +/// fsdocs targets a fixed .NET runtime. Let it run on whatever newer runtime the machine has, +/// prereleases included, so the SDK dotnet/fsharp needs is also enough to run the tool. +let fsdocsEnv = + [ "DOTNET_ROLL_FORWARD", "LatestMajor"; "DOTNET_ROLL_FORWARD_TO_PRERELEASE", "1" ] + +let fsdocsArgs = "--eval --sourcefolder fsharp --input fsharp/docs" + +pipeline "Build" { + description "Clone dotnet/fsharp, build FSharp.Compiler.Service and generate the docs into output/." + workingDir root + cloneStage + buildFcsStage + restoreStage + stage "docs" { + envVars fsdocsEnv + run $"dotnet fsdocs build {fsdocsArgs}" + } + runIfOnlySpecified false +} + +/// Everything after the pipeline name is passed on to `fsdocs watch`, for example +/// `dotnet fsi build.fsx -- -p Watch --nolaunch --port 8080`. +pipeline "Watch" { + description "Serve the docs with live reload. Expects a Build to have run first." + workingDir root + restoreStage + stage "watch" { + envVars fsdocsEnv + run (fun ctx -> + let extraArgs = + fsi.CommandLineArgs + |> Array.skipWhile (fun arg -> arg <> "Watch") + |> Array.skip 1 + |> String.concat " " + + ctx.RunCommand $"dotnet fsdocs watch {fsdocsArgs} {extraArgs}") + } + runIfOnlySpecified true +} + +tryPrintPipelineCommandHelp ()