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.
---

[](https://github.com/seacrew/helm-compose/actions/workflows/build.yaml)
[](https://goreportcard.com/report/github.com/seacrew/helm-compose)
[](https://sonarcloud.io/summary/new_code?id=seacrew_helm-compose)
[](https://sonarcloud.io/summary/new_code?id=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
================================================

[](https://github.com/seacrew/helm-compose/actions/workflows/build.yaml)
[](https://goreportcard.com/report/github.com/seacrew/helm-compose)
[](https://sonarcloud.io/summary/new_code?id=seacrew_helm-compose)
[](https://sonarcloud.io/summary/new_code?id=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 © 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
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
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": ". 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.