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
105 changes: 47 additions & 58 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@ on:
push:
branches:
- master
paths:
- '.github/workflows/release.yml'
- 'package.json'
- 'src/**'
workflow_dispatch:

# One release run at a time, in the order the runs began to wait: a run that
# starts while another runs waits for it, and up to 100 wait (queue: max), so a
# newer run never replaces one still waiting. Two pushes whose package.json holds
# the same new version never race npm publish, no version is skipped, and no
# newer run never replaces one still waiting. Each run decides its version from
# what the runs before it published, so no version is published twice and no
# publish is cancelled part way.
concurrency:
group: release
Expand All @@ -22,6 +18,8 @@ concurrency:

jobs:
publish:
# A run started by hand on another branch publishes nothing.
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -33,88 +31,79 @@ jobs:
- name: Check out repository
uses: actions/checkout@v5
with:
# Every commit and tag: the decision finds the commit the newest
# version was published from and compares this one with it.
fetch-depth: 0
# No step after this one fetches or pushes with git, and npm install runs
# dependencies' scripts in this checkout, so the job's write token stays out
# of .git/config. The release step passes its own token.
persist-credentials: false

- name: Use Node.js
uses: actions/setup-node@v5
with:
node-version: 22
registry-url: https://registry.npmjs.org

# A push that changed a file the package ships publishes the newest
# published version with its patch raised by one, or package.json's
# version when that is higher; only the run of master's head publishes
# (RELEASING.md).
- name: Decide the version to publish
id: plan
run: node scripts/release-version.mjs --notes "$RUNNER_TEMP"

- name: Install dependencies
if: steps.plan.outputs.publish == 'true'
run: npm install
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

- name: Run tests
if: steps.plan.outputs.publish == 'true'
run: npx vitest run
continue-on-error: false

- name: Build package
if: steps.plan.outputs.publish == 'true'
run: npm run build

- name: Read package version
id: pkg
shell: bash
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Check if version is already published
id: npm_check
# Into this checkout's package.json alone: nothing is committed to master.
- name: Write the version into package.json
if: steps.plan.outputs.publish == 'true'
env:
VERSION: ${{ steps.pkg.outputs.version }}
run: |
# The exact version, not npm's latest: a run of an older commit, such as a
# re-run, finds its version published and publishes nothing.
PUBLISHED=$(npm view "@framers/sql-storage-adapter@$VERSION" version 2>/dev/null || true)
if [ "$PUBLISHED" = "$VERSION" ]; then
echo "published=true" >> "$GITHUB_OUTPUT"
else
echo "published=false" >> "$GITHUB_OUTPUT"
fi
VERSION: ${{ steps.plan.outputs.version }}
run: npm version "$VERSION" --no-git-tag-version --allow-same-version

# Trusted publishing needs npm 11.5.1 or later.
- name: npm 11.20.0 for trusted publishing
if: steps.npm_check.outputs.published == 'false'
if: steps.plan.outputs.publish == 'true'
run: npm install -g npm@11.20.0 && npm --version

