Full Code of seacrew/helm-compose for AI

main 4fb6bfb902ce cached
59 files
147.8 KB
42.9k tokens
91 symbols
1 requests
Download .txt
Repository: seacrew/helm-compose
Branch: main
Commit: 4fb6bfb902ce
Files: 59
Total size: 147.8 KB

Directory structure:
gitextract_hj9wc7s7/

├── .github/
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── build.yaml
│       ├── ci.yaml
│       ├── dependabot-automerge.yaml
│       ├── docs.yaml
│       └── release.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── LICENSES/
│   ├── helm/
│   │   └── LICENSE
│   ├── helm-diff/
│   │   └── LICENSE
│   └── helm-template/
│       └── LICENSE
├── Makefile
├── README.md
├── cmd/
│   ├── down.go
│   ├── get.go
│   ├── list.go
│   ├── root.go
│   ├── template.go
│   └── up.go
├── docs/
│   ├── commands/
│   │   ├── down.md
│   │   ├── get.md
│   │   ├── list.md
│   │   ├── template.md
│   │   └── up.md
│   ├── compose-file-reference.md
│   ├── index.md
│   ├── key-features-and-use-cases.md
│   ├── quick-start.md
│   ├── storage-providers.md
│   └── stylesheets/
│       └── extra.css
├── examples/
│   ├── .gitignore
│   ├── k8s-storage-compose.yaml
│   ├── local-storage-compose.yaml
│   ├── s3-storage-compose.yaml
│   ├── simple-compose.yaml
│   ├── values/
│   │   └── wordpress.yaml
│   └── wordpress-compose.yaml
├── go.mod
├── go.sum
├── internal/
│   ├── compose/
│   │   ├── compose.go
│   │   └── helm.go
│   ├── config/
│   │   ├── config.go
│   │   ├── config_test.go
│   │   ├── types.go
│   │   └── types_test.go
│   ├── provider/
│   │   ├── kubernetes.go
│   │   ├── kubernetes_test.go
│   │   ├── local.go
│   │   ├── providers.go
│   │   ├── s3.go
│   │   └── s3_test.go
│   └── util/
│       ├── colors.go
│       ├── encoding.go
│       └── util.go
├── main.go
├── mkdocs.yaml
├── plugin.yaml
└── scripts/
    └── install.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--- Provide a general summary of your changes in the Title above -->

## Description

<!--- Describe your changes in detail -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->

## How Has This Been Tested?

<!--- Please describe in detail how you tested your changes. -->
<!--- See how your change affects other areas of the code, etc. -->

## Checklist:

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] My change requires a change to the documentation or CHANGELOG.
- [ ] I have updated the CHANGELOG.md accordingly.
- [ ] I have created a feature (non-master) branch for my PR.

================================================
FILE: .github/workflows/build.yaml
================================================
name: Build

