Reusable GitHub Actions workflows for the openCoreEMR organization, hosted in a public repo so that public caller repos can use them. (GitHub blocks public repos from calling reusable workflows in private/internal repos.)
Consumers reference workflows here as:
uses: opencoreemr/github-workflows-public/.github/workflows/<name>.yml@<ref>When invoking these reusable workflows, the caller must declare the permissions the workflow needs. Org policy on openCoreEMR requires explicit caller-side declarations even when the reusable workflow declares its own internally. Omit them and the job never starts (startup_failure with empty logs).
| Workflow | Required caller permissions |
|---|---|
actionlint.yml |
contents: read |
conventional-pr-title.yml |
pull-requests: read |
dclint.yml |
contents: read |
hadolint.yml |
contents: read |
php-composer-script.yml |
contents: read |
php-tests.yml |
contents: read |
release-please-reusable.yml |
contents: write, pull-requests: write |
Example:
jobs:
conventional-pr-title:
uses: openCoreEMR/github-workflows-public/.github/workflows/conventional-pr-title.yml@<tag>
permissions:
pull-requests: readEach reusable's header comment shows the full recommended caller stanza, including the required permissions: block.
Releases are managed by release-please using the openCoreEMR fork (release-please-action), which creates annotated tags by default. Conventional commits to main automatically open a release PR; merging that PR creates an annotated tag and GitHub release.
Pin to a specific version tag (e.g. @1.0.0). Pinning to @main works but may break without warning.
GitHub evaluates on: push.paths and on: pull_request.paths before the reusable is invoked, so the reusable only sees workflow_call and cannot influence whether the workflow runs at all. Always declare an appropriately scoped paths: filter in the caller — running every PHP linter on every CSS-only PR wastes both runner minutes and reviewer attention.
Each reusable workflow file has a header comment with the recommended caller stanza (including paths). Copy from there.
Two rules of thumb:
- Include
composer.jsonin every PHP linter caller. The linters are invoked through composer scripts (composer phpstan,composer phpcs, etc.), so a change tocomposer.jsoncan change what runs. For anything that runscomposer install, also includecomposer.lock. - Don't put a
pathsfilter ontests.yml. Tests depend on too many runtime assets (templates, SQL, config, fixtures) to safely enumerate, and missing a real test failure costs more than running tests on a docs-only PR.
And as a general rule, include the caller's own workflow file in the paths so the job re-runs whenever the caller changes.
Wraps the openCoreEMR release-please-action fork. Callers provide a release-please-config.json and a .release-please-manifest.json. The action creates annotated tags by default; set "annotated-tag": false in the config to opt out and get the lightweight tag the GitHub Releases API creates instead. The reusable workflow does not tag or release anything itself; it only invokes the action. Outputs include releases_created, release_created, tag_name, version, and paths_released so caller jobs can needs: the release-please job and gate on a release being created. A sixth output, release_outputs, carries the action's entire step-outputs object serialised as JSON, including the per-component keys named <path>--<key> (root-package keys are unprefixed) that the outputs above cannot express — in a multi-component manifest setup paths_released says which paths released, but only release_outputs says what tag or version each one got. Parse it with fromJSON and index the key you want:
jobs:
release-please:
uses: openCoreEMR/github-workflows-public/.github/workflows/release-please-reusable.yml@<tag>
permissions:
contents: write
pull-requests: write
secrets: inherit
deploy-web:
needs: release-please
if: ${{ fromJSON(needs.release-please.outputs.release_outputs)['apps/web--release_created'] == 'true' }}
uses: ./.github/workflows/deploy.yml
with:
release_tag: ${{ fromJSON(needs.release-please.outputs.release_outputs)['apps/web--tag_name'] }}release_outputs is always a parseable JSON object, even on a push that released nothing. Beyond the named outputs above it carries, for example, prs_created, pr and prs, and per released path sha, html_url, upload_url, major/minor/patch and body (the full release notes text). That list is illustrative, not exhaustive — the full key set is whatever the pinned action's outputReleases() and outputPRs() emit, so it can change when the action ref is bumped. body makes the blob large, and all of a job's outputs together must fit GitHub's 1 MB per-job output limit (50 MB across a whole workflow run), so prefer the named outputs above when they already answer the question.
Treat body, pr, prs and name as untrusted text: they come from commit messages and PR titles. Pass them to a script through env: — never interpolate them directly into a run: block.
Inputs:
| Name | Type | Default | Description |
|---|---|---|---|
config-file |
string | release-please-config.json |
Path to the release-please config |
manifest-file |
string | .release-please-manifest.json |
Path to the release-please manifest |
runs-on |
string | ubuntu-latest |
Runner label |
checkout-fetch-depth |
number | 0 |
fetch-depth for actions/checkout |
Secrets:
| Name | Required | Description |
|---|---|---|
token |
no | Pre-minted token for checkout and release-please. Falls back to App-minted token, then GITHUB_TOKEN. |
app-client-id |
no | GitHub App Client ID. Overrides the org-level RELEASE_PLEASE_CLIENT_ID variable. Paired with app-private-key. |
app-private-key |
no | GitHub App private key (PEM). Overrides the org-level RELEASE_PLEASE_PRIVATE_KEY secret. Paired with app-client-id. |
Callers in the openCoreEMR org should use secrets: inherit. The reusable workflow reads the org variable RELEASE_PLEASE_CLIENT_ID (auto-inherited) and the org secret RELEASE_PLEASE_PRIVATE_KEY (forwarded by inherit), mints a short-lived App installation token, and uses it for checkout and release-please. PRs opened by the App identity trigger downstream pull_request workflows; PRs opened by the default GITHUB_TOKEN do not (GitHub anti-recursion).
The pinned action ref (openCoreEMR/release-please-action@v5.0.0-oce.4) is hardcoded — GitHub Actions does not allow expressions in uses: references, so it can't be a workflow input.