From 6183692a9795892da3777f5852b175ffde1d33e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 11:14:31 +0000 Subject: [PATCH 1/6] Initial plan From f3ec150ea9839620ab75d4bba7f5fda10722bb63 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 11:26:45 +0000 Subject: [PATCH 2/6] Add Systems Thinking content, categorized init wizard, and best practices per stage Co-authored-by: chefgs <7605658+chefgs@users.noreply.github.com> --- cli/devopsos.py | 188 ++++++++++++++++++++++++++++++++---------- cli/process_first.py | 129 +++++++++++++++++++++++++++-- docs/PROCESS-FIRST.md | 113 ++++++++++++++++++++++++- 3 files changed, 373 insertions(+), 57 deletions(-) diff --git a/cli/devopsos.py b/cli/devopsos.py index a744910..4f45014 100644 --- a/cli/devopsos.py +++ b/cli/devopsos.py @@ -21,6 +21,7 @@ class ProcessFirstSection(str, enum.Enum): what = "what" mapping = "mapping" tips = "tips" + best_practices = "best_practices" all = "all" @@ -32,40 +33,123 @@ def init( ): """Interactive project initializer.""" typer.echo("Welcome to DevOps-OS Init Wizard!") + typer.echo("Tools are grouped by Process-First DevOps principles (Systems Thinking).\n") + + # ── Canonical tool lists ────────────────────────────────────────────── + ALL_LANGUAGES = ["python", "java", "node", "ruby", "csharp", "php", "rust", + "typescript", "kotlin", "c", "cpp", "javascript", "go"] + ALL_CICD = ["docker", "terraform", "kubectl", "helm", "github_actions", "jenkins"] + ALL_KUBERNETES = ["k9s", "kustomize", "argocd_cli", "lens", "kubeseal", + "flux", "kind", "minikube", "openshift_cli"] + ALL_BUILD_TOOLS = ["gradle", "maven", "ant", "make", "cmake"] + ALL_CODE_ANALYSIS = ["sonarqube", "checkstyle", "pmd", "eslint", "pylint"] + ALL_DEVOPS_TOOLS = ["nexus", "prometheus", "grafana", "elk", "jenkins"] - categories = { - "languages": ["python", "java", "node", "ruby", "csharp", "php", "rust", "typescript", "kotlin", "c", "cpp", "javascript", "go"], - "cicd": ["docker", "terraform", "kubectl", "helm", "github_actions", "jenkins"], - "kubernetes": ["k9s", "kustomize", "argocd_cli", "lens", "kubeseal", "flux", "kind", "minikube", "openshift_cli"], - "build_tools": ["gradle", "maven", "ant", "make", "cmake"], - "code_analysis": ["sonarqube", "checkstyle", "pmd", "eslint", "pylint"], - "devops_tools": ["nexus", "prometheus", "grafana", "elk", "jenkins"] - } versions_defaults = { "python": "3.11", "java": "17", "node": "20", "go": "1.21", "nexus": "3.50.0", "prometheus": "2.45.0", "grafana": "10.0.0", "k9s": "0.29.1", "argocd": "2.8.4", "flux": "2.1.2", "kustomize": "5.2.1", "jenkins": "2.440.1" } - config = {} - selected_versions = {} - selected_options = {} - for cat, opts in categories.items(): - selected = inquirer.checkbox(message=f"Select {cat.replace('_', ' ').title()}:", choices=opts).execute() - selected_options[cat] = selected - # Now build config with True/False for each option - for cat, opts in categories.items(): - config[cat] = {opt: (opt in selected_options[cat]) for opt in opts} - # Prompt for version only for selected - if cat == "languages" or cat == "devops_tools": - for opt in selected_options[cat]: - if opt in versions_defaults: - selected_versions[opt] = inquirer.text(message=f"{opt.title()} version:", default=versions_defaults[opt]).execute() - if cat == "kubernetes": - for opt in selected_options[cat]: - if opt in ["k9s", "argocd_cli", "flux", "kustomize"]: - vkey = opt if opt != "argocd_cli" else "argocd" - selected_versions[vkey] = inquirer.text(message=f"{opt.title()} version:", default=versions_defaults.get(vkey, "")).execute() - config["versions"] = selected_versions + + # ── Wizard groups aligned with Process-First DevOps principles ──────── + # Each group maps to a DevOps stage in the Systems Thinking value stream. + wizard_groups = { + "Languages": { + "choices": ALL_LANGUAGES, + "description": "Programming languages for your project", + }, + "Build Tools [BUILD stage]": { + "choices": ["docker", "gradle", "maven", "ant", "make", "cmake", "nexus"], + "description": "Tools to compile, package, and store build artifacts", + }, + "Test & Quality [TEST stage]": { + "choices": ["sonarqube", "checkstyle", "pmd", "eslint", "pylint"], + "description": "Static analysis and quality gates to enforce standards early", + }, + "IaC & Infrastructure [IaC stage]": { + "choices": ["terraform", "kubectl", "helm", "kustomize"], + "description": "Infrastructure as Code tools for reproducible environments", + }, + "Deploy & GitOps [DEPLOY stage]": { + "choices": ["github_actions", "jenkins", "argocd_cli", "flux"], + "description": "CI/CD pipelines and GitOps delivery tools", + }, + "SRE & Monitoring [SRE/MONITORING stage]": { + "choices": ["prometheus", "grafana", "elk", "k9s"], + "description": "Observability stack: metrics, dashboards, and logs", + }, + "Security & Kubernetes Dev [SECURITY stage]": { + "choices": ["kubeseal", "lens", "kind", "minikube", "openshift_cli"], + "description": "Secret management, security scanning, and local K8s dev tools", + }, + } + + selected_by_group: dict = {} + selected_versions: dict = {} + + for group_label, group_info in wizard_groups.items(): + typer.echo(f"\n 📌 {group_info['description']}") + selected = inquirer.checkbox( + message=f"Select {group_label}:", + choices=group_info["choices"], + ).execute() + selected_by_group[group_label] = selected + + # ── Version prompts ─────────────────────────────────────────────────── + all_selected = {tool for tools in selected_by_group.values() for tool in tools} + for tool in all_selected: + vkey = "argocd" if tool == "argocd_cli" else tool + if vkey in versions_defaults: + selected_versions[vkey] = inquirer.text( + message=f"{tool.title()} version:", + default=versions_defaults[vkey], + ).execute() + + # ── Map wizard selections back to legacy JSON structure ─────────────── + # Keep the devcontainer.env.json keys identical to scaffold_devcontainer + # output for backward compatibility. + def _sel(group): return selected_by_group.get(group, []) + + build_sel = _sel("Build Tools [BUILD stage]") + test_sel = _sel("Test & Quality [TEST stage]") + iac_sel = _sel("IaC & Infrastructure [IaC stage]") + deploy_sel = _sel("Deploy & GitOps [DEPLOY stage]") + sre_sel = _sel("SRE & Monitoring [SRE/MONITORING stage]") + security_sel = _sel("Security & Kubernetes Dev [SECURITY stage]") + lang_sel = _sel("Languages") + + config = { + "languages": {opt: opt in lang_sel for opt in ALL_LANGUAGES}, + "cicd": { + "docker": "docker" in build_sel, + "terraform": "terraform" in iac_sel, + "kubectl": "kubectl" in iac_sel, + "helm": "helm" in iac_sel, + "github_actions": "github_actions" in deploy_sel, + "jenkins": "jenkins" in deploy_sel, + }, + "kubernetes": { + "k9s": "k9s" in sre_sel, + "kustomize": "kustomize" in iac_sel, + "argocd_cli": "argocd_cli" in deploy_sel, + "lens": "lens" in security_sel, + "kubeseal": "kubeseal" in security_sel, + "flux": "flux" in deploy_sel, + "kind": "kind" in security_sel, + "minikube": "minikube" in security_sel, + "openshift_cli": "openshift_cli" in security_sel, + }, + "build_tools": {opt: opt in build_sel for opt in ALL_BUILD_TOOLS}, + "code_analysis": {opt: opt in test_sel for opt in ALL_CODE_ANALYSIS}, + "devops_tools": { + "nexus": "nexus" in build_sel, + "prometheus": "prometheus" in sre_sel, + "grafana": "grafana" in sre_sel, + "elk": "elk" in sre_sel, + "jenkins": "jenkins" in deploy_sel, + }, + "versions": selected_versions, + } # Review step: show config and confirm typer.echo("\nReview your configuration:") @@ -88,39 +172,51 @@ def init( build_args = {} # Languages lang_map = { - "python": "INSTALL_PYTHON", "java": "INSTALL_JAVA", "node": "INSTALL_JS", "ruby": "INSTALL_RUBY", - "csharp": "INSTALL_CSHARP", "php": "INSTALL_PHP", "rust": "INSTALL_RUST", "typescript": "INSTALL_TYPESCRIPT", - "kotlin": "INSTALL_KOTLIN", "c": "INSTALL_C", "cpp": "INSTALL_CPP", "javascript": "INSTALL_JAVASCRIPT", "go": "INSTALL_GO" + "python": "INSTALL_PYTHON", "java": "INSTALL_JAVA", "node": "INSTALL_JS", + "ruby": "INSTALL_RUBY", "csharp": "INSTALL_CSHARP", "php": "INSTALL_PHP", + "rust": "INSTALL_RUST", "typescript": "INSTALL_TYPESCRIPT", + "kotlin": "INSTALL_KOTLIN", "c": "INSTALL_C", "cpp": "INSTALL_CPP", + "javascript": "INSTALL_JAVASCRIPT", "go": "INSTALL_GO" } for lang, arg in lang_map.items(): build_args[arg] = str(config["languages"].get(lang, False)).lower() # CICD cicd_map = { - "docker": "INSTALL_DOCKER", "terraform": "INSTALL_TERRAFORM", "kubectl": "INSTALL_KUBECTL", "helm": "INSTALL_HELM", "github_actions": "INSTALL_GITHUB_ACTIONS", "jenkins": "INSTALL_JENKINS" + "docker": "INSTALL_DOCKER", "terraform": "INSTALL_TERRAFORM", + "kubectl": "INSTALL_KUBECTL", "helm": "INSTALL_HELM", + "github_actions": "INSTALL_GITHUB_ACTIONS", "jenkins": "INSTALL_JENKINS" } for tool, arg in cicd_map.items(): build_args[arg] = str(config["cicd"].get(tool, False)).lower() # Kubernetes k8s_map = { - "k9s": "INSTALL_K9S", "kustomize": "INSTALL_KUSTOMIZE", "argocd_cli": "INSTALL_ARGOCD_CLI", "lens": "INSTALL_LENS", "kubeseal": "INSTALL_KUBESEAL", "flux": "INSTALL_FLUX", "kind": "INSTALL_KIND", "minikube": "INSTALL_MINIKUBE", "openshift_cli": "INSTALL_OPENSHIFT_CLI" + "k9s": "INSTALL_K9S", "kustomize": "INSTALL_KUSTOMIZE", + "argocd_cli": "INSTALL_ARGOCD_CLI", "lens": "INSTALL_LENS", + "kubeseal": "INSTALL_KUBESEAL", "flux": "INSTALL_FLUX", + "kind": "INSTALL_KIND", "minikube": "INSTALL_MINIKUBE", + "openshift_cli": "INSTALL_OPENSHIFT_CLI" } for tool, arg in k8s_map.items(): build_args[arg] = str(config["kubernetes"].get(tool, False)).lower() # Build tools build_map = { - "gradle": "INSTALL_GRADLE", "maven": "INSTALL_MAVEN", "ant": "INSTALL_ANT", "make": "INSTALL_MAKE", "cmake": "INSTALL_CMAKE" + "gradle": "INSTALL_GRADLE", "maven": "INSTALL_MAVEN", "ant": "INSTALL_ANT", + "make": "INSTALL_MAKE", "cmake": "INSTALL_CMAKE" } for tool, arg in build_map.items(): build_args[arg] = str(config["build_tools"].get(tool, False)).lower() # Code analysis analysis_map = { - "sonarqube": "INSTALL_SONARQUBE", "checkstyle": "INSTALL_CHECKSTYLE", "pmd": "INSTALL_PMD", "eslint": "INSTALL_ESLINT", "pylint": "INSTALL_PYLINT" + "sonarqube": "INSTALL_SONARQUBE", "checkstyle": "INSTALL_CHECKSTYLE", + "pmd": "INSTALL_PMD", "eslint": "INSTALL_ESLINT", "pylint": "INSTALL_PYLINT" } for tool, arg in analysis_map.items(): build_args[arg] = str(config["code_analysis"].get(tool, False)).lower() # DevOps tools devops_map = { - "nexus": "INSTALL_NEXUS", "prometheus": "INSTALL_PROMETHEUS", "grafana": "INSTALL_GRAFANA", "elk": "INSTALL_ELK", "jenkins": "INSTALL_JENKINS" + "nexus": "INSTALL_NEXUS", "prometheus": "INSTALL_PROMETHEUS", + "grafana": "INSTALL_GRAFANA", "elk": "INSTALL_ELK", + "jenkins": "INSTALL_JENKINS" } for tool, arg in devops_map.items(): build_args[arg] = str(config["devops_tools"].get(tool, False)).lower() @@ -181,10 +277,11 @@ def process_first_cmd( ProcessFirstSection.all, help=( "Section to display:\n\n" - " 'what' — What Process-First is and its 5 core principles\n\n" - " 'mapping' — How each principle maps to a devopsos scaffold command\n\n" - " 'tips' — AI prompts and book recommendations for DevOps beginners\n\n" - " 'all' — All sections combined (default)" + " 'what' — What Process-First is, 5 core principles + thought leaders\n\n" + " 'mapping' — How each principle maps to a devopsos scaffold command\n\n" + " 'tips' — AI prompts and book recommendations for DevOps beginners\n\n" + " 'best_practices' — Best practices by stage (build/test/iac/deploy/sre/monitoring/security)\n\n" + " 'all' — All sections combined (default)" ), show_choices=True, ), @@ -194,15 +291,16 @@ def process_first_cmd( \b Quick invocation guide: - python -m cli.devopsos process-first # full overview - python -m cli.devopsos process-first --section what # core principles - python -m cli.devopsos process-first --section mapping # tool mapping table - python -m cli.devopsos process-first --section tips # AI prompts for beginners + python -m cli.devopsos process-first # full overview + python -m cli.devopsos process-first --section what # core principles + thought leaders + python -m cli.devopsos process-first --section mapping # tool mapping table + python -m cli.devopsos process-first --section tips # AI prompts for beginners + python -m cli.devopsos process-first --section best_practices # best practices by stage You can also run the module directly: python -m cli.process_first - python -m cli.process_first --section mapping + python -m cli.process_first --section best_practices """ process_first.display(section.value) diff --git a/cli/process_first.py b/cli/process_first.py index 8102e89..4596daa 100644 --- a/cli/process_first.py +++ b/cli/process_first.py @@ -23,6 +23,33 @@ "Tools are only as good as the processes that govern them." — Saravanan Gnanagur, Founder, CloudEngineLabs + Process-First is the Systems Thinking of DevOps. + +""" + +THOUGHT_LEADERS = """\ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + THOUGHT LEADERS ON PROCESS-FIRST & SYSTEMS THINKING +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + Gene Kim, Kevin Behr, and George Spafford + (The Phoenix Project / The DevOps Handbook): + + "The First Way emphasises the performance of the entire system, as opposed + to the performance of a specific silo of work or department." + + They define the "Three Ways" of DevOps, with the First Way focusing on + Systems Thinking — optimising the whole flow from development through + operations to the customer, not just individual components or stages. + + Patrick Debois + (Founder of DevOpsDays): + + As the founder of DevOpsDays, Patrick Debois emphasised the need to fix + the broken, inefficient processes between developers and operations before + introducing new tools. He showed that cultural and process problems, not + technology gaps, are the root cause of slow, unreliable software delivery. + """ WHAT_IS_PROCESS_FIRST = """\ @@ -35,6 +62,9 @@ centre of every engineering decision — before selecting tools, platforms, or frameworks. + It is the Systems Thinking of DevOps: optimise the whole value stream + from development to operations before optimising any individual step. + Core principles: 1. DEFINE before you BUILD @@ -139,20 +169,99 @@ """ +BEST_PRACTICES = """\ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + BEST PRACTICES BY STAGE — SYSTEMS THINKING IN DEVOPS +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + Applying Systems Thinking means defining the right process at each stage + of the value stream before selecting or configuring any tool. + + 🔨 BUILD + ──────── + • Define build standards and conventions before choosing tools + (Docker, Gradle, Maven, Make). + • Standardise base images and dependency management across all teams. + • Enforce reproducible builds with version-pinned dependencies. + • Use an artifact repository (Nexus) to cache, version, and audit + build outputs. + + 🧪 TEST & QUALITY + ───────────────── + • Define quality gates and acceptance criteria before writing tests. + • Automate unit, integration, and end-to-end tests in every pipeline run. + • Enforce code standards with static analysis (SonarQube, Checkstyle, + ESLint, Pylint) as non-negotiable pipeline gates. + • Fail fast: surface test failures early to prevent bad code from + advancing to later stages. + + 🏗️ IaC & INFRASTRUCTURE + ───────────────────────── + • Define infrastructure requirements and constraints before writing + Terraform or Helm code. + • Version-control every infrastructure definition — no manual changes + to production environments. + • Use Kustomize overlays to manage environment-specific configuration + (dev / staging / production) from a single base. + • Detect and alert on infrastructure drift regularly to maintain + the desired state. + + 🚀 DEPLOY & GITOPS + ────────────────── + • Define deployment runbooks and rollback procedures before the first + production release. + • Use GitOps (ArgoCD, Flux) to make deployment intent explicit in Git + and fully auditable. + • Implement blue/green or canary deployments to achieve zero-downtime + releases. + • Gate production deployments with automated approval workflows and + post-deploy smoke tests. + + 📈 SRE + ────── + • Define SLOs and SLAs before deploying to production — reliability + targets must exist before you can measure them. + • Use error budgets to balance reliability investment with feature + velocity. + • Alert on symptoms (SLO burn rate) rather than causes (CPU spikes). + • Establish on-call rotations and incident runbooks before going live. + + 📊 MONITORING + ───────────── + • Define what "good" looks like (golden signals: Rate, Errors, Duration, + Saturation) before creating dashboards. + • Instrument applications with standard metrics from day one. + • Centralise logs, metrics, and traces in a single observability + platform (ELK, Prometheus, Grafana). + • Set alerting thresholds based on SLO objectives, not arbitrary values. + + 🔒 SECURITY + ─────────── + • Shift security left: enforce security scanning in the CI pipeline + so vulnerabilities are caught before merge. + • Manage Kubernetes secrets safely using Sealed Secrets (Kubeseal) — + never store plaintext secrets in Git. + • Scan container images for vulnerabilities as part of every build. + • Enforce least-privilege access controls for all service accounts + and CI/CD pipelines. + +""" + USAGE_FOOTER = """\ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ HOW TO USE THIS COMMAND ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - python -m cli.devopsos process-first # full overview (this output) - python -m cli.devopsos process-first --section what # 5 core principles only - python -m cli.devopsos process-first --section mapping # devopsos scaffold command map - python -m cli.devopsos process-first --section tips # AI prompts for beginners - python -m cli.devopsos process-first --help # full option reference + python -m cli.devopsos process-first # full overview + python -m cli.devopsos process-first --section what # 5 core principles + python -m cli.devopsos process-first --section mapping # devopsos scaffold map + python -m cli.devopsos process-first --section tips # AI prompts for beginners + python -m cli.devopsos process-first --section best_practices # best practices by stage + python -m cli.devopsos process-first --help # full option reference You can also run the standalone module: - python -m cli.process_first [--section what|mapping|tips|all] + python -m cli.process_first [--section what|mapping|tips|best_practices|all] 📖 Full docs: docs/PROCESS-FIRST.md @@ -160,8 +269,10 @@ FULL_TEXT = ( PROCESS_FIRST_SUMMARY + + THOUGHT_LEADERS + WHAT_IS_PROCESS_FIRST + MAPPING_TO_TOOLING + + BEST_PRACTICES + BEGINNER_TIPS ) @@ -171,9 +282,10 @@ # --------------------------------------------------------------------------- SECTIONS = { - "what": PROCESS_FIRST_SUMMARY + WHAT_IS_PROCESS_FIRST, + "what": PROCESS_FIRST_SUMMARY + THOUGHT_LEADERS + WHAT_IS_PROCESS_FIRST, "mapping": PROCESS_FIRST_SUMMARY + MAPPING_TO_TOOLING, "tips": PROCESS_FIRST_SUMMARY + BEGINNER_TIPS, + "best_practices": PROCESS_FIRST_SUMMARY + BEST_PRACTICES, "all": FULL_TEXT + USAGE_FOOTER, } @@ -203,9 +315,10 @@ def parse_arguments(argv=None): default="all", help=( "Which section to display: " - "'what' (ideology overview), " + "'what' (ideology overview + thought leaders), " "'mapping' (tooling map), " "'tips' (beginner AI prompts), " + "'best_practices' (best practices by stage), " "or 'all' (default)." ), ) diff --git a/docs/PROCESS-FIRST.md b/docs/PROCESS-FIRST.md index ec46650..9fc4a60 100644 --- a/docs/PROCESS-FIRST.md +++ b/docs/PROCESS-FIRST.md @@ -3,6 +3,32 @@ > *"Tools are only as good as the processes that govern them."* > — Saravanan Gnanagur, Founder, CloudEngineLabs +**Process-First is the Systems Thinking of DevOps.** + +--- + +## Thought Leaders on Process-First & Systems Thinking + +> *"The First Way emphasises the performance of the entire system, as opposed to +> the performance of a specific silo of work or department."* +> +> — **Gene Kim, Kevin Behr, and George Spafford** (*The Phoenix Project* / *The DevOps Handbook*) + +Gene Kim, Kevin Behr, and George Spafford define the **"Three Ways"** of DevOps in +*The Phoenix Project* and *The DevOps Handbook*. The **First Way** focuses on +**Systems Thinking** — optimising the flow from development through operations to +the customer, rather than maximising the throughput of any single stage. + +> *"DevOps is about fixing the broken, inefficient processes between developers +> and operations before introducing new tools."* +> +> — **Patrick Debois** (Founder of DevOpsDays) + +Patrick Debois, as the founder of DevOpsDays, emphasised that cultural and +process problems — not technology gaps — are the root cause of slow, unreliable +software delivery. He showed that bringing developers and operations together +through shared processes is the prerequisite for any tooling to succeed. + --- ## What is Process-First? @@ -55,7 +81,7 @@ interactively in your terminal. # Full overview (default) python -m cli.devopsos process-first -# Section: what Process-First is and the 5 core principles +# Section: what Process-First is, 5 core principles + thought leaders python -m cli.devopsos process-first --section what # Section: how each principle maps to a DevOps-OS scaffold command @@ -63,20 +89,23 @@ python -m cli.devopsos process-first --section mapping # Section: AI prompts and reading recommendations for beginners python -m cli.devopsos process-first --section tips + +# Section: best practices for each stage (build/test/iac/deploy/sre/monitoring/security) +python -m cli.devopsos process-first --section best_practices ``` You can also invoke the module directly: ```bash python -m cli.process_first -python -m cli.process_first --section mapping +python -m cli.process_first --section best_practices ``` ### Options | Option | Default | Description | |--------|---------|-------------| -| `--section` | `all` | `what` · `mapping` · `tips` · `all` | +| `--section` | `all` | `what` · `mapping` · `tips` · `best_practices` · `all` | ### Where does this command help? @@ -85,7 +114,83 @@ python -m cli.process_first --section mapping | New team member onboarding | `all` — read the full overview first | | Pre-sprint process alignment meeting | `mapping` — show how each scaffold encodes a principle | | Self-study / learning DevOps from scratch | `tips` — use the AI prompts to go deep | -| Quick philosophy refresh | `what` — re-read the 5 core principles | +| Quick philosophy refresh | `what` — re-read the 5 core principles and thought leaders | +| Setting up a new DevOps pipeline | `best_practices` — best practices for each stage | + +--- + +## Best Practices by Stage — Systems Thinking in DevOps + +Applying Systems Thinking means defining the right process at each stage of the value +stream **before** selecting or configuring any tool. + +### 🔨 Build + +| Best Practice | Why it matters | +|---------------|----------------| +| Define build standards before choosing tools (Docker, Gradle, Maven, Make) | Prevents tool sprawl and ensures consistent outputs | +| Standardise base images and dependency management across all teams | Reduces "works on my machine" issues | +| Enforce reproducible builds with version-pinned dependencies | Makes builds auditable and roll-backable | +| Use an artifact repository (Nexus) to cache, version, and audit build outputs | Provides a single source of truth for artifacts | + +### 🧪 Test & Quality + +| Best Practice | Why it matters | +|---------------|----------------| +| Define quality gates and acceptance criteria before writing tests | Ensures tests validate business intent, not just code paths | +| Automate unit, integration, and end-to-end tests in every pipeline run | Catches regressions before they reach production | +| Enforce code standards with static analysis (SonarQube, Checkstyle, ESLint, Pylint) | Maintains code quality at scale without manual review | +| Fail fast: surface failures early to prevent bad code advancing | Reduces the cost of fixing defects | + +### 🏗️ IaC & Infrastructure + +| Best Practice | Why it matters | +|---------------|----------------| +| Define infrastructure requirements before writing Terraform or Helm code | Prevents over-engineering and unnecessary complexity | +| Version-control every infrastructure definition — no manual changes to production | Makes infrastructure changes auditable and reproducible | +| Use Kustomize overlays for environment-specific config (dev/staging/prod) | Reduces duplication and drift between environments | +| Detect and alert on infrastructure drift regularly | Ensures the running state matches the declared state | + +### 🚀 Deploy & GitOps + +| Best Practice | Why it matters | +|---------------|----------------| +| Define deployment runbooks and rollback procedures before the first release | Prevents panicked, undocumented changes during incidents | +| Use GitOps (ArgoCD, Flux) to make deployment intent explicit in Git | Creates a full audit trail for every change | +| Implement blue/green or canary deployments for zero-downtime releases | Reduces blast radius of bad deployments | +| Gate production deployments with automated approvals and smoke tests | Prevents untested code from reaching users | + +### 📈 SRE + +| Best Practice | Why it matters | +|---------------|----------------| +| Define SLOs and SLAs before deploying to production | You cannot measure reliability you haven't defined | +| Use error budgets to balance reliability with feature velocity | Gives teams a data-driven way to decide when to slow down | +| Alert on symptoms (SLO burn rate), not causes (CPU spikes) | Reduces alert fatigue and improves signal quality | +| Establish on-call rotations and incident runbooks before going live | Prevents chaotic, undocumented incident response | + +### 📊 Monitoring + +| Best Practice | Why it matters | +|---------------|----------------| +| Define golden signals (Rate, Errors, Duration, Saturation) before building dashboards | Ensures dashboards answer real operational questions | +| Instrument applications with standard metrics from day one | Eliminates blind spots in your observability stack | +| Centralise logs, metrics, and traces in one platform (ELK, Prometheus, Grafana) | Enables faster root-cause analysis during incidents | +| Set alerting thresholds based on SLO objectives, not arbitrary values | Prevents noisy, meaningless alerts that erode trust | + +### 🔒 Security + +| Best Practice | Why it matters | +|---------------|----------------| +| Shift security left: enforce scanning in the CI pipeline | Catches vulnerabilities before they reach production | +| Manage Kubernetes secrets with Sealed Secrets (Kubeseal) | Prevents plaintext secrets from being stored in Git | +| Scan container images for vulnerabilities on every build | Ensures base images are kept up to date and safe | +| Enforce least-privilege access controls for all service accounts | Limits the blast radius of a compromised component | + +```bash +# View best practices in your terminal +python -m cli.devopsos process-first --section best_practices +``` --- From 38052b7277c23cefedfd671ad399b44507271260 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 11:39:02 +0000 Subject: [PATCH 3/6] Add dedicated Containerization and Kubernetes wizard groups, add podman support Co-authored-by: chefgs <7605658+chefgs@users.noreply.github.com> --- cli/devopsos.py | 67 +++++++++++++++++++----------------- cli/process_first.py | 44 +++++++++++++++++++++-- cli/scaffold_devcontainer.py | 9 ++--- docs/PROCESS-FIRST.md | 29 ++++++++++++++-- 4 files changed, 107 insertions(+), 42 deletions(-) diff --git a/cli/devopsos.py b/cli/devopsos.py index 4f45014..cb38ab4 100644 --- a/cli/devopsos.py +++ b/cli/devopsos.py @@ -38,7 +38,7 @@ def init( # ── Canonical tool lists ────────────────────────────────────────────── ALL_LANGUAGES = ["python", "java", "node", "ruby", "csharp", "php", "rust", "typescript", "kotlin", "c", "cpp", "javascript", "go"] - ALL_CICD = ["docker", "terraform", "kubectl", "helm", "github_actions", "jenkins"] + ALL_CICD = ["docker", "podman", "terraform", "kubectl", "helm", "github_actions", "jenkins"] ALL_KUBERNETES = ["k9s", "kustomize", "argocd_cli", "lens", "kubeseal", "flux", "kind", "minikube", "openshift_cli"] ALL_BUILD_TOOLS = ["gradle", "maven", "ant", "make", "cmake"] @@ -58,29 +58,30 @@ def init( "choices": ALL_LANGUAGES, "description": "Programming languages for your project", }, + "Containerization [CONTAINER stage]": { + "choices": ["docker", "podman"], + "description": "Container runtimes to build, ship, and run application images", + }, "Build Tools [BUILD stage]": { - "choices": ["docker", "gradle", "maven", "ant", "make", "cmake", "nexus"], + "choices": ["gradle", "maven", "ant", "make", "cmake", "nexus"], "description": "Tools to compile, package, and store build artifacts", }, "Test & Quality [TEST stage]": { "choices": ["sonarqube", "checkstyle", "pmd", "eslint", "pylint"], "description": "Static analysis and quality gates to enforce standards early", }, - "IaC & Infrastructure [IaC stage]": { - "choices": ["terraform", "kubectl", "helm", "kustomize"], - "description": "Infrastructure as Code tools for reproducible environments", + "Kubernetes [KUBERNETES stage]": { + "choices": ["kubectl", "helm", "kustomize", "k9s", "argocd_cli", + "flux", "kind", "minikube", "lens", "kubeseal", "openshift_cli"], + "description": "Kubernetes CLI tools, GitOps engines, and local cluster runtimes", }, - "Deploy & GitOps [DEPLOY stage]": { - "choices": ["github_actions", "jenkins", "argocd_cli", "flux"], - "description": "CI/CD pipelines and GitOps delivery tools", + "CI/CD & Deploy [DEPLOY stage]": { + "choices": ["github_actions", "jenkins", "terraform"], + "description": "CI/CD pipelines, IaC provisioning, and deployment automation", }, "SRE & Monitoring [SRE/MONITORING stage]": { - "choices": ["prometheus", "grafana", "elk", "k9s"], - "description": "Observability stack: metrics, dashboards, and logs", - }, - "Security & Kubernetes Dev [SECURITY stage]": { - "choices": ["kubeseal", "lens", "kind", "minikube", "openshift_cli"], - "description": "Secret management, security scanning, and local K8s dev tools", + "choices": ["prometheus", "grafana", "elk"], + "description": "Observability stack: metrics, dashboards, and centralised logs", }, } @@ -110,34 +111,35 @@ def init( # output for backward compatibility. def _sel(group): return selected_by_group.get(group, []) + container_sel = _sel("Containerization [CONTAINER stage]") build_sel = _sel("Build Tools [BUILD stage]") test_sel = _sel("Test & Quality [TEST stage]") - iac_sel = _sel("IaC & Infrastructure [IaC stage]") - deploy_sel = _sel("Deploy & GitOps [DEPLOY stage]") + k8s_sel = _sel("Kubernetes [KUBERNETES stage]") + deploy_sel = _sel("CI/CD & Deploy [DEPLOY stage]") sre_sel = _sel("SRE & Monitoring [SRE/MONITORING stage]") - security_sel = _sel("Security & Kubernetes Dev [SECURITY stage]") lang_sel = _sel("Languages") config = { "languages": {opt: opt in lang_sel for opt in ALL_LANGUAGES}, "cicd": { - "docker": "docker" in build_sel, - "terraform": "terraform" in iac_sel, - "kubectl": "kubectl" in iac_sel, - "helm": "helm" in iac_sel, + "docker": "docker" in container_sel, + "podman": "podman" in container_sel, + "terraform": "terraform" in deploy_sel, + "kubectl": "kubectl" in k8s_sel, + "helm": "helm" in k8s_sel, "github_actions": "github_actions" in deploy_sel, "jenkins": "jenkins" in deploy_sel, }, "kubernetes": { - "k9s": "k9s" in sre_sel, - "kustomize": "kustomize" in iac_sel, - "argocd_cli": "argocd_cli" in deploy_sel, - "lens": "lens" in security_sel, - "kubeseal": "kubeseal" in security_sel, - "flux": "flux" in deploy_sel, - "kind": "kind" in security_sel, - "minikube": "minikube" in security_sel, - "openshift_cli": "openshift_cli" in security_sel, + "k9s": "k9s" in k8s_sel, + "kustomize": "kustomize" in k8s_sel, + "argocd_cli": "argocd_cli" in k8s_sel, + "lens": "lens" in k8s_sel, + "kubeseal": "kubeseal" in k8s_sel, + "flux": "flux" in k8s_sel, + "kind": "kind" in k8s_sel, + "minikube": "minikube" in k8s_sel, + "openshift_cli": "openshift_cli" in k8s_sel, }, "build_tools": {opt: opt in build_sel for opt in ALL_BUILD_TOOLS}, "code_analysis": {opt: opt in test_sel for opt in ALL_CODE_ANALYSIS}, @@ -180,9 +182,10 @@ def _sel(group): return selected_by_group.get(group, []) } for lang, arg in lang_map.items(): build_args[arg] = str(config["languages"].get(lang, False)).lower() - # CICD + # CICD (includes container runtimes docker/podman) cicd_map = { - "docker": "INSTALL_DOCKER", "terraform": "INSTALL_TERRAFORM", + "docker": "INSTALL_DOCKER", "podman": "INSTALL_PODMAN", + "terraform": "INSTALL_TERRAFORM", "kubectl": "INSTALL_KUBECTL", "helm": "INSTALL_HELM", "github_actions": "INSTALL_GITHUB_ACTIONS", "jenkins": "INSTALL_JENKINS" } diff --git a/cli/process_first.py b/cli/process_first.py index 4596daa..58bf3a1 100644 --- a/cli/process_first.py +++ b/cli/process_first.py @@ -107,6 +107,14 @@ │ │ ArgoCD, and Flux ensure every team │ │ │ starts from a reviewed baseline. │ ├─────────────────────────────┼──────────────────────────────────────────┤ + │ Standardise the container │ `devopsos scaffold devcontainer` │ + │ runtime & Kubernetes env │ encodes Docker/Podman runtime choice │ + │ │ and all Kubernetes CLI tools (kubectl, │ + │ │ helm, kustomize, argocd_cli, flux, │ + │ │ k9s, kind, minikube, kubeseal …) into a │ + │ │ reproducible devcontainer.json so every │ + │ │ engineer starts from the same baseline. │ + ├─────────────────────────────┼──────────────────────────────────────────┤ │ Automate the repeatable │ `devopsos scaffold argocd` encodes │ │ │ the GitOps sync process as code; │ │ │ `devopsos scaffold devcontainer` │ @@ -180,12 +188,42 @@ 🔨 BUILD ──────── • Define build standards and conventions before choosing tools - (Docker, Gradle, Maven, Make). - • Standardise base images and dependency management across all teams. - • Enforce reproducible builds with version-pinned dependencies. + (Gradle, Maven, Make). + • Standardise dependency management and enforce version-pinned builds. + • Enforce reproducible builds across all teams and environments. • Use an artifact repository (Nexus) to cache, version, and audit build outputs. + 🐳 CONTAINERIZATION + ──────────────────── + • Choose and document your container runtime (Docker or Podman) before + writing the first Dockerfile — consistency prevents environment drift. + • Standardise base images across all services: use a pinned, minimal + base image (e.g. distroless or Alpine) and update it on a schedule. + • Define a container image naming and tagging convention (e.g. + //:) before the first build. + • Scan every image for vulnerabilities during the CI build stage — + never push an unscanned image to a shared registry. + • Use multi-stage Dockerfiles to keep production images small and + free of build-time dependencies. + • Configure your dev environment with `devopsos scaffold devcontainer` + to standardise the container runtime for every team member. + + ☸️ KUBERNETES + ────────────── + • Define Kubernetes cluster topology and namespace strategy before + deploying any workload. + • Use a local cluster (kind or minikube) for development so that + every engineer can reproduce production-like conditions locally. + • Manage all manifests through version-controlled Kustomize overlays + or Helm charts — no kubectl apply from a developer laptop in production. + • Use GitOps (ArgoCD or Flux) as the single path to deploy and update + workloads; direct kubectl changes to production are prohibited. + • Manage Kubernetes secrets with Sealed Secrets (Kubeseal) so that + encrypted secrets can be safely stored in Git. + • Use k9s or Lens for day-two operations and cluster observability + rather than ad-hoc kubectl commands. + 🧪 TEST & QUALITY ───────────────── • Define quality gates and acceptance criteria before writing tests. diff --git a/cli/scaffold_devcontainer.py b/cli/scaffold_devcontainer.py index c384efc..34694a2 100644 --- a/cli/scaffold_devcontainer.py +++ b/cli/scaffold_devcontainer.py @@ -26,7 +26,7 @@ "rust", "typescript", "kotlin", "c", "cpp", "javascript", "go", ] ALL_CICD = [ - "docker", "terraform", "kubectl", "helm", "github_actions", "jenkins", + "docker", "podman", "terraform", "kubectl", "helm", "github_actions", "jenkins", ] ALL_KUBERNETES = [ "k9s", "kustomize", "argocd_cli", "lens", "kubeseal", "flux", @@ -47,7 +47,8 @@ "go": "INSTALL_GO", } CICD_ARG_MAP = { - "docker": "INSTALL_DOCKER", "terraform": "INSTALL_TERRAFORM", + "docker": "INSTALL_DOCKER", "podman": "INSTALL_PODMAN", + "terraform": "INSTALL_TERRAFORM", "kubectl": "INSTALL_KUBECTL", "helm": "INSTALL_HELM", "github_actions": "INSTALL_GITHUB_ACTIONS", "jenkins": "INSTALL_JENKINS", } @@ -272,8 +273,8 @@ def generate_devcontainer_json(env_config): extensions += ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "ms-vscode.vscode-typescript-next"] if langs.get("go"): extensions.append("golang.go") - if cicd.get("docker"): - extensions.append("ms-azuretools.vscode-docker") + if cicd.get("docker") or cicd.get("podman"): + extensions.append("ms-azuretools.vscode-docker") # Docker extension also works with Podman if cicd.get("terraform"): extensions.append("hashicorp.terraform") if cicd.get("kubectl") or cicd.get("helm"): diff --git a/docs/PROCESS-FIRST.md b/docs/PROCESS-FIRST.md index 9fc4a60..6a5e9de 100644 --- a/docs/PROCESS-FIRST.md +++ b/docs/PROCESS-FIRST.md @@ -63,6 +63,7 @@ immediately usable configuration artefact: |-------------------------|-------------------|-------------------| | **Define before you Build** | `devopsos scaffold cicd` / `gha` / `gitlab` | Interactive wizards capture intent before generating any config file | | **Standardise before Scale** | `devopsos scaffold gha`, `gitlab`, `jenkins` | Golden-path templates for GitHub Actions, GitLab CI, and Jenkins — reviewed baselines every team can adopt | +| **Standardise the container runtime & Kubernetes env** | `devopsos scaffold devcontainer` | Encodes Docker/Podman choice and all Kubernetes CLI tools (kubectl, helm, kustomize, argocd_cli, flux, k9s, kind, minikube, kubeseal …) into a reproducible `devcontainer.json` | | **Automate the Repeatable** | `devopsos scaffold argocd` | GitOps sync process encoded as an ArgoCD `Application` + `AppProject` CR | | **Automate the Repeatable** | `devopsos scaffold devcontainer` | Developer environment setup encoded as `devcontainer.json` — reproducible for every team member | | **Observe and Iterate** | `devopsos scaffold sre` | Prometheus alert rules, Grafana dashboards, and SLO manifests — close the measure-improve feedback loop | @@ -128,11 +129,33 @@ stream **before** selecting or configuring any tool. | Best Practice | Why it matters | |---------------|----------------| -| Define build standards before choosing tools (Docker, Gradle, Maven, Make) | Prevents tool sprawl and ensures consistent outputs | -| Standardise base images and dependency management across all teams | Reduces "works on my machine" issues | -| Enforce reproducible builds with version-pinned dependencies | Makes builds auditable and roll-backable | +| Define build standards before choosing tools (Gradle, Maven, Make) | Prevents tool sprawl and ensures consistent outputs | +| Standardise dependency management and enforce version-pinned builds | Reduces "works on my machine" issues | +| Enforce reproducible builds across all teams and environments | Makes builds auditable and roll-backable | | Use an artifact repository (Nexus) to cache, version, and audit build outputs | Provides a single source of truth for artifacts | +### 🐳 Containerization + +| Best Practice | Why it matters | +|---------------|----------------| +| Choose and document your container runtime (Docker or Podman) before writing the first Dockerfile | Consistency prevents environment drift across teams | +| Standardise base images: use pinned, minimal images (distroless or Alpine) updated on a schedule | Reduces attack surface and image size | +| Define a container image naming and tagging convention (`//:`) | Makes every image traceable back to its source commit | +| Scan every image for vulnerabilities in the CI build stage before pushing to a registry | Prevents known CVEs from reaching any environment | +| Use multi-stage Dockerfiles to keep production images free of build-time dependencies | Smaller images, faster pulls, smaller attack surface | +| Use `devopsos scaffold devcontainer` to standardise the container runtime for every team member | Every engineer starts from the same reproducible baseline | + +### ☸️ Kubernetes + +| Best Practice | Why it matters | +|---------------|----------------| +| Define cluster topology and namespace strategy before deploying any workload | Avoids namespace sprawl and permission confusion | +| Use a local cluster (kind or minikube) for development so every engineer can reproduce production-like conditions locally | Reduces "it works on staging but not prod" surprises | +| Manage all manifests through version-controlled Kustomize overlays or Helm charts | No kubectl apply from developer laptops in production | +| Use GitOps (ArgoCD or Flux) as the single deployment path — direct production kubectl changes are prohibited | Every change is auditable, reviewable, and reversible | +| Manage Kubernetes secrets with Sealed Secrets (Kubeseal) | Encrypted secrets can be safely stored in Git | +| Use k9s or Lens for day-two operations and cluster observability | Consistent tooling reduces operator error | + ### 🧪 Test & Quality | Best Practice | Why it matters | From 73ba5aa67cf5cedfa2a07e74b2ac71d24d0b1b5e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Mar 2026 12:26:06 +0000 Subject: [PATCH 4/6] Fix init wizard: add Space instruction to checkbox so selections are captured Co-authored-by: chefgs <7605658+chefgs@users.noreply.github.com> --- cli/devopsos.py | 1 + cli/test_cli.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/cli/devopsos.py b/cli/devopsos.py index cb38ab4..a8436d3 100644 --- a/cli/devopsos.py +++ b/cli/devopsos.py @@ -93,6 +93,7 @@ def init( selected = inquirer.checkbox( message=f"Select {group_label}:", choices=group_info["choices"], + instruction="(Space to select, ↑↓ to navigate, Enter to confirm)", ).execute() selected_by_group[group_label] = selected diff --git a/cli/test_cli.py b/cli/test_cli.py index 29e3725..dfa138e 100644 --- a/cli/test_cli.py +++ b/cli/test_cli.py @@ -69,6 +69,99 @@ def test_init_dir_option_creates_devcontainer_in_specified_dir(): "Expected devcontainer.env.json inside .devcontainer" ) +def test_init_checkbox_includes_space_instruction(): + """Each checkbox prompt must include an instruction so users know to press Space.""" + from unittest.mock import MagicMock, patch, call + from typer.testing import CliRunner + from cli.devopsos import app + + checkbox_mock = MagicMock() + checkbox_mock.execute.return_value = [] + + confirm_proceed = MagicMock() + confirm_proceed.execute.return_value = True + confirm_skip = MagicMock() + confirm_skip.execute.return_value = False + + with tempfile.TemporaryDirectory() as tmp: + with patch("cli.devopsos.inquirer.checkbox", return_value=checkbox_mock) as mock_cb, \ + patch("cli.devopsos.inquirer.confirm", + side_effect=[confirm_proceed, confirm_skip]): + CliRunner().invoke(app, ["init", "--dir", tmp]) + + # Every checkbox call must pass a non-empty 'instruction' keyword argument + assert mock_cb.call_count > 0, "checkbox was never called" + for c in mock_cb.call_args_list: + instruction = c.kwargs.get("instruction", "") + assert instruction, ( + f"checkbox call missing 'instruction' kwarg — " + f"users cannot tell they must press Space to select items. Call: {c}" + ) + assert "space" in instruction.lower(), ( + f"instruction '{instruction}' should mention Space so users know how to select" + ) + +def test_init_selections_written_to_config(): + """Selections made in the wizard must be reflected as True in devcontainer.env.json.""" + from unittest.mock import MagicMock, patch + from typer.testing import CliRunner + from cli.devopsos import app + + # Simulate user selecting one tool in each group + selections = [ + ["python"], # Languages + ["docker"], # Containerization + ["gradle"], # Build Tools + ["sonarqube"], # Test & Quality + ["kubectl", "k9s"], # Kubernetes + ["github_actions"], # CI/CD & Deploy + ["prometheus"], # SRE & Monitoring + ] + sel_iter = iter(selections) + + def _checkbox_factory(**kwargs): + mock = MagicMock() + mock.execute.return_value = next(sel_iter) + return mock + + text_mock = MagicMock() + text_mock.execute.return_value = "3.11" + + confirm_proceed = MagicMock() + confirm_proceed.execute.return_value = True + confirm_skip = MagicMock() + confirm_skip.execute.return_value = False + + with tempfile.TemporaryDirectory() as tmp: + with patch("cli.devopsos.inquirer.checkbox", side_effect=_checkbox_factory), \ + patch("cli.devopsos.inquirer.text", return_value=text_mock), \ + patch("cli.devopsos.inquirer.confirm", + side_effect=[confirm_proceed, confirm_skip]): + result = CliRunner().invoke(app, ["init", "--dir", tmp]) + + assert result.exit_code == 0, result.output + + cfg_path = Path(tmp) / ".devcontainer" / "devcontainer.env.json" + assert cfg_path.exists() + cfg = json.loads(cfg_path.read_text()) + + # Selected tools must be True; unselected must be False + assert cfg["languages"]["python"] is True, "python should be True" + assert cfg["languages"]["java"] is False, "java should be False" + assert cfg["cicd"]["docker"] is True, "docker should be True" + assert cfg["cicd"]["podman"] is False, "podman should be False" + assert cfg["cicd"]["github_actions"] is True, "github_actions should be True" + # kubectl and helm are mapped into the "cicd" section + assert cfg["cicd"]["kubectl"] is True, "kubectl should be True" + assert cfg["cicd"]["helm"] is False, "helm (not selected) should be False" + assert cfg["kubernetes"]["k9s"] is True, "k9s should be True" + assert cfg["kubernetes"]["flux"] is False, "flux should be False" + assert cfg["build_tools"]["gradle"] is True, "gradle should be True" + assert cfg["build_tools"]["maven"] is False, "maven should be False" + assert cfg["code_analysis"]["sonarqube"] is True, "sonarqube should be True" + assert cfg["devops_tools"]["prometheus"] is True, "prometheus should be True" + assert cfg["devops_tools"]["grafana"] is False, "grafana should be False" + def test_scaffold_unknown(): result = _run(["-m", "cli.devopsos", "scaffold", "unknown"]) assert "Unknown scaffold target" in result.stdout From aeecb53cd4c15b27e55a6a365ff56386d15009c7 Mon Sep 17 00:00:00 2001 From: Saravanan Gnanaguru Date: Sat, 7 Mar 2026 18:32:26 +0530 Subject: [PATCH 5/6] chore: Remove compiled Python bytecode files from the __pycache__ directory --- cli/__pycache__/__init__.cpython-313.pyc | Bin 146 -> 0 bytes cli/__pycache__/devopsos.cpython-313.pyc | Bin 8558 -> 0 bytes cli/__pycache__/scaffold_cicd.cpython-313.pyc | Bin 10625 -> 0 bytes cli/__pycache__/scaffold_gha.cpython-313.pyc | Bin 33172 -> 0 bytes .../scaffold_jenkins.cpython-313.pyc | Bin 25693 -> 0 bytes .../test_cli.cpython-313-pytest-8.4.0.pyc | Bin 2316 -> 0 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 cli/__pycache__/__init__.cpython-313.pyc delete mode 100644 cli/__pycache__/devopsos.cpython-313.pyc delete mode 100644 cli/__pycache__/scaffold_cicd.cpython-313.pyc delete mode 100644 cli/__pycache__/scaffold_gha.cpython-313.pyc delete mode 100644 cli/__pycache__/scaffold_jenkins.cpython-313.pyc delete mode 100644 cli/__pycache__/test_cli.cpython-313-pytest-8.4.0.pyc diff --git a/cli/__pycache__/__init__.cpython-313.pyc b/cli/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 504672ed56d14e29ac71a45eae5ad7eefd3173a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 146 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%0A&Pdjq*C^POP416f zAGsbmJ@5M0M(@0|<7&HibiG+}4;0;ll6!EoeM?_(j}=_wMc0hvnkl#fYm;|`_XAIK zMAHe_X^vy!z=X&wh}jz?Se3JJqEld%8HTXgqJ?*5JN=0L$cRB#O! zT@#XPqTrf*mO_1-*gY(U$`od_5lkVey@{(KT|fubHC+Z*!{lWW$lA* zwfwYozhH+Y_o<@$tmHns*}v6LaE}*U6Ghjoqx;3a`=m`faD4kTo)^tI{%e)qSGTeJ&&9n`?0$u_nD&myyQN=8Qube z_`wcES5R^V3$9QFdnfSM(0uo-RDZNs-zn91KC1V=JGwTs_Qp1s0kAhdy!rPx|6z5b zO*|JZoQ(c#QcNTz$Blw@2{ueaH>`G_6#K`ewsDbiZCl!kmgAD;c+qk~vYgndYY^?7 z59+!$>UP?X|J?tN{`KfTO^dZ{;1Y+tVjT`9ZqMf7&v@xXNTiWyZP`9QCQ{BK)h%ci+$u)D6vl|KS|rnAUvN1FreV)b{mPiyGkQVO`hP zUqM*!epu!GS~vm8|Sap{8Yq1`c8qkGh8zI4v`=z@99YAn{6 z=UR;3eCOO^G>0z`LM*zlkV(bQ(m0jUZ_$axD3s-UGqfP1=DWg>Txooap^vlPgtwC} z-LpGYxK4av%5>8B-t3VLIq0!D{-?lH3}2ko2cLTI|LHaqKY`C(NTYm&f)5ri`n;jO zxPlLvdRV93TZfN6%Ja>iP!6oq?X9bP{MgT78P_u(PW4yteO7rw@Ja0810PDreXnoy z9z&i+Wv$X9{_c@cI>XabX9PGW3UIT8W1_cox6pyEOV{2;hFgN`8MkK+x}-wyEZp|1 zR2xNOfVU*py+J?%$FDNMNZD}w3b4t;Coux<#lOI9YzzU5O4Iz$p@LIcc@4h- z-M_@s(@@+dcC7?eeaBufI(DdrBGq)CYI;Z=-Zs_T8F;^ShpI19&ij<}A=QkB1`DRc zJ5*zlYPnCfJfvFpHgMNN$_x1r6Nt2S^CkE=;)IS{kL_GHRLDyQTK->(J4crXL@2eCYkL_nuAcxGWudb-T9VL+g*Ncf;cGIjMI3 tiQcN|-nQ24>Y#pXuKFYHmr+4$_b@?P*YX9O^LM22Z+yEtf^1R*`5*Ql%QFA~ diff --git a/cli/__pycache__/scaffold_cicd.cpython-313.pyc b/cli/__pycache__/scaffold_cicd.cpython-313.pyc deleted file mode 100644 index db52f1fb802257df92dfddaaa23c2141881c8748..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10625 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 diff --git a/cli/__pycache__/scaffold_jenkins.cpython-313.pyc b/cli/__pycache__/scaffold_jenkins.cpython-313.pyc deleted file mode 100644 index 56c34c221a33cafec2ca00b2f62ee50cc483b4e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25693 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 LFeT__f`R`FqciJs diff --git a/cli/__pycache__/test_cli.cpython-313-pytest-8.4.0.pyc b/cli/__pycache__/test_cli.cpython-313-pytest-8.4.0.pyc deleted file mode 100644 index 1650d255a5ff57231865fe15c864f49d9b3430c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2316 zcmd5-O>7%Q6rTO@+Foa^rUlZ59JWzWY6;tI6Q^mx6^I{+QYC6OmOvI+ZR|;6SnsZ8 zcGEZrsRvFRKtl9@gv1FcK9w^kjvVdMs%Svsz^OMxZaML0|GbqVA)z8MYrprtH}Ac9 z^Y`XR(^LfQ<LekUOG2M0+LW8vsJ2=|bI4B=fwgpi9jB`WIZrc82r)T0n3?8%e7 zAflBZ{LNLlb)K2@>lTlL*Li4CCCvaU!qzN|!Lo}qi zZ1694O>&T-xTn?&6N^N0&$q-^0Jw`6V^#MDgxcX6aK-vmtj@4`WaL}Yl3*0xJG~?} z;!H9=<&1GG&ZCylfc50fD7H^U62w29NE1Gf8ZqFRbzgicpXVlai3G7x5<8iN^}mBi^&*W*xbjH#AXx@PEa+c_F5H$&B8 zh1RIwh0y9JOd*&Cwm+1Z1b&<>a?&_1Ta;qrhaOielq;_eHKx*F%Oh?FQ&5q>VG z$`soLW6I8&bk_~+Zs=w1*zT5Pn~v4Tto(YITF-UfBE%)^jJfTSzU7;E-?J^p@}Xy_ z5Pq~-iN}tnaw!XzvfxStqA1H*utNFu)5FFt*ITe8w7x?vV(q~|4v4UQ^=J!Cm)2oJ z>*1vN&Uyn@3<2L(!jCS$OY}=1chPU!%om%VZGK~XyZQCz;l}qjeq0&N-5Pyhjdty^ z*1!AqFQtW%ymG%eYyvxMj!P?#<@onNUKy1ZBKR;Prv{)rTu#w=f*$cuX-6u6{Sjn^ zPA*yEGb>q!T+bQ&Q^KN^f2KvN5EiZCzbsnn|FCGOnL_hVMXQt+ttmtMPl{G~mGJXR zazI#tcC77f*Y2`>;C$q`cbpBvugbwpD}s7rp!*it!9FQ+-x4SMViJv$X-@bPLC$b; z9!TcGkp;l)`S1!Q(ad->QiVSL;L)#uoOGS8|1=oQy+8WU83lu}cEB&xg^}F2uMIU| zhuXN*cr3@i2XbRnx)8yK896lo<)M Date: Sat, 7 Mar 2026 18:35:03 +0530 Subject: [PATCH 6/6] chore: Update .gitignore to include additional Python, environment, and build artifacts --- .gitignore | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 798e2dc..4c5e7f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,108 @@ +# Python __pycache__/ *.py[cod] *.pyo -.pytest_cache/ -*.egg-info/ +*.pyd +*$py.class +*.so +.Python + +# Virtual Environments +.env +.venv/ +venv/ +ENV/ +env/ +env.bak/ +venv.bak/ + +# Distribution / packaging dist/ build/ -.env +*.egg-info/ +*.egg +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover *.log +.pytest_cache/ +.hypothesis/ +pytestdebug.log + +# Jupyter Notebook +.ipynb_checkpoints +*.ipynb_checkpoints/ + +# PyCharm +.idea/ +*.iml +*.iws +.idea_modules/ + +# VS Code +.vscode/ +*.code-workspace + +# Spyder +.spyderproject +.spyproject + +# Rope +.ropeproject + +# mkdocs +site/ +docs/_build/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# Jekyll _site/ .jekyll-cache/ -.venv/ -venv/ -docs/test-report.html # Hugo build artifacts hugo-docs/public/ hugo-docs/resources/ hugo-docs/.hugo_build.lock +# Test reports +docs/test-report.html + # Scaffold-generated output files (created by `python -m cli.devopsos scaffold *`) .gitlab-ci.yml Jenkinsfile