{"id":31650730,"url":"https://github.com/pom4h/tsops","last_synced_at":"2026-05-01T11:01:17.524Z","repository":{"id":316407221,"uuid":"1061929554","full_name":"Pom4H/tsops","owner":"Pom4H","description":"TypeScript-first Kubernetes toolkit","archived":false,"fork":false,"pushed_at":"2026-04-24T21:56:25.000Z","size":4217,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T23:32:39.840Z","etag":null,"topics":["cli","devops","docker","k8s","typescript"],"latest_commit_sha":null,"homepage":"https://pom4h.github.io/tsops/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Pom4H.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-09-22T15:17:26.000Z","updated_at":"2026-04-24T21:56:01.000Z","dependencies_parsed_at":"2025-10-24T11:10:50.634Z","dependency_job_id":"3e8572f9-3453-4867-840a-b3508a30c922","html_url":"https://github.com/Pom4H/tsops","commit_stats":null,"previous_names":["pom4h/tsops"],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/Pom4H/tsops","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pom4H%2Ftsops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pom4H%2Ftsops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pom4H%2Ftsops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pom4H%2Ftsops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Pom4H","download_url":"https://codeload.github.com/Pom4H/tsops/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Pom4H%2Ftsops/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32494275,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","devops","docker","k8s","typescript"],"created_at":"2025-10-07T08:36:18.667Z","updated_at":"2026-05-01T11:01:17.450Z","avatar_url":"https://github.com/Pom4H.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tsops\n\nTypeScript-first toolkit for planning, building, and deploying containerized applications.\n\n[![npm version](https://badge.fury.io/js/tsops.svg)](https://www.npmjs.com/package/tsops)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## Quick Start\n\n```bash\nnpm install tsops\n\nor\n\npnpm add tsops\n```\n\nThen create a `tsops.config.ts` file:\n\n```typescript\nimport { defineConfig } from 'tsops'\n\nexport default defineConfig({\n  project: 'orchard',\n\n  namespaces: {\n    dev: { domain: 'dev.example.com', production: false },\n    prod: { domain: 'example.com', production: true }\n  },\n\n  clusters: {\n    platform: {\n      apiServer: 'https://k8s.example.com',\n      context: 'prod',\n      namespaces: ['dev', 'prod']\n    }\n  },\n\n  images: {\n    registry: 'ghcr.io/example',\n    tagStrategy: 'git-sha',\n    includeProjectInName: true\n  },\n\n  secrets: {\n    'api-secrets': ({ production }) =\u003e ({\n      JWT_SECRET: production\n        ? process.env.JWT_SECRET ?? ''\n        : 'dev-secret'\n    })\n  },\n\n  apps: {\n    api: {\n      build: {\n        type: 'dockerfile',\n        context: './apps/api',\n        dockerfile: './apps/api/Dockerfile'\n      },\n      // Simple object format - protocol auto-detects based on domain\n      ingress: ({ domain }) =\u003e ({ domain: `api.${domain}` }),\n      // Or explicit: protocol: production ? 'https' : 'http'\n      ports: [{ name: 'http', port: 80, targetPort: 8080 }],\n      env: ({ production, secret }) =\u003e ({\n        NODE_ENV: production ? 'production' : 'development',\n        JWT_SECRET: secret('api-secrets', 'JWT_SECRET'),\n        STRIPE_API_KEY: secret('api-secrets', 'STRIPE_KEY')\n        // ⚠️ Don't put service URLs here! Use DNS directly in your app:\n        // fetch('http://postgres/api') instead of process.env.POSTGRES_URL\n      })\n    }\n  }\n})\n```\n\nRoot-level secrets and configMaps execute in Node, so read environment variables directly via `process.env` (or your own helper) instead of the app-level `env()` helper.\n\nRun commands:\n\n```bash\n# Plan what will be deployed\ntsops plan\ntsops plan --namespace prod --app api\n\n# Build Docker images\ntsops build\ntsops build --app api\n\n# Deploy applications\ntsops deploy --namespace prod\ntsops deploy --namespace prod --app api\n```\n\n`tsops plan` resolves your configuration, validates shared resources once, previews per-app manifest updates with diffs, and lists orphaned resources that would be removed. Add `--dry-run` to inspect without invoking Docker or deployment tools. `tsops deploy` reuses that plan, blocks on missing secret values, applies manifests atomically, and cleans up orphans at the end.\n\n## Key Principle\n\n**tsops embraces Service Discovery.** Don't hardcode service URLs in ENV variables—use internal DNS directly in your application code. ENV is for secrets, external APIs, and configuration, not for internal service endpoints.\n\n## Features\n\n- 🎯 **Type-safe configuration** - Full TypeScript support with autocompletion\n- 📋 **Diff-first planning** - Validate namespaces/secrets/configMaps once and preview manifest updates\n- 🐳 **Docker integration** - Build and push images automatically\n- 🌐 **Manifests \u0026 networking** - Generate deployments, services, and ingress from a single definition\n- 🔒 **Secret validation** - Catch placeholders and missing keys before deploy\n- 🧹 **Orphan cleanup** - Detect and delete resources not declared in code\n- 🔗 **Service Discovery First** - Encourages proper internal DNS patterns\n\n## Documentation\n\nFull documentation is available at [GitHub Pages](https://pom4h.github.io/tsops/)\n\n## Packages\n\nThis is a monorepo containing:\n\n- **`tsops`** – CLI and configuration helper exports\n- **`@tsops/core`** – Core library with programmatic API\n- **`@tsops/node`** – Node-specific adapters and `createNodeTsOps`\n- **`@tsops/k8`** – Manifest builders for Kubernetes\n\n## Development\n\n```bash\n# Install dependencies\npnpm install\n\n# Build all packages\npnpm build\n\n# Run in watch mode\npnpm build:watch\n\n# Lint\npnpm lint\n\n# Run docs locally\npnpm docs:dev\n```\n\n## Service Discovery (Important!)\n\n**⚠️ Anti-pattern: Do NOT use ENV variables for service-to-service communication endpoints.**\n\nHardcoding service URLs in ENV breaks the Service Discovery pattern and creates tight coupling. Instead, use the runtime config helpers in your application code.\n\n### ❌ Wrong: Hardcoding in ENV\n\n```typescript\n// DON'T DO THIS\nenv: () =\u003e ({\n  BACKEND_URL: 'http://backend:3000'  // ❌ Hardcoded!\n})\n\n// In your app:\nfetch(process.env.BACKEND_URL)  // ❌ Not type-safe, breaks namespace switching\n```\n\n**Problems:**\n- Breaks Service Discovery\n- Not type-safe\n- Doesn't respect `TSOPS_NAMESPACE`\n- Hardcoded ports and hostnames\n\n### ✅ Correct: Use runtime config\n\n```typescript\n// In your app code:\nimport config from './tsops.config'\n\n// Short DNS (same namespace)\nconst BACKEND_URL = config.url('backend', 'service')  // http://backend\n// Or full cluster DNS (cross-namespace safe):\nconst BACKEND_URL = config.url('backend', 'cluster')  // http://backend.prod.svc.cluster.local\n\nfetch(`${BACKEND_URL}/api/data`)  // ✅ Type-safe, namespace-aware!\n```\n\n**Benefits:**\n- Type-safe (compile-time checking)\n- Respects `TSOPS_NAMESPACE` environment variable\n- Single source of truth\n- Platform handles service resolution automatically\n- Zero-downtime deployments work correctly\n\n### When to use ENV\n\nUse ENV variables **only** for:\n- **Secrets**: API keys, passwords, tokens\n- **External services**: Third-party APIs, databases outside the cluster\n- **Feature flags**: Application behavior configuration\n- **Build-time values**: Version numbers, git commits\n\n```typescript\nenv: ({ secret, env }) =\u003e ({\n  JWT_SECRET: secret('api-secrets', 'JWT_SECRET'),    // ✅ Secret\n  STRIPE_API_KEY: secret('payment', 'STRIPE_KEY'),    // ✅ Secret\n  EXTERNAL_API: 'https://api.external.com',           // ✅ External service\n  DATABASE_HOST: env('EXTERNAL_DB_HOST'),             // ✅ External database\n  LOG_LEVEL: 'info',                                  // ✅ Config\n  GIT_SHA: env('GIT_SHA', 'dev')                      // ✅ Build-time value\n})\n```\n\n**Never** put internal service URLs in ENV - use `config.url()` in runtime instead.\n\n## Runtime Helpers\n\nImport the compiled config in your application to reuse resolved values at runtime. The active namespace is selected via the `TSOPS_NAMESPACE` environment variable (defaults to the first namespace).\n\n```typescript\nimport config from './tsops.config.js'\n\nconst nodeEnv = config.env('api', 'NODE_ENV')\nconst external = config.url('api', 'ingress') // https://api.dev.example.com (public endpoint)\n```\n\n**Note:** Runtime helpers are primarily for getting public ingress URLs, not for service-to-service communication (use DNS directly for that).\n\n## License\n\nMIT © Roman Popov\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpom4h%2Ftsops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpom4h%2Ftsops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpom4h%2Ftsops/lists"}