diff --git a/.markdownlint.yaml b/.markdownlint.yaml index 305545f..4766f9f 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -47,7 +47,7 @@ MD013: # Number of characters line_length: 200 # Number of characters for headings - heading_line_length: 80 + heading_line_length: 100 # Number of characters for code blocks code_block_line_length: 80 # Include code blocks @@ -106,7 +106,7 @@ MD030: # MD033/no-inline-html - Inline HTML MD033: # Allowed elements - allowed_elements: [] + allowed_elements: [details, summary] # MD035/hr-style - Horizontal rule style MD035: @@ -135,6 +135,7 @@ MD044: - Prometheus - Git - GitOps + - Forgejo # Include code blocks code_blocks: false diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a216a96 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "yzhang.markdown-all-in-one", + "DavidAnson.vscode-markdownlint", + "Tim-Koehler.helm-intellisense", + "esbenp.prettier-vscode" + ] + } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..812b1f8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "yaml.schemas": { + "https://raw.githubusercontent.com/helm-unittest/helm-unittest/v0.3.6/schema/helm-testsuite.json": [ + "/unittests/**/*.yaml" + ] + }, + "yaml.schemaStore.enable": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f8f4f0..d4c06e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,21 +9,16 @@ refactorings for easier maintainability or documentation improvements. - [`helm`](https://helm.sh/docs/intro/install/) - `make` is optional; you may call the commands directly -When using Visual Studio Code as IDE, following plugins might be useful: - -- [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) -- [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) -- [Helm Intellisense](https://marketplace.visualstudio.com/items?itemName=Tim-Koehler.helm-intellisense) -- [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +When using Visual Studio Code as IDE, a [ready-to-use profile](.vscode/) is available. ## Documentation Requirements -The `README.md` must include all configuration options. The parameters section -is generated by extracting the parameter annotations from the `values.yaml` file, -by using [this tool](https://github.com/bitnami-labs/readme-generator-for-helm). +The `README.md` must include all configuration options. +The parameters section is generated by extracting the parameter annotations from the `values.yaml` file, by using [this tool](https://github.com/bitnami-labs/readme-generator-for-helm). -If changes were made on configuration options, run `make readme` to update the -README file. +If changes were made on configuration options, run `make readme` to update the README file. + +The ToC is created via the VSCode [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) extension which can/must also be used used to update it. ## Pull Request Requirements @@ -41,16 +36,15 @@ For local development and testing of pull requests, the following workflow can be used: 1. Install `minikube` and `helm`. -2. Start a `minikube` cluster via `minikube start`. -3. From the `gitea/helm-chart` directory execute the following command. This - will install the dependencies listed in `Chart.yml` and deploy the current - state of the helm chart found locally. If you want to test a branch, make - sure to switch to the respective branch first. - `helm install --dependency-update gitea . -f values.yaml`. -4. Gitea is now deployed in `minikube`. To access it, it's port needs to be - forwarded first from `minikube` to localhost first via `kubectl --namespace - default port-forward svc/gitea-http 3000:3000`. Now Gitea is accessible at - [http://localhost:3000](http://localhost:3000). +1. Start a `minikube` cluster via `minikube start`. +1. From the `forgejo-contrib/forgejo-helm` directory execute the following command. + This will install the dependencies listed in `Chart.yml` and deploy the current state of the helm chart found locally. + If you want to test a branch, make sure to switch to the respective branch first. + `helm install --dependency-update forgejo . -f values.yaml`. +1. Forgejo is now deployed in `minikube`. + To access it, it's port needs to be forwarded first from `minikube` to localhost first via `kubectl --namespace +default port-forward svc/gitea-http 3000:3000`. + Now Forgejo is accessible at [http://localhost:3000](http://localhost:3000). ### Unit tests @@ -61,3 +55,11 @@ $ helm plugin install https://github.com/helm-unittest/helm-unittest # run the unittests make unittests ``` + +See [plugin documentation](https://github.com/helm-unittest/helm-unittest/blob/main/DOCUMENT.md) for usage instructions. + +## Release process + +1. Create a tag following the tagging schema +1. Push the tag +1. Let CI do it's work diff --git a/Chart.lock b/Chart.lock index bb7e9fd..2bd11a1 100644 --- a/Chart.lock +++ b/Chart.lock @@ -1,9 +1,12 @@ dependencies: -- name: memcached - repository: oci://registry-1.docker.io/bitnamicharts - version: 6.7.1 - name: postgresql repository: oci://registry-1.docker.io/bitnamicharts version: 13.2.24 -digest: sha256:00c678cbc02c9f9e12ad5b1a9b3b2f3f6e6b6fcaadfbda5979baf8cf41475e66 -generated: "2023-12-06T00:08:05.974489063Z" +- name: postgresql-ha + repository: oci://registry-1.docker.io/bitnamicharts + version: 12.3.1 +- name: redis-cluster + repository: oci://registry-1.docker.io/bitnamicharts + version: 9.1.3 +digest: sha256:f8a15775e3245385109ae0d7a7374163e8e718fd6b38ef3f17dbe142dd600aea +generated: "2023-12-06T11:40:02.8712781+01:00" diff --git a/Chart.yaml b/Chart.yaml index 148ea88..e81030e 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -26,12 +26,18 @@ maintainers: # https://hub.docker.com/u/bitnamicharts # https://blog.bitnami.com/2023/01/bitnami-helm-charts-available-as-oci.html dependencies: - - name: memcached - repository: oci://registry-1.docker.io/bitnamicharts - version: 6.7.1 - condition: memcached.enabled - # Chart release date: 2023-04 + # https://github.com/bitnami/charts/blob/main/bitnami/postgresql/Chart.yaml - name: postgresql repository: oci://registry-1.docker.io/bitnamicharts version: 13.2.24 condition: postgresql.enabled + # https://github.com/bitnami/charts/blob/main/bitnami/postgresql-ha/Chart.yaml + - name: postgresql-ha + repository: oci://registry-1.docker.io/bitnamicharts + version: 12.3.1 + condition: postgresql-ha.enabled + # https://github.com/bitnami/charts/blob/main/bitnami/redis-cluster/Chart.yaml + - name: redis-cluster + repository: oci://registry-1.docker.io/bitnamicharts + version: 9.1.3 + condition: redis-cluster.enabled \ No newline at end of file diff --git a/Makefile b/Makefile index ff48c63..4e4b5bd 100644 --- a/Makefile +++ b/Makefile @@ -9,4 +9,9 @@ readme: prepare-environment .PHONY: unittests unittests: - helm unittest --strict -f 'unittests/**/*.yaml' ./ + helm unittest --strict -f 'unittests/**/*.yaml' -f 'unittests/dependency-major-image-check.yaml' ./ + +.PHONY: helm +update-helm-dependencies: + helm dependency update + \ No newline at end of file diff --git a/README.md b/README.md index 35aa133..fe7d2bc 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,68 @@ -# Forgejo Helm Chart + +# Forgejo Helm Chart [![status-badge](https://ci.dachary.org/api/badges/forgejo-contrib/forgejo-helm/status.svg)](https://ci.dachary.org/forgejo-contrib/forgejo-helm) -[Forgejo](https://forgejo.org/) is a community managed lightweight code hosting -solution written in Go. It is published under the MIT license. +- [Introduction](#introduction) +- [Update and versioning policy](#update-and-versioning-policy) +- [Dependencies](#dependencies) +- [Installing](#installing) +- [High Availability](#high-availability) +- [Configuration](#configuration) + - [Default Configuration](#default-configuration) + - [Database defaults](#database-defaults) + - [Server defaults](#server-defaults) + - [Metrics defaults](#metrics-defaults) + - [Rootless Defaults](#rootless-defaults) + - [Single-Pod Configurations](#single-pod-configurations) + - [Additional _app.ini_ settings](#additional-appini-settings) + - [User defined environment variables in app.ini](#user-defined-environment-variables-in-appini) + - [External Database](#external-database) + - [Ports and external url](#ports-and-external-url) + - [ClusterIP](#clusterip) + - [SSH and Ingress](#ssh-and-ingress) + - [SSH on crio based kubernetes cluster](#ssh-on-crio-based-kubernetes-cluster) + - [Cache](#cache) + - [Persistence](#persistence) + - [Admin User](#admin-user) + - [LDAP Settings](#ldap-settings) + - [OAuth2 Settings](#oauth2-settings) +- [Configure commit signing](#configure-commit-signing) +- [Metrics and profiling](#metrics-and-profiling) +- [Pod annotations](#pod-annotations) +- [Themes](#themes) +- [Renovate](#renovate) +- [Parameters](#parameters) + - [Global](#global) + - [strategy](#strategy) + - [Image](#image) + - [Security](#security) + - [Service](#service) + - [Ingress](#ingress) + - [deployment](#deployment) + - [ServiceAccount](#serviceaccount) + - [Persistence](#persistence-1) + - [Init](#init) + - [Signing](#signing) + - [Gitea](#gitea) + - [LivenessProbe](#livenessprobe) + - [ReadinessProbe](#readinessprobe) + - [StartupProbe](#startupprobe) + - [redis-cluster](#redis-cluster) + - [PostgreSQL-ha](#postgresql-ha) + - [PostgreSQL](#postgresql) + - [Advanced](#advanced) +- [Contributing](#contributing) +- [Upgrading](#upgrading) + +[Forgejo](https://forgejo.org/) is a community managed lightweight code hosting solution written in Go. +It is published under the MIT license. ## Introduction This helm chart is based on official [Gitea helm chart](https://gitea.com/gitea/helm-chart). -Additionally, this chart provides LDAP and admin user configuration with values, as well as being deployed as a statefulset to retain stored repositories. +Yet it takes a completely different approach in providing a database and cache with dependencies. +Additionally, this chart allows to provide LDAP and admin user configuration with values. ## Update and versioning policy @@ -30,8 +84,8 @@ This chart provides those dependencies, which can be enabled, or disabled via co Dependencies: -- PostgreSQL ([configuration](#postgresql)) -- Memcached ([configuration](#memcached)) +- PostgreSQL HA ([configuration](#postgresql)) +- Redis Cluster ([configuration](#cache)) ## Installing @@ -47,18 +101,18 @@ helm install forgejo -f values.yaml oci://codeberg.org/forgejo-contrib/forgejo When upgrading, please refer to the [Upgrading](#upgrading) section at the bottom of this document for major and breaking changes. -## Prerequisites +## High Availability -- Kubernetes 1.12+ -- Helm 3.0+ -- PV provisioner for persistent data support +This chart supports running Forgejo and it's dependencies in HA mode. +Care must be taken for production use as not all implementation details of Forgejo core are officially HA-ready yet. -## Examples +Deploying a HA-ready Forgejo instance requires some effort including using HA-ready dependencies. +See the [HA Setup](docs/ha-setup.md) document for more details. -### Forgejo Configuration +## Configuration Forgejo offers lots of configuration options. -This is fully described in the [Gitea Cheat Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/). +This is fully described in the [Gitea Cheat Sheet](https://docs.gitea.com/administration/config-cheat-sheet). ```yaml gitea: @@ -77,12 +131,12 @@ All defaults can be overwritten in `gitea.config`. INSTALL_LOCK is always set to true, since we want to configure Forgejo with this helm chart and everything is taken care of. -_All default settings are made directly in the generated app.ini, not in the Values._ +_All default settings are made directly in the generated `app.ini`, not in the Values._ #### Database defaults If a builtIn database is enabled the database configuration is set automatically. -For example, PostgreSQL builtIn will appear in the app.ini as: +For example, PostgreSQL builtIn will appear in the `app.ini` as: ```ini [database] @@ -93,18 +147,6 @@ PASSWD = gitea USER = gitea ``` -#### Memcached defaults - -Memcached is handled the exact same way as database builtIn. -Once Memcached builtIn is enabled, this chart will generate the following part in the `app.ini`: - -```ini -[cache] -ADAPTER = memcache -ENABLED = true -HOST = RELEASE-NAME-memcached.default.svc.cluster.local:11211 -``` - #### Server defaults The server defaults are a bit more complex. @@ -133,9 +175,101 @@ The Prometheus `/metrics` endpoint is disabled by default. ENABLED = false ``` +#### Rootless Defaults + +If `.Values.image.rootless: true`, then the following will occur. In case you use `.Values.image.fullOverride`, check that this works in your image: + +- `$HOME` becomes `/data/gitea/git` + + [see deployment.yaml](./templates/gitea/deployment.yaml) template inside (init-)container "env" declarations + +- `START_SSH_SERVER: true` (Unless explicity overwritten by `gitea.config.server.START_SSH_SERVER`) + + [see \_helpers.tpl](./templates/_helpers.tpl) in `gitea.inline_configuration.defaults.server` definition + +- `SSH_LISTEN_PORT: 2222` (Unless explicity overwritten by `gitea.config.server.SSH_LISTEN_PORT`) + + [see \_helpers.tpl](./templates/_helpers.tpl) in `gitea.inline_configuration.defaults.server` definition + +- `SSH_LOG_LEVEL` environment variable is not injected into the container + + [see deployment.yaml](./templates/gitea/deployment.yaml) template inside container "env" declarations + +### Single-Pod Configurations + +If HA is not needed/desired, the following configurations can be used to deploy a single-pod Forgejo instance. + +1. For a production-ready single-pod Forgejo instance without external dependencies (using the chart dependency `postgresql`): + +
+ + values.yml + + ```yaml + redis-cluster: + enabled: false + postgresql: + enabled: true + postgresql-ha: + enabled: false + + persistence: + enabled: true + + gitea: + config: + database: + DB_TYPE: postgres + session: + PROVIDER: db + cache: + ADAPTER: memory + queue: + TYPE: level + indexer: + ISSUE_INDEXER_TYPE: bleve + REPO_INDEXER_ENABLED: true + ``` + +
+ +2. For a minimal DEV installation (using the built-in sqlite DB instead of Postgres): + + This will result in a single-pod Forgejo instance _without any dependencies and persistence_. + **Do not use this configuration for production use**. + +
+ + values.yml + + ```yaml + redis-cluster: + enabled: false + postgresql: + enabled: false + postgresql-ha: + enabled: false + + persistence: + enabled: false + + gitea: + config: + database: + DB_TYPE: sqlite3 + session: + PROVIDER: memory + cache: + ADAPTER: memory + queue: + TYPE: level + ``` + +
+ ### Additional _app.ini_ settings -> **The [generic](https://docs.gitea.io/en-us/config-cheat-sheet/#overall-default) +> **The [generic](https://docs.gitea.com/administration/config-cheat-sheet#overall-default) > section cannot be defined that way.** Some settings inside _app.ini_ (like passwords or whole authentication configurations) must be considered sensitive and therefore should not be passed via plain text inside the _values.yaml_ file. @@ -153,8 +287,7 @@ gitea: name: gitea-app-ini-plaintext ``` -This would mount the two additional volumes (`oauth` and `some-additionals`) -from different sources to the init containerwhere the _app.ini_ gets updated. +This would mount the two additional volumes (`oauth` and `some-additionals`) from different sources to the init container where the _app.ini_ gets updated. All files mounted that way will be read and converted to environment variables and then added to the _app.ini_ using [environment-to-ini](https://github.com/go-gitea/gitea/tree/main/contrib/environment-to-ini). The key of such additional source represents the section inside the _app.ini_. @@ -195,8 +328,10 @@ stringData: Users are able to define their own environment variables, which are loaded into the containers. We also support to directly interact with the generated _app.ini_. -To inject self defined variables into the _app.ini_, environment variables need -to be prefixed with `FORGEJO`. +To inject self defined variables into the _app.ini_ a certain format needs to be honored. +This is described in detail on the [env-to-ini](https://github.com/go-gitea/gitea/tree/main/contrib/environment-to-ini) page. + +Environment variables need to be prefixed with `FORGEJO`. For example a database setting needs to have the following format: @@ -215,12 +350,13 @@ gitea: Priority (highest to lowest) for defining app.ini variables: 1. Environment variables prefixed with `FORGEJO` + 1. Additional config sources 1. Values defined in `gitea.config` ### External Database -Any external Database listed in [https://docs.gitea.io/en-us/database-prep/](https://docs.gitea.io/en-us/database-prep/) can be used instead of the built-in PostgreSQL. +Any external database listed in [https://docs.gitea.com/installation/database-prep](https://docs.gitea.com/installation/database-prep) can be used instead of the built-in PostgreSQL. In fact, it is **highly recommended** to use an external database to ensure a stable Forgejo installation longterm. If an external database is used, no matter which type, make sure to set `postgresql.enabled` to `false` to disable the use of the built-in PostgreSQL. @@ -306,34 +442,23 @@ More about this issue [here](https://gitea.com/gitea/helm-chart/issues/161). ### Cache -This helm chart can use a built in cache. -The default is Memcached from bitnami. +The cache handling is done via `redis-cluster` (via the `bitnami` chart) by default. +This deployment is HA-ready but can also be used for single-pod deployments. +By default, 6 replicas are deployed for a working `redis-cluster` deployment. +Many cloud providers offer a managed redis service, which can be used instead of the built-in `redis-cluster`. ```yaml -memcached: +redis-cluster: enabled: true ``` -If the built in cache should not be used simply configure the cache in `gitea.config`. - -```yaml -gitea: - config: - cache: - ENABLED: true - ADAPTER: memory - INTERVAL: 60 - HOST: 127.0.0.1:9090 -``` - ### Persistence -Forgejo will be deployed as a statefulset. +Forgejo will be deployed as a deployment. By simply enabling the persistence and setting the storage class according to your cluster everything else will be taken care of. -The following example will create a PVC as a part of the statefulset. -This PVC will not be deleted even if you uninstall the chart. +The following example will create a PVC as a part of the deployment. -Please note, that an empty storageClass in the persistence will result in kubernetes using your default storage class. +Please note, that an empty `storageClass` in the persistence will result in kubernetes using your default storage class. If you want to use your own storage class define it as follows: @@ -343,14 +468,12 @@ persistence: storageClass: myOwnStorageClass ``` -When using PostgreSQL as dependency, this will also be deployed as a statefulset by default. - If you want to manage your own PVC you can simply pass the PVC name to the chart. ```yaml persistence: enabled: true - existingClaim: MyAwesomeGiteaClaim + claimName: MyAwesomeGiteaClaim ``` In case that persistence has been disabled it will simply use an empty dir volume. @@ -362,13 +485,13 @@ You can interact with the postgres settings as displayed in the following exampl postgresql: persistence: enabled: true - existingClaim: MyAwesomeGiteaPostgresClaim + claimName: MyAwesomeGiteaPostgresClaim ``` ### Admin User This chart enables you to create a default admin user. -It is also possible to update the password for this user by upgrading or redeloying the chart. +It is also possible to update the password for this user by upgrading or redeploying the chart. It is not possible to delete an admin user after it has been created. This has to be done in the ui. You cannot use `admin` as username. @@ -403,7 +526,7 @@ gitea: ### LDAP Settings Like the admin user the LDAP settings can be updated. -All LDAP values from are available. +All LDAP values from are available. Multiple LDAP sources can be configured with additional LDAP list items. @@ -457,7 +580,7 @@ Affected options: Like the admin user, OAuth2 settings can be updated and disabled but not deleted. Deleting OAuth2 settings has to be done in the ui. -All OAuth2 values, which are documented [here](https://docs.gitea.io/en-us/command-line/#admin), are +All OAuth2 values, which are documented [here](https://docs.gitea.com/administration/command-line#admin), are available. Multiple OAuth2 sources can be configured with additional OAuth list items. @@ -534,9 +657,9 @@ signing: ``` To use the gpg key, Forgejo needs to be configured accordingly. -A detailed description can be found in the [official Gitea documentation](https://docs.gitea.io/en-us/signing/#general-configuration). +A detailed description can be found in the [official Gitea documentation](https://docs.gitea.com/administration/signing#general-configuration). -### Metrics and profiling +## Metrics and profiling A Prometheus `/metrics` endpoint on the `HTTP_PORT` and `pprof` profiling endpoints on port 6060 can be enabled under `gitea`. Beware that the metrics endpoint is exposed via the ingress, manage access using ingress annotations for example. @@ -555,7 +678,7 @@ gitea: ENABLE_PPROF: true ``` -### Pod Annotations +## Pod annotations Annotations can be added to the Forgejo pod. @@ -564,29 +687,137 @@ gitea: podAnnotations: {} ``` +## Themes + +Custom themes can be added via k8s secrets and referencing them in `values.yaml`. + +The [http provider](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) is useful here. + +```yaml +extraVolumes: + - name: gitea-themes + secret: + secretName: gitea-themes + +extraVolumeMounts: + - name: gitea-themes + readOnly: true + mountPath: "/data/gitea/public/assets/css" +``` + +The secret can be created via `terraform`: + +```hcl +resource "kubernetes_secret" "gitea-themes" { + metadata { + name = "gitea-themes" + namespace = "gitea" + } + + data = { + "my-theme.css" = data.http.gitea-theme-light.body + "my-theme-dark.css" = data.http.gitea-theme-dark.body + "my-theme-auto.css" = data.http.gitea-theme-auto.body + } + + type = "Opaque" +} + + +data "http" "gitea-theme-light" { + url = "" + + request_headers = { + Accept = "application/json" + } +} + +data "http" "gitea-theme-dark" { + url = "" + + request_headers = { + Accept = "application/json" + } +} + +data "http" "gitea-theme-auto" { + url = "" + + request_headers = { + Accept = "application/json" + } +} +``` + +or natively via `kubectl`: + +```bash +kubectl create secret generic gitea-themes --from-file={{FULL-PATH-TO-CSS}} --namespace gitea +``` + +## Renovate + +To be able to use a digest value which is automatically updated by `Renovate` a [customManager](https://docs.renovatebot.com/modules/manager/regex/) is required. +Here's an examplary `values.yml` definition which makes use of a digest: + +```yaml +image: + registry: codeberg.org + repository: forgejo/forgejo + tag: 1.20.2-0 + digest: sha256:f597c14a403c2fdee9a62dae8bae29d6442f7b2cc85872cc9bb535a24cb1630e +``` + +By default Renovate adds digest after the `tag`. +To comply with the Forgejo helm chart definition of the digest parameter, a "customManagers" definition is required: + +```json +"customManagers": [ + { + "customType": "regex", + "description": "Apply an explicit gitea digest field match", + "fileMatch": ["values\\.ya?ml"], + "matchStrings": ["(?forgejo\\/forgejo)\\n(?\\s+)tag: (?[^@].*?)\\n\\s+digest: (?sha256:[a-f0-9]+)"], + "datasourceTemplate": "docker", + "packageNameTemplate": "codeberg.org/{{depName}}", + "autoReplaceStringTemplate": "{{depName}}\n{{indentation}}tag: {{newValue}}\n{{indentation}}digest: {{#if newDigest}}{{{newDigest}}}{{else}}{{{currentDigest}}}{{/if}}" + } +] +``` + ## Parameters ### Global -| Name | Description | Value | -| ------------------------- | ------------------------------------------------------------------------- | --------------- | -| `global.imageRegistry` | global image registry override | `""` | -| `global.imagePullSecrets` | global image pull secrets override; can be extended by `imagePullSecrets` | `[]` | -| `global.storageClass` | global storage class override | `""` | -| `global.hostAliases` | global hostAliases which will be added to the pod's hosts files | `[]` | -| `replicaCount` | number of replicas for the statefulset | `1` | -| `clusterDomain` | cluster domain | `cluster.local` | +| Name | Description | Value | +| ------------------------- | ------------------------------------------------------------------------- | ----- | +| `global.imageRegistry` | global image registry override | `""` | +| `global.imagePullSecrets` | global image pull secrets override; can be extended by `imagePullSecrets` | `[]` | +| `global.storageClass` | global storage class override | `""` | +| `global.hostAliases` | global hostAliases which will be added to the pod's hosts files | `[]` | +| `replicaCount` | number of replicas for the deployment | `1` | + +### strategy + +| Name | Description | Value | +| --------------------------------------- | -------------- | --------------- | +| `strategy.type` | strategy type | `RollingUpdate` | +| `strategy.rollingUpdate.maxSurge` | maxSurge | `100%` | +| `strategy.rollingUpdate.maxUnavailable` | maxUnavailable | `0` | +| `clusterDomain` | cluster domain | `cluster.local` | ### Image -| Name | Description | Value | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------- | -| `image.registry` | image registry, e.g. gcr.io,docker.io | `codeberg.org` | -| `image.repository` | Image to start for this pod | `forgejo/forgejo` | -| `image.tag` | Visit: [Image tag](https://codeberg.org/forgejo/-/packages/container/forgejo/versions). Defaults to `appVersion` within Chart.yaml. | `""` | -| `image.pullPolicy` | Image pull policy | `Always` | -| `image.rootless` | Wether or not to pull the rootless version of Forgejo, only works on Forgejo 1.14.x or higher | `false` | -| `imagePullSecrets` | Secret to use for pulling the image | `[]` | +| Name | Description | Value | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | +| `image.registry` | image registry, e.g. gcr.io,docker.io | `codeberg.org` | +| `image.repository` | Image to start for this pod | `forgejo/forgejo` | +| `image.tag` | Visit: [Image tag](https://codeberg.org/forgejo/-/packages/container/forgejo/versions). Defaults to `appVersion` within Chart.yaml. | `""` | +| `image.digest` | Image digest. Allows to pin the given image tag. Useful for having control over mutable tags like `latest` | `""` | +| `image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `image.rootless` | Wether or not to pull the rootless version of Forgejo | `true` | +| `image.fullOverride` | Completely overrides the image registry, path/image, tag and digest. **Adjust `image.rootless` accordingly and review [Rootless defaults](#rootless-defaults).** | `""` | +| `imagePullSecrets` | Secret to use for pulling the image | `[]` | ### Security @@ -594,7 +825,8 @@ gitea: | ---------------------------- | --------------------------------------------------------------- | ------ | | `podSecurityContext.fsGroup` | Set the shared file system group for all containers in the pod. | `1000` | | `containerSecurityContext` | Security context | `{}` | -| `securityContext` | Run init and Forgejo containers as a specific securityContext | `{}` | +| `securityContext` | Run init and Forgejo containers as a specific securityContext | `{}` | +| `podDisruptionBudget` | Pod disruption budget | `{}` | ### Service @@ -602,7 +834,7 @@ gitea: | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | `service.http.type` | Kubernetes service type for web traffic | `ClusterIP` | | `service.http.port` | Port number for web traffic | `3000` | -| `service.http.clusterIP` | ClusterIP setting for http autosetup for statefulset is None | `None` | +| `service.http.clusterIP` | ClusterIP setting for http autosetup for deployment is None | `None` | | `service.http.loadBalancerIP` | LoadBalancer IP setting | `nil` | | `service.http.nodePort` | NodePort for http service | `nil` | | `service.http.externalTrafficPolicy` | If `service.http.type` is `NodePort` or `LoadBalancer`, set this to `Local` to enable source IP preservation | `nil` | @@ -613,7 +845,7 @@ gitea: | `service.http.annotations` | HTTP service annotations | `{}` | | `service.ssh.type` | Kubernetes service type for ssh traffic | `ClusterIP` | | `service.ssh.port` | Port number for ssh traffic | `22` | -| `service.ssh.clusterIP` | ClusterIP setting for ssh autosetup for statefulset is None | `None` | +| `service.ssh.clusterIP` | ClusterIP setting for ssh autosetup for deployment is None | `None` | | `service.ssh.loadBalancerIP` | LoadBalancer IP setting | `nil` | | `service.ssh.nodePort` | NodePort for ssh service | `nil` | | `service.ssh.externalTrafficPolicy` | If `service.ssh.type` is `NodePort` or `LoadBalancer`, set this to `Local` to enable source IP preservation | `nil` | @@ -637,38 +869,53 @@ gitea: | `ingress.tls` | Ingress tls settings | `[]` | | `ingress.apiVersion` | Specify APIVersion of ingress object. Mostly would only be used for argocd. | | -### StatefulSet +### deployment -| Name | Description | Value | -| ------------------------------------------- | ------------------------------------------------------ | ----- | -| `resources` | Kubernetes resources | `{}` | -| `schedulerName` | Use an alternate scheduler, e.g. "stork" | `""` | -| `nodeSelector` | NodeSelector for the statefulset | `{}` | -| `tolerations` | Tolerations for the statefulset | `[]` | -| `affinity` | Affinity for the statefulset | `{}` | -| `dnsConfig` | dnsConfig for the statefulset | `{}` | -| `priorityClassName` | priorityClassName for the statefulset | `""` | -| `statefulset.env` | Additional environment variables to pass to containers | `[]` | -| `statefulset.terminationGracePeriodSeconds` | How long to wait until forcefully kill the pod | `60` | -| `statefulset.labels` | Labels for the statefulset | `{}` | -| `statefulset.annotations` | Annotations for the Forgejo StatefulSet to be created | `{}` | +| Name | Description | Value | +| ------------------------------------------ | ------------------------------------------------------ | ----- | +| `resources` | Kubernetes resources | `{}` | +| `schedulerName` | Use an alternate scheduler, e.g. "stork" | `""` | +| `nodeSelector` | NodeSelector for the deployment | `{}` | +| `tolerations` | Tolerations for the deployment | `[]` | +| `affinity` | Affinity for the deployment | `{}` | +| `topologySpreadConstraints` | TopologySpreadConstraints for the deployment | `[]` | +| `dnsConfig` | dnsConfig for the deployment | `{}` | +| `priorityClassName` | priorityClassName for the deployment | `""` | +| `deployment.env` | Additional environment variables to pass to containers | `[]` | +| `deployment.terminationGracePeriodSeconds` | How long to wait until forcefully kill the pod | `60` | +| `deployment.labels` | Labels for the deployment | `{}` | +| `deployment.annotations` | Annotations for the Gitea deployment to be created | `{}` | + +### ServiceAccount + +| Name | Description | Value | +| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `serviceAccount.create` | Enable the creation of a ServiceAccount | `false` | +| `serviceAccount.name` | Name of the created ServiceAccount, defaults to release name. Can also link to an externally provided ServiceAccount that should be used. | `""` | +| `serviceAccount.automountServiceAccountToken` | Enable/disable auto mounting of the service account token | `false` | +| `serviceAccount.imagePullSecrets` | Image pull secrets, available to the ServiceAccount | `[]` | +| `serviceAccount.annotations` | Custom annotations for the ServiceAccount | `{}` | +| `serviceAccount.labels` | Custom labels for the ServiceAccount | `{}` | ### Persistence -| Name | Description | Value | -| ---------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------- | -| `persistence.enabled` | Enable persistent storage | `true` | -| `persistence.existingClaim` | Use an existing claim to store repository information | `nil` | -| `persistence.size` | Size for persistence to store repo information | `10Gi` | -| `persistence.accessModes` | AccessMode for persistence | `["ReadWriteOnce"]` | -| `persistence.labels` | Labels for the persistence volume claim to be created | `{}` | -| `persistence.annotations` | Annotations for the persistence volume claim to be created | `{}` | -| `persistence.storageClass` | Name of the storage class to use | `nil` | -| `persistence.subPath` | Subdirectory of the volume to mount at | `nil` | -| `extraVolumes` | Additional volumes to mount to the Forgejo statefulset | `[]` | -| `extraContainerVolumeMounts` | Mounts that are only mapped into the Forgejo runtime/main container, to e.g. override custom templates. | `[]` | -| `extraInitVolumeMounts` | Mounts that are only mapped into the init-containers. Can be used for additional preconfiguration. | `[]` | -| `extraVolumeMounts` | **DEPRECATED** Additional volume mounts for init containers and the Forgejo main container | `[]` | +| Name | Description | Value | +| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ---------------------- | +| `persistence.enabled` | Enable persistent storage | `true` | +| `persistence.create` | Whether to create the persistentVolumeClaim for shared storage | `true` | +| `persistence.mount` | Whether the persistentVolumeClaim should be mounted (even if not created) | `true` | +| `persistence.claimName` | Use an existing claim to store repository information | `gitea-shared-storage` | +| `persistence.size` | Size for persistence to store repo information | `10Gi` | +| `persistence.accessModes` | AccessMode for persistence | `["ReadWriteOnce"]` | +| `persistence.labels` | Labels for the persistence volume claim to be created | `{}` | +| `persistence.annotations.helm.sh/resource-policy` | Resource policy for the persistence volume claim | `keep` | +| `persistence.storageClass` | Name of the storage class to use | `nil` | +| `persistence.subPath` | Subdirectory of the volume to mount at | `nil` | +| `persistence.volumeName` | Name of persistent volume in PVC | `""` | +| `extraVolumes` | Additional volumes to mount to the Forgejo deployment | `[]` | +| `extraContainerVolumeMounts` | Mounts that are only mapped into the Forgejo runtime/main container, to e.g. override custom templates. | `[]` | +| `extraInitVolumeMounts` | Mounts that are only mapped into the init-containers. Can be used for additional preconfiguration. | `[]` | +| `extraVolumeMounts` | **DEPRECATED** Additional volume mounts for init containers and the Forgejo main container | `[]` | ### Init @@ -690,21 +937,22 @@ gitea: ### Gitea -| Name | Description | Value | -| -------------------------------------- | --------------------------------------------------------------------------------------------------------------- | -------------------- | -| `gitea.admin.username` | Username for the Forgejo admin user | `gitea_admin` | -| `gitea.admin.existingSecret` | Use an existing secret to store admin user credentials | `nil` | -| `gitea.admin.password` | Password for the Forgejo admin user | `r8sA8CPHD9!bt6d` | -| `gitea.admin.email` | Email for the Forgejo admin user | `gitea@local.domain` | -| `gitea.metrics.enabled` | Enable Forgejo metrics | `false` | -| `gitea.metrics.serviceMonitor.enabled` | Enable Forgejo metrics service monitor | `false` | -| `gitea.ldap` | LDAP configuration | `[]` | -| `gitea.oauth` | OAuth configuration | `[]` | -| `gitea.config` | Configuration for the Forgejo server,ref: [config-cheat-sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) | `{}` | -| `gitea.additionalConfigSources` | Additional configuration from secret or configmap | `[]` | -| `gitea.additionalConfigFromEnvs` | Additional configuration sources from environment variables | `[]` | -| `gitea.podAnnotations` | Annotations for the Forgejo pod | `{}` | -| `gitea.ssh.logLevel` | Configure OpenSSH's log level. Only available for root-based Forgejo image. | `INFO` | +| Name | Description | Value | +| -------------------------------------- | ------------------------------------------------------------------------- | -------------------- | +| `gitea.admin.username` | Username for the Forgejo admin user | `gitea_admin` | +| `gitea.admin.existingSecret` | Use an existing secret to store admin user credentials | `nil` | +| `gitea.admin.password` | Password for the Forgejo admin user | `r8sA8CPHD9!bt6d` | +| `gitea.admin.email` | Email for the Forgejo admin user | `gitea@local.domain` | +| `gitea.metrics.enabled` | Enable Forgejo metrics | `false` | +| `gitea.metrics.serviceMonitor.enabled` | Enable Forgejo metrics service monitor | `false` | +| `gitea.ldap` | LDAP configuration | `[]` | +| `gitea.oauth` | OAuth configuration | `[]` | +| `gitea.config.server.SSH_PORT` | SSH port for rootlful Forgejo image | `22` | +| `gitea.config.server.SSH_LISTEN_PORT` | SSH port for rootless Forgejo image | `2222` | +| `gitea.additionalConfigSources` | Additional configuration from secret or configmap | `[]` | +| `gitea.additionalConfigFromEnvs` | Additional configuration sources from environment variables | `[]` | +| `gitea.podAnnotations` | Annotations for the Forgejo pod | `{}` | +| `gitea.ssh.logLevel` | Configure OpenSSH's log level. Only available for root-based Forgejo image. | `INFO` | ### LivenessProbe @@ -742,14 +990,29 @@ gitea: | `gitea.startupProbe.successThreshold` | Success threshold for startup probe | `1` | | `gitea.startupProbe.failureThreshold` | Failure threshold for startup probe | `10` | -### Memcached +### redis-cluster -Memcached is loaded as a dependency from [Bitnami](https://github.com/bitnami/charts/tree/master/bitnami/memcached) if enabled in the values. Complete Configuration can be taken from their website. +| Name | Description | Value | +| -------------------------------- | -------------------------------------------- | ------- | +| `redis-cluster.enabled` | Enable redis | `true` | +| `redis-cluster.usePassword` | Whether to use password authentication | `false` | +| `redis-cluster.cluster.nodes` | Number of redis cluster master nodes | `3` | +| `redis-cluster.cluster.replicas` | Number of redis cluster master node replicas | `0` | -| Name | Description | Value | -| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| `memcached.enabled` | Memcached is loaded as a dependency from [Bitnami](https://github.com/bitnami/charts/tree/master/bitnami/memcached) if enabled in the values. Complete Configuration can be taken from their website. | `true` | -| `memcached.service.ports.memcached` | Port for Memcached | `11211` | +### PostgreSQL-ha + +| Name | Description | Value | +| ------------------------------------------- | ---------------------------------------------------------------- | ----------- | +| `postgresql-ha.enabled` | Enable PostgreSQL-ha | `true` | +| `postgresql-ha.postgresql.password` | Password for the `gitea` user (overrides `auth.password`) | `changeme4` | +| `postgresql-ha.global.postgresql.database` | Name for a custom database to create (overrides `auth.database`) | `gitea` | +| `postgresql-ha.global.postgresql.username` | Name for a custom user to create (overrides `auth.username`) | `gitea` | +| `postgresql-ha.global.postgresql.password` | Name for a custom password to create (overrides `auth.password`) | `gitea` | +| `postgresql-ha.postgresql.repmgrPassword` | Repmgr Password | `changeme2` | +| `postgresql-ha.postgresql.postgresPassword` | postgres Password | `changeme1` | +| `postgresql-ha.pgpool.adminPassword` | pgpool adminPassword | `changeme3` | +| `postgresql-ha.service.ports.postgresql` | PostgreSQL service port (overrides `service.ports.postgresql`) | `5432` | +| `postgresql-ha.primary.persistence.size` | PVC Storage Request for PostgreSQL-ha volume | `10Gi` | ### PostgreSQL @@ -757,7 +1020,7 @@ PostgreSQL is loaded as a dependency from [Bitnami](https://github.com/bitnami/c | Name | Description | Value | | ------------------------------------------------------- | ---------------------------------------------------------------- | ------- | -| `postgresql.enabled` | Enable PostgreSQL | `true` | +| `postgresql.enabled` | Enable PostgreSQL | `false` | | `postgresql.global.postgresql.auth.password` | Password for the `gitea` user (overrides `auth.password`) | `gitea` | | `postgresql.global.postgresql.auth.database` | Name for a custom database to create (overrides `auth.database`) | `gitea` | | `postgresql.global.postgresql.auth.username` | Name for a custom user to create (overrides `auth.username`) | `gitea` | @@ -782,5 +1045,6 @@ See [CONTRIBUTORS GUIDE](CONTRIBUTING.md) for details. ## Upgrading -This section lists major and breaking changes of each Helm Chart version -Please read them carefully to upgrade successfully. +This section lists major and breaking changes of each Helm Chart version. +Please read them carefully to upgrade successfully, especially the change of the **default database backend**! +If you miss this, blindly upgrading may delete your Postgres instance and you may lose your data! diff --git a/docs/ha-setup.md b/docs/ha-setup.md new file mode 100644 index 0000000..d2a32de --- /dev/null +++ b/docs/ha-setup.md @@ -0,0 +1,178 @@ +# High Availability + +All components (in-memory DB, volume/asset storage, code indexer) used by Forgejo must be deployed in a HA-ready fashion to achieve a full HA-ready Forgejo deployment. +The following document explains how to achieve this for all individual components. + +The resulting Forgejo deployment will consist of ~ 10 pods (depending on the chosen components and their replicas). +One should evaluate upfront whether a HA-deployment is required as switching between HA/non-HA comes with some effort. +For production instances, HA is always recommended to increase uptime and have a frictionless update process. + +A general comment about chart dependencies and external services: +Instead of relying on chart dependencies, it is often better to rely on an external, (managed) instances (in-memory database, asset storage provider, database, etc.). +Many cloud providers offer such services, at least for databases or in-memory databases. +They might cost a bit more than using a self-hosted k8s variant but are usually easier to maintain and scale, if needed. +Also they can be centrally managed and are not linked to the Forgejo helm chart or namespace. +Please consider using external services before you start with your Forgejo HA setup, it will make your life (and the life of the Forgejo maintainers) easier. + +This helm chart tries to help as much as possible to simplify and assert the provisioning of a HA-ready Forgejo instance by implementing smart conditionals if `replicaCount` is set to a value > 1. +Nevertheless, we cannot guarantee for every possible combination of Forgejo settings to work together perfectly in a HA setup. +As a general advice, we recommend to have a test environment aside on which to test possible changes/upgrades before applying these to a production installation. + +## Requirements for HA + +Storage-wise, the HA-Forgejo setup requires a RWX file-system which can be shared among the deployment-based replica pods. +In addition, the following components are required for full HA-readiness: + +- A HA-ready issue (and optionally code) indexer: `elasticsearch` or `meilisearch` +- A HA-ready external object/asset storage (`minio`) (optional, assets can also be stored on the RWX file-system) +- A HA-ready cache (`redis-cluster`) +- A HA-ready DB + +`postgres.enabled`, which default to `true`, must be set to `false` for a HA setup. +The default `postgres` chart dependency is not HA-ready (there's a dedicated `postgres-ha` chart). + +The following sections discuss each of the components in more detail. +Note that for each component discussed, the shown configurations only provides a (working) starting point, not necessarily the most optimal setup. +We try to optimize this document over time as we have gained more experience with HA setups from users. + +## Indexers (Issues and code/repo) + +The default code indexer `bleve` is not able to allow multiple connections and hence cannot be used in a HA setup. +Alternatives are `elasticsearch` and `meilisearch` (as of >= 1.19.2). +Unless you have an existing `elasticsearch` cluster, we recommend using `meilisearch` as it is faster and requires way less resources. + +Unfortunately, `meilisearch` does only support the `ISSUE_INDEXER` and not the `REPO_INDEXER` yet ([tracking issue](https://github.com/go-gitea/gitea/pull/24149)). +This means that the `REPO_INDEXER` must still be disabled for a HA setup right now. +An alternative to the two options above for the `ISSUE_INDEXER` is `"db"`, however we recommend to just go with `meilisearch` in this case and to not bother the DB with indexing. + +To configure `meilisearch` within Forgejo, do the following: + +```yml +gitea: + config: + indexer: + ISSUE_INDEXER_CONN_STR: .svc.cluster.local:7700> + ISSUE_INDEXER_ENABLED: true + ISSUE_INDEXER_TYPE: meilisearch + REPO_INDEXER_ENABLED: false + # REPO_INDEXER_TYPE: meilisearch # not yet working +``` + +Unfortunately `meilisearch` cannot be deployed in HA as of now. +Nevertheless it allows for multiple Forgejo requests at the same time and is therefore required in a HA setup. + +Exemplary configuration for the [meilisearch-kubernetes](https://github.com/meilisearch/meilisearch-kubernetes/tree/main/charts/meilisearch) chart: + +```yaml +persistence: + enabled: true + accessMode: ReadWriteOnce + size: 5Gi +``` + +## Cache, session and queue + +A `redis` instance is required for the in-memory cache. +Two options exist: + +- `redis` +- `redis-cluster` + +The chart provides `redis-cluster` as a dependency as this one can be used for both HA and non-HA setups. +You're also welcome to go with `redis` if you prefer or already have a running instance. + +It should be noted that `redis-cluster` support is only available starting with Forgejo 1.19.2. +You can also configure an external (managed) `redis` instance to be used. +To do so, you need to set the following configuration values yourself: + +- `gitea.config.queue.TYPE`: redis` +- `gitea.config.queue.CONN_STR`: `` + +- `gitea.config.session.PROVIDER`: `redis` +- `gitea.config.session.PROVIDER_CONFIG`: `` + +- `gitea.config.cache.ENABLED`: `true` +- `gitea.config.cache.ADAPTER`: `redis` +- `gitea.config.cache.HOST`: `` + +By default, the `redis-cluster` chart provisions three standalone master nodes of which each has a single replica. +To reduce the number of pods for a default Forgejo deployment, we opted to omit the replicas (`replicas: 0`) by default. +Only the minimum required number of master pods for a functional `redis-cluster` deployment are provisioned. +For a "proper" `redis-cluster` setup however, we recommend to set `replicas: 1` and `nodes: 6`. + +## Object and asset storage + +Object/asset storage refers to the storage of attachments, avatars, LFS files, etc. +While most of these can be stored on the RWX file-system, it is recommended to use an external S3-compatible object storage for such, mainly for performance reasons. + +By default the chart provisions a single RWO volume to store everything (repos, avatars, packages, etc.). +This volume cannot be mounted by multiple pods. +Hence, a RWX volume is required and (optionally) an external HA-ready object storage. + +> **Note:** Double-check that the file permissions are set correctly on the RWX volume! That is everything should be owned by the `git` user which usually has `uid=1000` and `gid=1000`. + +To use `minio` you need to deploy and configure an external `minio` instance yourself and explicitly define the `STORAGE_TYPE` values as shown below. + +Note that `MINIO_BUCKET` here is just a name and does not refer to a S3 bucket. +It's the root access point for all objects belonging to the respective application, i.e., to Forgejo in this case. + +```yaml +gitea: + config: + attachment: + STORAGE_TYPE: minio + lfs: + STORAGE_TYPE: minio + picture: + AVATAR_STORAGE_TYPE: minio + "storage.packages": + STORAGE_TYPE: minio + + storage: + MINIO_ENDPOINT: .svc.cluster.local:9000> + MINIO_LOCATION: + MINIO_ACCESS_KEY_ID: + MINIO_SECRET_ACCESS_KEY: + MINIO_BUCKET: + MINIO_USE_SSL: false +``` + +Exemplary configuration for the [bitnami minio](https://github.com/bitnami/charts/blob/main/bitnami/minio) chart: + +```yaml +auth: + rootUser: minio +mode: distributed +replicaCount: 4 +persistence: + enabled: true + size: 20Gi + accessModes: + - ReadWriteOnce +``` + +## Database + +If you do not have an HA-ready DB, using a managed database service in the cloud might be the easiest and most robust solution. +Remember: disable the built-in `postgres` dependency and configure the database connection manually via `gitea.config.database`: + +```yml +gitea: + database: + builtIn: + postgresql: + enabled: false + config: + database: + DB_TYPE: postgres + HOST: + NAME: + USER: +``` + +## Known issues + +- Currently Cron jobs are run on all replicas as no leader election is implemented. + See [https://github.com/go-gitea/gitea/issues/13791](https://github.com/go-gitea/gitea/issues/13791) for a discussion and possible solution. + +- Running with multiple replicas slows down Forgejo a bit, i.e. page loading time increases. diff --git a/package-lock.json b/package-lock.json index 6ae1f53..618f522 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "forgejo-helm-chart", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -18,12 +18,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -31,21 +31,21 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -54,9 +54,9 @@ } }, "node_modules/@bitnami/readme-generator-for-helm": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@bitnami/readme-generator-for-helm/-/readme-generator-for-helm-2.6.0.tgz", - "integrity": "sha512-LcByNCryaC2OJExL9rnhyFJ18+vrZu1gVoN2Z7j/HI42EjV4kLgT4G1KEPNnrKbls9HvozBqMG+sKZIDh0McFg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@bitnami/readme-generator-for-helm/-/readme-generator-for-helm-2.5.0.tgz", + "integrity": "sha512-bYggL/kWwyxjctSrIBMOcrTQSj8LA3yYcEzfGTJIFoHKl5M7ifZtox//8G5K3FTw6qdOnPZcA10fl2y4N6uB/g==", "dev": true, "dependencies": { "commander": "^7.1.0", @@ -78,6 +78,23 @@ "node": ">=10.13.0" } }, + "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, + "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/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -89,9 +106,9 @@ } }, "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, "node_modules/add-stream": { @@ -101,27 +118,27 @@ "dev": true }, "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==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "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==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, "node_modules/argparse": { @@ -166,19 +183,7 @@ "node": ">=4" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/color-convert": { + "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", @@ -187,30 +192,12 @@ "color-name": "1.1.3" } }, - "node_modules/chalk/node_modules/color-name": { + "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "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, - "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 - }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -270,9 +257,9 @@ } }, "node_modules/conventional-changelog-writer": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.0.tgz", - "integrity": "sha512-9/6vTDd3wDbH9yayZNOq53UWI4QqYlbiLBtWf+alsQA/bBFHG+k3KnQ8Fu/xzHqsbQfzPg3w1H1piWYn/GD9Tw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.1.tgz", + "integrity": "sha512-Uo+R9neH3r/foIvQ0MKcsXkX642hdm9odUp7TqgFS7BsalTcjzRlIfWZrZR1gbxOozKucaKt5KAbjW8J8xRSmA==", "dev": true, "dependencies": { "conventional-commits-filter": "^4.0.0", @@ -385,10 +372,16 @@ "node": ">=8" } }, + "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 + }, "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==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "node_modules/entities": { @@ -460,10 +453,13 @@ "dev": true }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/get-stdin": { "version": "9.0.0", @@ -495,9 +491,9 @@ } }, "node_modules/git-semver-tags": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-7.0.0.tgz", - "integrity": "sha512-U2oXCUEiXATRx1jhpmpvSPKrr7FZp4ifcfNy0Bfiyailu5uqTPU/ETc+bLB+m3+dz5pY5atq0tna7grOqzedyA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-7.0.1.tgz", + "integrity": "sha512-NY0ZHjJzyyNXHTDZmj+GG7PyuAKtMsyWSwh07CR2hOZFa+/yoTsXci/nF2obzL8UDhakFNkD9gNdt/Ed+cxh2Q==", "dev": true, "dependencies": { "meow": "^12.0.1", @@ -531,13 +527,13 @@ } }, "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -551,18 +547,6 @@ "uglify-js": "^3.1.4" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -572,10 +556,22 @@ "node": ">=4" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hosted-git-info": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", - "integrity": "sha512-ICclEpTLhHj+zCuSb2/usoNXSVkxUSIopre+b1w8NDY9Dntp9LO4vLdHYI336TH8sAqwrRgnSfdkBG2/YpisHA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "dependencies": { "lru-cache": "^10.0.1" @@ -584,15 +580,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -634,12 +621,12 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -682,12 +669,12 @@ "dev": true }, "node_modules/jackspeak": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.1.tgz", - "integrity": "sha512-juf9stUEwUaILepraGOWIJTLwg48bUnBmRqd2ln2Os1sW987zeoj/hzhbvRB95oMuS2ZTpjULmdwHNX4rzZIZw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { - "cliui": "^8.0.1" + "@isaacs/cliui": "^8.0.2" }, "engines": { "node": ">=14" @@ -699,20 +686,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jackspeak/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -732,9 +705,9 @@ } }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -778,9 +751,9 @@ } }, "node_modules/lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", + "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", "dev": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -817,15 +790,12 @@ "dev": true }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/markdown-it": { @@ -912,19 +882,19 @@ } }, "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.5.tgz", - "integrity": "sha512-bYUpUD7XDEHI4Q2O5a7PXGvyw4deKR70kHiDxzQbe925wbZknhOzUt2xBgTkYL6RBcVeXYuD9iNYeqoWbBZQnA==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -997,12 +967,12 @@ } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/neo-async": { @@ -1066,9 +1036,9 @@ } }, "node_modules/parse-json": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.0.0.tgz", - "integrity": "sha512-kP+TQYAzAiVnzOlWOe0diD6L35s9bJh0SCn95PIbZFKrOYuIRQsQkeWEYxzVDuHTt9V9YqvYCJ2Qo4z9wdfZPw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", + "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.21.4", @@ -1139,15 +1109,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/read-pkg": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", @@ -1222,6 +1183,18 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -1244,9 +1217,9 @@ } }, "node_modules/signal-exit": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", - "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { "node": ">=14" @@ -1265,9 +1238,9 @@ } }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -1291,9 +1264,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "dev": true }, "node_modules/split2": { @@ -1306,6 +1279,24 @@ } }, "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, + "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==", @@ -1319,7 +1310,22 @@ "node": ">=8" } }, - "node_modules/strip-ansi": { + "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, + "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 + }, + "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==", @@ -1331,6 +1337,43 @@ "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, + "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, + "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, + "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", @@ -1374,9 +1417,9 @@ "dev": true }, "node_modules/type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.8.3.tgz", + "integrity": "sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==", "dev": true, "engines": { "node": ">=16" @@ -1436,6 +1479,24 @@ "dev": true }, "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, + "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==", @@ -1452,6 +1513,92 @@ "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, + "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, + "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/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/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 + }, + "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 + }, + "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, + "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1485,1094 +1632,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "dev": true - }, - "@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - } - }, - "@bitnami/readme-generator-for-helm": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@bitnami/readme-generator-for-helm/-/readme-generator-for-helm-2.6.0.tgz", - "integrity": "sha512-LcByNCryaC2OJExL9rnhyFJ18+vrZu1gVoN2Z7j/HI42EjV4kLgT4G1KEPNnrKbls9HvozBqMG+sKZIDh0McFg==", - "dev": true, - "requires": { - "commander": "^7.1.0", - "dot-object": "^2.1.4", - "lodash": "^4.17.21", - "markdown-table": "^2.0.0", - "yaml": "^2.0.0-3" - } - }, - "@hutson/parse-repository-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-5.0.0.tgz", - "integrity": "sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==", - "dev": true - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", - "dev": true - }, - "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 - }, - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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 - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true - }, - "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 - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - } - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "conventional-changelog-conventionalcommits": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", - "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", - "dev": true, - "requires": { - "compare-func": "^2.0.0" - } - }, - "conventional-changelog-core": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-7.0.0.tgz", - "integrity": "sha512-UYgaB1F/COt7VFjlYKVE/9tTzfU3VUq47r6iWf6lM5T7TlOxr0thI63ojQueRLIpVbrtHK4Ffw+yQGduw2Bhdg==", - "dev": true, - "requires": { - "@hutson/parse-repository-url": "^5.0.0", - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^7.0.0", - "conventional-commits-parser": "^5.0.0", - "git-raw-commits": "^4.0.0", - "git-semver-tags": "^7.0.0", - "hosted-git-info": "^7.0.0", - "normalize-package-data": "^6.0.0", - "read-pkg": "^8.0.0", - "read-pkg-up": "^10.0.0" - } - }, - "conventional-changelog-writer": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-7.0.0.tgz", - "integrity": "sha512-9/6vTDd3wDbH9yayZNOq53UWI4QqYlbiLBtWf+alsQA/bBFHG+k3KnQ8Fu/xzHqsbQfzPg3w1H1piWYn/GD9Tw==", - "dev": true, - "requires": { - "conventional-commits-filter": "^4.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "meow": "^12.0.1", - "semver": "^7.5.2", - "split2": "^4.0.0" - } - }, - "conventional-commits-filter": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-4.0.0.tgz", - "integrity": "sha512-rnpnibcSOdFcdclpFwWa+pPlZJhXE7l+XK04zxhbWrhgpR96h33QLz8hITTXbcYICxVr3HZFtbtUAQ+4LdBo9A==", - "dev": true - }, - "conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "dev": true, - "requires": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "dargs": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", - "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", - "dev": true - }, - "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 - }, - "dot-object": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-2.1.4.tgz", - "integrity": "sha512-7FXnyyCLFawNYJ+NhkqyP9Wd2yzuo+7n9pGiYpkmXCTYa8Ci2U0eUNDVg5OuO5Pm6aFXI2SWN8/N/w7SJWu1WA==", - "dev": true, - "requires": { - "commander": "^4.0.0", - "glob": "^7.1.5" - }, - "dependencies": { - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - } - } - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "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 - }, - "entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "requires": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - } - }, - "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - } - }, - "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 - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", - "dev": true - }, - "git-raw-commits": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", - "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", - "dev": true, - "requires": { - "dargs": "^8.0.0", - "meow": "^12.0.1", - "split2": "^4.0.0" - } - }, - "git-semver-tags": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-7.0.0.tgz", - "integrity": "sha512-U2oXCUEiXATRx1jhpmpvSPKrr7FZp4ifcfNy0Bfiyailu5uqTPU/ETc+bLB+m3+dz5pY5atq0tna7grOqzedyA==", - "dev": true, - "requires": { - "meow": "^12.0.1", - "semver": "^7.5.2" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "hosted-git-info": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.0.tgz", - "integrity": "sha512-ICclEpTLhHj+zCuSb2/usoNXSVkxUSIopre+b1w8NDY9Dntp9LO4vLdHYI336TH8sAqwrRgnSfdkBG2/YpisHA==", - "dev": true, - "requires": { - "lru-cache": "^10.0.1" - }, - "dependencies": { - "lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "dev": true - } - } - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "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 - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "dev": true, - "requires": { - "text-extensions": "^2.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "jackspeak": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.1.1.tgz", - "integrity": "sha512-juf9stUEwUaILepraGOWIJTLwg48bUnBmRqd2ln2Os1sW987zeoj/hzhbvRB95oMuS2ZTpjULmdwHNX4rzZIZw==", - "dev": true, - "requires": { - "@pkgjs/parseargs": "^0.11.0", - "cliui": "^8.0.1" - }, - "dependencies": { - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "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, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "lines-and-columns": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", - "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", - "dev": true - }, - "linkify-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", - "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", - "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "requires": { - "p-locate": "^6.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "markdown-it": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", - "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", - "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - }, - "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, - "requires": { - "repeat-string": "^1.0.0" - } - }, - "markdownlint": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.31.1.tgz", - "integrity": "sha512-CKMR2hgcIBrYlIUccDCOvi966PZ0kJExDrUi1R+oF9PvqQmCrTqjOsgIvf2403OmJ+CWomuzDoylr6KbuMyvHA==", - "dev": true, - "requires": { - "markdown-it": "13.0.1", - "markdownlint-micromark": "0.1.7" - } - }, - "markdownlint-cli": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.37.0.tgz", - "integrity": "sha512-hNKAc0bWBBuVhJbSWbUhRzavstiB4o1jh3JeSpwC4/dt6eJ54lRfYHRxVdzVp4qGWBKbeE6Pg490PFEfrKjqSg==", - "dev": true, - "requires": { - "commander": "~11.0.0", - "get-stdin": "~9.0.0", - "glob": "~10.3.4", - "ignore": "~5.2.4", - "js-yaml": "^4.1.0", - "jsonc-parser": "~3.2.0", - "markdownlint": "~0.31.1", - "minimatch": "~9.0.3", - "run-con": "~1.3.2" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", - "dev": true - }, - "glob": { - "version": "10.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.5.tgz", - "integrity": "sha512-bYUpUD7XDEHI4Q2O5a7PXGvyw4deKR70kHiDxzQbe925wbZknhOzUt2xBgTkYL6RBcVeXYuD9iNYeqoWbBZQnA==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - } - }, - "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "markdownlint-micromark": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.7.tgz", - "integrity": "sha512-BbRPTC72fl5vlSKv37v/xIENSRDYL/7X/XoFzZ740FGEbs9vZerLrIkFRY0rv7slQKxDczToYuMmqQFN61fi4Q==", - "dev": true - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true - }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", - "dev": true, - "requires": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - } - }, - "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, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "requires": { - "yocto-queue": "^1.0.0" - } - }, - "p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "requires": { - "p-limit": "^4.0.0" - } - }, - "parse-json": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.0.0.tgz", - "integrity": "sha512-kP+TQYAzAiVnzOlWOe0diD6L35s9bJh0SCn95PIbZFKrOYuIRQsQkeWEYxzVDuHTt9V9YqvYCJ2Qo4z9wdfZPw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.21.4", - "error-ex": "^1.3.2", - "json-parse-even-better-errors": "^3.0.0", - "lines-and-columns": "^2.0.3", - "type-fest": "^3.8.0" - }, - "dependencies": { - "type-fest": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", - "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", - "dev": true - } - } - }, - "path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true - }, - "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 - }, - "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 - }, - "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, - "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.1.tgz", - "integrity": "sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==", - "dev": true - } - } - }, - "read-pkg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", - "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.1", - "normalize-package-data": "^6.0.0", - "parse-json": "^7.0.0", - "type-fest": "^4.2.0" - } - }, - "read-pkg-up": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", - "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", - "dev": true, - "requires": { - "find-up": "^6.3.0", - "read-pkg": "^8.1.0", - "type-fest": "^4.2.0" - } - }, - "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 - }, - "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, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~4.1.0", - "minimist": "^1.2.8", - "strip-json-comments": "~3.1.1" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "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, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "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 - }, - "signal-exit": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.1.tgz", - "integrity": "sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==", - "dev": true - }, - "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 - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true - }, - "split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true - }, - "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, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "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, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "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 - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", - "dev": true - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "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, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true - }, - "yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true - } } } diff --git a/renovate.json b/renovate.json index ec58ddb..a2ed136 100644 --- a/renovate.json +++ b/renovate.json @@ -36,13 +36,14 @@ { "description": "Automerge dev deps updates", "matchManagers": ["npm"], - "matchDepTypes":["devDependencies"], + "matchDepTypes": ["devDependencies"], "automerge": true } ], - "regexManagers": [ + "customManagers": [ { "description": "Update forgeo version in chart", + "customType": "regex", "fileMatch": ["^Chart\\.yaml$"], "matchStrings": ["appVersion: (?.+?)\\s"], "depNameTemplate": "forgejo", @@ -51,11 +52,23 @@ }, { "description": "Update helm unittest plugin", + "customType": "regex", "fileMatch": ["^\\.woodpecker/[a-z-]+\\.yml$"], - "matchStrings": ["helm plugin install --version (?.+?) https://github.com/helm-unittest/helm-unittest\\s"], + "matchStrings": [ + "helm plugin install --version (?.+?) https://github.com/helm-unittest/helm-unittest\\s" + ], "depNameTemplate": "helm-unittest", "packageNameTemplate": "helm-unittest/helm-unittest", "datasourceTemplate": "github-releases" + }, + { + "description": "Detect helm-unittest yaml schema file", + "customType": "regex", + "fileMatch": [".vscode/settings\\.json$"], + "matchStrings": [ + "https:\\/\\/raw\\.githubusercontent\\.com\\/(?[^\\s]+?)\\/(?v[0-9.]+?)\\/schema\\/helm-testsuite\\.json" + ], + "datasourceTemplate": "github-releases" } ] } diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 97c286c..0843da5 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -2,6 +2,27 @@ {{/* Expand the name of the chart. */}} + +{{- /* multiple replicas assertions */ -}} +{{- if gt .Values.replicaCount 1.0 -}} + {{- fail "When using multiple replicas, a RWX file system is required" -}} + {{- if eq (get (.Values.persistence.accessModes 0) "ReadWriteOnce") -}} + {{- fail "When using multiple replicas, a RWX file system is required" -}} + {{- end }} + + {{- if eq (get .Values.gitea.config.indexer "ISSUE_INDEXER_TYPE") "bleve" -}} + {{- fail "When using multiple replicas, the repo indexer must be set to 'meilisearch' or 'elasticsearch'" -}} + {{- end }} + + {{- if and (eq .Values.gitea.config.indexer.REPO_INDEXER_TYPE "bleve") (eq .Values.gitea.config.indexer.REPO_INDEXER_ENABLED "true") -}} + {{- fail "When using multiple replicas, the repo indexer must be set to 'meilisearch' or 'elasticsearch'" -}} + {{- end }} + + {{- if eq .Values.gitea.config.indexer.ISSUE_INDEXER_TYPE "bleve" -}} + {{- (printf "DEBUG: When using multiple replicas, the repo indexer must be set to 'meilisearch' or 'elasticsearch'") | fail -}} + {{- end }} +{{- end }} + {{- define "gitea.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} @@ -35,14 +56,22 @@ Create chart name and version as used by the chart label. Create image name and tag used by the deployment. */}} {{- define "gitea.image" -}} +{{- $fullOverride := .Values.image.fullOverride | default "" -}} {{- $registry := .Values.global.imageRegistry | default .Values.image.registry -}} -{{- $name := .Values.image.repository -}} +{{- $repository := .Values.image.repository -}} +{{- $separator := ":" -}} {{- $tag := .Values.image.tag | default .Chart.AppVersion -}} {{- $rootless := ternary "-rootless" "" (.Values.image.rootless) -}} -{{- if $registry -}} - {{- printf "%s/%s:%s%s" $registry $name $tag $rootless -}} +{{- $digest := "" -}} +{{- if .Values.image.digest }} + {{- $digest = (printf "@%s" (.Values.image.digest | toString)) -}} +{{- end -}} +{{- if $fullOverride }} + {{- printf "%s" $fullOverride -}} +{{- else if $registry }} + {{- printf "%s/%s%s%s%s%s" $registry $repository $separator $tag $rootless $digest -}} {{- else -}} - {{- printf "%s:%s%s" $name $tag $rootless -}} + {{- printf "%s%s%s%s%s" $repository $separator $tag $rootless $digest -}} {{- end -}} {{- end -}} @@ -91,16 +120,38 @@ app.kubernetes.io/name: {{ include "gitea.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end -}} -{{- define "postgresql.dns" -}} -{{- printf "%s-postgresql.%s.svc.%s:%g" .Release.Name .Release.Namespace .Values.clusterDomain .Values.postgresql.global.postgresql.service.ports.postgresql -}} +{{- define "postgresql-ha.dns" -}} +{{- if (index .Values "postgresql-ha").enabled -}} +{{- printf "%s-postgresql-ha-pgpool.%s.svc.%s:%g" .Release.Name .Release.Namespace .Values.clusterDomain (index .Values "postgresql-ha" "service" "ports" "postgresql") -}} +{{- end -}} {{- end -}} -{{- define "memcached.dns" -}} -{{- printf "%s-memcached.%s.svc.%s:%g" .Release.Name .Release.Namespace .Values.clusterDomain .Values.memcached.service.ports.memcached | trunc 63 | trimSuffix "-" -}} +{{- define "postgresql.dns" -}} +{{- if (index .Values "postgresql").enabled -}} +{{- printf "%s-postgresql.%s.svc.%s:%g" .Release.Name .Release.Namespace .Values.clusterDomain .Values.postgresql.global.postgresql.service.ports.postgresql -}} +{{- end -}} +{{- end -}} + +{{- define "redis.dns" -}} +{{- if (index .Values "redis-cluster").enabled -}} +{{- printf "redis+cluster://:%s@%s-redis-cluster-headless.%s.svc.%s:%g/0?pool_size=100&idle_timeout=180s&" (index .Values "redis-cluster").global.redis.password .Release.Name .Release.Namespace .Values.clusterDomain (index .Values "redis-cluster").service.ports.redis -}} +{{- end -}} +{{- end -}} + +{{- define "redis.port" -}} +{{- if (index .Values "redis-cluster").enabled -}} +{{ (index .Values "redis-cluster").service.ports.redis }} +{{- end -}} +{{- end -}} + +{{- define "redis.servicename" -}} +{{- if (index .Values "redis-cluster").enabled -}} +{{- printf "%s-redis-cluster-headless.%s.svc.%s" .Release.Name .Release.Namespace .Values.clusterDomain -}} +{{- end -}} {{- end -}} {{- define "gitea.default_domain" -}} -{{- printf "%s-gitea.%s.svc.%s" (include "gitea.fullname" .) .Release.Namespace .Values.clusterDomain | trunc 63 | trimSuffix "-" -}} +{{- printf "%s-http.%s.svc.%s" (include "gitea.fullname" .) .Release.Namespace .Values.clusterDomain -}} {{- end -}} {{- define "gitea.ldap_settings" -}} @@ -182,6 +233,7 @@ https {{- else -}} {{- (printf "Key %s cannot be on top level of configuration" $key) | fail -}} {{- end -}} + {{- end }} {{- end }} @@ -211,6 +263,18 @@ https {{- if not (hasKey .Values.gitea.config "oauth2") -}} {{- $_ := set .Values.gitea.config "oauth2" dict -}} {{- end -}} + {{- if not (hasKey .Values.gitea.config "session") -}} + {{- $_ := set .Values.gitea.config "session" dict -}} + {{- end -}} + {{- if not (hasKey .Values.gitea.config "queue") -}} + {{- $_ := set .Values.gitea.config "queue" dict -}} + {{- end -}} + {{- if not (hasKey .Values.gitea.config "queue.issue_indexer") -}} + {{- $_ := set .Values.gitea.config "queue.issue_indexer" dict -}} + {{- end -}} + {{- if not (hasKey .Values.gitea.config "indexer") -}} + {{- $_ := set .Values.gitea.config "indexer" dict -}} + {{- end -}} {{- end -}} {{- define "gitea.inline_configuration.defaults" -}} @@ -226,13 +290,27 @@ https {{- if not (hasKey .Values.gitea.config.metrics "ENABLED") -}} {{- $_ := set .Values.gitea.config.metrics "ENABLED" .Values.gitea.metrics.enabled -}} {{- end -}} - {{- if .Values.memcached.enabled -}} + {{- if (index .Values "redis-cluster").enabled -}} {{- $_ := set .Values.gitea.config.cache "ENABLED" "true" -}} - {{- $_ := set .Values.gitea.config.cache "ADAPTER" "memcache" -}} + {{- $_ := set .Values.gitea.config.cache "ADAPTER" "redis" -}} {{- if not (.Values.gitea.config.cache.HOST) -}} - {{- $_ := set .Values.gitea.config.cache "HOST" (include "memcached.dns" .) -}} + {{- $_ := set .Values.gitea.config.cache "HOST" (include "redis.dns" .) -}} {{- end -}} {{- end -}} + {{- /* redis queue */ -}} + {{- if (index .Values "redis-cluster").enabled -}} + {{- $_ := set .Values.gitea.config.queue "TYPE" "redis" -}} + {{- $_ := set .Values.gitea.config.queue "CONN_STR" (include "redis.dns" .) -}} + {{- end -}} + {{- if not (get .Values.gitea.config.session "PROVIDER") -}} + {{- $_ := set .Values.gitea.config.session "PROVIDER" "redis" -}} + {{- end -}} + {{- if not (get .Values.gitea.config.session "PROVIDER_CONFIG") -}} + {{- $_ := set .Values.gitea.config.session "PROVIDER_CONFIG" (include "redis.dns" .) -}} + {{- end -}} + {{- if not .Values.gitea.config.indexer.ISSUE_INDEXER_TYPE -}} + {{- $_ := set .Values.gitea.config.indexer "ISSUE_INDEXER_TYPE" "db" -}} + {{- end -}} {{- end -}} {{- define "gitea.inline_configuration.defaults.server" -}} @@ -244,7 +322,7 @@ https {{- end -}} {{- if not (.Values.gitea.config.server.DOMAIN) -}} {{- if gt (len .Values.ingress.hosts) 0 -}} - {{- $_ := set .Values.gitea.config.server "DOMAIN" (index .Values.ingress.hosts 0).host -}} + {{- $_ := set .Values.gitea.config.server "DOMAIN" ( tpl (index .Values.ingress.hosts 0).host $) -}} {{- else -}} {{- $_ := set .Values.gitea.config.server "DOMAIN" (include "gitea.default_domain" .) -}} {{- end -}} @@ -279,7 +357,16 @@ https {{- end -}} {{- define "gitea.inline_configuration.defaults.database" -}} - {{- if .Values.postgresql.enabled -}} + {{- if (index .Values "postgresql-ha" "enabled") -}} + {{- $_ := set .Values.gitea.config.database "DB_TYPE" "postgres" -}} + {{- if not (.Values.gitea.config.database.HOST) -}} + {{- $_ := set .Values.gitea.config.database "HOST" (include "postgresql-ha.dns" .) -}} + {{- end -}} + {{- $_ := set .Values.gitea.config.database "NAME" (index .Values "postgresql-ha" "global" "postgresql" "database") -}} + {{- $_ := set .Values.gitea.config.database "USER" (index .Values "postgresql-ha" "global" "postgresql" "username") -}} + {{- $_ := set .Values.gitea.config.database "PASSWD" (index .Values "postgresql-ha" "global" "postgresql" "password") -}} + {{- end -}} + {{- if (index .Values "postgresql" "enabled") -}} {{- $_ := set .Values.gitea.config.database "DB_TYPE" "postgres" -}} {{- if not (.Values.gitea.config.database.HOST) -}} {{- $_ := set .Values.gitea.config.database "HOST" (include "postgresql.dns" .) -}} @@ -311,3 +398,7 @@ https {{- define "gitea.gpg-key-secret-name" -}} {{ default (printf "%s-gpg-key" (include "gitea.fullname" .)) .Values.signing.existingSecret }} {{- end -}} + +{{- define "gitea.serviceAccountName" -}} +{{ .Values.serviceAccount.name | default (include "gitea.fullname" .) }} +{{- end -}} diff --git a/templates/gitea/config.yaml b/templates/gitea/config.yaml index 387785c..4627a88 100644 --- a/templates/gitea/config.yaml +++ b/templates/gitea/config.yaml @@ -16,6 +16,37 @@ metadata: {{- include "gitea.labels" . | nindent 4 }} type: Opaque stringData: + assertions: | + +{{- /*assert that only one PG dep is enabled */ -}} +{{- if and (.Values.postgresql.enabled) (index .Values "postgresql-ha" "enabled") -}} + {{- fail "Only one of postgresql or postgresql-ha can be enabled at the same time." -}} +{{- end }} + +{{- /* multiple replicas assertions */ -}} +{{- if gt .Values.replicaCount 1.0 -}} + {{- if (get (get .Values.gitea.config "cron.GIT_GC_REPOS") "ENABLED") -}} + {{- fail "Invoking the garbage collector via CRON is not yet supported when running with multiple replicas. Please set 'cron.GIT_GC_REPOS.enabled = false'." -}} + {{- end }} + + {{- if eq (first .Values.persistence.accessModes) "ReadWriteOnce" -}} + {{- fail "When using multiple replicas, a RWX file system is required and gitea.persistence.accessModes[0] must be set to ReadWriteMany." -}} + {{- end }} + + {{- if eq (get .Values.gitea.config.indexer "ISSUE_INDEXER_TYPE") "bleve" -}} + {{- fail "When using multiple replicas, the issue indexer (gitea.config.indexer.ISSUE_INDEXER_TYPE) must be set to a HA-ready provider such as 'meilisearch', 'elasticsearch' or 'db' (if the DB is HA-ready)." -}} + {{- end }} + {{- if .Values.gitea.config.indexer.REPO_INDEXER_TYPE -}} + {{- if eq (get .Values.gitea.config.indexer "REPO_INDEXER_TYPE") "bleve" -}} + {{- if .Values.gitea.config.indexer.REPO_INDEXER_ENABLED -}} + {{- if eq (get .Values.gitea.config.indexer "REPO_INDEXER_ENABLED") "true" -}} + {{- fail "When using multiple replicas, the repo indexer (gitea.config.indexer.REPO_INDEXER_TYPE) must be set to 'meilisearch' or 'elasticsearch' or disabled." -}} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + +{{- end }} config_environment.sh: |- #!/usr/bin/env bash set -euo pipefail @@ -57,10 +88,10 @@ stringData: return fi - local masked_section="${section//./_0X2E_}" # '//' instructs to replace all matches + local masked_section="${section//./_0X2E_}" # '//' instructs to replace all matches masked_section="${masked_section//-/_0X2D_}" - export "FORGEJO__${masked_section^^}__${setting^^}=${value}" # '^^' makes the variable content uppercase + export "FORGEJO__${masked_section^^}__${setting^^}=${value}" # '^^' makes the variable content uppercase } function env2ini::reload_preset_envs() { @@ -131,7 +162,7 @@ stringData: function env2ini::generate_initial_secrets() { # These environment variables will either be # - overwritten with user defined values, - # - initially used to set up Gitea + # - initially used to set up Forgejo # Anyway, they won't harm existing app.ini files export FORGEJO__SECURITY__INTERNAL_TOKEN=$(gitea generate secret INTERNAL_TOKEN) diff --git a/templates/gitea/statefulset.yaml b/templates/gitea/deployment.yaml similarity index 88% rename from templates/gitea/statefulset.yaml rename to templates/gitea/deployment.yaml index b11813b..247a560 100644 --- a/templates/gitea/statefulset.yaml +++ b/templates/gitea/deployment.yaml @@ -1,22 +1,28 @@ apiVersion: apps/v1 -kind: StatefulSet +kind: Deployment metadata: name: {{ include "gitea.fullname" . }} annotations: - {{- if .Values.statefulset.annotations }} - {{- toYaml .Values.statefulset.annotations | nindent 4 }} + {{- if .Values.deployment.annotations }} + {{- toYaml .Values.deployment.annotations | nindent 4 }} {{- end }} labels: {{- include "gitea.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} + strategy: + type: {{ .Values.strategy.type }} + {{- if eq .Values.strategy.type "RollingUpdate" }} + rollingUpdate: + maxUnavailable: {{ .Values.strategy.rollingUpdate.maxUnavailable }} + maxSurge: {{ .Values.strategy.rollingUpdate.maxSurge }} + {{- end }} selector: matchLabels: {{- include "gitea.selectorLabels" . | nindent 6 }} - {{- if .Values.statefulset.labels }} - {{- toYaml .Values.statefulset.labels | nindent 6 }} + {{- if .Values.deployment.labels }} + {{- toYaml .Values.deployment.labels | nindent 6 }} {{- end }} - serviceName: {{ include "gitea.fullname" . }} template: metadata: annotations: @@ -32,13 +38,16 @@ spec: {{- end }} labels: {{- include "gitea.labels" . | nindent 8 }} - {{- if .Values.statefulset.labels }} - {{- toYaml .Values.statefulset.labels | nindent 8 }} + {{- if .Values.deployment.labels }} + {{- toYaml .Values.deployment.labels | nindent 8 }} {{- end }} spec: {{- if .Values.schedulerName }} schedulerName: "{{ .Values.schedulerName }}" {{- end }} + {{- if (or .Values.serviceAccount.create .Values.serviceAccount.name) }} + serviceAccountName: {{ include "gitea.serviceAccountName" . }} + {{- end }} {{- if .Values.priorityClassName }} priorityClassName: "{{ .Values.priorityClassName }}" {{- end }} @@ -59,8 +68,8 @@ spec: value: /data - name: GITEA_TEMP value: /tmp/gitea - {{- if .Values.statefulset.env }} - {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- if .Values.deployment.env }} + {{- toYaml .Values.deployment.env | nindent 12 }} {{- end }} {{- if .Values.signing.enabled }} - name: GNUPGHOME @@ -94,8 +103,8 @@ spec: value: /data - name: GITEA_TEMP value: /tmp/gitea - {{- if .Values.statefulset.env }} - {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- if .Values.deployment.env }} + {{- toYaml .Values.deployment.env | nindent 12 }} {{- end }} {{- if .Values.gitea.additionalConfigFromEnvs }} {{- toYaml .Values.gitea.additionalConfigFromEnvs | nindent 12 }} @@ -173,6 +182,10 @@ spec: value: /data - name: GITEA_TEMP value: /tmp/gitea + {{- if .Values.image.rootless }} + - name: HOME + value: /data/gitea/git + {{- end }} {{- if .Values.gitea.ldap }} {{- range $idx, $value := .Values.gitea.ldap }} {{- if $value.existingSecret }} @@ -227,8 +240,8 @@ spec: - name: GITEA_ADMIN_PASSWORD value: {{ .Values.gitea.admin.password | quote }} {{- end }} - {{- if .Values.statefulset.env }} - {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- if .Values.deployment.env }} + {{- toYaml .Values.deployment.env | nindent 12 }} {{- end }} volumeMounts: - name: init @@ -243,7 +256,7 @@ spec: {{- include "gitea.init-additional-mounts" . | nindent 12 }} resources: {{- toYaml .Values.initContainers.resources | nindent 12 }} - terminationGracePeriodSeconds: {{ .Values.statefulset.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.deployment.terminationGracePeriodSeconds }} containers: - name: {{ .Chart.Name }} image: "{{ include "gitea.image" . }}" @@ -268,12 +281,16 @@ spec: value: /tmp/gitea - name: TMPDIR value: /tmp/gitea + {{- if .Values.image.rootless }} + - name: HOME + value: /data/gitea/git + {{- end }} {{- if .Values.signing.enabled }} - name: GNUPGHOME value: {{ .Values.signing.gpgHome }} {{- end }} - {{- if .Values.statefulset.env }} - {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- if .Values.deployment.env }} + {{- toYaml .Values.deployment.env | nindent 12 }} {{- end }} ports: - name: ssh @@ -329,6 +346,10 @@ spec: affinity: {{- toYaml . | nindent 8 }} {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} @@ -367,38 +388,13 @@ spec: path: private.asc defaultMode: 0100 {{- end }} - {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + {{- if .Values.persistence.enabled }} + {{- if .Values.persistence.mount }} - name: data persistentVolumeClaim: - {{- with .Values.persistence.existingClaim }} - claimName: {{ tpl . $ }} - {{- end }} + claimName: {{ .Values.persistence.claimName }} + {{- end }} {{- else if not .Values.persistence.enabled }} - name: data emptyDir: {} - {{- else if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} - volumeClaimTemplates: - - metadata: - name: data - {{- with .Values.persistence.annotations }} - annotations: - {{- range $key, $value := . }} - {{ $key }}: {{ $value }} - {{- end }} - {{- end }} - {{- with .Values.persistence.labels }} - labels: - {{- range $key, $value := . }} - {{ $key }}: {{ $value }} - {{- end }} - {{- end }} - spec: - accessModes: - {{- range .Values.persistence.accessModes }} - - {{ . | quote }} - {{- end }} - {{- include "gitea.persistence.storageClass" . | indent 8 }} - resources: - requests: - storage: {{ .Values.persistence.size | quote }} {{- end }} diff --git a/templates/gitea/ingress.yaml b/templates/gitea/ingress.yaml index 224e777..9991eec 100644 --- a/templates/gitea/ingress.yaml +++ b/templates/gitea/ingress.yaml @@ -15,10 +15,10 @@ metadata: name: {{ $fullName }} labels: {{- include "gitea.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: - {{- toYaml . | nindent 4 }} - {{- end }} + {{- range $key, $value := .Values.ingress.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} spec: {{- if .Values.ingress.className }} ingressClassName: {{ .Values.ingress.className }} @@ -28,14 +28,14 @@ spec: {{- range .Values.ingress.tls }} - hosts: {{- range .hosts }} - - {{ . | quote }} + - {{ tpl . $ | quote }} {{- end }} secretName: {{ .secretName }} {{- end }} {{- end }} rules: {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ tpl .host $ | quote }} http: paths: {{- range .paths }} diff --git a/templates/gitea/init.yaml b/templates/gitea/init.yaml index 838460b..000cd9b 100644 --- a/templates/gitea/init.yaml +++ b/templates/gitea/init.yaml @@ -58,9 +58,30 @@ stringData: { # try gitea migrate } || { # catch - echo "Gitea migrate might fail due to database connection...This init-container will try again in a few seconds" + echo "Forgejo migrate might fail due to database connection...This init-container will try again in a few seconds" exit 1 } + + {{- if include "redis.servicename" . }} + function test_redis_connection() { + local RETRY=0 + local MAX=30 + + echo 'Wait for redis to become avialable...' + until [ "${RETRY}" -ge "${MAX}" ]; do + nc -vz -w2 {{ include "redis.servicename" . }} {{ include "redis.port" . }} && break + RETRY=$[${RETRY}+1] + echo "...not ready yet (${RETRY}/${MAX})" + done + + if [ "${RETRY}" -ge "${MAX}" ]; then + echo "Redis not reachable after '${MAX}' attempts!" + exit 1 + fi + } + + test_redis_connection + {{- end }} {{- if or .Values.gitea.admin.existingSecret (and .Values.gitea.admin.username .Values.gitea.admin.password) }} diff --git a/templates/gitea/poddisruptionbudget.yaml b/templates/gitea/poddisruptionbudget.yaml new file mode 100644 index 0000000..d2b7e17 --- /dev/null +++ b/templates/gitea/poddisruptionbudget.yaml @@ -0,0 +1,17 @@ +{{- if .Values.podDisruptionBudget -}} +{{- if .Capabilities.APIVersions.Has "policy/v1" }} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "gitea.fullname" . }} + labels: + {{- include "gitea.labels" . | nindent 4 }} +spec: + selector: + matchLabels: + {{- include "gitea.selectorLabels" . | nindent 6 }} + {{- toYaml .Values.podDisruptionBudget | nindent 2 }} +{{- end -}} \ No newline at end of file diff --git a/templates/gitea/pvc.yaml b/templates/gitea/pvc.yaml new file mode 100644 index 0000000..995bd10 --- /dev/null +++ b/templates/gitea/pvc.yaml @@ -0,0 +1,26 @@ +{{- if and .Values.persistence.enabled .Values.persistence.create }} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Values.persistence.claimName }} + namespace: {{ $.Release.Namespace }} + annotations: +{{ .Values.persistence.annotations | toYaml | indent 4}} +spec: + accessModes: + {{- if gt .Values.replicaCount 1.0 }} + - ReadWriteMany + {{- else }} + {{- .Values.persistence.accessModes | toYaml | nindent 4 }} + {{- end }} + volumeMode: Filesystem + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + {{- with .Values.persistence.volumeName }} + volumeName: {{ . }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size }} +{{- end }} \ No newline at end of file diff --git a/templates/gitea/serviceaccount.yaml b/templates/gitea/serviceaccount.yaml new file mode 100644 index 0000000..e730f9c --- /dev/null +++ b/templates/gitea/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "gitea.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} + labels: + {{- include "gitea.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.labels }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- with .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: + {{- . | toYaml | nindent 2 }} +{{- end }} +{{- end }} diff --git a/templates/gitea/ssh-svc.yaml b/templates/gitea/ssh-svc.yaml index 620f624..3e8b3c2 100644 --- a/templates/gitea/ssh-svc.yaml +++ b/templates/gitea/ssh-svc.yaml @@ -39,7 +39,9 @@ spec: ports: - name: ssh port: {{ .Values.service.ssh.port }} + {{- if .Values.gitea.config.server.SSH_LISTEN_PORT }} targetPort: {{ .Values.gitea.config.server.SSH_LISTEN_PORT }} + {{- end }} protocol: TCP {{- if .Values.service.ssh.nodePort }} nodePort: {{ .Values.service.ssh.nodePort }} diff --git a/unittests/config/database-section_postgresql-ha.yaml b/unittests/config/database-section_postgresql-ha.yaml new file mode 100644 index 0000000..f416d79 --- /dev/null +++ b/unittests/config/database-section_postgresql-ha.yaml @@ -0,0 +1,30 @@ +suite: config template | database section (postgresql-ha) +release: + name: gitea-unittests + namespace: testing +tests: + - it: connects to pgpool service + template: templates/gitea/config.yaml + set: + postgresql: + enabled: false + postgresql-ha: + enabled: true + asserts: + - documentIndex: 0 + matchRegex: + path: stringData.database + pattern: HOST=gitea-unittests-postgresql-ha-pgpool.testing.svc.cluster.local:5432 + - it: renders the referenced service + template: charts/postgresql-ha/templates/pgpool/service.yaml + set: + postgresql: + enabled: false + postgresql-ha: + enabled: true + asserts: + - containsDocument: + kind: Service + apiVersion: v1 + name: gitea-unittests-postgresql-ha-pgpool + namespace: testing diff --git a/unittests/config/database-section_postgresql.yaml b/unittests/config/database-section_postgresql.yaml new file mode 100644 index 0000000..5a7501b --- /dev/null +++ b/unittests/config/database-section_postgresql.yaml @@ -0,0 +1,30 @@ +suite: config template | database section (postgresql) +release: + name: gitea-unittests + namespace: testing +tests: + - it: "connects to postgresql service" + template: templates/gitea/config.yaml + set: + postgresql: + enabled: true + postgresql-ha: + enabled: false + asserts: + - documentIndex: 0 + matchRegex: + path: stringData.database + pattern: HOST=gitea-unittests-postgresql.testing.svc.cluster.local:5432 + - it: "renders the referenced service" + template: charts/postgresql/templates/primary/svc.yaml + set: + postgresql: + enabled: true + postgresql-ha: + enabled: false + asserts: + - containsDocument: + kind: Service + apiVersion: v1 + name: gitea-unittests-postgresql + namespace: testing diff --git a/unittests/config/server-section_domain.yaml b/unittests/config/server-section_domain.yaml new file mode 100644 index 0000000..cf5a3b8 --- /dev/null +++ b/unittests/config/server-section_domain.yaml @@ -0,0 +1,67 @@ +suite: config template | server section (domain related) +release: + name: gitea-unittests + namespace: testing +tests: + - it: "[default values] uses ingress host for DOMAIN|SSH_DOMAIN|ROOT_URL" + template: templates/gitea/config.yaml + asserts: + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nDOMAIN=git.example.com + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nSSH_DOMAIN=git.example.com + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nROOT_URL=http://git.example.com + + ################################################ + + - it: "[no ingress hosts] uses gitea http service for DOMAIN|SSH_DOMAIN|ROOT_URL" + template: templates/gitea/config.yaml + set: + ingress: + hosts: [] + asserts: + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nDOMAIN=gitea-unittests-http.testing.svc.cluster.local + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nSSH_DOMAIN=gitea-unittests-http.testing.svc.cluster.local + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nROOT_URL=http://gitea-unittests-http.testing.svc.cluster.local + + ################################################ + + - it: "[provided via values] uses that for DOMAIN|SSH_DOMAIN|ROOT_URL" + template: templates/gitea/config.yaml + set: + gitea.config.server.DOMAIN: provided.example.com + ingress: + hosts: + - host: non-used.example.com + paths: + - path: / + pathType: Prefix + asserts: + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nDOMAIN=provided.example.com + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nSSH_DOMAIN=provided.example.com + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: \nROOT_URL=http://provided.example.com diff --git a/unittests/dependency-major-image-check.yaml b/unittests/dependency-major-image-check.yaml new file mode 100644 index 0000000..db33ed7 --- /dev/null +++ b/unittests/dependency-major-image-check.yaml @@ -0,0 +1,42 @@ +suite: Dependency update consistency +release: + name: gitea-unittests + namespace: testing +tests: + - it: "[postgresql-ha] ensures we detect major image version upgrades" + template: charts/postgresql-ha/templates/postgresql/statefulset.yaml + set: + postgresql: + enabled: false + postgresql-ha: + enabled: true + asserts: + - documentIndex: 0 + matchRegex: + path: spec.template.spec.containers[0].image + # IN CASE OF AN INTENTIONAL MAJOR BUMP, ADJUST THIS TEST + pattern: ^docker.io/bitnami/postgresql-repmgr:16.+$ + - it: "[postgresql] ensures we detect major image version upgrades" + template: charts/postgresql/templates/primary/statefulset.yaml + set: + postgresql: + enabled: true + postgresql-ha: + enabled: false + asserts: + - documentIndex: 0 + matchRegex: + path: spec.template.spec.containers[0].image + # IN CASE OF AN INTENTIONAL MAJOR BUMP, ADJUST THIS TEST + pattern: ^docker.io/bitnami/postgresql:15.+$ + - it: "[redis-cluster] ensures we detect major image version upgrades" + template: charts/redis-cluster/templates/redis-statefulset.yaml + set: + redis-cluster: + enabled: true + asserts: + - documentIndex: 0 + matchRegex: + path: spec.template.spec.containers[0].image + # IN CASE OF AN INTENTIONAL MAJOR BUMP, ADJUST THIS TEST + pattern: ^docker.io/bitnami/redis-cluster:7.+$ diff --git a/unittests/statefulset/basic.yaml b/unittests/deployment/basic.yaml similarity index 58% rename from unittests/statefulset/basic.yaml rename to unittests/deployment/basic.yaml index f1a4c41..bfcb9f2 100644 --- a/unittests/statefulset/basic.yaml +++ b/unittests/deployment/basic.yaml @@ -1,17 +1,17 @@ -suite: Statefulset template (basic) +suite: deployment template (basic) release: name: forgejo-unittests namespace: testing templates: - - templates/gitea/statefulset.yaml + - templates/gitea/deployment.yaml - templates/gitea/config.yaml tests: - - it: renders a statefulset - template: templates/gitea/statefulset.yaml + - it: renders a deployment + template: templates/gitea/deployment.yaml asserts: - hasDocuments: count: 1 - containsDocument: - kind: StatefulSet + kind: Deployment apiVersion: apps/v1 name: forgejo-unittests diff --git a/unittests/deployment/image-configuration.yaml b/unittests/deployment/image-configuration.yaml new file mode 100644 index 0000000..35f8981 --- /dev/null +++ b/unittests/deployment/image-configuration.yaml @@ -0,0 +1,93 @@ +suite: deployment template (image configuration) +release: + name: gitea-unittests + namespace: testing +chart: + # Override appVersion to be consistent with used digest :) + appVersion: 1.19.3 +templates: + - templates/gitea/deployment.yaml + - templates/gitea/config.yaml +tests: + - it: default values + template: templates/gitea/deployment.yaml + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "gitea/gitea:1.19.3-rootless" + - it: tag override + template: templates/gitea/deployment.yaml + set: + image.tag: "1.19.4" + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "gitea/gitea:1.19.4-rootless" + - it: root-based image + template: templates/gitea/deployment.yaml + set: + image.rootless: false + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "gitea/gitea:1.19.3" + - it: scoped registry + template: templates/gitea/deployment.yaml + set: + image.registry: "example.com" + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "example.com/gitea/gitea:1.19.3-rootless" + - it: global registry + template: templates/gitea/deployment.yaml + set: + global.imageRegistry: "global.example.com" + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "global.example.com/gitea/gitea:1.19.3-rootless" + - it: digest for rootless image + template: templates/gitea/deployment.yaml + set: + image: + rootless: true + digest: sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "gitea/gitea:1.19.3-rootless@sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a" + - it: image fullOverride (does not append rootless) + template: templates/gitea/deployment.yaml + set: + image: + fullOverride: gitea/gitea:1.19.3 + # setting rootless, registry, repository, tag, and digest to prove that override works + rootless: true + registry: example.com + repository: example/image + tag: "1.0.0" + digest: sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "gitea/gitea:1.19.3" + - it: digest for root-based image + template: templates/gitea/deployment.yaml + set: + image: + rootless: false + digest: sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "gitea/gitea:1.19.3@sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a" + - it: digest and global registry + template: templates/gitea/deployment.yaml + set: + global.imageRegistry: "global.example.com" + image.digest: "sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a" + asserts: + - equal: + path: spec.template.spec.containers[0].image + value: "global.example.com/gitea/gitea:1.19.3-rootless@sha256:b28e8f3089b52ebe6693295df142f8c12eff354e9a4a5bfbb5c10f296c3a537a" diff --git a/unittests/deployment/ingress-configuration.yaml b/unittests/deployment/ingress-configuration.yaml new file mode 100644 index 0000000..6a36eb0 --- /dev/null +++ b/unittests/deployment/ingress-configuration.yaml @@ -0,0 +1,23 @@ +suite: ingress template +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/ingress.yaml +tests: + - it: hostname using TPL + set: + global.giteaHostName: "gitea.example.com" + ingress.enabled: true + ingress.hosts[0].host: "{{ .Values.global.giteaHostName }}" + ingress.tls: + - secretName: gitea-tls + hosts: + - "{{ .Values.global.giteaHostName }}" + asserts: + - equal: + path: spec.tls[0].hosts[0] + value: "gitea.example.com" + - equal: + path: spec.rules[0].host + value: "gitea.example.com" diff --git a/unittests/deployment/inline-config.yaml b/unittests/deployment/inline-config.yaml new file mode 100644 index 0000000..545bb36 --- /dev/null +++ b/unittests/deployment/inline-config.yaml @@ -0,0 +1,33 @@ +suite: config template +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/config.yaml +tests: + - it: inline config stringData.server using TPL + set: + global.giteaHostName: "gitea.example.com" + ingress.enabled: true + ingress.hosts[0].host: "{{ .Values.global.giteaHostName }}" + ingress.tls: + - secretName: gitea-tls + hosts: + - "{{ .Values.global.giteaHostName }}" + asserts: + - documentIndex: 0 + matchRegex: + path: metadata.name + pattern: .*-inline-config$ + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: DOMAIN=gitea\.example\.com + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: ROOT_URL=https://gitea\.example\.com + - documentIndex: 0 + matchRegex: + path: stringData.server + pattern: SSH_DOMAIN=gitea\.example\.com diff --git a/unittests/statefulset/signing-disabled.yaml b/unittests/deployment/signing-disabled.yaml similarity index 76% rename from unittests/statefulset/signing-disabled.yaml rename to unittests/deployment/signing-disabled.yaml index 9e74700..7dd5b0e 100644 --- a/unittests/statefulset/signing-disabled.yaml +++ b/unittests/deployment/signing-disabled.yaml @@ -1,13 +1,13 @@ -suite: Statefulset template (signing disabled) +suite: deployment template (signing disabled) release: name: forgejo-unittests namespace: testing templates: - - templates/gitea/statefulset.yaml + - templates/gitea/deployment.yaml - templates/gitea/config.yaml tests: - it: skips gpg init container - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml asserts: - notContains: path: spec.template.spec.initContainers @@ -15,7 +15,7 @@ tests: content: name: configure-gpg - it: skips gpg env in `init-directories` init container - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml set: signing.enabled: false asserts: @@ -25,14 +25,14 @@ tests: name: GNUPGHOME value: /data/git/.gnupg - it: skips gpg env in runtime container - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml asserts: - notContains: path: spec.template.spec.containers[0].env content: name: GNUPGHOME - it: skips gpg volume spec - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml asserts: - notContains: path: spec.template.spec.volumes diff --git a/unittests/statefulset/signing-enabled.yaml b/unittests/deployment/signing-enabled.yaml similarity index 89% rename from unittests/statefulset/signing-enabled.yaml rename to unittests/deployment/signing-enabled.yaml index 9dd3901..d9ab251 100644 --- a/unittests/statefulset/signing-enabled.yaml +++ b/unittests/deployment/signing-enabled.yaml @@ -1,13 +1,13 @@ -suite: Statefulset template (signing enabled) +suite: deployment template (signing enabled) release: name: forgejo-unittests namespace: testing templates: - - templates/gitea/statefulset.yaml + - templates/gitea/deployment.yaml - templates/gitea/config.yaml tests: - it: adds gpg init container - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml set: signing: enabled: true @@ -39,7 +39,7 @@ tests: mountPath: /raw readOnly: true - it: adds gpg env in `init-directories` init container - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml set: signing.enabled: true signing.existingSecret: "custom-gpg-secret" @@ -50,7 +50,7 @@ tests: name: GNUPGHOME value: /data/git/.gnupg - it: adds gpg env in runtime container - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml set: signing.enabled: true signing.existingSecret: "custom-gpg-secret" @@ -61,7 +61,7 @@ tests: name: GNUPGHOME value: /data/git/.gnupg - it: adds gpg volume spec - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml set: signing: enabled: true @@ -78,7 +78,7 @@ tests: path: private.asc defaultMode: 0100 - it: supports gpg volume spec with external reference - template: templates/gitea/statefulset.yaml + template: templates/gitea/deployment.yaml set: signing: enabled: true diff --git a/unittests/deployment/ssh-configuration.yaml b/unittests/deployment/ssh-configuration.yaml new file mode 100644 index 0000000..543fd5f --- /dev/null +++ b/unittests/deployment/ssh-configuration.yaml @@ -0,0 +1,64 @@ +suite: deployment template (SSH configuration) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/deployment.yaml + - templates/gitea/config.yaml +tests: + - it: supports defining SSH log level for root based image + template: templates/gitea/deployment.yaml + set: + image.rootless: false + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSH_LOG_LEVEL + value: "INFO" + - it: supports overriding SSH log level + template: templates/gitea/deployment.yaml + set: + image.rootless: false + gitea.ssh.logLevel: "DEBUG" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSH_LOG_LEVEL + value: "DEBUG" + - it: supports overriding SSH log level (even when image.fullOverride set) + template: templates/gitea/deployment.yaml + set: + image.fullOverride: gitea/gitea:1.19.3 + image.rootless: false + gitea.ssh.logLevel: "DEBUG" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSH_LOG_LEVEL + value: "DEBUG" + - it: skips SSH_LOG_LEVEL for rootless image + template: templates/gitea/deployment.yaml + set: + image.rootless: true + gitea.ssh.logLevel: "DEBUG" # explicitly defining a non-standard level here + asserts: + - notContains: + path: spec.template.spec.containers[0].env + any: true + content: + name: SSH_LOG_LEVEL + - it: skips SSH_LOG_LEVEL for rootless image (even when image.fullOverride set) + template: templates/gitea/deployment.yaml + set: + image.fullOverride: gitea/gitea:1.19.3 + image.rootless: true + gitea.ssh.logLevel: "DEBUG" # explicitly defining a non-standard level here + asserts: + - notContains: + path: spec.template.spec.containers[0].env + any: true + content: + name: SSH_LOG_LEVEL diff --git a/unittests/gpg-secret/signing-enabled.yaml b/unittests/gpg-secret/signing-enabled.yaml index 3ef5997..b2f36e4 100644 --- a/unittests/gpg-secret/signing-enabled.yaml +++ b/unittests/gpg-secret/signing-enabled.yaml @@ -33,7 +33,7 @@ tests: kind: Secret apiVersion: v1 name: forgejo-unittests-gpg-key - - isNotEmpty: + - isNotNullOrEmpty: path: metadata.labels - equal: path: data.privateKey diff --git a/unittests/init/init_directory_structure.sh-rootless.yaml b/unittests/init/init_directory_structure.sh-rootless.yaml new file mode 100644 index 0000000..29dac81 --- /dev/null +++ b/unittests/init/init_directory_structure.sh-rootless.yaml @@ -0,0 +1,88 @@ +suite: Init template +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/init.yaml +tests: + - it: runs gpg in batch mode + set: + signing.enabled: true + signing.privateKey: |- + -----BEGIN PGP PRIVATE KEY BLOCK----- + {placeholder} + -----END PGP PRIVATE KEY BLOCK----- + asserts: + - equal: + path: stringData["configure_gpg_environment.sh"] + value: |- + #!/usr/bin/env bash + set -eu + + gpg --batch --import /raw/private.asc + - it: skips gpg script block for disabled signing + asserts: + - equal: + path: stringData["init_directory_structure.sh"] + value: |- + #!/usr/bin/env bash + + set -euo pipefail + + set -x + mkdir -p /data/git/.ssh + chmod -R 700 /data/git/.ssh + [ ! -d /data/gitea/conf ] && mkdir -p /data/gitea/conf + + # prepare temp directory structure + mkdir -p "${GITEA_TEMP}" + chmod ug+rwx "${GITEA_TEMP}" + - it: adds gpg script block for enabled signing + set: + signing.enabled: true + signing.privateKey: |- + -----BEGIN PGP PRIVATE KEY BLOCK----- + {placeholder} + -----END PGP PRIVATE KEY BLOCK----- + asserts: + - equal: + path: stringData["init_directory_structure.sh"] + value: |- + #!/usr/bin/env bash + + set -euo pipefail + + set -x + mkdir -p /data/git/.ssh + chmod -R 700 /data/git/.ssh + [ ! -d /data/gitea/conf ] && mkdir -p /data/gitea/conf + + # prepare temp directory structure + mkdir -p "${GITEA_TEMP}" + chmod ug+rwx "${GITEA_TEMP}" + + if [ ! -d "${GNUPGHOME}" ]; then + mkdir -p "${GNUPGHOME}" + chmod 700 "${GNUPGHOME}" + chown 1000:1000 "${GNUPGHOME}" + fi + - it: it does not chown /data even when image.fullOverride is set + template: templates/gitea/init.yaml + set: + image.fullOverride: gitea/gitea:1.20.5 + asserts: + - equal: + path: stringData["init_directory_structure.sh"] + value: |- + #!/usr/bin/env bash + + set -euo pipefail + + set -x + mkdir -p /data/git/.ssh + chmod -R 700 /data/git/.ssh + [ ! -d /data/gitea/conf ] && mkdir -p /data/gitea/conf + + # prepare temp directory structure + mkdir -p "${GITEA_TEMP}" + chmod ug+rwx "${GITEA_TEMP}" diff --git a/unittests/init/init_directory_structure.sh.yaml b/unittests/init/init_directory_structure.sh.yaml index 90f8672..fa1e969 100644 --- a/unittests/init/init_directory_structure.sh.yaml +++ b/unittests/init/init_directory_structure.sh.yaml @@ -7,6 +7,7 @@ templates: tests: - it: runs gpg in batch mode set: + image.rootless: false signing.enabled: true signing.privateKey: |- -----BEGIN PGP PRIVATE KEY BLOCK----- @@ -21,6 +22,8 @@ tests: gpg --batch --import /raw/private.asc - it: skips gpg script block for disabled signing + set: + image.rootless: false asserts: - equal: path: stringData["init_directory_structure.sh"] @@ -41,6 +44,7 @@ tests: chmod ug+rwx "${GITEA_TEMP}" - it: adds gpg script block for enabled signing set: + image.rootless: false signing.enabled: true signing.privateKey: |- -----BEGIN PGP PRIVATE KEY BLOCK----- diff --git a/unittests/serviceaccount/basic.yaml b/unittests/serviceaccount/basic.yaml new file mode 100644 index 0000000..73d8e1e --- /dev/null +++ b/unittests/serviceaccount/basic.yaml @@ -0,0 +1,82 @@ +suite: ServiceAccount template (basic) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/serviceaccount.yaml +tests: + - it: skips rendering by default + asserts: + - hasDocuments: + count: 0 + - it: renders default ServiceAccount object with serviceAccount.create=true + set: + serviceAccount.create: true + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: ServiceAccount + apiVersion: v1 + name: gitea-unittests + - equal: + path: automountServiceAccountToken + value: false + - notExists: + path: imagePullSecrets + - notExists: + path: metadata.annotations + - it: allows for adding custom labels + set: + serviceAccount: + create: true + labels: + custom: label + asserts: + - equal: + path: metadata.labels.custom + value: label + - it: allows for adding custom annotations + set: + serviceAccount: + create: true + annotations: + myCustom: annotation + asserts: + - equal: + path: metadata.annotations.myCustom + value: annotation + - it: allows to override the generated name + set: + serviceAccount: + create: true + name: provided-serviceaccount-name + asserts: + - equal: + path: metadata.name + value: provided-serviceaccount-name + - it: allows to mount the token + set: + serviceAccount: + create: true + automountServiceAccountToken: true + asserts: + - equal: + path: automountServiceAccountToken + value: true + - it: allows to reference image pull secrets + set: + serviceAccount: + create: true + imagePullSecrets: + - name: testing-image-pull-secret + - name: another-pull-secret + asserts: + - contains: + path: imagePullSecrets + content: + name: testing-image-pull-secret + - contains: + path: imagePullSecrets + content: + name: another-pull-secret diff --git a/unittests/serviceaccount/reference.yaml b/unittests/serviceaccount/reference.yaml new file mode 100644 index 0000000..25faa03 --- /dev/null +++ b/unittests/serviceaccount/reference.yaml @@ -0,0 +1,32 @@ +suite: ServiceAccount template (reference) +release: + name: gitea-unittests + namespace: testing +templates: + - templates/gitea/serviceaccount.yaml + - templates/gitea/deployment.yaml + - templates/gitea/config.yaml +tests: + - it: does not modify the deployment by default + template: templates/gitea/deployment.yaml + asserts: + - notExists: + path: spec.serviceAccountName + - it: adds the reference to the deployment with serviceAccount.create=true + template: templates/gitea/deployment.yaml + set: + serviceAccount.create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: gitea-unittests + - it: allows referencing an externally created ServiceAccount to the deployment + template: templates/gitea/deployment.yaml + set: + serviceAccount: + create: false # explicitly set to define rendering behavior + name: "externally-existing-serviceaccount" + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: externally-existing-serviceaccount diff --git a/unittests/statefulset/ssh-configuration.yaml b/unittests/statefulset/ssh-configuration.yaml deleted file mode 100644 index 24ffc3a..0000000 --- a/unittests/statefulset/ssh-configuration.yaml +++ /dev/null @@ -1,40 +0,0 @@ -suite: Statefulset template (SSH configuration) -release: - name: gitea-unittests - namespace: testing -templates: - - templates/gitea/statefulset.yaml - - templates/gitea/config.yaml -tests: - - it: supports defining SSH log level for root based image - template: templates/gitea/statefulset.yaml - set: - image.rootless: false - asserts: - - contains: - path: spec.template.spec.containers[0].env - content: - name: SSH_LOG_LEVEL - value: "INFO" - - it: supports overriding SSH log level - template: templates/gitea/statefulset.yaml - set: - image.rootless: false - gitea.ssh.logLevel: "DEBUG" - asserts: - - contains: - path: spec.template.spec.containers[0].env - content: - name: SSH_LOG_LEVEL - value: "DEBUG" - - it: skips SSH_LOG_LEVEL for rootless image - template: templates/gitea/statefulset.yaml - set: - image.rootless: true - gitea.ssh.logLevel: "DEBUG" # explicitly defining a non-standard level here - asserts: - - notContains: - path: spec.template.spec.containers[0].env - any: true - content: - name: SSH_LOG_LEVEL diff --git a/values.yaml b/values.yaml index 8b615a7..c730361 100644 --- a/values.yaml +++ b/values.yaml @@ -20,9 +20,19 @@ global: # hostnames: # - example.com -## @param replicaCount number of replicas for the statefulset +## @param replicaCount number of replicas for the deployment replicaCount: 1 +## @section strategy +## @param strategy.type strategy type +## @param strategy.rollingUpdate.maxSurge maxSurge +## @param strategy.rollingUpdate.maxUnavailable maxUnavailable +strategy: + type: "RollingUpdate" + rollingUpdate: + maxSurge: "100%" + maxUnavailable: 0 + ## @param clusterDomain cluster domain clusterDomain: cluster.local @@ -30,15 +40,19 @@ clusterDomain: cluster.local ## @param image.registry image registry, e.g. gcr.io,docker.io ## @param image.repository Image to start for this pod ## @param image.tag Visit: [Image tag](https://codeberg.org/forgejo/-/packages/container/forgejo/versions). Defaults to `appVersion` within Chart.yaml. +## @param image.digest Image digest. Allows to pin the given image tag. Useful for having control over mutable tags like `latest` ## @param image.pullPolicy Image pull policy -## @param image.rootless Wether or not to pull the rootless version of Forgejo, only works on Forgejo 1.14.x or higher +## @param image.rootless Wether or not to pull the rootless version of Forgejo +## @param image.fullOverride Completely overrides the image registry, path/image, tag and digest. **Adjust `image.rootless` accordingly and review [Rootless defaults](#rootless-defaults).** image: - registry: "codeberg.org" + registry: codeberg.org repository: forgejo/forgejo # Overrides the image tag whose default is the chart appVersion. tag: "" - pullPolicy: Always - rootless: false # only possible when running 1.14 or later + digest: "" + pullPolicy: IfNotPresent + rootless: true + fullOverride: "" ## @param imagePullSecrets Secret to use for pulling the image imagePullSecrets: [] @@ -74,11 +88,16 @@ containerSecurityContext: {} ## @param securityContext Run init and Forgejo containers as a specific securityContext securityContext: {} +## @param podDisruptionBudget Pod disruption budget +podDisruptionBudget: {} +# maxUnavailable: 1 +# minAvailable: 1 + ## @section Service service: ## @param service.http.type Kubernetes service type for web traffic ## @param service.http.port Port number for web traffic - ## @param service.http.clusterIP ClusterIP setting for http autosetup for statefulset is None + ## @param service.http.clusterIP ClusterIP setting for http autosetup for deployment is None ## @param service.http.loadBalancerIP LoadBalancer IP setting ## @param service.http.nodePort NodePort for http service ## @param service.http.externalTrafficPolicy If `service.http.type` is `NodePort` or `LoadBalancer`, set this to `Local` to enable source IP preservation @@ -101,7 +120,7 @@ service: annotations: {} ## @param service.ssh.type Kubernetes service type for ssh traffic ## @param service.ssh.port Port number for ssh traffic - ## @param service.ssh.clusterIP ClusterIP setting for ssh autosetup for statefulset is None + ## @param service.ssh.clusterIP ClusterIP setting for ssh autosetup for deployment is None ## @param service.ssh.loadBalancerIP LoadBalancer IP setting ## @param service.ssh.nodePort NodePort for ssh service ## @param service.ssh.externalTrafficPolicy If `service.ssh.type` is `NodePort` or `LoadBalancer`, set this to `Local` to enable source IP preservation @@ -155,7 +174,7 @@ ingress: # If helm doesn't correctly detect your ingress API version you can set it here. # apiVersion: networking.k8s.io/v1 -## @section StatefulSet +## @section deployment # ## @param resources Kubernetes resources resources: @@ -177,26 +196,29 @@ resources: ## @param schedulerName Use an alternate scheduler, e.g. "stork" schedulerName: "" -## @param nodeSelector NodeSelector for the statefulset +## @param nodeSelector NodeSelector for the deployment nodeSelector: {} -## @param tolerations Tolerations for the statefulset +## @param tolerations Tolerations for the deployment tolerations: [] -## @param affinity Affinity for the statefulset +## @param affinity Affinity for the deployment affinity: {} -## @param dnsConfig dnsConfig for the statefulset +## @param topologySpreadConstraints TopologySpreadConstraints for the deployment +topologySpreadConstraints: [] + +## @param dnsConfig dnsConfig for the deployment dnsConfig: {} -## @param priorityClassName priorityClassName for the statefulset +## @param priorityClassName priorityClassName for the deployment priorityClassName: "" -## @param statefulset.env Additional environment variables to pass to containers -## @param statefulset.terminationGracePeriodSeconds How long to wait until forcefully kill the pod -## @param statefulset.labels Labels for the statefulset -## @param statefulset.annotations Annotations for the Forgejo StatefulSet to be created -statefulset: +## @param deployment.env Additional environment variables to pass to containers +## @param deployment.terminationGracePeriodSeconds How long to wait until forcefully kill the pod +## @param deployment.labels Labels for the deployment +## @param deployment.annotations Annotations for the Forgejo deployment to be created +deployment: env: [] # - name: VARIABLE @@ -205,28 +227,52 @@ statefulset: labels: {} annotations: {} +## @section ServiceAccount + +## @param serviceAccount.create Enable the creation of a ServiceAccount +## @param serviceAccount.name Name of the created ServiceAccount, defaults to release name. Can also link to an externally provided ServiceAccount that should be used. +## @param serviceAccount.automountServiceAccountToken Enable/disable auto mounting of the service account token +## @param serviceAccount.imagePullSecrets Image pull secrets, available to the ServiceAccount +## @param serviceAccount.annotations Custom annotations for the ServiceAccount +## @param serviceAccount.labels Custom labels for the ServiceAccount +serviceAccount: + create: false + name: "" + automountServiceAccountToken: false + imagePullSecrets: [] + # - name: private-registry-access + annotations: {} + labels: {} + ## @section Persistence # ## @param persistence.enabled Enable persistent storage -## @param persistence.existingClaim Use an existing claim to store repository information +## @param persistence.create Whether to create the persistentVolumeClaim for shared storage +## @param persistence.mount Whether the persistentVolumeClaim should be mounted (even if not created) +## @param persistence.claimName Use an existing claim to store repository information ## @param persistence.size Size for persistence to store repo information ## @param persistence.accessModes AccessMode for persistence ## @param persistence.labels Labels for the persistence volume claim to be created -## @param persistence.annotations Annotations for the persistence volume claim to be created +## @param persistence.annotations.helm.sh/resource-policy Resource policy for the persistence volume claim ## @param persistence.storageClass Name of the storage class to use ## @param persistence.subPath Subdirectory of the volume to mount at +## @param persistence.volumeName Name of persistent volume in PVC persistence: enabled: true - existingClaim: + create: true + mount: true + claimName: gitea-shared-storage size: 10Gi accessModes: - ReadWriteOnce labels: {} - annotations: {} storageClass: subPath: + volumeName: "" + annotations: + helm.sh/resource-policy: keep -## @param extraVolumes Additional volumes to mount to the Forgejo statefulset +## @param extraVolumes Additional volumes to mount to the Forgejo deployment extraVolumes: [] # - name: postgres-ssl-vol # secret: @@ -341,13 +387,14 @@ gitea: # customProfileUrl: # customEmailUrl: - ## @param gitea.config Configuration for the Forgejo server,ref: [config-cheat-sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) - config: {} - # APP_NAME: "Forgejo: Git with a cup of tea" - # RUN_MODE: dev - # - # server: - # SSH_PORT: 22 + ## @param gitea.config.server.SSH_PORT SSH port for rootlful Forgejo image + ## @param gitea.config.server.SSH_LISTEN_PORT SSH port for rootless Forgejo image + config: + # APP_NAME: "Forgejo: Git with a cup of tea" + # RUN_MODE: dev + server: + SSH_PORT: 22 # rootful image + SSH_LISTEN_PORT: 2222 # rootless image # # security: # PASSWORD_COMPLEXITY: spec @@ -429,26 +476,49 @@ gitea: successThreshold: 1 failureThreshold: 10 -## @section Memcached -## @descriptionStart -## Memcached is loaded as a dependency from [Bitnami](https://github.com/bitnami/charts/tree/master/bitnami/memcached) if enabled in the values. Complete Configuration can be taken from their website. -## @descriptionEnd -# -## @param memcached.enabled Memcached is loaded as a dependency from [Bitnami](https://github.com/bitnami/charts/tree/master/bitnami/memcached) if enabled in the values. Complete Configuration can be taken from their website. -## ref: https://hub.docker.com/r/bitnami/memcached/tags/ -## @param memcached.service.ports.memcached Port for Memcached -memcached: +## @section redis-cluster +## @param redis-cluster.enabled Enable redis +## @param redis-cluster.usePassword Whether to use password authentication +## @param redis-cluster.cluster.nodes Number of redis cluster master nodes +## @param redis-cluster.cluster.replicas Number of redis cluster master node replicas +redis-cluster: enabled: true - # image: - # registry: docker.io - # repository: bitnami/memcached - # tag: "" - # digest: "" - # pullPolicy: IfNotPresent - # pullSecrets: [] + usePassword: false + cluster: + nodes: 3 # default: 6 + replicas: 0 # default: 1 + +## @section postgresql-ha +# +## @param postgresql-ha.enabled Enable postgresql-ha +## @param postgresql-ha.postgresql.password Password for the `gitea` user (overrides `auth.password`) +## @param postgresql-ha.global.postgresql.database Name for a custom database to create (overrides `auth.database`) +## @param postgresql-ha.global.postgresql.username Name for a custom user to create (overrides `auth.username`) +## @param postgresql-ha.global.postgresql.password Name for a custom password to create (overrides `auth.password`) +## @param postgresql-ha.postgresql.repmgrPassword Repmgr Password +## @param postgresql-ha.postgresql.postgresPassword postgres Password +## @param postgresql-ha.pgpool.adminPassword pgpool adminPassword +## @param postgresql-ha.service.ports.postgresql postgresql service port (overrides `service.ports.postgresql`) +## @param postgresql-ha.primary.persistence.size PVC Storage Request for postgresql-ha volume +postgresql-ha: + global: + postgresql: + database: gitea + password: gitea + username: gitea + enabled: true + postgresql: + repmgrPassword: changeme2 + postgresPassword: changeme1 + password: changeme4 + pgpool: + adminPassword: changeme3 service: ports: - memcached: 11211 + postgresql: 5432 + primary: + persistence: + size: 10Gi ## @section PostgreSQL ## @descriptionStart @@ -462,7 +532,7 @@ memcached: ## @param postgresql.global.postgresql.service.ports.postgresql PostgreSQL service port (overrides `service.ports.postgresql`) ## @param postgresql.primary.persistence.size PVC Storage Request for PostgreSQL volume postgresql: - enabled: true + enabled: false global: postgresql: auth: