diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c60d0c2..84190e3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -134,6 +134,60 @@ jobs:
name: test-report
path: report.html
+ # The same reports, rendered and linkable. An artifact is a zip behind an authenticated
+ # download, so it cannot be linked from the readme; Pages can. Master only, and the site is
+ # public, which is what a public repository's CI output already is.
+ pages:
+ name: publish reports
+ needs: [summary, coverage]
+ if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
+ runs-on: ubuntu-latest
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deploy.outputs.page_url }}
+
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: test-report
+ path: site/tests
+
+ - uses: actions/download-artifact@v4
+ with:
+ name: coverage-site
+ path: site/coverage
+
+ - name: Landing page
+ shell: bash
+ run: |
+ mv site/tests/report.html site/tests/index.html
+ cat > site/index.html <<'HTML'
+
+
+
ImpromptuInterface — CI reports
+
+ ImpromptuInterface — CI reports
+ Generated by the latest master build.
+
+ - Test results — every platform: net8.0, net10.0, net47 and browser-wasm
+ - Coverage — line and branch, for the library
+
+ Back to the repository
+ HTML
+
+ - uses: actions/upload-pages-artifact@v3
+ with:
+ path: site
+
+ - id: deploy
+ uses: actions/deploy-pages@v4
+
# Line and branch coverage of the library, through coverlet's MTP extension - the test exe
# instruments itself, so there is no external collector. Restricted to the library, since
# coverlet will instrument anything it can find sources for.
@@ -169,7 +223,7 @@ jobs:
dotnet tool install --global dotnet-reportgenerator-globaltool
export PATH="$PATH:$HOME/.dotnet/tools"
reportgenerator -reports:'coverage/coverage.cobertura.*.xml' -targetdir:coverage/report \
- -reporttypes:'Cobertura;MarkdownSummaryGithub'
+ -reporttypes:'Cobertura;MarkdownSummaryGithub;Html'
cat coverage/report/SummaryGithub.md >> "$GITHUB_STEP_SUMMARY"
- name: Upload coverage
@@ -178,6 +232,64 @@ jobs:
name: coverage
path: coverage/report/Cobertura.xml
+ - name: Upload the browsable coverage report
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-site
+ path: coverage/report
+
+ # After a green master run, rewrite the README's test and coverage badges from that run's own
+ # numbers, and commit only if one changed. [skip ci] so the badge commit starts no new run.
+ # No external badge service and no GitHub Pages: the shields URLs are static text in the
+ # README, and this edits them.
+ badges:
+ name: badges
+ needs: [build, wasm, coverage]
+ if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/download-artifact@v4
+ with:
+ name: results-ubuntu-latest
+ path: results
+
+ - uses: actions/download-artifact@v4
+ with:
+ name: coverage
+ path: coverage
+
+ - name: Rewrite badges
+ shell: bash
+ run: |
+ set -e
+ tests=$(python3 - <<'PY'
+ import json, glob
+ d = json.load(open(glob.glob('results/**/net10.0.json', recursive=True)[0]))
+ print(sum(1 for a in d['Assemblies'] for f in a['Fixtures'] for t in f['Tests']
+ for r in t['Results'] if r['Kind'] == 'Success'))
+ PY
+ )
+ read line branch < <(python3 - <<'PY'
+ import glob, xml.etree.ElementTree as ET
+ t = ET.parse(glob.glob('coverage/**/Cobertura.xml', recursive=True)[0]).getroot()
+ print(round(float(t.get('line-rate')) * 100), round(float(t.get('branch-rate')) * 100))
+ PY
+ )
+ color() { if [ "$1" -ge 90 ]; then echo brightgreen; elif [ "$1" -ge 75 ]; then echo green; else echo yellow; fi; }
+ sed -i -E "s|tests-[0-9]+%20passed-[a-z]+|tests-${tests}%20passed-brightgreen|" Readme.md
+ sed -i -E "s|line%20coverage-[0-9]+%25-[a-z]+|line%20coverage-${line}%25-$(color $line)|" Readme.md
+ sed -i -E "s|branch%20coverage-[0-9]+%25-[a-z]+|branch%20coverage-${branch}%25-$(color $branch)|" Readme.md
+ git diff --quiet Readme.md && { echo "badges unchanged"; exit 0; }
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git commit -am "Readme: badges from the latest master run (${tests} tests, ${line}% line, ${branch}% branch) [skip ci]"
+ git push
+
publish:
name: publish prerelease
needs: [build, wasm]
diff --git a/Readme.md b/Readme.md
index 3418d2a..e4b1b22 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,14 +1,18 @@
net4.0/netstd2.0 framework to allow you to wrap any object (static or dynamic) with a static interface even though it didn't inherit from it. It does this by emitting cached dynamic binding code inside a proxy.
-ImpromptuInterface is available Nuget [](https://www.nuget.org/packages/ImpromptuInterface/)
+[](https://github.com/ekonbenefits/impromptu-interface/actions/workflows/build.yml?query=branch%3Amaster)
+[](https://github.com/ekonbenefits/impromptu-interface/actions/workflows/build.yml?query=branch%3Amaster)
+[](https://github.com/ekonbenefits/impromptu-interface/actions/workflows/build.yml?query=branch%3Amaster)
+[](https://github.com/ekonbenefits/impromptu-interface/actions/workflows/build.yml?query=branch%3Amaster)
+[](https://www.nuget.org/packages/ImpromptuInterface/)
+
+Available on [NuGet](https://www.nuget.org/packages/ImpromptuInterface/). One workflow covers
+`net40`, `netstandard2.0` and `net10.0` on Windows, macOS and Linux, plus a browser-wasm leg.
+From the latest master build: [test results](https://ekonbenefits.github.io/impromptu-interface/tests/)
+and [coverage](https://ekonbenefits.github.io/impromptu-interface/coverage/). Every run also
+carries its own per-platform table in the run summary.
-Platform | Status
--------- | ------
-NET4 (Win) | [](https://github.com/ekonbenefits/impromptu-interface/actions/workflows/dotnet48.yml?query=branch%3Amaster)
-NETSTD (Win/Mac/Linux) | [](https://github.com/ekonbenefits/impromptu-interface/actions/workflows/dotnet.yml?query=branch%3Amaster)
-
-
Some of the features of `ImpromptuInterface` have been moved into another library called [Dynamitey](https://github.com/ekonbenefits/dynamitey), `ImpromptuInterface` depends on `Dynamitey`.
`ImpromptuInterface.FSharp` has been spun off into [FSharp.Interop.Dynamic](https://github.com/fsprojects/FSharp.Interop.Dynamic) and also depends on `Dynamitey`.