From 32ee175c287ed023867bfb6ee855cf4f918c7467 Mon Sep 17 00:00:00 2001 From: Michael Stonis <120685+michaelstonis@users.noreply.github.com> Date: Mon, 5 Oct 2026 14:51:37 -0500 Subject: [PATCH 1/3] ci(deps): tell the dependency bot how to work within its shell allowlist The 2026-10-05 run reached the model but gave up, reporting a network block. Nothing was blocked at the network: the commands it tried used curl, cd and timeout, which are outside the allowlist, so the whole command line was refused. The instructions now name the allowed commands and give dotnet-only ways to find newer versions, including the MAUI packages that Stellar.slnf leaves out. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/dependency-update.lock.yml | 2 +- .github/workflows/dependency-update.md | 42 ++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dependency-update.lock.yml b/.github/workflows/dependency-update.lock.yml index 2a2f25f..ed9f96f 100644 --- a/.github/workflows/dependency-update.lock.yml +++ b/.github/workflows/dependency-update.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5a185ee6552756f4f3bda31b98def58ce695928921edb46c97e7d93531fcdced","body_hash":"2f89223b78269fd08d35e0803910748976203fb2b088b3d56ce1ebcf053f4aaa","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-5"} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5a185ee6552756f4f3bda31b98def58ce695928921edb46c97e7d93531fcdced","body_hash":"e7648f4ac5e9dcff5fc4505892967a4ead3fd758581c72e553742a2dce70462a","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5.3.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} # ___ _ _ # / _ \ | | (_) diff --git a/.github/workflows/dependency-update.md b/.github/workflows/dependency-update.md index 5515a24..a90adfb 100644 --- a/.github/workflows/dependency-update.md +++ b/.github/workflows/dependency-update.md @@ -52,6 +52,48 @@ Every package version in this repository lives in `Directory.Packages.props`. Central package management is enabled, so **that is the only file whose versions you should edit**. Do not add `Version=` attributes to any `.csproj`. +## The shell you have + +Only allowlisted commands run: `dotnet`, `git status` and `git diff` from this +workflow's `tools.bash` list, the `git` commands needed to branch and commit, and +a few read-only basics (`cat`, `ls`, `grep`, `head`, `tail`, `sort`, `uniq`, `wc`, +`echo`, `printf`). A command line that +contains anything else is refused as a whole with "Permission denied", even when +the rest of it is allowed. That refusal is the allowlist, **not a network block**: +nuget.org is reachable through `dotnet`. + +- Do not use `curl`, `wget`, `cd`, `timeout`, `xargs`, `sed`, `awk` or scripts. + The working directory is already the repository root, so `cd` is never needed. +- Run each `dotnet` command on its own line, exactly as written in this file. +- If a command is refused, drop the part that is not on the list and run it again + before concluding anything. Do not report the task as incomplete because of a + refused `curl`. + +## How to find newer versions + +``` +dotnet restore Stellar.slnf +dotnet list Stellar.slnf package --outdated +dotnet list Stellar.slnf package --outdated --highest-minor +``` + +The first listing shows the newest stable version of each package; the second shows +the newest within the current major, which is the one to use for any package a rule +below holds on its current major line. + +`Stellar.slnf` leaves out the MAUI projects, so those packages do not appear in +either listing. Look each of them up by id instead (`Microsoft.Maui.Controls`, +`Microsoft.AspNetCore.Components.WebView.Maui`, `ReactiveUI.Maui.Reactive`, +`CommunityToolkit.Maui`, `CommunityToolkit.Maui.Markup`, `CommunityToolkit.Mvvm`, +`Mopups`): + +``` +dotnet package search Microsoft.Maui.Controls --exact-match --source https://api.nuget.org/v3/index.json +``` + +That prints every published version; take the highest one without a prerelease +suffix. + ## Rules about what may be updated These are not suggestions. Each one exists because taking the newest version From d4a452001c7729e0be10e1a24af2cf9fcad6be0f Mon Sep 17 00:00:00 2001 From: Michael Stonis <120685+michaelstonis@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:24:20 -0500 Subject: [PATCH 2/3] ci(deps): stop the bot skipping sample builds and pin down the ReactiveUI hold The bot was told to apply skip-samples to batches with a major bump, while ci.yml says never to skip the samples for a major. The bot cannot build MAUI itself, so it now never applies the label. The ReactiveUI rule named ReactiveUI.SourceGenerators, which is on 3.x, as part of the 24.x line. The hold is also recorded beside the versions in Directory.Packages.props. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/ci.yml | 6 ++++-- .github/workflows/dependency-update.lock.yml | 2 +- .github/workflows/dependency-update.md | 16 +++++++++------- Directory.Packages.props | 11 ++++++++++- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f28d72e..f778647 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,8 +125,10 @@ jobs: # # They are not merely decorative. The samples, and only the samples, caught # the CommunityToolkit.Maui / Microsoft.Maui.Essentials version conflict and - # the Android minSdk 21 break during the .NET 10 upgrade. Skip them for a - # patch bump; do not skip them for anything that moves a major. + # the Android minSdk 21 break during the .NET 10 upgrade. Skip them by hand + # for a patch bump you have reason to trust; do not skip them for anything + # that moves a major. The dependency bot never applies the label, because + # these jobs are the only MAUI app coverage its pull requests get. samples: name: Sample apps (${{ matrix.project }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-samples') }} diff --git a/.github/workflows/dependency-update.lock.yml b/.github/workflows/dependency-update.lock.yml index ed9f96f..bdee82e 100644 --- a/.github/workflows/dependency-update.lock.yml +++ b/.github/workflows/dependency-update.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5a185ee6552756f4f3bda31b98def58ce695928921edb46c97e7d93531fcdced","body_hash":"e7648f4ac5e9dcff5fc4505892967a4ead3fd758581c72e553742a2dce70462a","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-5"} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"5a185ee6552756f4f3bda31b98def58ce695928921edb46c97e7d93531fcdced","body_hash":"1e72d01a2b6a09b3a647c261f3d68e9de393c9aad7250361d622d917feff01c9","compiler_version":"v0.77.5","strict":true,"agent_id":"copilot","agent_model":"claude-sonnet-5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5.3.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"3ea13c02d765410340d533515cb31a7eef2baaf0","version":"v0.77.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.58"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.58"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.22"},{"image":"ghcr.io/github/github-mcp-server:v1.1.0"},{"image":"node:lts-alpine","digest":"sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14","pinned_image":"node:lts-alpine@sha256:2bdb65ed1dab192432bc31c95f94155ca5ad7fc1392fb7eb7526ab682fa5bf14"}]} # ___ _ _ # / _ \ | | (_) diff --git a/.github/workflows/dependency-update.md b/.github/workflows/dependency-update.md index a90adfb..0a51e5e 100644 --- a/.github/workflows/dependency-update.md +++ b/.github/workflows/dependency-update.md @@ -109,13 +109,15 @@ they would just get no generated code. For analyzers and generators the referenc is a compatibility floor, not a version to maximise. Leave it alone. **Keep ReactiveUI on the 24.x line.** This covers every `ReactiveUI.*.Reactive` -package and `ReactiveUI.SourceGenerators`. ReactiveUI 25 moves binding onto a +package. `ReactiveUI.SourceGenerators` is versioned separately and stays on 3.x: +ReactiveUI 25 bundles 4.x and the two move together. ReactiveUI 25 moves binding onto a source generator: `Bind` calls through private view fields, which is how Stellar views are written, get no generated binding and throw at run time, and the `ICreatesCustomizedCommandRebinding` extension point that `Stellar.Maui/MauiCommandRebinding.cs` implements no longer exists. Minor and patch -updates within 24.x are fine. Do not attempt the move to 25 yourself — note in the -pull request body that it is available and leave the versions unchanged. +updates within 24.x (and within 3.x for the generators) are fine. Do not attempt +the move to 25 or later yourself — note in the pull request body that it is +available and leave the versions unchanged. **Keep the packages a ReactiveUI update depends on in step with it.** `CentralPackageTransitivePinningEnabled` is on, so when a ReactiveUI package @@ -159,10 +161,10 @@ one. Apply the `dependencies` label to every pull request you open. -Also apply `skip-samples` **only when the batch contains at least one major version -bump**. That label makes CI skip the two MAUI sample app builds, which take about -twelve minutes. Batches of patch and minor updates must not carry it, so those get -the full sample coverage. +**Never apply `skip-samples`.** That label makes CI skip the two MAUI sample app +builds. You cannot build the MAUI projects here, so those jobs are the only thing +that tests your update against a MAUI app head, and they are what catches version +conflicts that `Stellar.slnf` cannot see. The label is for a person to add by hand. ## The pull request diff --git a/Directory.Packages.props b/Directory.Packages.props index 6e1d690..6f33ee5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -26,7 +26,16 @@ new Primitives engine as the default packages, but keeps System.Reactive 7 types (Unit, IScheduler, Subject) at the API boundary, which Stellar's public API is built on. System.Reactive is referenced directly because the - default ReactiveUI packages no longer pull it in transitively. --> + default ReactiveUI packages no longer pull it in transitively. + + Held on 24.x (and ReactiveUI.SourceGenerators on 3.x) on purpose. ReactiveUI + 25 moves binding onto a source generator: Bind calls through private view + fields, which is how Stellar views are written, get no generated binding and + throw at run time, and the ICreatesCustomizedCommandRebinding extension point + behind Stellar.Maui's MauiCommandRebinding is gone. Moving is a breaking + release, not a version bump. ReactiveUI.Maui.Reactive also sets the floor for + Microsoft.Maui.Controls and Microsoft.Extensions.*; with transitive pinning + on, raise those with it or restore fails with NU1109. --> From 8ca3a91e9985b4657ec3ac8ac884e4962a5c01f1 Mon Sep 17 00:00:00 2001 From: Michael Stonis <120685+michaelstonis@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:24:20 -0500 Subject: [PATCH 3/3] ci(release): pack every package before publishing any A release that failed part-way left a version on nuget.org that could not be completed, as v2.1.3 did. Packing is now a matrix job that uploads its package, and a single publish job runs only when all of them succeeded. The push uses --skip-duplicate so a failed publish can be re-run. Packing also moves to the xcode-27 image with Xcode 27.0, matching ci.yml. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/nuget.yml | 51 ++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index f14ce38..8f4760b 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -1,5 +1,9 @@ -# This workflow will build a .NET project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net +# Packs every package on a version tag and publishes them to nuget.org. +# +# Packing and publishing are separate jobs on purpose. nuget.org versions cannot +# be replaced, so a release that publishes some packages and then fails leaves a +# version that can never be completed: v2.1.3 shipped four of eleven packages +# that way. Nothing is pushed until every package has built and packed. name: .NET @@ -8,10 +12,18 @@ on: tags: - 'v**' +permissions: + contents: read + jobs: - build: - runs-on: macos-26 + pack: + name: Pack ${{ matrix.name }} + # Same image and Xcode as the MAUI jobs in ci.yml, so a release is packed on + # the toolchain pull requests were validated on. Move the two together. + runs-on: xcode-27 strategy: + # Let every package finish so one failure reports all of them at once. + fail-fast: false matrix: include: - name: Stellar @@ -57,7 +69,7 @@ jobs: dotnet-version: '10.x' - uses: maxim-lobanov/setup-xcode@v1.7.0 with: - xcode-version: latest-stable + xcode-version: '27.0' - name: Install MAUI Workloads run: dotnet workload install maui --ignore-failed-sources - name: Semver Parse @@ -67,5 +79,30 @@ jobs: run: dotnet build ${{ matrix.project }} - name: Pack ${{ matrix.name }} run: dotnet pack --configuration Release /p:AssemblyVersion=${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }} /p:Version=${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }}.${{ steps.version.outputs.patch }} ${{ matrix.project }} - - name: Publish ${{ matrix.name }} - run: dotnet nuget push ${{ matrix.output }}/*.nupkg -k ${{ secrets.NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json + - name: Upload ${{ matrix.name }} + uses: actions/upload-artifact@v7 + with: + name: nupkg-${{ matrix.name }} + path: ${{ matrix.output }}/*.nupkg + if-no-files-found: error + + publish: + name: Publish to nuget.org + needs: pack + runs-on: ubuntu-latest + steps: + - uses: actions/setup-dotnet@v6 + with: + dotnet-version: '10.x' + - uses: actions/download-artifact@v8 + with: + pattern: nupkg-* + path: packages + merge-multiple: true + - name: List packages + run: ls -l packages + # --skip-duplicate so that re-running this job after a partial push (a + # nuget.org outage, say) finishes the release instead of failing on the + # packages that already went up. + - name: Publish + run: dotnet nuget push "packages/*.nupkg" -k ${{ secrets.NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json --skip-duplicate