changelog(back): Platform.init() idempotency fix #792
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Open Pryv.io v2 CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [master] | |
| # Tag pushes trigger the same test+lint+typecheck jobs as master | |
| # pushes so the release moment is gated by the same checks. The | |
| # docker job below additionally requires `refs/tags/*` so it only | |
| # publishes on tag pushes, never on master pushes. | |
| tags: ['*'] | |
| jobs: | |
| # PostgreSQL is the default baseStorage engine; SQLite is the | |
| # alternative. CI runs the PG matrix sequentially. | |
| test-postgres: | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 40 | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: pryv | |
| POSTGRES_PASSWORD: pryv | |
| POSTGRES_DB: pryv-node-test | |
| ports: | |
| - 5432:5432 | |
| # pg_isready WITHOUT -d defaults to db=$USER (pryv), which is not | |
| # created — every healthcheck triggered a backend fork that hit | |
| # `FATAL: database "pryv" does not exist` (visible 5s-interval | |
| # spam in PG service logs). Hypothesis: that churn (~615 PG-touching | |
| # processes / 5s window during test startup) contributed to the | |
| # first pool.connect() ECONNRESET that broke test-postgres since | |
| # 2026-05-21. Probing the real test DB stops the spam and avoids | |
| # backend churn during the bootstrap window. | |
| options: >- | |
| --health-cmd "pg_isready -U pryv -d pryv-node-test" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: extractions/setup-just@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Setup dev environment | |
| run: just setup-dev-env | |
| - name: Install InfluxDB | |
| run: | | |
| wget -q https://dl.influxdata.com/influxdb/releases/influxdb-1.8.10_linux_amd64.tar.gz | |
| tar xzf influxdb-1.8.10_linux_amd64.tar.gz | |
| sudo cp influxdb-1.8.10-1/usr/bin/influx* /usr/local/bin/ | |
| - name: Install dependencies | |
| run: npm install --ignore-scripts && npm rebuild | |
| - name: Start services | |
| run: | | |
| ./storages/engines/rqlite/scripts/start & | |
| influxd & | |
| sleep 10 | |
| - name: Warm up PostgreSQL connection | |
| # The first pg-pool connect from the test runner sporadically returns | |
| # ECONNRESET (broken on master since 2026-05-21). Doing one real client | |
| # connection here lets PG fork and warm its connection-handling path | |
| # so the test runner's pool sees a stable backend. | |
| env: | |
| PGPASSWORD: pryv | |
| run: | | |
| for i in 1 2 3 4 5; do | |
| if psql -h 127.0.0.1 -p 5432 -U pryv -d pryv-node-test -c "SELECT 1;" > /dev/null 2>&1; then | |
| echo "PG warmup OK on attempt $i" | |
| break | |
| fi | |
| echo "PG warmup attempt $i failed; retrying..." | |
| sleep 2 | |
| done | |
| - name: Run PostgreSQL test suite | |
| # CI stays SEQUENTIAL on purpose — parallel mode disables the | |
| # integrity-check beforeEach/afterEach + bypasses the caching | |
| # layer + uses a fallback for cluster_kv (no real IPC). Those | |
| # are three real coverage gaps; sequential exercises them. | |
| # Parallel mode is a local-dev productivity tool | |
| # (`just test-parallel all`), not the matrix-of-record. | |
| run: just clean-test-data && just test all | |
| env: | |
| # PGPASSWORD lets the `dropdb` calls inside `just clean-test-data` | |
| # authenticate cleanly. Without it, dropdb's stdin-password fallback | |
| # has been observed to silently block the whole step on CI runners | |
| # — even when an earlier `psql` connection succeeded. | |
| PGPASSWORD: pryv | |
| storages__engines__postgresql__host: localhost | |
| storages__engines__postgresql__port: 5432 | |
| storages__engines__postgresql__user: pryv | |
| storages__engines__postgresql__password: pryv | |
| storages__engines__postgresql__database: pryv-node-test | |
| lint: | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Install dependencies | |
| # Cannot use --omit=optional: ESLint 9's import-x plugin pulls in | |
| # `unrs-resolver`, which loads its platform-specific native binding | |
| # (e.g. @unrs/resolver-binding-linux-x64-gnu) via npm's optional | |
| # dependencies mechanism. Skipping optionals leaves the binding | |
| # missing and ESLint exits with "Cannot find native binding". | |
| run: npm install --ignore-scripts | |
| - name: Lint | |
| run: npx eslint . | |
| - name: Production-dependency integrity | |
| # No runtime TOP-LEVEL require of a dev-only package (pruned by | |
| # `--omit=dev`, which crash-loops the production image). CI installs | |
| # devDependencies, so eslint/tests never see this class — rc.7 shipped | |
| # a top-level require('cuid') (a devDependency) and was dead on arrival | |
| # (#106). This static gate catches it without a prod build. | |
| run: ./scripts/prod-dep-integrity | |
| typecheck: | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: extractions/setup-just@v4 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Install dependencies | |
| run: npm install --ignore-scripts | |
| - name: Typecheck | |
| run: just typecheck | |
| - name: Type coverage floor | |
| run: just type-coverage | |
| # Ground-truth guard against the #106 class: a REAL production install | |
| # (`--omit=dev`, which prunes devDependencies) + verify every external | |
| # package that runtime code top-level-requires still resolves. rc.7 shipped | |
| # a top-level require('cuid') (a devDependency) and crash-looped on prod | |
| # because CI only ever installed WITH devDependencies. The `docker` release | |
| # job below `needs` this, so a prod-broken build can never publish an image. | |
| prod-boot-smoke: | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 8 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Production install (--omit=dev — devDependencies pruned) | |
| run: npm install --omit=dev --ignore-scripts | |
| - name: Production boot smoke | |
| run: ./scripts/prod-boot-smoke | |
| # Runtime-dependency vulnerability gate. Fails on any high/critical advisory | |
| # affecting the SHIPPED (--omit=dev) dependency tree, so a freshly-disclosed | |
| # CVE breaks the build instead of only showing up in the Dependabot tab. | |
| # devDependencies are out of scope (pruned from the image). Accepted | |
| # advisories are documented in the script's allowlist, not silenced. | |
| security-audit: | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| - name: Install dependencies | |
| run: npm install --ignore-scripts | |
| - name: Audit runtime dependencies | |
| run: ./scripts/audit-prod-deps | |
| # Software Bill of Materials + composition scan. Emits a CycloneDX SBOM for | |
| # the source tree (published as a build artifact for downstream consumers / | |
| # procurement) and runs Grype against it. The scan fails only on CRITICAL so | |
| # it does not duplicate the high-severity gate above; high/medium are | |
| # reported as annotations. Container-image SBOM is emitted in the docker job. | |
| sbom: | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Generate CycloneDX SBOM (source) | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| path: . | |
| format: cyclonedx-json | |
| output-file: sbom.cyclonedx.json | |
| artifact-name: sbom-source.cyclonedx.json | |
| - name: Scan SBOM with Grype (fail on critical) | |
| uses: anchore/scan-action@v6 | |
| with: | |
| sbom: sbom.cyclonedx.json | |
| fail-build: true | |
| severity-cutoff: critical | |
| # OFFICIAL release path: tag-push → CI runs tests → builds + pushes | |
| # `pryvio/open-pryv.io:<tag>` + rolling `:2.0.0-pre` to Docker Hub. | |
| # This is the channel implementers + downstream consumers pull from. | |
| # | |
| # SIDE CHANNEL for in-house iteration (NOT a release): build on mbp2 | |
| # + tar+scp to test-temp.datasafe.dev for quick smoke without going | |
| # through Docker Hub: | |
| # ./_local/scripts/rc-build-and-push.sh [--tag <tag>] [--force] | |
| # ./_local/scripts/rc-smoketest.sh [--host <fqdn>] | |
| # The side channel doesn't replace the tag-push release; it's just | |
| # the dev-loop tool while iterating on RC fixes. | |
| docker: | |
| needs: [test-postgres, lint, typecheck, prod-boot-smoke, security-audit, sbom] | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') | |
| runs-on: ubuntu-22.04 | |
| # id-token: write enables keyless (OIDC) cosign signing + SLSA provenance — | |
| # no long-lived signing key is stored; the identity is this workflow run. | |
| # attestations: write lets actions/attest-build-provenance record the | |
| # provenance attestation against the image. | |
| permissions: | |
| contents: read | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: docker/login-action@v4 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USER }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - uses: docker/build-push-action@v7 | |
| id: build_stock | |
| with: | |
| context: . | |
| push: true | |
| # Bake the git tag into the image as PRYV_IMAGE_TAG so the install | |
| # wizard's generated run-pryv.sh + check-config.sh launchers default | |
| # their PRYV_IMAGE to the same tag the container was pulled from. | |
| build-args: | | |
| IMAGE_TAG=${{ github.ref_name }} | |
| tags: | | |
| pryvio/open-pryv.io:${{ github.ref_name }} | |
| pryvio/open-pryv.io:2.0.0-pre | |
| # Container-image SBOM (CycloneDX) for the released image, published as a | |
| # build artifact so downstream consumers / procurement can ingest the | |
| # exact shipped composition (base image layers + npm tree). | |
| - name: Generate image SBOM (CycloneDX) | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| image: pryvio/open-pryv.io@${{ steps.build_stock.outputs.digest }} | |
| format: cyclonedx-json | |
| output-file: sbom-image.cyclonedx.json | |
| artifact-name: sbom-image.cyclonedx.json | |
| # Keyless (OIDC) signature over the pushed image digest. Pullers verify | |
| # with: cosign verify pryvio/open-pryv.io@<digest> | |
| # --certificate-identity-regexp 'https://github.com/pryv/open-pryv.io/.*' | |
| # --certificate-oidc-issuer https://token.actions.githubusercontent.com | |
| - name: Install cosign | |
| uses: sigstore/cosign-installer@v3 | |
| - name: Sign image (keyless) | |
| run: cosign sign --yes pryvio/open-pryv.io@${{ steps.build_stock.outputs.digest }} | |
| # SLSA build provenance attestation for the released image, signed keyless | |
| # and recorded so consumers can verify how + where the image was built. | |
| - name: Attest build provenance | |
| uses: actions/attest-build-provenance@v2 | |
| with: | |
| subject-name: docker.io/pryvio/open-pryv.io | |
| subject-digest: ${{ steps.build_stock.outputs.digest }} | |
| push-to-registry: true | |
| # Encryption-at-rest variant: layers the container-encrypted-volume payload | |
| # onto the stock image just pushed above (base image unchanged; encryption | |
| # opt-in via CEV_ENABLED at runtime). The payload is pulled from Docker Hub | |
| # (pryvio/container-encrypted-volume), public + same registry as this image. | |
| - name: Build + push encryption-at-rest variant | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: Dockerfile.encrypted | |
| push: true | |
| build-args: | | |
| IMAGE_TAG=${{ github.ref_name }} | |
| tags: | | |
| pryvio/open-pryv.io-encrypted:${{ github.ref_name }} | |
| pryvio/open-pryv.io-encrypted:2.0.0-pre |