Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 9 additions & 12 deletions .github/workflows/lint-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ on:
branches: ["main"]

env:
GO_VERSION: '1.24'
GO_VERSION: '1.25'

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: src/go.sum
Expand All @@ -29,22 +29,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache-dependency-path: src/go.sum

- name: Build binary
run: make build
- name: Install ginkgo
run: make dev-setup

- name: Run unit tests
run: make test-unit
- name: Run tests
run: make test

- name: Generate test coverage report
run: make test-coverage

- name: Run integration tests
run: make test-integration
2 changes: 1 addition & 1 deletion Containerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG GO_VERSION=1.24
ARG GO_VERSION=1.25.7
ARG GOARCH=amd64

# Build stage
Expand Down
35 changes: 9 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ GO_TEST := $(GO_CMD) test
GO_CLEAN := $(GO_CMD) clean
GO_VET := $(GO_CMD) vet
GO_FMT := gofmt
GO_VERSION := 1.24
GOLANGCI_LINT_VERSION := v2.1.6
GO_VERSION := 1.25.7
GOLANGCI_LINT_VERSION := v2.10.1
GINKGO_VERSION := v2.28.1
SRC_DIR := src

# Default target
Expand Down Expand Up @@ -49,27 +50,16 @@ deps: ## Download and install Go dependencies
.PHONY: test-unit
test-unit: ## Run unit tests
@echo "Running unit tests..."
cd $(SRC_DIR) && $(GO_TEST) -v ./...
ginkgo -v ./...

.PHONY: test-coverage
test-coverage: ## Run unit tests with coverage report
@echo "Running unit tests with coverage..."
cd $(SRC_DIR) && $(GO_TEST) ./... -coverprofile=coverage.out
cd $(SRC_DIR) && $(GO_CMD) tool cover -func=coverage.out

.PHONY: shellcheck
shellcheck: ## Run shellcheck on integration test script
@echo "Running shellcheck on integration test script..."
@podman run --pull always -v "$(PWD):/mnt:z" docker.io/koalaman/shellcheck:stable test/integration_test.sh
@echo "Shellcheck passed"

.PHONY: test-integration
test-integration: build shellcheck ## Run integration tests
@echo "Running integration tests..."
PATTERNIZER_BINARY=./$(SRC_DIR)/$(NAME) ./test/integration_test.sh

.PHONY: test
test: test-unit test-integration ## Run all tests (unit + integration)
test: test-unit ## Run all tests (unit + integration)

.PHONY: lint
lint: lint-fmt lint-vet lint-golangci ## Run all linting checks
Expand Down Expand Up @@ -116,19 +106,12 @@ dev-setup: deps ## Set up development environment
echo "Installing golangci-lint..."; \
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $$(go env GOPATH)/bin $(GOLANGCI_LINT_VERSION); \
fi
@if ! command -v ginkgo >/dev/null 2>&1; then \
echo "Installing ginkgo CLI..."; \
go install github.com/onsi/ginkgo/v2/ginkgo@$(GINKGO_VERSION); \
fi
@echo "Development environment ready"

.PHONY: version
version: ## Show version information
@echo "Go version: $$(go version)"
@echo "Git commit: $$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
@echo "Build date: $$(date -u +%Y-%m-%dT%H:%M:%SZ)"

.PHONY: docs
docs: ## Generate Go documentation
@echo "Generating documentation..."
cd $(SRC_DIR) && $(GO_CMD) doc -all ./...

.PHONY: check
check: lint-fmt lint-vet build test-unit ## Quick check (format, vet, build, unit tests)

Expand Down
102 changes: 26 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
# Patternizer

