mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
build: env file improve
This commit is contained in:
parent
8432e1bbb4
commit
97dd02fe7a
10 changed files with 241 additions and 108 deletions
17
.env.example
17
.env.example
|
|
@ -1,14 +1,9 @@
|
|||
# Environment configuration example
|
||||
# Copy this file to .env and adjust values as needed
|
||||
|
||||
# NODE_ENV - Set the Node environment (development, production, staging)
|
||||
NODE_ENV=development
|
||||
|
||||
# PRODUCTION - Set to true for production builds
|
||||
PRODUCTION=false
|
||||
|
||||
# STAGE - Set to true for staging builds
|
||||
STAGE=false
|
||||
|
||||
# Additional environment variables can be added here
|
||||
# They will be accessible in the Angular app via process.env
|
||||
# Example API keys and tokens (add your actual values in .env)
|
||||
# GOOGLE_DRIVE_TOKEN=your-token-here
|
||||
# DROPBOX_API_KEY=your-api-key-here
|
||||
# WEBDAV_URL=https://your-webdav-server.com
|
||||
# WEBDAV_USERNAME=your-username
|
||||
# WEBDAV_PASSWORD=your-password
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -2,14 +2,9 @@
|
|||
|
||||
# environment files
|
||||
.env
|
||||
.env.production
|
||||
.env.stage
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# auto-generated environment file
|
||||
src/environments/environment.ts
|
||||
|
||||
# compiled output
|
||||
/.tmp
|
||||
/dist
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@
|
|||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
"with": "src/environments/environment.stage.ts"
|
||||
}
|
||||
],
|
||||
"optimization": {
|
||||
|
|
@ -171,7 +171,7 @@
|
|||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
"with": "src/environments/environment.stage.ts"
|
||||
}
|
||||
],
|
||||
"optimization": {
|
||||
|
|
|
|||
|
|
@ -1,62 +1,122 @@
|
|||
# Environment Configuration
|
||||
# Environment Configuration Setup
|
||||
|
||||
Super Productivity now uses `.env` files for environment-specific configuration instead of Angular's traditional environment files.
|
||||
This project uses a hybrid approach for environment configuration:
|
||||
|
||||
## Setup
|
||||
- **Base configuration** (production/stage flags) are in static TypeScript files
|
||||
- **Secrets and dynamic values** are loaded from `.env` files at build time
|
||||
|
||||
1. Copy `.env.example` to `.env` for local development:
|
||||
## Overview
|
||||
|
||||
### Static Environment Files
|
||||
|
||||
- `src/environments/environment.ts` - Development configuration
|
||||
- `src/environments/environment.prod.ts` - Production configuration
|
||||
- `src/environments/environment.stage.ts` - Staging configuration
|
||||
|
||||
These files contain base configuration like `production`, `stage`, and `version` flags.
|
||||
|
||||
### Dynamic Environment Variables
|
||||
|
||||
- `.env` - Environment variables for all environments
|
||||
|
||||
This file contains secrets and environment-specific values that should not be committed to version control.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
1. **Create your .env file**
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Create environment-specific files as needed:
|
||||
- `.env` - Default development environment
|
||||
- `.env.production` - Production environment
|
||||
- `.env.stage` - Staging environment
|
||||
2. **Add your environment variables**
|
||||
|
||||
## How it Works
|
||||
```bash
|
||||
# .env
|
||||
GOOGLE_DRIVE_TOKEN=your-token-here
|
||||
DROPBOX_API_KEY=your-api-key-here
|
||||
```
|
||||
|
||||
The build process automatically generates `src/environments/environment.ts` from the appropriate `.env` file using `tools/generate-env.js`.
|
||||
3. **Access environment variables in your code**
|
||||
|
||||
## Available Variables
|
||||
```typescript
|
||||
// Direct access (works everywhere)
|
||||
const googleToken = process.env.GOOGLE_DRIVE_TOKEN;
|
||||
|
||||
- `NODE_ENV` - Environment name (development, production, staging)
|
||||
- `PRODUCTION` - Set to "true" for production builds
|
||||
- `STAGE` - Set to "true" for staging builds
|
||||
// Or using utility functions
|
||||
import { getEnv, getEnvOrDefault } from './app/util/env';
|
||||
|
||||
## Usage
|
||||
const googleToken = getEnv('GOOGLE_DRIVE_TOKEN');
|
||||
const dropboxKey = getEnvOrDefault('DROPBOX_API_KEY', 'default-key');
|
||||
```
|
||||
|
||||
### Development
|
||||
## Running the Application
|
||||
|
||||
The npm scripts automatically load the `.env` file:
|
||||
|
||||
```bash
|
||||
# Development
|
||||
npm run startFrontend
|
||||
```
|
||||
|
||||
### Production
|
||||
|
||||
```bash
|
||||
# Production configuration
|
||||
npm run startFrontend:prod
|
||||
# or
|
||||
npm run buildFrontend:prod:es6
|
||||
|
||||
# Staging configuration
|
||||
npm run startFrontend:stage
|
||||
```
|
||||
|
||||
### Staging
|
||||
Note: All commands use the same `.env` file. The difference between environments is controlled by the Angular configuration (production/stage flags).
|
||||
|
||||
## Build Commands
|
||||
|
||||
Build commands also load the appropriate environment:
|
||||
|
||||
```bash
|
||||
npm run startFrontend:stage
|
||||
# or
|
||||
# Production build
|
||||
npm run buildFrontend:prod:es6
|
||||
|
||||
# Staging build
|
||||
npm run buildFrontend:stage:es6
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **dotenv-run** loads variables from `.env` file before running Angular commands
|
||||
2. **webpack DefinePlugin** injects these variables as `process.env.*` at build time
|
||||
3. **Pure utility functions** in `src/app/util/env.ts` provide helper functions (optional)
|
||||
4. **TypeScript declarations** in `src/types/environment.d.ts` provide type safety
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Never commit `.env` files to version control
|
||||
- Secrets are injected at build time, not included in source code
|
||||
- Access variables directly via `process.env` or use the utility functions
|
||||
- Add new environment variables to both `.env.example` and `src/types/environment.d.ts`
|
||||
|
||||
## Adding New Environment Variables
|
||||
|
||||
1. Add the variable to your `.env` files
|
||||
2. Update `tools/generate-env.js` to include the new variable in the generated environment
|
||||
3. Use the variable in your code via `environment.yourVariable`
|
||||
1. Add to `.env.example` as documentation:
|
||||
|
||||
## Important Notes
|
||||
```bash
|
||||
NEW_API_KEY=your-api-key-here
|
||||
```
|
||||
|
||||
- The `src/environments/environment.ts` file is auto-generated and should not be edited directly
|
||||
- All `.env` files are gitignored for security
|
||||
- Always use `.env.example` as a template for required variables
|
||||
2. Add TypeScript declaration in `src/types/environment.d.ts`:
|
||||
|
||||
```typescript
|
||||
interface ProcessEnv {
|
||||
NEW_API_KEY?: string;
|
||||
// ... other variables
|
||||
}
|
||||
```
|
||||
|
||||
3. Use in your code:
|
||||
|
||||
```typescript
|
||||
// Direct access
|
||||
const apiKey = process.env.NEW_API_KEY;
|
||||
|
||||
// Or with utility function
|
||||
import { getEnv } from './app/util/env';
|
||||
const apiKey = getEnv('NEW_API_KEY');
|
||||
```
|
||||
|
|
|
|||
18
package.json
18
package.json
|
|
@ -32,12 +32,12 @@
|
|||
"buildAllElectron:noTests:prod": "npm run lint && npm run buildFrontend:prod:es6 && npm run electron:build",
|
||||
"buildAllElectron:prod": "npm run preCheck && npm run buildFrontend:prod:es6 && npm run electron:build",
|
||||
"buildAllElectron:stage": "npm run preCheck && npm run buildFrontend:stage:es6 && npm run electron:build",
|
||||
"buildFrontend:prod:es6": "npm run prebuild && node tools/generate-env.js production && cross-env BROWSERSLIST_ENV='modern' ng build --configuration production && npm run removeWOFF1",
|
||||
"buildFrontend:prod:watch": "npm run prebuild && node tools/generate-env.js production && ng build --configuration production --watch",
|
||||
"buildFrontend:prodWeb": "npm run prebuild && node tools/generate-env.js production && ng build --configuration productionWeb",
|
||||
"buildFrontend:stage:es6": "npm run prebuild && node tools/generate-env.js stage && cross-env BROWSERSLIST_ENV='modern' ng build --configuration stage && npm run removeWOFF1",
|
||||
"buildFrontend:stageWeb": "npm run prebuild && node tools/generate-env.js stage && ng build --configuration stageWeb",
|
||||
"buildFrontend:stageWeb:unminified": "npm run prebuild && node tools/generate-env.js stage && ng build --configuration stageWeb --optimization=false --aot=false",
|
||||
"buildFrontend:prod:es6": "npm run prebuild && dotenv-run -- cross-env BROWSERSLIST_ENV='modern' ng build --configuration production && npm run removeWOFF1",
|
||||
"buildFrontend:prod:watch": "npm run prebuild && ng build --configuration production --watch",
|
||||
"buildFrontend:prodWeb": "npm run prebuild && dotenv-run -- ng build --configuration productionWeb",
|
||||
"buildFrontend:stage:es6": "npm run prebuild && dotenv-run -- cross-env BROWSERSLIST_ENV='modern' ng build --configuration stage && npm run removeWOFF1",
|
||||
"buildFrontend:stageWeb": "npm run prebuild && dotenv-run -- ng build --configuration stageWeb",
|
||||
"buildFrontend:stageWeb:unminified": "npm run prebuild && ng build --configuration stageWeb --optimization=false --aot=false",
|
||||
"dist": "npm run buildAllElectron:prod && electron-builder",
|
||||
"dist:android": "npm run buildFrontend:stageWeb:unminified && npm run sync:android && npm run assemble:android:stage && echo 'Staging Android APK generated at android/app/build/outputs/apk/debug/'",
|
||||
"dist:android:prod": "npm run buildFrontend:prodWeb && npm run sync:android && npm run assemble:android:prod && echo 'Production Android APK generated at android/app/build/outputs/apk/release/'",
|
||||
|
|
@ -85,9 +85,9 @@
|
|||
"removeWOFF1": "node ./tools/remove-woff.js",
|
||||
"serveProd": "ng serve --configuration production",
|
||||
"start": "npm run electron:build && cross-env NODE_ENV=DEV electron .",
|
||||
"startFrontend": "node tools/generate-env.js development && ng serve",
|
||||
"startFrontend:prod": "node tools/generate-env.js production && ng serve --configuration production",
|
||||
"startFrontend:stage": "node tools/generate-env.js stage && ng serve --configuration stage",
|
||||
"startFrontend": "dotenv-run -- ng serve",
|
||||
"startFrontend:prod": "dotenv-run -- ng serve --configuration production",
|
||||
"startFrontend:stage": "dotenv-run -- ng serve --configuration stage",
|
||||
"sync:android": "npx cap sync android",
|
||||
"stats": "ng build --configuration production --source-map --stats-json && npx esbuild-visualizer --metadata .tmp/angular-dist/stats.json && xdg-open stats.html",
|
||||
"test": "cross-env TZ='Europe/Berlin' ng test --watch=false && npm run test:tz:ci",
|
||||
|
|
|
|||
59
src/app/core/env/env.service.ts
vendored
Normal file
59
src/app/core/env/env.service.ts
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
declare const process: {
|
||||
env: {
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class EnvService {
|
||||
/**
|
||||
* Get an environment variable value.
|
||||
* Returns undefined if the variable is not set.
|
||||
*/
|
||||
get(key: string): string | undefined {
|
||||
// Check if we're in a browser environment with process.env injected by webpack
|
||||
if (typeof process !== 'undefined' && process.env) {
|
||||
return process.env[key];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an environment variable value with a default fallback.
|
||||
*/
|
||||
getOrDefault(key: string, defaultValue: string): string {
|
||||
return this.get(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an environment variable is set.
|
||||
*/
|
||||
has(key: string): boolean {
|
||||
return this.get(key) !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an environment variable as a boolean.
|
||||
* Returns true for 'true', '1', 'yes', 'on' (case-insensitive).
|
||||
* Returns false for any other value or if not set.
|
||||
*/
|
||||
getBoolean(key: string): boolean {
|
||||
const value = this.get(key)?.toLowerCase();
|
||||
return value === 'true' || value === '1' || value === 'yes' || value === 'on';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an environment variable as a number.
|
||||
* Returns undefined if the value is not a valid number.
|
||||
*/
|
||||
getNumber(key: string): number | undefined {
|
||||
const value = this.get(key);
|
||||
if (value === undefined) return undefined;
|
||||
const num = Number(value);
|
||||
return isNaN(num) ? undefined : num;
|
||||
}
|
||||
}
|
||||
43
src/app/util/env.ts
Normal file
43
src/app/util/env.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Pure functions for accessing environment variables.
|
||||
* These can be used anywhere in the codebase, including outside Angular context.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get an environment variable value.
|
||||
* Returns undefined if the variable is not set.
|
||||
*/
|
||||
export const getEnv = (key: string): string | undefined => {
|
||||
if (typeof process !== 'undefined' && process.env) {
|
||||
return process.env[key];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an environment variable value with a default fallback.
|
||||
*/
|
||||
export const getEnvOrDefault = (key: string, defaultValue: string): string => {
|
||||
return getEnv(key) ?? defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an environment variable as a boolean.
|
||||
* Returns true for 'true', '1', 'yes', 'on' (case-insensitive).
|
||||
* Returns false for any other value or if not set.
|
||||
*/
|
||||
export const getEnvBoolean = (key: string): boolean => {
|
||||
const value = getEnv(key)?.toLowerCase();
|
||||
return value === 'true' || value === '1' || value === 'yes' || value === 'on';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an environment variable as a number.
|
||||
* Returns undefined if the value is not a valid number.
|
||||
*/
|
||||
export const getEnvNumber = (key: string): number | undefined => {
|
||||
const value = getEnv(key);
|
||||
if (value === undefined) return undefined;
|
||||
const num = Number(value);
|
||||
return isNaN(num) ? undefined : num;
|
||||
};
|
||||
8
src/environments/environment.ts
Normal file
8
src/environments/environment.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// This file is the default environment configuration for development
|
||||
import pkg from '../../package.json';
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
stage: false,
|
||||
version: pkg.version,
|
||||
};
|
||||
22
src/types/environment.d.ts
vendored
Normal file
22
src/types/environment.d.ts
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Type declarations for environment variables.
|
||||
* Add your environment variable types here.
|
||||
*/
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
// Example environment variables - add your own here
|
||||
GOOGLE_DRIVE_TOKEN?: string;
|
||||
DROPBOX_API_KEY?: string;
|
||||
WEBDAV_URL?: string;
|
||||
WEBDAV_USERNAME?: string;
|
||||
WEBDAV_PASSWORD?: string;
|
||||
|
||||
// Add more environment variables as needed
|
||||
[key: string]: string | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure process is available globally in the browser
|
||||
declare const process: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
};
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Get environment name from command line or default to development
|
||||
const envName = process.argv[2] || 'development';
|
||||
|
||||
// Map environment names to .env files
|
||||
const envFileMap = {
|
||||
development: '.env',
|
||||
production: '.env.production',
|
||||
stage: '.env.stage',
|
||||
};
|
||||
|
||||
// Load environment variables manually
|
||||
const envFile = envFileMap[envName] || '.env';
|
||||
const envPath = path.join(process.cwd(), envFile);
|
||||
const envContent = fs.readFileSync(envPath, 'utf8');
|
||||
const env = {};
|
||||
|
||||
// Parse .env file
|
||||
envContent.split('\n').forEach((line) => {
|
||||
line = line.trim();
|
||||
if (line && !line.startsWith('#')) {
|
||||
const [key, value] = line.split('=');
|
||||
if (key && value) {
|
||||
env[key.trim()] = value.trim();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Generate environment.ts content
|
||||
const envTsContent = `// This file is auto-generated by tools/generate-env.js
|
||||
// Do not modify directly
|
||||
import pkg from '../../package.json';
|
||||
|
||||
export const environment = {
|
||||
production: ${env.PRODUCTION === 'true'},
|
||||
stage: ${env.STAGE === 'true'},
|
||||
version: pkg.version,
|
||||
};
|
||||
`;
|
||||
|
||||
// Write to environment.ts
|
||||
const targetPath = path.join(__dirname, '../src/environments/environment.ts');
|
||||
fs.writeFileSync(targetPath, envTsContent);
|
||||
|
||||
console.log(`✅ Generated environment.ts from ${envFile}`);
|
||||
Loading…
Add table
Add a link
Reference in a new issue