Reusable GitHub Actions workflows and composite actions shared across owners. The repo is public so any owner can call them.
Owner-specific steps (deploys, distribution) run as extra caller jobs from the owner's own repos. For example, Midworld apps run midworld-apps/ci's rollout-publish.yml after android-build-app.yml, and midworld-apps/manifests' bump-manifests.yml after dotnet-build-app.yml.
| Workflow | Jobs |
|---|---|
android-build-app.yml |
build (on a version-bump push, only the signed APK as the release-apk artifact; tests ran on the PR) · dependabot |
android-build-library.yml |
build (on a version-bump push, only publish to GitHub Packages Maven and wait until it serves them; tests ran on the PR) → dependents · dependabot |
android-update-dependencies.yml |
update (consumer side of dependents: bump gradle/libs.versions.toml, PR, auto-merge unless breaking) |
dotnet-build-app.yml |
build → publish (GHCR image, opt-in, on a default-branch push; then prunes old image versions) · dependabot |
dotnet-build-library.yml |
build (optional dotnet-validate) → publish (GitHub Packages; when packable files changed; waits until the feed serves them) → dependents (dispatch update-dependencies) · dependabot. With nuget-org: true, publish (nuget-org-publish) and dependents are caller jobs; see the header |
dotnet-release-app.yml |
release (per-runtime archives as GitHub Release assets, on a version tag) |
dotnet-update-dependencies.yml |
update (consumer side of dependents: bump, PR, auto-merge unless major) |
nanoframework-build-app.yml |
build (Windows) · package-lock · packages-updated (PRs) · dependabot |
nanoframework-build-library.yml |
build (Windows, nuspec pack; outputs publish when packable files changed) · package-lock · packages-updated (PRs) · dependabot. The caller adds publish (nuget-org-publish) and dependents (dispatch-dependents.yml) jobs; see the header |
nanoframework-update-dependencies.yml |
nanodu update PR (cron/dispatch) → manage-prs (close superseded, auto-merge unless major) |
npm-build-app.yml |
build (npm ci, format:check, lint, test, build with build env) · dependabot |
npm-build-action.yml |
build (the same, then fails unless the committed dist/ matches the build) · dependabot |
npm-release-action.yml |
on a version tag: verify (tag on the default branch, npm-build with the dist/ check) → publish (GitHub Release) → major (move the major tag) |
move-major-tag.yml |
moves a major tag (e.g. v2) with the andy App token; also a manual rollback |
dispatch-dependents.yml |
nested in the GitHub Packages library workflows, a caller job for nuget.org libraries: find dependents, send update-dependencies (see Dependents) |
dependabot.yml |
merge (non-major Dependabot PRs) · update-behind |
The caller shape, inputs and contract are in each workflow's header.
Callers:
- Keep the calling job id
pipeline. The required check ispipeline / build. - Set
permissions:andconcurrency:in the caller workflow. A called workflow can only lower permissions, and it sees the caller'sgithub.workflow. - Pass secrets by name.
secrets: inheritdoesn't cross orgs. - Reference
@master: there are no release tags, so stage a breaking change by pointing one caller at@<branch>first.
After a publish, the library workflows (or, for a nuget.org library, its dependents caller job) send repository_dispatch: update-dependencies to the repos in dependents and the listeners found in the discover-dependents accounts. A listener is a workflow that takes update-dependencies and calls the platform's updater (dotnet-, android- or nanoframework-update-dependencies.yml), which opens the bump PR.
dotnet-build-library.yml and android-build-library.yml take dispatch:
all(default): every discovered listener. nanoFramework always dispatches to all.opt-in: only the discovered listeners the library PR names inUpdate-Dependents:lines. Explicitdependentsare always sent.
Update-Dependents: rollout-web, midworld-apps/shelver-web
Update-Dependents: videobrowser-web
- A line starts with
Update-Dependents:(case-insensitive); a PR body can have several. - Entries are a bare
name(matched in everydiscover-dependentsaccount) orowner/name, separated by commas or spaces. allanywhere in a line opts in every listener.- Lines inside HTML comments (
<!-- ... -->) are ignored, so a PR template can carry a commented hint.
When it's read:
- Push to the default branch: the current body of the merged PR for the pushed commit. These dependents get the dispatch after the publish.
pull_request: the PR body in the event (with the defaultpull_requesttypes, editing the body alone doesn't start a run; push a commit). This is a preview: the dependents job logs each discovered listener as kept or(not opted in), and never sends.
With no line (or no merged PR, e.g. a direct push), nothing is dispatched and the push skips the dependents job. Dependents pick the version up from their Dependabot schedule (weekly with the repo-standards templates) or a manual PR.
A name that matches no listener in its account logs ::warning::Update-Dependents: <owner/name> is not an update-dependencies listener; an owner/name outside the scanned accounts logs ... is not in a scanned account. Invalid entries are warnings too; none of these fail the build.
android-build-library.yml reads the published coordinates from each build/publications/*/pom-default.xml and, once GitHub Packages serves maven-metadata.xml with the new version, sends packages as group:name. A listener calls android-update-dependencies.yml, which sets the matching [libraries] entries in gradle/libs.versions.toml (their version.ref key, or an inline version) and opens a bot/update-dependencies/<source>/... PR. It auto-merges unless the bump is breaking: a major change, or a minor change of a 0.x version.
dotnet-build: nbgv version, restore, build, then the test projects matchingtest-patterns, with a test report and TRX artifact.dotnet-pack: packs everyIsPackableproject at the given version and checks each package exists.discover-dependents: an account's repos whoseupdate-dependencieslistener calls a given updater workflow, optionally limited to an opt-in list.dependents-opt-in: theUpdate-Dependents:names from the PR body.dependents-plan: groupsdependentsanddiscover-dependentsby account into theownersmatrixdispatch-dependents.ymltakes.update-package-versions: sets package ids to one version in every trackedDirectory.Packages.props.setup-nanoframework,build-nanoframework: nanobuild, MSBuild and NuGet CLI (Windows); locked restore and Release build stamped with nbgv.nanoframework-package-lock,nanoframework-packages-updated: the nanoFramework PR checks.nuget-org-publish: pushes the run'snuget-packagesartifact to nuget.org with Trusted Publishing (NuGet/login), thennuget-wait. Use it from a job in the caller's own workflow file: nuget.org matches the policy against that file.nuget-wait: waits until a feed's flat container (exact restore) or registration index ("latest") lists every package version.maven-wait: waits until a Maven repository'smaven-metadata.xmllists the version for every artifact.update-catalog-versions: sets Maven libraries to one version in a Gradle version catalog.npm-build: Node from.nvmrc,npm ci, thennpm run format:check,lint,test(if present) andbuild; optional build env and committed-output check.gradle-build: JDK (Temurin) and Gradle setup, CIgradle.propertieslines (appended), the wrapper run, and an optional coverage artifact.version-changed: whether any watched file changed in the pushed range.
test.yml runs on PRs and master pushes, and its job ids are required checks:
actionlintshellcheckon the composite scriptsfixture-gradle(gradle-buildagainsttest/fixtures/gradle)fixture-dotnet-{pass,fail,no-tests,library,update}(the .NET composites againsttest/fixtures/dotnet)fixture-discover(discover-dependents --localagainsttest/fixtures/listeners,dependents-plan, anddependents-opt-inparsing)fixture-android-update(update-catalog-versionsagainsttest/fixtures/catalog)fixture-npm-app,fixture-npm-action,fixture-npm-action-stale(npm-buildagainsttest/fixtures/npm)
Inside this repo, workflows reference the composites with $/ (uses: $/.github/actions/gradle-build). That resolves to this repo at the running commit, even when the workflow is called from another owner.
npm callers provide these package.json scripts: format (write), format:check, lint, test (single run; optional), test:coverage, build. CI runs format:check, lint, test and build.
That repo is archived; its workflows live here now.
| Old | New |
|---|---|
nanoframework-build-publish.yml with publish-nuget: true |
nanoframework-build-library.yml |
nanoframework-build-publish.yml without publish-nuget |
nanoframework-build-app.yml |
nanoframework-update-dependencies.yml |
nanoframework-update-dependencies.yml (same inputs) |
.github/actions/setup-nanoframework, build-nanoframework |
ccswe-apps/actions/.github/actions/setup-nanoframework, build-nanoframework |
- The library workflow takes no nuget.org key: the caller's
publishjob uses Trusted Publishing (a nuget.org policy for the caller repo and workflow file). It publishes only whensrc,version.json,Directory.Build.propsornuget.configchange, and addsdiscover-dependentsnext todependents. - Dependents are found by a listener calling
ccswe-apps/actions/.github/workflows/nanoframework-update-dependencies.yml; update the listener when the library switches. - Check names stay
pipeline / build,pipeline / package-lock,pipeline / packages-updated.