Skip to content
Merged
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
064b3f2
chore: add publish npm action
juleswritescode Feb 21, 2025
a9f36e7
don't have that yet
juleswritescode Feb 21, 2025
3e24df4
copy that schema
juleswritescode Feb 21, 2025
4afd792
add thangs
juleswritescode Feb 21, 2025
125f8fc
ok?
juleswritescode Feb 21, 2025
6baf503
trim names
juleswritescode Feb 21, 2025
6b0aa3d
debug thing
juleswritescode Feb 21, 2025
eaf3097
ok?
juleswritescode Feb 21, 2025
0ce8fa3
changy change
juleswritescode Feb 21, 2025
f63de17
like so?
juleswritescode Feb 21, 2025
d7cf708
make it test
juleswritescode Feb 21, 2025
dbe1924
well
juleswritescode Feb 21, 2025
9e3e092
thats not a valid alias
juleswritescode Feb 21, 2025
711fb48
use pipes
juleswritescode Feb 21, 2025
f4abd92
rework
juleswritescode Feb 21, 2025
38e30cf
ocd
juleswritescode Feb 21, 2025
584c67a
wrong platform name
juleswritescode Feb 21, 2025
ebd6760
debug
juleswritescode Feb 21, 2025
c698b4c
well...
juleswritescode Feb 21, 2025
10d0822
more sexy
juleswritescode Feb 21, 2025
92c9658
thats not an underscore
juleswritescode Feb 21, 2025
348dfaf
schema src
juleswritescode Feb 21, 2025
d436948
escape?
juleswritescode Feb 21, 2025
a1607c5
cleanup
juleswritescode Feb 21, 2025
c3b03b2
herantasten
juleswritescode Feb 21, 2025
bbde5f7
these underscores dang
juleswritescode Feb 21, 2025
29e9dbd
it apparently works
juleswritescode Feb 21, 2025
9250363
restructure
juleswritescode Feb 21, 2025
7a4f004
ls that stuff
juleswritescode Feb 21, 2025
59bd643
cat instead
juleswritescode Feb 21, 2025
7aed7f4
this the script?
juleswritescode Feb 21, 2025
5bf5a70
desc
juleswritescode Feb 21, 2025
9d190c7
prepwork
juleswritescode Feb 21, 2025
82cf431
improve left & right
juleswritescode Feb 21, 2025
ee914d7
test-release name
juleswritescode Feb 21, 2025
72499a5
?
juleswritescode Feb 21, 2025
0661574
ok
juleswritescode Feb 21, 2025
ea97ad8
ack
juleswritescode Feb 21, 2025
78be9bf
another
juleswritescode Feb 21, 2025
d920fc9
verify
juleswritescode Feb 21, 2025
099350c
ok
juleswritescode Feb 21, 2025
612445c
test
juleswritescode Feb 22, 2025
e4adedb
workaround
juleswritescode Feb 22, 2025
2f0522c
no
juleswritescode Feb 22, 2025
36b97eb
try github
juleswritescode Feb 22, 2025
5f74068
camels everywhere
juleswritescode Feb 22, 2025
92d9ae8
await that
juleswritescode Feb 22, 2025
1663eb6
works
juleswritescode Feb 22, 2025
075702b
log
juleswritescode Feb 22, 2025
c639376
axiosga .!
juleswritescode Feb 22, 2025
fe42a6c
no infinite
juleswritescode Feb 22, 2025
9b629ca
sanity
juleswritescode Feb 22, 2025
2c65368
ok
juleswritescode Feb 22, 2025
03f40fd
better check
juleswritescode Feb 22, 2025
654c94b
stringify
juleswritescode Feb 22, 2025
c78a12c
stringify
juleswritescode Feb 22, 2025
69cc656
change for test
juleswritescode Feb 22, 2025
bc9a1f4
pieced together
juleswritescode Feb 22, 2025
00704b4
permissions
juleswritescode Feb 22, 2025
9f1be0d
fix
juleswritescode Feb 22, 2025
328a2b4
update msg
juleswritescode Feb 22, 2025
bd8999f
trim?
juleswritescode Feb 22, 2025
c989536
log package
juleswritescode Feb 22, 2025
8d1457a
path wrong?
juleswritescode Feb 22, 2025
8f1394e
classic
juleswritescode Feb 22, 2025
8c0d480
test name
juleswritescode Feb 22, 2025
d5e93aa
huh
juleswritescode Feb 22, 2025
d6a2039
name
juleswritescode Feb 22, 2025
a465869
print package.json
juleswritescode Feb 22, 2025
81004cc
test 3
juleswritescode Feb 22, 2025
7b3a0d6
ah yes...
juleswritescode Feb 22, 2025
c3462b7
no scope
juleswritescode Feb 22, 2025
b4d410d
test5
juleswritescode Feb 22, 2025
32b7ead
adjust that
juleswritescode Feb 22, 2025
8cb16d3
test 6
juleswritescode Feb 22, 2025
db9c843
test 6
juleswritescode Feb 22, 2025
091c0a9
add bin
juleswritescode Feb 22, 2025
186b347
test 7
juleswritescode Feb 22, 2025
c54ff06
remove placeholders
juleswritescode Feb 22, 2025
90f9603
publishconfig
juleswritescode Feb 22, 2025
cbeb91f
hm
juleswritescode Feb 22, 2025
1e9a314
release 8
juleswritescode Feb 22, 2025
fe4e009
binary file
juleswritescode Feb 22, 2025
119705b
hm
juleswritescode Feb 22, 2025
5c71309
release 9
juleswritescode Feb 22, 2025
4d5857e
nightly test
juleswritescode Feb 22, 2025
deee03c
string
juleswritescode Feb 22, 2025
a587b31
nightly2 test
juleswritescode Feb 22, 2025
e630ea6
sweet lord!
juleswritescode Feb 22, 2025
93377b1
ok
juleswritescode Feb 22, 2025
5301e60
name change
juleswritescode Feb 24, 2025
ee9461a
already caught two
juleswritescode Feb 24, 2025
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
72 changes: 72 additions & 0 deletions .github/workflows/publish.dispatch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Publish NPM (Manual)

