-
Notifications
You must be signed in to change notification settings - Fork 2
chore(env): add repo-wide .env.example and adopt provider-aware site resolution for docs #115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f5d4964
chore(env): add repo-wide .env.example and adopt provider-aware site …
yacosta738 39123d6
Merge branch 'main' into chore/add-env-example
yacosta738 dad2569
chore(changelog): fix templates, label extractors, and sort order; en…
yacosta738 5b5c0b5
style(docs): fix imports and unused catch variable per lint
yacosta738 9bada94
chore(env): sanitize .env.example placeholders; compute astro base fr…
yacosta738 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| # corvus/.env.example | ||
| # | ||
| # Canonical list of environment variables used across this repository. | ||
| # - DO NOT store real secrets here. Use CI secrets or your local secret manager (direnv, .env.local, etc.). | ||
| # - This file is a documentation + example file. Copy it to .env for local development and fill in real values where required. | ||
| # - Frontend/public envs: the shared getEnv helper accepts keys with no prefix, PUBLIC_ or VITE_. | ||
|
|
||
| ######################################## | ||
| # Global / environment | ||
| ######################################## | ||
| # NODE_ENV - common runtime mode. optional. values: development|production | ||
| NODE_ENV=development | ||
|
|
||
| # Generic site fallback used by resolveSiteUrl() when provider-specific keys are missing | ||
| # Example: https://example.com or http://localhost:4321 | ||
| SITE_URL=http://localhost:9988 | ||
|
|
||
| ######################################## | ||
| # Local dev ports (sane defaults used by shared env helper) | ||
| ######################################## | ||
| # These are the PORTS mapping used across the web packages. Change only if you have port conflicts. | ||
| CHAT_PORT=4323 | ||
| DOCS_PORT=4321 | ||
| MARKETING_PORT=9988 | ||
| PLUGINS_PORT=9990 | ||
|
|
||
| ######################################## | ||
| # Web apps / Static sites | ||
| ######################################## | ||
| # Docs site | ||
| DOCS_URL=http://localhost:${DOCS_PORT} | ||
| # Marketing site - provider-aware. Local default uses MARKETING_PORT above. | ||
| MARKETING_URL=http://localhost:${MARKETING_PORT} | ||
|
|
||
| # Hosting provider fallbacks (set by provider automatically in their build environments) | ||
| # These are read by resolveSiteUrl({ providerKeys: { cloudflare: 'CF_PAGES_URL', vercel: 'VERCEL_URL', netlify: 'URL' }}) | ||
| CF_PAGES_URL= | ||
| URL= | ||
| VERCEL_URL= | ||
|
|
||
| ######################################## | ||
| # Frontend public keys (safe for client; mark explicitly if client-side) | ||
| ######################################## | ||
| # Public keys that are intentionally exposed to browser code should use PUBLIC_ or VITE_ prefix | ||
| # Example (marketing analytics; safe to be public): | ||
| PUBLIC_AHREFS_KEY=your_ahrefs_public_key_here | ||
|
|
||
| ######################################## | ||
| # Agent runtime / backend (Corvus) | ||
| ######################################## | ||
| # CORVUS_API_KEY - API key for internal agent auth (keep secret) | ||
| CORVUS_API_KEY=your_corvus_api_key_here | ||
| CORVUS_GATEWAY_HOST=127.0.0.1 | ||
| CORVUS_GATEWAY_PORT=4000 | ||
| CORVUS_OPEN_SKILLS_ENABLED=false | ||
| CORVUS_WORKSPACE=default | ||
|
|
||
| ######################################## | ||
| # SurrealDB (primary app DB for agent runtime) | ||
| ######################################## | ||
| # Use a local dev SurrealDB for development. Production should be set via secrets. | ||
| CORVUS_SURREALDB_DATABASE=corvus_db | ||
| CORVUS_SURREALDB_NAMESPACE=corvus | ||
| CORVUS_SURREALDB_PASSWORD=your_local_dev_db_password_here | ||
| CORVUS_SURREALDB_TOKEN= | ||
| CORVUS_SURREALDB_URL=http://127.0.0.1:8000 | ||
| CORVUS_SURREALDB_USERNAME=root | ||
|
|
||
| # Test-specific SurrealDB (used by CI/tests when present) | ||
| CORVUS_TEST_SURREALDB_DATABASE=corvus_test_db | ||
| CORVUS_TEST_SURREALDB_NAMESPACE=corvus_test | ||
| CORVUS_TEST_SURREALDB_URL=http://127.0.0.1:8001 | ||
|
|
||
| ######################################## | ||
| # Third-party provider keys (examples/placeholders) | ||
| ######################################## | ||
| # Google APIs (if used) | ||
| GOOGLE_API_KEY=your_google_api_key_here | ||
|
|
||
| # Anthropic / OpenAI / LLM providers | ||
| ANTHROPIC_API_KEY=your_anthropic_api_key_here | ||
| OPENAI_API_KEY=your_openai_api_key_here | ||
|
|
||
| # GitHub / CI tokens | ||
| GH_TOKEN=your_gh_token_here | ||
| GITHUB_TOKEN=your_github_token_here | ||
|
|
||
| ######################################## | ||
| # CI, release & publishing secrets (store in GitHub Secrets / CI provider) | ||
| ######################################## | ||
| # GPG / signing used in release pipelines (example placeholders only) | ||
| SIGNING_IN_MEMORY_KEY=your_signing_key_base64_here | ||
| SIGNING_IN_MEMORY_KEY_PASSWORD=your_signing_key_password_here | ||
|
|
||
| # Maven Central | ||
| MAVEN_CENTRAL_USERNAME=your_maven_username | ||
| MAVEN_CENTRAL_PASSWORD=your_maven_password | ||
|
|
||
| # Cargo / crates.io | ||
| CARGO_REGISTRY_TOKEN=your_crates_io_token_here | ||
|
|
||
| # NPM / packages | ||
| NPM_TOKEN=your_npm_token_here | ||
|
|
||
| # DockerHub | ||
| DOCKERHUB_USERNAME=your_dockerhub_username_here | ||
| DOCKERHUB_TOKEN=your_dockerhub_token_here | ||
|
|
||
| # Code quality / telemetry | ||
| SONAR_TOKEN= | ||
| CODECOV_TOKEN= | ||
|
|
||
| ######################################## | ||
| # Gradle / build / repo config | ||
| ######################################## | ||
| # Project coordinates and build cache credentials (optional) | ||
| GROUP=com.profiletailors | ||
| VERSION=0.1.0 | ||
| BUILD_CACHE_USER= | ||
| BUILD_CACHE_PWD= | ||
| PRIREPO_URL= | ||
| PRIREPO_USERNAME= | ||
| PRIREPO_PASSWORD= | ||
| ENABLE_LOCAL_CONFIG=true | ||
|
|
||
| ######################################## | ||
| # Optional / feature toggles & local helpers | ||
| ######################################## | ||
| # If you need a shorter local override for SITE_URL per app, set the specific one above (MARKETING_URL, DOCS_URL, ...) | ||
|
|
||
| # Example debug flags | ||
| DEBUG=true | ||
|
|
||
| ######################################## | ||
| # Notes | ||
| ######################################## | ||
| # - Copy this file to .env and fill in the secret values before running local services. | ||
| # - For CI and releases: add all SECRET values (signing keys, repository tokens, package tokens) to your | ||
| # repository's GitHub Secrets or your CI provider's secret store. Never commit real credentials to the repo. | ||
| # - Frontend/public variables: prefix with PUBLIC_ or VITE_ if they are safe to expose to the browser. | ||
| # - The repo's shared env helper will prefer provider-specific envs (CF_PAGES_URL, VERCEL_URL, URL) when present | ||
| # and fall back to the primary key (MARKETING_URL / DOCS_URL / SITE_URL) for site resolution. | ||
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import { describe, it, expect } from "vitest"; | ||
| import { resolveSiteUrl, getPortFromUrl, PORTS } from "../env.mjs"; | ||
|
|
||
| const DEFAULT_DEV_URL = `http://localhost:${PORTS.DOCS}`; | ||
| const DEFAULT_PROD_URL = "https://docs.profiletailors.com"; | ||
|
|
||
| describe("resolveSiteUrl and getPortFromUrl matrix", () => { | ||
| it("resolveSiteUrl_prefers_DOCS_URL_over_providers_and_falls_back_to_SITE_URL", () => { | ||
| // Explicit DOCS_URL should win | ||
| const explicit = resolveSiteUrl({ | ||
| env: { DOCS_URL: "http://explicit.example/path" }, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(explicit).toBe("http://explicit.example/path"); | ||
|
|
||
| // Cloudflare provider should be selected when DOCS_URL missing | ||
| const cloudflare = resolveSiteUrl({ | ||
| env: { CF_PAGES_URL: "docs.cloudflare.test/sub" }, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(cloudflare).toBe("https://docs.cloudflare.test/sub"); | ||
|
|
||
| // Vercel provider | ||
| const vercel = resolveSiteUrl({ | ||
| env: { VERCEL_URL: "my-vercel.app/docs" }, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(vercel).toBe("https://my-vercel.app/docs"); | ||
|
|
||
| // Netlify provider (URL) | ||
| const netlify = resolveSiteUrl({ | ||
| env: { URL: "https://netlify.example/base/" }, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(netlify).toBe("https://netlify.example/base"); | ||
|
|
||
| // Generic SITE_URL fallback | ||
| const generic = resolveSiteUrl({ | ||
| env: { SITE_URL: "http://fallback.test:9001/fpath" }, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(generic).toBe("http://fallback.test:9001/fpath"); | ||
|
|
||
| // No envs -> dev fallback | ||
| const fallbackDev = resolveSiteUrl({ | ||
| env: {}, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(fallbackDev).toBe(DEFAULT_DEV_URL); | ||
|
|
||
| // No envs -> prod fallback when isProdLike | ||
| const fallbackProd = resolveSiteUrl({ | ||
| env: {}, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: true, | ||
| }); | ||
| expect(fallbackProd).toBe(DEFAULT_PROD_URL); | ||
| }); | ||
|
|
||
| it("getPortFromUrl_derives_port_from_docsUrl", () => { | ||
| // URL with explicit port | ||
| const withPort = "http://example.com:8080/path"; | ||
| expect(getPortFromUrl(withPort, PORTS.DOCS)).toBe(8080); | ||
|
|
||
| // URL without port -> fallback | ||
| const withoutPort = "https://example.com/path"; | ||
| expect(getPortFromUrl(withoutPort, PORTS.DOCS)).toBe(PORTS.DOCS); | ||
|
|
||
| // Derived from resolveSiteUrl result | ||
| const docsUrl = resolveSiteUrl({ | ||
| env: { DOCS_URL: "http://localhost:12345/docs" }, | ||
| primaryKey: "DOCS_URL", | ||
| localDefault: DEFAULT_DEV_URL, | ||
| productionDefault: DEFAULT_PROD_URL, | ||
| genericKeys: ["SITE_URL"], | ||
| providerKeys: { cloudflare: "CF_PAGES_URL", vercel: "VERCEL_URL", netlify: "URL" }, | ||
| isProdLike: false, | ||
| }); | ||
| expect(getPortFromUrl(docsUrl, PORTS.DOCS)).toBe(12345); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shell variable interpolation may not work with all env loaders.
The
${DOCS_PORT}and${MARKETING_PORT}syntax relies on shell interpolation, which won't work with many env loaders (e.g., Node.jsdotenvdoesn't expand variables by default). Consider using literal values or documenting this limitation.Option 1: Use literal values
Option 2: Add a note about interpolation
Add a comment above these lines:
+# Note: ${VAR} interpolation requires shell expansion or dotenv-expand; use literal ports if your loader doesn't support it. DOCS_URL=http://localhost:${DOCS_PORT}📝 Committable suggestion
🤖 Prompt for AI Agents