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
8 changes: 4 additions & 4 deletions .github/workflows/helm-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ jobs:
RELEASE_NAME: openops
NAMESPACE: openops
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7.0.1

- name: Set up Helm
uses: azure/setup-helm@v4
uses: azure/setup-helm@v5.0.1
with:
version: v3.14.4

- name: Cache Docker images
uses: actions/cache@v4
uses: actions/cache@v6.1.0
with:
path: /var/lib/docker
key: docker-${{ runner.os }}-${{ hashFiles('chart/values.yaml', 'chart/values.ci.yaml') }}
restore-keys: |
docker-${{ runner.os }}-

- name: Create kind cluster
uses: helm/kind-action@v1.9.0
uses: helm/kind-action@v1.15.0
with:
cluster_name: openops-e2e
wait: 180s
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/helm-validate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7.0.1

- name: Set up Helm
uses: azure/setup-helm@v4
uses: azure/setup-helm@v5.0.1
with:
version: v3.14.4

Expand All @@ -33,3 +33,6 @@ jobs:

- name: Template with example overrides
run: helm template openops chart -f chart/values.overrides-example.yaml

- name: Template with MCP enabled
run: helm template openops chart -f chart/values.mcp-example.yaml
39 changes: 25 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
# The release identity's federated credentials are scoped to this environment rather than to
# a branch, so a release runs from any branch - and the environment is where an approval gate
# for publishing to the public registry belongs. Without it the OIDC subject does not match.
environment: release
env:
CHART_VERSION: ${{ inputs.draft && format('{0}-draft', inputs.version) || inputs.version }}
APP_VERSION: ${{ inputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v7.0.1

- name: Set up Helm
uses: azure/setup-helm@v4
uses: azure/setup-helm@v5.0.1
with:
version: v3.14.4

Expand All @@ -54,23 +59,29 @@ jobs:
chart_package=$(printf '%s\n' "$pkg_output" | awk '/Successfully packaged chart and saved it to:/ {print $NF}')
echo "CHART_PACKAGE=$chart_package" >> "$GITHUB_ENV"

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
- name: Azure login
uses: azure/login@7ddb5af1ef8758cf1353cf3b42f940aee27ba21c # v3.0.2
with:
aws-access-key-id: ${{ secrets.ECR_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.ECR_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.ECR_PUBLIC_REGION }}
client-id: ${{ secrets.AZURE_ACR_RELEASE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: Push chart to public ECR
- name: Push chart to the public ACR
env:
ACR_PUBLIC_NAME: ${{ vars.ACR_PUBLIC_NAME }}
ACR_PUBLIC_LOGIN_SERVER: ${{ vars.ACR_PUBLIC_LOGIN_SERVER }}
run: |
set -euo pipefail
aws ecr-public get-login-password --region ${{ vars.ECR_PUBLIC_REGION }} \
| helm registry login --username AWS --password-stdin public.ecr.aws
helm push "$CHART_PACKAGE" oci://public.ecr.aws/openops/helm
echo "- ✅ Pushed \`oci://public.ecr.aws/openops/helm/openops:${CHART_VERSION}\`" >> $GITHUB_STEP_SUMMARY
# --expose-token rather than plain `az acr login`: that writes Docker's credential
# store, which helm does not read. helm wants its own registry login.
TOKEN=$(az acr login --name "$ACR_PUBLIC_NAME" --expose-token --output tsv --query accessToken)
helm registry login "$ACR_PUBLIC_LOGIN_SERVER" \
--username 00000000-0000-0000-0000-000000000000 --password-stdin <<<"$TOKEN"
helm push "$CHART_PACKAGE" "oci://$ACR_PUBLIC_LOGIN_SERVER/helm"
echo "- ✅ Pushed \`oci://$ACR_PUBLIC_LOGIN_SERVER/helm/openops:${CHART_VERSION}\`" >> $GITHUB_STEP_SUMMARY

- name: Create GitHub release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3.0.3
with:
target_commitish: ${{ github.sha }}
tag_name: ${{ env.CHART_VERSION }}
Expand All @@ -91,5 +102,5 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY
echo "Install with:" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "helm install openops oci://public.ecr.aws/openops/helm/openops --version $CHART_VERSION" >> $GITHUB_STEP_SUMMARY
echo "helm install openops oci://${{ vars.ACR_PUBLIC_LOGIN_SERVER }}/helm/openops --version $CHART_VERSION" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
20 changes: 11 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
- `/chart/values.ci.yaml`: Resource-constrained overlay for CI environments.
- `/chart/values.dev.yaml`: Development overlay for local development environments.
- `/chart/values.production.yaml`: Production overlay with externalized dependencies and cloud settings.
- `/chart/templates/`: Kubernetes manifests rendered by Helm (43 files). Includes deployments/statefulsets, services, configmaps (`configmap-*.yaml`), secrets (`secret-env.yaml`, `external-secret.yaml`), service accounts, PodDisruptionBudgets, HorizontalPodAutoscalers, NetworkPolicy, LimitRange, ServiceMonitor for Prometheus, and Helm tests. Shared template helpers live in `_helpers.tpl` (561 lines with 49+ helper functions). Postgres and Redis use StatefulSets with volumeClaimTemplates for stable storage and safe rollouts.
- `/chart/values.mcp-example.yaml`: Overlay enabling the opt-in MCP server for external agents; rendered by `helm-validate.yaml` so the enabled path stays valid.
- `/chart/templates/`: Kubernetes manifests rendered by Helm (48 files). Includes deployments/statefulsets, services, configmaps (`configmap-*.yaml`), secrets (`secret-env.yaml`, `external-secret.yaml`), service accounts, PodDisruptionBudgets, HorizontalPodAutoscalers, NetworkPolicy, LimitRange, ServiceMonitor for Prometheus, and Helm tests. Shared template helpers live in `_helpers.tpl` (561 lines with 49+ helper functions). Postgres and Redis use StatefulSets with volumeClaimTemplates for stable storage and safe rollouts.
- `/chart/templates/NOTES.txt`: Helm installation notes displayed after deployment with important warnings and next steps.
- `/chart/.helmignore`: Excludes development and repository files from packaged charts to reduce size and prevent leaking unnecessary files.
- `/LICENSE`: Apache 2.0 license for this Helm chart repository.
Expand All @@ -24,32 +25,33 @@

## Production features
- **Security-first design**: Security contexts enabled by default (runAsNonRoot, drop ALL capabilities, seccomp RuntimeDefault profile).
- **Service accounts**: Dedicated service accounts for each component (app, engine, tables, analytics, nginx, postgres, redis) with configurable annotations for AWS IAM roles (IRSA), GCP Workload Identity, or Azure Managed Identity.
- **Service accounts**: Dedicated service accounts for each component (app, worker, mcp, tables, analytics, nginx, postgres, redis) with configurable annotations for AWS IAM roles (IRSA), GCP Workload Identity, or Azure Managed Identity.
- **External Secrets Operator**: Built-in support for AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, and Azure Key Vault integration.
- **PodDisruptionBudgets (PDBs)**: Configured for `app`, `engine`, `nginx`, `analytics` and `tables` — note that `tables` is stateful (PVC, ReadWriteOnce), while the Postgres and Redis StatefulSets have no PDB. All default to `maxUnavailable: 1` so voluntary disruptions (node drains, upgrades) can always proceed. Each component also accepts `minAvailable` when `maxUnavailable` is unset, but avoid it where a component runs a single replica: `minAvailable: 1` then evaluates to `disruptionsAllowed: 0` and blocks every drain. `analytics` and `tables` default to one replica.
- **HorizontalPodAutoscalers (HPAs)**: Optional autoscaling for app, engine, analytics, and nginx based on CPU/memory metrics.
- **PodDisruptionBudgets (PDBs)**: Configured for `app`, `worker`, `mcp`, `nginx`, `analytics` and `tables` — note that `tables` is stateful (PVC, ReadWriteOnce), while the Postgres and Redis StatefulSets have no PDB. All default to `maxUnavailable: 1` so voluntary disruptions (node drains, upgrades) can always proceed. Each component also accepts `minAvailable` when `maxUnavailable` is unset, but avoid it where a component runs a single replica: `minAvailable: 1` then evaluates to `disruptionsAllowed: 0` and blocks every drain. `analytics` and `tables` default to one replica.
- **HorizontalPodAutoscalers (HPAs)**: Optional autoscaling for app, worker, mcp, analytics, and nginx based on CPU/memory metrics.
- **NetworkPolicy**: Optional network segmentation to restrict pod-to-pod communication and enforce least-privilege networking with explicit allow rules.
- **LimitRange**: Optional namespace-level resource defaults and constraints to prevent resource exhaustion.
- **ServiceMonitor**: Prometheus Operator integration for scraping application metrics from `/metrics` endpoints.
- **Helm tests**: Post-installation connectivity tests to validate deployment health.
- **MCP server (opt-in)**: `mcp.enabled` deploys `openops-mcp` behind nginx at `/mcp` and derives the API's OAuth env from `global.publicUrl`. The MCP pod renders only `mcp.env`. `OPS_OAUTH_RS_CLIENT_SECRET` is deliberately not a default `openopsEnvSecrets` key: External Secrets fails the whole sync on a missing property, so the key, `mcp.env` secret collection, nginx routes and NetworkPolicy are all gated on `mcp.enabled`, and `openops.validateMcp` fails the render if the key is undeclared, too short, or `global.publicUrl` is not https.
- **Validation helpers**: Runtime validation of required secrets (OPS_ENCRYPTION_KEY, OPS_JWT_SECRET, etc.) with helpful error messages at render time.

## Release workflow
- **`.github/workflows/release.yml`**: Packages the Helm chart and pushes it as an OCI artifact to `public.ecr.aws/openops/helm/openops`.
- **`.github/workflows/release.yml`**: Packages the Helm chart and pushes it as an OCI artifact to `openops.azurecr.io/helm/openops`.
Comment thread
Copilot marked this conversation as resolved.
- Triggered via `workflow_dispatch` with two inputs:
- `version` (required): The release version (e.g., `0.6.15`). Sets both `Chart.yaml` version/appVersion and `global.version` (image tags).
- `draft` (boolean, default `true`): When true, appends `-draft` to the chart version (e.g., `0.6.15-draft`). Draft versions are overwritable on ECR; final versions are immutable.
- `draft` (boolean, default `true`): When true, appends `-draft` to the chart version (e.g., `0.6.15-draft`). The suffix is a naming convention only - ACR does not enforce tag immutability unless it is configured on the registry, so a final version can be overwritten by a later push of the same version.
- Also triggered cross-repo by `openops-cloud/openops` release workflow.
- Creates a GitHub release (draft or published) with the packaged `.tgz` as an asset.
- **Do not bump versions in `Chart.yaml` or `values.yaml` manually**—the release workflow sets them at build time. The repo defaults are `version: 0.0.1-dev` and `appVersion: 0.0.1-dev`.
- Required secrets: `ECR_ACCESS_KEY_ID`, `ECR_SECRET_ACCESS_KEY`; required vars: `ECR_PUBLIC_REGION`.
- Authenticates with GitHub OIDC. Required secrets: `AZURE_ACR_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID`; required vars: `ACR_PUBLIC_NAME`, `ACR_PUBLIC_LOGIN_SERVER`.

## Versioning strategy
- All *release* versions are unified: chart version = appVersion = `global.version` (image tags) = OpenOps release version. The in-repo development defaults (`0.0.1-dev`) are normalized by the release workflow.
- Exception: draft releases use `{version}-draft` for the chart version only; `appVersion` and image tags use the clean version.
- The chart is published to `oci://public.ecr.aws/openops/helm/openops`. Users install with:
- The chart is published to `oci://openops.azurecr.io/helm/openops`. Users install with:
```
helm install openops oci://public.ecr.aws/openops/helm/openops --version <VERSION>
helm install openops oci://openops.azurecr.io/helm/openops --version <VERSION>
```

## PR lint rules
Expand Down
Loading
Loading