on:
workflow_dispatch:
inputs:
release-tag:
type: string
required: true
description: Release Tag to Publish

jobs:
validate_tag:
runs-on: ubuntu-latest
outputs:
is-prerelease: ${{ steps.validate-release.outputs.is-prerelease }}
steps:
- uses: actions/github-script@v7
id: validate-release
with:
script: |
/** the "core" module does not have access to workflow_dispatch inputs */
const tag = '${{ inputs.release-tag }}';

/** Releases don't have a guaranteed order, so we'll have to paginate */
let exhausted = false;
let page = 1;
while (!exhausted) {
const releases = await github.rest.repos.listReleases({
owner: context.repo.owner,
repo: context.repo.repo,
page,
per_page: 100,
}).then(r => r.data);

const matchingRelease = releases.find(r => r.tag_name === tag);
if (matchingRelease) {
core.setOutput('has-release', 'true');
core.setOutput('is-prerelease', matchingRelease.prerelease.toString());
return;
}

if (releases.length < 100) {
exhausted = true;
} else if (page >= 10) {
throw new Error("We iterated over 10 pages. Does the script work?");
} else {
page++
}

}

core.setOutput('has-release', 'false');
core.setOutput('is-prerelease', 'false');

- name: Abort
if: steps.validate-release.outputs.has-release != 'true'
run: |
{
echo "Tag ${{ github.event.inputs.release_tag }} not found."
exit 1
}

publish_npm:
needs: validate_tag
uses: ./.github/workflows/publish.reusable.yml
permissions:
contents: write
id-token: write
with:
release-tag: ${{ github.event.inputs.release-tag }}
is-prerelease: ${{ needs.validate_tag.outputs.is-prerelease }}
secrets: inherit
68 changes: 68 additions & 0 deletions .github/workflows/publish.reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Publish to NPM & Brew