on:
  workflow_dispatch:
  push:
    branches:
      - main
    paths-ignore:
      - .github/**
      - docs/**
      - LICENSES/**
      - README.md
      - mkdocs.yaml
  workflow_run:
    workflows: [CI]
    types: [completed]
    branches: [main]

permissions:
  contents: write

jobs:
  build:
    runs-on: ubuntu-latest
    if: ${{ github.event_name == 'workflow_dispatch' || github.event_name == 'push' || github.event.workflow_run.conclusion == 'success' }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
          check-latest: true
          cache: true

      - name: Create Helm Compose Distributions
        run: make dist

      - uses: actions/upload-artifact@v4
        with:
          name: helm-compose
          path: release/*

      - name: Publish release
        if: "contains(github.event.head_commit.message, 'docs: release')"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          TAG=$(git describe --abbrev=0)

          for FILE in release/*.tgz; do 
            gh release upload ${TAG} ${FILE} --clobber
          done

          gh release edit ${TAG} --latest --prerelease=false



================================================
FILE: .github/workflows/ci.yaml
================================================
name: CI

on:
  workflow_dispatch:
  pull_request:
    branches:
      - main

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version-file: go.mod
          check-latest: true
          cache: true

      - uses: azure/setup-helm@v4
        with:
          version: v3.11.0

      - name: Install Helm Compose
        run: make install

      - uses: helm/kind-action@v1.10.0
      
      - name: Run Helm Compose
        run: helm compose up -f examples/simple-compose.yaml



================================================
FILE: .github/workflows/dependabot-automerge.yaml
================================================
name: Dependabot auto-approve
on: pull_request

permissions:
  pull-requests: write

jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: github.actor == 'dependabot[bot]'
    steps:
      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v2
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Approve PR
        run: gh pr review --approve "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GH_TOKEN: ${{secrets.GITHUB_TOKEN}}

      - name: Auto-merge PR
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

================================================
FILE: .github/workflows/docs.yaml
================================================
name: Deploy docs
on:
  push:
    branches:
      - main
    paths:
      - docs/**
      - mkdocs.yaml
  workflow_dispatch:
permissions:
  contents: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.event.repository.fork == false
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - uses: actions/setup-python@v5
        with:
          python-version: 3.x

      - name: Run generate cache key
        run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV

      - uses: actions/cache@v4
        with:
          key: mkdocs-cache-${{ env.cache_id }}
          path: .cache
          restore-keys: |
            mkdocs-

      - name: Run setup mkdocs dependencies
        run: pip install mkdocs mkdocs-material mkdocs-autolinks-plugin mkdocs-drawio-file mkdocs-git-revision-date-localized-plugin fontawesome_markdown mike pymdown-extensions

      - name: Run mkdocs deployment
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
          mike deploy -b gh-pages -p -F mkdocs.yaml -u next

      - name: Run mkdocs release deployment
        if: "contains(github.event.head_commit.message, 'docs: release')"
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

          LATEST_TAG=$(git describe --abbrev=0)
          SHORT_VERSION=${LATEST_TAG%.*}

          mike deploy -b gh-pages -p -F mkdocs.yaml -u ${SHORT_VERSION} latest



================================================
FILE: .github/workflows/release.yaml
================================================
name: Create Release

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version number of the new release'
        required: true
        type: string

permissions:
  contents: write
  pull-requests: write

jobs:
  release:
    name: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          fetch-tags: true

      - name: Update and create tag
        run: |
          # Setup git
          git config --global user.name "github-actions[bot]"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

          # Update version in documentation and helm plugin config
          sed -i 's\--version .*\--version ${{ inputs.version }}\g' docs/quick-start.md
          sed -i 's\--version .*\--version ${{ inputs.version }}\g' README.md
          sed -i 's\^version:.*\version: ${{ inputs.version }}\g' plugin.yaml

          # Update changelog
          sed -i 's\# Changes since .*\# ${{ inputs.version }}\g' CHANGELOG.md
          echo -e "# Changes since ${{ inputs.version }}\n$(cat CHANGELOG.md)" > CHANGELOG.md

          # Commit, tag and push
          git commit -am "docs: release ${{ inputs.version }}"
          git tag ${{ inputs.version }} -am "${{ inputs.version }}"
          git push --follow-tags 

      - name: Create release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TAG: ${{ inputs.version }}
        run: |
          gh release create "${TAG}" \
              --title="${TAG}" \
              --generate-notes \
              --prerelease


================================================
FILE: .gitignore
================================================
.vscode
.hcstate

vendor/
bin/
build/
release/

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/


================================================
FILE: CHANGELOG.md
================================================
# Changes since 1.3.0
# 1.3.0
- feat: add templating command
- chore: upgrade to golang 1.22

# 1.2.0
- feat: add wait option
- feat: apiVersion 1.1 for wait option and add better version handling
- fix: stop creation of revisions if the helm compose file didn't change

# 1.1.2
- chore: dependency upgrades

# 1.1.1
- bugfix: when the target bucket is empty no state files are written

# 1.1.0
- feat: add support for s3 as a revision storage provider / backend
- chore: upgrade dependencies to the latest versions


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright © 2023 The Helm Compose Authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: LICENSES/helm/LICENSE
================================================
=== https://github.com/helm/helm ===

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2016 The Kubernetes Authors All Rights Reserved

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: LICENSES/helm-diff/LICENSE
================================================
=== https://github.com/databus23/helm-diff ===

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: LICENSES/helm-template/LICENSE
================================================
=== https://github.com/technosophos/helm-template ===

Helm Template Plugin
Copyright (C) 2016, Matt Butcher

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

================================================
FILE: Makefile
================================================
HELM_HOME := $(shell bash -c 'eval $$(helm env); echo $$HELM_PLUGINS')
LDFLAGS := -s -w

.PHONY: install
install: build
	mkdir -p $(HELM_HOME)/helm-compose/bin
	cp bin/compose $(HELM_HOME)/helm-compose/bin
	cp plugin.yaml $(HELM_HOME)/helm-compose/

.PHONY: build
build:
	mkdir -p bin/
	go build -v -o bin/compose

.PHONY: dist
dist: export COPYFILE_DISABLE=1 #teach OSX tar to not put ._* files in tar archive
dist: export CGO_ENABLED=0
dist:
	rm -rf build/compose/* release/*
	mkdir -p build/compose/bin release/
	cp README.md LICENSE plugin.yaml build/compose
	GOOS=linux GOARCH=amd64 go build -o build/compose/bin/compose -trimpath -ldflags="$(LDFLAGS)"
	tar -C build/ -zcvf $(CURDIR)/release/helm-compose-linux-amd64.tgz compose/
	GOOS=linux GOARCH=arm64 go build -o build/compose/bin/compose -trimpath -ldflags="$(LDFLAGS)"
	tar -C build/ -zcvf $(CURDIR)/release/helm-compose-linux-arm64.tgz compose/
	GOOS=freebsd GOARCH=amd64 go build -o build/compose/bin/compose -trimpath -ldflags="$(LDFLAGS)"
	tar -C build/ -zcvf $(CURDIR)/release/helm-compose-freebsd-amd64.tgz compose/
	GOOS=darwin GOARCH=amd64 go build -o build/compose/bin/compose -trimpath -ldflags="$(LDFLAGS)"
	tar -C build/ -zcvf $(CURDIR)/release/helm-compose-macos-amd64.tgz compose/
	GOOS=darwin GOARCH=arm64 go build -o build/compose/bin/compose -trimpath -ldflags="$(LDFLAGS)"
	tar -C build/ -zcvf $(CURDIR)/release/helm-compose-macos-arm64.tgz compose/
	rm build/compose/bin/compose
	GOOS=windows GOARCH=amd64 go build -o build/compose/bin/compose.exe -trimpath -ldflags="$(LDFLAGS)"
	tar -C build/ -zcvf $(CURDIR)/release/helm-compose-windows-amd64.tgz compose/

================================================
FILE: README.md
================================================
# ⚠️ Project Discontinued

This open-source project is **no longer actively maintained**.

- No new features or bug fixes will be added.
- Pull requests and issues may not receive responses.

---

![helm-compose-banner](https://user-images.githubusercontent.com/18513179/240495789-e76890d3-f0f9-48b9-9d18-89e53effe65b.png)

[![Build Status](https://github.com/seacrew/helm-compose/actions/workflows/build.yaml/badge.svg)](https://github.com/seacrew/helm-compose/actions/workflows/build.yaml)
[![Go Report Card](https://goreportcard.com/badge/github.com/seacrew/helm-compose)](https://goreportcard.com/report/github.com/seacrew/helm-compose)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=seacrew_helm-compose&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=seacrew_helm-compose)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=seacrew_helm-compose&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=seacrew_helm-compose)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/seacrew/helm-compose)](https://github.com/seacrew/helm-compose/releases/latest)

Helm Compose is a tool for managing multiple releases of one or many different Helm charts. It is heavily inspired by Docker Compose and is an extension of the package manager idea behind Helm itself. It allows for full configuration-as-code capabilities in an single yaml file.

## Installation

It is requirement to use helm v3.10.0+. 

Install a specific version of helm compose (recommended). Click [here](https://github.com/seacrew/helm-compose/releases/latest) for the latest version.

```
helm plugin install https://github.com/seacrew/helm-compose --version 1.3.0
```

Install the latest version.

```
helm plugin install https://github.com/seacrew/helm-compose
```

## Quick Start Guide

Helm Compose makes it easy to define a list of Releases and all necessary Repositories for the charts you use in a single compose file.

Install your releases:

```bash
$ helm compose up -f helm-compose.yaml
```

Uninstall your releases

```bash
$ helm compose down -f helm-compose.yaml
```

A Helm Compose file looks something like this:

```yaml
apiVersion: 1.1

storage:
  name: mycompose
  type: local # default
  path: .hcstate # default

releases:
  wordpress:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
  wordpress2:
    chart: bitnami/wordpress
    chartVersion: 15.2.22
    namespace: homepage
    createNamespace: true
  postgres:
    chart: bitnami/postgresql
    chartVersion: 12.1.9
    namespace: database
    createNamespace: true

repositories:
  bitnami: https://charts.bitnami.com/bitnami
```

Check out the [examples](https://github.com/seacrew/helm-compose/tree/main/examples) directory.

## Documentation

Checkout the complete [documentation.](https://seacrew.github.io/helm-compose/)


================================================
FILE: cmd/down.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"github.com/seacrew/helm-compose/internal/compose"
	"github.com/seacrew/helm-compose/internal/config"
	"github.com/spf13/cobra"
)

// downCmd represents the down command
var downCmd = &cobra.Command{
	Use:   "down",
	Short: "This command uninstalls all releases defined in your compose file.",
	Long:  ``,
	RunE: func(cmd *cobra.Command, args []string) error {
		cmd.SilenceUsage = true

		if err := compose.CompatibleHelmVersion(); err != nil {
			return err
		}

		config, err := config.ParseComposeFile(composeFile)
		if err != nil {
			return err
		}

		return compose.RunDown(config)
	},
}

func init() {
	rootCmd.AddCommand(downCmd)
}


================================================
FILE: cmd/get.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"fmt"
	"strconv"

	"github.com/seacrew/helm-compose/internal/compose"
	"github.com/seacrew/helm-compose/internal/config"
	"github.com/spf13/cobra"
)

var getCmd = &cobra.Command{
	Use:   "get [revision]",
	Short: "This command retrieves the decoded content from the revision id you specify.",
	Long:  ``,
	Args:  cobra.ExactArgs(1),
	RunE: func(cmd *cobra.Command, args []string) error {
		cmd.SilenceUsage = true

		if err := compose.CompatibleHelmVersion(); err != nil {
			return err
		}

		config, err := config.ParseComposeFile(composeFile)
		if err != nil {
			return err
		}

		revision, err := strconv.Atoi(args[0])
		if err != nil {
			return fmt.Errorf("REVISION must be a number")
		}

		if revision < 1 {
			return fmt.Errorf("REVISION must be a positiv number")
		}

		return compose.GetRevision(revision, config)
	},
}

func init() {
	rootCmd.AddCommand(getCmd)
}


================================================
FILE: cmd/list.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"github.com/seacrew/helm-compose/internal/compose"
	"github.com/seacrew/helm-compose/internal/config"
	"github.com/spf13/cobra"
)

var listCmd = &cobra.Command{
	Use:   "list",
	Short: "This command lists the revisions ids for your compose file.",
	Long:  ``,
	RunE: func(cmd *cobra.Command, args []string) error {
		cmd.SilenceUsage = true

		if err := compose.CompatibleHelmVersion(); err != nil {
			return err
		}

		config, err := config.ParseComposeFile(composeFile)
		if err != nil {
			return err
		}

		return compose.ListRevisions(config)
	},
}

func init() {
	rootCmd.AddCommand(listCmd)
}


================================================
FILE: cmd/root.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"os"

	"github.com/spf13/cobra"
)

// upCmd represents the up command
var composeFile string

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "compose",
	Short: "Compose is a helm plugin to define and manage multiple helm releases as a single entity.",
	Long: `With the Helm Compose plugin you are able to create a single compose file
to manage a multitude of releases. Either for deploying one chart multiple times in
the same or different namespaces or to deploy multiple charts as a single entity together.

This idea is heavily inspired and influenced by the idea behind docker-compose.`,
}

func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	rootCmd.PersistentFlags().StringVarP(&composeFile, "file", "f", "", "Compose configuration file")
}


================================================
FILE: cmd/template.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"github.com/seacrew/helm-compose/internal/compose"
	"github.com/seacrew/helm-compose/internal/config"
	"github.com/spf13/cobra"
)

var templateCmd = &cobra.Command{
	Use:   "template [RELEASE ...]",
	Short: "Render templates for all releases locally and display the output.",
	Long:  ``,
	RunE: func(cmd *cobra.Command, args []string) error {
		cmd.SilenceUsage = true

		if err := compose.CompatibleHelmVersion(); err != nil {
			return err
		}

		config, err := config.ParseComposeFile(composeFile)
		if err != nil {
			return err
		}

		return compose.Template(config, args)
	},
}

func init() {
	rootCmd.AddCommand(templateCmd)
}


================================================
FILE: cmd/up.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"github.com/seacrew/helm-compose/internal/compose"
	"github.com/seacrew/helm-compose/internal/config"
	"github.com/spf13/cobra"
)

var upCmd = &cobra.Command{
	Use:   "up",
	Short: "This command installs or upgrades all the releases defined in your compose file and uninstalls releases that have been removed since the last revision.",
	Long:  ``,
	RunE: func(cmd *cobra.Command, args []string) error {
		cmd.SilenceUsage = true

		if err := compose.CompatibleHelmVersion(); err != nil {
			return err
		}

		config, err := config.ParseComposeFile(composeFile)
		if err != nil {
			return err
		}

		return compose.RunUp(config)
	},
}

func init() {
	rootCmd.AddCommand(upCmd)
}


================================================
FILE: docs/commands/down.md
================================================
# helm compose down

Uninstall all releases defined in your `helm-compose.yaml`

## Usage

The following command will uninstall all releases of the previous revision if one exists. Otherwise the releases defined in your current `helm-compose.yaml` will be uninstalled.

```
helm compose down [flags]
```

## Options

```
Flags:
  -h, --help   help for down

Global Flags:
  -f, --file string   Compose configuration file
```


================================================
FILE: docs/commands/get.md
================================================
# helm compose get

Get a previous revision of your `helm-compose.yaml`

## Usage

The following command will return the previous `helm-compose.yaml` content on stdout.

```
helm compose get [revision] [flags]
```

## Options

```
Flags:
  -h, --help   help for get

Global Flags:
  -f, --file string   Compose configuration file
```


================================================
FILE: docs/commands/list.md
================================================
# helm compose list

List previous revisions of your `helm-compose.yaml`

## Usage

```
helm compose list [flags]
```

## Options

```
Flags:
  -h, --help   help for list

Global Flags:
  -f, --file string   Compose configuration file
```

## Example

```
$ helm compose list

| Date             | Revision |
| ---------------- | -------- |
| 2023-05-22 10:24 |        1 |
| 2023-05-22 11:19 |        2 |
```


================================================
FILE: docs/commands/template.md
================================================
# helm compose template

Template all kubernetes resources for the releases specified in your `helm-compose.yaml` and print them out to stdout.

## Usage

The following command will print out all kubernetes resources that would be installed or upgraded on stdout.

```
helm compose template [releases...] [flags]
```

## Options

```
Flags:
  -h, --help   help for template

Global Flags:
  -f, --file string   Compose configuration file
```


================================================
FILE: docs/commands/up.md
================================================
# helm compose up

Install all releases and repositories defined in your `helm-compose.yaml`

## Usage

The following command will install all releases defined in your `helm-compose.yaml` and will compare it to the latest previous revision and uninstall all releases that have been removed since then.

```
helm compose up [flags]
```

## Options

```
Flags:
  -h, --help   help for up

Global Flags:
  -f, --file string   Compose configuration file
```


================================================
FILE: docs/compose-file-reference.md
================================================
# Compose File Reference

## storage

```yaml
storage:
  name: my-compose
  type: local
  numberOfRevisions: 10
```

| Option            | Type   | Description                                                                                 | Required | Default | ApiVersion |
| ----------------- | ------ | ------------------------------------------------------------------------------------------- | -------- | ------- | ---------- |
| name              | string | Name to be used to store revisions with a storage provider.                                 | true     |         | 1.0        |
| type              | string | Type / name of the storage provider you want to use. By default local files will be stored. | false    | local   | 1.0        |
| numberOfRevisions | int    | Number of revisions to be stored and to be able to rollback to.                             | false    | 10      | 1.0        |

More details regarding the available storage providers and provider specific options can be found [here.](storage-providers.md)

## releases

```yaml
releases:
  my-website:
    chart: bitnami/wordpress
```

| Option           | Type   | Description                                                                                                                                                                 | Required | Default        | ApiVersion |
| ---------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------- | ---------- |
| chart            | string | Name of the chart to be used.                                                                                                                                               | true     |                | 1.0        |
| chartVersion     | string | Version of the chart to be used.                                                                                                                                            | false    | latest         | 1.0        |
| forceUpdate      | bool   | Force resource updates through a replacement strategy                                                                                                                       | false    | false          | 1.0        |
| historyMax       | int    | Limit the maximum number of revisions saved per release.                                                                                                                    | false    | 10             | 1.0        |
| createNamespace  | bool   | Create the release namespace if not present                                                                                                                                 | false    | false          | 1.0        |
| cleanUpOnFail    | bool   | Allow deletion of new resources created in this upgrade when upgrade fails.                                                                                                 | false    | false          | 1.0        |
| dependencyUpdate | bool   | Update dependencies if they are missing before installing the chart                                                                                                         | false    | false          | 1.0        |
| skipTlsVerify    | bool   | Skip tls certificate checks for the chart download                                                                                                                          | false    | false          | 1.0        |
| skipCrds         | bool   | If set, no CRDs will be installed.                                                                                                                                          | false    | false          | 1.0        |
| postRenderer     | string | The path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path | false    |                | 1.0        |
| postRendererArgs | array  | An argument to the post-renderer (can specify multiple) (default [])                                                                                                        | false    |                | 1.0        |
| kubeconfig       | string | Path to the kubeconfig file                                                                                                                                                 | false    | ~/.kube/config | 1.0        |
| kubecontext      | string | Name of the kubeconfig context to use                                                                                                                                       | false    |                | 1.0        |
| caFile           | string | Verify certificates of HTTPS-enabled servers using this CA bundle                                                                                                           | false    |                | 1.0        |
| certFile         | string | Identify HTTPS client using this SSL certificate file                                                                                                                       | false    |                | 1.0        |
| keyFile          | string | Identify HTTPS client using this SSL key file                                                                                                                               | false    |                | 1.0        |
| timeout          | string | Time to wait for any individual Kubernetes operation (like Jobs for hooks) (default 5m0s)                                                                                   | false    | 5m             | 1.0        |
| wait             | bool   | Waits until all Pods are in a ready state,  It will wait for as long as the --timeout value                                                                                 | false    |                | 1.1        |
| values           | map    | Map of values with highest priority to overwrite any values in the chart values or your additional values files. (Allows for usage of environment variables.)               | false    |                | 1.0        |
| valueFiles       | string | List of paths to value files.                                                                                                                                               | false    | 5m             | 1.0        |

Uninstall options:

| Option           | Type   | Description                                                                                                  | Required | Default    | ApiVersion |
| ---------------- | ------ | ------------------------------------------------------------------------------------------------------------ | -------- | ---------- | ---------- |
| deletionStrategy | string | Must be "background", "orphan", or "foreground". Selects the deletion cascading strategy for the dependents. | false    | background | 1.0        |
| deletionTimeout  | string | Time to wait for any individual Kubernetes operation (like Jobs for hooks) (default 5m0s)                    | false    | 5m         | 1.0        |
| deletionNoHooks  | bool   | Prevent hooks from running during uninstallation                                                             | false    | false      | 1.0        |
| keepHistory      | bool   | Remove all associated resources and mark the release as deleted, but retain the release history              | false    | false      | 1.0        |

## repositories

A map of repository names and their respective urls.

```yaml
repositories:
  bitnami: https://charts.bitnami.com/bitnami
```


================================================
FILE: docs/index.md
================================================
![helm-compose-banner](https://user-images.githubusercontent.com/18513179/212496531-1d166236-ed88-411d-8403-ad1f94d28846.png)

[![Build Status](https://github.com/seacrew/helm-compose/actions/workflows/build.yaml/badge.svg)](https://github.com/seacrew/helm-compose/actions/workflows/build.yaml)
[![Go Report Card](https://goreportcard.com/badge/github.com/seacrew/helm-compose)](https://goreportcard.com/report/github.com/seacrew/helm-compose)
[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=seacrew_helm-compose&metric=reliability_rating)](https://sonarcloud.io/summary/new_code?id=seacrew_helm-compose)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=seacrew_helm-compose&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=seacrew_helm-compose)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/seacrew/helm-compose)](https://github.com/seacrew/helm-compose/releases/latest)

Helm Compose is a tool for managing multiple releases for one or many different Helm charts. It is an extension of the package manager idea behind Helm and is heavily inspired by Docker Compose.


================================================
FILE: docs/key-features-and-use-cases.md
================================================
# Key Features and Use Cases

The main idea behind `helm-compose` is to control / configure all helm related options as code by providing a compose file structure to configure everything you need to setup your helm based infrastructure.

## Repository handling

Configuration based installation of all necessary repositories you define as a dependency in your `helm-compose.yaml` before triggering the installation of your releases.

```yaml
apiVersion: 1.1

repositories:
  bitnami: https://charts.bitnami.com/bitnami
```

## Multi release handling

The main feature of `helm-compose` is the ability to define a multitude of releases inside a single file. `helm-compose` supports single kubernetes-cluster and multi-cluster configuration.

### Single cluster

Define as many releases as you would like for one or more namespaces.

```yaml
apiVersion: 1.1

releases:
  wordpress:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
    namespace: homepage
  wordpress2:
    chart: bitnami/wordpress
    chartVersion: 15.2.22
    namespace: homepage
repositories:
  bitnami: https://charts.bitnami.com/bitnami
```

### Multi cluster

You can either use the `kubeconfig` options to point to a different path and use the `kubecontext` to select a specific context inside your kubeconfig.

```yaml
apiVersion: 1.1

releases:
  wordpress-dev:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
    namespace: homepage
    kubeconfig: ~/.kube/dev
  wordpress-int:
    chart: bitnami/wordpress
    chartVersion: 15.2.22
    namespace: homepage
    kubeconfig: ~/.kube/int
repositories:
  bitnami: https://charts.bitnami.com/bitnami
```

### Environment variables

`helm-compose` is able to inject environment variables inside your values block to deal with secrets that shouldn't be committed to your source control.

Syntax: `${MY_ENV_VARIABLE}`.

```bash
export WORDPRESS_ADMIN_PASSWORD="xxx"
export MARIADB_ROOT_PASSWORD="xxx"
helm compose up
```

```yaml
apiVersion: 1.1

releases:
  wordpress:
    chart: bitnami/wordpress
    values:
      wordpressPassword: ${WORDPRESS_ADMIN_PASSWORD}
      mariadb.auth.rootPassword: ${MARIADB_ROOT_PASSWORD}
```

## Revision handling

Revisions are essentially snapshots of your current `helm-compose.yaml`. Every time you trigger `helm compose up` a new revision will be created and stored (By default the last 10 revisions are kept).

### Configuration

```yaml
apiVersion: 1.1

storage:
  name: wordpress
  type: local # default: local
  numberOfRevisions: 5 # default: 10
```

### Usage

You can list your revisions and get the content of your previous revisions via the [`helm compose list`](commands/list.md) and [`helm compose get`](commands/get.md) commands.

### Rollback

Just select a revision you want to go back to. You can check the content with the `helm compose get` command and parse it directly into any `helm compose` command like so:

```bash
$ helm compose list
| Date             | Revision |
| ---------------- | -------- |
| 2023-05-24 23:56 |       12 |
| 2023-05-24 23:56 |       13 |
| 2023-05-24 23:57 |       14 |
| 2023-05-24 23:57 |       15 |
| 2023-05-24 23:57 |       16 |

# select revision 15 and use the pipe | operator to parse the content back into compose up with -f -
helm compose get 15 | helm compose up -f -
```


================================================
FILE: docs/quick-start.md
================================================
# Quick Start

## Installation

Install a specific version (recommended). Click [here](https://github.com/seacrew/helm-compose/releases/latest) for the latest version.

```
helm plugin install https://github.com/seacrew/helm-compose --version 1.3.0
```

Install latest version.

```
helm plugin install https://github.com/seacrew/helm-compose
```

## How to use helm compose

Helm Compose makes it easy to define a Compose file containing a list of Releases and necessary Repositories for the charts you use.

Install your releases:

```
helm compose up -f helm-compose.yaml
```

Uninstall your releases

```
helm compose down -f helm-compose.yaml
```

A Helm Compose file looks something like this:

```yaml
apiVersion: 1.1

storage:
  name: mycompose
  type: local # default
  path: .hcstate # default

releases:
  wordpress:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
  wordpress2:
    chart: bitnami/wordpress
    chartVersion: 15.2.22
    namespace: homepage
    createNamespace: true
  postgres:
    chart: bitnami/postgresql
    chartVersion: 12.1.9
    namespace: database
    createNamespace: true

repositories:
  bitnami: https://charts.bitnami.com/bitnami
```

All `helm-compose` commands accept the `-f` flag to pass your compose file location. Otherwise `helm-compose` will automatically look for a list of file names inside your current directory:

- helm-compose.yaml
- helm-compose.yml
- helmcompose.yaml
- helm-compose.yml
- helmcompose.yaml
- helmcompose.yml
- helmcompose
- compose.yaml
- compose.yml

Check out the [helm compose examples](https://github.com/seacrew/helm-compose/tree/main/examples).


================================================
FILE: docs/storage-providers.md
================================================
# Storage Providers

Following options are applicable regardless of the selected provider.

| Option            | Type   | Description                                                                                 | Required | Default | ApiVersion |
| ----------------- | ------ | ------------------------------------------------------------------------------------------- | -------- | ------- | ---------- |
| name              | string | Name to be used to store revisions with a storage provider.                                 | true     |         | 1.0        |
| type              | string | Type / name of the storage provider you want to use. By default local files will be stored. | false    | local   | 1.0        |
| numberOfRevisions | int    | Number of revisions to be stored and to be able to rollback to.                             | false    | 10      | 1.0        |

## Local

Stores your compose revisions locally inside the `.hcstate` directory next to your `helm-compose.yaml`.

| Option | Type   | Description                                                                                           | Required | Default  | ApiVersion |
| ------ | ------ | ----------------------------------------------------------------------------------------------------- | -------- | -------- | ---------- |
| path   | string | The directory path to store your revisions (Relative to the directory you execute `helm compose` in). | false    | .hcstate | 1.0        |

## Kubernetes

Stores your compose revisions similar to helm releases inside secrets in a kubernetes cluster namespace.

| Option      | Type   | Description                                        | Required | Default         | ApiVersion |
| ----------- | ------ | -------------------------------------------------- | -------- | --------------- | ---------- |
| namespace   | string | The namespace to store your revisions in.          | false    | default         | 1.0        |
| kubeconfig  | string | The path to your kubeconfig file                   | false    | ~/.kube/config  | 1.0        |
| kubecontext | string | The context to use from your specified kubeconfig. | false    | current-context | 1.0        |

## S3

Stores your compose revisions inside a s3 bucket. You will need to set your AWS credentials (access and secret key) via [environment variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) or the `~/.aws/config` file. [Official AWS documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html).


| Option           | Type   | Description                                                                                                | Required | Default                                                                      | ApiVersion |
| ---------------- | ------ | ---------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------- | ---------- |
| s3bucket         | string | Specify the bucket name to upload to and download from.                                                    | true     |                                                                              | 1.0        |
| s3prefix         | string | Specify the object prefix (directory) for the revisions to be uploaded to and downloaded from.             | false    | (root path)                                                                  | 1.0        |
| s3region         | string | Set a custom S3 region.                                                                                    | false    | By default the region will be read from the AWS_REGION environment variable. | 1.0        |
| s3endpoint       | string | Set a custom S3 endpoint / host url.                                                                       | false    | Default AWS S3 service endpoint.                                             | 1.0        |
| s3insecure       | bool   | Disable the verification of the servers certificate chain and hostname.                                    | false    | false                                                                        | 1.0        |
| s3disableSSL     | bool   | Disable the usage of SSL / https.                                                                          | false    | false                                                                        | 1.0        |
| s3forcePathStyle | bool   | Enforce to use path style. Especially useful for none AWS S3 provider which often only support path style. | false    | false                                                                        | 1.0        |

## GCS

Not yet implemented


================================================
FILE: docs/stylesheets/extra.css
================================================
:root {
  --md-primary-fg-color:        #00acc1;
}

================================================
FILE: examples/.gitignore
================================================
.hcstate
.override

================================================
FILE: examples/k8s-storage-compose.yaml
================================================
apiVersion: 1.1

storage:
  name: k8s-test
  type: kubernetes
  numberOfRevisions: 5

releases:
  k8s-nginx:
    chart: bitnami/nginx

repositories:
  bitnami: https://charts.bitnami.com/bitnami


================================================
FILE: examples/local-storage-compose.yaml
================================================
apiVersion: 1.1

storage:
  name: local
  type: local
  path: .override

releases:
  default:
    chart: bitnami/nginx
    chartVersion: 14.2.1
    wait: true

repositories:
  bitnami: https://charts.bitnami.com/bitnami


================================================
FILE: examples/s3-storage-compose.yaml
================================================
apiVersion: 1.1

storage:
  name: s3-test
  type: s3
  s3bucket: helm-compose
  s3region: eu-central-1
  s3prefix: wordpress

releases:
  wordpress:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
    namespace: wordpress
    createNamespace: true
    valueFiles:
      - ./values/wordpress.yaml
    values:
      wordpressBlogName: Awesome Site
      wordpressPassword: "${WORDPRESS_PASSWORD}}"
      mariadb.auth:
        rootPassword: "${MARIADB_ROOT_PASSWORD}}"

repositories:
  bitnami: https://charts.bitnami.com/bitnami


================================================
FILE: examples/simple-compose.yaml
================================================
apiVersion: 1.1

storage:
  name: simple
  type: local

releases:
  default:
    chart: bitnami/nginx
    chartVersion: 14.2.1
  jekyll:
    chart: bitnami/nginx
    chartVersion: 14.2.1
    values:
      cloneStaticSiteFromGit:
        enabled: true
        repository: https://github.com/jekyll/jekyll
        branch: gh-pages

repositories:
  bitnami: https://charts.bitnami.com/bitnami


================================================
FILE: examples/values/wordpress.yaml
================================================
wordpressUsername: user
wordpressEmail: user@example.com
allowEmptyPassword: false
mariadb:
  primary:
    persistence:
      enabled: false
persistence:
  enabled: false


================================================
FILE: examples/wordpress-compose.yaml
================================================
apiVersion: 1.1

storage:
  name: wordpress
  type: local

releases:
  site1:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
    namespace: site1
    createNamespace: true
    valueFiles:
      - ./values/wordpress.yaml
    values:
      wordpressBlogName: Awesome Site
      wordpressPassword: awesome
      mariadb.auth:
        rootPassword: "awesome-password"
  site2:
    chart: bitnami/wordpress
    chartVersion: 14.3.2
    namespace: site2
    createNamespace: true
    valueFiles:
      - ./values/wordpress.yaml
    values:
      wordpressBlogName: Super Awesome Site
      wordpressPassword: super-awesome
      mariadb.auth:
        rootPassword: "super-awesome-password"

repositories:
  bitnami: https://charts.bitnami.com/bitnami


================================================
FILE: go.mod
================================================
module github.com/seacrew/helm-compose

go 1.22.0

toolchain go1.22.2

require (
	github.com/Masterminds/semver v1.5.0
	github.com/aws/aws-sdk-go v1.53.7
	github.com/jwalton/go-supportscolor v1.2.0
	github.com/spf13/cobra v1.8.0
	github.com/stretchr/testify v1.9.0
	gopkg.in/yaml.v2 v2.4.0
	k8s.io/apimachinery v0.30.1
	k8s.io/client-go v0.30.1
)

require (
	github.com/google/gnostic-models v0.6.8 // indirect
	github.com/jmespath/go-jmespath v0.4.0 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
)

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/emicklei/go-restful/v3 v3.12.0 // indirect
	github.com/go-logr/logr v1.4.2 // indirect
	github.com/go-openapi/jsonpointer v0.21.0 // indirect
	github.com/go-openapi/jsonreference v0.21.0 // indirect
	github.com/go-openapi/swag v0.23.0 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang/protobuf v1.5.4 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/imdario/mergo v0.3.16 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/josharian/intern v1.0.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/mailru/easyjson v0.7.7 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/spf13/pflag v1.0.5 // indirect
	golang.org/x/net v0.25.0 // indirect
	golang.org/x/oauth2 v0.20.0 // indirect
	golang.org/x/sys v0.20.0 // indirect
	golang.org/x/term v0.20.0 // indirect
	golang.org/x/text v0.15.0 // indirect
	golang.org/x/time v0.5.0 // indirect
	google.golang.org/protobuf v1.34.1 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
	k8s.io/api v0.30.1
	k8s.io/klog/v2 v2.120.1 // indirect
	k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a // indirect
	k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
	sigs.k8s.io/yaml v1.4.0 // indirect
)


================================================
FILE: go.sum
================================================
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/aws/aws-sdk-go v1.53.5 h1:1OcVWMjGlwt7EU5OWmmEEXqaYfmX581EK317QJZXItM=
github.com/aws/aws-sdk-go v1.53.5/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go v1.53.7 h1:ZSsRYHLRxsbO2rJR2oPMz0SUkJLnBkN+1meT95B6Ixs=
github.com/aws/aws-sdk-go v1.53.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8=
github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g=
github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE=
github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY=
k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM=
k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U=
k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q=
k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 h1:SbdLaI6mM6ffDSJCadEaD4IkuPzepLDGlkd2xV0t1uA=
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a h1:zD1uj3Jf+mD4zmA7W+goE5TxDkI7OGJjBNBzq5fJtLA=
k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc=
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=


================================================
FILE: internal/compose/compose.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose

import (
	"fmt"
	"sync"

	cfg "github.com/seacrew/helm-compose/internal/config"
	prov "github.com/seacrew/helm-compose/internal/provider"
	"github.com/seacrew/helm-compose/internal/util"
)

func RunUp(config *cfg.Config) error {
	for name, url := range config.Repositories {
		if err := addHelmRepository(name, url); err != nil {
			return err
		}
	}

	previousConfig, err := prov.Load(config)
	if err != nil {
		return err
	}

	if !config.Equal(previousConfig) {
		if err := prov.Store(config); err != nil {
			return err
		}
	}

	var wg sync.WaitGroup

	for name, release := range config.Releases {
		wg.Add(1)
		go func(name string, release cfg.Release) {
			installHelmRelease(name, &release)
			wg.Done()
		}(name, release)
	}

	if previousConfig == nil {
		wg.Wait()
		return nil
	}

	for name, release := range previousConfig.Releases {
		wg.Add(1)
		go func(name string, release cfg.Release) {
			if _, ok := config.Releases[name]; ok {
				wg.Done()
				return
			}

			uninstallHelmRelease(name, &release)
			wg.Done()
		}(name, release)
	}

	wg.Wait()

	return nil
}

func RunDown(config *cfg.Config) error {
	previousConfig, err := prov.Load(config)
	if err != nil {
		return err
	}

	if previousConfig != nil {
		config = previousConfig
	}

	var wg sync.WaitGroup

	for name, release := range config.Releases {
		wg.Add(1)
		go func(name string, release cfg.Release) {
			uninstallHelmRelease(name, &release)
			wg.Done()
		}(name, release)
	}

	wg.Wait()

	return nil
}

func ListRevisions(config *cfg.Config) error {
	revisions, err := prov.List(config)
	if err != nil {
		return err
	}

	fmt.Printf("| Date             | Revision |\n")
	fmt.Printf("| ---------------- | -------- |\n")
	for _, rev := range revisions {
		fmt.Printf("| %d-%02d-%02d %02d:%02d | %8d |\n",
			rev.DateTime.Year(), rev.DateTime.Month(), rev.DateTime.Day(),
			rev.DateTime.Hour(), rev.DateTime.Minute(), rev.Revision)
	}

	return nil
}

func GetRevision(rev int, config *cfg.Config) error {
	revision, err := prov.Get(rev, config)
	if err != nil {
		return err
	}

	fmt.Printf("%s\n", *revision)

	return nil
}

func Template(config *cfg.Config, releases []string) error {
	util.PrintColors = false

	for name, url := range config.Repositories {
		if err := addHelmRepository(name, url); err != nil {
			return err
		}
	}

	for name, release := range config.Releases {
		if len(releases) == 0 {
			templateHelmRelease(name, &release)
			continue
		}

		for _, rel := range releases {
			if rel == name {
				templateHelmRelease(name, &release)
			}
		}
	}

	return nil
}


================================================
FILE: internal/compose/helm.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/Masterminds/semver"
	cfg "github.com/seacrew/helm-compose/internal/config"
	"github.com/seacrew/helm-compose/internal/util"
)

var (
	helm       = os.Getenv("HELM_BIN")
	versionRE  = regexp.MustCompile(`Version:\s*"([^"]+)"`)
	minVersion = semver.MustParse("v3.0.0")
)

type HelmCommand string

const (
	HELM_UPGRADE   HelmCommand = "upgrade"
	HELM_UNINSTALL HelmCommand = "uninstall"
	HELM_TEMPLATE  HelmCommand = "template"
)

func CompatibleHelmVersion() error {
	cmd := exec.Command(helm, "version")
	util.DebugPrint("Executing %s", strings.Join(cmd.Args, " "))

	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to run `%s version`: %v", os.Getenv("HELM_BIN"), err)
	}

	versionOutput := string(output)

	matches := versionRE.FindStringSubmatch(versionOutput)
	if matches == nil {
		return fmt.Errorf("failed to find version in output %#v", versionOutput)
	}

	helmVersion, err := semver.NewVersion(matches[1])
	if err != nil {
		return fmt.Errorf("failed to parse version %#v: %v", matches[1], err)
	}

	if minVersion.GreaterThan(helmVersion) {
		return fmt.Errorf("helm compose requires at least helm version %s", minVersion.String())
	}
	return nil
}

func addHelmRepository(name string, url string) error {
	output, err := util.Execute(helm, "repo", "add", "--force-update", name, url)

	if err != nil {
		return errors.New(output)
	}

	return nil
}

func installHelmRelease(name string, release *cfg.Release) {
	args, err := createHelmArguments(HELM_UPGRADE, name, release)
	if err != nil {
		cp := util.NewColorPrinter(name)
		cp.Printf("%s |\t\t%s", name, err)
	}

	helmExec(name, args)
}

func templateHelmRelease(name string, release *cfg.Release) {
	args, err := createHelmArguments(HELM_TEMPLATE, name, release)
	if err != nil {
		cp := util.NewColorPrinter(name)
		cp.Printf("# %s |\t\t%s", name, err)
	}

	helmExec("", args)
}

func uninstallHelmRelease(name string, release *cfg.Release) {
	var args []string

	args = append(args, "uninstall")

	if release.Namespace != "" {
		args = append(args, fmt.Sprintf("--namespace=%s", release.Namespace))
	}

	if release.KubeConfig != "" {
		args = append(args, fmt.Sprintf("--kubeconfig=%s", release.KubeConfig))
	}

	if release.KubeContext != "" {
		args = append(args, fmt.Sprintf("--kube-context=%s", release.KubeContext))
	}

	if release.DeletionStrategy != "" {
		args = append(args, fmt.Sprintf("--cascade=%s", release.DeletionStrategy))
	}

	if release.DeletionTimeout != "" {
		args = append(args, fmt.Sprintf("--timeout=%s", release.DeletionTimeout))
	}

	if release.DeletionNoHooks {
		args = append(args, "--no-hooks")
	}

	if release.KeepHistory {
		args = append(args, "--keep-history")
	}

	args = append(args, name)

	helmExec(name, args)
}

func createHelmArguments(command HelmCommand, name string, release *cfg.Release) ([]string, error) {
	var args []string

	args = append(args, string(command))

	if command == HELM_UPGRADE {
		args = append(args, "--install")
	}

	if release.ChartVersion != "" {
		args = append(args, fmt.Sprintf("--version=%s", release.ChartVersion))
	}

	if release.Namespace != "" {
		args = append(args, fmt.Sprintf("--namespace=%s", release.Namespace))
	}

	if release.ForceUpdate {
		args = append(args, "--force")
	}

	if release.HistoryMax < 0 {
		args = append(args, fmt.Sprintf("--history-max=%d", 0))
	} else if release.HistoryMax > 0 {
		args = append(args, fmt.Sprintf("--history-max=%d", release.HistoryMax))
	}

	if release.CreateNamespace {
		args = append(args, "--create-namespace")
	}

	if release.CleanUpOnFail {
		args = append(args, "--cleanup-on-fail")
	}

	if release.DependencyUpdate {
		args = append(args, "--dependency-update")
	}

	if release.SkipTLSVerify {
		args = append(args, "--insecure-skip-tls-verify")
	}

	if release.SkipCRDs {
		args = append(args, "--skip-crds")
	}

	if release.PostRenderer != "" {
		args = append(args, fmt.Sprintf("--post-renderer=%s", release.PostRenderer))
	}

	if len(release.PostRendererArgs) > 0 {
		args = append(args, fmt.Sprintf("--post-renderer-args=[%s]", strings.Join(release.PostRendererArgs, ",")))
	}

	if release.CAFile != "" {
		args = append(args, fmt.Sprintf("--ca-file=%s", release.CAFile))
	}

	if release.CertFile != "" {
		args = append(args, fmt.Sprintf("--cert-file=%s", release.CertFile))
	}

	if release.KeyFile != "" {
		args = append(args, fmt.Sprintf("--key-file=%s", release.KeyFile))
	}

	if release.Timeout != "" {
		args = append(args, fmt.Sprintf("--timeout=%s", release.Timeout))
	}

	if release.Wait {
		args = append(args, "--wait")
	}

	if release.KubeConfig != "" {
		args = append(args, fmt.Sprintf("--kubeconfig=%s", release.KubeConfig))
	}

	if release.KubeContext != "" {
		args = append(args, fmt.Sprintf("--kube-context=%s", release.KubeContext))
	}

	for _, file := range release.ValueFiles {
		args = append(args, fmt.Sprintf("--values=%s", file))
	}

	var jsonValues []string
	for key := range release.Values {
		data := util.ConvertJson(release.Values[key])
		values, err := json.Marshal(data)
		if err != nil {
			return nil, err
		}

		jsonValues = append(jsonValues, fmt.Sprintf("%s=%s", key, values))
	}

	if len(jsonValues) > 0 {
		args = append(args, fmt.Sprintf("--set-json=%s", strings.Join(jsonValues, ",")))
	}

	args = append(args, name)
	args = append(args, release.Chart)

	return args, nil
}

func helmExec(name string, args []string) {
	cp := util.NewColorPrinter(name)
	output, _ := util.Execute(helm, args...)

	scanner := bufio.NewScanner(strings.NewReader(output))
	for scanner.Scan() {
		if len(name) == 0 {
			fmt.Printf("%s\n", scanner.Text())
		} else {
			cp.Printf("%s |\t\t%s", name, scanner.Text())
		}
	}

	err := scanner.Err()

	if err != nil {
		cp.Printf(err.Error())
	}
}


================================================
FILE: internal/config/config.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config

import (
	"bufio"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path/filepath"

	"github.com/Masterminds/semver"
	"gopkg.in/yaml.v2"
)

var (
	V1_0 = semver.MustParse("1.0")
	V1_1 = semver.MustParse("1.1")
)

func findComposeConfig() []string {
	var files []string
	filenames := []string{
		"helm-compose.yaml",
		"helm-compose.yml",
		"helmcompose.yaml",
		"helm-compose.yml",
		"helmcompose.yaml",
		"helmcompose.yml",
		"helmcompose",
		"compose.yaml",
		"compose.yml",
	}

	filepath.WalkDir(".", func(s string, d fs.DirEntry, e error) error {
		file := filepath.Base(s)

		for _, filename := range filenames {
			if file == filename {
				files = append(files, file)
			}
		}
		return nil
	})
	return files
}

func ParseComposeFile(filename string) (*Config, error) {
	var files []string
	if filename == "" {
		files = findComposeConfig()
	} else if filename == "-" {
		reader := bufio.NewReader(os.Stdin)

		data := []byte{}
		for {
			b, err := reader.ReadBytes('\n')
			if err == io.EOF {
				break
			} else if err != nil {
				return nil, err
			}

			data = append(data, b...)
		}

		return parseComposeData(data)
	} else if _, err := os.Stat(filename); err != nil {
		return nil, fmt.Errorf("provided compose file not found")
	} else {
		files = []string{filename}
	}

	if len(files) == 0 {
		return nil, fmt.Errorf("no compose file found")
	}

	if len(files) > 1 {
		return nil, fmt.Errorf("expected only one compose file but found multiple: %v", files)
	}

	file, err := os.ReadFile(files[0])
	if err != nil {
		return nil, err
	}

	return parseComposeData(file)
}

func parseComposeData(data []byte) (*Config, error) {
	config := Config{}
	err := yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}

	if err := validateCompose(&config); err != nil {
		return nil, err
	}

	return &config, nil
}

func validateCompose(config *Config) error {
	if config.Version == "" {
		return fmt.Errorf("missing apiVersion in config")
	}

	version, err := semver.NewVersion(config.Version)
	if err != nil {
		return fmt.Errorf("failed to parse apiVersion: %s", config.Version)
	}

	if version.LessThan(V1_0) {
		return fmt.Errorf("helm compose requires at least apiVersion 1.0 but got %s", config.Version)
	}

	if err := validateComposeFeatures(version, config); err != nil {
		return err
	}

	return nil
}

func validateComposeFeatures(version *semver.Version, config *Config) error {
	if err := validateCompose1_1(version, config); err != nil {
		return fmt.Errorf("apiVersion 1.1+ necessary: %s", err)
	}

	return nil
}

func validateCompose1_1(version *semver.Version, config *Config) error {
	if version.GreaterThan(V1_0) {
		return nil
	}

	for name, release := range config.Releases {
		if release.Wait {
			return fmt.Errorf("trying to use 'wait' in release '%s'", name)
		}
	}

	return nil
}


================================================
FILE: internal/config/config_test.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config

import (
	"log"
	"testing"
)

func TestParseSimpleConfig(t *testing.T) {
	config, err := ParseComposeFile("../../examples/simple-compose.yaml")

	if err != nil {
		log.Fatal(err)
	}

	if config.Storage.Name != "simple" {
		log.Fatalf("Was expecting revision name 'simple' but got '%s'", config.Storage.Name)
	}

	if config.Storage.Type != Local {
		log.Fatalf("Was expecting revision provider type '%s' but got '%s'", Local, config.Storage.Type)
	}

	if len(config.Releases) != 2 {
		log.Fatalf("Was expecting 2 release but got %d", len(config.Releases))
	}
}


================================================
FILE: internal/config/types.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config

import "reflect"

type ProviderType string

const (
	Local      ProviderType = "local"
	Kubernetes ProviderType = "kubernetes"
	S3         ProviderType = "s3"
)

type Config struct {
	Version      string             `yaml:"apiVersion,omitempty"`
	Storage      Storage            `yaml:"storage,omitempty"`
	Releases     map[string]Release `yaml:"releases,omitempty"`
	Repositories map[string]string  `yaml:"repositories,omitempty"`
}

type Release struct {
	Name             string                 `yaml:"name,omitempty"`
	Chart            string                 `yaml:"chart,omitempty"`
	ChartVersion     string                 `yaml:"chartVersion,omitempty"`
	Namespace        string                 `yaml:"namespace,omitempty"`
	ForceUpdate      bool                   `yaml:"forceUpdate,omitempty"`
	HistoryMax       int                    `yaml:"historyMax,omitempty"`
	CreateNamespace  bool                   `yaml:"createNamespace,omitempty"`
	CleanUpOnFail    bool                   `yaml:"cleanupOnFail,omitempty"`
	DependencyUpdate bool                   `yaml:"dependencyUpdate,omitempty"`
	SkipTLSVerify    bool                   `yaml:"skipTlsVerify,omitempty"`
	SkipCRDs         bool                   `yaml:"skipCrds,omitempty"`
	PostRenderer     string                 `yaml:"postRenderer,omitempty"`
	PostRendererArgs []string               `yaml:"postRendererArgs,omitempty"`
	KubeConfig       string                 `yaml:"kubeconfig,omitempty"`
	KubeContext      string                 `yaml:"kubecontext,omitempty"`
	CAFile           string                 `yaml:"caFile,omitempty"`
	CertFile         string                 `yaml:"certFile,omitempty"`
	KeyFile          string                 `yaml:"keyFile,omitempty"`
	Timeout          string                 `yaml:"timeout,omitempty"`
	Wait             bool                   `yaml:"wait,omitempty"`
	Values           map[string]interface{} `yaml:"values,omitempty"`
	ValueFiles       []string               `yaml:"valueFiles,omitempty"`

	// Uninstall flags
	DeletionStrategy string `yaml:"deletionStrategy,omitempty"`
	DeletionTimeout  string `yaml:"deletionTimeout,omitempty"`
	DeletionNoHooks  bool   `yaml:"deletionNoHooks,omitempty"`
	KeepHistory      bool   `yaml:"keepHistory,omitempty"`
}

type Storage struct {
	Type              ProviderType `yaml:"type,omitempty"`
	Name              string       `yaml:"name,omitempty"`
	NumberOfRevisions int          `yaml:"numberOfRevisions,omitempty"`
	// Local storage fields
	Path string `yaml:"path,omitempty"`
	// K8s storage fields
	Namespace   string `yaml:"namespace,omitempty"`
	KubeConfig  string `yaml:"kubeconfig,omitempty"`
	KubeContext string `yaml:"kubecontext,omitempty"`
	// S3 storage fields
	S3Bucket         string `yaml:"s3bucket,omitempty"`
	S3Prefix         string `yaml:"s3prefix,omitempty"`
	S3Region         string `yaml:"s3region,omitempty"`
	S3Endpoint       string `yaml:"s3endpoint,omitempty"`
	S3Insecure       bool   `yaml:"s3insecure,omitempty"`
	S3DisableSSL     bool   `yaml:"s3disableSSL,omitempty"`
	S3ForcePathStyle bool   `yaml:"s3forcePathStyle,omitempty"`
}

func (c *Config) Equal(o *Config) bool {
	if o == nil {
		return false
	}

	if c.Version != o.Version {
		return false
	}

	if c.Storage != o.Storage {
		return false
	}

	if len(c.Releases) != len(o.Releases) {
		return false
	}

	if !reflect.DeepEqual(c.Releases, o.Releases) {
		return false
	}

	if len(c.Repositories) != len(o.Repositories) {
		return false
	}

	for key, value := range c.Repositories {
		if val, ok := o.Repositories[key]; !ok || val != value {
			return false
		}
	}

	return true
}


================================================
FILE: internal/config/types_test.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestConfigEqualVersion(t *testing.T) {
	cfg := Config{Version: "v1.0"}
	otherCfg := Config{Version: "v1.0"}
	assert.True(t, cfg.Equal(&otherCfg))

	cfg = Config{Version: "v1.0"}
	otherCfg = Config{Version: "v1.1"}
	assert.False(t, cfg.Equal(&otherCfg))

}

func TestConfigEqualStorage(t *testing.T) {
	cfg := Config{
		Storage: Storage{
			Type: Local,
			Name: "mycompose",
			Path: ".state",
		},
	}
	assert.True(t, cfg.Equal(&cfg))

	otherCfg := Config{
		Storage: Storage{
			Type: Local,
			Name: "mycompose",
		},
	}
	assert.False(t, cfg.Equal(&otherCfg))
}

func TestConfigEqualRepositories(t *testing.T) {
	cfg := Config{
		Repositories: map[string]string{},
	}

	otherCfg := Config{
		Repositories: map[string]string{
			"bitnami": "https://charts.bitnami.com/bitnami",
		},
	}
	assert.False(t, cfg.Equal(&otherCfg))

	cfg = Config{
		Repositories: map[string]string{
			"bitnami": "https://charts.bitnami.com/bitnami",
		},
	}

	otherCfg = Config{
		Repositories: map[string]string{
			"bitnami": "https://charts.bitnami.com/bitnami",
		},
	}
	assert.True(t, cfg.Equal(&otherCfg))

	cfg = Config{
		Repositories: map[string]string{
			"bitnami": "https://charts.bitnami.com/bitnami",
		},
	}

	otherCfg = Config{
		Repositories: map[string]string{
			"bitnami": "https://charts.bitnami.net/bitnami",
		},
	}
	assert.False(t, cfg.Equal(&otherCfg))
}

func TestConfigEqualReleases(t *testing.T) {
	cfg := Config{
		Releases: map[string]Release{},
	}

	otherCfg := Config{
		Releases: map[string]Release{
			"wordpress": {
				Chart:        "bitnami/wordpress",
				ChartVersion: "14.2.1",
			},
		},
	}
	assert.False(t, cfg.Equal(&otherCfg))

	cfg = Config{
		Releases: map[string]Release{
			"wordpress": {
				Chart:        "bitnami/wordpress",
				ChartVersion: "14.2.1",
				Values: map[string]interface{}{
					"wordpressBlogName": "my-site",
				},
			},
		},
	}

	otherCfg = Config{
		Releases: map[string]Release{
			"wordpress": {
				Chart:        "bitnami/wordpress",
				ChartVersion: "14.2.1",
				Values: map[string]interface{}{
					"wordpressBlogName": "my-site",
				},
			},
		},
	}
	assert.True(t, cfg.Equal(&otherCfg))

	cfg = Config{
		Releases: map[string]Release{
			"wordpress": {
				Chart:        "bitnami/wordpress",
				ChartVersion: "14.2.1",
				Values: map[string]interface{}{
					"wordpressBlogName": "my-site",
				},
			},
		},
	}

	otherCfg = Config{
		Releases: map[string]Release{
			"wordpress": {
				Chart:        "bitnami/wordpress",
				ChartVersion: "14.2.1",
				Values: map[string]interface{}{
					"wordpressBlogName": "my-new-site",
				},
			},
		},
	}
	assert.False(t, cfg.Equal(&otherCfg))
}


================================================
FILE: internal/provider/kubernetes.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package provider

import (
	"context"
	"fmt"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strconv"

	b64 "encoding/base64"

	cfg "github.com/seacrew/helm-compose/internal/config"
	"github.com/seacrew/helm-compose/internal/util"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	k8sSecretNameFormat  = "helm.compose.%s.v%d"
	k8sSecretNamePattern = "^helm.compose.%s.v(\\d+)$"
)

type KubernetesProvider struct {
	name              string
	numberOfRevisions int
	namespace         string
	client            *kubernetes.Clientset
	listOptions       *metav1.ListOptions
}

func newKubernetesProvider(providerConfig *cfg.Storage) (*KubernetesProvider, error) {
	namespace := providerConfig.Namespace
	if len(namespace) == 0 {
		namespace = "default"
	}

	kubeconfig := providerConfig.KubeConfig
	if len(kubeconfig) == 0 {
		homedir, err := os.UserHomeDir()
		if err != nil {
			return nil, err
		}

		kubeconfig = filepath.Join(
			homedir, ".kube", "config",
		)
	}

	var err error
	var config *rest.Config

	if len(providerConfig.KubeContext) == 0 {
		config, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, &clientcmd.ConfigOverrides{CurrentContext: providerConfig.KubeContext}).ClientConfig()
		if err != nil {
			return nil, err
		}
	} else {
		config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
		if err != nil {
			return nil, err
		}

	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	labelSelector := metav1.LabelSelector{MatchLabels: map[string]string{"app.kubernetes.io/managed-by": "Helm-Compose", "helm-compose/name": providerConfig.Name}}
	listOptions := metav1.ListOptions{
		LabelSelector: labels.Set(labelSelector.MatchLabels).String(),
	}

	provider := &KubernetesProvider{
		name:              providerConfig.Name,
		numberOfRevisions: providerConfig.NumberOfRevisions,
		namespace:         namespace,
		client:            clientset,
		listOptions:       &listOptions,
	}

	return provider, nil
}

func (p KubernetesProvider) load() (*[]byte, error) {
	secrets, err := p.client.CoreV1().Secrets(p.namespace).List(context.Background(), *p.listOptions)
	if err != nil {
		return nil, err
	}

	if len(secrets.Items) == 0 {
		return nil, nil
	}

	_, _, latest, err := p.minMax(secrets.Items)
	if err != nil {
		return nil, err
	}

	if latest == nil {
		return nil, nil
	}

	data, err := b64.StdEncoding.DecodeString(string(latest.Data["compose"]))
	if err != nil {
		return nil, err
	}

	return &data, nil
}

func (p KubernetesProvider) store(encodedConfig *string) error {
	secrets, err := p.client.CoreV1().Secrets(p.namespace).List(context.Background(), *p.listOptions)
	if err != nil {
		return err
	}

	minimum, maximum, _, err := p.minMax(secrets.Items)
	if err != nil {
		return err
	}

	revision := maximum + 1

	data := b64.StdEncoding.EncodeToString([]byte(*encodedConfig))

	secret := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      fmt.Sprintf(k8sSecretNameFormat, p.name, revision),
			Namespace: p.namespace,
			Labels: map[string]string{
				"app.kubernetes.io/managed-by": "Helm-Compose",
				"helm-compose/name":            p.name,
			},
		},
		Immutable: util.NewBool(true),
		Data: map[string][]byte{
			"compose": []byte(data),
		},
		Type: "helm-compose/revision.v1",
	}

	_, err = p.client.CoreV1().Secrets(p.namespace).Create(context.Background(), secret, metav1.CreateOptions{})
	if err != nil {
		return err
	}

	if minimum > revision-p.numberOfRevisions {
		return nil
	}

	for i := minimum; i <= revision-p.numberOfRevisions; i++ {
		if err = p.client.CoreV1().Secrets(p.namespace).Delete(context.Background(), fmt.Sprintf(k8sSecretNameFormat, p.name, i), metav1.DeleteOptions{}); err != nil {
			fmt.Println(err)
		}
	}

	return nil
}

func (p KubernetesProvider) list() ([]ComposeRevision, error) {
	secrets, err := p.client.CoreV1().Secrets(p.namespace).List(context.Background(), metav1.ListOptions{})
	if err != nil {
		return nil, err
	}

	if len(secrets.Items) == 0 {
		return nil, nil
	}

	revisions := []ComposeRevision{}

	r, err := regexp.Compile(fmt.Sprintf(k8sSecretNamePattern, p.name))
	if err != nil {
		return nil, err
	}

	for _, item := range secrets.Items {
		matches := r.FindStringSubmatch(item.Name)
		if len(matches) == 0 {
			continue
		}

		revision, err := strconv.Atoi(matches[1])
		if err != nil {
			return nil, err
		}

		revisions = append(revisions, ComposeRevision{revision, item.CreationTimestamp.Time})
	}

	return revisions, nil
}

func (p KubernetesProvider) get(revision int) (*[]byte, error) {
	secret, err := p.client.CoreV1().Secrets(p.namespace).Get(context.Background(), fmt.Sprintf(k8sSecretNameFormat, p.name, revision), metav1.GetOptions{})
	if err != nil {
		return nil, err
	}

	data, err := b64.StdEncoding.DecodeString(string(secret.Data["compose"]))
	if err != nil {
		return nil, err
	}

	return &data, nil
}

func (p KubernetesProvider) minMax(secrets []corev1.Secret) (int, int, *corev1.Secret, error) {
	if len(secrets) == 0 {
		return 0, 0, nil, nil
	}

	minimum, maximum := math.MaxInt, 0

	r, err := regexp.Compile(fmt.Sprintf(k8sSecretNamePattern, p.name))
	if err != nil {
		return -1, -1, nil, err
	}

	var latest corev1.Secret

	for _, secret := range secrets {
		matches := r.FindStringSubmatch(secret.Name)
		if len(matches) == 0 {
			continue
		}

		revision, err := strconv.Atoi(matches[1])
		if err != nil {
			return -1, -1, nil, err
		}

		if revision > maximum {
			maximum = revision
			latest = secret
		}

		if revision < minimum {
			minimum = revision
		}
	}

	return minimum, maximum, &latest, nil
}


================================================
FILE: internal/provider/kubernetes_test.go
================================================
package provider

import (
	"testing"
)

func TestStoreAndLoad(t *testing.T) {
	//k8s, err := newKubernetes(&config.Storage{
	//	Name: "hello-world",
	//})

	//if err != nil {
	//	log.Fatal(err)
	//}

	//_, err = k8s.load()
	//if err != nil {
	//	log.Fatal(err)
	//}
}


================================================
FILE: internal/provider/local.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package provider

import (
	"fmt"
	"os"
	"regexp"
	"strconv"

	cfg "github.com/seacrew/helm-compose/internal/config"
	"github.com/seacrew/helm-compose/internal/util"
)

const (
	pathFormat = "%s/%s-%d"
)

type LocalProvider struct {
	name              string
	path              string
	numberOfRevisions int
}

func newLocalProvider(providerConfig *cfg.Storage) *LocalProvider {
	provider := LocalProvider{
		name:              providerConfig.Name,
		path:              providerConfig.Path,
		numberOfRevisions: providerConfig.NumberOfRevisions,
	}

	if len(provider.path) == 0 {
		provider.path = ".hcstate"
	}

	return &provider
}

func (p LocalProvider) load() (*[]byte, error) {
	if _, err := os.Stat(p.path); os.IsNotExist(err) {
		if err := os.Mkdir(p.path, os.ModePerm); err != nil {
			return nil, err
		}
	}

	_, maximum, err := p.minMax(p.name, p.path)
	if err != nil {
		return nil, err
	}

	if maximum == 0 {
		return nil, nil
	}

	file, err := os.ReadFile(fmt.Sprintf(pathFormat, p.path, p.name, maximum))
	if err != nil {
		return nil, err
	}

	return &file, nil
}

func (p LocalProvider) store(encodedConfig *string) error {
	minimum, maximum, err := p.minMax(p.name, p.path)
	if err != nil {
		return err
	}

	maximum = maximum + 1

	if err := os.WriteFile(fmt.Sprintf(pathFormat, p.path, p.name, maximum), []byte(*encodedConfig), 0644); err != nil {
		return err
	}

	if minimum > maximum-p.numberOfRevisions {
		return nil
	}

	for i := minimum; i <= maximum-p.numberOfRevisions; i++ {
		if err := os.Remove(fmt.Sprintf(pathFormat, p.path, p.name, i)); err != nil {
			fmt.Println(err)
		}
	}

	return nil
}

func (p LocalProvider) list() ([]ComposeRevision, error) {
	files, err := os.ReadDir(p.path)
	if err != nil {
		return nil, err
	}

	r, _ := regexp.Compile(fmt.Sprintf("^%s-(\\d+)$", p.name))

	revisions := []ComposeRevision{}
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		matches := r.FindStringSubmatch(file.Name())
		if len(matches) == 0 {
			continue
		}

		revision, err := strconv.Atoi(matches[1])
		if err != nil {
			return nil, err
		}

		info, err := file.Info()
		if err != nil {
			return nil, err
		}

		revisions = append(revisions, ComposeRevision{revision, info.ModTime()})
	}

	return revisions, nil
}

func (p LocalProvider) get(revision int) (*[]byte, error) {
	file, err := os.ReadFile(fmt.Sprintf(pathFormat, p.path, p.name, revision))
	if err != nil {
		return nil, err
	}

	return &file, nil
}

func (p LocalProvider) minMax(name string, path string) (int, int, error) {
	files, err := os.ReadDir(path)
	if err != nil {
		return -1, -1, err
	}

	r, _ := regexp.Compile(fmt.Sprintf("^%s-(\\d+)$", name))

	revisions := []int{}
	for _, file := range files {
		if file.IsDir() {
			continue
		}

		matches := r.FindStringSubmatch(file.Name())
		if len(matches) == 0 {
			continue
		}

		revision, err := strconv.Atoi(matches[1])
		if err != nil {
			return -1, -1, nil
		}

		revisions = append(revisions, revision)
	}

	minimum, maximum := util.MinMax(revisions)
	return minimum, maximum, nil
}


================================================
FILE: internal/provider/providers.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package provider

import (
	"fmt"
	"sort"
	"time"

	cfg "github.com/seacrew/helm-compose/internal/config"
	"github.com/seacrew/helm-compose/internal/util"
	"gopkg.in/yaml.v2"
)

type ComposeRevision struct {
	Revision int
	DateTime time.Time
}

type Provider interface {
	load() (*[]byte, error)
	store(encodedConfig *string) error
	list() ([]ComposeRevision, error)
	get(revision int) (*[]byte, error)
}

var provider Provider

func getProvider(providerConfig *cfg.Storage) (Provider, error) {
	if provider != nil {
		return provider, nil
	}

	if providerConfig.NumberOfRevisions <= 0 {
		providerConfig.NumberOfRevisions = 10
	}

	var err error

	switch providerConfig.Type {
	case cfg.Local:
		provider = newLocalProvider(providerConfig)
		return provider, nil
	case cfg.Kubernetes:
		provider, err = newKubernetesProvider(providerConfig)
		return provider, err
	case cfg.S3:
		provider, err = newS3Provider(providerConfig)
		return provider, err
	default:
		return nil, fmt.Errorf("unknown provider type %q", providerConfig.Type)
	}
}

func Load(config *cfg.Config) (*cfg.Config, error) {
	provider, err := getProvider(&config.Storage)
	if err != nil {
		return nil, err
	}

	data, err := provider.load()
	if err != nil {
		return nil, err
	}

	if data == nil {
		return nil, nil
	}

	prevConfig, err := util.DecodeComposeConfig(string(*data))
	if err != nil {
		return nil, err
	}

	return prevConfig, nil
}

func Store(config *cfg.Config) error {
	provider, err := getProvider(&config.Storage)
	if err != nil {
		return err
	}

	encodedConfig, err := util.EncodeComposeConfig(config)

	if err != nil {
		return err
	}

	if err := provider.store(&encodedConfig); err != nil {
		return err
	}

	return nil
}

func List(config *cfg.Config) ([]ComposeRevision, error) {
	provider, err := getProvider(&config.Storage)
	if err != nil {
		return nil, err
	}

	revisions, err := provider.list()
	if err != nil {
		return nil, err
	}

	sort.Slice(revisions, func(i, j int) bool {
		return revisions[i].Revision < revisions[j].Revision
	})

	return revisions, nil
}

func Get(revision int, config *cfg.Config) (*string, error) {
	provider, err := getProvider(&config.Storage)
	if err != nil {
		return nil, err
	}

	data, err := provider.get(revision)
	if err != nil {
		return nil, err
	}

	revConfig, err := util.DecodeComposeConfig(string(*data))
	if err != nil {
		return nil, err
	}

	b, err := yaml.Marshal(revConfig)
	if err != nil {
		return nil, err
	}

	revYaml := string(b)
	return &revYaml, nil
}


================================================
FILE: internal/provider/s3.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package provider

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"math"
	"net/http"
	"os"
	"regexp"
	"strconv"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	cfg "github.com/seacrew/helm-compose/internal/config"
	"github.com/seacrew/helm-compose/internal/util"
)

const (
	s3ObjectNameFormat  = "%s.v%d.hcstate"
	s3ObjectNamePattern = "%s.v(\\d+).hcstate$"
)

type S3Provider struct {
	name              string
	numberOfRevisions int
	bucket            *string
	prefix            *string
	lister            *s3.S3
	uploader          *s3manager.Uploader
	downloader        *s3manager.Downloader
}

func newS3Provider(providerConfig *cfg.Storage) (*S3Provider, error) {
	config := &aws.Config{}

	if len(providerConfig.S3Region) > 0 {
		config.Region = &providerConfig.S3Region
	} else if os.Getenv("AWS_REGION") != "" {
		config.Region = aws.String(os.Getenv("AWS_REGION"))
	} else {
		return nil, fmt.Errorf("AWS region not specified")
	}

	if len(providerConfig.S3Endpoint) > 0 {
		config.Endpoint = &providerConfig.S3Endpoint
	}

	if providerConfig.S3DisableSSL {
		config.DisableSSL = util.NewBool(true)
	}

	if providerConfig.S3Insecure {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		config.HTTPClient = &http.Client{Transport: tr}
	}

	if providerConfig.S3ForcePathStyle {
		config.S3ForcePathStyle = util.NewBool(true)
	}

	sess, err := session.NewSession(config)
	if err != nil {
		return nil, err
	}

	provider := &S3Provider{
		name:              providerConfig.Name,
		numberOfRevisions: providerConfig.NumberOfRevisions,
		bucket:            &providerConfig.S3Bucket,
		prefix:            &providerConfig.S3Prefix,
		lister:            s3.New(sess),
		uploader:          s3manager.NewUploader(sess),
		downloader:        s3manager.NewDownloader(sess),
	}

	return provider, nil
}

func (p S3Provider) load() (*[]byte, error) {
	resp, err := p.lister.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: p.bucket, Prefix: p.prefix})
	if err != nil {
		return nil, err
	}

	if len(resp.Contents) == 0 {
		return nil, nil
	}

	_, _, latest, err := p.minMax(resp.Contents)
	if err != nil {
		return nil, err
	}

	if latest == nil {
		return nil, nil
	}

	buff := &aws.WriteAtBuffer{}
	if _, err := p.downloader.Download(buff, &s3.GetObjectInput{Bucket: p.bucket, Key: latest.Key}); err != nil {
		return nil, err
	}

	content := buff.Bytes()
	return &content, nil
}

func (p S3Provider) store(encodedConfig *string) error {
	resp, err := p.lister.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: p.bucket, Prefix: p.prefix})
	if err != nil {
		return err
	}

	minimum, maximum, _, err := p.minMax(resp.Contents)
	if err != nil {
		return err
	}

	revision := maximum + 1

	key := fmt.Sprintf(s3ObjectNameFormat, p.name, revision)
	if len(*p.prefix) > 0 {
		key = fmt.Sprintf("%s/%s", *p.prefix, key)
	}

	reader := bytes.NewReader([]byte(*encodedConfig))

	_, err = p.uploader.Upload(
		&s3manager.UploadInput{
			Bucket: p.bucket,
			Key:    &key,
			Body:   reader,
		})

	if err != nil {
		return err
	}

	if minimum > revision-p.numberOfRevisions {
		return nil
	}

	for i := minimum; i < revision-p.numberOfRevisions; i++ {
		key := fmt.Sprintf(s3ObjectNameFormat, p.name, i)
		if len(*p.prefix) > 0 {
			key = fmt.Sprintf("%s/%s", *p.prefix, key)
		}

		_, err := p.lister.DeleteObject(&s3.DeleteObjectInput{Bucket: p.bucket, Key: &key})
		if err != nil {
			fmt.Println(err)
		}
	}

	return nil
}

func (p S3Provider) list() ([]ComposeRevision, error) {
	resp, err := p.lister.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: p.bucket, Prefix: p.prefix})
	if err != nil {
		return nil, err
	}

	var revisions []ComposeRevision

	r, err := regexp.Compile(fmt.Sprintf(s3ObjectNamePattern, p.name))
	if err != nil {
		return nil, err
	}

	for _, item := range resp.Contents {
		matches := r.FindStringSubmatch(*item.Key)
		if len(matches) == 0 {
			continue
		}

		revision, err := strconv.Atoi(matches[1])
		if err != nil {
			return nil, err
		}

		revisions = append(revisions, ComposeRevision{revision, *item.LastModified})
	}

	return revisions, nil
}

func (p S3Provider) get(revision int) (*[]byte, error) {
	key := fmt.Sprintf(s3ObjectNameFormat, p.name, revision)
	if len(*p.prefix) > 0 {
		key = fmt.Sprintf("%s/%s", *p.prefix, key)
	}

	buff := &aws.WriteAtBuffer{}
	_, err := p.downloader.Download(buff,
		&s3.GetObjectInput{
			Bucket: p.bucket,
			Key:    &key,
		})

	if err != nil {
		return nil, err
	}

	content := buff.Bytes()
	return &content, nil
}

func (p S3Provider) minMax(objects []*s3.Object) (int, int, *s3.Object, error) {
	if len(objects) == 0 {
		return 0, 0, nil, nil
	}

	minimum, maximum := math.MaxInt, 0
	var latest *s3.Object

	r, err := regexp.Compile(fmt.Sprintf(s3ObjectNamePattern, p.name))
	if err != nil {
		return -1, -1, nil, err
	}

	for _, item := range objects {
		matches := r.FindStringSubmatch(*item.Key)

		if len(matches) == 0 {
			continue
		}

		revision, err := strconv.Atoi(matches[1])
		if err != nil {
			return -1, -1, nil, err
		}

		if revision < minimum {
			minimum = revision
		}

		if revision > maximum {
			maximum = revision
			latest = item
		}
	}

	return minimum, maximum, latest, nil
}


================================================
FILE: internal/provider/s3_test.go
================================================
package provider

import (
	"log"
	"testing"

	cfg "github.com/seacrew/helm-compose/internal/config"
)

func TestS3List(t *testing.T) {
	config := &cfg.Storage{
		Name:              "test",
		NumberOfRevisions: 5,
		S3Bucket:          "helm-compose",
		S3Prefix:          "test",
		S3Region:          "eu-central-1",
	}

	provider, err := newS3Provider(config)

	if err != nil {
		log.Fatal(err)
	}

	revisions, err := provider.list()
	if err != nil {
		log.Fatal(err)
	}

	log.Fatal(revisions)
}


================================================
FILE: internal/util/colors.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util

import (
	"fmt"
	"hash/fnv"
	"strconv"

	"github.com/jwalton/go-supportscolor"
)

type ColorPrinter struct {
	colorFunc func(...interface{}) string
}

var PrintColors = true

func NewColorPrinter(s string) *ColorPrinter {
	c := hashColor(s)
	return &ColorPrinter{
		colorFunc: c,
	}
}

func (c ColorPrinter) Printf(format string, a ...any) {
	if PrintColors {
		fmt.Printf(c.colorFunc(format)+"\n", a...)
		return
	}

	fmt.Printf(format+"\n", a...)
}

var colorFuncs [](func(...interface{}) string) = [](func(...interface{}) string){
	color("%s"), // fallback
	color("\033[1;32m%s\033[0m"),
	color("\033[1;33m%s\033[0m"),
	color("\033[1;34m%s\033[0m"),
	color("\033[1;35m%s\033[0m"),
	color("\033[1;36m%s\033[0m"),
	color("\033[1;90m%s\033[0m"),
	color("\033[1;92m%s\033[0m"),
	color("\033[1;93m%s\033[0m"),
	color("\033[1;94m%s\033[0m"),
	color("\033[1;95m%s\033[0m"),
	color("\033[1;96m%s\033[0m"),
}

func color(colorString string) func(...interface{}) string {
	sprint := func(args ...interface{}) string {
		return fmt.Sprintf(colorString,
			fmt.Sprint(args...))
	}
	return sprint
}

func hashColor(s string) func(...interface{}) string {
	if !supportscolor.Stdout().SupportsColor {
		return colorFuncs[0]
	}

	h := fnv.New32a()
	h.Write([]byte(s))

	hash := fmt.Sprint(h.Sum32())
	for {
		subtotal := 0
		for _, r := range hash {
			value, _ := strconv.Atoi(string(r))
			subtotal += value
		}

		if subtotal < len(colorFuncs) {
			return colorFuncs[subtotal]
		}

		hash = fmt.Sprint(subtotal)
	}
}


================================================
FILE: internal/util/encoding.go
================================================
/*
Copyright 2016 The Kubernetes Authors All Rights Reserved

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"io/ioutil"

	c "github.com/seacrew/helm-compose/internal/config"
	"gopkg.in/yaml.v2"
)

var b64 = base64.StdEncoding

var magicGzip = []byte{0x1f, 0x8b, 0x08}

// encodeComposeConfig encodes the config file returning a base64 encoded
// gzipped string representation, or error.
func EncodeComposeConfig(config *c.Config) (string, error) {
	b, err := yaml.Marshal(config)
	if err != nil {
		return "", err
	}
	var buf bytes.Buffer
	w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
	if err != nil {
		return "", err
	}
	if _, err = w.Write(b); err != nil {
		return "", err
	}
	w.Close()

	return b64.EncodeToString(buf.Bytes()), nil
}

// decodeComposeConfig decodes the bytes of data into a compose
// config. Data must contain a base64 encoded gzipped string of a
// valid release, otherwise an error is returned.
func DecodeComposeConfig(data string) (*c.Config, error) {
	// base64 decode string
	b, err := b64.DecodeString(data)
	if err != nil {
		return nil, err
	}

	r, err := gzip.NewReader(bytes.NewReader(b))
	if err != nil {
		return nil, err
	}
	defer r.Close()
	b2, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, err
	}
	b = b2

	var config c.Config
	// unmarshal release object bytes
	if err := yaml.Unmarshal(b, &config); err != nil {
		return nil, err
	}
	return &config, nil
}


================================================
FILE: internal/util/util.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util

import (
	"fmt"
	"math"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

var (
	re = regexp.MustCompile(`\$\{(.*?)\}`)
)

func IsDebug() bool {
	return os.Getenv("HELM_DEBUG") == "true"
}

func DebugPrint(format string, a ...interface{}) {
	if IsDebug() {
		fmt.Printf(format+"\n", a...)
	}
}

func Execute(command string, args ...string) (string, error) {
	cmd := exec.Command(command, args...)
	output, err := cmd.CombinedOutput()

	if output == nil {
		return "", err
	}

	text := string(output)

	if len(text) > 5 && text[:6] == "Error:" {
		text = strings.TrimSpace(text[6:])
	}

	return text, err
}

func ConvertJson(obj interface{}) interface{} {
	switch c := obj.(type) {
	case map[interface{}]interface{}:
		m := map[string]interface{}{}
		for k, v := range c {
			m[k.(string)] = ConvertJson(v)
		}
		return m
	case []interface{}:
		for k, v := range c {
			c[k] = ConvertJson(v)
		}
	case string:
		str := obj.(string)
		matches := re.FindStringSubmatch(str)
		for _, match := range matches {
			str = strings.Replace(str, "${"+match+"}", os.Getenv(match), 1)
		}

		return str
	}
	return obj
}

func MinMax(ints []int) (int, int) {
	if len(ints) == 0 {
		return 0, 0
	}

	minimum, maximum := math.MaxInt, 0

	for _, i := range ints {
		if i > maximum {
			maximum = i
		}

		if i < minimum {
			minimum = i
		}
	}

	return minimum, maximum
}

func NewBool(b bool) *bool {
	return &b
}


================================================
FILE: main.go
================================================
/*
Copyright © 2023 The Helm Compose Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main

import "github.com/seacrew/helm-compose/cmd"

func main() {
	cmd.Execute()
}


================================================
FILE: mkdocs.yaml
================================================
site_name: Helm Compose Documentation
docs_dir: docs
repo_url: https://github.com/seacrew/helm-compose

nav:
  - Home: "index.md"
  - "Quick Start": "quick-start.md"
  - "Key Features and Use Cases": "key-features-and-use-cases.md"
  - "Compose File Reference": "compose-file-reference.md"
  - "Storage Providers": "storage-providers.md"
  - Commands:
      - "helm compose up": "commands/up.md"
      - "helm compose down": "commands/down.md"
      - "helm compose list": "commands/list.md"
      - "helm compose get": "commands/get.md"
      - "helm compose template": "commands/template.md"

theme:
  name: material
  logo: https://user-images.githubusercontent.com/18513179/239762487-ec236277-f782-4da3-8a23-692ad017bb92.svg
  favicon: https://user-images.githubusercontent.com/18513179/212496536-76db8b48-fe7c-42a3-b851-da1f281e1ad6.svg
  palette:
    - media: "(prefers-color-scheme: light)"
      scheme: default
      primary: custom
      accent: custom
      toggle:
        icon: material/brightness-7
        name: Switch to dark mode
    - media: "(prefers-color-scheme: dark)"
      scheme: slate
      primary: custom
      accent: custom
      toggle:
        icon: material/brightness-4
        name: Switch to light mode
  features:
    - navigation.sections
    - navigation.expand
    - navigation.path
    - navigation.indexes
    - navigation.footer
    - content.code.copy

extra_css:
  - stylesheets/extra.css

plugins:
  - search
  - autolinks
  - drawio_file
  - git-revision-date-localized

markdown_extensions:
  - abbr
  - tables
  - pymdownx.highlight:
      anchor_linenums: true
      line_spans: __span
      pygments_lang_class: true
  - pymdownx.inlinehilite
  - pymdownx.snippets
  - pymdownx.superfences

extra:
  copyright: Copyright &copy; 2023 Seacrew Authors
  social:
    - icon: fontawesome/brands/github
      link: https://github.com/seacrew
  version:
    provider: mike
  consent:
    title: Cookie consent
    description: >-
      We use cookies to recognize your repeated visits and preferences, as well
      as to measure the effectiveness of our documentation and whether users
      find what they're searching for. With your consent, you're helping us to
      make our documentation better.


================================================
FILE: plugin.yaml
================================================
name: compose
version: 1.3.0
usage: Compose is a helm plugin to define and manage multiple helm releases as single entity.
command: $HELM_PLUGIN_DIR/bin/compose
hooks:
  install: $HELM_PLUGIN_DIR/scripts/install.sh
  update: $HELM_PLUGIN_DIR/scripts/install.sh


================================================
FILE: scripts/install.sh
================================================
#!/usr/bin/env bash

if [ "${HELM_DEBUG:-}" = "1" ] || [ "${HELM_DEBUG:-}" = "true" ] || [ -n "${HELM_SECRETS_DEBUG+x}" ]; then
    set -x
fi

PLUGIN_NAME="helm-compose"
GITHUB_REPO="seacrew/helm-compose"

[ -z "$HELM_BIN" ] && HELM_BIN=$(command -v helm)

[ -z "$HELM_HOME" ] && HELM_HOME=$(helm env | grep 'HELM_DATA_HOME' | cut -d '=' -f2 | tr -d '"')

# Convert HELM_BIN and HELM_PLUGIN_DIR to unix if cygpath is
# available. This is the case when using MSYS2 or Cygwin
# on Windows where helm returns a Windows path but we
# need a Unix path

if command -v cygpath >/dev/null 2>&1; then
  HELM_BIN="$(cygpath -u "${HELM_BIN}")"
  HELM_PLUGIN_DIR="$(cygpath -u "${HELM_PLUGIN_DIR}")"
fi

mkdir -p "$HELM_HOME"

: "${HELM_PLUGIN_DIR:="$HELM_HOME/plugins/helm-compose"}"

if [[ $SKIP_BIN_INSTALL == "1" ]]; then
  echo "Skipping binary install"
  exit
fi

# which mode is the common installer script running in
SCRIPT_MODE="install"
if [ "$1" = "-u" ]; then
  SCRIPT_MODE="update"
fi

# initArch discovers the architecture for this system.
initArch() {
  ARCH=$(uname -m)
  case $ARCH in
    armv5*) ARCH="armv5";;
    armv6*) ARCH="armv6";;
    armv7*) ARCH="armv7";;
    aarch64) ARCH="arm64";;
    x86) ARCH="386";;
    x86_64) ARCH="amd64";;
    i686) ARCH="386";;
    i386) ARCH="386";;
  esac
}

# initOS discovers the operating system for this system.
initOS() {
  OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')

  case "$OS" in
    # Msys support
    msys*) OS='windows';;
    # Minimalist GNU for Windows
    mingw*) OS='windows';;
    darwin) OS='macos';;
  esac
}

# verifySupported checks that the os/arch combination is supported for
# binary builds.
verifySupported() {
  local supported="linux-amd64\nlinux-arm64\nfreebsd-amd64\nmacos-amd64\nmacos-arm64\nwindows-amd64"
  if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then
    echo "No prebuild binary for ${OS}-${ARCH}."
    exit 1
  fi

  if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then
    echo "Either curl or wget is required"
    exit 1
  fi
}

# getDownloadURL checks the latest available version.
getDownloadURL() {
  version=$(git -C "$HELM_PLUGIN_DIR" describe --tags --exact-match 2>/dev/null || :)
  if [ "$SCRIPT_MODE" = "install" ] && [ -n "$version" ]; then
    DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/download/$version/helm-compose-$OS-$ARCH.tgz"
  else
    DOWNLOAD_URL="https://github.com/$GITHUB_REPO/releases/latest/download/helm-compose-$OS-$ARCH.tgz"
  fi
}

# downloadFile downloads the latest binary package and also the checksum
# for that binary.
downloadFile() {
  HELM_TMP="$(mktemp -d -t ${PLUGIN_NAME}-XXXXXX)"
  PLUGIN_TMP_FILE="${HELM_TMP}/${PLUGIN_NAME}.tgz"
  echo "Downloading $DOWNLOAD_URL"
  if command -v curl >/dev/null 2>&1; then
    curl -sSf -L "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE"
  elif command -v wget >/dev/null 2>&1; then
    wget -q -O - "$DOWNLOAD_URL" >"$PLUGIN_TMP_FILE"
  fi
}

# installFile verifies the SHA256 for the file, then unpacks and
# installs it.
installFile() {
  tar xzf "$PLUGIN_TMP_FILE" -C "$HELM_TMP"
  HELM_TMP_BIN="$HELM_TMP/compose/bin/compose"
  if [ "${OS}" = "windows" ]; then
    HELM_TMP_BIN="$HELM_TMP_BIN.exe"
  fi
  echo "Preparing to install into ${HELM_PLUGIN_DIR}"
  mkdir -p "$HELM_PLUGIN_DIR/bin"
  cp "$HELM_TMP_BIN" "$HELM_PLUGIN_DIR/bin"
}

# fail_trap is executed if an error occurs.
fail_trap() {
  result=$?
  if [ "$result" != "0" ]; then
    echo "Failed to install $PLUGIN_NAME"
    echo "\tFor support, go to https://github.com/$GITHUB_REPO/helm-compose."
  fi
  exit $result
}

# testVersion tests the installed client to make sure it is working.
testVersion() {
  echo "$PLUGIN_NAME installed into $HELM_PLUGIN_DIR"
  #"${HELM_PLUGIN_DIR}/bin/compose" -h
}

# Execution

#Stop execution on any error
trap "fail_trap" EXIT
initArch
initOS
verifySupported
getDownloadURL
downloadFile
installFile
testVersion
Download .txt
gitextract_hj9wc7s7/

├── .github/
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── build.yaml
│       ├── ci.yaml
│       ├── dependabot-automerge.yaml
│       ├── docs.yaml
│       └── release.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── LICENSES/
│   ├── helm/
│   │   └── LICENSE
│   ├── helm-diff/
│   │   └── LICENSE
│   └── helm-template/
│       └── LICENSE
├── Makefile
├── README.md
├── cmd/
│   ├── down.go
│   ├── get.go
│   ├── list.go
│   ├── root.go
│   ├── template.go
│   └── up.go
├── docs/
│   ├── commands/
│   │   ├── down.md
│   │   ├── get.md
│   │   ├── list.md
│   │   ├── template.md
│   │   └── up.md
│   ├── compose-file-reference.md
│   ├── index.md
│   ├── key-features-and-use-cases.md
│   ├── quick-start.md
│   ├── storage-providers.md
│   └── stylesheets/
│       └── extra.css
├── examples/
│   ├── .gitignore
│   ├── k8s-storage-compose.yaml
│   ├── local-storage-compose.yaml
│   ├── s3-storage-compose.yaml
│   ├── simple-compose.yaml
│   ├── values/
│   │   └── wordpress.yaml
│   └── wordpress-compose.yaml
├── go.mod
├── go.sum
├── internal/
│   ├── compose/
│   │   ├── compose.go
│   │   └── helm.go
│   ├── config/
│   │   ├── config.go
│   │   ├── config_test.go
│   │   ├── types.go
│   │   └── types_test.go
│   ├── provider/
│   │   ├── kubernetes.go
│   │   ├── kubernetes_test.go
│   │   ├── local.go
│   │   ├── providers.go
│   │   ├── s3.go
│   │   └── s3_test.go
│   └── util/
│       ├── colors.go
│       ├── encoding.go
│       └── util.go
├── main.go
├── mkdocs.yaml
├── plugin.yaml
└── scripts/
    └── install.sh
Download .txt
SYMBOL INDEX (91 symbols across 22 files)

FILE: cmd/down.go
  function init (line 45) | func init() {

FILE: cmd/get.go
  function init (line 57) | func init() {

FILE: cmd/list.go
  function init (line 44) | func init() {

FILE: cmd/root.go
  function Execute (line 38) | func Execute() {
  function init (line 45) | func init() {

FILE: cmd/template.go
  function init (line 44) | func init() {

FILE: cmd/up.go
  function init (line 44) | func init() {

FILE: internal/compose/compose.go
  function RunUp (line 27) | func RunUp(config *cfg.Config) error {
  function RunDown (line 78) | func RunDown(config *cfg.Config) error {
  function ListRevisions (line 103) | func ListRevisions(config *cfg.Config) error {
  function GetRevision (line 120) | func GetRevision(rev int, config *cfg.Config) error {
  function Template (line 131) | func Template(config *cfg.Config, releases []string) error {

FILE: internal/compose/helm.go
  type HelmCommand (line 39) | type HelmCommand
  constant HELM_UPGRADE (line 42) | HELM_UPGRADE   HelmCommand = "upgrade"
  constant HELM_UNINSTALL (line 43) | HELM_UNINSTALL HelmCommand = "uninstall"
  constant HELM_TEMPLATE (line 44) | HELM_TEMPLATE  HelmCommand = "template"
  function CompatibleHelmVersion (line 47) | func CompatibleHelmVersion() error {
  function addHelmRepository (line 74) | func addHelmRepository(name string, url string) error {
  function installHelmRelease (line 84) | func installHelmRelease(name string, release *cfg.Release) {
  function templateHelmRelease (line 94) | func templateHelmRelease(name string, release *cfg.Release) {
  function uninstallHelmRelease (line 104) | func uninstallHelmRelease(name string, release *cfg.Release) {
  function createHelmArguments (line 142) | func createHelmArguments(command HelmCommand, name string, release *cfg....
  function helmExec (line 250) | func helmExec(name string, args []string) {

FILE: internal/config/config.go
  function findComposeConfig (line 35) | func findComposeConfig() []string {
  function ParseComposeFile (line 62) | func ParseComposeFile(filename string) (*Config, error) {
  function parseComposeData (line 104) | func parseComposeData(data []byte) (*Config, error) {
  function validateCompose (line 118) | func validateCompose(config *Config) error {
  function validateComposeFeatures (line 139) | func validateComposeFeatures(version *semver.Version, config *Config) er...
  function validateCompose1_1 (line 147) | func validateCompose1_1(version *semver.Version, config *Config) error {

FILE: internal/config/config_test.go
  function TestParseSimpleConfig (line 23) | func TestParseSimpleConfig(t *testing.T) {

FILE: internal/config/types.go
  type ProviderType (line 20) | type ProviderType
  constant Local (line 23) | Local      ProviderType = "local"
  constant Kubernetes (line 24) | Kubernetes ProviderType = "kubernetes"
  constant S3 (line 25) | S3         ProviderType = "s3"
  type Config (line 28) | type Config struct
    method Equal (line 86) | func (c *Config) Equal(o *Config) bool {
  type Release (line 35) | type Release struct
  type Storage (line 66) | type Storage struct

FILE: internal/config/types_test.go
  function TestConfigEqualVersion (line 24) | func TestConfigEqualVersion(t *testing.T) {
  function TestConfigEqualStorage (line 35) | func TestConfigEqualStorage(t *testing.T) {
  function TestConfigEqualRepositories (line 54) | func TestConfigEqualRepositories(t *testing.T) {
  function TestConfigEqualReleases (line 93) | func TestConfigEqualReleases(t *testing.T) {

FILE: internal/provider/kubernetes.go
  constant k8sSecretNameFormat (line 40) | k8sSecretNameFormat  = "helm.compose.%s.v%d"
  constant k8sSecretNamePattern (line 41) | k8sSecretNamePattern = "^helm.compose.%s.v(\\d+)$"
  type KubernetesProvider (line 44) | type KubernetesProvider struct
    method load (line 107) | func (p KubernetesProvider) load() (*[]byte, error) {
    method store (line 134) | func (p KubernetesProvider) store(encodedConfig *string) error {
    method list (line 183) | func (p KubernetesProvider) list() ([]ComposeRevision, error) {
    method get (line 217) | func (p KubernetesProvider) get(revision int) (*[]byte, error) {
    method minMax (line 231) | func (p KubernetesProvider) minMax(secrets []corev1.Secret) (int, int,...
  function newKubernetesProvider (line 52) | func newKubernetesProvider(providerConfig *cfg.Storage) (*KubernetesProv...

FILE: internal/provider/kubernetes_test.go
  function TestStoreAndLoad (line 7) | func TestStoreAndLoad(t *testing.T) {

FILE: internal/provider/local.go
  constant pathFormat (line 29) | pathFormat = "%s/%s-%d"
  type LocalProvider (line 32) | type LocalProvider struct
    method load (line 52) | func (p LocalProvider) load() (*[]byte, error) {
    method store (line 76) | func (p LocalProvider) store(encodedConfig *string) error {
    method list (line 101) | func (p LocalProvider) list() ([]ComposeRevision, error) {
    method get (line 136) | func (p LocalProvider) get(revision int) (*[]byte, error) {
    method minMax (line 145) | func (p LocalProvider) minMax(name string, path string) (int, int, err...
  function newLocalProvider (line 38) | func newLocalProvider(providerConfig *cfg.Storage) *LocalProvider {

FILE: internal/provider/providers.go
  type ComposeRevision (line 28) | type ComposeRevision struct
  type Provider (line 33) | type Provider interface
  function getProvider (line 42) | func getProvider(providerConfig *cfg.Storage) (Provider, error) {
  function Load (line 68) | func Load(config *cfg.Config) (*cfg.Config, error) {
  function Store (line 91) | func Store(config *cfg.Config) error {
  function List (line 110) | func List(config *cfg.Config) ([]ComposeRevision, error) {
  function Get (line 128) | func Get(revision int, config *cfg.Config) (*string, error) {

FILE: internal/provider/s3.go
  constant s3ObjectNameFormat (line 37) | s3ObjectNameFormat  = "%s.v%d.hcstate"
  constant s3ObjectNamePattern (line 38) | s3ObjectNamePattern = "%s.v(\\d+).hcstate$"
  type S3Provider (line 41) | type S3Provider struct
    method load (line 99) | func (p S3Provider) load() (*[]byte, error) {
    method store (line 127) | func (p S3Provider) store(encodedConfig *string) error {
    method list (line 177) | func (p S3Provider) list() ([]ComposeRevision, error) {
    method get (line 207) | func (p S3Provider) get(revision int) (*[]byte, error) {
    method minMax (line 228) | func (p S3Provider) minMax(objects []*s3.Object) (int, int, *s3.Object...
  function newS3Provider (line 51) | func newS3Provider(providerConfig *cfg.Storage) (*S3Provider, error) {

FILE: internal/provider/s3_test.go
  function TestS3List (line 10) | func TestS3List(t *testing.T) {

FILE: internal/util/colors.go
  type ColorPrinter (line 26) | type ColorPrinter struct
    method Printf (line 39) | func (c ColorPrinter) Printf(format string, a ...any) {
  function NewColorPrinter (line 32) | func NewColorPrinter(s string) *ColorPrinter {
  function color (line 63) | func color(colorString string) func(...interface{}) string {
  function hashColor (line 71) | func hashColor(s string) func(...interface{}) string {

FILE: internal/util/encoding.go
  function EncodeComposeConfig (line 34) | func EncodeComposeConfig(config *c.Config) (string, error) {
  function DecodeComposeConfig (line 55) | func DecodeComposeConfig(data string) (*c.Config, error) {

FILE: internal/util/util.go
  function IsDebug (line 31) | func IsDebug() bool {
  function DebugPrint (line 35) | func DebugPrint(format string, a ...interface{}) {
  function Execute (line 41) | func Execute(command string, args ...string) (string, error) {
  function ConvertJson (line 58) | func ConvertJson(obj interface{}) interface{} {
  function MinMax (line 82) | func MinMax(ints []int) (int, int) {
  function NewBool (line 102) | func NewBool(b bool) *bool {

FILE: main.go
  function main (line 20) | func main() {
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (162K chars).
[
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 833,
    "preview": "<!--- Provide a general summary of your changes in the Title above -->\n\n## Description\n\n<!--- Describe your changes in d"
  },
  {
    "path": ".github/workflows/build.yaml",
    "chars": 1308,
    "preview": "name: Build\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - main\n    paths-ignore:\n      - .github/**\n      - do"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 575,
    "preview": "name: CI\n\non:\n  workflow_dispatch:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  ci:\n    runs-on: ubuntu-latest\n  "
  },
  {
    "path": ".github/workflows/dependabot-automerge.yaml",
    "chars": 739,
    "preview": "name: Dependabot auto-approve\non: pull_request\n\npermissions:\n  pull-requests: write\n\njobs:\n  dependabot:\n    runs-on: ub"
  },
  {
    "path": ".github/workflows/docs.yaml",
    "chars": 1662,
    "preview": "name: Deploy docs\non:\n  push:\n    branches:\n      - main\n    paths:\n      - docs/**\n      - mkdocs.yaml\n  workflow_dispa"
  },
  {
    "path": ".github/workflows/release.yaml",
    "chars": 1630,
    "preview": "name: Create Release\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version number of the ne"
  },
  {
    "path": ".gitignore",
    "chars": 317,
    "preview": ".vscode\n.hcstate\n\nvendor/\nbin/\nbuild/\nrelease/\n\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# T"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 516,
    "preview": "# Changes since 1.3.0\n# 1.3.0\n- feat: add templating command\n- chore: upgrade to golang 1.22\n\n# 1.2.0\n- feat: add wait o"
  },
  {
    "path": "LICENSE",
    "chars": 11356,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "LICENSES/helm/LICENSE",
    "chars": 11409,
    "preview": "=== https://github.com/helm/helm ===\n\n                                 Apache License\n                           Version"
  },
  {
    "path": "LICENSES/helm-diff/LICENSE",
    "chars": 11404,
    "preview": "=== https://github.com/databus23/helm-diff ===\n\n                                 Apache License\n                        "
  },
  {
    "path": "LICENSES/helm-template/LICENSE",
    "chars": 1132,
    "preview": "=== https://github.com/technosophos/helm-template ===\n\nHelm Template Plugin\nCopyright (C) 2016, Matt Butcher\n\nPermission"
  },
  {
    "path": "Makefile",
    "chars": 1638,
    "preview": "HELM_HOME := $(shell bash -c 'eval $$(helm env); echo $$HELM_PLUGINS')\nLDFLAGS := -s -w\n\n.PHONY: install\ninstall: build\n"
  },
  {
    "path": "README.md",
    "chars": 2966,
    "preview": "# ⚠️ Project Discontinued\n\nThis open-source project is **no longer actively maintained**.\n\n- No new features or bug fixe"
  },
  {
    "path": "cmd/down.go",
    "chars": 1234,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "cmd/get.go",
    "chars": 1471,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "cmd/list.go",
    "chars": 1194,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "cmd/root.go",
    "chars": 1448,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "cmd/template.go",
    "chars": 1227,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "cmd/up.go",
    "chars": 1272,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "docs/commands/down.md",
    "chars": 425,
    "preview": "# helm compose down\n\nUninstall all releases defined in your `helm-compose.yaml`\n\n## Usage\n\nThe following command will un"
  },
  {
    "path": "docs/commands/get.md",
    "chars": 334,
    "preview": "# helm compose get\n\nGet a previous revision of your `helm-compose.yaml`\n\n## Usage\n\nThe following command will return the"
  },
  {
    "path": "docs/commands/list.md",
    "chars": 409,
    "preview": "# helm compose list\n\nList previous revisions of your `helm-compose.yaml`\n\n## Usage\n\n```\nhelm compose list [flags]\n```\n\n#"
  },
  {
    "path": "docs/commands/template.md",
    "chars": 442,
    "preview": "# helm compose template\n\nTemplate all kubernetes resources for the releases specified in your `helm-compose.yaml` and pr"
  },
  {
    "path": "docs/commands/up.md",
    "chars": 454,
    "preview": "# helm compose up\n\nInstall all releases and repositories defined in your `helm-compose.yaml`\n\n## Usage\n\nThe following co"
  },
  {
    "path": "docs/compose-file-reference.md",
    "chars": 7744,
    "preview": "# Compose File Reference\n\n## storage\n\n```yaml\nstorage:\n  name: my-compose\n  type: local\n  numberOfRevisions: 10\n```\n\n| O"
  },
  {
    "path": "docs/index.md",
    "chars": 1175,
    "preview": "![helm-compose-banner](https://user-images.githubusercontent.com/18513179/212496531-1d166236-ed88-411d-8403-ad1f94d28846"
  },
  {
    "path": "docs/key-features-and-use-cases.md",
    "chars": 3297,
    "preview": "# Key Features and Use Cases\n\nThe main idea behind `helm-compose` is to control / configure all helm related options as "
  },
  {
    "path": "docs/quick-start.md",
    "chars": 1632,
    "preview": "# Quick Start\n\n## Installation\n\nInstall a specific version (recommended). Click [here](https://github.com/seacrew/helm-c"
  },
  {
    "path": "docs/storage-providers.md",
    "chars": 4773,
    "preview": "# Storage Providers\n\nFollowing options are applicable regardless of the selected provider.\n\n| Option            | Type  "
  },
  {
    "path": "docs/stylesheets/extra.css",
    "chars": 50,
    "preview": ":root {\n  --md-primary-fg-color:        #00acc1;\n}"
  },
  {
    "path": "examples/.gitignore",
    "chars": 18,
    "preview": ".hcstate\n.override"
  },
  {
    "path": "examples/k8s-storage-compose.yaml",
    "chars": 195,
    "preview": "apiVersion: 1.1\n\nstorage:\n  name: k8s-test\n  type: kubernetes\n  numberOfRevisions: 5\n\nreleases:\n  k8s-nginx:\n    chart: "
  },
  {
    "path": "examples/local-storage-compose.yaml",
    "chars": 220,
    "preview": "apiVersion: 1.1\n\nstorage:\n  name: local\n  type: local\n  path: .override\n\nreleases:\n  default:\n    chart: bitnami/nginx\n "
  },
  {
    "path": "examples/s3-storage-compose.yaml",
    "chars": 533,
    "preview": "apiVersion: 1.1\n\nstorage:\n  name: s3-test\n  type: s3\n  s3bucket: helm-compose\n  s3region: eu-central-1\n  s3prefix: wordp"
  },
  {
    "path": "examples/simple-compose.yaml",
    "chars": 390,
    "preview": "apiVersion: 1.1\n\nstorage:\n  name: simple\n  type: local\n\nreleases:\n  default:\n    chart: bitnami/nginx\n    chartVersion: "
  },
  {
    "path": "examples/values/wordpress.yaml",
    "chars": 171,
    "preview": "wordpressUsername: user\nwordpressEmail: user@example.com\nallowEmptyPassword: false\nmariadb:\n  primary:\n    persistence:\n"
  },
  {
    "path": "examples/wordpress-compose.yaml",
    "chars": 752,
    "preview": "apiVersion: 1.1\n\nstorage:\n  name: wordpress\n  type: local\n\nreleases:\n  site1:\n    chart: bitnami/wordpress\n    chartVers"
  },
  {
    "path": "go.mod",
    "chars": 2210,
    "preview": "module github.com/seacrew/helm-compose\n\ngo 1.22.0\n\ntoolchain go1.22.2\n\nrequire (\n\tgithub.com/Masterminds/semver v1.5.0\n\t"
  },
  {
    "path": "go.sum",
    "chars": 16251,
    "preview": "github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=\ngithub.com/Masterminds/semver v1.5."
  },
  {
    "path": "internal/compose/compose.go",
    "chars": 3152,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/compose/helm.go",
    "chars": 6439,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/config/config.go",
    "chars": 3386,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/config/config_test.go",
    "chars": 1146,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/config/types.go",
    "chars": 4216,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/config/types_test.go",
    "chars": 3304,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/provider/kubernetes.go",
    "chars": 6392,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/provider/kubernetes_test.go",
    "chars": 269,
    "preview": "package provider\n\nimport (\n\t\"testing\"\n)\n\nfunc TestStoreAndLoad(t *testing.T) {\n\t//k8s, err := newKubernetes(&config.Stor"
  },
  {
    "path": "internal/provider/local.go",
    "chars": 3636,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/provider/providers.go",
    "chars": 3075,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/provider/s3.go",
    "chars": 5881,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/provider/s3_test.go",
    "chars": 497,
    "preview": "package provider\n\nimport (\n\t\"log\"\n\t\"testing\"\n\n\tcfg \"github.com/seacrew/helm-compose/internal/config\"\n)\n\nfunc TestS3List("
  },
  {
    "path": "internal/util/colors.go",
    "chars": 2091,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "internal/util/encoding.go",
    "chars": 1967,
    "preview": "/*\nCopyright 2016 The Kubernetes Authors All Rights Reserved\n\nLicensed under the Apache License, Version 2.0 (the \"Licen"
  },
  {
    "path": "internal/util/util.go",
    "chars": 1973,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "main.go",
    "chars": 661,
    "preview": "/*\nCopyright © 2023 The Helm Compose Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may no"
  },
  {
    "path": "mkdocs.yaml",
    "chars": 2247,
    "preview": "site_name: Helm Compose Documentation\ndocs_dir: docs\nrepo_url: https://github.com/seacrew/helm-compose\n\nnav:\n  - Home: \""
  },
  {
    "path": "plugin.yaml",
    "chars": 261,
    "preview": "name: compose\nversion: 1.3.0\nusage: Compose is a helm plugin to define and manage multiple helm releases as single entit"
  },
  {
    "path": "scripts/install.sh",
    "chars": 3905,
    "preview": "#!/usr/bin/env bash\n\nif [ \"${HELM_DEBUG:-}\" = \"1\" ] || [ \"${HELM_DEBUG:-}\" = \"true\" ] || [ -n \"${HELM_SECRETS_DEBUG+x}\" "
  }
]

About this extraction

This page contains the full source code of the seacrew/helm-compose GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (147.8 KB), approximately 42.9k tokens, and a symbol index with 91 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!