diff --git a/.cm/gitstream.cm b/.cm/gitstream.cm new file mode 100644 index 0000000..4ef8c65 --- /dev/null +++ b/.cm/gitstream.cm @@ -0,0 +1,162 @@ +# -*- mode: yaml -*- +# This example configuration provides basic automations to get started with gitStream. +# View the gitStream quickstart for more examples: https://docs.gitstream.cm/examples/ +manifest: + version: 1.0 + + +automations: + # Use LinearB's AI service to review the changes + linearb_ai_review: + on: + - pr_created + - commit + if: + - {{ not pr.draft }} + - {{ not is.bot }} + run: + - action: code-review@v1 + args: + approve_on_LGTM: {{ calc.safe_changes }} + + # Use LinearB's AI service to add a description to the PR + linearb_ai_description: + on: + - pr_created + - commit + if: + - {{ not pr.draft }} + - {{ not is.bot }} + run: + - action: describe-changes@v1 + args: + concat_mode: append + + # Add a label indicating how long it will take to review the PR. + estimated_time_to_review: + if: + - true + run: + - action: add-label@v1 + args: + label: "{{ calc.etr }} min review" + color: {{ colors.red if (calc.etr >= 20) else ( colors.yellow if (calc.etr >= 5) else colors.green ) }} + + # Inform PR authors when they fail to reference Jira tickets in the PR title or description. + label_missing_jira_info: + if: + - {{ not (has.jira_ticket_in_title or has.jira_ticket_in_desc) }} + run: + - action: add-label@v1 + args: + label: "missing-jira" + color: {{ colors.red }} + - action: add-comment@v1 + args: + comment: | + This PR is missing a Jira ticket reference in the title or description. + Please add a Jira ticket reference to the title or description of this PR. + + # Label PRs where the user indicated Claude Code usage via checkbox + label_claude_code_pr: + on: + - pr_created + - commit + if: + - {{ pr.comments | filter(attr='commenter', term='gitstream-cm') | filter (attr='content', regex=r/\- \[x\] Claude Code Assisted/) | some}} + run: + - action: add-label@v1 + args: + label: '🤖 Claude Code' + + # Label PRs that contain Claude Code commits (by detecting commit message signature) + label_claude_code_by_commit: + on: + - pr_created + - commit + if: + - {{ source.commits | some(attr='message', regex=r/🤖 Generated with \[Claude Code\]|Co-Authored-By: Claude /) }} + run: + - action: add-label@v1 + args: + label: '🤖 Claude Code' + + # Label PRs created with GitHub Copilot assistance via checkbox + label_github_copilot_pr: + on: + - pr_created + - commit + if: + - {{ pr.comments | filter(attr='commenter', term='gitstream-cm') | filter (attr='content', regex=r/\- \[x\] GitHub Copilot Assisted/) | some}} + run: + - action: add-label@v1 + args: + label: '🤖 GitHub Copilot' + + # Label PRs that contain GitHub Copilot commits (by detecting commit message patterns) + label_github_copilot_by_commit: + on: + - pr_created + - commit + if: + - {{ source.commits | some(attr='message', regex=r/Co-authored-by: github-copilot|Generated by GitHub Copilot|Copilot|with GitHub Copilot/i) }} + run: + - action: add-label@v1 + args: + label: '🤖 GitHub Copilot' + + # Label Claude Code PRs - Label by Tag + label_claude_code: + if: + - {{ claude_code_tag.pr_title or claude_code_tag.pr_desc or claude_code_tag.pr_comments or claude_code_tag.commit_messages }} + run: + - action: add-label@v1 + args: + label: '🤖 Claude Code' + + # Label GitHub Copilot PRs - Label by Tag + label_copilot: + if: + - {{ copilot_tag.pr_title or copilot_tag.pr_desc or copilot_tag.pr_comments or copilot_tag.commit_messages }} + run: + - action: add-label@v1 + args: + label: '🤖 Copilot' + +claude_code_tag: + pr_title: {{ pr.title | includes(regex=r/#claude_code#/) }} + pr_desc: {{pr.description | includes(regex=r/#claude_code#/) }} + pr_comments: {{ pr.comments | map(attr='content') | match(regex=r/#claude_code#/) | some }} + commit_messages: {{ branch.commits.messages | match(regex=r/#claude_code#/) | some }} + +copilot_tag: + pr_title: {{ pr.title | includes(regex=r/#copilot#/) }} + pr_desc: {{ pr.description | includes(regex=r/#copilot#/) }} + pr_comments: {{ pr.comments | map(attr='content') | match(regex=r/#copilot#/) | some }} + commit_messages: {{ branch.commits.messages | match(regex=r/#copilot#/) | some }} + + +# +----------------------------------------------------------------------------+ +# | Custom Expressions | +# | https://docs.gitstream.cm/how-it-works/#custom-expressions | +# +----------------------------------------------------------------------------+ + +calc: + etr: {{ branch | estimatedReviewTime }} + safe_changes: {{ is.formatting or is.docs or is.tests or is.image }} + +has: + jira_ticket_in_title: {{ pr.title | includes(regex=r/\b[A-Za-z]+-\d+\b/) }} + jira_ticket_in_desc: {{ pr.description | includes(regex=r/atlassian.net\/browse\/\w{1,}-\d{3,4}/) }} + +colors: + red: 'b60205' + yellow: 'fbca04' + green: '0e8a16' + +is: + formatting: {{ source.diff.files | isFormattingChange }} + docs: {{ files | allDocs }} + tests: {{ files | allTests }} + image: {{ files | allImages }} + bot: {{ pr.author | match(list=['github-actions', '_bot_', '[bot]', 'dependabot']) | some }} diff --git a/.github/workflows/fossa.yaml b/.github/workflows/fossa.yaml new file mode 100644 index 0000000..367c5e5 --- /dev/null +++ b/.github/workflows/fossa.yaml @@ -0,0 +1,16 @@ +--- +name: Fossa + +on: + push: + workflow_dispatch: + +jobs: + fossa: + uses: Updater/.github-private/.github/workflows/fossa.yaml@main + secrets: + fossa-api-key: ${{ secrets.FOSSA_API_KEY }} + token: ${{ secrets.GITHUB_TOKEN }} + with: + repository: ${{ github.repository }} + ref: ${{ github.ref }} diff --git a/.github/workflows/gitstream.yml b/.github/workflows/gitstream.yml new file mode 100644 index 0000000..4720f73 --- /dev/null +++ b/.github/workflows/gitstream.yml @@ -0,0 +1,49 @@ +# Code generated by gitStream GitHub app - DO NOT EDIT + +name: gitStream workflow automation +run-name: | + /:\ gitStream: PR #${{ fromJSON(fromJSON(github.event.inputs.client_payload)).pullRequestNumber }} from ${{ github.event.inputs.full_repository }} + +on: + workflow_dispatch: + inputs: + client_payload: + description: The Client payload + required: true + full_repository: + description: the repository name include the owner in `owner/repo_name` format + required: true + head_ref: + description: the head sha + required: true + base_ref: + description: the base ref + required: true + installation_id: + description: the installation id + required: false + resolver_url: + description: the resolver url to pass results to + required: true + resolver_token: + description: Optional resolver token for resolver service + required: false + default: '' + +jobs: + gitStream: + timeout-minutes: 15 + runs-on: ubuntu-latest + name: gitStream workflow automation + steps: + - name: Evaluate Rules + uses: linear-b/gitstream-github-action@v2-lite + id: rules-engine + with: + full_repository: ${{ github.event.inputs.full_repository }} + head_ref: ${{ github.event.inputs.head_ref }} + base_ref: ${{ github.event.inputs.base_ref }} + client_payload: ${{ github.event.inputs.client_payload }} + installation_id: ${{ github.event.inputs.installation_id }} + resolver_url: ${{ github.event.inputs.resolver_url }} + resolver_token: ${{ github.event.inputs.resolver_token }} diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index dbb9cb3..016d2d6 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -9,7 +9,7 @@ jobs: name: Install chart-testing and test presence in path steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install chart-testing uses: ./ - name: Check install! @@ -17,8 +17,8 @@ jobs: ct version CT_VERSION_OUTPUT=$(ct version 2>&1 /dev/null) ACTUAL_VERSION=$(echo "$CT_VERSION_OUTPUT" | grep Version | rev | cut -d ' ' -f1 | rev) - if [[ $ACTUAL_VERSION != 'v3.10.1' ]]; then - echo 'should be v3.10.1' + if [[ $ACTUAL_VERSION != 'v3.14.0' ]]; then + echo 'should be v3.14.0' exit 1 else exit 0 @@ -38,7 +38,7 @@ jobs: name: Install Custom chart-testing and test presence in path steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install chart-testing uses: ./ with: @@ -65,3 +65,49 @@ jobs: else exit 0 fi + + test_ct_action_with_helm: + runs-on: ubuntu-latest + + name: run action to test a helm chart + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1 + with: + version: v3.17.0 + + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.x' + check-latest: true + + - name: Install chart-testing + uses: ./ + + - run: | + sed -i "s/version: .*/version: 2.0.0/" testdata/simple-deployment/Chart.yaml + cat testdata/simple-deployment/Chart.yaml + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --chart-dirs=testdata --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Run chart-testing (lint) + if: steps.list-changed.outputs.changed == 'true' + run: ct lint --chart-dirs=testdata --target-branch ${{ github.event.repository.default_branch }} + + - name: Create kind cluster + if: steps.list-changed.outputs.changed == 'true' + uses: helm/kind-action@v1.13.0 + + - name: Run chart-testing (install) + if: steps.list-changed.outputs.changed == 'true' + run: ct install --chart-dirs=testdata --target-branch ${{ github.event.repository.default_branch }} diff --git a/README.md b/README.md index 55d92f9..3c762d8 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,15 @@ A GitHub Action for installing the [helm/chart-testing](https://github.com/helm/ 1. A GitHub repo containing a directory with your Helm charts (e.g: `charts`) 1. A workflow YAML file in your `.github/workflows` directory. An [example workflow](#example-workflow) is available below. - For more information, reference the GitHub Help Documentation for [Creating a workflow file](https://help.github.com/en/articles/configuring-a-workflow#creating-a-workflow-file) + For more information, reference the GitHub Help Documentation for [Creating a workflow file](https://docs.github.com/en/actions/writing-workflows/quickstart#creating-your-first-workflow) ### Inputs For more information on inputs, see the [API Documentation](https://developer.github.com/v3/repos/releases/#input) -- `version`: The chart-testing version to install (default: `3.9.0`) -- `yamllint_version`: The chart-testing version to install (default: `1.27.1`) -- `yamale_version`: The chart-testing version to install (default: `3.0.4`) +- `version`: The chart-testing version to install (default: `3.14.0`) +- `yamllint_version`: The `yamllint` version to install (default: `1.33.0`) +- `yamale_version`: The `yamale` version to install (default: `6.0.0`) ### Example Workflow @@ -32,27 +32,29 @@ name: Lint and Test Charts on: pull_request +permissions: {} + jobs: lint-test: runs-on: ubuntu-latest + permisions: + contents: read steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5.0.0 with: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v3 - with: - version: v3.12.1 + uses: azure/setup-helm@v4.3.1 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v6.0.0 with: - python-version: '3.10' + python-version: '3.x' check-latest: true - name: Set up chart-testing - uses: helm/chart-testing-action@v2.6.0 + uses: helm/chart-testing-action@v2.8.0 - name: Run chart-testing (list-changed) id: list-changed @@ -68,7 +70,7 @@ jobs: - name: Create kind cluster if: steps.list-changed.outputs.changed == 'true' - uses: helm/kind-action@v1.8.0 + uses: helm/kind-action@v1.12.0 - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' diff --git a/action.yml b/action.yml index b4d131b..df3006d 100644 --- a/action.yml +++ b/action.yml @@ -6,21 +6,22 @@ branding: icon: anchor inputs: version: - description: "The chart-testing version to install (default: 3.10.1)" + description: "The chart-testing version to install (default: 3.12.0)" required: false - default: '3.10.1' + default: '3.14.0' yamllint_version: description: "The yamllint version to install (default: 1.27.1)" required: false - default: '1.27.1' + default: '1.33.0' yamale_version: - description: "The yamale version to install (default: 3.0.4)" + description: "The yamale version to install (default: 6.0.0)" required: false - default: '3.0.4' + default: '6.0.0' runs: using: composite steps: - - uses: sigstore/cosign-installer@9614fae9e5c5eddabb09f90a270fcb487c9f7149 # v3.3.0 + - uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 + - uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c # v7.0.0 - run: | cd $GITHUB_ACTION_PATH \ && ./ct.sh \ diff --git a/ct.sh b/ct.sh index 4394de4..93931e8 100755 --- a/ct.sh +++ b/ct.sh @@ -4,9 +4,9 @@ set -o errexit set -o nounset set -o pipefail -DEFAULT_CHART_TESTING_VERSION=3.10.1 -DEFAULT_YAMLLINT_VERSION=1.27.1 -DEFAULT_YAMALE_VERSION=3.0.4 +DEFAULT_CHART_TESTING_VERSION=3.14.0 +DEFAULT_YAMLLINT_VERSION=1.33.0 +DEFAULT_YAMALE_VERSION=6.0.0 show_help() { cat << EOF @@ -92,11 +92,11 @@ install_chart_testing() { mkdir -p "${cache_dir}" echo "Installing chart-testing v${version}..." - CT_CERT=https://github.com/helm/chart-testing/releases/download/v$version/chart-testing_${version#v}_linux_$arch.tar.gz.pem - CT_SIG=https://github.com/helm/chart-testing/releases/download/v$version/chart-testing_${version#v}_linux_$arch.tar.gz.sig + CT_CERT=https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version#v}_linux_${arch}.tar.gz.pem + CT_SIG=https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version#v}_linux_${arch}.tar.gz.sig - curl --retry 5 --retry-delay 1 -sSLo ct.tar.gz "https://github.com/helm/chart-testing/releases/download/v$version/chart-testing_${version#v}_linux_$arch.tar.gz" - cosign verify-blob --certificate $CT_CERT --signature $CT_SIG \ + curl --retry 5 --retry-delay 1 -sSLo ct.tar.gz "https://github.com/helm/chart-testing/releases/download/v${version}/chart-testing_${version#v}_linux_${arch}.tar.gz" + cosign verify-blob --certificate "${CT_CERT}" --signature "${CT_SIG}" \ --certificate-identity "https://github.com/helm/chart-testing/.github/workflows/release.yaml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" ct.tar.gz retVal=$? @@ -109,17 +109,15 @@ install_chart_testing() { rm -f ct.tar.gz echo 'Creating virtual Python environment...' - python3 -m venv "${venv_dir}" - - echo 'Activating virtual environment...' - # shellcheck disable=SC1090 - source "${venv_dir}/bin/activate" + export UV_LINK_MODE=copy + uv venv "${venv_dir}" + export VIRTUAL_ENV="${venv_dir}" echo 'Installing yamllint...' - pip3 install "yamllint==${yamllint_version}" + uv pip install "yamllint==${yamllint_version}" echo 'Installing Yamale...' - pip3 install "yamale==${yamale_version}" + uv pip install "yamale==${yamale_version}" fi # https://github.com/helm/chart-testing-action/issues/62 diff --git a/testdata/simple-deployment/Chart.yaml b/testdata/simple-deployment/Chart.yaml new file mode 100644 index 0000000..8ac9244 --- /dev/null +++ b/testdata/simple-deployment/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: nginx +version: 0.1.0 +maintainers: + - name: cpanato + - name: davidkarlsen diff --git a/testdata/simple-deployment/README.md b/testdata/simple-deployment/README.md new file mode 100644 index 0000000..280f67f --- /dev/null +++ b/testdata/simple-deployment/README.md @@ -0,0 +1,4 @@ +Simple chart with a Deployment. + +The integration test will install first simple-deployment and then try to upgrade +to simple-deployment-different-selector failing as expected diff --git a/testdata/simple-deployment/templates/_helpers.tpl b/testdata/simple-deployment/templates/_helpers.tpl new file mode 100644 index 0000000..165d01e --- /dev/null +++ b/testdata/simple-deployment/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "nginx.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "nginx.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "nginx.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/testdata/simple-deployment/templates/deployment.yaml b/testdata/simple-deployment/templates/deployment.yaml new file mode 100644 index 0000000..ac64e0d --- /dev/null +++ b/testdata/simple-deployment/templates/deployment.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "nginx.fullname" . }} + labels: + app.kubernetes.io/name: {{ include "nginx.name" . }} + helm.sh/chart: {{ include "nginx.chart" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: {{ include "nginx.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "nginx.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP diff --git a/testdata/simple-deployment/values.yaml b/testdata/simple-deployment/values.yaml new file mode 100644 index 0000000..cefd3a0 --- /dev/null +++ b/testdata/simple-deployment/values.yaml @@ -0,0 +1,11 @@ +# Default values for nginx. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +image: + repository: nginx + tag: stable + pullPolicy: IfNotPresent + +nameOverride: "" +fullnameOverride: ""