on:
workflow_call:
inputs:
release-tag:
type: string
required: true
is-prerelease:
type: string
required: true
default: "false"

jobs:
publish:
name: Publish All the Things
runs-on: ubuntu-latest
permissions:
contents: write
# ? what's this?! required for executing the node script?
id-token: write
steps:
- uses: actions/checkout@v4

- name: Install Node
uses: actions/setup-node@v4
with:
node-version: lts/*
registry-url: "https://registry.npmjs.org"

- name: Generate Packages
id: generate-packages
run: node packages/@pglt/pglt/scripts/generate-packages.mjs
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ inputs.release-tag }}
PRERELEASE: ${{ inputs.is-prerelease }}

- name: Verify NPM TOKEN exists
run: |
if [ -z "${{ secrets.NPM_TOKEN }}" ]; then
echo "Secret is not defined"
exit 1
else
echo "Secret is defined"
fi

- name: Print package.json
run: |
cat packages/@pglt/pglt/package.json

- name: Publish npm packages as nightly
if: inputs.is-prerelease == 'true'
run: |
for package in packages/@pglt/*; do
npm publish $package --tag nightly --access public --provenance
done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} #

- name: Publish npm packages as latest
if: inputs.is-prerelease != 'true'
run: |
for package in packages/@pglt/*; do
npm publish $package --tag latest --access public --provenance
done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
16 changes: 16 additions & 0 deletions .github/workflows/publish.trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Publish NPM (Automatic)

on:
release:
types: [released, prereleased]

jobs:
publish_npm:
uses: ./.github/workflows/publish.reusable.yml
permissions:
contents: write
id-token: write
with:
release-tag: ${{ github.event.release.tag_name }}
is-prerelease: ${{ github.event.release.prerelease }}
secrets: inherit
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -130,7 +130,7 @@ jobs:
fail_on_unmatched_files: true
draft: true

- name: ✅ Output Link to Worflow Summary
- name: ✅ Output Link to Workflow Summary
run: |
{
echo "# 🚀 Release completed!"
46 changes: 46 additions & 0 deletions packages/@pglt/pglt/bin/pglt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env node
const { platform, arch, env } = process;

/**
* platform and arch are values injected into the node runtime.
* We use the values documented on https://nodejs.org.
*/
const PLATFORMS = {
win32: {
x64: "pglt-x86_64-windows-msvc/pglt.exe",
arm64: "pglt-aarch64-windows-msvc/pglt.exe",
},
darwin: {
x64: "pglt-x86_64-apple-darwin/pglt",
arm64: "pglt-aarch64-apple-darwin/pglt",
},
linux: {
x64: "pglt-x86_64-linux-gnu/pglt",
arm64: "pglt-aarch64-linux-gnu/pglt",
},
};

const binPath = env.PGLT_BINARY || PLATFORMS?.[platform]?.[arch];

if (binPath) {
const result = require("child_process").spawnSync(
require.resolve(binPath),
process.argv.slice(2),
{
shell: false,
stdio: "inherit",
env,
}
);

if (result.error) {
throw result.error;
}

process.exitCode = result.status;
} else {
console.error(
"The pglt CLI package doesn't ship with prebuilt binaries for your platform yet. Please file an issue in the main repository."
);
process.exitCode = 1;
}
43 changes: 43 additions & 0 deletions packages/@pglt/pglt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "pglt",
"version": "<placeholder>",
"bin": {
"pglt": "bin/pglt"
},
"repository": {
"type": "git",
"url": "git+https://github.com/supabase-community/postgres_lsp.git",
"directory": "packages/@pglt/pglt"
},
"author": "Supabase Community",
"contributors": [
{
"name": "Philipp Steinrötter",
"url": "https://github.com/psteinroe"
},
{
"name": "Julian Domke",
"url": "https://github.com/juleswritescode"
}
],
"license": "MIT or Apache-2.0",
"description": "A collection of language tools and a Language Server Protocol (LSP) implementation for Postgres, focusing on developer experience and reliable SQL tooling.",
"files": [
"bin/pglt",
"schema.json"
],
"engines": {
"node": ">=20"
},
"publishConfig": {
"provenance": true
},
"optionalDependencies": {
"pglt-x86_64-windows-msvc": "<placeholder>",
"pglt-aarch64-windows-msvc": "<placeholder>",
"pglt-x86_64-apple-darwin": "<placeholder>",
"pglt-aarch64-apple-darwin": "<placeholder>",
"pglt-x86_64-linux-gnu": "<placeholder>",
"pglt-aarch64-linux-gnu": "<placeholder>"
}
}
241 changes: 241 additions & 0 deletions packages/@pglt/pglt/scripts/generate-packages.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import assert from "node:assert";
import * as fs from "node:fs";
import { pipeline } from "node:stream";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
const streamPipeline = promisify(pipeline);

