From c107e8775f4ad09a268a6d52b3e3fe5e181a7a4a Mon Sep 17 00:00:00 2001 From: Tom Atwood Date: Tue, 15 Sep 2026 22:16:46 -0400 Subject: [PATCH] docs: land leftover Copilot nits from the 6.0.0 pass why.md missing-field example, void Add, release checklist, XML docs for Direct/!?, Operators conversion wording, DocFX alert selectors and link RGB, SECURITY.md triggers, VS Code task, changelog tryGet and 5.0.0.25 notes. Closes #114 Closes #115 Closes #116 Closes #117 Closes #118 Closes #119 Closes #120 Closes #121 Closes #122 Closes #123 Closes #124 Closes #125 Closes #126 --- .vscode/tasks.json | 33 ++++++++++++++------ CHANGELOG.md | 7 +++-- FSharp.Interop.Dynamic/Dyn.fsi | 4 ++- FSharp.Interop.Dynamic/Operators.fsi | 2 +- FSharp.Interop.Dynamic/TopLevelOperators.fsi | 4 ++- SECURITY.md | 8 ++--- docfx/docs/operators.md | 2 +- docfx/docs/releasing.md | 10 +++--- docfx/docs/why.md | 13 ++++++-- docfx/template/public/main.css | 17 ++++++++++ 10 files changed, 73 insertions(+), 27 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1dd2836..bb7204f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,10 +1,25 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "0.1.0", - "command": "./build.fsx", - "isShellCommand": true, - "args": [], - "showOutput": "always", - "problemMatcher":"$msCompile" -} \ No newline at end of file + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "dotnet build --configuration Release", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile" + }, + { + "label": "test", + "type": "shell", + "command": "dotnet test Tests/Tests.fsproj --configuration Release", + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": "$msCompile" + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index bc5b5e0..3466d2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Each release has a `## [version]` section. When a `v*.*.*` tag is pushed, the Pu ### Docs - Caveats document 15-argument `TypeLoadException`, C# optional parameters, and `tryGet` / `exists` on a null target (#110). +- Copilot leftovers from the 6.0.0 docs pass: why.md examples, operators void `Add`, release checklist, XML docs for `Direct` / `!?` / Operators, DocFX theme alerts and link RGB, SECURITY.md triggers, VS Code task, changelog wording (#114–#126). ## [6.0.0] @@ -17,7 +18,7 @@ Each release has a `## [version]` section. When a `v*.*.*` tag is pushed, the Pu ### Added -- `Dyn.tryGet` and `Dyn.exists`: look up a member as `'T option`, or test for it, without throwing (#42, #43). +- `Dyn.tryGet` and `Dyn.exists`: look up a member as `'T option`, or test for it, without throwing on a missing-member binder exception. Conversion failures and throwing getters still throw (#42, #43, #118). ### Build, security, and docs @@ -35,7 +36,9 @@ Each release has a `## [version]` section. When a `v*.*.*` tag is pushed, the Pu ## [5.0.0.25] - 2019-09-11 -No release notes were published for this version. +- Handles more corner cases for dynamic invocation. +- Rewrite of `Dyn` module functions favoring piping (`|>`). +- Adds a .NET Standard 2.0 target. ## [4.0.3.130] - 2018-04-05 diff --git a/FSharp.Interop.Dynamic/Dyn.fsi b/FSharp.Interop.Dynamic/Dyn.fsi index d29aefe..dc0240e 100644 --- a/FSharp.Interop.Dynamic/Dyn.fsi +++ b/FSharp.Interop.Dynamic/Dyn.fsi @@ -41,7 +41,9 @@ namespace FSharp.Interop.Dynamic /// Direct to invoke itself. /// /// - /// If the inferred result type is not a function, a DLR get converted to that type. + /// If the inferred result type is not a function: a DLR get (InvokeGet) for + /// Member / GenericMember; for Direct, + /// converted to that type (no get). /// If the inferred result type is a function, a callable: applying it does a DLR invoke /// (InvokeMember for a named member, Invoke for Direct). /// A void CLR method is an unit-returning function. diff --git a/FSharp.Interop.Dynamic/Operators.fsi b/FSharp.Interop.Dynamic/Operators.fsi index 632837a..af99820 100644 --- a/FSharp.Interop.Dynamic/Operators.fsi +++ b/FSharp.Interop.Dynamic/Operators.fsi @@ -1,6 +1,6 @@ namespace FSharp.Interop.Dynamic /// - /// Dynamic DLR binary operators plus an inferred conversion of the result. Not auto-opened; open FSharp.Interop.Dynamic.Operators. + /// Dynamic DLR binary operators. Arithmetic and bitwise operators unbox the DLR result (:?>). Comparisons convert that result to bool with Dyn.explicitConvert. Not auto-opened; open FSharp.Interop.Dynamic.Operators. /// module Operators = begin /// Dynamically take modulo of by . diff --git a/FSharp.Interop.Dynamic/TopLevelOperators.fsi b/FSharp.Interop.Dynamic/TopLevelOperators.fsi index d9183ae..bcd4196 100644 --- a/FSharp.Interop.Dynamic/TopLevelOperators.fsi +++ b/FSharp.Interop.Dynamic/TopLevelOperators.fsi @@ -28,7 +28,9 @@ namespace FSharp.Interop.Dynamic /// /// Callable object to invoke. /// - /// The converted invoke result, or a callable if the inferred result type is a function. + /// If the inferred result type is not a function, converted + /// to that type (no invoke). If it is a function, a callable: applying it invokes + /// . /// val ( !? ) : target:obj -> 'TResult end diff --git a/SECURITY.md b/SECURITY.md index 020c761..dffb0a2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -96,10 +96,10 @@ required checks below. | Check | When | | --- | --- | | Dependabot | Weekly NuGet and GitHub Actions; 7-day cooldown | -| OSV-Scanner (full) | `master` only | -| Scorecard SARIF upload | `master` only | -| GitHub Pages deploy | `master` only | -| NuGet publish + Sigstore provenance | Version tags `v*.*.*` only (Trusted Publishing / `NuGet/login`) | +| OSV-Scanner (full) | Push to `master`, weekly schedule, `workflow_dispatch` | +| Scorecard SARIF upload | Non-PR events (`master` push, schedule, `workflow_dispatch`) | +| GitHub Pages deploy | Push to `master` | +| NuGet publish + Sigstore provenance | Version tags `v*.*.*` and `workflow_dispatch` with a version (Trusted Publishing / `NuGet/login`). `workflow_dispatch` does not create a GitHub release | Central Package Management (`Directory.Packages.props`) and `packages.lock.json` pin restore. GitHub Actions `uses:` lines are commit SHAs. diff --git a/docfx/docs/operators.md b/docfx/docs/operators.md index 4d3d580..86542b8 100644 --- a/docfx/docs/operators.md +++ b/docfx/docs/operators.md @@ -20,7 +20,7 @@ A void CLR method is an `unit`-returning function: ```fsharp let items = ResizeArray() -items?Add("1") +let _: unit = items?Add("1") ``` ## `?<-` — set a named member diff --git a/docfx/docs/releasing.md b/docfx/docs/releasing.md index 7613c83..a9da30a 100644 --- a/docfx/docs/releasing.md +++ b/docfx/docs/releasing.md @@ -17,12 +17,12 @@ git push origin vX.Y.Z The Publish workflow will: -1. Extract the `## [6.0.0]` section of `CHANGELOG.md`, and fail if there isn't one +1. Extract the `## [X.Y.Z]` section of `CHANGELOG.md` for the tagged version, and fail if there isn't one 2. Restore, build `-warnaserror`, test -3. Pack `FSharp.Interop.Dynamic` at `6.0.0` (nupkg + snupkg) +3. Pack `FSharp.Interop.Dynamic` at `X.Y.Z` (nupkg + snupkg) 4. Attest SLSA build provenance for the nupkg and snupkg, signed with Sigstore through GitHub's OIDC identity 5. `dotnet nuget push` to nuget.org with `--skip-duplicate` -6. Create the GitHub release `v6.0.0` with those notes, the packages, and the provenance bundle `FSharp.Interop.Dynamic.6.0.0.intoto.jsonl` attached. A version with a `-` suffix is marked as a prerelease. +6. Create the GitHub release `vX.Y.Z` with those notes, the packages, and the provenance bundle `FSharp.Interop.Dynamic.X.Y.Z.intoto.jsonl` attached. A version with a `-` suffix is marked as a prerelease. `workflow_dispatch` with a version input does steps 1–5 without a tag and creates no GitHub release. Prefer the tag. @@ -31,11 +31,11 @@ The Publish workflow will: Anyone can check that a package was built by this repository's Publish workflow: ```bash -gh attestation verify FSharp.Interop.Dynamic.6.0.0.nupkg --repo fsprojects/FSharp.Interop.Dynamic +gh attestation verify FSharp.Interop.Dynamic.X.Y.Z.nupkg --repo fsprojects/FSharp.Interop.Dynamic ``` Pull requests never publish. ## After it lands -Update the README / docs callout that nuget.org is still 5.0.1.268. The next patch is `v6.0.1`, not another 5.x. +Confirm nuget.org lists the new version and that README / getting-started install instructions match it. The next version after 6.0.0 is `v6.0.1` (or `v6.1.0` / `v7.0.0`), not another 5.x. diff --git a/docfx/docs/why.md b/docfx/docs/why.md index dd6f9f1..3943f9e 100644 --- a/docfx/docs/why.md +++ b/docfx/docs/why.md @@ -1,6 +1,6 @@ # Why this library -F# has no `dynamic` keyword. C# does. A lot of .NET still hands you objects whose shape is decided at runtime: `ExpandoObject`, `DynamicObject`, COM, pythonnet, SignalR clients, JSON bags deserialized without a type. From F#, without this library, you write a C# helper, sprinkle `:?` casts, or wrap every access in `try/with` on `RuntimeBinderException`. +F# has no `dynamic` keyword. C# does. A lot of .NET still hands you objects whose shape is decided at runtime: `ExpandoObject`, `DynamicObject`, COM, pythonnet, SignalR clients, JSON bags deserialized without a type. From F#, without this library, you write a C# helper, sprinkle `:?` type tests, or wrap every access in `try/with` on `RuntimeBinderException`. This library is the F# spelling of that call: `?`, `?<-`, `!?`, and `Dyn.*`, including `tryGet` / `exists` which C# `dynamic` does not have. @@ -27,8 +27,13 @@ let userId: string = payload?userId `?` throws `RuntimeBinderException` when the binder cannot find the member. That is the wrong primitive for optional JSON. ```fsharp -let role: string option = payload |> Dyn.tryGet "role" -let userId: string option = payload |> Dyn.tryGet "userId" +open System.Dynamic +open FSharp.Interop.Dynamic + +let payload = ExpandoObject() +payload?userId <- "u-17" +let role: string option = payload |> Dyn.tryGet "role" // None +let userId: string option = payload |> Dyn.tryGet "userId" // Some "u-17" ``` `tryGet` is `None` on a miss and `Some` on a hit, including a present null. A present value that cannot convert to `'T` still throws. See [tryGet and exists](tryget.md). @@ -69,6 +74,8 @@ pythonnet wraps Python objects as DLR objects. Excel/COM does too. The F# you wr ```fsharp // Sketch only — pythonnet is not referenced here. +// open FSharp.Interop.Dynamic +// open FSharp.Interop.Dynamic.Operators // np?cos(np?pi ?*? 2) // np?array([| 6.; 5.; 4. |], Dyn.namedArg "dtype" np?int32) ``` diff --git a/docfx/template/public/main.css b/docfx/template/public/main.css index adda0de..087037e 100644 --- a/docfx/template/public/main.css +++ b/docfx/template/public/main.css @@ -38,6 +38,8 @@ --bs-link-color: var(--fid-cyan-700); --bs-link-hover-color: var(--fid-cyan-800); + --bs-link-color-rgb: 26, 122, 163; + --bs-link-hover-color-rgb: 20, 94, 126; --bs-primary: var(--fid-cyan-700); --bs-primary-rgb: 26, 122, 163; @@ -61,6 +63,8 @@ --bs-link-color: #6ec4e4; --bs-link-hover-color: var(--fid-cyan-300); + --bs-link-color-rgb: 110, 196, 228; + --bs-link-hover-color-rgb: 126, 200, 232; --bs-primary: #6ec4e4; --bs-primary-rgb: 110, 196, 228; @@ -216,6 +220,11 @@ article table thead th { border-top: 0; } +.NOTE, +.TIP, +.WARNING, +.CAUTION, +.IMPORTANT, .alert { border: 1px solid var(--bs-border-color); border-left: 3px solid var(--bs-primary); @@ -224,15 +233,23 @@ article table thead th { color: var(--bs-body-color); } +.WARNING, +.CAUTION, .alert.WARNING, .alert.CAUTION { border-left-color: #d94f2b; } +.IMPORTANT, .alert.IMPORTANT { border-left-color: var(--fid-cyan-500); } +.NOTE > p:last-child, +.TIP > p:last-child, +.WARNING > p:last-child, +.CAUTION > p:last-child, +.IMPORTANT > p:last-child, .alert > p:last-child { margin-bottom: 0; }