diff --git a/.github/workflows/continuous-release.yml b/.github/workflows/continuous-release.yml index f5fe4042..53326a3f 100644 --- a/.github/workflows/continuous-release.yml +++ b/.github/workflows/continuous-release.yml @@ -45,6 +45,9 @@ jobs: test: runs-on: ubuntu-latest needs: [lint, typecheck] + permissions: + id-token: write + contents: read steps: - name: Checkout code uses: actions/checkout@v4 @@ -58,18 +61,26 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Install 1Password CLI - uses: 1password/install-cli-action@v1 + - name: Install Doppler CLI + uses: dopplerhq/cli-action@v3 + + - name: Get OIDC token + run: | + TOKEN=$(curl -s -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ + "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=https://github.com/$GITHUB_REPOSITORY_OWNER") + echo "OIDC_TOKEN=$(echo $TOKEN | jq -r '.value')" >> $GITHUB_ENV + + - name: Authenticate with Doppler + run: | + doppler oidc login --scope=. --identity=${{ vars.DOPPLER_SERVICE_IDENTITY_ID }} --token=$OIDC_TOKEN + doppler configure set project proofkit + doppler configure set config test - name: Run Unit Tests - run: op run --env-file=op.env -- pnpm test - env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: pnpm test - name: Run fmodata E2E Tests - run: op run --env-file=op.env -- pnpm --filter @proofkit/fmodata test:e2e:ci - env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: pnpm --filter @proofkit/fmodata test:e2e build: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba73d112..a6f9d52c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,18 +44,26 @@ jobs: - name: Type Check run: pnpm typecheck - - name: Install 1Password CLI - uses: 1password/install-cli-action@v1 + - name: Install Doppler CLI + uses: dopplerhq/cli-action@v3 + + - name: Get OIDC token + run: | + TOKEN=$(curl -s -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ + "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=https://github.com/$GITHUB_REPOSITORY_OWNER") + echo "OIDC_TOKEN=$(echo $TOKEN | jq -r '.value')" >> $GITHUB_ENV + + - name: Authenticate with Doppler + run: | + doppler oidc login --scope=. --identity=${{ vars.DOPPLER_SERVICE_IDENTITY_ID }} --token=$OIDC_TOKEN + doppler configure set project proofkit + doppler configure set config test - name: Run Tests - run: op run --env-file=op.env -- pnpm test - env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: pnpm test - name: Run fmodata E2E Tests - run: op run --env-file=op.env -- pnpm --filter @proofkit/fmodata test:e2e:ci - env: - OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} + run: pnpm --filter @proofkit/fmodata test:e2e release: name: Release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..bca0681e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to ProofKit + +## Getting Started + +### Prerequisites + +- Node.js >= 18 +- pnpm (managed via corepack) +- [Doppler CLI](https://docs.doppler.com/docs/install-cli) for secrets management + +### Setup + +1. Clone the repository and install dependencies: + +```bash +git clone https://github.com/proofgeist/proofkit.git +cd proofkit +corepack enable +pnpm install +``` + +2. Install and configure Doppler: + +```bash +# Install Doppler CLI +brew install dopplerhq/cli/doppler # macOS +# or: curl -Ls https://cli.doppler.com/install.sh | sh # Linux + +# Login to Doppler +doppler login + +# Setup project (select proofkit project, dev config) +doppler setup +``` + +### Running Tests + +Test scripts automatically use `doppler run` to inject secrets: + +```bash +# Run all tests +pnpm test + +# Run specific package tests +pnpm --filter @proofkit/fmodata test +pnpm --filter @proofkit/fmodata test:e2e +``` + +### Building + +```bash +pnpm build +``` + +### Linting and Formatting + +```bash +pnpm lint +pnpm format +``` + +## Development Workflow + +1. Create a new branch for your feature or fix +2. Make your changes +3. Run tests with `pnpm test:local` +4. Run `pnpm lint` to check for issues +5. Submit a pull request + +## Code Style + +This project uses [Ultracite](https://github.com/proofgeist/ultracite) for linting and formatting. Run `pnpm dlx ultracite fix` to auto-fix issues before committing. diff --git a/doppler.yaml b/doppler.yaml new file mode 100644 index 00000000..b5162c45 --- /dev/null +++ b/doppler.yaml @@ -0,0 +1,3 @@ +setup: + project: proofkit + config: test diff --git a/op.env b/op.env deleted file mode 100644 index d0b56ebb..00000000 --- a/op.env +++ /dev/null @@ -1,31 +0,0 @@ -# __ _____ _ -# /_ | __ \ | | -# | | |__) |_ _ ___ _____ _____ _ __ __| | ___ _ ____ __ -# | | ___/ _` / __/ __\ \ /\ / / _ \| '__/ _` | / _ \ '_ \ \ / / -# | | | | (_| \__ \__ \\ V V / (_) | | | (_| || __/ | | \ V / -# |_|_| \__,_|___/___/ \_/\_/ \___/|_| \__,_(_)___|_| |_|\_/ -# -# This file is intentionally committed to source control. -# It should only reference secrets in 1Password - -# fmdapi & typegen secrets -FM_SERVER="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/FM_SERVER" -FM_DATABASE="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/FM_DATABASE" -OTTO_API_KEY="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/OTTO_API_KEY" -FM_USERNAME="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/username" -FM_PASSWORD="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/password" -DIFFERENT_FM_SERVER="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/FM_SERVER" -DIFFERENT_FM_DATABASE="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/FM_DATABASE" -DIFFERENT_OTTO_API_KEY="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/OTTO_API_KEY" -TEST_FM_SERVER="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/FM_SERVER" -TEST_FM_DATABASE="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/FM_DATABASE" -TEST_OTTO_API_KEY="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/1Password env Values/OTTO_API_KEY" -TEST_USERNAME="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/username" -TEST_PASSWORD="op://xrs5sehh2gm36me62rlfpmsyde/ztcjgvgc4i5aa6cjh5vudpco4m/password" - -# fmodata secrets -FMODATA_SERVER_URL="op://xrs5sehh2gm36me62rlfpmsyde/fmdapi_test/1Password env Values/FM_SERVER" -FMODATA_DATABASE="op://xrs5sehh2gm36me62rlfpmsyde/fmdapi_test/1Password env Values/FM_DATABASE" -FMODATA_API_KEY="op://xrs5sehh2gm36me62rlfpmsyde/fmdapi_test/1Password env Values/OTTO_API_KEY" -FMODATA_USERNAME="op://xrs5sehh2gm36me62rlfpmsyde/fmdapi_test/username" -FMODATA_PASSWORD="op://xrs5sehh2gm36me62rlfpmsyde/fmdapi_test/password" diff --git a/package.json b/package.json index b8b79e3d..591b7f53 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "sherif": "pnpm dlx sherif@latest", "sherif:fix": "pnpm sherif --fix", "release": "turbo run build --filter={./packages/*} && changeset publish", - "test": "vitest", + "test": "turbo run test", "knip": "knip", "prepare": "husky" }, diff --git a/packages/better-auth/package.json b/packages/better-auth/package.json index 0870f3b0..6009e3df 100644 --- a/packages/better-auth/package.json +++ b/packages/better-auth/package.json @@ -6,7 +6,7 @@ "main": "dist/esm/index.js", "scripts": { "dev": "pnpm build:watch", - "test": "vitest run", + "test": "doppler run -c test_betterauth -- vitest run", "typecheck": "tsc --noEmit", "build": "vite build && publint --strict", "build:watch": "vite build --watch", diff --git a/packages/better-auth/tests/adapter.test.ts b/packages/better-auth/tests/adapter.test.ts index 26a58fdd..ed06e86a 100644 --- a/packages/better-auth/tests/adapter.test.ts +++ b/packages/better-auth/tests/adapter.test.ts @@ -51,7 +51,7 @@ describe("My Adapter Tests", async () => { } } } - }); + }, 60_000); if (!process.env.FM_SERVER) { throw new Error("FM_SERVER is not set"); diff --git a/packages/better-auth/tests/setupEnv.ts b/packages/better-auth/tests/setupEnv.ts deleted file mode 100644 index 0ef7a1df..00000000 --- a/packages/better-auth/tests/setupEnv.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { config } from "dotenv"; - -config({ path: ".env.local" }); diff --git a/packages/better-auth/vitest.config.ts b/packages/better-auth/vitest.config.ts index b6fc7906..8d7d7f61 100644 --- a/packages/better-auth/vitest.config.ts +++ b/packages/better-auth/vitest.config.ts @@ -1,13 +1,7 @@ import { defineConfig } from "vitest/config"; -// import dotenv from "dotenv"; -// import path from "path"; - -// // Load .env.local file explicitly -// dotenv.config({ path: path.resolve(__dirname, ".env.local") }); export default defineConfig({ test: { testTimeout: 15_000, // 15 seconds, since we're making a network call to FM - setupFiles: ["./tests/setupEnv.ts"], }, }); diff --git a/packages/cli/.env.test b/packages/cli/.env.test deleted file mode 100644 index 10160dc4..00000000 --- a/packages/cli/.env.test +++ /dev/null @@ -1,8 +0,0 @@ -# Test environment variables for CLI tests -# Replace these values with your test environment values - -OTTO_SERVER_URL=https://acme-dev.ottomatic.cloud -OTTO_ADMIN_API_KEY=ak_rSvHqOI5nFIKjVDNjn16wzo2MvdlI4Wl -FM_DATA_API_KEY=dk_fptdBZ9fSxUoVbwNikFX83Q5qS2u2lw9 -FM_FILE_NAME=Foxtail_Demo.fmp12 -FM_LAYOUT_NAME=API_Customers diff --git a/packages/cli/package.json b/packages/cli/package.json index cb091f02..b0b549fd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -51,7 +51,7 @@ "pub:beta": "NODE_ENV=production pnpm build && npm publish --tag beta --access public", "pub:next": "NODE_ENV=production pnpm build && npm publish --tag next --access public", "pub:release": "NODE_ENV=production pnpm build && npm publish --access public", - "test": "vitest run" + "test": "doppler run -c test_cli -- vitest run" }, "dependencies": { "@better-fetch/fetch": "1.1.17", diff --git a/packages/cli/tests/browser-apps.test.ts b/packages/cli/tests/browser-apps.test.ts index 4e12052a..106c123f 100644 --- a/packages/cli/tests/browser-apps.test.ts +++ b/packages/cli/tests/browser-apps.test.ts @@ -14,7 +14,7 @@ describe("Non-Interactive CLI Tests", () => { // Parse test environment variables const testEnv = z .object({ - OTTO_SERVER_URL: z.string().url(), + OTTO_SERVER_URL: z.url(), OTTO_ADMIN_API_KEY: z.string().min(1), FM_DATA_API_KEY: z.string().min(1), FM_FILE_NAME: z.string().min(1), diff --git a/packages/cli/vitest.config.ts b/packages/cli/vitest.config.ts index ded13629..3216b420 100644 --- a/packages/cli/vitest.config.ts +++ b/packages/cli/vitest.config.ts @@ -1,10 +1,5 @@ -import path from "node:path"; -import dotenv from "dotenv"; import { defineConfig } from "vitest/config"; -// Load test environment variables -dotenv.config({ path: path.resolve(__dirname, ".env.test") }); - export default defineConfig({ test: { globals: true, diff --git a/packages/fmdapi/package.json b/packages/fmdapi/package.json index 82010a18..f361ae49 100644 --- a/packages/fmdapi/package.json +++ b/packages/fmdapi/package.json @@ -44,9 +44,8 @@ "format": "biome format --write .", "dev": "tsc --watch", "ci": "pnpm build && pnpm check-format && pnpm publint --strict && pnpm test", - "test": "vitest run", + "test": "doppler run -- vitest run", "typecheck": "tsc --noEmit", - "setup-env": "op inject -i ../../op.env -o .env.local -f", "changeset": "changeset", "release": "pnpm build && changeset publish --access public", "knip": "knip", diff --git a/packages/fmdapi/tests/setup.ts b/packages/fmdapi/tests/setup.ts index 18b32901..63ad775b 100644 --- a/packages/fmdapi/tests/setup.ts +++ b/packages/fmdapi/tests/setup.ts @@ -1,9 +1,6 @@ -import dotenv from "dotenv"; import type { OttoFMSAPIKey } from "../src/adapters/otto"; import { DataApi, FetchAdapter, OttoAdapter } from "../src/index"; -dotenv.config({ path: ".env.local" }); - if (!(process.env.FM_SERVER && process.env.FM_DATABASE && process.env.OTTO_API_KEY)) { throw new Error("FM_SERVER, FM_DATABASE, and OTTO_API_KEY must be set in the environment"); } diff --git a/packages/fmodata/package.json b/packages/fmodata/package.json index da0c75ea..0c2b8e05 100644 --- a/packages/fmodata/package.json +++ b/packages/fmodata/package.json @@ -21,21 +21,17 @@ "scripts": { "build": "tsc && vite build && publint --strict", "build:watch": "tsc && vite build --watch", - "check-format": "biome format --check .", - "format": "biome format --write .", "lint": "biome check . --write", "lint:summary": "biome check . --reporter=summary", - "lint:fix": "pnpm lint --fix", "dev": "tsc --watch", - "test": "vitest run --typecheck", + "test": "doppler run -- vitest run --typecheck", "typecheck": "tsc --noEmit", "test:typecheck": "vitest run --typecheck", "test:watch": "vitest --typecheck", "test:build": "pnpm build && TEST_BUILD=true vitest run --typecheck", "test:watch:build": "TEST_BUILD=true vitest --typecheck", - "test:e2e": "op inject -i ../../op.env -o .env.local -f && vitest run tests/e2e.test.ts", - "test:e2e:ci": "vitest run tests/e2e.test.ts", - "capture": "op inject -i ../../op.env -o .env.local -f && tsx scripts/capture-responses.ts", + "test:e2e": "doppler run -- vitest run tests/e2e.test.ts", + "capture": "doppler run -- tsx scripts/capture-responses.ts", "knip": "knip", "pub:alpha": "bun run scripts/publish-alpha.ts", "global:link": "pnpm link --global" diff --git a/packages/fmodata/tests/e2e/setup.ts b/packages/fmodata/tests/e2e/setup.ts index 999b9866..563b3303 100644 --- a/packages/fmodata/tests/e2e/setup.ts +++ b/packages/fmodata/tests/e2e/setup.ts @@ -5,13 +5,9 @@ * used across all E2E test files. */ -import path from "node:path"; import { fmTableOccurrence, textField, timestampField } from "@proofkit/fmodata"; -import { config } from "dotenv"; import { z } from "zod/v4"; -config({ path: path.resolve(__dirname, "../../.env.local") }); - // Load environment variables export const serverUrl = process.env.FMODATA_SERVER_URL; export const apiKey = process.env.FMODATA_API_KEY; diff --git a/packages/typegen/package.json b/packages/typegen/package.json index f278a1b7..6b17cf31 100644 --- a/packages/typegen/package.json +++ b/packages/typegen/package.json @@ -8,10 +8,9 @@ "dev": "pnpm build:watch", "dev:ui": "concurrently -n \"web,api\" -c \"cyan,magenta\" \"pnpm -C web dev\" \"pnpm run dev:api\"", "dev:api": "concurrently -n \"build,server\" -c \"cyan,magenta\" \"pnpm build:watch\" \"nodemon --watch dist/esm --delay 1 --exec 'node dist/esm/cli.js ui --port 3141 --no-open'\"", - "test": "vitest run", + "test": "doppler run -- vitest run", "test:watch": "vitest --watch", "typecheck": "tsc --noEmit", - "setup-env": "op inject -i ../../op.env -o .env.local -f", "build": "pnpm -C web build && pnpm vite build && node scripts/build-copy.js && publint --strict", "build:watch": "vite build --watch", "ci": "pnpm run build && pnpm run test", diff --git a/packages/typegen/tests/setupEnv.ts b/packages/typegen/tests/setupEnv.ts index eee4e1e8..48cece51 100644 --- a/packages/typegen/tests/setupEnv.ts +++ b/packages/typegen/tests/setupEnv.ts @@ -1,10 +1,4 @@ -import path from "node:path"; -import dotenv from "dotenv"; - -// Load .env.local from the parent directory (packages/typegen) relative to this setup file (packages/typegen/tests) -dotenv.config({ path: path.resolve(__dirname, "../.env.local") }); - -console.log("SetupEnv: Loading .env.local from", path.resolve(__dirname, "../.env.local")); +// Environment variables are injected by doppler run console.log("SetupEnv: DIFFERENT_FM_SERVER=", process.env.DIFFERENT_FM_SERVER ? "Loaded" : "Not Loaded"); console.log("SetupEnv: DIFFERENT_FM_DATABASE=", process.env.DIFFERENT_FM_DATABASE ? "Loaded" : "Not Loaded"); console.log("SetupEnv: DIFFERENT_OTTO_API_KEY=", process.env.DIFFERENT_OTTO_API_KEY ? "Loaded" : "Not Loaded"); diff --git a/turbo.json b/turbo.json index 24d7b5a3..576b445c 100644 --- a/turbo.json +++ b/turbo.json @@ -18,8 +18,20 @@ "dependsOn": ["^build"] }, "test": { - "outputs": ["coverage/**"], - "dependsOn": [] + "inputs": [ + "$TURBO_DEFAULT$", + "vitest.config.*", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.spec.ts", + "**/*.spec.tsx" + ], + "outputs": [], + "dependsOn": ["^build"] + }, + "test:watch": { + "cache": false, + "persistent": true }, "typecheck": { "dependsOn": ["^build"],