![Version: 0.2.1](https://img.shields.io/badge/Version-0.2.1-informational?style=flat-square)
![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-informational?style=flat-square)
[![Quay Repository](https://img.shields.io/badge/Quay.io-patternizer-blue?logo=quay)](https://quay.io/repository/validatedpatterns/patternizer)
[![CI Pipeline](https://github.com/validatedpatterns/patternizer/actions/workflows/build-push.yaml/badge.svg?branch=main)](https://github.com/validatedpatterns/patternizer/actions/workflows/build-push.yaml)

**Patternizer** is a command-line tool that bootstraps a Git repository containing Helm charts into a ready-to-use Validated Pattern. It automatically generates the necessary scaffolding, configuration files, and utility scripts, so you can get your pattern up and running in minutes.
**Patternizer** is a command-line tool that bootstraps a Git repository containing Helm charts into a ready-to-use Validated Pattern. It automatically generates the necessary scaffolding, configuration files, and utility scripts, so you can get your pattern up and running in minutes. It can also be used to upgrade existing patterns as described in [the Validated Pattern's blog](https://validatedpatterns.io/blog/2025-08-29-new-common-makefile-structure/).

> **Note:** This tool was developed with assistance from [Cursor](https://cursor.sh), an AI-powered code editor.
**Note:** This repo was developed with AI tools including [Cursor](https://cursor.com/), [Claude](https://claude.ai/login) and [Gemini](https://gemini.google.com/app).

- [Patternizer](#patternizer)
- [Features](#features)
- [Quick Start](#quick-start)
- [Example Workflow](#example-workflow)
- [Usage Details](#usage-details)
Expand All @@ -22,25 +21,10 @@
- [Development \& Contributing](#development--contributing)
- [Prerequisites](#prerequisites)
- [Local Development Workflow](#local-development-workflow)
- [Common Makefile Targets](#common-makefile-targets)
- [Testing Strategy](#testing-strategy)
- [Architecture](#architecture)
- [CI/CD Pipeline](#cicd-pipeline)
- [How to Contribute](#how-to-contribute)

## Features

- 🚀 **CLI-first design** with intuitive commands and help system
- 📦 **Container-native** for consistent execution across all environments
- 🔍 **Auto-discovery** of Helm charts and Git repository metadata
- 🔐 **Optional secrets integration** with Vault and External Secrets
- 🏗️ **Makefile-driven** utility scripts for easy pattern management
- ♻️ **Upgrade command** to refresh existing pattern repositories to the latest common structure

## Quick Start

This guide assumes you have a Git repository containing one or more Helm charts.

**Prerequisites:**

- Podman or Docker
Expand All @@ -49,29 +33,28 @@ This guide assumes you have a Git repository containing one or more Helm charts.
Navigate to your repository's root directory and run the initialization command:

```bash
# In the root of your pattern-repo
podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init
podman run --pull=newer -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init
```

This single command will generate all the necessary files to turn your repository into a Validated Pattern.

## Example Workflow

1. **Clone or create your pattern repository:**
1. **Clone or create your pattern repository**

```bash
git clone https://github.com/your-org/your-pattern.git
cd your-pattern
git checkout -b initialize-pattern
```

2. **Initialize the pattern using Patternizer:**
2. **Initialize the pattern using Patternizer**

```bash
podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init
podman run --pull=newer -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init
```

3. **Review, commit, and push the generated files:**
3. **Review, commit, and push the generated files**

```bash
git status
Expand All @@ -80,7 +63,13 @@ This single command will generate all the necessary files to turn your repositor
git push -u origin initialize-pattern
```

4. **Install the pattern:**
4. **Login to an OpenShift cluster**

```bash
export KUBECONFIG=/path/to/cluster/kubeconfig
```

5. **Install the pattern**

```bash
./pattern.sh make install
Expand All @@ -90,18 +79,18 @@ This single command will generate all the necessary files to turn your repositor

### Container Usage (Recommended)

Using the prebuilt container is the easiest way to run Patternizer, as it requires no local installation. The `-v "$PWD:$PWD:z" -w "$PWD"` flag mounts your current directory into the container's `/repo` workspace.
Using the prebuilt container is the easiest way to run Patternizer, as it requires no local installation.

#### **Initialize without secrets:**

```bash
podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init
podman run --pull=newer -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init
```

#### **Initialize with secrets support:**

```bash
podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init --with-secrets
podman run --pull=newer -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer init --with-secrets
```

#### **Upgrade an existing pattern repository:**
Expand All @@ -110,19 +99,18 @@ Use this to migrate or refresh an existing pattern repo to the latest common str

```bash
# Refresh common assets, keep your Makefile unless it lacks the include
podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer upgrade
podman run --pull=newer -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer upgrade

# Replace your Makefile with the default from Patternizer
podman run -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer upgrade --replace-makefile
podman run --pull=newer -v "$PWD:$PWD:z" -w "$PWD" quay.io/validatedpatterns/patternizer upgrade --replace-makefile
```

What upgrade does:

- Removes the `common/` directory if it exists
- Removes `./pattern.sh` if it exists (symlink or file)
- Copies `resources/Makefile-common` and `resources/pattern.sh` into the repo root
- Updates `ansible.cfg`, `Makefile-common`, and `pattern.sh` to the latest versions from [the resources directory](./resources/)
- Makefile handling:
- If `--replace-makefile` is set: copies the default `Makefile` into the repo root (overwriting any existing one)
- If `--replace-makefile` is set: replaces an existing Makefile, if present, to [`Makefile`](./resources/Makefile) from the resources directory
- If not set:
- If no `Makefile` exists: copies the default `Makefile`
- If a `Makefile` exists and already contains `include Makefile-common` anywhere: leaves it unchanged
Expand All @@ -147,6 +135,7 @@ Running `patternizer init` creates the following:
- `pattern.sh`: A utility script for common pattern operations (`install`, `upgrade`, etc.).
- `Makefile`: A simple Makefile that includes `Makefile-common`.
- `Makefile-common`: The core Makefile with all pattern-related targets.
- `ansible.cfg`: Configuration for the ansible installation used when `./pattern.sh` is called

Using the `--with-secrets` flag additionally creates:

Expand All @@ -159,7 +148,7 @@ This section is for developers who want to contribute to the Patternizer project

### Prerequisites

- Go (see `go.mod` for version)
- Go (see [`go.mod`](./src/go.mod) for version)
- Podman or Docker
- Git
- Make
Expand All @@ -168,10 +157,10 @@ This section is for developers who want to contribute to the Patternizer project

```bash
# 1. Clone the repository
git clone https://github.com/dminnear-rh/patternizer.git
git clone https://github.com/validatedpatterns/patternizer.git
cd patternizer

# 2. Set up the development environment (installs tools)
# 2. Set up the development environment
make dev-setup

# 3. Make your changes...
Expand All @@ -180,45 +169,6 @@ make dev-setup
make ci
```

### Common Makefile Targets

The `Makefile` is the single source of truth for all development and CI tasks.

- `make help`: Show all available targets.
- `make check`: Quick feedback loop (format, vet, build, unit tests).
- `make build`: Build the `patternizer` binary.
- `make test`: Run all tests (unit and integration).
- `make test-unit`: Run unit tests only.
- `make test-integration`: Run integration tests only.
- `make lint`: Run all code quality checks.
- `make amd64`: Build the amd64 container image locally.
- `make arm64`: Build the arm64 container image locally.

### Testing Strategy

Patternizer has a comprehensive test suite to ensure stability and correctness.

- **Unit Tests:** Located alongside the code they test (e.g., `src/internal/helm/helm_test.go`), these tests cover individual functions and packages in isolation. They validate Helm chart detection, Git URL parsing, and YAML processing logic.
- **Integration Tests:** The integration test suite (`test/integration_test.sh`) validates the end-to-end CLI workflow against a real Git repository. Key scenarios include:
1. **Basic Init:** Validates default file generation without secrets.
2. **Init with Secrets:** Ensures secrets-related applications and files are correctly added.
3. **Configuration Preservation:** Verifies that existing custom values are preserved when the tool is re-run.
4. **Sequential Execution:** Tests running `init` and then `init --with-secrets` to ensure a clean upgrade.
5. **Selective File Overwriting:** Confirms that running `init` on a repository with pre-existing custom files correctly **merges YAML configurations**, preserves user-modified files (like `Makefile` and `values-secret.yaml.template`), and only overwrites essential, generated scripts (`pattern.sh`, `Makefile-common`).
6. **Mixed State Handling:** Validates that the tool correctly initializes a partially-configured repository, **creating files that are missing** while leaving existing ones untouched.
7. **Upgrade (no replace):** Removes legacy `common/` and `pattern.sh` symlink, copies `Makefile-common`/`pattern.sh`, and injects `include Makefile-common` at the top of `Makefile` when missing.
8. **Upgrade (include present):** Leaves the existing `Makefile` unchanged when it already contains `include Makefile-common` anywhere.
9. **Upgrade with `--replace-makefile`:** Replaces `Makefile` with the default and refreshes common assets.
10. **Upgrade (no Makefile present):** Creates the default `Makefile` and refreshes common assets when a `Makefile` does not exist.

### Architecture

The CLI is organized into focused packages following Go best practices, with a clean separation of concerns between command-line logic (`cmd`), core business logic (`internal`), and file operations (`fileutils`). This modular design makes the codebase maintainable, testable, and extensible.

### CI/CD Pipeline

The GitHub Actions pipeline (`.github/workflows/ci.yaml`) runs on every push and pull request. It uses the same `Makefile` targets that developers use locally, ensuring perfect consistency between local and CI environments.

### How to Contribute

1. Fork the repository.
Expand Down
10 changes: 5 additions & 5 deletions resources/Makefile-common
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MAKEFLAGS += --no-print-directory
ANSIBLE_STDOUT_CALLBACK ?= null # null silences all ansible output. Override this with default, minimal, oneline, etc. when debugging.
ANSIBLE_RUN := ANSIBLE_STDOUT_CALLBACK=$(ANSIBLE_STDOUT_CALLBACK) ansible-playbook $(EXTRA_PLAYBOOK_OPTS)
ANSIBLE_STDOUT_CALLBACK ?= rhvp.cluster_utils.readable
ANSIBLE_RUN ?= ANSIBLE_STDOUT_CALLBACK=$(ANSIBLE_STDOUT_CALLBACK) ansible-playbook $(EXTRA_PLAYBOOK_OPTS)
DOCS_URL := https://validatedpatterns.io/blog/2025-08-29-new-common-makefile-structure/

.PHONY: help
Expand All @@ -20,9 +20,9 @@ operator-deploy operator-upgrade: ## Installs/updates the pattern on a cluster (
.PHONY: install
install: pattern-install ## Installs the pattern onto a cluster (Loads secrets as well if configured)

.PHONY: uninstall ## Prints a notice that patterns cannot currently be uninstalled
uninstall:
@echo "Uninstall is not possible at the moment so this target is empty. We are working to implement it as well as we can."
.PHONY:
uninstall: uninstall ## (EXPERIMENTAL) See https://validatedpatterns.io/blog/2026-02-16-pattern-uninstall/.
@$(ANSIBLE_RUN) rhvp.cluster_utils.uninstall

.PHONY: pattern-install
pattern-install:
Expand Down
3 changes: 3 additions & 0 deletions resources/ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ filter_plugins=~/.ansible/plugins/filter:./ansible/plugins/filter:/usr/share/ans
# use the collections from the util. container,
# change below if you want to test local collections
collections_path=/usr/share/ansible/collections

[inventory]
inventory_unparsed_warning=False
Loading