diff --git a/.ci/ct/chart_schema.yaml b/.ci/ct/chart_schema.yaml new file mode 100644 index 00000000..2a26d9ba --- /dev/null +++ b/.ci/ct/chart_schema.yaml @@ -0,0 +1,37 @@ +name: str() +home: str(required=False) +version: str() +apiVersion: str() +appVersion: any(str(), num(), required=False) +description: str(required=False) +keywords: list(str(), required=False) +sources: list(str(), required=False) +maintainers: list(include('maintainer'), required=False) +dependencies: list(include('dependency'), required=False) +icon: str(required=False) +engine: str(required=False) +condition: str(required=False) +tags: str(required=False) +deprecated: bool(required=False) +kubeVersion: str(required=False) +annotations: map(str(), str(), required=False) +type: str(required=False) +--- +maintainer: + name: str() + email: str(required=False) + url: str(required=False) +--- +dependency: + name: str() + version: str() + repository: str(required=False) + condition: str(required=False) + tags: list(str(), required=False) + enabled: bool(required=False) + import-values: any(list(str()), list(include('import-value')), required=False) + alias: str(required=False) +--- +import-value: + child: str() + parent: str() diff --git a/.ci/ct/ct-lint.yaml b/.ci/ct/ct-lint.yaml new file mode 100644 index 00000000..7db37dcc --- /dev/null +++ b/.ci/ct/ct-lint.yaml @@ -0,0 +1,17 @@ +chart-yaml-schema: .ci/ct/chart_schema.yaml +lint-conf: .ci/ct/lintconf.yaml + +remote: origin +target-branch: main + +helm-extra-args: --timeout 600s + +chart-dirs: + - charts/library + - charts/stable + +excluded-charts: + +chart-repos: + - bitnami=https://charts.bitnami.com/bitnami + - bjw-s=https://bjw-s.github.io/helm-charts diff --git a/.ci/ct/lintconf.yaml b/.ci/ct/lintconf.yaml new file mode 100644 index 00000000..90f48c88 --- /dev/null +++ b/.ci/ct/lintconf.yaml @@ -0,0 +1,42 @@ +--- +rules: + braces: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: -1 + max-spaces-inside-empty: -1 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 2 + document-end: disable + document-start: disable # No --- to start a file + empty-lines: + max: 2 + max-start: 0 + max-end: 0 + hyphens: + max-spaces-after: 1 + indentation: + spaces: consistent + indent-sequences: whatever # - list indentation will handle both indentation and without + check-multi-line-strings: false + key-duplicates: enable + line-length: disable # Lines can be any length + new-line-at-end-of-file: enable + new-lines: + type: unix + trailing-spaces: enable + truthy: + level: warning diff --git a/.github/actions/collect-changes/action.yaml b/.github/actions/collect-changes/action.yaml new file mode 100644 index 00000000..7f53c79d --- /dev/null +++ b/.github/actions/collect-changes/action.yaml @@ -0,0 +1,45 @@ +name: "Collect changes" +description: "Collects and stores changed files/charts" + +outputs: + changesDetected: + description: "Whether or not changes to charts have been detected" + value: ${{ steps.filter.outputs.addedOrModified }} + addedOrModifiedFiles: + description: "A list of the files changed" + value: ${{ steps.filter.outputs.addedOrModified_files }} + addedOrModifiedCharts: + description: "A list of the charts changed" + value: ${{ steps.filter-charts.outputs.addedOrModified }} + +runs: + using: "composite" + steps: + - name: Collect changed files + uses: dorny/paths-filter@v2 + id: filter + with: + list-files: shell + filters: | + addedOrModified: + - added|modified: 'charts/*/**' + + - name: Collect changed charts + if: | + steps.filter.outputs.addedOrModified == 'true' + id: filter-charts + shell: bash + run: | + CHARTS=() + PATHS=(${{ steps.filter.outputs.addedOrModified_files }}) + # Get only the chart paths + for CHARTPATH in "${PATHS[@]}" + do + IFS='/' read -r -a path_parts <<< "${CHARTPATH}" + CHARTS+=("${path_parts[1]}/${path_parts[2]}") + done + + # Remove duplicates + CHARTS=( `printf "%s\n" "${CHARTS[@]}" | sort -u` ) + # Set output to changed charts + printf "::set-output name=addedOrModified::%s\n" "${CHARTS[*]}" diff --git a/.github/actions/label-from-status/action.yaml b/.github/actions/label-from-status/action.yaml new file mode 100644 index 00000000..a89fc9c6 --- /dev/null +++ b/.github/actions/label-from-status/action.yaml @@ -0,0 +1,48 @@ +name: "Set issue labels based on status" +description: "Sets / removes issue labels based on CI job status" +inputs: + token: + required: true + description: "The Github API token to use" + issue-number: + required: true + description: "The issue to label" + prefix: + required: true + description: "The label prefix (e.g. lint, install)" + job-status: + required: true + description: "The status of the CI job" + remove-on-skipped: + required: false + default: "false" + description: "Remove the label if the job was skipped" + +runs: + using: "composite" + steps: + - name: Label success + uses: andymckay/labeler@1.0.4 + if: ${{ inputs.job-status == 'success' }} + with: + repo-token: ${{ inputs.token }} + issue-number: ${{ inputs.issue-number }} + add-labels: "${{ inputs.prefix }}:ok" + remove-labels: "${{ inputs.prefix }}:failed" + + - name: Label failure + uses: andymckay/labeler@1.0.4 + if: ${{ inputs.job-status == 'failure' }} + with: + repo-token: ${{ inputs.token }} + issue-number: ${{ inputs.issue-number }} + add-labels: "${{ inputs.prefix }}:failed" + remove-labels: "${{ inputs.prefix }}:ok" + + - name: Remove label + uses: andymckay/labeler@1.0.4 + if: ${{ (inputs.job-status == 'skipped') && (inputs.remove-on-skipped == 'true') }} + with: + repo-token: ${{ inputs.token }} + issue-number: ${{ inputs.issue-number }} + remove-labels: "${{ inputs.prefix }}:ok, ${{ inputs.prefix }}:failed" diff --git a/.github/workflows/charts-lint-test.yaml b/.github/workflows/charts-lint-test.yaml new file mode 100644 index 00000000..ee207700 --- /dev/null +++ b/.github/workflows/charts-lint-test.yaml @@ -0,0 +1,53 @@ +name: "Charts: Lint" + +on: + workflow_call: + inputs: + checkoutCommit: + required: true + type: string + chartChangesDetected: + required: true + type: string + +jobs: + lint: + name: Lint charts + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ inputs.checkoutCommit }} + + - name: Install Kubernetes tools + uses: yokawasa/action-setup-kube-tools@v0.8.2 + with: + setup-tools: | + helmv3 + helm: "3.8.0" + + - name: Install Helm + uses: Azure/setup-helm@v3.3 + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.2.1 + + - name: Collect changes + id: list-changed + if: inputs.chartChangesDetected == 'true' + run: | + EXCLUDED=$(yq eval -o=json '.excluded-charts // []' .ci/ct/ct-lint.yaml) + CHARTS=$(ct list-changed --config .ci/ct/ct-lint.yaml) + CHARTS_JSON=$(echo "${CHARTS}" | jq -R -s -c 'split("\n")[:-1]') + OUTPUT_JSON=$(echo "{\"excluded\": ${EXCLUDED}, \"all\": ${CHARTS_JSON}}" | jq -c '.all-.excluded') + echo ::set-output name=charts::${OUTPUT_JSON} + if [[ $(echo ${OUTPUT_JSON} | jq -c '. | length') -gt 0 ]]; then + echo "::set-output name=detected::true" + fi + + - name: Run chart-testing (lint) + id: lint + if: steps.list-changed.outputs.detected == 'true' + run: ct lint --config .ci/ct/ct-lint.yaml diff --git a/.github/workflows/pr-metadata.yaml b/.github/workflows/pr-metadata.yaml new file mode 100644 index 00000000..0496a185 --- /dev/null +++ b/.github/workflows/pr-metadata.yaml @@ -0,0 +1,60 @@ +name: "Pull Request: Get metadata" + +on: + workflow_call: + outputs: + isRenovatePR: + description: "Is the PR coming from Renovate?" + value: ${{ jobs.pr-metadata.outputs.isRenovatePR }} + isFork: + description: "Is the PR coming from a forked repo?" + value: ${{ jobs.pr-metadata.outputs.isFork }} + addedOrModified: + description: "Does the PR contain any changes?" + value: ${{ jobs.pr-changes.outputs.addedOrModified }} + addedOrModifiedFiles: + description: "A list of the files changed in this PR" + value: ${{ jobs.pr-changes.outputs.addedOrModifiedFiles }} + addedOrModifiedCharts: + description: "A list of the charts changed in this PR" + value: ${{ jobs.pr-changes.outputs.addedOrModifiedCharts }} + +jobs: + pr-metadata: + name: Collect PR metadata + runs-on: ubuntu-latest + outputs: + isRenovatePR: ${{ startsWith(steps.branch-name.outputs.current_branch, 'renovate/') }} + isFork: ${{ github.event.pull_request.head.repo.full_name != github.repository }} + steps: + - name: Get branch name + id: branch-name + uses: tj-actions/branch-names@v5.4 + + - name: Save PR data to file + env: + PR_NUMBER: ${{ github.event.number }} + run: | + echo $PR_NUMBER > pr_number.txt + + - name: Store pr data in artifact + uses: actions/upload-artifact@v3 + with: + name: pr_metadata + path: ./pr_number.txt + retention-days: 5 + + pr-changes: + name: Collect PR changes + runs-on: ubuntu-latest + outputs: + addedOrModified: ${{ steps.collect-changes.outputs.changesDetected }} + addedOrModifiedFiles: ${{ steps.collect-changes.outputs.addedOrModifiedFiles }} + addedOrModifiedCharts: ${{ steps.collect-changes.outputs.addedOrModifiedCharts }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Collect changes + id: collect-changes + uses: ./.github/actions/collect-changes diff --git a/.github/workflows/pr-validate.yaml b/.github/workflows/pr-validate.yaml new file mode 100644 index 00000000..119a8103 --- /dev/null +++ b/.github/workflows/pr-validate.yaml @@ -0,0 +1,55 @@ +name: "Pull Request: Validate" + +on: + pull_request: + branches: + - main + - development + types: + - opened + - edited + - reopened + - ready_for_review + - synchronize + +concurrency: + group: ${{ github.head_ref }}-pr-validate + cancel-in-progress: true + +jobs: + pr-metadata: + uses: bjw-s/charts/.github/workflows/pr-metadata.yaml@main + + pre-commit-check: + uses: bjw-s/charts/.github/workflows/pre-commit-check.yaml@main + needs: + - pr-metadata + with: + modifiedFiles: ${{ needs.pr-metadata.outputs.addedOrModifiedFiles }} + + # charts-changelog: + # uses: bjw-s/charts/.github/workflows/charts-changelog.yaml@main + # needs: + # - pr-metadata + # - pre-commit-check + # with: + # isRenovatePR: ${{ needs.pr-metadata.outputs.isRenovatePR }} + # modifiedCharts: ${{ needs.pr-metadata.outputs.addedOrModifiedCharts }} + + charts-lint: + uses: bjw-s/charts/.github/workflows/charts-lint.yaml@main + needs: + - pr-metadata + - charts-changelog + with: + checkoutCommit: ${{ needs.charts-changelog.outputs.commitHash }} + chartChangesDetected: ${{ needs.pr-metadata.outputs.addedOrModified }} + + # charts-test: + # uses: bjw-s/charts/.github/workflows/charts-test.yaml@main + # needs: + # - pr-metadata + # - charts-changelog + # with: + # checkoutCommit: ${{ needs.charts-changelog.outputs.commitHash }} + # chartChangesDetected: ${{ needs.pr-metadata.outputs.addedOrModified }} diff --git a/.taskfiles/charts.yaml b/.taskfiles/charts.yaml new file mode 100644 index 00000000..fb0ed894 --- /dev/null +++ b/.taskfiles/charts.yaml @@ -0,0 +1,15 @@ +--- +version: "3" + +tasks: + lint: + desc: Run ct-lint on charts + cmds: + - docker run --rm -it --workdir=/data --volume $(pwd):/data quay.io/helmpack/chart-testing:v3.6.0 ct lint --config .ci/ct/ct-lint.yaml + silent: true + + lint-all: + desc: Run ct-lint on all charts + cmds: + - docker run --rm -it --workdir=/data --volume $(pwd):/data quay.io/helmpack/chart-testing:v3.6.0 ct lint --config .ci/ct/ct-lint.yaml --all + silent: true diff --git a/Taskfile.yaml b/Taskfile.yaml new file mode 100644 index 00000000..214e993a --- /dev/null +++ b/Taskfile.yaml @@ -0,0 +1,15 @@ +--- +version: "3" + +vars: + PROJECT_DIR: + sh: "git rev-parse --show-toplevel" + +includes: + charts: .taskfiles/charts.yaml + +tasks: + default: + silent: true + cmds: + - task -l