SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/CodySwannGT/lisa --skill expo-env-config명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
any non-trivial request —…
This skill should be used for any non-trivial request — features, bugs, stories, epics, spikes, or multi-step tasks. It accepts a ticket URL (Jira, Linear, GitHub), a file path containing a spec, or a plain-text prompt. It assembles an agent team, breaks the work into structured tasks, and manages the full lifecycle from research through implementation, code review, deploy, and empirical verification.
| name | expo-env-config |
| description | creating, modifying, or… |
This skill enforces type-safe, validated environment variable management for Expo/React Native using Zod schemas. Environment variables are validated at build time and provide full TypeScript inference, regardless of their source (.env files, EAS Build secrets, CI/CD pipelines, or command-line exports).
Unlike NestJS's @nestjs/config, Expo has no official type-safe env solution. The Zod validation pattern provides:
z.infer<typeof schema>.env, EAS secrets, CI variablessrc/lib/env.ts)import { z } from "zod";
/**
* Environment variable schema with Zod validation.
* Variables are validated at module load time.
*/
const envSchema = z.object({
// Required variables
EXPO_PUBLIC_API_URL: z.string().url(),
EXPO_PUBLIC_APP_ENV: z.enum(["development", "staging", "production"]),
// Optional variables with defaults
EXPO_PUBLIC_SENTRY_DSN: z.string().optional(),
EXPO_PUBLIC_FEATURE_FLAG: z
.string()
.transform(v => v === "true")
.default("false"),
});
/**
* Validated environment configuration.
* Throws at module load if validation fails.
*/
export const env = envSchema.parse(process.env);
/**
* Type-safe environment configuration.
*/
export type Env = z.infer<typeof envSchema>;
import { env } from "@/lib/env";
// Full autocomplete and type safety
const apiUrl = env.EXPO_PUBLIC_API_URL; // string
const isDev = env.EXPO_PUBLIC_APP_ENV === "development"; // boolean
app.config.ts)For variables needed during the build process, validate in app.config.ts:
// app.config.ts
const { z } = require("zod");
const buildEnvSchema = z.object({
EXPO_PUBLIC_API_URL: z.string().url(),
EXPO_PUBLIC_APP_ENV: z.enum(["development", "staging", "production"]),
// Build-only secrets (not exposed to client)
SENTRY_AUTH_TOKEN: z.string().optional(),
});
// Throws during `eas build` if invalid
const env = buildEnvSchema.parse(process.env);
module.exports = {
name: "MyApp",
slug: "my-app",
extra: {
apiUrl: env.EXPO_PUBLIC_API_URL,
appEnv: env.EXPO_PUBLIC_APP_ENV,
},
};
Environment variables arrive in process.env from multiple sources:
| Source | When Available | How Set |
|---|---|---|
.env.local | Local dev | Expo CLI auto-loads |
.env.development | Local dev | Copied to .env.local via npm script |
eas.json env | EAS Build | build.production.env section |
| EAS Secrets | EAS Build | eas secret:create |
| CI Variables | CI builds | GitHub Actions / GitLab CI settings |
The Zod pattern validates process.env directly - it doesn't care how variables got there.
jest.setup.local.ts)// Mock the env module for all tests
jest.mock("@/lib/env", () => ({
env: {
EXPO_PUBLIC_API_URL: "https://test.example.com",
EXPO_PUBLIC_APP_ENV: "development",
EXPO_PUBLIC_SENTRY_DSN: undefined,
EXPO_PUBLIC_FEATURE_FLAG: false,
},
}));
import { env } from "@/lib/env";
jest.mock("@/lib/env");
describe("ProductionFeature", () => {
beforeEach(() => {
(env as jest.Mocked<typeof env>).EXPO_PUBLIC_APP_ENV = "production";
});
it("should behave differently in production", () => {
// Test production-specific behavior
});
});
This pattern is enforced by ESLint's no-restricted-syntax rule in eslint.config.mjs:
"no-restricted-syntax": [
"error",
{
selector: "MemberExpression[object.name='process'][property.name='env']",
message: "Direct process.env access is forbidden. Import { env } from '@/lib/env' instead.",
},
],
Exceptions (files allowed to use process.env):
lib/env.ts - The env validation module itselfapp.config.ts - Expo build configcodegen.ts - GraphQL codegen configplaywright.config.ts - E2E test configlighthouserc.js - Lighthouse CI configVariables without this prefix are not available in client code:
// CORRECT - available in client
EXPO_PUBLIC_API_URL=https://api.example.com
// INCORRECT - only available at build time
API_URL=https://api.example.com
Always use the validated env object:
// CORRECT - type-safe, validated
import { env } from "@/lib/env";
const url = env.EXPO_PUBLIC_API_URL;
// INCORRECT - untyped, unvalidated
const url = process.env.EXPO_PUBLIC_API_URL;
Validation happens at module load. If a required variable is missing, the app fails immediately with a clear error rather than at runtime.
Environment variables are always strings. Use Zod transforms:
const envSchema = z.object({
// Boolean from string
EXPO_PUBLIC_DEBUG: z
.string()
.transform(v => v === "true")
.default("false"),
// Number from string
EXPO_PUBLIC_TIMEOUT_MS: z
.string()
.transform(v => parseInt(v, 10))
.default("5000"),
// Array from comma-separated string
EXPO_PUBLIC_ALLOWED_HOSTS: z
.string()
.transform(v => v.split(",").map(s => s.trim()))
.default(""),
});
Keep sensitive build-time variables out of the client schema:
// Client variables (embedded in JS bundle)
const clientSchema = z.object({
EXPO_PUBLIC_API_URL: z.string().url(),
});
// Build-only variables (NOT in bundle)
const buildSchema = z.object({
SENTRY_AUTH_TOKEN: z.string(),
EAS_PROJECT_ID: z.string(),
});
src/
lib/
env.ts # Main env schema and exports
app.config.ts # Build-time validation (if needed)
.env.localhost # Local development (git-ignored)
.env.development # Development environment
.env.staging # Staging environment
.env.production # Production environment
For comprehensive patterns, transforms, and testing examples:
// WRONG - untyped, could be undefined
const Component = () => {
const url = process.env.EXPO_PUBLIC_API_URL;
// url is string | undefined, no validation
};
// CORRECT - validated and typed
import { env } from "@/lib/env";
const Component = () => {
const url = env.EXPO_PUBLIC_API_URL;
// url is string, guaranteed to be valid URL
};
// WRONG - skipping validation
export const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:3000";
// CORRECT - always validate
const envSchema = z.object({
EXPO_PUBLIC_API_URL: z.string().url().default("http://localhost:3000"),
});
export const { EXPO_PUBLIC_API_URL: API_URL } = envSchema.parse(process.env);
// WRONG - secrets exposed in client bundle
EXPO_PUBLIC_API_SECRET=super-secret-key
// CORRECT - secrets only at build time, passed securely
SENTRY_AUTH_TOKEN=secret # Build-only, not in bundle
When adding or modifying environment variables:
EXPO_PUBLIC_ (if needed in client code)src/lib/env.tsjest.setup.local.ts.env.example or .env.development.env files