const CLI_ROOT = resolve(fileURLToPath(import.meta.url), "../..");
const PACKAGES_PGLT_ROOT = resolve(CLI_ROOT, "..");
const PGLT_ROOT = resolve(PACKAGES_PGLT_ROOT, "../..");
const MANIFEST_PATH = resolve(CLI_ROOT, "package.json");
const SUPPORTED_PLATFORMS = [
"pc-windows-msvc",
"apple-darwin",
"unknown-linux-gnu",
];
const SUPPORTED_ARCHITECTURES = ["x86_64", "aarch64"];

async function downloadSchema(releaseTag, githubToken) {
const assetUrl = `https://github.com/supabase-community/postgres_lsp/releases/download/${releaseTag}/schema.json`;

const response = await fetch(assetUrl.trim(), {
headers: {
Authorization: `token ${githubToken}`,
Accept: `application/octet-stream`,
},
});

if (!response.ok) {
throw new Error(`Failed to Fetch Asset from ${assetUrl}`);
}

// download to root.
const fileStream = fs.createWriteStream(resolve(PGLT_ROOT, "schema.json"));

await streamPipeline(response.body, fileStream);

console.log(`Downloaded schema for ${releaseTag}`);
}

async function downloadBinary(platform, arch, os, releaseTag, githubToken) {
const buildName = getBuildName(platform, arch);

const assetUrl = `https://github.com/supabase-community/postgres_lsp/releases/download/${releaseTag}/${buildName}`;

const response = await fetch(assetUrl.trim(), {
headers: {
Authorization: `token ${githubToken}`,
Accept: `application/octet-stream`,
},
});

if (!response.ok) {
const error = await response.text();
throw new Error(
`Failed to Fetch Asset from ${assetUrl} (Reason: ${error})`
);
}

// just download to root.
const fileStream = fs.createWriteStream(getBinarySource(platform, arch, os));

await streamPipeline(response.body, fileStream);

console.log(`Downloaded asset for ${buildName} (v${releaseTag})`);
}

async function overwriteManifestVersions(releaseTag, isPrerelease) {
const version = getVersion(releaseTag, isPrerelease);

const manifestClone = structuredClone(rootManifest());

manifestClone.version = version;
for (const dep in manifestClone.optionalDependencies) {
manifestClone.optionalDependencies[dep] = version;
}

/**
* writeFileSync seemed to not work reliably?
*/
await new Promise((res, rej) => {
fs.writeFile(MANIFEST_PATH, JSON.stringify(manifestClone, null, 2), (e) =>
e ? rej(e) : res()
);
});
}

async function makePackageDir(platform, arch) {
const buildName = getBuildName(platform, arch);
const packageRoot = resolve(PACKAGES_PGLT_ROOT, buildName);

await new Promise((res, rej) => {
fs.mkdir(packageRoot, {}, (e) => (e ? rej(e) : res()));
});
}

