Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 72 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,104 @@
name: Release

# Creates the vX.Y.Z tag and GitHub Release whenever package.json's version
# changes on main (i.e. a release PR is merged). Ordinary merges are no-ops
# because the version guard skips versions that already have a release.
#
# Split into two jobs so dependency code never runs where the write token
# exists: `build` installs and runs everything read-only; `release` waits for
# approval of the `release` environment, installs nothing, and publishes the
# prebuilt files.

on:
push:
tags: ["v*.*.*"]
branches: [main]

permissions:
contents: read

concurrency:
group: release
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.v.outputs.tag }}
released: ${{ steps.v.outputs.released }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: 24
cache: npm
- name: Tag matches package.json

- name: Determine version and whether it is already released
id: v
env:
TAG: ${{ github.ref_name }}
run: test "$TAG" = "v$(node -p "require('./package.json').version")"
- run: npm ci --ignore-scripts
- run: npm run lint
- run: npm run bundle
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
GH_TOKEN: ${{ github.token }}
run: |
TAG="v$(node -p "require('./package.json').version")"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "released=true" >> "$GITHUB_OUTPUT"
echo "::notice::$TAG already released, skipping."
else
echo "released=false" >> "$GITHUB_OUTPUT"
echo "::notice::Releasing $TAG."
fi

- name: Install dependencies
if: steps.v.outputs.released == 'false'
run: npm ci --ignore-scripts

- name: Verify and bundle
if: steps.v.outputs.released == 'false'
run: |
npm run lint
npm run bundle

- name: Upload bundle
if: steps.v.outputs.released == 'false'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: spec
path: dist/
retention-days: 1

release:
# Holds the write token; installs and runs nothing from npm.
needs: build
# Only when there is a new version, so ordinary merges don't wait for approval.
if: needs.build.outputs.released == 'false'
# Requires a maintainer's approval before this job (and its write token)
# runs, so a stolen GitHub login can't publish a release on its own.
environment: release
runs-on: ubuntu-latest
permissions:
contents: write
contents: write # create the release tag + GitHub Release
steps:
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- name: Download bundle
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: spec
path: dist
- env:
path: dist/

# Idempotent: creates the tag and Release only if missing. --target
# creates the tag at this commit through the API, so no git credentials
# are needed.
- name: Ensure release tag and GitHub Release exist
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
run: gh release create "$TAG" dist/openapi.json dist/openapi.yaml --repo "$GITHUB_REPOSITORY" --title "$TAG" --generate-notes
TAG: ${{ needs.build.outputs.tag }}
REPO: ${{ github.repository }}
run: |
if ! gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; then
gh release create "$TAG" dist/openapi.json dist/openapi.yaml \
--repo "$REPO" \
--target "$GITHUB_SHA" \
--title "$TAG" \
--notes "See [CHANGELOG.md](https://github.com/${REPO}/blob/main/CHANGELOG.md) for details."
fi
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ OpenAPI 3.0 spec for the Nomba API, generated from the API reference at develope
- The docs are the source of truth. Prefer fixing the docs and running `npm run sync` over hand-editing `openapi/`; a hand edit makes `npm run sync -- --check` fail until the docs catch up.
- Never add entries to `.redocly.lint-ignore.yaml`. Remove them as problems are fixed, and update `ALIGNMENT.md`.

## Releasing
Never create tags or Releases by hand; see `RELEASING.md`.

## Before pushing
`npm run lint && npm run bundle`
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

## 0.1.0 (unreleased)
## [0.1.0] - 2026-11-03

- First release. 100 operations across 94 paths, generated from the developer.nomba.com API reference.
- Adds global payout, global collection, direct debits, betting, terminal payment requests and the v2 bills and transfers endpoints, none of which are in `kudi-inc/vendor-openapi-spec`.
First release. 100 operations across 94 paths, generated from the developer.nomba.com API reference.

### Added

- Global payout and global collection, direct debits, betting, terminal payment requests and the v2 bills and transfers endpoints, none of which are in `kudi-inc/vendor-openapi-spec`.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ CI lints and bundles every pull request and checks for drift against the docs ev

## Releasing

Bump `version` in `package.json`, add a `CHANGELOG.md` entry, merge, then tag `vX.Y.Z` on main. The release workflow checks the tag matches `package.json`, bundles the spec and attaches both files to a GitHub release.
Automated: merge a `chore: release X.Y.Z` PR and approve the `release` deployment. See [RELEASING.md](RELEASING.md).

## License

Expand Down
32 changes: 32 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Releasing

Releasing is automated; you never create tags or Releases by hand.

## Steps

1. Merge your PRs to `main`. Main squash-merges, so each PR title must be a valid Conventional Commit.
2. Open a `chore: release X.Y.Z` PR that bumps the version with `npm version X.Y.Z --no-git-tag-version` and adds a `## [X.Y.Z] - YYYY-MM-DD` section to `CHANGELOG.md` (Breaking, Added, Fixed, Changed, in plain language).
3. Merge it. `release.yml` lints and bundles the spec, then waits. Open the run in **Actions** and approve the **release** deployment. Only then does it create the `vX.Y.Z` tag and GitHub Release with `openapi.json` and `openapi.yaml` attached.

## Choosing the version

| Change since the last release | Bump |
|-------------------------------|------|
| An operation, parameter or schema field removed or renamed, or a type changed | major |
| An operation, parameter or schema field added | minor |
| Descriptions, examples or lint fixes only | patch |
| only `chore:` `docs:` `refactor:` `test:` `ci:` with no spec change | no release |

Before 1.0.0, breaking changes bump the minor version.

## After releasing

Confirm the tag, the Release and both files:

```bash
curl -sIL https://github.com/NombaOSS/openapi/releases/latest/download/openapi.json | grep -E '^HTTP' | tail -1
```

## One-time setup (repo owner)

- GitHub: an environment named `release` with yourself as required reviewer, limited to protected branches. Done for this repo.
Loading