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
5 changes: 5 additions & 0 deletions .changeset/standalone-compose-deployments.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"caplets": patch
---

Publish checkout-free SQLite, convenience PostgreSQL, and hardened PostgreSQL Docker Compose deployments with image-packaged migration helpers.
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ jobs:
- name: Run quality gates
run: pnpm verify

compose:
name: Compose smoke
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24

- name: Run standalone deployment smoke tests
run: node scripts/smoke-compose-deployments.mjs

changeset:
name: Changeset
runs-on: ubuntu-latest
Expand Down
53 changes: 30 additions & 23 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,57 +69,53 @@ jobs:
CAPLETS_SENTRY_ENVIRONMENT: production
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Check whether CLI package was published
if: steps.changesets.outputs.published == 'true'
id: cli-package
- name: Detect CLI release for this commit
id: cli-release
env:
PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
run: |
cli_published=$(node <<'NODE'
const publishedPackages = JSON.parse(process.env.PUBLISHED_PACKAGES || '[]');
const cliPublished = publishedPackages.some((pkg) => pkg && pkg.name === 'caplets');
process.stdout.write(cliPublished ? 'true' : 'false');
NODE
)
echo "published=${cli_published}" >> "$GITHUB_OUTPUT"

- name: Read Docker image version
if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'
id: image-version
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
version=$(node -p "require('./packages/cli/package.json').version")
tag="caplets@${version}"
git fetch --force --tags origin
tag_commit=$(git rev-list -n 1 "$tag" 2>/dev/null || true)
published=false
if [ "$tag_commit" = "$GITHUB_SHA" ]; then
gh release view "$tag" >/dev/null
published=true
fi
echo "published=${published}" >> "$GITHUB_OUTPUT"
echo "version=${version}" >> "$GITHUB_OUTPUT"

- name: Setup QEMU
if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'
if: steps.cli-release.outputs.published == 'true'
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3

- name: Setup Docker Buildx
if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'
if: steps.cli-release.outputs.published == 'true'
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5

- name: Log in to GitHub Container Registry
if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'
if: steps.cli-release.outputs.published == 'true'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate Docker metadata
if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'
if: steps.cli-release.outputs.published == 'true'
id: docker-meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9
with:
images: ghcr.io/spiritledsoftware/caplets
tags: |
type=raw,value=latest
type=raw,value=${{ steps.image-version.outputs.version }}
type=raw,value=v${{ steps.image-version.outputs.version }}
type=raw,value=${{ steps.cli-release.outputs.version }}
type=raw,value=v${{ steps.cli-release.outputs.version }}
type=sha,format=short,prefix=sha-

- name: Publish Docker image
if: steps.changesets.outputs.published == 'true' && steps.cli-package.outputs.published == 'true'
if: steps.cli-release.outputs.published == 'true'
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf
with:
context: .
Expand All @@ -133,3 +129,14 @@ jobs:
CAPLETS_RUNTIME_SENTRY_DSN=${{ secrets.CAPLETS_RUNTIME_SENTRY_DSN }}
CAPLETS_SENTRY_RELEASE=caplets-runtime@${{ github.sha }}
CAPLETS_SENTRY_ENVIRONMENT=production

- name: Publish Docker Compose release assets
if: steps.cli-release.outputs.published == 'true'
run: >-
gh release upload "caplets@${{ steps.cli-release.outputs.version }}"
docker-compose.yml
docker-compose.postgres.yml
docker-compose.postgres-hardened.yml
--clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ RUN mkdir -p /data/config /data/state && \
chown -R node:root /app /data

COPY --from=build --chown=node:root /deploy ./
COPY --from=build --chown=node:root /app/deploy/postgres/*.mjs /usr/local/lib/caplets/postgres/

VOLUME ["/data"]

EXPOSE 5387
Comment thread
greptile-apps[bot] marked this conversation as resolved.

USER node
Expand Down
52 changes: 0 additions & 52 deletions deploy/postgres/init-caplets-roles.sh

This file was deleted.

34 changes: 34 additions & 0 deletions deploy/postgres/postgres-environment.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { readFileSync } from "node:fs";

export function readCredential(name) {
const direct = process.env[name];
const file = process.env[`${name}_FILE`];
if (direct && file) throw new Error(`set ${name} or ${name}_FILE, not both`);
if (direct) return direct;
if (!file) throw new Error(`${name} or ${name}_FILE is required`);
const value = readFileSync(file, "utf8").replace(/\r?\n$/u, "");
if (!value) throw new Error(`${name}_FILE must not be empty`);
return value;
}

export function postgresSchema() {
const schema = process.env.CAPLETS_POSTGRES_SCHEMA || "caplets";
if (!/^[a-z_][a-z0-9_]{0,62}$/u.test(schema)) {
throw new Error("CAPLETS_POSTGRES_SCHEMA must match ^[a-z_][a-z0-9_]{0,62}$");
}
return schema;
}

export function postgresConnectionString(user, password) {
const connection = new URL("postgresql://localhost");
connection.username = user;
connection.password = password;
connection.hostname = process.env.CAPLETS_POSTGRES_HOST || "caplets-postgres";
connection.port = process.env.CAPLETS_POSTGRES_PORT || "5432";
connection.pathname = `/${process.env.CAPLETS_POSTGRES_DATABASE || "caplets"}`;
return connection.toString();
}

export function quoteIdentifier(identifier) {
return `"${identifier.replaceAll('"', '""')}"`;
}
Loading