function copyBinaryToNativePackage(platform, arch, os) {
// Update the package.json manifest
const buildName = getBuildName(platform, arch);
const packageRoot = resolve(PACKAGES_PGLT_ROOT, buildName);
const packageName = getPackageName(platform, arch);

const { version, license, repository, engines } = rootManifest();

/**
* We need to map rust triplets to NPM-known values.
* Otherwise, npm will abort the package installation.
*/
const npm_arch = arch === "aarch64" ? "arm64" : "x64";
let libc = undefined;
let npm_os = undefined;

switch (os) {
case "linux": {
libc = "gnu";
npm_os = "linux";
break;
}
case "windows": {
libc = "msvc";
npm_os = "win32";
break;
}
case "darwin": {
libc = undefined;
npm_os = "darwin";
break;
}
default: {
throw new Error(`Unsupported os: ${os}`);
}
}

const manifest = JSON.stringify(
{
name: packageName,
version,
license,
repository,
engines,
os: [npm_os],
cpu: [npm_arch],
libc,
},
null,
2
);

const ext = getBinaryExt(os);
const manifestPath = resolve(packageRoot, "package.json");
console.info(`Update manifest ${manifestPath}`);
fs.writeFileSync(manifestPath, manifest);

// Copy the CLI binary
const binarySource = getBinarySource(platform, arch, os);
const binaryTarget = resolve(packageRoot, `pglt${ext}`);

if (!fs.existsSync(binarySource)) {
console.error(
`Source for binary for ${buildName} not found at: ${binarySource}`
);
process.exit(1);
}

console.info(`Copy binary ${binaryTarget}`);
fs.copyFileSync(binarySource, binaryTarget);
fs.chmodSync(binaryTarget, 0o755);
}

function copySchemaToNativePackage(platform, arch) {
const buildName = getBuildName(platform, arch);
const packageRoot = resolve(PACKAGES_PGLT_ROOT, buildName);

const schemaSrc = resolve(PGLT_ROOT, `schema.json`);
const schemaTarget = resolve(packageRoot, `schema.json`);

if (!fs.existsSync(schemaSrc)) {
console.error(`schema.json not found at: ${schemaSrc}`);
process.exit(1);
}

console.info(`Copying schema.json`);
fs.copyFileSync(schemaSrc, schemaTarget);
fs.chmodSync(schemaTarget, 0o666);
}

const rootManifest = () =>
JSON.parse(fs.readFileSync(MANIFEST_PATH).toString("utf-8"));

function getBinaryExt(os) {
return os === "windows" ? ".exe" : "";
}

function getBinarySource(platform, arch, os) {
const ext = getBinaryExt(os);
return resolve(PGLT_ROOT, `${getBuildName(platform, arch)}${ext}`);
}

function getBuildName(platform, arch) {
return `pglt_${arch}-${platform}`;
}

function getPackageName(platform, arch) {
// trim the "unknown" from linux and the "pc" from windows
const platformName = platform.split("-").slice(-2).join("-");
return `pglt-${arch}-${platformName}`;
}

function getOs(platform) {
return platform.split("-").find((_, idx) => idx === 1);
}

function getVersion(releaseTag, isPrerelease) {
return releaseTag + (isPrerelease ? "-rc" : "");
}

(async function main() {
const githubToken = process.env.GITHUB_TOKEN;
let releaseTag = process.env.RELEASE_TAG;
assert(githubToken, "GITHUB_TOKEN not defined!");
assert(releaseTag, "RELEASE_TAG not defined!");

const isPrerelease = process.env.PRERELEASE === "true";

await downloadSchema(releaseTag, githubToken);
await overwriteManifestVersions(releaseTag, isPrerelease);

for (const platform of SUPPORTED_PLATFORMS) {
const os = getOs(platform);

for (const arch of SUPPORTED_ARCHITECTURES) {
await makePackageDir(platform, arch);
await downloadBinary(platform, arch, os, releaseTag, githubToken);
copyBinaryToNativePackage(platform, arch, os);
copySchemaToNativePackage(platform, arch);
}
}

process.exit(0);
})();