Skip to content

GitOps and CI

Myks writes its output to rendered/, and your deployment tool applies what is committed there. Two things keep that safe: every change to the inputs arrives together with its rendered output, and nothing reaches the default branch with stale output. This page shows CI workflows for both, with GitHub Actions examples that translate to any CI system.

myks init --ci github writes the two workflows below into .github/workflows/, pinned to the running myks release. In an existing project, myks init --print --components ci-github prints them.

  1. A contributor changes inputs in a branch and runs myks render, or lets CI do it.
  2. CI renders the pull request, validates the result, and either commits the output or fails when the committed output is stale.
  3. Reviewers read the rendered/ diff.
  4. After the merge, a check on the default branch catches output that is still stale (for example after a merge race), and the deployment tool syncs.

Smart Mode selects what to render from the files that differ from a base revision. It compares that revision with the working tree, so in a pull request use the merge base, not the tip of the target branch. Otherwise commits that landed on the target branch after the branch was created count as changes too:

Terminal window
base=$(git merge-base origin/main HEAD)
myks render --smart-mode.base-revision="$base"

The checkout needs enough history for the merge base: fetch the full history (fetch-depth: 0), or at least the target branch.

Preview the selection without rendering, for example to post it as a comment:

Terminal window
myks render --smart-mode.base-revision="$base" --smart-mode.only-print=json

This workflow renders what the pull request changed and pushes the result to the pull request branch, so contributors do not need myks installed. It works for branches of the same repository; pushing to forks needs a different setup.

.github/workflows/render.yaml
name: Render manifests
on:
pull_request:
permissions:
contents: write
concurrency:
group: render-${{ github.head_ref }}
cancel-in-progress: true
jobs:
render:
runs-on: ubuntu-latest
env:
# renovate: datasource=github-releases depName=mykso/myks
MYKS_VERSION: v5.16.0
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.head_ref }}
fetch-depth: 0
- uses: mykso/setup-action@v3.0.16
with:
myks: ${{ env.MYKS_VERSION }}
- name: Restore the source cache
uses: actions/cache@v6
with:
path: .myks/vendir-cache
key: myks-vendir-${{ github.run_id }}
restore-keys: myks-vendir-
- name: Render
env:
BASE_REF: ${{ github.base_ref }}
run: |
base=$(git merge-base "origin/$BASE_REF" HEAD)
myks render --smart-mode.base-revision="$base"
myks cleanup --manifests
# Commits pushed with GITHUB_TOKEN do not trigger workflow runs. If other
# required checks must run on this commit, push with a GitHub App token.
- name: Commit the rendered output
run: |
git add rendered
git diff --cached --quiet && exit 0
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
git commit -m 'chore: render manifests'
git push

GitHub-hosted Ubuntu runners include Helm. On other runners, install it before rendering.

Commits pushed with the default GITHUB_TOKEN do not trigger new workflow runs. If other required checks must run on the rendered commit, push with a GitHub App token or a deploy key instead.

If contributors render locally, CI only checks that they did. Replace the last step of the workflow above with:

- name: Check that rendered/ is up to date
run: |
git add --intent-to-add rendered
git diff --exit-code --stat rendered

--intent-to-add makes new files show up in the diff, so a forgotten new application fails the check too.

Rendering succeeds long before the result is deployable. A chart can emit a manifest the API server rejects, or a ytt directive can end up in the output verbatim. Run validators on the rendered output as a plugin, with the same selection as the render:

- name: Validate
run: |
base=$(git merge-base "origin/${{ github.base_ref }}" HEAD)
myks validate --smart-mode.base-revision="$base"

The plugins page has a validate plugin built on kubeconform. The same mechanism runs promtool check rules on Prometheus rules, conftest policies, or any other tool that takes file paths.

myks inspect apps --redundant exits non-zero when a values file sets a value to what it already inherits. Run it in CI to keep overrides minimal (see Inspecting and debugging).

Even with checks on pull requests, the default branch can end up with stale output: two pull requests that touch the same application merged one after the other, a check that was skipped, a direct push. Render every push to the default branch against the previous commit, and fail when the output changes. The first push of the branch has no previous commit, so it renders everything:

.github/workflows/drift.yaml
name: Check rendered manifests
on:
push:
permissions:
contents: read
jobs:
drift:
if: github.ref_name == github.event.repository.default_branch
runs-on: ubuntu-latest
env:
# renovate: datasource=github-releases depName=mykso/myks
MYKS_VERSION: v5.16.0
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: mykso/setup-action@v3.0.16
with:
myks: ${{ env.MYKS_VERSION }}
- name: Restore the source cache
uses: actions/cache@v6
with:
path: .myks/vendir-cache
key: myks-vendir-${{ github.run_id }}
restore-keys: myks-vendir-
- name: Render
env:
BEFORE: ${{ github.event.before }}
run: |
# The first push of a branch has no previous commit to compare with.
if git cat-file -e "$BEFORE^{commit}" 2>/dev/null; then
myks render --smart-mode.base-revision="$BEFORE"
else
myks render ALL ALL
fi
- name: Fail on drift
run: |
git add --intent-to-add rendered
git diff --exit-code --stat rendered

Instead of failing, the job can open a pull request with the re-rendered output (for example with peter-evans/create-pull-request) and assign it to the author of the commit. Keep the base revision of the first detection while that pull request is open: rendering later pushes against their own previous commits makes Smart Mode skip the applications that drifted.

A scheduled full render (myks render ALL ALL) catches the remaining case: sources with moving references, such as Git branches synced with lazy: false, change without any commit in your repository.

Pass credentials for protected chart repositories and Git servers as VENDIR_SECRET_<NAME>_USERNAME and VENDIR_SECRET_<NAME>_PASSWORD environment variables from CI secrets (see Sources):

- name: Render
env:
VENDIR_SECRET_CHARTS_USERNAME: ${{ secrets.CHARTS_USERNAME }}
VENDIR_SECRET_CHARTS_PASSWORD: ${{ secrets.CHARTS_PASSWORD }}
run: myks render --smart-mode.base-revision="$base"
  • Cache .myks/vendir-cache/ between runs, as above. With lazy: true sources, a warm cache means no downloads at all.
  • Limit parallelism on small runners with --async (or MYKS_ASYNC); the default runs every application at once.
  • --print-stats prints how much CPU and memory each tool used, to find the expensive applications.

See Optimizations.