Skip to content

Commit 5cf4b3c

Browse files
committed
chore: publish to npm
1 parent c63b0b7 commit 5cf4b3c

7 files changed

Lines changed: 189 additions & 172 deletions

File tree

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,13 @@ vendor/
2424

2525
# Intellij
2626
.idea/
27+
28+
# Node.js
29+
node_modules/
30+
scripts/*.cjs
31+
32+
# Goreleaser
33+
dist/
34+
35+
# Local binary
36+
pg-schema-diff

.goreleaser.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# This is an example .goreleaser.yml file with some sensible defaults.
2+
# Make sure to check the documentation at https://goreleaser.com
3+
4+
# The lines below are called `modelines`. See `:help modeline`
5+
# Feel free to remove those if you don't want/need to use them.
6+
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
7+
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
8+
9+
version: 2
10+
11+
before:
12+
hooks:
13+
- go mod tidy
14+
15+
builds:
16+
- main: ./cmd/pg-schema-diff
17+
binary: pg-schema-diff
18+
env:
19+
- CGO_ENABLED=0
20+
goos:
21+
- linux
22+
- windows
23+
- darwin
24+
25+
archives:
26+
- format: zip
27+
# this name template makes the OS and Arch compatible with the results of `uname`.
28+
name_template: >-
29+
{{ .ProjectName }}-
30+
{{- title .Os }}-
31+
{{- if eq .Arch "amd64" }}x86_64
32+
{{- else if eq .Arch "386" }}i386
33+
{{- else }}{{ .Arch }}{{ end }}
34+
{{- if .Arm }}v{{ .Arm }}{{ end }}
35+
36+
release:
37+
draft: true
38+
replace_existing_draft: true
39+
replace_existing_artifacts: true
40+
github:
41+
owner: pg-nano
42+
name: pg-schema-diff
43+
44+
changelog:
45+
sort: asc
46+
filters:
47+
exclude:
48+
- "^docs:"
49+
- "^test:"

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
.PHONY: code_gen format go_lint go_lint_fix go_mod_tidy lint sqlc sql_lint sql_lint_fix vendor
1+
.PHONY: build release code_gen format go_lint go_lint_fix go_mod_tidy lint sqlc sql_lint sql_lint_fix vendor
2+
3+
build:
4+
go build -o pg-schema-diff ./cmd/pg-schema-diff
5+
6+
release:
7+
goreleaser release --clean
28

39
code_gen: go_mod_tidy sqlc
410

README.md

Lines changed: 17 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,187 +1,33 @@
1-
# pg-schema-diff
2-
[![run_tests](https://github.com/stripe/pg-schema-diff/actions/workflows/run-tests.yaml/badge.svg)](https://github.com/stripe/pg-schema-diff/actions/workflows/run-tests.yaml)
3-
[![Go Report Card](https://goreportcard.com/badge/github.com/stripe/pg-schema-diff)](https://goreportcard.com/report/github.com/stripe/pg-schema-diff)
4-
[![Go Reference](https://pkg.go.dev/badge/github.com/stripe/pg-schema-diff.svg)](https://pkg.go.dev/github.com/stripe/pg-schema-diff)
5-
![GitHub Release](https://img.shields.io/github/v/release/stripe/pg-schema-diff?include_prereleases)
1+
# @pg-nano/pg-schema-diff
62

7-
Computes the diff(erences) between Postgres database schemas and generates the SQL required to get your database schema from point A to B with
8-
minimal downtime & locks. This enables you to take your database and migrate it to any desired schema defined in plain DDL.
3+
A cross-platform distribution of the `pg-schema-diff` binary, tailored for use with [pg-nano](https://github.com/pg-nano/pg-nano).
94

10-
The tooling attempts to use native postgres migration operations to perform online migrations and avoid locking wherever possible. Not all migrations will
11-
be lock-free and some might require downtime, but the hazards system will warn you ahead of time when that's the case.
12-
Stateful online migration techniques, like shadow tables, aren't yet supported.
5+
## Installation
136

14-
### Online index Replacement
15-
Your project's diff:
16-
```
17-
$ git diff
18-
diff --git a/schema/schema.sql b/schema/schema.sql
19-
index cc3a14b..cf4b32d 100644
20-
--- a/schema/schema.sql
21-
+++ b/schema/schema.sql
22-
@@ -2,5 +2,5 @@ CREATE TABLE foobar(
23-
created_at timestamp,
24-
message text
25-
);
26-
-CREATE INDEX message_idx ON foobar(message);
27-
+CREATE INDEX message_idx ON foobar(message, created_at);
28-
```
29-
The generated plan (*queries using `message_idx` will always have an index backing them, even while the new index is being built*):
30-
```
31-
$ pg-schema-diff plan --dsn "postgres://postgres:postgres@localhost:5432/postgres" --schema-dir ./schema
32-
################################ Generated plan ################################
33-
1. ALTER INDEX "message_idx" RENAME TO "pgschemadiff_tmpidx_message_idx_IiaKzkvPQtyA7ob9piVqiQ";
34-
-- Statement Timeout: 3s
35-
36-
2. CREATE INDEX CONCURRENTLY message_idx ON public.foobar USING btree (message, created_at);
37-
-- Statement Timeout: 20m0s
38-
-- Lock Timeout: 3s
39-
-- Hazard INDEX_BUILD: This might affect database performance. Concurrent index builds require a non-trivial amount of CPU, potentially affecting database performance. They also can take a while but do not lock out writes.
40-
41-
3. DROP INDEX CONCURRENTLY "pgschemadiff_tmpidx_message_idx_IiaKzkvPQtyA7ob9piVqiQ";
42-
-- Statement Timeout: 20m0s
43-
-- Lock Timeout: 3s
44-
-- Hazard INDEX_DROPPED: Dropping this index means queries that use this index might perform worse because they will no longer will be able to leverage it.
45-
```
46-
### Online `NOT NULL` constraint creation
47-
Your project's diff:
48-
```
49-
diff --git a/schema/schema.sql b/schema/schema.sql
50-
index cc3a14b..5a1cec2 100644
51-
--- a/schema/schema.sql
52-
+++ b/schema/schema.sql
53-
@@ -1,5 +1,5 @@
54-
CREATE TABLE foobar(
55-
- created_at timestamp,
56-
+ created_at timestamp NOT NULL,
57-
message text
58-
);
59-
CREATE INDEX message_idx ON foobar(message);
60-
```
61-
The generated plan (*leverages check constraints to eliminate the need for a long-lived access-exclusive lock on the table*):
62-
```
63-
$ pg-schema-diff plan --dsn "postgres://postgres:postgres@localhost:5432/postgres" --schema-dir ./schema
64-
################################ Generated plan ################################
65-
1. ALTER TABLE "public"."foobar" ADD CONSTRAINT "pgschemadiff_tmpnn_BCOxMXqAQwaXlKPCRXoMMg" CHECK("created_at" IS NOT NULL) NOT VALID;
66-
-- Statement Timeout: 3s
67-
68-
2. ALTER TABLE "public"."foobar" VALIDATE CONSTRAINT "pgschemadiff_tmpnn_BCOxMXqAQwaXlKPCRXoMMg";
69-
-- Statement Timeout: 3s
70-
71-
3. ALTER TABLE "public"."foobar" ALTER COLUMN "created_at" SET NOT NULL;
72-
-- Statement Timeout: 3s
73-
74-
4. ALTER TABLE "public"."foobar" DROP CONSTRAINT "pgschemadiff_tmpnn_BCOxMXqAQwaXlKPCRXoMMg";
75-
-- Statement Timeout: 3s
76-
```
77-
78-
# Key features
79-
* Declarative schema migrations
80-
* The use of postgres native operations for zero-downtime migrations wherever possible:
81-
* Concurrent index builds
82-
* Online index replacement: If some index is changed, the new version will be built before the old version is dropped, preventing a window where no index is backing queries
83-
* Online constraint builds: Constraints (check, foreign key) are added as `INVALID` before being validated, eliminating the need
84-
for a long access-exclusive lock on the table
85-
* Online `NOT NULL` constraint creation using check constraints to eliminate the need for an access-exclusive lock on the table
86-
* Prioritized index builds: Building new indexes is always prioritized over deleting old indexes
87-
* A comprehensive set of features to ensure the safety of planned migrations:
88-
* Operators warned of dangerous operations.
89-
* Migration plans are validated first against a temporary database exactly as they would be performed against the real database.
90-
* Strong support of partitions
91-
# Install
92-
## CLI
937
```bash
94-
go install github.com/stripe/pg-schema-diff/cmd/pg-schema-diff
8+
pnpm add @pg-nano/pg-schema-diff
959
```
9610

97-
## Library
98-
```bash
99-
go get -u github.com/stripe/pg-schema-diff
100-
```
101-
# Using CLI
102-
## 1. Apply schema to fresh database
103-
Create a directory to hold your schema files. Then, generate sql files and place them into a schema dir.
104-
```bash
105-
mkdir schema
106-
echo "CREATE TABLE foobar (id int);" > schema/foobar.sql
107-
echo "CREATE TABLE bar (id varchar(255), message TEXT NOT NULL);" > schema/bar.sql
108-
```
11+
## Usage
10912

110-
Apply the schema to a fresh database. [The connection string spec can be found here](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING).
111-
Setting the `PGPASSWORD` env var will override any password set in the connection string and is recommended.
11213
```bash
113-
pg-schema-diff apply --dsn "postgres://postgres:postgres@localhost:5432/postgres" --schema-dir schema
14+
pnpm pg-schema-diff --help
11415
```
11516

116-
## 2. Updating schema
117-
Update the SQL file(s)
118-
```bash
119-
echo "CREATE INDEX message_idx ON bar(message)" >> schema/bar.sql
120-
```
121-
122-
Apply the schema. Any hazards in the generated plan must be approved
123-
```bash
124-
pg-schema-diff apply --dsn "postgres://postgres:postgres@localhost:5432/postgres" --schema-dir schema --allow-hazards INDEX_BUILD
125-
```
126-
127-
# Using Library
128-
Docs to use the library can be found [here](https://pkg.go.dev/github.com/stripe/pg-schema-diff). Check out [the CLI](https://github.com/stripe/pg-schema-diff/tree/main/cmd/pg-schema-diff)
129-
for an example implementation with the library
130-
131-
## 1. Generating plan
132-
```go
133-
// The tempDbFactory is used in plan generation to extract the new schema and validate the plan
134-
tempDbFactory, err := tempdb.NewOnInstanceFactory(ctx, func(ctx context.Context, dbName string) (*sql.DB, error) {
135-
copiedConfig := connConfig.Copy()
136-
copiedConfig.Database = dbName
137-
return openDbWithPgxConfig(copiedConfig)
138-
})
139-
if err != nil {
140-
panic("Generating the TempDbFactory failed")
141-
}
142-
defer tempDbFactory.Close()
143-
// Generate the migration plan
144-
plan, err := diff.Generate(ctx, connPool, diff.DDLSchemaSource(ddl),
145-
diff.WithTempDbFactory(tempDbFactory),
146-
diff.WithDataPackNewTables(),
147-
)
148-
if err != nil {
149-
panic("Generating the plan failed")
150-
}
151-
```
17+
You also have the option of spawning a child process to run the command:
15218

153-
## 2. Applying plan
154-
We leave plan application up to the user. For example, you might want to take out a session-level advisory lock if you are
155-
concerned about concurrent migrations on your database. You might also want a second user to approve the plan
156-
before applying it.
19+
```js
20+
import spawn from "tinyspawn";
15721

158-
Example apply:
159-
```go
160-
for _, stmt := range plan.Statements {
161-
if _, err := conn.ExecContext(ctx, fmt.Sprintf("SET SESSION statement_timeout = %d", stmt.Timeout.Milliseconds())); err != nil {
162-
panic(fmt.Sprintf("setting statement timeout: %s", err))
163-
}
164-
if _, err := conn.ExecContext(ctx, fmt.Sprintf("SET SESSION lock_timeout = %d", stmt.LockTimeout.Milliseconds())); err != nil {
165-
panic(fmt.Sprintf("setting lock timeout: %s", err))
166-
}
167-
if _, err := conn.ExecContext(ctx, stmt.ToSQL()); err != nil {
168-
panic(fmt.Sprintf("executing migration statement. the database maybe be in a dirty state: %s: %s", stmt, err))
169-
}
170-
}
22+
await spawn("./node_modules/.bin/pg-schema-diff --help", {
23+
stdio: "inherit",
24+
});
17125
```
17226

173-
# Supported Postgres versions
174-
Supported: 14, 15, 16
175-
Unsupported: <= 13 are not supported. Use at your own risk.
27+
## Thanks
17628

177-
# Unsupported migrations
178-
An abridged list of unsupported migrations:
179-
- Views (Planned)
180-
- Privileges (Planned)
181-
- Types (Only enums are currently supported)
182-
- Renaming. The diffing library relies on names to identify the old and new versions of a table, index, etc. If you rename
183-
an object, it will be treated as a drop and an add
29+
This package wouldn't exist without these tools:
18430

185-
# Contributing
186-
This project is in its early stages. We appreciate all the feature/bug requests we receive, but we have limited cycles
187-
to review direct code contributions at this time. See [Contributing](CONTRIBUTING.md) to learn more.
31+
- [stripe/pg-schema-diff](https://github.com/stripe/pg-schema-diff)
32+
- [goreleaser](https://github.com/goreleaser/goreleaser)
33+
- [prantlf/grab-github-release](https://github.com/prantlf/grab-github-release)

package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@pg-nano/pg-schema-diff",
3+
"version": "0.7.0",
4+
"type": "module",
5+
"bin": {
6+
"pg-schema-diff": "./pg-schema-diff"
7+
},
8+
"scripts": {
9+
"prepare": "esbuild scripts/postinstall.ts --bundle --format=cjs --platform=node --target=node16 --outfile=scripts/postinstall.cjs",
10+
"postinstall": "node -e \"fs.existsSync('.git') || import('./scripts/postinstall.cjs')\""
11+
},
12+
"files": ["scripts/postinstall.cjs"],
13+
"devDependencies": {
14+
"grab-github-release": "^2.1.0"
15+
},
16+
"pnpm": {
17+
"patchedDependencies": {
18+
"grab-github-release": "patches/grab-github-release.patch"
19+
}
20+
}
21+
}

patches/grab-github-release.patch

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
diff --git a/dist/index.cjs b/dist/index.cjs
2+
deleted file mode 100644
3+
index a23197fd5220995405fb3b160ac39ac63a26b47e..0000000000000000000000000000000000000000
4+
diff --git a/dist/index.cjs.map b/dist/index.cjs.map
5+
deleted file mode 100644
6+
index 6d11408a37fb7ed8204c75651204202b7b193d27..0000000000000000000000000000000000000000
7+
diff --git a/dist/index.mjs b/dist/index.mjs
8+
index 1965810d04b1f1513d513a047ab60fef0d6f86e8..696c883f1b9f9d8e6041581e87577f3b6d4985a9 100644
9+
--- a/dist/index.mjs
10+
+++ b/dist/index.mjs
11+
@@ -278,7 +278,7 @@ async function useCacheOrDownload(repo, version, url, archive, targetDirectory,
12+
}
13+
return { archivePath: cachePath, fromCache: true }
14+
}
15+
-function unpack(archive, targetDirectory) {
16+
+function unpack(archive, name, targetDirectory) {
17+
log('unpack "%s"', archive);
18+
return new Promise((resolve, reject) =>
19+
open(archive, { lazyEntries: true }, (err, zip) => {
20+
@@ -286,7 +286,13 @@ function unpack(archive, targetDirectory) {
21+
zip
22+
.on('entry', entry => {
23+
const { fileName } = entry;
24+
- if (fileName.endsWith('/')) return new Error('directory in archive')
25+
+ if (!fileName.startsWith(name)) {
26+
+ zip.readEntry();
27+
+ return;
28+
+ }
29+
+ if (fileName.endsWith('/')) {
30+
+ return reject(new Error('directory in archive'));
31+
+ }
32+
const filePath = targetDirectory ? join(targetDirectory, fileName) : fileName;
33+
log('write "%s"', filePath);
34+
zip.openReadStream(entry, (err, stream) => {
35+
@@ -324,7 +330,7 @@ async function grab({ name, repository, version, platformSuffixes, archSuffixes,
36+
}
37+
const { archivePath, fromCache } = await useCacheOrDownload(repository, version, url, archive, targetDirectory, unpackExecutable, cache, token);
38+
if (unpackExecutable) {
39+
- const executable = await unpack(archivePath, targetDirectory);
40+
+ const executable = await unpack(archivePath, name, targetDirectory);
41+
await makeExecutable(executable);
42+
if (!fromCache) {
43+
log('remove "%s"', archivePath);
44+
diff --git a/package.json b/package.json
45+
index 3e44d37a4f88ef98a000876d85f0b36d32b3db05..adcfd3eb061f1b6cfc1e2f9986f752473e7b8e38 100644
46+
--- a/package.json
47+
+++ b/package.json
48+
@@ -27,10 +27,9 @@
49+
},
50+
"type": "module",
51+
"module": "dist/index.mjs",
52+
- "main": "dist/index.cjs",
53+
"types": "dist/index.d.ts",
54+
"exports": {
55+
- "require": "./dist/index.cjs",
56+
+ "types": "./dist/index.d.ts",
57+
"import": "./dist/index.mjs"
58+
},
59+
"bin": {

scripts/postinstall.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { grab } from 'grab-github-release'
2+
import fs from 'node:fs'
3+
4+
async function main() {
5+
const { version } = JSON.parse(fs.readFileSync('package.json', 'utf8'))
6+
7+
await grab({
8+
name: 'pg-schema-diff',
9+
repository: 'pg-nano/pg-schema-diff',
10+
version,
11+
platformSuffixes: {
12+
darwin: ['Darwin'],
13+
linux: ['Linux'],
14+
win32: ['Windows'],
15+
},
16+
archSuffixes: {
17+
arm64: [],
18+
ia32: ['i386'],
19+
x64: ['x86_64'],
20+
},
21+
unpackExecutable: true,
22+
verbose: true,
23+
})
24+
}
25+
26+
main()

0 commit comments

Comments
 (0)