From b726409caaa298a039f25159a0c6dc6ec833d847 Mon Sep 17 00:00:00 2001 From: Markus Pesch Date: Wed, 23 Jul 2025 18:14:01 +0200 Subject: [PATCH] Initial Commit --- .editorconfig | 12 + .gitea/scripts/add-annotations.sh | 114 + .gitea/workflows/generate-readme.yaml | 32 + .gitea/workflows/helm.yaml | 42 + .gitea/workflows/markdown-linters.yaml | 46 + .gitea/workflows/release.yaml | 61 + .gitignore | 6 + .helmignore | 60 + .markdownlint.yaml | 155 + .markdownlintignore | 4 + .npmrc | 1 + .prettierignore | 1 + .vscode/extensions.json | 8 + .yamllint.yaml | 20 + CODEOWNERS | 1 + CONTRIBUTING.md | 82 + Chart.yaml | 26 + LICENSE | 21 + Makefile | 91 + README.md | 293 ++ package-lock.json | 2646 +++++++++++++++++ package.json | 21 + renovate.json | 68 + templates/_common.tpl | 58 + templates/_deployment.tpl | 75 + templates/_hpa.tpl | 19 + templates/_ingress.tpl | 19 + templates/_networkPolicy.tpl | 19 + templates/_persistentVolumeClaim.tpl | 25 + templates/_pod.tpl | 17 + templates/_serviceAccount.tpl | 17 + templates/_services.tpl | 27 + templates/deployment.yaml | 140 + templates/hpa.yaml | 29 + templates/ingress.yaml | 45 + templates/networkPolicy.yaml | 32 + templates/persistentVolumeClaim.yaml | 24 + templates/service.yaml | 57 + templates/serviceAccount.yaml | 25 + unittests/deployment/deployment.yaml | 447 +++ .../mountPersistentVolumeClaim.yaml | 52 + unittests/hpa/hpa.yaml | 152 + unittests/ingress/http.yaml | 110 + unittests/networkPolicy/networkPolicy.yaml | 98 + .../persistentVolumeClaim.yaml | 97 + unittests/serviceAccount/serviceAccount.yaml | 79 + unittests/services/service.yaml | 172 ++ values.yaml | 412 +++ 48 files changed, 6058 insertions(+) create mode 100644 .editorconfig create mode 100755 .gitea/scripts/add-annotations.sh create mode 100644 .gitea/workflows/generate-readme.yaml create mode 100644 .gitea/workflows/helm.yaml create mode 100644 .gitea/workflows/markdown-linters.yaml create mode 100644 .gitea/workflows/release.yaml create mode 100644 .gitignore create mode 100644 .helmignore create mode 100644 .markdownlint.yaml create mode 100644 .markdownlintignore create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .vscode/extensions.json create mode 100644 .yamllint.yaml create mode 100644 CODEOWNERS create mode 100644 CONTRIBUTING.md create mode 100644 Chart.yaml create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 renovate.json create mode 100644 templates/_common.tpl create mode 100644 templates/_deployment.tpl create mode 100644 templates/_hpa.tpl create mode 100644 templates/_ingress.tpl create mode 100644 templates/_networkPolicy.tpl create mode 100644 templates/_persistentVolumeClaim.tpl create mode 100644 templates/_pod.tpl create mode 100644 templates/_serviceAccount.tpl create mode 100644 templates/_services.tpl create mode 100644 templates/deployment.yaml create mode 100644 templates/hpa.yaml create mode 100644 templates/ingress.yaml create mode 100644 templates/networkPolicy.yaml create mode 100644 templates/persistentVolumeClaim.yaml create mode 100644 templates/service.yaml create mode 100644 templates/serviceAccount.yaml create mode 100644 unittests/deployment/deployment.yaml create mode 100644 unittests/deployment/mountPersistentVolumeClaim.yaml create mode 100644 unittests/hpa/hpa.yaml create mode 100644 unittests/ingress/http.yaml create mode 100644 unittests/networkPolicy/networkPolicy.yaml create mode 100644 unittests/persistentVolumeClaim/persistentVolumeClaim.yaml create mode 100644 unittests/serviceAccount/serviceAccount.yaml create mode 100644 unittests/services/service.yaml create mode 100644 values.yaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b53e68c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.gitea/scripts/add-annotations.sh b/.gitea/scripts/add-annotations.sh new file mode 100755 index 0000000..b22cf91 --- /dev/null +++ b/.gitea/scripts/add-annotations.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +set -e + +CHART_FILE="Chart.yaml" +if [ ! -f "${CHART_FILE}" ]; then + echo "ERROR: ${CHART_FILE} not found!" 1>&2 + exit 1 +fi + +DEFAULT_NEW_TAG="$(git tag --sort=-version:refname | head -n 1)" +DEFAULT_OLD_TAG="$(git tag --sort=-version:refname | head -n 2 | tail -n 1)" + +if [ -z "${1}" ]; then + read -p "Enter start tag [${DEFAULT_OLD_TAG}]: " OLD_TAG + if [ -z "${OLD_TAG}" ]; then + OLD_TAG="${DEFAULT_OLD_TAG}" + fi + + while [ -z "$(git tag --list "${OLD_TAG}")" ]; do + echo "ERROR: Tag '${OLD_TAG}' not found!" 1>&2 + read -p "Enter start tag [${DEFAULT_OLD_TAG}]: " OLD_TAG + if [ -z "${OLD_TAG}" ]; then + OLD_TAG="${DEFAULT_OLD_TAG}" + fi + done +else + OLD_TAG=${1} + if [ -z "$(git tag --list "${OLD_TAG}")" ]; then + echo "ERROR: Tag '${OLD_TAG}' not found!" 1>&2 + exit 1 + fi +fi + +if [ -z "${2}" ]; then + read -p "Enter end tag [${DEFAULT_NEW_TAG}]: " NEW_TAG + if [ -z "${NEW_TAG}" ]; then + NEW_TAG="${DEFAULT_NEW_TAG}" + fi + + while [ -z "$(git tag --list "${NEW_TAG}")" ]; do + echo "ERROR: Tag '${NEW_TAG}' not found!" 1>&2 + read -p "Enter end tag [${DEFAULT_NEW_TAG}]: " NEW_TAG + if [ -z "${NEW_TAG}" ]; then + NEW_TAG="${DEFAULT_NEW_TAG}" + fi + done +else + NEW_TAG=${2} + + if [ -z "$(git tag --list "${NEW_TAG}")" ]; then + echo "ERROR: Tag '${NEW_TAG}' not found!" 1>&2 + exit 1 + fi +fi + +CHANGE_LOG_YAML=$(mktemp) +echo "[]" > "${CHANGE_LOG_YAML}" + +function map_type_to_kind() { + case "${1}" in + feat) + echo "added" + ;; + fix) + echo "fixed" + ;; + chore|style|test|ci|docs|refac) + echo "changed" + ;; + revert) + echo "removed" + ;; + sec) + echo "security" + ;; + *) + echo "skip" + ;; + esac +} + +COMMIT_TITLES="$(git log --pretty=format:"%s" "${OLD_TAG}..${NEW_TAG}")" + +echo "INFO: Generate change log entries from ${OLD_TAG} until ${NEW_TAG}" + +while IFS= read -r line; do + if [[ "${line}" =~ ^([a-zA-Z]+)(\([^\)]+\))?\:\ (.+)$ ]]; then + TYPE="${BASH_REMATCH[1]}" + KIND=$(map_type_to_kind "${TYPE}") + + if [ "${KIND}" == "skip" ]; then + continue + fi + + DESC="${BASH_REMATCH[3]}" + + echo "- ${KIND}: ${DESC}" + + jq --arg kind "${KIND}" --arg description "${DESC}" '. += [ $ARGS.named ]' < "${CHANGE_LOG_YAML}" > "${CHANGE_LOG_YAML}.new" + mv "${CHANGE_LOG_YAML}.new" "${CHANGE_LOG_YAML}" + + fi +done <<< "${COMMIT_TITLES}" + +if [ -s "${CHANGE_LOG_YAML}" ]; then + yq --inplace --input-format json --output-format yml "${CHANGE_LOG_YAML}" + yq --no-colors --inplace ".annotations.\"artifacthub.io/changes\" |= loadstr(\"${CHANGE_LOG_YAML}\") | sort_keys(.)" "${CHART_FILE}" +else + echo "ERROR: Changelog file is empty: ${CHANGE_LOG_YAML}" 1>&2 + exit 1 +fi + +rm "${CHANGE_LOG_YAML}" diff --git a/.gitea/workflows/generate-readme.yaml b/.gitea/workflows/generate-readme.yaml new file mode 100644 index 0000000..6708ab1 --- /dev/null +++ b/.gitea/workflows/generate-readme.yaml @@ -0,0 +1,32 @@ +name: Generate README + +on: + pull_request: + paths: [ "README.md", "values.yaml" ] + types: [ "opened", "reopened", "synchronize" ] + push: + branches: + - '**' + paths: [ "README.md", "values.yaml" ] + tags-ignore: + - '**' + workflow_dispatch: {} + +jobs: + generate-parameters: + container: + image: docker.io/library/node:24.1.0-alpine + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Generate parameter section in README + run: | + npm install + npm run readme:parameters + - name: Compare diff + run: git diff --exit-code --name-only README.md diff --git a/.gitea/workflows/helm.yaml b/.gitea/workflows/helm.yaml new file mode 100644 index 0000000..a293898 --- /dev/null +++ b/.gitea/workflows/helm.yaml @@ -0,0 +1,42 @@ +name: Helm + +on: + pull_request: + types: [ "opened", "reopened", "synchronize" ] + push: + branches: + - '**' + tags-ignore: + - '**' + workflow_dispatch: {} + +jobs: + helm-lint: + container: + image: docker.io/volkerraschek/helm:3.18.2 + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Lint helm files + run: | + helm lint --values values.yaml . + + helm-unittest: + container: + image: docker.io/volkerraschek/helm:3.18.2 + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Unittest + run: | + helm unittest --strict --file 'unittests/**/*.yaml' ./ \ No newline at end of file diff --git a/.gitea/workflows/markdown-linters.yaml b/.gitea/workflows/markdown-linters.yaml new file mode 100644 index 0000000..4b27fd2 --- /dev/null +++ b/.gitea/workflows/markdown-linters.yaml @@ -0,0 +1,46 @@ +name: Markdown linter + +on: + pull_request: + paths: [ "**/*.md" ] + types: [ "opened", "reopened", "synchronize" ] + push: + branches: + - '**' + paths: [ "**/*.md" ] + tags-ignore: + - '**' + workflow_dispatch: {} + +jobs: + markdown-link-checker: + container: + image: docker.io/library/node:24.1.0-alpine + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git npm + - uses: actions/checkout@v4.2.2 + - name: Verify links in markdown files + run: | + npm install + npm run readme:link + + markdown-lint: + container: + image: docker.io/library/node:24.1.0-alpine + runs-on: + - ubuntu-latest + steps: + - name: Install tooling + run: | + apk update + apk add git + - uses: actions/checkout@v4.2.2 + - name: Lint markdown files + run: | + npm install + npm run readme:lint diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml new file mode 100644 index 0000000..ebe41f9 --- /dev/null +++ b/.gitea/workflows/release.yaml @@ -0,0 +1,61 @@ +name: Release + +on: + push: + tags: + - "**" + +jobs: + publish-chart: + container: + image: docker.io/volkerraschek/helm:3.18.2 + runs-on: ubuntu-latest + steps: + - name: Install packages via apk + run: | + apk update + apk add git npm jq yq + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Add Artifacthub.io annotations + run: | + NEW_TAG="$(git tag --sort=-version:refname | head -n 1)" + OLD_TAG="$(git tag --sort=-version:refname | head -n 2 | tail -n 1)" + .gitea/scripts/add-annotations.sh "${OLD_TAG}" "${NEW_TAG}" + + - name: Extract meta information + run: | + echo "PACKAGE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + echo "REPOSITORY_NAME=$(echo ${GITHUB_REPOSITORY} | cut -d '/' -f 2 | sed --regexp-extended 's/-charts?//g')" >> $GITHUB_ENV + echo "REPOSITORY_OWNER=$(echo ${GITHUB_REPOSITORY} | cut -d '/' -f 1)" >> $GITHUB_ENV + + - name: Update Helm Chart version in README.md + run: sed -i -E "s/^CHART_VERSION=.*/CHART_VERSION=${PACKAGE_VERSION}/g" README.md + + - name: Package chart + run: | + helm dependency build + helm package --version "${PACKAGE_VERSION}" ./ + + - name: Upload Chart to ChartMuseum + env: + CHARTMUSEUM_PASSWORD: ${{ secrets.CHARTMUSEUM_PASSWORD }} + CHARTMUSEUM_REPOSITORY: ${{ vars.CHARTMUSEUM_REPOSITORY }} + CHARTMUSEUM_USERNAME: ${{ secrets.CHARTMUSEUM_USERNAME }} + CHARTMUSEUM_HOSTNAME: ${{ vars.CHARTMUSEUM_HOSTNAME }} + run: | + helm repo add --username ${CHARTMUSEUM_USERNAME} --password ${CHARTMUSEUM_PASSWORD} chartmuseum https://${CHARTMUSEUM_HOSTNAME}/${CHARTMUSEUM_REPOSITORY} + helm cm-push ${REPOSITORY_NAME}-${PACKAGE_VERSION}.tgz chartmuseum + helm repo remove chartmuseum + + - name: Upload Chart to Gitea + env: + GITEA_PACKAGE_REGISTRY_TOKEN: ${{ secrets.GIT_CRYPTIC_SYSTEMS_PACKAGE_REGISTRY_TOKEN }} + GITEA_SERVER_URL: ${{ github.server_url }} + run: | + helm repo add --username ${REPOSITORY_OWNER} --password ${GITEA_PACKAGE_REGISTRY_TOKEN} gitea ${GITEA_SERVER_URL}/api/packages/${REPOSITORY_OWNER}/helm + helm cm-push ${REPOSITORY_NAME}-${PACKAGE_VERSION}.tgz gitea + helm repo remove gitea diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba69fff --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +charts +node_modules +target +values2.yml +values2.yaml +*.tgz diff --git a/.helmignore b/.helmignore new file mode 100644 index 0000000..a954747 --- /dev/null +++ b/.helmignore @@ -0,0 +1,60 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store + +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ + +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ + +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ + +# drone +.drone.yml + +# editorconfig +.editorconfig + +# customized values +values2.yml +values2.yaml + +# helm packages +*.tgz +.helmignore +unittests + +# markdownlint +.markdownlint.yml +.markdownlint.yaml +.markdownlintignore + +# npm +.prettierignore +.npmrc +package* + +# yamllint +.yamllint.yaml + +# Others +CONTRIBUTING.md +CODEOWNERS +Makefile +renovate.json diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..247f790 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,155 @@ +# markdownlint YAML configuration +# https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "atx" + +# MD004/ul-style - Unordered list style +MD004: + style: "dash" + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 120 + # Number of characters for headings + heading_line_length: 120 + # Number of characters for code blocks + code_block_line_length: 80 + # Include code blocks + code_blocks: false + # Include tables + tables: false + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: + # Only check sibling headings + siblings_only: true + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD033/no-inline-html - Inline HTML +MD033: + # Allowed elements + allowed_elements: [] + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "---" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: + - Git + - GitDevOps + - Gitea + - GitHub + - GitLab + - GitOps + - kube-prometheus-stack + - Memcached + - Oracle + - PostgreSQL + - Prometheus + - prometheus-exporter + - SSL + - TLS + # Include code blocks + code_blocks: false + +# MD046/code-block-style - Code block style +MD046: + # Block style + style: "fenced" + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence syle + style: "backtick" diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 0000000..9fe4803 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,4 @@ +.github/ +Chart.lock +charts/ +node_modules/ \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..fafeafa --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +Chart.lock \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..08a5b51 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "DavidAnson.vscode-markdownlint", + "esbenp.prettier-vscode", + "Tim-Koehler.helm-intellisense", + "yzhang.markdown-all-in-one" + ] +} \ No newline at end of file diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..90128be --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,20 @@ +--- +extends: default + +ignore: | + .yamllint + node_modules + templates + + +rules: + truthy: + allowed-values: ['true', 'false'] + check-keys: False + level: error + line-length: disable + document-start: disable + comments: + min-spaces-from-content: 1 + braces: + max-spaces-inside: 2 \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..af45fc8 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @volker.raschek diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6347fe2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,82 @@ +# Contributing + +I am very happy if you would like to provide a pull request 👍 + +The content of this file describes which requirements contributors should fulfill before submitting a pull request (PR). + +1. [Valid Git commits](#valid-git-commits) + +## Valid Git commits + +### Commit message + +The repository is subject to a strict commit message template. This states that there are several types of commits. For +example, `fix`, `chore`, `refac`, `test` or `doc`. All types are described in more detail below. + +| type | description | +| ------------------- | ----------------------------------------------------------------- | +| `feat` | New feature. | +| `fix` | Fixes a bug. | +| `refac` | Refactoring production code. | +| `style` | Fixes formatting issues. No production code change. | +| `docs` | Adapt documentation. No production code change. | +| `test` | Adds new or modifies existing tests. No production code change. | +| `chore` | Updating grunt tasks. Is everything which the user does not see. | + +Based on these types, commit messaged can then be created. Here are a few examples: + +```text +style(README): Wrong indentation +feat(deployment): support restartPolicy +fix(my-app): Add missing volume +docs(CONTRIBUTING): Describe how to commit correctly +``` + +This type of commit message makes it easier for me as maintainer to keep an overview and does not cause the commits of a +pull request PR to be combined into one commit (squashing). + +### Smart commits + +Smart commits are excellent when it comes to tracking bugs or issues. In this repository, however, the rebasing of +commits is prohibited, which means that only merge commits are possible. This means that a smart commit message only +needs to be added to the merge commit. + +This has the advantage that the maintainer can use the smart commit to find the merge commit and undo the entire history +of a merge without having to select individual commits. The following history illustrates the correct use of smart commits. + +```text +* 823edbc7 Volker Raschek (G) | [Close #2] feat(deployment): support additional containers +|\ +| * 321aebc3 Volker Raschek (G) | doc(README): generate README with new deployment attributes +| * 8d101dd3 Volker Raschek (G) | test(deployment): Extend unittest of additional containers +| * 6f2abd93 Volker Raschek (G) | fix(deployment): Extend deployment of additional containers +|/ +* aa5ebda bob (N) | [Close #1] feat(deployment): support initContainers +``` + +### Commit signing + +Another problem with Git is the chain of trust. Git allows the configuration of any name and e-mail address. An attacker +can impersonate any person and submit pull requests under a false identity. For as Linux Torvalds, the maintainer of the +Linux kernel. + +```bash +git config --global user.name 'Linux Torvalds' +git config --global user.email 'torvalds@linux-foundation.org' +``` + +To avoid this, some Git repositories expect signed commits. In particular, repositories that are subject to direct +delivery to customers. For this reason, the repository is subject to a branch protection rule that only allows signed +commits. *Until* there is *no verified* and *no signed* commit, the pull request is blocked. + +The following articles describes how Git can be configured to sign commits. Please keep in mind, that the e-mail +address, which is used as UID of the GPG keyring must also be defined in the profile settings of your GitHub account. +Otherwise will be marked the Git commit as *Unverified*. + +1. [Signing Commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) +2. [Tell Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key) + +Inspect your Git commit via `git log`. There should be mentioned, that your commit is signed. + +Furthermore, the GPG key is unique. **Don't loose your private GPG key**. Backup your private key on a safe device. For +example an external USB drive. diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..2a2b099 --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,26 @@ +annotations: + artifacthub.io/links: | + - name: Reposilite (binary) + url: https://github.com/dzikoysk/reposilite + - name: support + url: https://git.cryptic.systems/volker.raschek/reposilite-charts/issues +apiVersion: v2 +appVersion: "3.5.25" +description: | + Lightweight and easy-to-use repository management software + dedicated for the Maven based artifacts in the JVM ecosystem +icon: https://raw.githubusercontent.com/dzikoysk/reposilite/main/reposilite-site/public/images/favicon.png +keywords: + - artifact-repository-manager + - deploy + - gradle + - maven + - reposilite + - repository-manager + - sbt +name: reposilite +sources: + - https://github.com/dzikoysk/reposilite + - https://git.cryptic.systems/volker.raschek/reposilite-charts +type: application +version: "0.1.0" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0d79dbb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Markus Pesch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..323bccd --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +# CONTAINER_RUNTIME +CONTAINER_RUNTIME?=$(shell which podman) + +# HELM_IMAGE +HELM_IMAGE_REGISTRY_HOST?=docker.io +HELM_IMAGE_REPOSITORY?=volkerraschek/helm +HELM_IMAGE_VERSION?=3.18.2 # renovate: datasource=docker registryUrl=https://registry-nexus.orbis.dedalus.com depName=volkerraschek/helm +HELM_IMAGE_FULLY_QUALIFIED=${HELM_IMAGE_REGISTRY_HOST}/${HELM_IMAGE_REPOSITORY}:${HELM_IMAGE_VERSION} + +# NODE_IMAGE +NODE_IMAGE_REGISTRY_HOST?=docker.io +NODE_IMAGE_REPOSITORY?=library/node +NODE_IMAGE_VERSION?=24.1.0-alpine # renovate: datasource=docker registryUrl=https://docker.io depName=docker.io/library/node packageName=library/node +NODE_IMAGE_FULLY_QUALIFIED=${NODE_IMAGE_REGISTRY_HOST}/${NODE_IMAGE_REPOSITORY}:${NODE_IMAGE_VERSION} + +# MISSING DOT +# ============================================================================== +missing-dot: + grep --perl-regexp '## @(param|skip).*[^.]$$' values.yaml + +# CONTAINER RUN - README +# ============================================================================== +PHONY+=container-run/readme +container-run/readme: container-run/readme/link container-run/readme/lint container-run/readme/parameters + +container-run/readme/link: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${NODE_IMAGE_FULLY_QUALIFIED} \ + npm install && npm run readme:link + +container-run/readme/lint: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${NODE_IMAGE_FULLY_QUALIFIED} \ + npm install && npm run readme:lint + +container-run/readme/parameters: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${NODE_IMAGE_FULLY_QUALIFIED} \ + npm install && npm run readme:parameters + +# CONTAINER RUN - HELM UNITTESTS +# ============================================================================== +PHONY+=container-run/helm-unittests +container-run/helm-unittests: + ${CONTAINER_RUNTIME} run \ + --env HELM_REPO_PASSWORD=${CHART_SERVER_PASSWORD} \ + --env HELM_REPO_USERNAME=${CHART_SERVER_USERNAME} \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${HELM_IMAGE_FULLY_QUALIFIED} \ + unittest --strict --file 'unittests/**/*.yaml' ./ + +# CONTAINER RUN - HELM UPDATE DEPENDENCIES +# ============================================================================== +PHONY+=container-run/helm-update-dependencies +container-run/helm-update-dependencies: + ${CONTAINER_RUNTIME} run \ + --env HELM_REPO_PASSWORD=${CHART_SERVER_PASSWORD} \ + --env HELM_REPO_USERNAME=${CHART_SERVER_USERNAME} \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${HELM_IMAGE_FULLY_QUALIFIED} \ + dependency update + +# CONTAINER RUN - MARKDOWN-LINT +# ============================================================================== +PHONY+=container-run/helm-lint +container-run/helm-lint: + ${CONTAINER_RUNTIME} run \ + --rm \ + --volume $(shell pwd):$(shell pwd) \ + --workdir $(shell pwd) \ + ${HELM_IMAGE_FULLY_QUALIFIED} \ + lint --values values.yaml . + +# PHONY +# ============================================================================== +# Declare the contents of the PHONY variable as phony. We keep that information +# in a variable so we can use it in if_changed. +.PHONY: ${PHONY} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8110e3c --- /dev/null +++ b/README.md @@ -0,0 +1,293 @@ +# Reposilite + +[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/reposilite)](https://artifacthub.io/packages/search?repo=reposilite) + +This helm chart enables the deployment of [Reposilite](https://github.com/dzikoysk/reposilite), a lightweight and +easy-to-use repository management software dedicated for the Maven-based artifacts in the JVM ecosystem. + +The helm chart supports the individual configuration of additional containers/initContainers, mounting of volumes, +defining additional environment variables and much more. + +Chapter [configuration and installation](#helm-configuration-and-installation) describes the basics how to configure helm +and use it to deploy the exporter. It also contains further configuration examples. + +Furthermore, this helm chart contains unit tests to detect regressions and stabilize the deployment. Additionally, this +helm chart is tested for deployment scenarios with **ArgoCD**. + +## Helm: configuration and installation + +1. A helm chart repository must be configured, to pull the helm charts from. +2. All available [parameters](#parameters) are documented in detail below. The parameters can be defined via the helm + `--set` flag or directly as part of a `values.yaml` file. The following example defines the `reposilite` repository + and use the `--set` flag for a basic deployment. + +```bash +helm repo add volker.raschek https://charts.cryptic.systems/volker.raschek +helm repo update +helm install reposilite volker.raschek/reposilite +``` + +Instead of passing all parameters via the *set* flag, it is also possible to define them as part of the `values.yaml`. +The following command downloads the `values.yaml` for a specific version of this chart. Please keep in mind, that the +version of the chart must be in sync with the `values.yaml`. Newer *minor* versions can have new features. New *major* +versions can break something! + +```bash +CHART_VERSION=0.1.0 +helm show values volker.raschek/reposilite --version "${CHART_VERSION}" > values.yaml +``` + +A complete list of available helm chart versions can be displayed via the following command: + +```bash +helm search repo reposilite --versions +``` + +The helm chart also contains a persistent volume claim definition. It persistent volume claim is not enabled by default. +Use the `--set` argument to persist your data. + +```bash +CHART_VERSION=0.1.0 +helm install --version "${CHART_VERSION}" reposilite volker.raschek/reposilite \ + persistentVolumeClaim.enabled=true +``` + +### Examples + +The following examples serve as individual configurations and as inspiration for how deployment problems can be solved. + +#### TLS encryption + +The example describe how to deploy Reposilite with TLS encryption. If Reposilite is deployed behind reverse proxy, for +example an ingress nginx controller, please instruct the ingress to establish an TLS encrypted connection to avoid +connection problems. + +> [!WARNING] +> The secret `reposilite-tls` containing the TLS certificate is already present. The keys `ca.crt`, `tls.key` and +> `tls.crt` must be renamed matching the file extension `.pem`. Otherwise throws the used TLS library of Reposilite and +> error. + +```bash +CHART_VERSION=0.1.0 +helm install --version "${CHART_VERSION}" reposilite volker.raschek/reposilite \ + --set 'deployment.reposilite.env[1].name=REPOSILITE_LOCAL_SSLENABLED' \ + --set 'deployment.reposilite.env[1].value="true"' \ + --set 'deployment.reposilite.env[2].name=REPOSILITE_LOCAL_SSLPORT' \ + --set 'deployment.reposilite.env[2].value=8443' \ + --set 'deployment.reposilite.env[3].name=REPOSILITE_LOCAL_KEYPATH' \ + --set 'deployment.reposilite.env[3].value="/etc/reposilite/tls/cert.pem /etc/reposilite/tls/priv-key.pem"' \ + --set 'deployment.reposilite.volumeMounts[0].name=tls' \ + --set 'deployment.reposilite.volumeMounts[0].mountPath=/etc/reposilite/tls' \ + --set 'deployment.reposilite.volumeMounts[0].readOnly=true' \ + --set 'deployment.volumes[0].name=tls' \ + --set 'deployment.volumes[0].items[0].key=ca.crt' \ + --set 'deployment.volumes[0].items[0].path=ca.pem' \ + --set 'deployment.volumes[0].items[1].key=tls.crt' \ + --set 'deployment.volumes[0].items[1].path=cert.pem' \ + --set 'deployment.volumes[0].items[2].key=tls.key' \ + --set 'deployment.volumes[0].items[2].path=priv-key.pem' \ + --set 'deployment.volumes[0].secret.secretName=reposilite-tls' \ + --set 'service.port=8443' +``` + +#### TLS certificate rotation + +If Reposilite uses TLS certificates that are mounted as a secret in the container file system like the example +[above](#tls-encryption), Reposlite will not automatically apply them when the TLS certificates are rotated. Such a +rotation can be for example triggered, when the [cert-manager](https://cert-manager.io/) issues new TLS certificates +before expiring. + +Until Reposilite does not support rotating TLS certificate a workaround can be applied. For example stakater's +[reloader](https://github.com/stakater/Reloader) controller can be used to trigger a rolling update. The following +annotation must be added to instruct the reloader controller to trigger a rolling update, when the mounted configMaps +and secrets have been changed. + +```yaml +deployment: + annotations: + reloader.stakater.com/auto: "true" +``` + +Instead of triggering a rolling update for configMap and secret resources, this action can also be defined for +individual items. For example, when the secret named `reposilite-tls` is mounted and the reloader controller should only +listen for changes of this secret: + +```yaml +deployment: + annotations: + secret.reloader.stakater.com/reload: "reposilite-tls" +``` + +### Network policies + +Network policies can only take effect, when the used CNI plugin support network policies. The chart supports no custom +network policy implementation of CNI plugins. It's support only the official API resource of `networking.k8s.io/v1`. + +The example below is an excerpt of the `values.yaml` file. The network policy contains ingress rules to allow incoming +traffic from an ingress controller. Additionally one egress rule is defined, to allow the application outgoing access +to the internal running DNS server `core-dns`. + +> [!IMPORTANT] +> Please keep in mind, that the namespace and pod selector labels can be different from environment to environment. For +> this reason, there is are not default network policy rules defined. + +```yaml +networkPolicies: + enabled: true + annotations: {} + labels: {} + policyTypes: + - Egress + - Ingress + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + podSelector: + matchLabels: + k8s-app: kube-dns + ports: + - port: 53 + protocol: TCP + - port: 53 + protocol: UDP + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: ingress-nginx + podSelector: + matchLabels: + app.kubernetes.io/name: ingress-nginx + ports: + - port: http + protocol: TCP +``` + +## Parameters + +### Global + +| Name | Description | Value | +| ------------------ | ----------------------------------------- | ----- | +| `nameOverride` | Individual release name suffix. | `""` | +| `fullnameOverride` | Override the complete release name logic. | `""` | + +### Deployment + +| Name | Description | Value | +| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------- | +| `deployment.annotations` | Additional deployment annotations. | `{}` | +| `deployment.labels` | Additional deployment labels. | `{}` | +| `deployment.additionalContainers` | List of additional containers. | `[]` | +| `deployment.affinity` | Affinity for the Reposilite deployment. | `{}` | +| `deployment.initContainers` | List of additional init containers. | `[]` | +| `deployment.dnsConfig` | dnsConfig of the Reposilite deployment. | `{}` | +| `deployment.dnsPolicy` | dnsPolicy of the Reposilite deployment. | `""` | +| `deployment.hostname` | Individual hostname of the pod. | `""` | +| `deployment.subdomain` | Individual domain of the pod. | `""` | +| `deployment.hostNetwork` | Use the kernel network namespace of the host system. | `false` | +| `deployment.imagePullSecrets` | Secret to use for pulling the image. | `[]` | +| `deployment.reposilite.args` | Arguments passed to the Reposilite container. | `[]` | +| `deployment.reposilite.command` | Command passed to the Reposilite container. | `[]` | +| `deployment.reposilite.env` | List of environment variables for the Reposilite container. | | +| `deployment.reposilite.envFrom` | List of environment variables mounted from configMaps or secrets for the Reposilite container. | `[]` | +| `deployment.reposilite.image.registry` | Image registry, eg. `docker.io`. | `docker.io` | +| `deployment.reposilite.image.repository` | Image repository, eg. `library/busybox`. | `dzikoysk/reposilite` | +| `deployment.reposilite.image.tag` | Custom image tag, eg. `0.1.0`. Defaults to `appVersion`. | `""` | +| `deployment.reposilite.image.pullPolicy` | Image pull policy. | `IfNotPresent` | +| `deployment.reposilite.resources` | CPU and memory resources of the pod. | `{}` | +| `deployment.reposilite.securityContext` | Security context of the container of the deployment. | `{}` | +| `deployment.reposilite.volumeMounts` | Additional volume mounts. | `[]` | +| `deployment.nodeSelector` | NodeSelector of the Reposilite deployment. | `{}` | +| `deployment.priorityClassName` | PriorityClassName of the Reposilite deployment. | `""` | +| `deployment.replicas` | Number of replicas for the Reposilite deployment. | `1` | +| `deployment.restartPolicy` | Restart policy of the Reposilite deployment. | `""` | +| `deployment.securityContext` | Security context of the Reposilite deployment. | `{}` | +| `deployment.strategy.type` | Strategy type - `Recreate` or `RollingUpdate`. | `RollingUpdate` | +| `deployment.strategy.rollingUpdate.maxSurge` | The maximum number of pods that can be scheduled above the desired number of pods during a rolling update. | `1` | +| `deployment.strategy.rollingUpdate.maxUnavailable` | The maximum number of pods that can be unavailable during a rolling update. | `1` | +| `deployment.terminationGracePeriodSeconds` | How long to wait until forcefully kill the pod. | `60` | +| `deployment.tolerations` | Tolerations of the Reposilite deployment. | `[]` | +| `deployment.topologySpreadConstraints` | TopologySpreadConstraints of the Reposilite deployment. | `[]` | +| `deployment.volumes` | Additional volumes to mount into the pods of the prometheus-exporter deployment. | `[]` | + +### Horizontal Pod Autoscaler (HPA) + +| Name | Description | Value | +| ------------------------ | -------------------------------------------------------------------------------------------------- | ----------- | +| `hpa.enabled` | Enable the horizontal pod autoscaler (HPA). | `false` | +| `hpa.annotations` | Additional annotations for the HPA. | `{}` | +| `hpa.labels` | Additional labels for the HPA. | `{}` | +| `hpa.behavior.scaleDown` | Scaling policy for scaling down. | | +| `hpa.metrics` | Metrics contains the specifications for which to use to calculate the desired replica count. | `undefined` | +| `hpa.minReplicas` | Min replicas is the lower limit for the number of replicas to which the autoscaler can scale down. | `1` | +| `hpa.maxReplicas` | Upper limit for the number of pods that can be set by the autoscaler. | `10` | + +### Ingress + +| Name | Description | Value | +| --------------------- | ----------------------------------------------------------------------------------------------- | ------- | +| `ingress.enabled` | Enable creation of an ingress resource. Requires, that the http service is also enabled. | `false` | +| `ingress.className` | Ingress class. | `nginx` | +| `ingress.annotations` | Additional ingress annotations. | `{}` | +| `ingress.labels` | Additional ingress labels. | `{}` | +| `ingress.hosts` | Ingress specific configuration. Let ingress controller listen on specific DNS names and path's. | `[]` | +| `ingress.tls` | Ingress specific TLS configuration. Use TLS encryption for specific hostnames. | `[]` | + +### Network Policy + +| Name | Description | Value | +| --------------------------- | ------------------------------------------------------------------------- | ------- | +| `networkPolicy.enabled` | Enable network policies in general. | `false` | +| `networkPolicy.annotations` | Additional network policy annotations. | `{}` | +| `networkPolicy.labels` | Additional network policy labels. | `{}` | +| `networkPolicy.policyTypes` | List of policy types. Supported is ingress, egress or ingress and egress. | `[]` | +| `networkPolicy.egress` | Concrete egress network policy implementation. | `[]` | +| `networkPolicy.ingress` | Concrete ingress network policy implementation. | `[]` | + +### Persistent Volume Claim + +| Name | Description | Value | +| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | +| `persistentVolumeClaim.enabled` | Enable persistence in general. | `false` | +| `persistentVolumeClaim.path` | Path where the persistent volume claim should be mounted into the container file system. | `/app/data` | +| `persistentVolumeClaim.existing.enabled` | Enable an existing persistent volume claim. | `false` | +| `persistentVolumeClaim.existing.persistentVolumeClaimName` | Name of the existing persistent volume claim. | `""` | +| `persistentVolumeClaim.new.annotations` | Additional service account annotations. | `{}` | +| `persistentVolumeClaim.new.labels` | Additional service account labels. | `{}` | +| `persistentVolumeClaim.new.accessMode` | Access mode of the persistent volume claim. More information about the access modes are in detail documented [online](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes). | `ReadWriteOnce` | +| `persistentVolumeClaim.new.size` | Size of the persistent volume claim. | `10Gi` | +| `persistentVolumeClaim.new.storageClass` | Custom storage class. Left it empty to use the clusters default storage class. | `""` | + +### Service + +| Name | Description | Value | +| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| `service.enabled` | Enable the service. | `true` | +| `service.annotations` | Additional service annotations. | `{}` | +| `service.externalIPs` | External IPs for the service. | `[]` | +| `service.externalTrafficPolicy` | If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster external traffic. Furthermore, this enables source IP preservation. | `Cluster` | +| `service.internalTrafficPolicy` | If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster internal traffic. | `Cluster` | +| `service.ipFamilies` | IPFamilies is list of IP families (e.g. `IPv4`, `IPv6`) assigned to this service. This field is usually assigned automatically based on cluster configuration and only required for customization. | `[]` | +| `service.labels` | Additional service labels. | `{}` | +| `service.loadBalancerClass` | LoadBalancerClass is the class of the load balancer implementation this Service belongs to. Requires service from type `LoadBalancer`. | `""` | +| `service.loadBalancerIP` | LoadBalancer will get created with the IP specified in this field. Requires service from type `LoadBalancer`. | `""` | +| `service.loadBalancerSourceRanges` | Source range filter for LoadBalancer. Requires service from type `LoadBalancer`. | `[]` | +| `service.port` | Port to forward the traffic to. | `8080` | +| `service.sessionAffinity` | Supports `ClientIP` and `None`. Enable client IP based session affinity via `ClientIP`. | `None` | +| `service.sessionAffinityConfig` | Contains the configuration of the session affinity. | `{}` | +| `service.type` | Kubernetes service type for the traffic. | `ClusterIP` | + +### ServiceAccount + +| Name | Description | Value | +| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `serviceAccount.existing.enabled` | Use an existing service account instead of creating a new one. Assumes that the user has all the necessary kubernetes API authorizations. | `false` | +| `serviceAccount.existing.serviceAccountName` | Name of the existing service account. | `""` | +| `serviceAccount.new.annotations` | Additional service account annotations. | `{}` | +| `serviceAccount.new.labels` | Additional service account labels. | `{}` | +| `serviceAccount.new.automountServiceAccountToken` | Enable/disable auto mounting of the service account token. | `true` | +| `serviceAccount.new.imagePullSecrets` | ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this serviceAccount. | `[]` | +| `serviceAccount.new.secrets` | Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. | `[]` | diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..cfd6398 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2646 @@ +{ + "name": "reposilite-charts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "reposilite-charts", + "license": "MIT", + "devDependencies": { + "@bitnami/readme-generator-for-helm": "^2.5.0", + "markdown-link-check": "^3.13.6", + "markdownlint-cli": "^0.45.0" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } + }, + "node_modules/@bitnami/readme-generator-for-helm": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@bitnami/readme-generator-for-helm/-/readme-generator-for-helm-2.7.2.tgz", + "integrity": "sha512-7eXyJzxQTQj2ajpHlIhadciCCYWOqN8ieaweU25bStHOZowQ2c2CQyjO/bX4gxIf73LoRKxHhEYgLTllJY9SIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "commander": "^13.1.0", + "dot-object": "^2.1.5", + "lodash": "^4.17.21", + "markdown-table": "^2.0.0", + "yaml": "^2.7.0" + }, + "bin": { + "readme-generator": "bin/index.js" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@oozcitak/dom": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz", + "integrity": "sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/url": "1.0.4", + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@oozcitak/infra": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.8.tgz", + "integrity": "sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@oozcitak/url": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.4.tgz", + "integrity": "sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@oozcitak/util": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.8.tgz", + "integrity": "sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cheerio": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.0.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.12.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-object": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-2.1.5.tgz", + "integrity": "sha512-xHF8EP4XH/Ba9fvAF2LDd5O3IITVolerVV6xvkxoM8zlGEiCUrggpAnHyOoKJKCrhvPcGATFAUwIujj7bRG5UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^6.1.0", + "glob": "^7.1.6" + }, + "bin": { + "dot-object": "bin/dot-object" + } + }, + "node_modules/dot-object/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/html-link-extractor": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-link-extractor/-/html-link-extractor-1.0.5.tgz", + "integrity": "sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio": "^1.0.0-rc.10" + } + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-absolute-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-relative-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-4.0.0.tgz", + "integrity": "sha512-PkzoL1qKAYXNFct5IKdKRH/iBQou/oCC85QhXj6WKtUQBliZ4Yfd3Zk27RHu9KQG8r6zgvAA2AQKC9p+rqTszg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-absolute-url": "^4.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/katex": { + "version": "0.16.22", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", + "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/link-check": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/link-check/-/link-check-5.4.0.tgz", + "integrity": "sha512-0Pf4xBVUnwJdbDgpBlhHNmWDtbVjHTpIFs+JaBuIsC9PKRxjv4KMGCO2Gc8lkVnqMf9B/yaNY+9zmMlO5MyToQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-relative-url": "^4.0.0", + "ms": "^2.1.3", + "needle": "^3.3.1", + "node-email-verifier": "^2.0.0", + "proxy-agent": "^6.4.0" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-link-check": { + "version": "3.13.7", + "resolved": "https://registry.npmjs.org/markdown-link-check/-/markdown-link-check-3.13.7.tgz", + "integrity": "sha512-Btn3HU8s2Uyh1ZfzmyZEkp64zp2+RAjwfQt1u4swq2Xa6w37OW0T2inQZrkSNVxDSa2jSN2YYhw/JkAp5jF1PQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "async": "^3.2.6", + "chalk": "^5.3.0", + "commander": "^13.1.0", + "link-check": "^5.4.0", + "markdown-link-extractor": "^4.0.2", + "needle": "^3.3.1", + "progress": "^2.0.3", + "proxy-agent": "^6.4.0", + "xmlbuilder2": "^3.1.1" + }, + "bin": { + "markdown-link-check": "markdown-link-check" + } + }, + "node_modules/markdown-link-extractor": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/markdown-link-extractor/-/markdown-link-extractor-4.0.2.tgz", + "integrity": "sha512-5cUOu4Vwx1wenJgxaudsJ8xwLUMN7747yDJX3V/L7+gi3e4MsCm7w5nbrDQQy8nEfnl4r5NV3pDXMAjhGXYXAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "html-link-extractor": "^1.0.5", + "marked": "^12.0.1" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdownlint": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz", + "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.45.0.tgz", + "integrity": "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "~13.1.0", + "glob": "~11.0.2", + "ignore": "~7.0.4", + "js-yaml": "~4.1.0", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdown-it": "~14.1.0", + "markdownlint": "~0.38.0", + "minimatch": "~10.0.1", + "run-con": "~1.3.2", + "smol-toml": "~1.3.4" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-email-verifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-email-verifier/-/node-email-verifier-2.0.0.tgz", + "integrity": "sha512-AHcppjOH2KT0mxakrxFMOMjV/gOVMRpYvnJUkNfgF9oJ3INdVmqcMFJ5TlM8elpTPwt6A7bSp1IMnnWcxGom/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3", + "validator": "^13.11.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/smol-toml": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz", + "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/socks": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", + "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", + "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlbuilder2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.1.1.tgz", + "integrity": "sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oozcitak/dom": "1.15.10", + "@oozcitak/infra": "1.0.8", + "@oozcitak/util": "8.3.8", + "js-yaml": "3.14.1" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/xmlbuilder2/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/xmlbuilder2/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/xmlbuilder2/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8282410 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "reposilite-charts", + "homepage": "https://git.cryptic.systems/volker.raschek/reposilite-charts.git", + "license": "MIT", + "private": true, + "engineStrict": true, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "scripts": { + "readme:link": "markdown-link-check *.md", + "readme:lint": "markdownlint *.md -f", + "readme:parameters": "readme-generator -v values.yaml -r README.md" + }, + "devDependencies": { + "@bitnami/readme-generator-for-helm": "^2.5.0", + "markdown-link-check": "^3.13.6", + "markdownlint-cli": "^0.45.0" + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..408b613 --- /dev/null +++ b/renovate.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>volker.raschek/renovate-config:default#master", + "local>volker.raschek/renovate-config:container#master", + "local>volker.raschek/renovate-config:actions#master", + "local>volker.raschek/renovate-config:npm#master", + "local>volker.raschek/renovate-config:regexp#master" + ], + "customManagers": [ + { + "fileMatch": [ + "^Chart\\.yaml$" + ], + "matchStrings": [ + "appVersion: \"(?.*?)\"\\s+" + ], + "datasourceTemplate": "docker", + "depNameTemplate": "dzikoysk/reposilite", + "lookupNameTemplate": "docker.io/dzikoysk/reposilite", + "versioningTemplate": "semver" + }, + { + "fileMatch": ["^README\\.md$"], + "matchStrings": [ + "CHART_VERSION=(?.*)" + ], + "depNameTemplate": "volker.raschek/reposilite-charts", + "packageNameTemplate": "https://git.cryptic.systems/volker.raschek/reposilite-charts", + "datasourceTemplate": "git-tags", + "versioningTemplate": "semver" + } + ], + "packageRules": [ + { + "addLabels": [ + "renovate/automerge", + "renovate/container" + ], + "automerge": true, + "excludePackagePatterns": [ + "dzikoysk/reposilite" + ], + "matchDatasources": [ + "docker" + ], + "matchUpdateTypes": [ + "minor", + "patch" + ] + }, + { + "addLabels": [ + "renovate/automerge", + "renovate/documentation" + ], + "automerge": true, + "matchDepNames": [ + "volker.raschek/reposilite-charts" + ], + "matchUpdateTypes": [ + "major", + "minor", + "patch" + ] + } + ] +} diff --git a/templates/_common.tpl b/templates/_common.tpl new file mode 100644 index 0000000..2755913 --- /dev/null +++ b/templates/_common.tpl @@ -0,0 +1,58 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "reposilite.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 "reposilite.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 "reposilite.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common annotations +*/}} +{{- define "reposilite.annotations" -}} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "reposilite.labels" -}} +helm.sh/chart: {{ include "reposilite.chart" . }} +{{ include "reposilite.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "reposilite.selectorLabels" -}} +app.kubernetes.io/name: {{ include "reposilite.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + diff --git a/templates/_deployment.tpl b/templates/_deployment.tpl new file mode 100644 index 0000000..a50f17b --- /dev/null +++ b/templates/_deployment.tpl @@ -0,0 +1,75 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "reposilite.deployment.annotations" -}} +{{ include "reposilite.annotations" . }} +{{- if .Values.deployment.annotations }} +{{ toYaml .Values.deployment.annotations }} +{{- end }} +{{- end }} + + +{{/* env */}} + +{{- define "reposilite.deployment.reposilite.env" -}} +{{- $env := .Values.deployment.reposilite.env | default list }} +{{- if .Values.persistentVolumeClaim.enabled }} +{{- $env = concat $env (list (dict "name" "REPOSILITE_DATA" "value" .Values.persistentVolumeClaim.path )) }} +{{- end }} +{{ toYaml (dict "env" $env) }} +{{- end -}} + +REPOSILITE_DATA + +{{/* image */}} + +{{- define "reposilite.deployment.images.reposilite.fqin" -}} +{{- $registry := .Values.deployment.reposilite.image.registry -}} +{{- $repository := .Values.deployment.reposilite.image.repository -}} +{{- $tag := default .Chart.AppVersion .Values.deployment.reposilite.image.tag -}} +{{- printf "%s/%s:%s" $registry $repository $tag -}} +{{- end -}} + +{{/* labels */}} + +{{- define "reposilite.deployment.labels" -}} +{{ include "reposilite.labels" . }} +{{- if .Values.deployment.labels }} +{{ toYaml .Values.deployment.labels }} +{{- end }} +{{- end }} + +{{/* serviceAccount */}} + +{{- define "reposilite.deployment.serviceAccount" -}} +{{- if .Values.serviceAccount.existing.enabled -}} +{{- printf "%s" .Values.serviceAccount.existing.serviceAccountName -}} +{{- else -}} +{{- include "reposilite.fullname" . -}} +{{- end -}} +{{- end }} + +{{/* volumeMounts */}} + +{{- define "reposilite.deployment.reposilite.volumeMounts" -}} +{{- $volumeMounts := .Values.deployment.reposilite.volumeMounts | default list }} +{{- if .Values.persistentVolumeClaim.enabled }} +{{- $volumeMounts = concat $volumeMounts (list (dict "name" "data" "mountPath" .Values.persistentVolumeClaim.path )) }} +{{- end }} +{{ toYaml (dict "volumeMounts" $volumeMounts) }} +{{- end -}} + +{{/* volumes */}} + +{{- define "reposilite.deployment.volumes" -}} +{{- $volumes := .Values.deployment.volumes | default list }} + +{{- if and .Values.persistentVolumeClaim.enabled (not .Values.persistentVolumeClaim.existing.enabled) }} +{{- $persistentVolumeClaimName := include "reposilite.persistentVolumeClaim.name" $ -}} +{{- $volumes = concat $volumes (list (dict "name" "data" "persistentVolumeClaim" (dict "claimName" $persistentVolumeClaimName))) }} +{{- end }} + +{{ toYaml (dict "volumes" $volumes) }} + +{{- end -}} \ No newline at end of file diff --git a/templates/_hpa.tpl b/templates/_hpa.tpl new file mode 100644 index 0000000..ef5fdab --- /dev/null +++ b/templates/_hpa.tpl @@ -0,0 +1,19 @@ +--- + +{{/* annotations */}} + +{{- define "reposilite.hpa.annotations" -}} +{{ include "reposilite.annotations" . }} +{{- if .Values.hpa.annotations }} +{{ toYaml .Values.hpa.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.hpa.labels" -}} +{{ include "reposilite.labels" . }} +{{- if .Values.hpa.labels }} +{{ toYaml .Values.hpa.labels }} +{{- end }} +{{- end }} diff --git a/templates/_ingress.tpl b/templates/_ingress.tpl new file mode 100644 index 0000000..a02adef --- /dev/null +++ b/templates/_ingress.tpl @@ -0,0 +1,19 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "reposilite.ingress.annotations" -}} +{{ include "reposilite.annotations" . }} +{{- if .Values.ingress.annotations }} +{{ toYaml .Values.ingress.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.ingress.labels" -}} +{{ include "reposilite.labels" . }} +{{- if .Values.ingress.labels }} +{{ toYaml .Values.ingress.labels }} +{{- end }} +{{- end }} diff --git a/templates/_networkPolicy.tpl b/templates/_networkPolicy.tpl new file mode 100644 index 0000000..60dbd1b --- /dev/null +++ b/templates/_networkPolicy.tpl @@ -0,0 +1,19 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "reposilite.networkPolicy.annotations" -}} +{{ include "reposilite.annotations" . }} +{{- if .Values.networkPolicy.annotations }} +{{ toYaml .Values.networkPolicy.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.networkPolicy.labels" -}} +{{ include "reposilite.labels" . }} +{{- if .Values.networkPolicy.labels }} +{{ toYaml .Values.networkPolicy.labels }} +{{- end }} +{{- end }} diff --git a/templates/_persistentVolumeClaim.tpl b/templates/_persistentVolumeClaim.tpl new file mode 100644 index 0000000..5754363 --- /dev/null +++ b/templates/_persistentVolumeClaim.tpl @@ -0,0 +1,25 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "reposilite.persistentVolumeClaim.annotations" -}} +helm.sh/resource-policy: keep +{{- if .Values.persistentVolumeClaim.new.annotations }} +{{ toYaml .Values.persistentVolumeClaim.new.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.persistentVolumeClaim.labels" -}} +{{ include "reposilite.labels" . }} +{{- if .Values.persistentVolumeClaim.new.labels }} +{{ toYaml .Values.persistentVolumeClaim.new.labels }} +{{- end }} +{{- end }} + +{{/* name */}} + +{{- define "reposilite.persistentVolumeClaim.name" -}} +{{ include "reposilite.fullname" . }} +{{- end }} \ No newline at end of file diff --git a/templates/_pod.tpl b/templates/_pod.tpl new file mode 100644 index 0000000..266df6d --- /dev/null +++ b/templates/_pod.tpl @@ -0,0 +1,17 @@ +--- + +{{/* annotations */}} + +{{- define "reposilite.pod.annotations" -}} +{{ include "reposilite.annotations" . }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.pod.labels" -}} +{{ include "reposilite.labels" . }} +{{- end }} + +{{- define "reposilite.pod.selectorLabels" -}} +{{ include "reposilite.selectorLabels" . }} +{{- end }} \ No newline at end of file diff --git a/templates/_serviceAccount.tpl b/templates/_serviceAccount.tpl new file mode 100644 index 0000000..4ade784 --- /dev/null +++ b/templates/_serviceAccount.tpl @@ -0,0 +1,17 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "reposilite.serviceAccount.annotations" -}} +{{- if .Values.serviceAccount.new.annotations }} +{{ toYaml .Values.serviceAccount.new.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.serviceAccount.labels" -}} +{{- if .Values.serviceAccount.new.labels }} +{{ toYaml .Values.serviceAccount.new.labels }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/_services.tpl b/templates/_services.tpl new file mode 100644 index 0000000..dfb4d1f --- /dev/null +++ b/templates/_services.tpl @@ -0,0 +1,27 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* annotations */}} + +{{- define "reposilite.service.annotations" -}} +{{ include "reposilite.annotations" . }} +{{- if .Values.service.annotations }} +{{ toYaml .Values.service.annotations }} +{{- end }} +{{- end }} + +{{/* labels */}} + +{{- define "reposilite.service.labels" -}} +{{ include "reposilite.labels" . }} +{{- if .Values.service.labels }} +{{ toYaml .Values.service.labels }} +{{- end }} +{{- end }} + +{{/* names */}} + +{{- define "reposilite.service.name" -}} +{{- if .Values.service.enabled -}} +{{ include "reposilite.fullname" . }} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/templates/deployment.yaml b/templates/deployment.yaml new file mode 100644 index 0000000..fe6b2f8 --- /dev/null +++ b/templates/deployment.yaml @@ -0,0 +1,140 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + {{- with (include "reposilite.deployment.annotations" . | fromYaml) }} + annotations: + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- with (include "reposilite.deployment.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + replicas: {{ .Values.deployment.replicas }} + selector: + matchLabels: + {{- include "reposilite.pod.selectorLabels" . | nindent 6 }} + {{- with .Values.deployment.strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + template: + metadata: + {{- with (include "reposilite.pod.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "reposilite.pod.labels" . | nindent 8 }} + spec: + {{- with .Values.deployment.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - {{- if gt (len .Values.deployment.reposilite.args) 0 }} + args: + {{- range .Values.deployment.reposilite.args }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- if gt (len .Values.deployment.reposilite.command) 0 }} + command: + {{- range .Values.deployment.reposilite.command }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- $env := (include "reposilite.deployment.reposilite.env" . | fromYaml) }} + {{- if and (hasKey $env "env") (gt (len $env.env) 0) }} + env: + {{- toYaml $env.env | nindent 8 }} + {{- end }} + {{- with .Values.deployment.reposilite.envFrom }} + envFrom: + {{- toYaml . | nindent 10 }} + {{- end }} + image: {{ include "reposilite.deployment.images.reposilite.fqin" . | quote }} + imagePullPolicy: {{ .Values.deployment.reposilite.image.pullPolicy }} + livenessProbe: + tcpSocket: + port: http + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 3 + name: reposilite + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + readinessProbe: + tcpSocket: + port: http + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + {{- with .Values.deployment.reposilite.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.deployment.reposilite.securityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- $volumeMounts := (include "reposilite.deployment.reposilite.volumeMounts" . | fromYaml) }} + {{- if and (hasKey $volumeMounts "volumeMounts") (gt (len $volumeMounts.volumeMounts) 0) }} + volumeMounts: + {{- toYaml $volumeMounts.volumeMounts | nindent 8 }} + {{- end }} + {{- with .Values.deployment.dnsConfig }} + dnsConfig: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.deployment.dnsPolicy }} + dnsPolicy: {{ .Values.deployment.dnsPolicy }} + {{- end }} + {{- if .Values.deployment.hostname }} + hostname: {{ .Values.deployment.hostname }} + {{- end }} + hostNetwork: {{ .Values.deployment.hostNetwork }} + {{- with .Values.deployment.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.deployment.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.deployment.priorityClassName }} + priorityClassName: {{ .Values.deployment.priorityClassName }} + {{- end }} + {{- if .Values.deployment.restartPolicy }} + restartPolicy: {{ .Values.deployment.restartPolicy }} + {{- end }} + {{- with .Values.deployment.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccount: {{ include "reposilite.deployment.serviceAccount" . }} + {{- if .Values.deployment.subdomain }} + subdomain: {{ .Values.deployment.subdomain }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.deployment.terminationGracePeriodSeconds }} + {{- with .Values.deployment.tolerations }} + tolerations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.deployment.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- $volumes := (include "reposilite.deployment.volumes" . | fromYaml) }} + {{- if and (hasKey $volumes "volumes") (gt (len $volumes.volumes) 0) }} + volumes: + {{- toYaml $volumes.volumes | nindent 6 }} + {{- end }} \ No newline at end of file diff --git a/templates/hpa.yaml b/templates/hpa.yaml new file mode 100644 index 0000000..1815a88 --- /dev/null +++ b/templates/hpa.yaml @@ -0,0 +1,29 @@ +{{- if .Values.hpa.enabled -}} +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + {{- with (include "reposilite.hpa.annotations" . | fromYaml) }} + annotations: + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- with (include "reposilite.hpa.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + {{- with .Values.hpa.behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} + metrics: + {{- toYaml .Values.hpa.metrics | nindent 2 }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + minReplicas: {{ .Values.hpa.minReplicas }} + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "reposilite.fullname" . }} +{{- end -}} \ No newline at end of file diff --git a/templates/ingress.yaml b/templates/ingress.yaml new file mode 100644 index 0000000..37c34c2 --- /dev/null +++ b/templates/ingress.yaml @@ -0,0 +1,45 @@ +{{- if .Values.ingress.enabled -}} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + {{- with (include "reposilite.ingress.annotations" . | fromYaml) }} + annotations: + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- with (include "reposilite.ingress.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + ingressClassName: {{ .Values.ingress.className }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ tpl .host $ | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if .pathType }} + pathType: {{ .pathType }} + {{- end }} + backend: + service: + name: {{ include "reposilite.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ tpl . $ | quote }} + {{- end }} + secretName: {{ .secretName | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/templates/networkPolicy.yaml b/templates/networkPolicy.yaml new file mode 100644 index 0000000..77487af --- /dev/null +++ b/templates/networkPolicy.yaml @@ -0,0 +1,32 @@ +{{- if .Values.networkPolicy.enabled }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + {{- with (include "reposilite.networkPolicy.annotations" . | fromYaml) }} + annotations: + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- with (include "reposilite.networkPolicy.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + {{- include "reposilite.pod.selectorLabels" $ | nindent 6 }} + {{- with .Values.networkPolicy.policyTypes }} + policyTypes: + {{- toYaml . | nindent 2 }} + {{- end }} + {{- with .Values.networkPolicy.egress }} + egress: + {{- toYaml . | nindent 2 }} + {{- end }} + {{- with .Values.networkPolicy.ingress }} + ingress: + {{- toYaml . | nindent 2 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/templates/persistentVolumeClaim.yaml b/templates/persistentVolumeClaim.yaml new file mode 100644 index 0000000..15ddadf --- /dev/null +++ b/templates/persistentVolumeClaim.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.persistentVolumeClaim.enabled (not .Values.persistentVolumeClaim.existing.enabled) -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- with (include "reposilite.persistentVolumeClaim.annotations" . | fromYaml) }} + annotations: + {{- tpl (toYaml .) $ | nindent 4 }} + {{- end }} + {{- with (include "reposilite.persistentVolumeClaim.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + accessModes: + - {{ .Values.persistentVolumeClaim.new.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistentVolumeClaim.new.size | quote }} + {{- if .Values.persistentVolumeClaim.new.storageClass }} + storageClassName: {{ .Values.persistentVolumeClaim.new.storageClass | quote }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/templates/service.yaml b/templates/service.yaml new file mode 100644 index 0000000..1672d39 --- /dev/null +++ b/templates/service.yaml @@ -0,0 +1,57 @@ +{{- if .Values.service.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + {{- with (include "reposilite.service.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "reposilite.service.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.service.name" . }} + namespace: {{ .Release.Namespace }} +spec: + {{- if not (empty .Values.service.externalIPs) }} + externalIPs: + {{- range .Values.service.externalIPs }} + - {{ . }} + {{- end }} + {{- end }} + {{- if and (or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort") ) .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} + {{- end }} + internalTrafficPolicy: {{ required "No internal traffic policy defined!" .Values.service.internalTrafficPolicy }} + {{- if .Values.service.ipFamilies }} + ipFamilies: + {{- range .Values.service.ipFamilies }} + - {{ . }} + {{- end }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.service.loadBalancerClass }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if eq .Values.service.type "LoadBalancer" }} + loadBalancerSourceRanges: + {{- range .Values.service.loadBalancerSourceRanges }} + - {{ . }} + {{- end }} + {{- end }} + ports: + - name: http + protocol: TCP + port: {{ required "No service port defined!" .Values.service.port }} + selector: + {{- include "reposilite.pod.selectorLabels" . | nindent 4 }} + sessionAffinity: {{ required "No session affinity defined!" .Values.service.sessionAffinity }} + {{- with .Values.service.sessionAffinityConfig }} + sessionAffinityConfig: + {{- toYaml . | nindent 4}} + {{- end }} + type: {{ required "No service type defined!" .Values.service.type }} +{{- end }} \ No newline at end of file diff --git a/templates/serviceAccount.yaml b/templates/serviceAccount.yaml new file mode 100644 index 0000000..cb85e56 --- /dev/null +++ b/templates/serviceAccount.yaml @@ -0,0 +1,25 @@ +{{- if not .Values.serviceAccount.existing.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- with (include "reposilite.serviceAccount.annotations" . | fromYaml) }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with (include "reposilite.serviceAccount.labels" . | fromYaml) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "reposilite.fullname" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ .Values.serviceAccount.new.automountServiceAccountToken }} +{{- with .Values.serviceAccount.new.imagePullSecrets }} +imagePullSecrets: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- with .Values.serviceAccount.new.secrets }} +secrets: + {{- toYaml . | nindent 4 }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/unittests/deployment/deployment.yaml b/unittests/deployment/deployment.yaml new file mode 100644 index 0000000..5b45354 --- /dev/null +++ b/unittests/deployment/deployment.yaml @@ -0,0 +1,447 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Deployment template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/deployment.yaml +tests: +- it: Rendering default + set: {} + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: apps/v1 + kind: Deployment + name: reposilite-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - equal: + path: spec.replicas + value: 1 + - isSubset: + path: spec.selector.matchLabels + content: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/name: reposilite + - equal: + path: spec.strategy + value: + type: "RollingUpdate" + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + - notExists: + path: spec.template.metadata.annotations + value: sadsdf + - equal: + path: spec.template.metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - notExists: + path: spec.template.spec.affinity + - notExists: + path: spec.template.spec.containers[0].args + - notExists: + path: spec.template.spec.containers[0].command + - contains: + path: spec.template.spec.containers[0].env + content: + name: JAVA_OPTS + value: "-Xmx64M" + - notExists: + path: spec.template.spec.containers[0].envFrom + - equal: + path: spec.template.spec.containers[0].image + value: docker.io/dzikoysk/reposilite:0.1.0 + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: IfNotPresent + - isSubset: + path: spec.template.spec.containers[0].livenessProbe + content: + tcpSocket: + port: http + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 3 + - equal: + path: spec.template.spec.containers[0].name + value: reposilite + - contains: + path: spec.template.spec.containers[0].ports + content: + name: http + containerPort: 8080 + protocol: TCP + - isSubset: + path: spec.template.spec.containers[0].readinessProbe + content: + tcpSocket: + port: http + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + - notExists: + path: spec.template.spec.containers[0].resources + - notExists: + path: spec.template.spec.containers[0].securityContext + - notExists: + path: spec.template.spec.containers[0].volumeMounts + - notExists: + path: spec.template.spec.dnsConfig + - notExists: + path: spec.template.spec.dnsPolicy + - notExists: + path: spec.template.spec.hostname + - equal: + path: spec.template.spec.hostNetwork + value: false + - notExists: + path: spec.template.spec.imagePullSecrets + - notExists: + path: spec.template.spec.initContainers + - notExists: + path: spec.template.spec.nodeSelector + - notExists: + path: spec.template.spec.priorityClassName + - notExists: + path: spec.template.spec.restartPolicy + - notExists: + path: spec.template.spec.subdomain + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 60 + - notExists: + path: spec.template.spec.tolerations + - notExists: + path: spec.template.spec.topologySpreadConstraints + - notExists: + path: spec.template.spec.volumes + +- it: Test custom replicas + set: + deployment.replicas: 3 + asserts: + - equal: + path: spec.replicas + value: 3 + +- it: Test custom strategy + set: + deployment.strategy: + type: "RollingUpdate" + rollingUpdate: + maxSurge: 10 + maxUnavailable: 5 + asserts: + - equal: + path: spec.strategy + value: + type: "RollingUpdate" + rollingUpdate: + maxSurge: 10 + maxUnavailable: 5 + +- it: Test custom affinity + set: + deployment.affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - antarctica-east1 + - antarctica-west1 + asserts: + - equal: + path: spec.template.spec.affinity + value: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: topology.kubernetes.io/zone + operator: In + values: + - antarctica-east1 + - antarctica-west1 + +- it: Test additional arguments + set: + deployment.reposilite.args: + - "--foo=bar" + - "--bar=foo" + asserts: + - equal: + path: spec.template.spec.containers[0].args + value: + - --foo=bar + - --bar=foo + +- it: Test additional commands + set: + deployment.reposilite.command: + - "/bin/bash" + asserts: + - equal: + path: spec.template.spec.containers[0].command + value: + - /bin/bash + +- it: Test custom imageRegistry and imageRepository + set: + deployment.reposilite.image.registry: registry.example.local + deployment.reposilite.image.repository: path/special/reposilite + deployment.reposilite.image.tag: 2.0.0 + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: registry.example.local/path/special/reposilite:2.0.0 + +- it: Test custom imagePullPolicy + set: + deployment.reposilite.image.pullPolicy: Always + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: Always + +- it: Test custom port + set: + service.port: 8443 + asserts: + - contains: + path: spec.template.spec.containers[0].ports + content: + name: http + containerPort: 8443 + protocol: TCP + +- it: Test custom resources + set: + deployment.reposilite.resources: + limits: + cpu: 100m + memory: 250MB + requests: + cpu: 25m + memory: 100MB + asserts: + - equal: + path: spec.template.spec.containers[0].resources + value: + limits: + cpu: 100m + memory: 250MB + requests: + cpu: 25m + memory: 100MB + +- it: Test custom securityContext + set: + deployment.reposilite.securityContext: + capabilities: + add: + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: + capabilities: + add: + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +- it: Test custom volumeMounts + set: + deployment.reposilite.volumeMounts: + - name: data + mountPath: /usr/lib/data + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: data + mountPath: /usr/lib/data + +- it: Test dnsConfig + set: + deployment.dnsConfig: + nameservers: + - "8.8.8.8" + - "8.8.4.4" + asserts: + - equal: + path: spec.template.spec.dnsConfig + value: + nameservers: + - "8.8.8.8" + - "8.8.4.4" + +- it: Test dnsPolicy + set: + deployment.dnsPolicy: ClusterFirst + asserts: + - equal: + path: spec.template.spec.dnsPolicy + value: ClusterFirst + +- it: Test hostNetwork, hostname, subdomain + set: + deployment.hostNetwork: true + deployment.hostname: pg-exporter + deployment.subdomain: exporters.internal + asserts: + - equal: + path: spec.template.spec.hostNetwork + value: true + - equal: + path: spec.template.spec.hostname + value: pg-exporter + - equal: + path: spec.template.spec.subdomain + value: exporters.internal + +- it: Test imagePullSecrets + set: + deployment.imagePullSecrets: + - name: my-pull-secret + - name: my-special-secret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets + value: + - name: my-pull-secret + - name: my-special-secret + +- it: Test nodeSelector + set: + deployment.nodeSelector: + foo: bar + asserts: + - equal: + path: spec.template.spec.nodeSelector + value: + foo: bar + +- it: Test priorityClassName + set: + deployment.priorityClassName: my-priority + asserts: + - equal: + path: spec.template.spec.priorityClassName + value: my-priority + +- it: Test restartPolicy + set: + deployment.restartPolicy: Always + asserts: + - equal: + path: spec.template.spec.restartPolicy + value: Always + +- it: Test custom securityContext + set: + deployment.securityContext: + fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + asserts: + - equal: + path: spec.template.spec.securityContext + value: + fsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + +- it: Test terminationGracePeriodSeconds + set: + deployment.terminationGracePeriodSeconds: 120 + asserts: + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 120 + +- it: Test tolerations + set: + deployment.tolerations: + - key: hdd/type + operator: Equal + value: ssd + effect: NoSchedule + asserts: + - equal: + path: spec.template.spec.tolerations + value: + - key: hdd/type + operator: Equal + value: ssd + effect: NoSchedule + +- it: Test topologySpreadConstraints + set: + deployment.topologySpreadConstraints: + - topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/instance: reposilite + asserts: + - equal: + path: spec.template.spec.topologySpreadConstraints + value: + - topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/instance: reposilite + +- it: Test additional volumes + set: + deployment.volumes: + - name: data + hostPath: + path: /usr/lib/data + asserts: + - equal: + path: spec.template.spec.volumes + value: + - name: data + hostPath: + path: /usr/lib/data diff --git a/unittests/deployment/mountPersistentVolumeClaim.yaml b/unittests/deployment/mountPersistentVolumeClaim.yaml new file mode 100644 index 0000000..7b58b1b --- /dev/null +++ b/unittests/deployment/mountPersistentVolumeClaim.yaml @@ -0,0 +1,52 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Mount persistent volume claim +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/deployment.yaml +tests: +- it: Rendering default volumes and volumeMounts with persistent volume claim + set: + persistentVolumeClaim.enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REPOSILITE_DATA + value: /app/data + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: data + mountPath: /app/data + - contains: + path: spec.template.spec.volumes + content: + name: data + persistentVolumeClaim: + claimName: reposilite-unittest + +- it: Rendering custom volumes and volumeMounts with persistent volume claim + set: + persistentVolumeClaim.enabled: true + persistentVolumeClaim.path: /usr/lib/reposilite/data + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: REPOSILITE_DATA + value: /usr/lib/reposilite/data + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: data + mountPath: /usr/lib/reposilite/data + - contains: + path: spec.template.spec.volumes + content: + name: data + persistentVolumeClaim: + claimName: reposilite-unittest diff --git a/unittests/hpa/hpa.yaml b/unittests/hpa/hpa.yaml new file mode 100644 index 0000000..9d9056f --- /dev/null +++ b/unittests/hpa/hpa.yaml @@ -0,0 +1,152 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: HPA template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/hpa.yaml +tests: +- it: Skip rendering by default. + asserts: + - hasDocuments: + count: 0 + +- it: Rendering when enabled - default + set: + hpa.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: autoscaling/v2 + kind: HorizontalPodAutoscaler + name: reposilite-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - isSubset: + path: spec.behavior + content: + scaleDown: + policies: + - type: Pods + value: 1 + periodSeconds: 60 + stabilizationWindowSeconds: 300 + - lengthEqual: + path: spec.metrics + count: 2 + - contains: + path: spec.metrics + content: + resource: + name: cpu + target: + averageUtilization: 65 + type: Utilization + type: Resource + - contains: + path: spec.metrics + content: + resource: + name: memory + target: + averageUtilization: 65 + type: Utilization + type: Resource + - equal: + path: spec.maxReplicas + value: 10 + - equal: + path: spec.minReplicas + value: 1 + - equal: + path: spec.scaleTargetRef + value: + apiVersion: apps/v1 + kind: Deployment + name: reposilite-unittest + +- it: Rendering when enabled - custom values + set: + hpa.enabled: true + hpa.annotations: + foo: bar + hpa.labels: + bar: foo + hpa.maxReplicas: 25 + hpa.minReplicas: 5 + hpa.metrics: + - resource: + name: cpu + target: + averageUtilization: 60 + type: Utilization + type: Resource + - resource: + name: memory + target: + averageUtilization: 60 + type: Utilization + type: Resource + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: autoscaling/v2 + kind: HorizontalPodAutoscaler + name: reposilite-unittest + namespace: testing + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + bar: foo + helm.sh/chart: reposilite-0.1.0 + - contains: + path: spec.metrics + content: + resource: + name: cpu + target: + averageUtilization: 60 + type: Utilization + type: Resource + - contains: + path: spec.metrics + content: + resource: + name: memory + target: + averageUtilization: 60 + type: Utilization + type: Resource + - equal: + path: spec.maxReplicas + value: 25 + - equal: + path: spec.minReplicas + value: 5 + - equal: + path: spec.scaleTargetRef + value: + apiVersion: apps/v1 + kind: Deployment + name: reposilite-unittest \ No newline at end of file diff --git a/unittests/ingress/http.yaml b/unittests/ingress/http.yaml new file mode 100644 index 0000000..bffa8b1 --- /dev/null +++ b/unittests/ingress/http.yaml @@ -0,0 +1,110 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Ingress HTTP template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/ingress.yaml +tests: +- it: Skip rendering when disabled + set: + ingress.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Rendering default values + set: + ingress.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: networking.k8s.io/v1 + kind: Ingress + name: reposilite-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - equal: + path: spec.ingressClassName + value: nginx + - isNullOrEmpty: + path: spec.rules + - notExists: + path: spec.tls + +- it: Render custom annotations and labels + set: + ingress.annotations: + foo: bar + ingress.enabled: true + ingress.labels: + foo: bar + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + foo: bar + helm.sh/chart: reposilite-0.1.0 + +- it: Render custom HTTP path and TLS + set: + ingress.enabled: true + ingress.hosts: + - host: reposilite.example.local + paths: + - path: / + pathType: Prefix + ingress.tls: + - secretName: reposilite-http-tls + hosts: + - reposilite.example.local + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: networking.k8s.io/v1 + kind: Ingress + name: reposilite-unittest + namespace: testing + - equal: + path: spec.ingressClassName + value: nginx + - contains: + path: spec.rules + content: + host: reposilite.example.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: reposilite-unittest + port: + number: 8080 + - contains: + path: spec.tls + content: + hosts: + - reposilite.example.local + secretName: reposilite-http-tls diff --git a/unittests/networkPolicy/networkPolicy.yaml b/unittests/networkPolicy/networkPolicy.yaml new file mode 100644 index 0000000..ba803ca --- /dev/null +++ b/unittests/networkPolicy/networkPolicy.yaml @@ -0,0 +1,98 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: NetworkPolicy template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/networkPolicy.yaml +tests: +- it: Skip rendering networkPolicy + set: + networkPolicy.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Render default networkPolicy + set: + networkPolicy.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + name: reposilite-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - equal: + path: spec.podSelector.matchLabels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/name: reposilite + - notExists: + path: spec.policyTypes + - notExists: + path: spec.egress + - notExists: + path: spec.ingress + +- it: Template networkPolicy with policyTypes, egress and ingress configuration + set: + networkPolicy.enabled: true + networkPolicy.policyTypes: + - Egress + - Ingress + networkPolicy.ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: monitoring + podSelector: + matchLabels: + app.kubernetes.io/name: prometheus + networkPolicy.egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: ingress-nginx + podSelector: + matchLabels: + app.kubernetes.io/name: ingress-nginx + asserts: + - equal: + path: spec.policyTypes + value: + - Egress + - Ingress + - equal: + path: spec.egress + value: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: ingress-nginx + podSelector: + matchLabels: + app.kubernetes.io/name: ingress-nginx + - equal: + path: spec.ingress + value: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: monitoring + podSelector: + matchLabels: + app.kubernetes.io/name: prometheus diff --git a/unittests/persistentVolumeClaim/persistentVolumeClaim.yaml b/unittests/persistentVolumeClaim/persistentVolumeClaim.yaml new file mode 100644 index 0000000..82dca64 --- /dev/null +++ b/unittests/persistentVolumeClaim/persistentVolumeClaim.yaml @@ -0,0 +1,97 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: PersistentVolumeClaim template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/persistentVolumeClaim.yaml +tests: +- it: Skip rendering persistentVolumeClaim, when it's disabled in general + set: + persistentVolumeClaim.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Skip rendering persistentVolumeClaim, when using an existing persistentVolumeClaim + set: + persistentVolumeClaim.enabled: true + persistentVolumeClaim.existing.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Render default persistentVolumeClaim + set: + persistentVolumeClaim.enabled: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: PersistentVolumeClaim + name: reposilite-unittest + namespace: testing + - equal: + path: metadata.annotations + value: + helm.sh/resource-policy: keep + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - contains: + path: spec.accessModes + content: + ReadWriteOnce + - equal: + path: spec.resources.requests.storage + value: 10Gi + +- it: Test custom annotations and labels + set: + persistentVolumeClaim.enabled: true + persistentVolumeClaim.new.annotations: + foo: bar + persistentVolumeClaim.new.labels: + foo: bar + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + helm.sh/resource-policy: keep + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + foo: bar + helm.sh/chart: reposilite-0.1.0 + +- it: Test custom access mode + set: + persistentVolumeClaim.enabled: true + persistentVolumeClaim.new.accessMode: ReadWriteMany + asserts: + - contains: + path: spec.accessModes + content: + ReadWriteMany + +- it: Test custom size + set: + persistentVolumeClaim.enabled: true + persistentVolumeClaim.new.size: 50Gi + asserts: + - equal: + path: spec.resources.requests.storage + value: 50Gi \ No newline at end of file diff --git a/unittests/serviceAccount/serviceAccount.yaml b/unittests/serviceAccount/serviceAccount.yaml new file mode 100644 index 0000000..7157e45 --- /dev/null +++ b/unittests/serviceAccount/serviceAccount.yaml @@ -0,0 +1,79 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: ServiceAccount reposilite template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/serviceAccount.yaml +tests: +- it: Skip rendering. + set: + serviceAccount.existing.enabled: true + asserts: + - hasDocuments: + count: 0 + +- it: Rendering serviceAccount with default values. + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: ServiceAccount + name: reposilite-unittest + namespace: testing + - notExists: + path: metadata.annotations + - notExists: + path: metadata.labels + - equal: + path: automountServiceAccountToken + value: true + - notExists: + path: imagePullSecrets + - notExists: + path: secrets + + +- it: Rendering serviceAccount with custom values. + set: + serviceAccount.new.annotations: + foo: bar + serviceAccount.new.labels: + bar: foo + serviceAccount.new.automountServiceAccountToken: false + serviceAccount.new.imagePullSecrets: + - name: "my-pull-secret" + serviceAccount.new.secrets: + - name: "my-secret" + namespace: "my-namespace" + fieldPath: "my-path" + asserts: + - hasDocuments: + count: 1 + - exists: + path: metadata.annotations + value: + foo: bar + - exists: + path: metadata.labels + value: + bar: foo + - equal: + path: metadata.name + value: reposilite-unittest + - equal: + path: automountServiceAccountToken + value: false + - equal: + path: imagePullSecrets + value: + - name: "my-pull-secret" + - equal: + path: secrets + value: + - name: "my-secret" + namespace: "my-namespace" + fieldPath: "my-path" diff --git a/unittests/services/service.yaml b/unittests/services/service.yaml new file mode 100644 index 0000000..6bfd465 --- /dev/null +++ b/unittests/services/service.yaml @@ -0,0 +1,172 @@ +chart: + appVersion: 0.1.0 + version: 0.1.0 +suite: Service http template +release: + name: reposilite-unittest + namespace: testing +templates: +- templates/service.yaml +tests: +- it: Skip service when disabled. + set: + service.enabled: false + asserts: + - hasDocuments: + count: 0 + +- it: Rendering service with default values. + asserts: + - hasDocuments: + count: 1 + - containsDocument: + apiVersion: v1 + kind: Service + name: reposilite-unittest + namespace: testing + - notExists: + path: metadata.annotations + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + - notExists: + path: spec.externalIPs + - notExists: + path: spec.externalTrafficPolicy + - equal: + path: spec.internalTrafficPolicy + value: Cluster + - notExists: + path: spec.ipFamilies + - notExists: + path: spec.loadBalancerClass + - notExists: + path: spec.loadBalancerIP + - notExists: + path: spec.loadBalancerSourceRanges + - equal: + path: spec.ports[0].name + value: http + - equal: + path: spec.ports[0].protocol + value: TCP + - equal: + path: spec.ports[0].port + value: 8080 + - equal: + path: spec.selector + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/name: reposilite + - equal: + path: spec.sessionAffinity + value: None + - notExists: + path: spec.sessionAffinityConfig + - equal: + path: spec.type + value: ClusterIP + +- it: Require internalTrafficPolicy. + set: + service.internalTrafficPolicy: "" + asserts: + - failedTemplate: + errorMessage: No internal traffic policy defined! + +- it: Require port. + set: + service.port: "" + asserts: + - failedTemplate: + errorMessage: No service port defined! + +- it: Require sessionAffinity. + set: + service.sessionAffinity: "" + asserts: + - failedTemplate: + errorMessage: No session affinity defined! + +- it: Require service type. + set: + service.type: "" + asserts: + - failedTemplate: + errorMessage: No service type defined! + +- it: Render service with custom annotations and labels. + set: + service.annotations: + foo: bar + service.labels: + bar: foo + asserts: + - equal: + path: metadata.annotations + value: + foo: bar + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: reposilite-unittest + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: reposilite + app.kubernetes.io/version: 0.1.0 + helm.sh/chart: reposilite-0.1.0 + bar: foo + +- it: Change defaults + set: + service.externalIPs: + - "10.11.12.13/32" + service.externalTrafficPolicy: Local + service.internalTrafficPolicy: Local + service.ipFamilies: + - IPv4 + service.loadBalancerClass: aws + service.loadBalancerIP: "11.12.13.14" + service.loadBalancerSourceRanges: + - "11.12.0.0/17" + service.port: 10443 + service.sessionAffinity: ClientIP + service.type: LoadBalancer + asserts: + - equal: + path: spec.externalIPs + value: + - 10.11.12.13/32 + - equal: + path: spec.externalTrafficPolicy + value: Local + - equal: + path: spec.internalTrafficPolicy + value: Local + - equal: + path: spec.ipFamilies + value: + - IPv4 + - equal: + path: spec.loadBalancerClass + value: aws + - equal: + path: spec.loadBalancerIP + value: "11.12.13.14" + - equal: + path: spec.loadBalancerSourceRanges + value: + - "11.12.0.0/17" + - equal: + path: spec.ports[0].port + value: 10443 + - equal: + path: spec.sessionAffinity + value: ClientIP + - equal: + path: spec.type + value: LoadBalancer \ No newline at end of file diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..1a0af50 --- /dev/null +++ b/values.yaml @@ -0,0 +1,412 @@ +# Declare variables to be passed into your templates. + +## @section Global +## @param nameOverride Individual release name suffix. +## @param fullnameOverride Override the complete release name logic. +nameOverride: "" +fullnameOverride: "" + +## @section Deployment +deployment: + ## @param deployment.annotations Additional deployment annotations. + ## @param deployment.labels Additional deployment labels. + annotations: {} + labels: {} + + ## @param deployment.additionalContainers List of additional containers. + additionalContainers: [] + # - command: [ "sh", "-c", "echo hello world" ] + # image: "docker.io/library/busybox:latest" + # name: side-car + + ## @param deployment.affinity Affinity for the Reposilite deployment. + affinity: {} + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/os + # operator: In + # values: + # - linux + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 20 + # preference: + # matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + + ## @param deployment.initContainers List of additional init containers. + initContainers: [] + # - command: [ "sh", "-c", "echo hello world" ] + # image: "docker.io/library/busybox:latest" + # name: init + + ## @param deployment.dnsConfig dnsConfig of the Reposilite deployment. + dnsConfig: {} + # nameservers: + # - 192.0.2.1 # this is an example + # searches: + # - ns1.svc.cluster-domain.example + # - my.dns.search.suffix + # options: + # - name: ndots + # value: "2" + # - name: edns0 + + ## @param deployment.dnsPolicy dnsPolicy of the Reposilite deployment. + dnsPolicy: "" + + ## @param deployment.hostname Individual hostname of the pod. + ## @param deployment.subdomain Individual domain of the pod. + hostname: "" + subdomain: "" + + ## @param deployment.hostNetwork Use the kernel network namespace of the host system. + hostNetwork: false + + ## @param deployment.imagePullSecrets Secret to use for pulling the image. + imagePullSecrets: [] + # - name: "my-custom-secret" + + reposilite: + ## @param deployment.reposilite.args Arguments passed to the Reposilite container. + args: [] + # - "-c" + # - "echo HelloWorld" + + ## @param deployment.reposilite.command Command passed to the Reposilite container. + command: [] + # - "/bin/bash" + + ## @extra deployment.reposilite.env List of environment variables for the Reposilite container. + ## @skip deployment.reposilite.env[0].name JAVA_OPTS: Environment variable to define custom java options. + ## @skip deployment.reposilite.env[0].value Set custom java options. Use `-Xmx64M` to reduce the heap of the JVM. + env: + - name: JAVA_OPTS + value: "-Xmx64M" + # - name: SPECIAL_ENV_A + # value: special-key + # - name: SPECIAL_ENV + # valueFrom: + # configMapKeyRef: + # name: special-config + # key: special-key + # - name: SPECIAL_ENV + # valueFrom: + # secretKeyRef: + # name: special-secret + # key: special-key + + ## @param deployment.reposilite.envFrom List of environment variables mounted from configMaps or secrets for the Reposilite container. + envFrom: [] + # - configMapRef: + # name: special-config + # - secretRef: + # name: special-secret + + ## @param deployment.reposilite.image.registry Image registry, eg. `docker.io`. + ## @param deployment.reposilite.image.repository Image repository, eg. `library/busybox`. + ## @param deployment.reposilite.image.tag Custom image tag, eg. `0.1.0`. Defaults to `appVersion`. + ## @param deployment.reposilite.image.pullPolicy Image pull policy. + image: + registry: docker.io + repository: dzikoysk/reposilite + tag: "" + pullPolicy: IfNotPresent + + ## @param deployment.reposilite.resources CPU and memory resources of the pod. + resources: {} + # limits: + # cpu: + # ephemeral-storage: + # memory: + # requests: + # cpu: + # ephemeral-storage: + # memory: + + ## @param deployment.reposilite.securityContext Security context of the container of the deployment. + securityContext: {} + # capabilities: + # add: + # - NET_RAW + # drop: + # - ALL + # privileged: false + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + + ## @param deployment.reposilite.volumeMounts Additional volume mounts. + volumeMounts: [] + # - name: my-configmap-volume + # mountPath: /configmap + # readOnly: true + + ## @param deployment.nodeSelector NodeSelector of the Reposilite deployment. + nodeSelector: {} + + ## @param deployment.priorityClassName PriorityClassName of the Reposilite deployment. + priorityClassName: "" + + ## @param deployment.replicas Number of replicas for the Reposilite deployment. + replicas: 1 + + ## @param deployment.restartPolicy Restart policy of the Reposilite deployment. + restartPolicy: "" + + ## @param deployment.securityContext Security context of the Reposilite deployment. + securityContext: {} + # fsGroup: 2000 + + ## @param deployment.strategy.type Strategy type - `Recreate` or `RollingUpdate`. + ## @param deployment.strategy.rollingUpdate.maxSurge The maximum number of pods that can be scheduled above the desired number of pods during a rolling update. + ## @param deployment.strategy.rollingUpdate.maxUnavailable The maximum number of pods that can be unavailable during a rolling update. + strategy: + type: "RollingUpdate" + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + + ## @param deployment.terminationGracePeriodSeconds How long to wait until forcefully kill the pod. + terminationGracePeriodSeconds: 60 + + ## @param deployment.tolerations Tolerations of the Reposilite deployment. + tolerations: [] + # - key: host.kubernetes.io/ssd + # operator: Equal + # value: "true" + # effect: NoSchedule + + ## @param deployment.topologySpreadConstraints TopologySpreadConstraints of the Reposilite deployment. + topologySpreadConstraints: [] + # - topologyKey: kubernetes.io/hostname + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: prometheus-reposilite + + ## @param deployment.volumes Additional volumes to mount into the pods of the prometheus-exporter deployment. + volumes: [] + # - name: my-configmap-volume + # config: + # name: my-configmap + # - name: my-secret-volume + # secret: + # secretName: my-secret + + +## @section Horizontal Pod Autoscaler (HPA) +# In order for the HPA to function successfully, a metric server is required, especially for resource consumption. The +# metric server enables the CPU and memory utilisation to be recorded. If such a metric server is not available, the HPA +# cannot scale pods based on CPU or memory utilisation. Further information be be found here: +# https://github.com/kubernetes-sigs/metrics-server#deployment +hpa: + ## @param hpa.enabled Enable the horizontal pod autoscaler (HPA). + ## @param hpa.annotations Additional annotations for the HPA. + ## @param hpa.labels Additional labels for the HPA. + ## @extra hpa.behavior.scaleDown Scaling policy for scaling down. + ## @skip hpa.behavior.scaleDown.policies Skip individual scale down policies. + ## @skip hpa.behavior.scaleDown.stabilizationWindowSeconds Skip individual stabilizationWindowSeconds. + ## @param hpa.metrics Metrics contains the specifications for which to use to calculate the desired replica count. + ## @skip hpa.metrics Skip individual HPA metric configurations. + ## @param hpa.minReplicas Min replicas is the lower limit for the number of replicas to which the autoscaler can scale down. + ## @param hpa.maxReplicas Upper limit for the number of pods that can be set by the autoscaler. + enabled: false + annotations: {} + labels: {} + behavior: + scaleDown: + policies: + - type: Pods + value: 1 + periodSeconds: 60 + stabilizationWindowSeconds: 300 + metrics: + - resource: + name: cpu + target: + averageUtilization: 65 + type: Utilization + type: Resource + - resource: + name: memory + target: + averageUtilization: 65 + type: Utilization + type: Resource + minReplicas: 1 + maxReplicas: 10 + + +## @section Ingress +ingress: + ## @param ingress.enabled Enable creation of an ingress resource. Requires, that the http service is also enabled. + ## @param ingress.className Ingress class. + ## @param ingress.annotations Additional ingress annotations. + ## @param ingress.labels Additional ingress labels. + enabled: false + className: "nginx" + annotations: {} + labels: {} + + ## @param ingress.hosts Ingress specific configuration. Let ingress controller listen on specific DNS names and path's. + ## @skip ingress.hosts Skip individual host configuration. + hosts: [] + # - host: reposilite.example.local + # paths: + # - path: / + # pathType: Prefix + + ## @param ingress.tls Ingress specific TLS configuration. Use TLS encryption for specific hostnames. + ## @skip ingress.tls Skip individual TLS configuration. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + +## @section Network Policy +networkPolicy: + ## @param networkPolicy.enabled Enable network policies in general. + ## @param networkPolicy.annotations Additional network policy annotations. + ## @param networkPolicy.labels Additional network policy labels. + ## @param networkPolicy.policyTypes List of policy types. Supported is ingress, egress or ingress and egress. + ## @param networkPolicy.egress Concrete egress network policy implementation. + ## @skip networkPolicy.egress Skip individual egress configuration. + ## @param networkPolicy.ingress Concrete ingress network policy implementation. + ## @skip networkPolicy.ingress Skip individual ingress configuration. + enabled: false + annotations: {} + labels: {} + policyTypes: [] + # - Egress + # - Ingress + egress: [] + # Allow outgoing DNS traffic to the internal running DNS-Server. For example core-dns. + # + # - to: + # - namespaceSelector: + # matchLabels: + # kubernetes.io/metadata.name: kube-system + # podSelector: + # matchLabels: + # k8s-app: kube-dns + # ports: + # - port: 53 + # protocol: TCP + # - port: 53 + # protocol: UDP + + ingress: [] + # Allow incoming HTTP traffic from prometheus. + # + # - from: + # - namespaceSelector: + # matchLabels: + # kubernetes.io/metadata.name: monitoring + # podSelector: + # matchLabels: + # app.kubernetes.io/name: prometheus + # ports: + # - port: http + # protocol: TCP + + # Allow incoming HTTP traffic from ingress-nginx. + # + # - from: + # - namespaceSelector: + # matchLabels: + # kubernetes.io/metadata.name: ingress-nginx + # podSelector: + # matchLabels: + # app.kubernetes.io/name: ingress-nginx + # ports: + # - port: http + # protocol: TCP + + +## @section Persistent Volume Claim +persistentVolumeClaim: + ## @param persistentVolumeClaim.enabled Enable persistence in general. + ## @param persistentVolumeClaim.path Path where the persistent volume claim should be mounted into the container file system. + enabled: false + path: /app/data + + ## @param persistentVolumeClaim.existing.enabled Enable an existing persistent volume claim. + ## @param persistentVolumeClaim.existing.persistentVolumeClaimName Name of the existing persistent volume claim. + existing: + enabled: false + persistentVolumeClaimName: "" + + ## @param persistentVolumeClaim.new.annotations Additional service account annotations. + ## @param persistentVolumeClaim.new.labels Additional service account labels. + ## @param persistentVolumeClaim.new.accessMode Access mode of the persistent volume claim. More information about the access modes are in detail documented [online](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes). + ## @param persistentVolumeClaim.new.size Size of the persistent volume claim. + ## @param persistentVolumeClaim.new.storageClass Custom storage class. Left it empty to use the clusters default storage class. + new: + annotations: {} + labels: {} + accessMode: ReadWriteOnce + size: 10Gi + storageClass: "" + + +## @section Service +## @param service.enabled Enable the service. +## @param service.annotations Additional service annotations. +## @param service.externalIPs External IPs for the service. +## @param service.externalTrafficPolicy If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster external traffic. Furthermore, this enables source IP preservation. +## @param service.internalTrafficPolicy If `service.type` is `NodePort` or `LoadBalancer`, set this to `Local` to tell kube-proxy to only use node local endpoints for cluster internal traffic. +## @param service.ipFamilies IPFamilies is list of IP families (e.g. `IPv4`, `IPv6`) assigned to this service. This field is usually assigned automatically based on cluster configuration and only required for customization. +## @param service.labels Additional service labels. +## @param service.loadBalancerClass LoadBalancerClass is the class of the load balancer implementation this Service belongs to. Requires service from type `LoadBalancer`. +## @param service.loadBalancerIP LoadBalancer will get created with the IP specified in this field. Requires service from type `LoadBalancer`. +## @param service.loadBalancerSourceRanges Source range filter for LoadBalancer. Requires service from type `LoadBalancer`. +## @param service.port Port to forward the traffic to. +## @param service.sessionAffinity Supports `ClientIP` and `None`. Enable client IP based session affinity via `ClientIP`. +## @param service.sessionAffinityConfig Contains the configuration of the session affinity. +## @param service.type Kubernetes service type for the traffic. +service: + enabled: true + annotations: {} + externalIPs: [] + externalTrafficPolicy: "Cluster" + internalTrafficPolicy: "Cluster" + ipFamilies: [] + labels: {} + loadBalancerClass: "" + loadBalancerIP: "" + loadBalancerSourceRanges: [] + port: 8080 + sessionAffinity: "None" + sessionAffinityConfig: {} + type: "ClusterIP" + + +## @section ServiceAccount +serviceAccount: + ## @param serviceAccount.existing.enabled Use an existing service account instead of creating a new one. Assumes that the user has all the necessary kubernetes API authorizations. + ## @param serviceAccount.existing.serviceAccountName Name of the existing service account. + existing: + enabled: false + serviceAccountName: "" + + ## @param serviceAccount.new.annotations Additional service account annotations. + ## @param serviceAccount.new.labels Additional service account labels. + ## @param serviceAccount.new.automountServiceAccountToken Enable/disable auto mounting of the service account token. + ## @param serviceAccount.new.imagePullSecrets ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this serviceAccount. + ## @param serviceAccount.new.secrets Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. + new: + annotations: {} + labels: {} + automountServiceAccountToken: true + imagePullSecrets: [] + # - name: "my-image-pull-secret" + secrets: [] + # - name: "my-secret" + # namespace: "my-namespace" + # fieldPath: "my-field"