- name: Publish to npm
if: steps.npm_check.outputs.published == 'false'
run: npm publish --access public
if: steps.plan.outputs.publish == 'true'
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Check if GitHub release already exists
id: release_check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if gh release view "v${{ steps.pkg.outputs.version }}" &>/dev/null; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Extract release notes from CHANGELOG
id: changelog
if: steps.release_check.outputs.exists == 'false'
run: |
VERSION=${{ steps.pkg.outputs.version }}
NOTES=$(awk "/^## \[$VERSION\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md)
if [ -z "$NOTES" ]; then
NOTES="Release v$VERSION"
fi
echo "notes<<EOF" >> "$GITHUB_OUTPUT"
echo "$NOTES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"

- name: Create GitHub release
if: steps.release_check.outputs.exists == 'false'
if: steps.plan.outputs.release != ''
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: v${{ steps.pkg.outputs.version }}
tag: v${{ steps.plan.outputs.release }}
# A new tag names the commit this run published, not master's head when the release is made.
commit: ${{ github.sha }}
name: sql-storage-adapter v${{ steps.pkg.outputs.version }}
body: ${{ steps.changelog.outputs.notes }}
commit: ${{ steps.plan.outputs.release_commit }}
name: sql-storage-adapter v${{ steps.plan.outputs.release }}
bodyFile: ${{ runner.temp }}/release.md
allowUpdates: true

# The newest published version when no tag names it: a run that published
# it and failed before its release.
- name: Make the missing release of the newest published version
if: steps.plan.outputs.repair != ''
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: v${{ steps.plan.outputs.repair }}
commit: ${{ steps.plan.outputs.repair_commit }}
name: sql-storage-adapter v${{ steps.plan.outputs.repair }}
bodyFile: ${{ runner.temp }}/repair.md
allowUpdates: true
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## [Unreleased]

## [0.8.3] - 2026-10-25

### Added
- `PostgresExporterOptions.unsetEnvironment` names libpq variables left out of `pg_dump`'s environment. The exporter of a PostgreSQL adapter leaves out `PGSSLNEGOTIATION`.

Expand All @@ -10,6 +12,7 @@
- The exporter of a PostgreSQL adapter has `pg_dump` check the server as the pool's pg does. pg's TLS is Node's, which checks the server's certificate and its host name, and versions through 0.8.2 wrote `sslmode=require` for it, with which libpq checks neither without a root certificate file: an export connected to a server whose certificate the pool refuses. `pg_dump` gets `sslmode=verify-full`, or `verify-ca` where pg checks no host name (`uselibpqcompat=true` with `verify-ca`, or `require` beside a root certificate), against the root certificate file the connection string names, else the file `PGSSLROOTCERT` names, else the system's certificate authorities (`sslrootcert=system`, which libpq reads from PostgreSQL 16 on; with an older libpq the export fails, saying what to do). `uselibpqcompat=true` with `sslmode=verify-ca` and no root certificate is refused before `pg_dump` starts, as pg refuses it.

### Changed
- The release workflow publishes on every push to `master` that changes a file the package ships: the newest published version with its patch raised by one, or `package.json`'s version when that is higher, written into the run's checkout alone (`scripts/release-version.mjs`, RELEASING.md). Through 0.8.2 it published only a `package.json` version npm did not hold, so the fixes merged after 0.8.2 went unpublished.
- `new PostgresExporter(connectionString)` takes a function that gives the connection string, or a promise of it, when an export runs.
- `createDatabase()` on Node treats only a `postgres://` or `postgresql://` URL or connection string as naming a PostgreSQL server, besides a pool, host, database or user. A URL of another scheme (`file:./dev.db`, which `DATABASE_URL` holds in a project that runs SQLite through Prisma, or `sqlite:...`) opens the local default again, as through 0.6.13, and with `strict` fails with an error that says the option names no server. From 0.6.14 through 0.8.2 such a URL chose `['postgres']` with no fallback and failed at start. To open PostgreSQL from a `socket:` URL or a socket path, pass `type: 'postgres'` or use `connectDatabase()`.
- `connectDatabase()` opens PostgreSQL alone, in every runtime, from any connection string pg reads, and refuses a configuration that names no server (no pool, an empty or unset connection string, and no host, database or user, as `connectDatabase(process.env.DATABASE_URL)` gives with the variable unset): it throws an error that names the missing setting before it opens anything. Versions through 0.8.2 opened the SQLite file `db_data/app.sqlite3` under the working directory on Node when given no server, though the function is documented as connecting to a remote database, and in a browser opened IndexedDB whatever they were given. `createSyncManager()` calls it only with a URL, as before.
Expand Down
12 changes: 4 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,10 @@ docs: update README with Capacitor examples

## Release Process

A release is a version bump merged to `master`. The [release workflow](https://github.com/framerslab/sql-storage-adapter/blob/master/.github/workflows/release.yml) runs on every push to `master` that changes `package.json`, a file under `src/` or the workflow itself. It installs, runs the tests and builds; when npm holds no release of `package.json`'s version, it publishes to npm, then creates the GitHub release `v<version>` with the notes of that version's section in `CHANGELOG.md`. The release creates the tag.

1. **Update the version** in `package.json` following [semantic versioning](https://semver.org/):
- **MAJOR**: breaking changes (1.0.0 to 2.0.0)
- **MINOR**: new features, backward compatible (1.0.0 to 1.1.0)
- **PATCH**: bug fixes, backward compatible (1.0.0 to 1.0.1)
2. **Add a section to `CHANGELOG.md`** headed `## [x.y.z]`, with what was added, changed, deprecated, removed and fixed.
3. **Open a pull request** with both changes and merge it to `master` when CI is green. The merge publishes the version.
Every push to `master` that changes what the package ships publishes it. The [release workflow](https://github.com/framerslab/sql-storage-adapter/blob/master/.github/workflows/release.yml) decides the version before it installs anything: the newest published version with its patch raised by one, or `package.json`'s version when that is higher. It then installs, runs the tests, builds, publishes to npm and creates the GitHub release `v<version>`, which creates the tag. Nothing is committed to `master`. [RELEASING.md](./RELEASING.md) has the whole decision.

- **A fix** needs no release step: its merge publishes the next patch. Add its notes to `CHANGELOG.md` under `## [Unreleased]`.
- **A new minor or major version**: raise `version` in `package.json` in the pull request, following [semantic versioning](https://semver.org/) (MAJOR for breaking changes, 1.0.0 to 2.0.0; MINOR for new features, backward compatible, 1.0.0 to 1.1.0), and add a section to `CHANGELOG.md` headed `## [x.y.z]` with what was added, changed, deprecated, removed and fixed. The merge publishes that version, with that section as its release notes.

Do not push a version tag by hand: the workflow creates it with the GitHub release.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ TLS follows pg's, and so does the check of the server. pg's TLS is Node's, which

- GitHub Actions workflows:
- `ci.yml` runs on pushes and pull requests to `master`: lint, type check, tests with coverage and the build, then it packs the package, installs the tarball into a new ES module project and type-checks a consumer with `--module nodenext --moduleResolution nodenext`. The job starts a PostgreSQL 16 service, and the specs that need a server read its URL from `POSTGRES_TEST_URL`.
- `release.yml` publishes new versions to npm, tags the commit (`vX.Y.Z`), and creates/updates the GitHub Release when `CHANGELOG.md` and `package.json` bump the version.
- `release.yml` runs on every push to `master`: when a file the package ships changed since the newest published version, it publishes the next patch to npm (or `package.json`'s version when that is higher), tags the commit (`vX.Y.Z`) and creates the GitHub Release.
- Check badge health whenever builds fail:
- CI badge should be green for `master`.
- Codecov badge updates after coverage reports upload.
Expand Down
28 changes: 18 additions & 10 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@

## What starts a release

A push to `master` that changes `package.json`, a file under `src/` or the workflow. A maintainer can also start the workflow by hand.
Every push to `master`. A maintainer can also start the workflow by hand on `master`; started on another branch, it publishes nothing.

## What the workflow does

Its runs go one at a time, in the order they began to wait: a run that starts while another runs waits for it, and up to 100 wait, so a newer run never replaces one still waiting.

1. Installs the dependencies, runs the tests and builds the package.
2. Reads the version in `package.json` and asks npm whether it holds that exact version.
3. When npm does not, publishes the package to npm.
4. Creates the GitHub release `v<version>`, with the notes of that version's `## [<version>]` section in `CHANGELOG.md`, unless a release with that tag exists. The release creates the tag.
1. Decides what to publish, with [`scripts/release-version.mjs`](https://github.com/framerslab/sql-storage-adapter/blob/master/scripts/release-version.mjs), before it installs anything. Only the run of master's head decides: a run of an older commit (a re-run, or a push master moved past while the run waited) publishes nothing, since master's head holds its changes and that commit's own run publishes them. The newest published version is the highest of the versions npm holds and the `v<x.y.z>` tags (a run's tag is read at once, while npm's documents can name the older state for minutes after a publish), and the commit it was published from is its tag's, or npm's `gitHead` for it.
- When `package.json` names a version above the newest published one, that version is published.
- Otherwise, when a file the package ships changed since that commit, the newest published version with its patch raised by one is published. The files it ships and builds from are `src/`, `package.json`, `README.md`, `LICENSE`, `tsconfig.json`, `tsconfig.build.json`, `scripts/fix-esm-imports.mjs` and `scripts/build-cjs.mjs`.
- Otherwise nothing is published: a push that changed only the tests, the docs outside the package, `CHANGELOG.md` or CI publishes nothing.
- When npm already holds the version the run would publish, that version is the newest published one and the decision is made again from its commit, so a run never publishes past a version it has not compared with its own commit.
- A commit whose history does not hold the commit the newest version was published from fails the run, which says to raise `package.json`'s version above that version, or to delete a tag npm holds no version for.
- When no tag names the newest published version (a run published it and failed before its release), the run makes that version's GitHub release and tag at the commit it was published from, beside any version it publishes.
2. Installs the dependencies, runs the tests and builds the package.
3. Writes the version into its checkout's `package.json` and publishes the package to npm with a provenance statement. Nothing is committed to `master`: its `package.json` keeps the version a maintainer last named, while npm and the `v<version>` tags hold the published ones.
4. Creates the GitHub release `v<version>` on the published commit, with the notes of that version's `## [<version>]` section in `CHANGELOG.md`, or, when it has none, the commits since the previous version that changed what the package ships. The release creates the tag.

A push whose version npm already has publishes nothing.
Each run's job summary says what it published, or why it published nothing.

## Releasing a version

1. Raise `version` in `package.json` (semantic versioning: a breaking change raises the major version, a new feature the minor version, a fix the patch version).
2. Add the version's section to `CHANGELOG.md`, headed `## [x.y.z]`.
3. Merge both to `master` through a pull request with green CI.
A merged change to what the package ships needs no step: its push publishes the next patch.

Do not push a version tag by hand.
For a new minor or major version, raise `version` in `package.json` in the pull request (semantic versioning: a breaking change raises the major version, a new feature the minor version) and add the version's section to `CHANGELOG.md`, headed `## [x.y.z]`. Its merge publishes that version.

A pull request's notes for `CHANGELOG.md` go under `## [Unreleased]`, which a maintainer heads with the version that published them.

Do not push a version tag by hand: the decision counts from the newest tag, so a tag above npm's versions moves every later version past it.

## How the publish authenticates

Expand Down
Loading
Loading