mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-06 16:38:46 +01:00
Compare commits
1 Commits
stable
...
appsetdocs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37efa1880b |
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: ['bug', 'triage/pending']
|
labels: 'bug'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@ assignees: ''
|
|||||||
|
|
||||||
Checklist:
|
Checklist:
|
||||||
|
|
||||||
- [ ] I've searched in the docs and FAQ for my answer: https://bit.ly/argocd-faq.
|
* [ ] I've searched in the docs and FAQ for my answer: https://bit.ly/argocd-faq.
|
||||||
- [ ] I've included steps to reproduce the bug.
|
* [ ] I've included steps to reproduce the bug.
|
||||||
- [ ] I've pasted the output of `argocd version`.
|
* [ ] I've pasted the output of `argocd version`.
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
name: Enhancement proposal
|
name: Enhancement proposal
|
||||||
about: Propose an enhancement for this project
|
about: Propose an enhancement for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: ['enhancement', 'triage/pending']
|
labels: 'enhancement'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
What change you think needs making.
|
What change you think needs making.
|
||||||
@@ -16,4 +15,4 @@ Please give examples of your use case, e.g. when would you use this.
|
|||||||
|
|
||||||
# Proposal
|
# Proposal
|
||||||
|
|
||||||
How do you think this should be implemented?
|
How do you think this should be implemented?
|
||||||
14
.github/ISSUE_TEMPLATE/new_dev_tool.md
vendored
14
.github/ISSUE_TEMPLATE/new_dev_tool.md
vendored
@@ -2,17 +2,17 @@
|
|||||||
name: New Dev Tool Request
|
name: New Dev Tool Request
|
||||||
about: This is a request for adding a new tool for setting up a dev environment.
|
about: This is a request for adding a new tool for setting up a dev environment.
|
||||||
title: ''
|
title: ''
|
||||||
labels: ['component:dev-env', 'triage/pending']
|
labels: ''
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
Checklist:
|
Checklist:
|
||||||
|
|
||||||
- [ ] I am willing to maintain this tool, or have another Argo CD maintainer who is.
|
* [ ] I am willing to maintain this tool, or have another Argo CD maintainer who is.
|
||||||
- [ ] I have another Argo CD maintainer who is willing to help maintain this tool (there needs to be at least two maintainers willing to maintain this tool)
|
* [ ] I have another Argo CD maintainer who is willing to help maintain this tool (there needs to be at least two maintainers willing to maintain this tool)
|
||||||
- [ ] I have a lead sponsor who is a core Argo CD maintainer
|
* [ ] I have a lead sponsor who is a core Argo CD maintainer
|
||||||
- [ ] There is a PR which adds said tool - this is so that the maintainers can assess the impact of having this in the tree
|
* [ ] There is a PR which adds said tool - this is so that the maintainers can assess the impact of having this in the tree
|
||||||
- [ ] I have given a motivation why this should be added
|
* [ ] I have given a motivation why this should be added
|
||||||
|
|
||||||
### The proposer
|
### The proposer
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ Checklist:
|
|||||||
|
|
||||||
### Motivation
|
### Motivation
|
||||||
|
|
||||||
<!-- Why this tool would be useful to have in the tree. -->
|
<!-- Why this tool would be useful to have in the tree. -->
|
||||||
|
|
||||||
### Link to PR (Optional)
|
### Link to PR (Optional)
|
||||||
|
|
||||||
|
|||||||
84
.github/ISSUE_TEMPLATE/release.md
vendored
84
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -9,78 +9,18 @@ assignees: ''
|
|||||||
Target RC1 date: ___. __, ____
|
Target RC1 date: ___. __, ____
|
||||||
Target GA date: ___. __, ____
|
Target GA date: ___. __, ____
|
||||||
|
|
||||||
## RC1 Release Checklist
|
- [ ] 1wk before feature freeze post in #argo-contributors that PRs must be merged by DD-MM-YYYY to be included in the release - ask approvers to drop items from milestone they can’t merge
|
||||||
|
|
||||||
- [ ] 1wk before feature freeze post in #argo-contributors that PRs must be merged by DD-MM-YYYY to be included in the release - ask approvers to drop items from milestone they can't merge
|
|
||||||
- [ ] At least two days before RC1 date, draft RC blog post and submit it for review (or delegate this task)
|
- [ ] At least two days before RC1 date, draft RC blog post and submit it for review (or delegate this task)
|
||||||
- [ ] Create new release branch (or delegate this task to an Approver)
|
|
||||||
- [ ] Add the release branch to ReadTheDocs
|
|
||||||
- [ ] Cut RC1 (or delegate this task to an Approver and coordinate timing)
|
- [ ] Cut RC1 (or delegate this task to an Approver and coordinate timing)
|
||||||
- [ ] Run the [Init ArgoCD Release workflow](https://github.com/argoproj/argo-cd/actions/workflows/init-release.yaml) from the release branch
|
- [ ] Create new release branch
|
||||||
- [ ] Review and merge the generated version bump PR
|
- [ ] Add the release branch to ReadTheDocs
|
||||||
- [ ] Run `./hack/trigger-release.sh` to push the release tag
|
- [ ] Confirm that tweet and blog post are ready
|
||||||
- [ ] Monitor the [Publish ArgoCD Release workflow](https://github.com/argoproj/argo-cd/actions/workflows/release.yaml)
|
- [ ] Trigger the release
|
||||||
- [ ] Verify the release on [GitHub releases](https://github.com/argoproj/argo-cd/releases)
|
- [ ] After the release is finished, publish tweet and blog post
|
||||||
- [ ] Verify the container image on [Quay.io](https://quay.io/repository/argoproj/argocd?tab=tags)
|
- [ ] Post in #argo-cd and #argo-announcements with lots of emojis announcing the release and requesting help testing
|
||||||
- [ ] Confirm the new version appears in [Read the Docs](https://argo-cd.readthedocs.io/)
|
- [ ] Monitor support channels for issues, cherry-picking bugfixes and docs fixes as appropriate (or delegate this task to an Approver and coordinate timing)
|
||||||
- [ ] Verify the docs release build in https://app.readthedocs.org/projects/argo-cd/ succeeded and retry if failed (requires an Approver with admin creds to readthedocs)
|
- [ ] At release date, evaluate if any bugs justify delaying the release. If not, cut the release (or delegate this task to an Approver and coordinate timing)
|
||||||
- [ ] Announce RC1 release
|
- [ ] If unreleased changes are on the release branch for {current minor version minus 3}, cut a final patch release for that series (or delegate this task to an Approver and coordinate timing)
|
||||||
- [ ] Confirm that tweet and blog post are ready
|
- [ ] After the release, post in #argo-cd that the {current minor version minus 3} has reached EOL (example: https://cloud-native.slack.com/archives/C01TSERG0KZ/p1667336234059729)
|
||||||
- [ ] Publish tweet and blog post
|
|
||||||
- [ ] Post in #argo-cd and #argo-announcements requesting help testing:
|
|
||||||
```
|
|
||||||
:mega: Argo CD v{MAJOR}.{MINOR}.{PATCH}-rc{RC_NUMBER} is OUT NOW! :argocd::tada:
|
|
||||||
|
|
||||||
Please go through the following resources to know more about the release:
|
|
||||||
|
|
||||||
Release notes: https://github.com/argoproj/argo-cd/releases/tag/v{VERSION}
|
|
||||||
Blog: {BLOG_POST_URL}
|
|
||||||
|
|
||||||
We'd love your help testing this release candidate! Please try it out in your environments and report any issues you find. This helps us ensure a stable GA release.
|
|
||||||
|
|
||||||
Thanks to all the folks who spent their time contributing to this release in any way possible!
|
|
||||||
```
|
|
||||||
- [ ] Monitor support channels for issues, cherry-picking bugfixes and docs fixes as appropriate during the RC period (or delegate this task to an Approver and coordinate timing)
|
|
||||||
|
|
||||||
## GA Release Checklist
|
|
||||||
|
|
||||||
- [ ] At GA release date, evaluate if any bugs justify delaying the release
|
|
||||||
- [ ] Prepare for EOL version (version that is 3 releases old)
|
|
||||||
- [ ] If unreleased changes are on the release branch for {current minor version minus 3}, cut a final patch release for that series (or delegate this task to an Approver and coordinate timing)
|
|
||||||
- [ ] Edit the final patch release on GitHub and add the following notice at the top:
|
|
||||||
```markdown
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> **END OF LIFE NOTICE**
|
|
||||||
>
|
|
||||||
> This is the final release of the {EOL_SERIES} release series. As of {GA_DATE}, this version has reached end of life and will no longer receive bug fixes or security updates.
|
|
||||||
>
|
|
||||||
> **Action Required**: Please upgrade to a [supported version](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) (v{SUPPORTED_VERSION_1}, v{SUPPORTED_VERSION_2}, or v{NEW_VERSION}).
|
|
||||||
```
|
|
||||||
- [ ] Cut GA release (or delegate this task to an Approver and coordinate timing)
|
|
||||||
- [ ] Run the [Init ArgoCD Release workflow](https://github.com/argoproj/argo-cd/actions/workflows/init-release.yaml) from the release branch
|
|
||||||
- [ ] Review and merge the generated version bump PR
|
|
||||||
- [ ] Run `./hack/trigger-release.sh` to push the release tag
|
|
||||||
- [ ] Monitor the [Publish ArgoCD Release workflow](https://github.com/argoproj/argo-cd/actions/workflows/release.yaml)
|
|
||||||
- [ ] Verify the release on [GitHub releases](https://github.com/argoproj/argo-cd/releases)
|
|
||||||
- [ ] Verify the container image on [Quay.io](https://quay.io/repository/argoproj/argocd?tab=tags)
|
|
||||||
- [ ] Verify the `stable` tag has been updated
|
|
||||||
- [ ] Confirm the new version appears in [Read the Docs](https://argo-cd.readthedocs.io/)
|
|
||||||
- [ ] Verify the docs release build in https://app.readthedocs.org/projects/argo-cd/ succeeded and retry if failed (requires an Approver with admin creds to readthedocs)
|
|
||||||
- [ ] Announce GA release with EOL notice
|
|
||||||
- [ ] Confirm that tweet and blog post are ready
|
|
||||||
- [ ] Publish tweet and blog post
|
|
||||||
- [ ] Post in #argo-cd and #argo-announcements announcing the release and EOL:
|
|
||||||
```
|
|
||||||
:mega: Argo CD v{MAJOR}.{MINOR} is OUT NOW! :argocd::tada:
|
|
||||||
|
|
||||||
Please go through the following resources to know more about the release:
|
|
||||||
|
|
||||||
Upgrade instructions: https://argo-cd.readthedocs.io/en/latest/operator-manual/upgrading/{PREV_MINOR}-{MAJOR}.{MINOR}/
|
|
||||||
Blog: {BLOG_POST_URL}
|
|
||||||
|
|
||||||
:warning: IMPORTANT: With the release of Argo CD v{MAJOR}.{MINOR}, support for Argo CD v{EOL_VERSION} has officially reached End of Life (EOL).
|
|
||||||
|
|
||||||
Thanks to all the folks who spent their time contributing to this release in any way possible!
|
|
||||||
```
|
|
||||||
- [ ] (For the next release champion) Review the [items scheduled for the next release](https://github.com/orgs/argoproj/projects/25). If any item does not have an assignee who can commit to finish the feature, move it to the next release.
|
- [ ] (For the next release champion) Review the [items scheduled for the next release](https://github.com/orgs/argoproj/projects/25). If any item does not have an assignee who can commit to finish the feature, move it to the next release.
|
||||||
- [ ] (For the next release champion) Schedule a time mid-way through the release cycle to review items again.
|
- [ ] (For the next release champion) Schedule a time mid-way through the release cycle to review items again.
|
||||||
8
.github/ISSUE_TEMPLATE/security_logs.md
vendored
8
.github/ISSUE_TEMPLATE/security_logs.md
vendored
@@ -1,11 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: Security log
|
name: Security log
|
||||||
about: Propose adding security-related logs or tagging existing logs with security fields
|
about: Propose adding security-related logs or tagging existing logs with security fields
|
||||||
title: 'seclog: [Event Description]'
|
title: "seclog: [Event Description]"
|
||||||
labels: ['security', 'triage/pending']
|
labels: security-log
|
||||||
assignees: ''
|
assignees: notfromstatefarm
|
||||||
---
|
---
|
||||||
|
|
||||||
# Event to be logged
|
# Event to be logged
|
||||||
|
|
||||||
Specify the event that needs to be logged or existing logs that need to be tagged.
|
Specify the event that needs to be logged or existing logs that need to be tagged.
|
||||||
@@ -17,3 +16,4 @@ What security level should these events be logged under? Refer to https://argo-c
|
|||||||
# Common Weakness Enumeration
|
# Common Weakness Enumeration
|
||||||
|
|
||||||
Is there an associated [CWE](https://cwe.mitre.org/) that could be tagged as well?
|
Is there an associated [CWE](https://cwe.mitre.org/) that could be tagged as well?
|
||||||
|
|
||||||
|
|||||||
3
.github/cherry-pick-bot.yml
vendored
Normal file
3
.github/cherry-pick-bot.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
enabled: true
|
||||||
|
preservePullRequestTitle: true
|
||||||
|
|
||||||
15
.github/configs/renovate-config.js
vendored
15
.github/configs/renovate-config.js
vendored
@@ -1,15 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
platform: 'github',
|
|
||||||
gitAuthor: 'renovate[bot] <renovate[bot]@users.noreply.github.com>',
|
|
||||||
autodiscover: false,
|
|
||||||
allowPostUpgradeCommandTemplating: true,
|
|
||||||
allowedPostUpgradeCommands: ["make mockgen"],
|
|
||||||
extends: [
|
|
||||||
"github>argoproj/argo-cd//renovate-presets/commons.json5",
|
|
||||||
"github>argoproj/argo-cd//renovate-presets/custom-managers/shell.json5",
|
|
||||||
"github>argoproj/argo-cd//renovate-presets/custom-managers/yaml.json5",
|
|
||||||
"github>argoproj/argo-cd//renovate-presets/fix/disable-all-updates.json5",
|
|
||||||
"github>argoproj/argo-cd//renovate-presets/devtool.json5",
|
|
||||||
"github>argoproj/argo-cd//renovate-presets/docs.json5"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
@@ -10,7 +10,7 @@ updates:
|
|||||||
groups:
|
groups:
|
||||||
otel:
|
otel:
|
||||||
patterns:
|
patterns:
|
||||||
- "go.opentelemetry.io/*"
|
- "^go.opentelemetry.io/.*"
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
@@ -22,11 +22,10 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
|
||||||
# Disabled since this code is rarely used.
|
- package-ecosystem: "npm"
|
||||||
# - package-ecosystem: "npm"
|
directory: "/ui-test/"
|
||||||
# directory: "/ui-test/"
|
schedule:
|
||||||
# schedule:
|
interval: "daily"
|
||||||
# interval: "daily"
|
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
@@ -53,8 +52,7 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
|
||||||
# Disabled since this code is rarely used.
|
- package-ecosystem: "docker"
|
||||||
# - package-ecosystem: "docker"
|
directory: "/ui-test/"
|
||||||
# directory: "/ui-test/"
|
schedule:
|
||||||
# schedule:
|
interval: "daily"
|
||||||
# interval: "daily"
|
|
||||||
|
|||||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -8,7 +8,7 @@ Checklist:
|
|||||||
|
|
||||||
* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
|
* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
|
||||||
* [ ] The title of the PR states what changed and the related issues number (used for the release note).
|
* [ ] The title of the PR states what changed and the related issues number (used for the release note).
|
||||||
* [ ] The title of the PR conforms to the [Title of the PR](https://argo-cd.readthedocs.io/en/latest/developer-guide/submit-your-pr/#title-of-the-pr)
|
* [ ] The title of the PR conforms to the [Toolchain Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/toolchain-guide/#title-of-the-pr)
|
||||||
* [ ] I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
|
* [ ] I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
|
||||||
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
|
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
|
||||||
* [ ] Does this PR require documentation updates?
|
* [ ] Does this PR require documentation updates?
|
||||||
|
|||||||
2
.github/workflows/README.md
vendored
2
.github/workflows/README.md
vendored
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
## image-reuse.yaml
|
## image-reuse.yaml
|
||||||
|
|
||||||
- The reusable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published.
|
- The resuable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published.
|
||||||
- A GO version `must` be specified e.g. 1.21
|
- A GO version `must` be specified e.g. 1.21
|
||||||
- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type.
|
- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type.
|
||||||
- Multiple platforms can be specified e.g. linux/amd64,linux/arm64
|
- Multiple platforms can be specified e.g. linux/amd64,linux/arm64
|
||||||
|
|||||||
6
.github/workflows/bump-major-version.yaml
vendored
6
.github/workflows/bump-major-version.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Add ~/go/bin to PATH
|
- name: Add ~/go/bin to PATH
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
rsync -a --exclude=.git /home/runner/go/src/github.com/argoproj/argo-cd/ ../argo-cd
|
rsync -a --exclude=.git /home/runner/go/src/github.com/argoproj/argo-cd/ ../argo-cd
|
||||||
|
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||||
with:
|
with:
|
||||||
commit-message: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
commit-message: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
||||||
title: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
title: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
||||||
|
|||||||
121
.github/workflows/cherry-pick-single.yml
vendored
121
.github/workflows/cherry-pick-single.yml
vendored
@@ -1,121 +0,0 @@
|
|||||||
name: Cherry Pick Single
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
merge_commit_sha:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: "The merge commit SHA to cherry-pick"
|
|
||||||
version_number:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: "The version number (from cherry-pick/ label)"
|
|
||||||
pr_number:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: "The original PR number"
|
|
||||||
pr_title:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
description: "The original PR title"
|
|
||||||
secrets:
|
|
||||||
CHERRYPICK_APP_ID:
|
|
||||||
required: true
|
|
||||||
CHERRYPICK_APP_PRIVATE_KEY:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cherry-pick:
|
|
||||||
name: Cherry Pick to ${{ inputs.version_number }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Generate a token
|
|
||||||
id: generate-token
|
|
||||||
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.CHERRYPICK_APP_ID }}
|
|
||||||
private-key: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
|
||||||
|
|
||||||
- name: Configure Git
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions[bot]"
|
|
||||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
|
|
||||||
- name: Cherry pick commit
|
|
||||||
id: cherry-pick
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
|
|
||||||
MERGE_COMMIT="${{ inputs.merge_commit_sha }}"
|
|
||||||
TARGET_BRANCH="release-${{ inputs.version_number }}"
|
|
||||||
|
|
||||||
echo "🍒 Cherry-picking commit $MERGE_COMMIT to branch $TARGET_BRANCH"
|
|
||||||
|
|
||||||
# Check if target branch exists
|
|
||||||
if ! git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then
|
|
||||||
echo "❌ Target branch '$TARGET_BRANCH' does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create new branch for cherry-pick
|
|
||||||
CHERRY_PICK_BRANCH="cherry-pick-${{ inputs.pr_number }}-to-${TARGET_BRANCH}"
|
|
||||||
git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH"
|
|
||||||
|
|
||||||
# Perform cherry-pick
|
|
||||||
if git cherry-pick -m 1 "$MERGE_COMMIT"; then
|
|
||||||
echo "✅ Cherry-pick successful"
|
|
||||||
|
|
||||||
# Extract Signed-off-by from the cherry-pick commit
|
|
||||||
SIGNOFF=$(git log -1 --pretty=format:"%B" | grep -E '^Signed-off-by:' || echo "")
|
|
||||||
|
|
||||||
# Push the new branch
|
|
||||||
git push origin "$CHERRY_PICK_BRANCH"
|
|
||||||
|
|
||||||
# Save data for PR creation
|
|
||||||
echo "branch_name=$CHERRY_PICK_BRANCH" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "signoff=$SIGNOFF" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "target_branch=$TARGET_BRANCH" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "❌ Cherry-pick failed due to conflicts"
|
|
||||||
git cherry-pick --abort
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
|
||||||
run: |
|
|
||||||
# Create cherry-pick PR
|
|
||||||
TITLE="${PR_TITLE} (cherry-pick #${{ inputs.pr_number }} for ${{ inputs.version_number }})"
|
|
||||||
BODY=$(cat <<EOF
|
|
||||||
Cherry-picked ${PR_TITLE} (#${{ inputs.pr_number }})
|
|
||||||
|
|
||||||
${{ steps.cherry-pick.outputs.signoff }}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
|
|
||||||
gh pr create \
|
|
||||||
--title "$TITLE" \
|
|
||||||
--body "$BODY" \
|
|
||||||
--base "${{ steps.cherry-pick.outputs.target_branch }}" \
|
|
||||||
--head "${{ steps.cherry-pick.outputs.branch_name }}"
|
|
||||||
|
|
||||||
# Comment on original PR
|
|
||||||
gh pr comment ${{ inputs.pr_number }} \
|
|
||||||
--body "🍒 Cherry-pick PR created for ${{ inputs.version_number }}: #$(gh pr list --head ${{ steps.cherry-pick.outputs.branch_name }} --json number --jq '.[0].number')"
|
|
||||||
env:
|
|
||||||
PR_TITLE: ${{ inputs.pr_title }}
|
|
||||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
|
||||||
|
|
||||||
- name: Comment on failure
|
|
||||||
if: failure()
|
|
||||||
run: |
|
|
||||||
gh pr comment ${{ inputs.pr_number }} \
|
|
||||||
--body "❌ Cherry-pick failed for ${{ inputs.version_number }}. Please check the [workflow logs](https://github.com/argoproj/argo-cd/actions/runs/${{ github.run_id }}) for details."
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
|
||||||
53
.github/workflows/cherry-pick.yml
vendored
53
.github/workflows/cherry-pick.yml
vendored
@@ -1,53 +0,0 @@
|
|||||||
name: Cherry Pick
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
types: ["labeled", "closed"]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
find-labels:
|
|
||||||
name: Find Cherry Pick Labels
|
|
||||||
if: |
|
|
||||||
github.event.pull_request.merged == true && (
|
|
||||||
(github.event.action == 'labeled' && startsWith(github.event.label.name, 'cherry-pick/')) ||
|
|
||||||
(github.event.action == 'closed' && contains(toJSON(github.event.pull_request.labels.*.name), 'cherry-pick/'))
|
|
||||||
)
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
labels: ${{ steps.extract-labels.outputs.labels }}
|
|
||||||
steps:
|
|
||||||
- name: Extract cherry-pick labels
|
|
||||||
id: extract-labels
|
|
||||||
run: |
|
|
||||||
if [[ "${{ github.event.action }}" == "labeled" ]]; then
|
|
||||||
# Label was just added - use it directly
|
|
||||||
LABEL_NAME="${{ github.event.label.name }}"
|
|
||||||
VERSION="${LABEL_NAME#cherry-pick/}"
|
|
||||||
CHERRY_PICK_DATA='[{"label":"'$LABEL_NAME'","version":"'$VERSION'"}]'
|
|
||||||
else
|
|
||||||
# PR was closed - find all cherry-pick labels
|
|
||||||
CHERRY_PICK_DATA=$(echo '${{ toJSON(github.event.pull_request.labels) }}' | jq -c '[.[] | select(.name | startswith("cherry-pick/")) | {label: .name, version: (.name | sub("cherry-pick/"; ""))}]')
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "labels=$CHERRY_PICK_DATA" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Found cherry-pick data: $CHERRY_PICK_DATA"
|
|
||||||
|
|
||||||
cherry-pick:
|
|
||||||
name: Cherry Pick
|
|
||||||
needs: find-labels
|
|
||||||
if: needs.find-labels.outputs.labels != '[]'
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include: ${{ fromJSON(needs.find-labels.outputs.labels) }}
|
|
||||||
fail-fast: false
|
|
||||||
uses: ./.github/workflows/cherry-pick-single.yml
|
|
||||||
with:
|
|
||||||
merge_commit_sha: ${{ github.event.pull_request.merge_commit_sha }}
|
|
||||||
version_number: ${{ matrix.version }}
|
|
||||||
pr_number: ${{ github.event.pull_request.number }}
|
|
||||||
pr_title: ${{ github.event.pull_request.title }}
|
|
||||||
secrets:
|
|
||||||
CHERRYPICK_APP_ID: ${{ vars.CHERRYPICK_APP_ID }}
|
|
||||||
CHERRYPICK_APP_PRIVATE_KEY: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
|
|
||||||
134
.github/workflows/ci-build.yaml
vendored
134
.github/workflows/ci-build.yaml
vendored
@@ -14,11 +14,11 @@ on:
|
|||||||
env:
|
env:
|
||||||
# Golang version to use across CI steps
|
# Golang version to use across CI steps
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
GOLANG_VERSION: '1.25.5'
|
GOLANG_VERSION: '1.24.1'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
cancel-in-progress: true
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -31,8 +31,8 @@ jobs:
|
|||||||
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
|
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
|
||||||
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
|
- uses: tj-actions/changed-files@bab30c2299617f6615ec02a68b9a40d10bd21366 # v45.0.5
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
||||||
@@ -55,9 +55,9 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Download all Go modules
|
- name: Download all Go modules
|
||||||
@@ -67,6 +67,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
go mod tidy
|
go mod tidy
|
||||||
git diff --exit-code -- .
|
git diff --exit-code -- .
|
||||||
|
|
||||||
build-go:
|
build-go:
|
||||||
name: Build & cache Go code
|
name: Build & cache Go code
|
||||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||||
@@ -75,13 +76,13 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
|
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
@@ -102,16 +103,16 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Run golangci-lint
|
- name: Run golangci-lint
|
||||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
|
||||||
with:
|
with:
|
||||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||||
version: v2.5.0
|
version: v1.64.7
|
||||||
args: --verbose
|
args: --verbose
|
||||||
|
|
||||||
test-go:
|
test-go:
|
||||||
@@ -128,11 +129,11 @@ jobs:
|
|||||||
- name: Create checkout directory
|
- name: Create checkout directory
|
||||||
run: mkdir -p ~/go/src/github.com/argoproj
|
run: mkdir -p ~/go/src/github.com/argoproj
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Create symlink in GOPATH
|
- name: Create symlink in GOPATH
|
||||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Install required packages
|
- name: Install required packages
|
||||||
@@ -152,7 +153,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
|
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
@@ -173,7 +174,7 @@ jobs:
|
|||||||
- name: Run all unit tests
|
- name: Run all unit tests
|
||||||
run: make test-local
|
run: make test-local
|
||||||
- name: Generate test results artifacts
|
- name: Generate test results artifacts
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
path: test-results
|
path: test-results
|
||||||
@@ -192,11 +193,11 @@ jobs:
|
|||||||
- name: Create checkout directory
|
- name: Create checkout directory
|
||||||
run: mkdir -p ~/go/src/github.com/argoproj
|
run: mkdir -p ~/go/src/github.com/argoproj
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Create symlink in GOPATH
|
- name: Create symlink in GOPATH
|
||||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Install required packages
|
- name: Install required packages
|
||||||
@@ -216,7 +217,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
|
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
@@ -237,7 +238,7 @@ jobs:
|
|||||||
- name: Run all unit tests
|
- name: Run all unit tests
|
||||||
run: make test-race-local
|
run: make test-race-local
|
||||||
- name: Generate test results artifacts
|
- name: Generate test results artifacts
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: race-results
|
name: race-results
|
||||||
path: test-results/
|
path: test-results/
|
||||||
@@ -250,16 +251,15 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Create symlink in GOPATH
|
- name: Create symlink in GOPATH
|
||||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/go/src/github.com/argoproj
|
mkdir -p ~/go/src/github.com/argoproj
|
||||||
cp -a ../${{ github.event.repository.name }} ~/go/src/github.com/argoproj
|
cp -a ../argo-cd ~/go/src/github.com/argoproj
|
||||||
- name: Add ~/go/bin to PATH
|
- name: Add ~/go/bin to PATH
|
||||||
run: |
|
run: |
|
||||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||||
@@ -271,14 +271,12 @@ jobs:
|
|||||||
# We need to vendor go modules for codegen yet
|
# We need to vendor go modules for codegen yet
|
||||||
go mod download
|
go mod download
|
||||||
go mod vendor -v
|
go mod vendor -v
|
||||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
|
|
||||||
- name: Install toolchain for codegen
|
- name: Install toolchain for codegen
|
||||||
run: |
|
run: |
|
||||||
make install-codegen-tools-local
|
make install-codegen-tools-local
|
||||||
make install-go-tools-local
|
make install-go-tools-local
|
||||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
|
|
||||||
# We install kustomize in the dist directory
|
# We install kustomize in the dist directory
|
||||||
- name: Add dist to PATH
|
- name: Add dist to PATH
|
||||||
run: |
|
run: |
|
||||||
@@ -289,14 +287,12 @@ jobs:
|
|||||||
export GOPATH=$(go env GOPATH)
|
export GOPATH=$(go env GOPATH)
|
||||||
git checkout -- go.mod go.sum
|
git checkout -- go.mod go.sum
|
||||||
make codegen-local
|
make codegen-local
|
||||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
|
|
||||||
- name: Check nothing has changed
|
- name: Check nothing has changed
|
||||||
run: |
|
run: |
|
||||||
set -xo pipefail
|
set -xo pipefail
|
||||||
git diff --exit-code -- . ':!go.sum' ':!go.mod' ':!assets/swagger.json' | tee codegen.patch
|
git diff --exit-code -- . ':!go.sum' ':!go.mod' ':!assets/swagger.json' | tee codegen.patch
|
||||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
|
|
||||||
|
|
||||||
build-ui:
|
build-ui:
|
||||||
name: Build, test & lint UI code
|
name: Build, test & lint UI code
|
||||||
@@ -307,15 +303,15 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup NodeJS
|
- name: Setup NodeJS
|
||||||
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
# renovate: datasource=node-version packageName=node versioning=node
|
# renovate: datasource=node-version packageName=node versioning=node
|
||||||
node-version: '22.9.0'
|
node-version: '22.9.0'
|
||||||
- name: Restore node dependency cache
|
- name: Restore node dependency cache
|
||||||
id: cache-dependencies
|
id: cache-dependencies
|
||||||
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
|
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
with:
|
with:
|
||||||
path: ui/node_modules
|
path: ui/node_modules
|
||||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||||
@@ -337,15 +333,6 @@ jobs:
|
|||||||
run: yarn lint
|
run: yarn lint
|
||||||
working-directory: ui/
|
working-directory: ui/
|
||||||
|
|
||||||
shellcheck:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
||||||
- run: |
|
|
||||||
sudo apt-get install shellcheck
|
|
||||||
shellcheck -e SC2059 -e SC2154 -e SC2034 -e SC2016 -e SC1091 $(find . -type f -name '*.sh' | grep -v './ui/node_modules') | tee sc.log
|
|
||||||
test ! -s sc.log
|
|
||||||
|
|
||||||
analyze:
|
analyze:
|
||||||
name: Process & analyze test artifacts
|
name: Process & analyze test artifacts
|
||||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||||
@@ -359,12 +346,12 @@ jobs:
|
|||||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Restore node dependency cache
|
- name: Restore node dependency cache
|
||||||
id: cache-dependencies
|
id: cache-dependencies
|
||||||
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
|
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
with:
|
with:
|
||||||
path: ui/node_modules
|
path: ui/node_modules
|
||||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||||
@@ -372,12 +359,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rm -rf ui/node_modules/argo-ui/node_modules
|
rm -rf ui/node_modules/argo-ui/node_modules
|
||||||
- name: Get e2e code coverage
|
- name: Get e2e code coverage
|
||||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: e2e-code-coverage
|
name: e2e-code-coverage
|
||||||
path: e2e-code-coverage
|
path: e2e-code-coverage
|
||||||
- name: Get unit test code coverage
|
- name: Get unit test code coverage
|
||||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
path: test-results
|
path: test-results
|
||||||
@@ -389,15 +376,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
|
go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
|
||||||
- name: Upload code coverage information to codecov.io
|
- name: Upload code coverage information to codecov.io
|
||||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
|
||||||
with:
|
with:
|
||||||
files: test-results/full-coverage.out
|
file: test-results/full-coverage.out
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
- name: Upload test results to Codecov
|
- name: Upload test results to Codecov
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
|
||||||
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
|
uses: codecov/test-results-action@9739113ad922ea0a9abb4b2c0f8bf6a4aa8ef820 # v1.0.1
|
||||||
with:
|
with:
|
||||||
file: test-results/junit.xml
|
file: test-results/junit.xml
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
@@ -406,31 +393,35 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7.0.0
|
uses: SonarSource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 # v4.2.1
|
||||||
if: env.sonar_secret != ''
|
if: env.sonar_secret != ''
|
||||||
test-e2e:
|
test-e2e:
|
||||||
name: Run end-to-end tests
|
name: Run end-to-end tests
|
||||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||||
runs-on: ${{ github.repository == 'argoproj/argo-cd' && 'oracle-vm-16cpu-64gb-x86-64' || 'ubuntu-22.04' }}
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
# latest: true means that this version mush upload the coverage report to codecov.io
|
# latest: true means that this version mush upload the coverage report to codecov.io
|
||||||
# We designate the latest version because we only collect code coverage for that version.
|
# We designate the latest version because we only collect code coverage for that version.
|
||||||
k3s:
|
k3s:
|
||||||
- version: v1.34.2
|
|
||||||
latest: true
|
|
||||||
- version: v1.33.1
|
|
||||||
latest: false
|
|
||||||
- version: v1.32.1
|
- version: v1.32.1
|
||||||
latest: false
|
latest: true
|
||||||
- version: v1.31.0
|
- version: v1.31.0
|
||||||
latest: false
|
latest: false
|
||||||
|
- version: v1.30.4
|
||||||
|
latest: false
|
||||||
|
- version: v1.29.8
|
||||||
|
latest: false
|
||||||
needs:
|
needs:
|
||||||
- build-go
|
- build-go
|
||||||
- changes
|
- changes
|
||||||
env:
|
env:
|
||||||
|
GOPATH: /home/runner/go
|
||||||
ARGOCD_FAKE_IN_CLUSTER: 'true'
|
ARGOCD_FAKE_IN_CLUSTER: 'true'
|
||||||
|
ARGOCD_SSH_DATA_PATH: '/tmp/argo-e2e/app/config/ssh'
|
||||||
|
ARGOCD_TLS_DATA_PATH: '/tmp/argo-e2e/app/config/tls'
|
||||||
|
ARGOCD_E2E_SSH_KNOWN_HOSTS: '../fixture/certs/ssh_known_hosts'
|
||||||
ARGOCD_E2E_K3S: 'true'
|
ARGOCD_E2E_K3S: 'true'
|
||||||
ARGOCD_IN_CI: 'true'
|
ARGOCD_IN_CI: 'true'
|
||||||
ARGOCD_E2E_APISERVER_PORT: '8088'
|
ARGOCD_E2E_APISERVER_PORT: '8088'
|
||||||
@@ -447,14 +438,11 @@ jobs:
|
|||||||
swap-storage: false
|
swap-storage: false
|
||||||
tool-cache: false
|
tool-cache: false
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Set GOPATH
|
|
||||||
run: |
|
|
||||||
echo "GOPATH=$HOME/go" >> $GITHUB_ENV
|
|
||||||
- name: GH actions workaround - Kill XSP4 process
|
- name: GH actions workaround - Kill XSP4 process
|
||||||
run: |
|
run: |
|
||||||
sudo pkill mono || true
|
sudo pkill mono || true
|
||||||
@@ -465,19 +453,19 @@ jobs:
|
|||||||
set -x
|
set -x
|
||||||
curl -sfL https://get.k3s.io | sh -
|
curl -sfL https://get.k3s.io | sh -
|
||||||
sudo chmod -R a+rw /etc/rancher/k3s
|
sudo chmod -R a+rw /etc/rancher/k3s
|
||||||
sudo mkdir -p $HOME/.kube && sudo chown -R $(whoami) $HOME/.kube
|
sudo mkdir -p $HOME/.kube && sudo chown -R runner $HOME/.kube
|
||||||
sudo k3s kubectl config view --raw > $HOME/.kube/config
|
sudo k3s kubectl config view --raw > $HOME/.kube/config
|
||||||
sudo chown $(whoami) $HOME/.kube/config
|
sudo chown runner $HOME/.kube/config
|
||||||
sudo chmod go-r $HOME/.kube/config
|
sudo chmod go-r $HOME/.kube/config
|
||||||
kubectl version
|
kubectl version
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
|
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
- name: Add ~/go/bin to PATH
|
- name: Add ~/go/bin to PATH
|
||||||
run: |
|
run: |
|
||||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||||
- name: Add /usr/local/bin to PATH
|
- name: Add /usr/local/bin to PATH
|
||||||
run: |
|
run: |
|
||||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
@@ -497,13 +485,13 @@ jobs:
|
|||||||
git config --global user.email "john.doe@example.com"
|
git config --global user.email "john.doe@example.com"
|
||||||
- name: Pull Docker image required for tests
|
- name: Pull Docker image required for tests
|
||||||
run: |
|
run: |
|
||||||
docker pull ghcr.io/dexidp/dex:v2.43.0
|
docker pull ghcr.io/dexidp/dex:v2.41.1
|
||||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||||
docker pull redis:8.2.3-alpine
|
docker pull redis:7.2.7-alpine
|
||||||
- name: Create target directory for binaries in the build-process
|
- name: Create target directory for binaries in the build-process
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
chown $(whoami) dist
|
chown runner dist
|
||||||
- name: Run E2E server and wait for it being available
|
- name: Run E2E server and wait for it being available
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
run: |
|
run: |
|
||||||
@@ -529,13 +517,13 @@ jobs:
|
|||||||
goreman run stop-all || echo "goreman trouble"
|
goreman run stop-all || echo "goreman trouble"
|
||||||
sleep 30
|
sleep 30
|
||||||
- name: Upload e2e coverage report
|
- name: Upload e2e coverage report
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: e2e-code-coverage
|
name: e2e-code-coverage
|
||||||
path: /tmp/coverage
|
path: /tmp/coverage
|
||||||
if: ${{ matrix.k3s.latest }}
|
if: ${{ matrix.k3s.latest }}
|
||||||
- name: Upload e2e-server logs
|
- name: Upload e2e-server logs
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
||||||
path: /tmp/e2e-server.log
|
path: /tmp/e2e-server.log
|
||||||
|
|||||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -29,11 +29,11 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
|
|
||||||
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|||||||
21
.github/workflows/image-reuse.yaml
vendored
21
.github/workflows/image-reuse.yaml
vendored
@@ -56,27 +56,26 @@ jobs:
|
|||||||
image-digest: ${{ steps.image.outputs.digest }}
|
image-digest: ${{ steps.image.outputs.digest }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
if: ${{ github.ref_type == 'tag'}}
|
if: ${{ github.ref_type == 'tag'}}
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
if: ${{ github.ref_type != 'tag'}}
|
if: ${{ github.ref_type != 'tag'}}
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ inputs.go-version }}
|
go-version: ${{ inputs.go-version }}
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||||
|
|
||||||
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
- uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||||
|
|
||||||
- name: Setup tags for container image as a CSV type
|
- name: Setup tags for container image as a CSV type
|
||||||
run: |
|
run: |
|
||||||
@@ -103,7 +102,7 @@ jobs:
|
|||||||
echo 'EOF' >> $GITHUB_ENV
|
echo 'EOF' >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Login to Quay.io
|
- name: Login to Quay.io
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
registry: quay.io
|
registry: quay.io
|
||||||
username: ${{ secrets.quay_username }}
|
username: ${{ secrets.quay_username }}
|
||||||
@@ -111,7 +110,7 @@ jobs:
|
|||||||
if: ${{ inputs.quay_image_name && inputs.push }}
|
if: ${{ inputs.quay_image_name && inputs.push }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ secrets.ghcr_username }}
|
username: ${{ secrets.ghcr_username }}
|
||||||
@@ -119,7 +118,7 @@ jobs:
|
|||||||
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
||||||
|
|
||||||
- name: Login to dockerhub Container Registry
|
- name: Login to dockerhub Container Registry
|
||||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.docker_username }}
|
username: ${{ secrets.docker_username }}
|
||||||
password: ${{ secrets.docker_password }}
|
password: ${{ secrets.docker_password }}
|
||||||
@@ -142,7 +141,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push container image
|
- name: Build and push container image
|
||||||
id: image
|
id: image
|
||||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
|
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 #v6.10.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: ${{ inputs.platforms }}
|
platforms: ${{ inputs.platforms }}
|
||||||
|
|||||||
62
.github/workflows/image.yaml
vendored
62
.github/workflows/image.yaml
vendored
@@ -19,49 +19,16 @@ jobs:
|
|||||||
set-vars:
|
set-vars:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
# Always run to calculate variables - other jobs check outputs
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
outputs:
|
outputs:
|
||||||
image-tag: ${{ steps.image.outputs.tag}}
|
image-tag: ${{ steps.image.outputs.tag}}
|
||||||
platforms: ${{ steps.platforms.outputs.platforms }}
|
platforms: ${{ steps.platforms.outputs.platforms }}
|
||||||
image_namespace: ${{ steps.image.outputs.image_namespace }}
|
|
||||||
image_repository: ${{ steps.image.outputs.image_repository }}
|
|
||||||
quay_image_name: ${{ steps.image.outputs.quay_image_name }}
|
|
||||||
ghcr_image_name: ${{ steps.image.outputs.ghcr_image_name }}
|
|
||||||
ghcr_provenance_image: ${{ steps.image.outputs.ghcr_provenance_image }}
|
|
||||||
allow_ghcr_publish: ${{ steps.image.outputs.allow_ghcr_publish }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
|
|
||||||
- name: Set image tag and names
|
- name: Set image tag for ghcr
|
||||||
run: |
|
run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||||
# Calculate image tag
|
|
||||||
TAG="$(cat ./VERSION)-${GITHUB_SHA::8}"
|
|
||||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Calculate image names with defaults
|
|
||||||
IMAGE_NAMESPACE="${{ vars.IMAGE_NAMESPACE || 'argoproj' }}"
|
|
||||||
IMAGE_REPOSITORY="${{ vars.IMAGE_REPOSITORY || 'argocd' }}"
|
|
||||||
GHCR_NAMESPACE="${{ vars.GHCR_NAMESPACE || github.repository }}"
|
|
||||||
GHCR_REPOSITORY="${{ vars.GHCR_REPOSITORY || 'argocd' }}"
|
|
||||||
|
|
||||||
echo "image_namespace=$IMAGE_NAMESPACE" >> $GITHUB_OUTPUT
|
|
||||||
echo "image_repository=$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Construct image name
|
|
||||||
echo "quay_image_name=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY:latest" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
ALLOW_GHCR_PUBLISH=false
|
|
||||||
if [[ "${{ github.repository }}" == "argoproj/argo-cd" || "$GHCR_NAMESPACE" != argoproj/* ]]; then
|
|
||||||
ALLOW_GHCR_PUBLISH=true
|
|
||||||
echo "ghcr_image_name=ghcr.io/$GHCR_NAMESPACE/$GHCR_REPOSITORY:$TAG" >> $GITHUB_OUTPUT
|
|
||||||
echo "ghcr_provenance_image=ghcr.io/$GHCR_NAMESPACE/$GHCR_REPOSITORY" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "GhCR publish skipped: refusing to push to namespace '$GHCR_NAMESPACE'. Please override GHCR_* for forks." >&2
|
|
||||||
echo "ghcr_image_name=" >> $GITHUB_OUTPUT
|
|
||||||
echo "ghcr_provenance_image=" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
echo "allow_ghcr_publish=$ALLOW_GHCR_PUBLISH" >> $GITHUB_OUTPUT
|
|
||||||
id: image
|
id: image
|
||||||
|
|
||||||
- name: Determine image platforms to use
|
- name: Determine image platforms to use
|
||||||
@@ -81,12 +48,12 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
if: ${{ (github.repository == 'argoproj/argo-cd' || needs.set-vars.outputs.image_namespace != 'argoproj') && github.event_name != 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name != 'push' }}
|
||||||
uses: ./.github/workflows/image-reuse.yaml
|
uses: ./.github/workflows/image-reuse.yaml
|
||||||
with:
|
with:
|
||||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
go-version: 1.25.5
|
go-version: 1.24.1
|
||||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||||
push: false
|
push: false
|
||||||
|
|
||||||
@@ -96,14 +63,14 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
if: ${{ (github.repository == 'argoproj/argo-cd' || needs.set-vars.outputs.image_namespace != 'argoproj') && github.event_name == 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||||
uses: ./.github/workflows/image-reuse.yaml
|
uses: ./.github/workflows/image-reuse.yaml
|
||||||
with:
|
with:
|
||||||
quay_image_name: ${{ needs.set-vars.outputs.quay_image_name }}
|
quay_image_name: quay.io/argoproj/argocd:latest
|
||||||
ghcr_image_name: ${{ needs.set-vars.outputs.ghcr_image_name }}
|
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
||||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
go-version: 1.25.5
|
go-version: 1.24.1
|
||||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||||
push: true
|
push: true
|
||||||
secrets:
|
secrets:
|
||||||
@@ -114,17 +81,16 @@ jobs:
|
|||||||
|
|
||||||
build-and-publish-provenance: # Push attestations to GHCR, latest image is polluting quay.io
|
build-and-publish-provenance: # Push attestations to GHCR, latest image is polluting quay.io
|
||||||
needs:
|
needs:
|
||||||
- set-vars
|
|
||||||
- build-and-publish
|
- build-and-publish
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # for detecting the Github Actions environment.
|
actions: read # for detecting the Github Actions environment.
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||||
if: ${{ (github.repository == 'argoproj/argo-cd' || needs.set-vars.outputs.image_namespace != 'argoproj') && github.event_name == 'push' && needs.set-vars.outputs.allow_ghcr_publish == 'true'}}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||||
with:
|
with:
|
||||||
image: ${{ needs.set-vars.outputs.ghcr_provenance_image }}
|
image: ghcr.io/argoproj/argo-cd/argocd
|
||||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||||
registry-username: ${{ github.actor }}
|
registry-username: ${{ github.actor }}
|
||||||
secrets:
|
secrets:
|
||||||
@@ -140,7 +106,7 @@ jobs:
|
|||||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||||
env:
|
env:
|
||||||
TOKEN: ${{ secrets.TOKEN }}
|
TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
|||||||
10
.github/workflows/init-release.yaml
vendored
10
.github/workflows/init-release.yaml
vendored
@@ -21,15 +21,9 @@ jobs:
|
|||||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||||
name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }}
|
name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
env:
|
|
||||||
# Calculate image names with defaults, this will be used in the make manifests-local command
|
|
||||||
# to generate the correct image name in the manifests
|
|
||||||
IMAGE_REGISTRY: ${{ vars.IMAGE_REGISTRY || 'quay.io' }}
|
|
||||||
IMAGE_NAMESPACE: ${{ vars.IMAGE_NAMESPACE || 'argoproj' }}
|
|
||||||
IMAGE_REPOSITORY: ${{ vars.IMAGE_REPOSITORY || 'argocd' }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -70,7 +64,7 @@ jobs:
|
|||||||
git stash pop
|
git stash pop
|
||||||
|
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||||
with:
|
with:
|
||||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||||
|
|||||||
151
.github/workflows/release.yaml
vendored
151
.github/workflows/release.yaml
vendored
@@ -11,99 +11,38 @@ permissions: {}
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
GOLANG_VERSION: '1.25.5' # Note: go-version must also be set in job argocd-image.with.go-version
|
GOLANG_VERSION: '1.24.1' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
argocd-image:
|
argocd-image:
|
||||||
needs: [setup-variables]
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
packages: write # used to push images to `ghcr.io` if used.
|
packages: write # used to push images to `ghcr.io` if used.
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
uses: ./.github/workflows/image-reuse.yaml
|
uses: ./.github/workflows/image-reuse.yaml
|
||||||
with:
|
with:
|
||||||
quay_image_name: ${{ needs.setup-variables.outputs.quay_image_name }}
|
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
go-version: 1.25.5
|
go-version: 1.24.1
|
||||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||||
push: true
|
push: true
|
||||||
secrets:
|
secrets:
|
||||||
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||||
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||||
|
|
||||||
setup-variables:
|
|
||||||
name: Setup Release Variables
|
|
||||||
if: github.repository == 'argoproj/argo-cd' || (github.repository_owner != 'argoproj' && vars.ENABLE_FORK_RELEASES == 'true' && vars.IMAGE_NAMESPACE && vars.IMAGE_NAMESPACE != 'argoproj')
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
outputs:
|
|
||||||
is_pre_release: ${{ steps.var.outputs.is_pre_release }}
|
|
||||||
is_latest_release: ${{ steps.var.outputs.is_latest_release }}
|
|
||||||
enable_fork_releases: ${{ steps.var.outputs.enable_fork_releases }}
|
|
||||||
image_namespace: ${{ steps.var.outputs.image_namespace }}
|
|
||||||
image_repository: ${{ steps.var.outputs.image_repository }}
|
|
||||||
quay_image_name: ${{ steps.var.outputs.quay_image_name }}
|
|
||||||
provenance_image: ${{ steps.var.outputs.provenance_image }}
|
|
||||||
allow_fork_release: ${{ steps.var.outputs.allow_fork_release }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Setup variables
|
|
||||||
id: var
|
|
||||||
run: |
|
|
||||||
set -xue
|
|
||||||
# Fetch all tag information
|
|
||||||
git fetch --prune --tags --force
|
|
||||||
|
|
||||||
LATEST_RELEASE_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | grep -v '-' | tail -n1)
|
|
||||||
|
|
||||||
PRE_RELEASE=false
|
|
||||||
# Check if latest tag is a pre-release
|
|
||||||
if echo ${{ github.ref_name }} | grep -E -- '-rc[0-9]+$';then
|
|
||||||
PRE_RELEASE=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
IS_LATEST=false
|
|
||||||
# Ensure latest release tag matches github.ref_name
|
|
||||||
if [[ $LATEST_RELEASE_TAG == ${{ github.ref_name }} ]];then
|
|
||||||
IS_LATEST=true
|
|
||||||
fi
|
|
||||||
echo "is_pre_release=$PRE_RELEASE" >> $GITHUB_OUTPUT
|
|
||||||
echo "is_latest_release=$IS_LATEST" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Calculate configuration with defaults
|
|
||||||
ENABLE_FORK_RELEASES="${{ vars.ENABLE_FORK_RELEASES || 'false' }}"
|
|
||||||
IMAGE_NAMESPACE="${{ vars.IMAGE_NAMESPACE || 'argoproj' }}"
|
|
||||||
IMAGE_REPOSITORY="${{ vars.IMAGE_REPOSITORY || 'argocd' }}"
|
|
||||||
|
|
||||||
echo "enable_fork_releases=$ENABLE_FORK_RELEASES" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
echo "image_namespace=$IMAGE_NAMESPACE" >> $GITHUB_OUTPUT
|
|
||||||
echo "image_repository=$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT
|
|
||||||
echo "quay_image_name=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY:${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
|
||||||
echo "provenance_image=quay.io/$IMAGE_NAMESPACE/$IMAGE_REPOSITORY" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
ALLOW_FORK_RELEASE=false
|
|
||||||
if [[ "${{ github.repository_owner }}" != "argoproj" && "$ENABLE_FORK_RELEASES" == "true" && "$IMAGE_NAMESPACE" != "argoproj" && "${{ github.ref }}" == refs/tags/* ]]; then
|
|
||||||
ALLOW_FORK_RELEASE=true
|
|
||||||
fi
|
|
||||||
echo "allow_fork_release=$ALLOW_FORK_RELEASE" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
argocd-image-provenance:
|
argocd-image-provenance:
|
||||||
needs: [setup-variables, argocd-image]
|
needs: [argocd-image]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # for detecting the Github Actions environment.
|
actions: read # for detecting the Github Actions environment.
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||||
with:
|
with:
|
||||||
image: ${{ needs.setup-variables.outputs.provenance_image }}
|
image: quay.io/argoproj/argocd
|
||||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||||
secrets:
|
secrets:
|
||||||
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||||
@@ -111,20 +50,18 @@ jobs:
|
|||||||
|
|
||||||
goreleaser:
|
goreleaser:
|
||||||
needs:
|
needs:
|
||||||
- setup-variables
|
|
||||||
- argocd-image
|
- argocd-image
|
||||||
- argocd-image-provenance
|
- argocd-image-provenance
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # used for uploading assets
|
contents: write # used for uploading assets
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
env:
|
|
||||||
GORELEASER_MAKE_LATEST: ${{ needs.setup-variables.outputs.is_latest_release }}
|
|
||||||
outputs:
|
outputs:
|
||||||
hashes: ${{ steps.hash.outputs.hashes }}
|
hashes: ${{ steps.hash.outputs.hashes }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -133,16 +70,14 @@ jobs:
|
|||||||
run: git fetch --force --tags
|
run: git fetch --force --tags
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in release branches.
|
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in release branches.
|
||||||
run: |
|
run: |
|
||||||
set -xue
|
set -xue
|
||||||
GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }}) || exit 1
|
echo "GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }})" >> $GITHUB_ENV
|
||||||
echo "GORELEASER_PREVIOUS_TAG=$GORELEASER_PREVIOUS_TAG" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set environment variables for ldflags
|
- name: Set environment variables for ldflags
|
||||||
id: set_ldflag
|
id: set_ldflag
|
||||||
@@ -159,7 +94,7 @@ jobs:
|
|||||||
tool-cache: false
|
tool-cache: false
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||||
id: run-goreleaser
|
id: run-goreleaser
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
@@ -168,8 +103,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
||||||
GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }}
|
GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }}
|
||||||
# Used to determine the current repository in the goreleaser config to display correct manifest links
|
|
||||||
GORELEASER_CURRENT_REPOSITORY: ${{ github.repository }}
|
|
||||||
|
|
||||||
- name: Generate subject for provenance
|
- name: Generate subject for provenance
|
||||||
id: hash
|
id: hash
|
||||||
@@ -186,14 +119,14 @@ jobs:
|
|||||||
echo "hashes=$hashes" >> $GITHUB_OUTPUT
|
echo "hashes=$hashes" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
goreleaser-provenance:
|
goreleaser-provenance:
|
||||||
needs: [goreleaser, setup-variables]
|
needs: [goreleaser]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # for detecting the Github Actions environment
|
actions: read # for detecting the Github Actions environment
|
||||||
id-token: write # Needed for provenance signing and ID
|
id-token: write # Needed for provenance signing and ID
|
||||||
contents: write # Needed for release uploads
|
contents: write # Needed for release uploads
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||||
with:
|
with:
|
||||||
base64-subjects: '${{ needs.goreleaser.outputs.hashes }}'
|
base64-subjects: '${{ needs.goreleaser.outputs.hashes }}'
|
||||||
provenance-name: 'argocd-cli.intoto.jsonl'
|
provenance-name: 'argocd-cli.intoto.jsonl'
|
||||||
@@ -204,25 +137,23 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- argocd-image
|
- argocd-image
|
||||||
- goreleaser
|
- goreleaser
|
||||||
- setup-variables
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # Needed for release uploads
|
contents: write # Needed for release uploads
|
||||||
outputs:
|
outputs:
|
||||||
hashes: ${{ steps.sbom-hash.outputs.hashes }}
|
hashes: ${{ steps.sbom-hash.outputs.hashes}}
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: Generate SBOM (spdx)
|
- name: Generate SBOM (spdx)
|
||||||
id: spdx-builder
|
id: spdx-builder
|
||||||
@@ -235,7 +166,7 @@ jobs:
|
|||||||
# managers (gomod, yarn, npm).
|
# managers (gomod, yarn, npm).
|
||||||
PROJECT_FOLDERS: '.,./ui'
|
PROJECT_FOLDERS: '.,./ui'
|
||||||
# full qualified name of the docker image to be inspected
|
# full qualified name of the docker image to be inspected
|
||||||
DOCKER_IMAGE: ${{ needs.setup-variables.outputs.quay_image_name }}
|
DOCKER_IMAGE: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
yarn install --cwd ./ui
|
yarn install --cwd ./ui
|
||||||
go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION
|
go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION
|
||||||
@@ -264,7 +195,7 @@ jobs:
|
|||||||
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Upload SBOM
|
- name: Upload SBOM
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
@@ -272,14 +203,14 @@ jobs:
|
|||||||
/tmp/sbom.tar.gz
|
/tmp/sbom.tar.gz
|
||||||
|
|
||||||
sbom-provenance:
|
sbom-provenance:
|
||||||
needs: [generate-sbom, setup-variables]
|
needs: [generate-sbom]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # for detecting the Github Actions environment
|
actions: read # for detecting the Github Actions environment
|
||||||
id-token: write # Needed for provenance signing and ID
|
id-token: write # Needed for provenance signing and ID
|
||||||
contents: write # Needed for release uploads
|
contents: write # Needed for release uploads
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
# Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||||
with:
|
with:
|
||||||
base64-subjects: '${{ needs.generate-sbom.outputs.hashes }}'
|
base64-subjects: '${{ needs.generate-sbom.outputs.hashes }}'
|
||||||
provenance-name: 'argocd-sbom.intoto.jsonl'
|
provenance-name: 'argocd-sbom.intoto.jsonl'
|
||||||
@@ -287,20 +218,17 @@ jobs:
|
|||||||
|
|
||||||
post-release:
|
post-release:
|
||||||
needs:
|
needs:
|
||||||
- setup-variables
|
|
||||||
- argocd-image
|
- argocd-image
|
||||||
- goreleaser
|
- goreleaser
|
||||||
- generate-sbom
|
- generate-sbom
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # Needed to push commit to update stable tag
|
contents: write # Needed to push commit to update stable tag
|
||||||
pull-requests: write # Needed to create PR for VERSION update.
|
pull-requests: write # Needed to create PR for VERSION update.
|
||||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
env:
|
|
||||||
TAG_STABLE: ${{ needs.setup-variables.outputs.is_latest_release }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -311,6 +239,27 @@ jobs:
|
|||||||
git config --global user.email 'ci@argoproj.com'
|
git config --global user.email 'ci@argoproj.com'
|
||||||
git config --global user.name 'CI'
|
git config --global user.name 'CI'
|
||||||
|
|
||||||
|
- name: Check if tag is the latest version and not a pre-release
|
||||||
|
run: |
|
||||||
|
set -xue
|
||||||
|
# Fetch all tag information
|
||||||
|
git fetch --prune --tags --force
|
||||||
|
|
||||||
|
LATEST_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n1)
|
||||||
|
|
||||||
|
PRE_RELEASE=false
|
||||||
|
# Check if latest tag is a pre-release
|
||||||
|
if echo $LATEST_TAG | grep -E -- '-rc[0-9]+$';then
|
||||||
|
PRE_RELEASE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure latest tag matches github.ref_name & not a pre-release
|
||||||
|
if [[ $LATEST_TAG == ${{ github.ref_name }} ]] && [[ $PRE_RELEASE != 'true' ]];then
|
||||||
|
echo "TAG_STABLE=true" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "TAG_STABLE=false" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Update stable tag to latest version
|
- name: Update stable tag to latest version
|
||||||
run: |
|
run: |
|
||||||
git tag -f stable ${{ github.ref_name }}
|
git tag -f stable ${{ github.ref_name }}
|
||||||
@@ -344,7 +293,7 @@ jobs:
|
|||||||
if: ${{ env.UPDATE_VERSION == 'true' }}
|
if: ${{ env.UPDATE_VERSION == 'true' }}
|
||||||
|
|
||||||
- name: Create PR to update VERSION on master branch
|
- name: Create PR to update VERSION on master branch
|
||||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||||
with:
|
with:
|
||||||
commit-message: Bump version in master
|
commit-message: Bump version in master
|
||||||
title: 'chore: Bump version in master'
|
title: 'chore: Bump version in master'
|
||||||
|
|||||||
39
.github/workflows/renovate.yaml
vendored
39
.github/workflows/renovate.yaml
vendored
@@ -1,39 +0,0 @@
|
|||||||
name: Renovate
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 * * * *'
|
|
||||||
workflow_dispatch: {}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
renovate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
|
||||||
steps:
|
|
||||||
- name: Get token
|
|
||||||
id: get_token
|
|
||||||
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
|
||||||
with:
|
|
||||||
app-id: ${{ vars.RENOVATE_APP_ID }}
|
|
||||||
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
|
|
||||||
|
|
||||||
# Some codegen commands require Go to be setup
|
|
||||||
- name: Setup Golang
|
|
||||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
|
|
||||||
with:
|
|
||||||
# renovate: datasource=golang-version packageName=golang
|
|
||||||
go-version: 1.25.5
|
|
||||||
|
|
||||||
- name: Self-hosted Renovate
|
|
||||||
uses: renovatebot/github-action@5712c6a41dea6cdf32c72d92a763bd417e6606aa #44.0.5
|
|
||||||
with:
|
|
||||||
configurationFile: .github/configs/renovate-config.js
|
|
||||||
token: '${{ steps.get_token.outputs.token }}'
|
|
||||||
env:
|
|
||||||
LOG_LEVEL: 'debug'
|
|
||||||
RENOVATE_REPOSITORIES: '${{ github.repository }}'
|
|
||||||
6
.github/workflows/scorecard.yaml
vendored
6
.github/workflows/scorecard.yaml
vendored
@@ -30,12 +30,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: "Run analysis"
|
- name: "Run analysis"
|
||||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||||
with:
|
with:
|
||||||
results_file: results.sarif
|
results_file: results.sarif
|
||||||
results_format: sarif
|
results_format: sarif
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
|
|||||||
2
.github/workflows/update-snyk.yaml
vendored
2
.github/workflows/update-snyk.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build reports
|
- name: Build reports
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,7 +8,6 @@ ui/dist/app/*
|
|||||||
!ui/dist/app/gitkeep
|
!ui/dist/app/gitkeep
|
||||||
site/
|
site/
|
||||||
*.iml
|
*.iml
|
||||||
.tilt-bin/
|
|
||||||
# delve debug binaries
|
# delve debug binaries
|
||||||
cmd/**/debug
|
cmd/**/debug
|
||||||
debug.test
|
debug.test
|
||||||
@@ -20,7 +19,6 @@ node_modules/
|
|||||||
.kube/
|
.kube/
|
||||||
./test/cmp/*.sock
|
./test/cmp/*.sock
|
||||||
.envrc.remote
|
.envrc.remote
|
||||||
.mirrord/
|
|
||||||
.*.swp
|
.*.swp
|
||||||
rerunreport.txt
|
rerunreport.txt
|
||||||
|
|
||||||
@@ -29,6 +27,3 @@ cmd/argocd/argocd
|
|||||||
cmd/argocd-application-controller/argocd-application-controller
|
cmd/argocd-application-controller/argocd-application-controller
|
||||||
cmd/argocd-repo-server/argocd-repo-server
|
cmd/argocd-repo-server/argocd-repo-server
|
||||||
cmd/argocd-server/argocd-server
|
cmd/argocd-server/argocd-server
|
||||||
|
|
||||||
# ignore generated `.argocd-helm-dep-up` marker file; this should not be committed to git
|
|
||||||
reposerver/repository/testdata/**/.argocd-helm-dep-up
|
|
||||||
21
.gitpod.Dockerfile
vendored
Normal file
21
.gitpod.Dockerfile
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
FROM gitpod/workspace-full@sha256:a47a68ee7f9da10cd889ccce4661bc73f2c0d5a98d3d087e8bdfc0230b27964c
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
|
RUN curl -o /usr/local/bin/kubectl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
|
||||||
|
chmod +x /usr/local/bin/kubectl
|
||||||
|
|
||||||
|
RUN curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.1/kubebuilder_2.3.1_$(go env GOOS)_$(go env GOARCH).tar.gz | \
|
||||||
|
tar -xz -C /tmp/ && mv /tmp/kubebuilder_2.3.1_$(go env GOOS)_$(go env GOARCH) /usr/local/kubebuilder
|
||||||
|
|
||||||
|
ENV GOCACHE=/go-build-cache
|
||||||
|
|
||||||
|
RUN apt-get install redis-server -y
|
||||||
|
RUN go install github.com/mattn/goreman@latest
|
||||||
|
|
||||||
|
RUN chown -R gitpod:gitpod /go-build-cache
|
||||||
|
|
||||||
|
USER gitpod
|
||||||
|
|
||||||
|
ENV ARGOCD_REDIS_LOCAL=true
|
||||||
|
ENV KUBECONFIG=/tmp/kubeconfig
|
||||||
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
image:
|
||||||
|
file: .gitpod.Dockerfile
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- init: make mod-download-local dep-ui-local && GO111MODULE=off go install github.com/mattn/goreman@latest
|
||||||
|
command: make start-test-k8s
|
||||||
367
.golangci.yaml
367
.golangci.yaml
@@ -1,253 +1,156 @@
|
|||||||
formatters:
|
|
||||||
enable:
|
|
||||||
- gofumpt
|
|
||||||
- goimports
|
|
||||||
|
|
||||||
settings:
|
|
||||||
goimports:
|
|
||||||
local-prefixes:
|
|
||||||
- github.com/argoproj/argo-cd/v3
|
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
exclude:
|
||||||
|
- SA5011
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
|
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
|
exclude-rules:
|
||||||
|
- path: '(.+)_test\.go'
|
||||||
|
linters:
|
||||||
|
- unparam
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
|
- errcheck
|
||||||
- errorlint
|
- errorlint
|
||||||
- exptostd
|
|
||||||
- gocritic
|
- gocritic
|
||||||
|
- gofumpt
|
||||||
|
- goimports
|
||||||
- gomodguard
|
- gomodguard
|
||||||
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- importas
|
- importas
|
||||||
|
- ineffassign
|
||||||
- misspell
|
- misspell
|
||||||
- noctx
|
# Disabled because of https://github.com/argoproj/argo-cd/issues/21705
|
||||||
|
# - nolintlint
|
||||||
- perfsprint
|
- perfsprint
|
||||||
- revive
|
- revive
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- testifylint
|
- testifylint
|
||||||
- thelper
|
- thelper
|
||||||
- tparallel
|
|
||||||
- unparam
|
- unparam
|
||||||
|
- unused
|
||||||
- usestdlibvars
|
- usestdlibvars
|
||||||
- usetesting
|
- usetesting
|
||||||
- whitespace
|
- whitespace
|
||||||
|
linters-settings:
|
||||||
exclusions:
|
gocritic:
|
||||||
|
disabled-checks:
|
||||||
|
- appendAssign
|
||||||
|
- assignOp # Keep it disabled for readability
|
||||||
|
- exitAfterDefer
|
||||||
|
- mapKey
|
||||||
|
- typeSwitchVar
|
||||||
|
goimports:
|
||||||
|
local-prefixes: github.com/argoproj/argo-cd/v3
|
||||||
|
gomodguard:
|
||||||
|
blocked:
|
||||||
|
modules:
|
||||||
|
- github.com/golang-jwt/jwt/v4:
|
||||||
|
recommendations:
|
||||||
|
- github.com/golang-jwt/jwt/v5
|
||||||
|
- github.com/imdario/mergo:
|
||||||
|
recommendations:
|
||||||
|
- dario.cat/mergo
|
||||||
|
reason: '`github.com/imdario/mergo` has been renamed.'
|
||||||
|
- github.com/pkg/errors:
|
||||||
|
recommendations:
|
||||||
|
- errors
|
||||||
|
importas:
|
||||||
|
alias:
|
||||||
|
- alias: jwtgo
|
||||||
|
pkg: github.com/golang-jwt/jwt/v5
|
||||||
|
- alias: appsv1
|
||||||
|
pkg: k8s.io/api/apps/v1
|
||||||
|
- alias: corev1
|
||||||
|
pkg: k8s.io/api/core/v1
|
||||||
|
- alias: rbacv1
|
||||||
|
pkg: k8s.io/api/rbac/v1
|
||||||
|
- alias: apierrors
|
||||||
|
pkg: k8s.io/apimachinery/pkg/api/errors
|
||||||
|
- alias: apiextensionsv1
|
||||||
|
pkg: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
|
||||||
|
- alias: metav1
|
||||||
|
pkg: k8s.io/apimachinery/pkg/apis/meta/v1
|
||||||
|
- alias: informersv1
|
||||||
|
pkg: k8s.io/client-go/informers/core/v1
|
||||||
|
- alias: stderrors
|
||||||
|
pkg: errors
|
||||||
|
nolintlint:
|
||||||
|
require-specific: true
|
||||||
|
perfsprint:
|
||||||
|
# Optimizes even if it requires an int or uint type cast.
|
||||||
|
int-conversion: true
|
||||||
|
# Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.
|
||||||
|
err-error: true
|
||||||
|
# Optimizes `fmt.Errorf`.
|
||||||
|
errorf: true
|
||||||
|
# Optimizes `fmt.Sprintf` with only one argument.
|
||||||
|
sprintf1: true
|
||||||
|
# Optimizes into strings concatenation.
|
||||||
|
strconcat: true
|
||||||
|
revive:
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
|
||||||
rules:
|
rules:
|
||||||
- linters:
|
- name: bool-literal-in-expr
|
||||||
- unparam
|
- name: blank-imports
|
||||||
path: (.+)_test\.go
|
disabled: true
|
||||||
|
- name: context-as-argument
|
||||||
presets:
|
arguments:
|
||||||
- comments
|
- allowTypesBefore: '*testing.T,testing.TB'
|
||||||
- common-false-positives
|
- name: context-keys-type
|
||||||
- legacy
|
disabled: true
|
||||||
- std-error-handling
|
- name: dot-imports
|
||||||
|
disabled: true
|
||||||
warn-unused: true
|
- name: duplicated-imports
|
||||||
|
- name: early-return
|
||||||
settings:
|
arguments:
|
||||||
gocritic:
|
- 'preserveScope'
|
||||||
enable-all: true
|
- name: empty-block
|
||||||
# Most of these should probably be enabled one-by-one.
|
disabled: true
|
||||||
disabled-checks:
|
- name: error-naming
|
||||||
- appendAssign
|
disabled: true
|
||||||
- appendCombine # Leave disabled, multi-line assigns can be more readable.
|
- name: error-return
|
||||||
- assignOp # Leave disabled, assign operations can be more confusing than helpful.
|
- name: error-strings
|
||||||
- commentedOutCode
|
disabled: true
|
||||||
- deferInLoop
|
- name: errorf
|
||||||
- exitAfterDefer
|
- name: identical-branches
|
||||||
- hugeParam
|
- name: if-return
|
||||||
- importShadow
|
- name: increment-decrement
|
||||||
- paramTypeCombine # Leave disabled, there are too many failures to be worth fixing.
|
- name: indent-error-flow
|
||||||
- rangeValCopy
|
arguments:
|
||||||
- tooManyResultsChecker
|
- 'preserveScope'
|
||||||
- unnamedResult
|
- name: modifies-parameter
|
||||||
- whyNoLint
|
- name: optimize-operands-order
|
||||||
|
- name: range
|
||||||
gomodguard:
|
- name: receiver-naming
|
||||||
blocked:
|
- name: redefines-builtin-id
|
||||||
modules:
|
disabled: true
|
||||||
- github.com/golang-jwt/jwt/v4:
|
- name: redundant-import-alias
|
||||||
recommendations:
|
- name: superfluous-else
|
||||||
- github.com/golang-jwt/jwt/v5
|
arguments:
|
||||||
|
- 'preserveScope'
|
||||||
- github.com/imdario/mergo:
|
- name: time-equal
|
||||||
recommendations:
|
- name: time-naming
|
||||||
- dario.cat/mergo
|
disabled: true
|
||||||
reason: '`github.com/imdario/mergo` has been renamed.'
|
- name: unexported-return
|
||||||
|
disabled: true
|
||||||
- github.com/pkg/errors:
|
- name: unnecessary-stmt
|
||||||
recommendations:
|
- name: unreachable-code
|
||||||
- errors
|
- name: unused-parameter
|
||||||
|
- name: use-any
|
||||||
govet:
|
- name: useless-break
|
||||||
disable:
|
- name: var-declaration
|
||||||
- fieldalignment
|
- name: var-naming
|
||||||
- shadow
|
arguments:
|
||||||
enable-all: true
|
- ["ID"]
|
||||||
|
- ["VM"]
|
||||||
importas:
|
- - skipPackageNameChecks: true
|
||||||
alias:
|
upperCaseConst: true
|
||||||
- pkg: github.com/golang-jwt/jwt/v5
|
testifylint:
|
||||||
alias: jwtgo
|
enable-all: true
|
||||||
|
disable:
|
||||||
- pkg: k8s.io/api/apps/v1
|
- go-require
|
||||||
alias: appsv1
|
usetesting:
|
||||||
|
os-mkdir-temp: false
|
||||||
- pkg: k8s.io/api/core/v1
|
run:
|
||||||
alias: corev1
|
timeout: 50m
|
||||||
|
|
||||||
- pkg: k8s.io/api/rbac/v1
|
|
||||||
alias: rbacv1
|
|
||||||
|
|
||||||
- pkg: k8s.io/apimachinery/pkg/api/errors
|
|
||||||
alias: apierrors
|
|
||||||
|
|
||||||
- pkg: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
|
|
||||||
alias: apiextensionsv1
|
|
||||||
|
|
||||||
- pkg: k8s.io/apimachinery/pkg/apis/meta/v1
|
|
||||||
alias: metav1
|
|
||||||
|
|
||||||
- pkg: k8s.io/client-go/informers/core/v1
|
|
||||||
alias: informersv1
|
|
||||||
|
|
||||||
- pkg: errors
|
|
||||||
alias: stderrors
|
|
||||||
|
|
||||||
- pkg: github.com/argoproj/argo-cd/v3/util/io
|
|
||||||
alias: utilio
|
|
||||||
|
|
||||||
nolintlint:
|
|
||||||
require-specific: true
|
|
||||||
|
|
||||||
perfsprint:
|
|
||||||
# Optimizes even if it requires an int or uint type cast.
|
|
||||||
int-conversion: true
|
|
||||||
# Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.
|
|
||||||
err-error: true
|
|
||||||
# Optimizes `fmt.Errorf`.
|
|
||||||
errorf: true
|
|
||||||
# Optimizes `fmt.Sprintf` with only one argument.
|
|
||||||
sprintf1: true
|
|
||||||
# Optimizes into strings concatenation.
|
|
||||||
strconcat: true
|
|
||||||
|
|
||||||
revive:
|
|
||||||
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
|
|
||||||
rules:
|
|
||||||
- name: bool-literal-in-expr
|
|
||||||
|
|
||||||
- name: blank-imports
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: context-as-argument
|
|
||||||
arguments:
|
|
||||||
- allowTypesBefore: '*testing.T,testing.TB'
|
|
||||||
|
|
||||||
- name: context-keys-type
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: dot-imports
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: duplicated-imports
|
|
||||||
|
|
||||||
- name: early-return
|
|
||||||
arguments:
|
|
||||||
- preserveScope
|
|
||||||
|
|
||||||
- name: empty-block
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: error-naming
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: error-return
|
|
||||||
|
|
||||||
- name: error-strings
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: errorf
|
|
||||||
|
|
||||||
- name: identical-branches
|
|
||||||
|
|
||||||
- name: if-return
|
|
||||||
|
|
||||||
- name: increment-decrement
|
|
||||||
|
|
||||||
- name: indent-error-flow
|
|
||||||
arguments:
|
|
||||||
- preserveScope
|
|
||||||
|
|
||||||
- name: modifies-parameter
|
|
||||||
|
|
||||||
- name: optimize-operands-order
|
|
||||||
|
|
||||||
- name: range
|
|
||||||
|
|
||||||
- name: receiver-naming
|
|
||||||
|
|
||||||
- name: redefines-builtin-id
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: redundant-import-alias
|
|
||||||
|
|
||||||
- name: superfluous-else
|
|
||||||
arguments:
|
|
||||||
- preserveScope
|
|
||||||
|
|
||||||
- name: time-equal
|
|
||||||
|
|
||||||
- name: time-naming
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: unexported-return
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- name: unnecessary-stmt
|
|
||||||
|
|
||||||
- name: unreachable-code
|
|
||||||
|
|
||||||
- name: unused-parameter
|
|
||||||
|
|
||||||
- name: use-any
|
|
||||||
|
|
||||||
- name: useless-break
|
|
||||||
|
|
||||||
- name: var-declaration
|
|
||||||
|
|
||||||
- name: var-naming
|
|
||||||
arguments:
|
|
||||||
- - ID
|
|
||||||
- - VM
|
|
||||||
- - skipPackageNameChecks: true
|
|
||||||
upperCaseConst: true
|
|
||||||
|
|
||||||
staticcheck:
|
|
||||||
checks:
|
|
||||||
- all
|
|
||||||
- -SA5011
|
|
||||||
- -ST1003
|
|
||||||
- -ST1016
|
|
||||||
|
|
||||||
testifylint:
|
|
||||||
enable-all: true
|
|
||||||
|
|
||||||
disable:
|
|
||||||
- go-require
|
|
||||||
|
|
||||||
unused:
|
|
||||||
field-writes-are-uses: false
|
|
||||||
exported-fields-are-used: false
|
|
||||||
|
|
||||||
usetesting:
|
|
||||||
os-mkdir-temp: false
|
|
||||||
|
|
||||||
output:
|
|
||||||
show-stats: false
|
|
||||||
|
|
||||||
version: "2"
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ builds:
|
|||||||
- -extldflags="-static"
|
- -extldflags="-static"
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
- windows
|
|
||||||
- darwin
|
- darwin
|
||||||
|
- windows
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
@@ -45,18 +45,17 @@ builds:
|
|||||||
|
|
||||||
archives:
|
archives:
|
||||||
- id: argocd-archive
|
- id: argocd-archive
|
||||||
ids:
|
builds:
|
||||||
- argocd-cli
|
- argocd-cli
|
||||||
name_template: |-
|
name_template: |-
|
||||||
{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}
|
{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}
|
||||||
formats: [binary]
|
format: binary
|
||||||
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'cli_checksums.txt'
|
name_template: 'cli_checksums.txt'
|
||||||
algorithm: sha256
|
algorithm: sha256
|
||||||
|
|
||||||
release:
|
release:
|
||||||
make_latest: '{{ .Env.GORELEASER_MAKE_LATEST }}'
|
|
||||||
prerelease: auto
|
prerelease: auto
|
||||||
draft: false
|
draft: false
|
||||||
header: |
|
header: |
|
||||||
@@ -66,14 +65,14 @@ release:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl create namespace argocd
|
kubectl create namespace argocd
|
||||||
kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/{{ .Env.GORELEASER_CURRENT_REPOSITORY }}/{{.Tag}}/manifests/install.yaml
|
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/{{.Tag}}/manifests/install.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### HA:
|
### HA:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl create namespace argocd
|
kubectl create namespace argocd
|
||||||
kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/{{ .Env.GORELEASER_CURRENT_REPOSITORY }}/{{.Tag}}/manifests/ha/install.yaml
|
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/{{.Tag}}/manifests/ha/install.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Release Signatures and Provenance
|
## Release Signatures and Provenance
|
||||||
@@ -81,27 +80,26 @@ release:
|
|||||||
All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify.
|
All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify.
|
||||||
|
|
||||||
## Release Notes Blog Post
|
## Release Notes Blog Post
|
||||||
For a detailed breakdown of the key changes and improvements in this release, check out the [official blog post](https://blog.argoproj.io/argo-cd-v3-0-release-candidate-a0b933f4e58f)
|
For a detailed breakdown of the key changes and improvements in this release, check out the [official blog post](https://blog.argoproj.io/argo-cd-v2-14-release-candidate-57a664791e2a)
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
If upgrading from a different minor version, be sure to read the [upgrading](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) documentation.
|
If upgrading from a different minor version, be sure to read the [upgrading](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) documentation.
|
||||||
footer: |
|
footer: |
|
||||||
**Full Changelog**: https://github.com/{{ .Env.GORELEASER_CURRENT_REPOSITORY }}/compare/{{ .PreviousTag }}...{{ .Tag }}
|
**Full Changelog**: https://github.com/argoproj/argo-cd/compare/{{ .PreviousTag }}...{{ .Tag }}
|
||||||
|
|
||||||
<a href="https://argoproj.github.io/cd/"><img src="https://raw.githubusercontent.com/argoproj/argo-site/master/content/pages/cd/gitops-cd.png" width="25%" ></a>
|
<a href="https://argoproj.github.io/cd/"><img src="https://raw.githubusercontent.com/argoproj/argo-site/master/content/pages/cd/gitops-cd.png" width="25%" ></a>
|
||||||
|
|
||||||
|
|
||||||
snapshot: #### To be removed for PR
|
snapshot: #### To be removed for PR
|
||||||
version_template: '2.6.0'
|
name_template: "2.6.0"
|
||||||
|
|
||||||
changelog:
|
changelog:
|
||||||
use: github
|
use:
|
||||||
|
github
|
||||||
sort: asc
|
sort: asc
|
||||||
abbrev: 0
|
abbrev: 0
|
||||||
groups: # Regex use RE2 syntax as defined here: https://github.com/google/re2/wiki/Syntax.
|
groups: # Regex use RE2 syntax as defined here: https://github.com/google/re2/wiki/Syntax.
|
||||||
- title: 'Breaking Changes'
|
|
||||||
regexp: '^.*?(\([[:word:]]+\))??!:.+$'
|
|
||||||
order: 0
|
|
||||||
- title: 'Features'
|
- title: 'Features'
|
||||||
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
||||||
order: 100
|
order: 100
|
||||||
@@ -121,4 +119,7 @@ changelog:
|
|||||||
- '^test:'
|
- '^test:'
|
||||||
- '^.*?Bump(\([[:word:]]+\))?.+$'
|
- '^.*?Bump(\([[:word:]]+\))?.+$'
|
||||||
- '^.*?\[Bot\](\([[:word:]]+\))?.+$'
|
- '^.*?\[Bot\](\([[:word:]]+\))?.+$'
|
||||||
|
|
||||||
|
|
||||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
|
||||||
|
|||||||
110
.mockery.yaml
110
.mockery.yaml
@@ -1,96 +1,82 @@
|
|||||||
dir: '{{.InterfaceDir}}/mocks'
|
# global config
|
||||||
filename: '{{.InterfaceName}}.go'
|
filename: "{{.InterfaceName}}.go"
|
||||||
include-auto-generated: true # Needed since mockery 3.6.1
|
dir: "{{.InterfaceDir}}/mocks"
|
||||||
|
outpkg: "mocks"
|
||||||
|
mockname: "{{.InterfaceName}}"
|
||||||
|
with-expecter: false
|
||||||
|
# individual interface config
|
||||||
packages:
|
packages:
|
||||||
github.com/argoproj/argo-cd/v3/applicationset/generators:
|
github.com/argoproj/argo-cd/v3/applicationset/generators:
|
||||||
interfaces:
|
interfaces:
|
||||||
Generator: {}
|
Generator:
|
||||||
github.com/argoproj/argo-cd/v3/applicationset/services:
|
github.com/argoproj/argo-cd/v3/applicationset/services:
|
||||||
interfaces:
|
interfaces:
|
||||||
Repos: {}
|
Repos:
|
||||||
github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider:
|
github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider:
|
||||||
|
config:
|
||||||
|
dir: "applicationset/services/scm_provider/aws_codecommit/mocks"
|
||||||
interfaces:
|
interfaces:
|
||||||
AWSCodeCommitClient: {}
|
AWSCodeCommitClient:
|
||||||
AWSTaggingClient: {}
|
AWSTaggingClient:
|
||||||
AzureDevOpsClientFactory: {}
|
github.com/microsoft/azure-devops-go-api/azuredevops/git:
|
||||||
|
config:
|
||||||
|
dir: "applicationset/services/scm_provider/azure_devops/git/mocks"
|
||||||
|
interfaces:
|
||||||
|
Client:
|
||||||
github.com/argoproj/argo-cd/v3/applicationset/utils:
|
github.com/argoproj/argo-cd/v3/applicationset/utils:
|
||||||
interfaces:
|
interfaces:
|
||||||
Renderer: {}
|
Renderer:
|
||||||
github.com/argoproj/argo-cd/v3/commitserver/apiclient:
|
|
||||||
interfaces:
|
|
||||||
CommitServiceClient: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/commitserver/commit:
|
github.com/argoproj/argo-cd/v3/commitserver/commit:
|
||||||
interfaces:
|
interfaces:
|
||||||
RepoClientFactory: {}
|
RepoClientFactory:
|
||||||
|
github.com/argoproj/argo-cd/v3/commitserver/apiclient:
|
||||||
|
interfaces:
|
||||||
|
CommitServiceClient:
|
||||||
|
Clientset:
|
||||||
github.com/argoproj/argo-cd/v3/controller/cache:
|
github.com/argoproj/argo-cd/v3/controller/cache:
|
||||||
interfaces:
|
interfaces:
|
||||||
LiveStateCache: {}
|
LiveStateCache:
|
||||||
github.com/argoproj/argo-cd/v3/controller/hydrator:
|
|
||||||
interfaces:
|
|
||||||
Dependencies: {}
|
|
||||||
RepoGetter: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster:
|
|
||||||
interfaces:
|
|
||||||
ClusterServiceServer: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/pkg/apiclient/project:
|
|
||||||
interfaces:
|
|
||||||
ProjectServiceClient: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/pkg/apiclient/session:
|
|
||||||
interfaces:
|
|
||||||
SessionServiceClient: {}
|
|
||||||
SessionServiceServer: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1:
|
|
||||||
interfaces:
|
|
||||||
AppProjectInterface: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/reposerver/apiclient:
|
github.com/argoproj/argo-cd/v3/reposerver/apiclient:
|
||||||
interfaces:
|
interfaces:
|
||||||
RepoServerServiceClient: {}
|
RepoServerServiceClient:
|
||||||
RepoServerService_GenerateManifestWithFilesClient: {}
|
RepoServerService_GenerateManifestWithFilesClient:
|
||||||
github.com/argoproj/argo-cd/v3/server/application:
|
github.com/argoproj/argo-cd/v3/server/application:
|
||||||
interfaces:
|
interfaces:
|
||||||
Broadcaster: {}
|
Broadcaster:
|
||||||
github.com/argoproj/argo-cd/v3/server/extension:
|
github.com/argoproj/argo-cd/v3/server/extension:
|
||||||
interfaces:
|
interfaces:
|
||||||
ApplicationGetter: {}
|
ApplicationGetter:
|
||||||
ExtensionMetricsRegistry: {}
|
ExtensionMetricsRegistry:
|
||||||
ProjectGetter: {}
|
ProjectGetter:
|
||||||
RbacEnforcer: {}
|
RbacEnforcer:
|
||||||
SettingsGetter: {}
|
SettingsGetter:
|
||||||
UserGetter: {}
|
UserGetter:
|
||||||
github.com/argoproj/argo-cd/v3/util/db:
|
github.com/argoproj/argo-cd/v3/util/db:
|
||||||
interfaces:
|
interfaces:
|
||||||
ArgoDB: {}
|
ArgoDB:
|
||||||
RepoCredsDB: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/util/git:
|
github.com/argoproj/argo-cd/v3/util/git:
|
||||||
interfaces:
|
interfaces:
|
||||||
Client: {}
|
Client:
|
||||||
github.com/argoproj/argo-cd/v3/util/helm:
|
github.com/argoproj/argo-cd/v3/util/helm:
|
||||||
interfaces:
|
interfaces:
|
||||||
Client: {}
|
Client:
|
||||||
github.com/argoproj/argo-cd/v3/util/io:
|
github.com/argoproj/argo-cd/v3/util/io:
|
||||||
interfaces:
|
interfaces:
|
||||||
TempPaths: {}
|
TempPaths:
|
||||||
github.com/argoproj/argo-cd/v3/util/notification/argocd:
|
github.com/argoproj/argo-cd/v3/util/notification/argocd:
|
||||||
interfaces:
|
interfaces:
|
||||||
Service: {}
|
Service:
|
||||||
github.com/argoproj/argo-cd/v3/util/oci:
|
|
||||||
interfaces:
|
|
||||||
Client: {}
|
|
||||||
github.com/argoproj/argo-cd/v3/util/workloadidentity:
|
github.com/argoproj/argo-cd/v3/util/workloadidentity:
|
||||||
interfaces:
|
interfaces:
|
||||||
TokenProvider: {}
|
TokenProvider:
|
||||||
github.com/argoproj/gitops-engine/pkg/cache:
|
# These mocks are not currently used, but they are part of the public API of this package.
|
||||||
|
github.com/argoproj/argo-cd/v3/pkg/apiclient/session:
|
||||||
interfaces:
|
interfaces:
|
||||||
ClusterCache: {}
|
SessionServiceServer:
|
||||||
github.com/argoproj/gitops-engine/pkg/diff:
|
SessionServiceClient:
|
||||||
|
github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster:
|
||||||
interfaces:
|
interfaces:
|
||||||
ServerSideDryRunner: {}
|
ClusterServiceServer:
|
||||||
github.com/microsoft/azure-devops-go-api/azuredevops/v7/git:
|
github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1:
|
||||||
config:
|
|
||||||
dir: applicationset/services/scm_provider/azure_devops/git/mocks
|
|
||||||
interfaces:
|
interfaces:
|
||||||
Client: {}
|
AppProjectInterface:
|
||||||
pkgname: mocks
|
|
||||||
structname: '{{.InterfaceName}}'
|
|
||||||
template-data:
|
|
||||||
unroll-variadic: true
|
|
||||||
|
|||||||
@@ -12,9 +12,3 @@
|
|||||||
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||||
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||||
/sonar-project.properties @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
/sonar-project.properties @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||||
|
|
||||||
# CLI
|
|
||||||
/cmd/argocd/** @argoproj/argocd-approvers @argoproj/argocd-approvers-cli
|
|
||||||
/cmd/main.go @argoproj/argocd-approvers @argoproj/argocd-approvers-cli
|
|
||||||
# Also include @argoproj/argocd-approvers-docs to avoid requiring CLI approvers for docs-only PRs.
|
|
||||||
/docs/operator-manual/ @argoproj/argocd-approvers @argoproj/argocd-approvers-docs @argoproj/argocd-approvers-cli
|
|
||||||
|
|||||||
40
Dockerfile
40
Dockerfile
@@ -1,12 +1,10 @@
|
|||||||
ARG BASE_IMAGE=docker.io/library/ubuntu:25.10@sha256:5922638447b1e3ba114332c896a2c7288c876bb94adec923d70d58a17d2fec5e
|
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Builder image
|
# Builder image
|
||||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||||
# Also used as the image in CI jobs so needs all dependencies
|
# Also used as the image in CI jobs so needs all dependencies
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a AS builder
|
FROM docker.io/library/golang:1.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418 AS builder
|
||||||
|
|
||||||
WORKDIR /tmp
|
|
||||||
|
|
||||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||||
|
|
||||||
@@ -25,6 +23,8 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
|||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
WORKDIR /tmp
|
||||||
|
|
||||||
COPY hack/install.sh hack/tool-versions.sh ./
|
COPY hack/install.sh hack/tool-versions.sh ./
|
||||||
COPY hack/installers installers
|
COPY hack/installers installers
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ LABEL org.opencontainers.image.source="https://github.com/argoproj/argo-cd"
|
|||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
ENV ARGOCD_USER_ID=999 \
|
ENV ARGOCD_USER_ID=999
|
||||||
DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
||||||
useradd -r -u $ARGOCD_USER_ID -g argocd argocd && \
|
useradd -r -u $ARGOCD_USER_ID -g argocd argocd && \
|
||||||
@@ -55,13 +55,11 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
|||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
COPY hack/gpg-wrapper.sh \
|
COPY hack/gpg-wrapper.sh /usr/local/bin/gpg-wrapper.sh
|
||||||
hack/git-verify-wrapper.sh \
|
COPY hack/git-verify-wrapper.sh /usr/local/bin/git-verify-wrapper.sh
|
||||||
entrypoint.sh \
|
|
||||||
/usr/local/bin/
|
|
||||||
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
|
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
|
||||||
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
|
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
# keep uid_entrypoint.sh for backward compatibility
|
# keep uid_entrypoint.sh for backward compatibility
|
||||||
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
||||||
|
|
||||||
@@ -85,7 +83,7 @@ WORKDIR /home/argocd
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Argo CD UI stage
|
# Argo CD UI stage
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:23.0.0@sha256:9d09fa506f5b8465c5221cbd6f980e29ae0ce9a3119e2b9bc0842e6a3f37bb59 AS argocd-ui
|
FROM --platform=$BUILDPLATFORM docker.io/library/node:23.0.0@sha256:e643c0b70dca9704dff42e12b17f5b719dbe4f95e6392fc2dfa0c5f02ea8044d AS argocd-ui
|
||||||
|
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
||||||
@@ -103,25 +101,23 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a AS argocd-build
|
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418 AS argocd-build
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
COPY go.* ./
|
COPY go.* ./
|
||||||
RUN mkdir -p gitops-engine
|
|
||||||
COPY gitops-engine/go.* ./gitops-engine
|
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
# Perform the build
|
# Perform the build
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app
|
COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app
|
||||||
ARG TARGETOS \
|
ARG TARGETOS
|
||||||
TARGETARCH
|
ARG TARGETARCH
|
||||||
# These build args are optional; if not specified the defaults will be taken from the Makefile
|
# These build args are optional; if not specified the defaults will be taken from the Makefile
|
||||||
ARG GIT_TAG \
|
ARG GIT_TAG
|
||||||
BUILD_DATE \
|
ARG BUILD_DATE
|
||||||
GIT_TREE_STATE \
|
ARG GIT_TREE_STATE
|
||||||
GIT_COMMIT
|
ARG GIT_COMMIT
|
||||||
RUN GIT_COMMIT=$GIT_COMMIT \
|
RUN GIT_COMMIT=$GIT_COMMIT \
|
||||||
GIT_TREE_STATE=$GIT_TREE_STATE \
|
GIT_TREE_STATE=$GIT_TREE_STATE \
|
||||||
GIT_TAG=$GIT_TAG \
|
GIT_TAG=$GIT_TAG \
|
||||||
@@ -134,7 +130,6 @@ RUN GIT_COMMIT=$GIT_COMMIT \
|
|||||||
# Final image
|
# Final image
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM argocd-base
|
FROM argocd-base
|
||||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
|
||||||
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
|
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
@@ -149,3 +144,4 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
|||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
||||||
|
|
||||||
USER $ARGOCD_USER_ID
|
USER $ARGOCD_USER_ID
|
||||||
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
FROM docker.io/library/golang:1.25.5@sha256:31c1e53dfc1cc2d269deec9c83f58729fa3c53dc9a576f6426109d1e319e9e9a
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
|
||||||
curl \
|
|
||||||
openssh-server \
|
|
||||||
nginx \
|
|
||||||
unzip \
|
|
||||||
fcgiwrap \
|
|
||||||
git \
|
|
||||||
git-lfs \
|
|
||||||
make \
|
|
||||||
wget \
|
|
||||||
gcc \
|
|
||||||
sudo \
|
|
||||||
zip \
|
|
||||||
tini \
|
|
||||||
gpg \
|
|
||||||
tzdata \
|
|
||||||
connect-proxy
|
|
||||||
|
|
||||||
RUN go install github.com/go-delve/delve/cmd/dlv@latest
|
|
||||||
|
|
||||||
COPY hack/install.sh hack/tool-versions.sh ./
|
|
||||||
COPY hack/installers installers
|
|
||||||
|
|
||||||
RUN ./install.sh helm && \
|
|
||||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
|
||||||
|
|
||||||
COPY hack/gpg-wrapper.sh \
|
|
||||||
hack/git-verify-wrapper.sh \
|
|
||||||
entrypoint.sh \
|
|
||||||
/usr/local/bin/
|
|
||||||
|
|
||||||
# support for mounting configuration from a configmap
|
|
||||||
WORKDIR /app/config/ssh
|
|
||||||
RUN touch ssh_known_hosts && \
|
|
||||||
ln -s /app/config/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts
|
|
||||||
|
|
||||||
WORKDIR /app/config
|
|
||||||
RUN mkdir -p tls && \
|
|
||||||
mkdir -p gpg/source && \
|
|
||||||
mkdir -p gpg/keys
|
|
||||||
|
|
||||||
COPY .tilt-bin/argocd_linux /usr/local/bin/argocd
|
|
||||||
|
|
||||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
|
||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server && \
|
|
||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller && \
|
|
||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex && \
|
|
||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-notifications && \
|
|
||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-applicationset-controller && \
|
|
||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
|
||||||
|
|
||||||
# directory for Tilt restart file
|
|
||||||
RUN mkdir -p /tilt
|
|
||||||
|
|
||||||
# overridden by Tiltfile
|
|
||||||
ENTRYPOINT ["/usr/bin/tini", "-s", "--", "dlv", "exec", "--continue", "--accept-multiclient", "--headless", "--listen=:2345", "--api-version=2"]
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
FROM node:20
|
|
||||||
|
|
||||||
WORKDIR /app/ui
|
|
||||||
|
|
||||||
COPY ui /app/ui
|
|
||||||
|
|
||||||
RUN yarn install
|
|
||||||
|
|
||||||
ENTRYPOINT ["yarn", "start"]
|
|
||||||
88
Makefile
88
Makefile
@@ -3,16 +3,7 @@ CURRENT_DIR=$(shell pwd)
|
|||||||
DIST_DIR=${CURRENT_DIR}/dist
|
DIST_DIR=${CURRENT_DIR}/dist
|
||||||
CLI_NAME=argocd
|
CLI_NAME=argocd
|
||||||
BIN_NAME=argocd
|
BIN_NAME=argocd
|
||||||
|
CGO_FLAG=0
|
||||||
UNAME_S:=$(shell uname)
|
|
||||||
IS_DARWIN:=$(if $(filter Darwin, $(UNAME_S)),true,false)
|
|
||||||
|
|
||||||
# When using OSX/Darwin, you might need to enable CGO for local builds
|
|
||||||
DEFAULT_CGO_FLAG:=0
|
|
||||||
ifeq ($(IS_DARWIN),true)
|
|
||||||
DEFAULT_CGO_FLAG:=1
|
|
||||||
endif
|
|
||||||
CGO_FLAG?=${DEFAULT_CGO_FLAG}
|
|
||||||
|
|
||||||
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
||||||
|
|
||||||
@@ -43,21 +34,10 @@ endif
|
|||||||
DOCKER_SRCDIR?=$(GOPATH)/src
|
DOCKER_SRCDIR?=$(GOPATH)/src
|
||||||
DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd
|
DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
# Allows you to control which Docker network the test-util containers attach to.
|
|
||||||
# This is particularly useful if you are running Kubernetes in Docker (e.g., k3d)
|
|
||||||
# and want the test containers to reach the Kubernetes API via an already-existing Docker network.
|
|
||||||
DOCKER_NETWORK ?= default
|
|
||||||
|
|
||||||
ifneq ($(DOCKER_NETWORK),default)
|
|
||||||
DOCKER_NETWORK_ARG := --network $(DOCKER_NETWORK)
|
|
||||||
else
|
|
||||||
DOCKER_NETWORK_ARG :=
|
|
||||||
endif
|
|
||||||
|
|
||||||
ARGOCD_PROCFILE?=Procfile
|
ARGOCD_PROCFILE?=Procfile
|
||||||
|
|
||||||
# pointing to python 3.12 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yaml
|
# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml
|
||||||
MKDOCS_DOCKER_IMAGE?=python:3.12-alpine
|
MKDOCS_DOCKER_IMAGE?=python:3.7-alpine
|
||||||
MKDOCS_RUN_ARGS?=
|
MKDOCS_RUN_ARGS?=
|
||||||
|
|
||||||
# Configuration for building argocd-test-tools image
|
# Configuration for building argocd-test-tools image
|
||||||
@@ -76,10 +56,8 @@ ARGOCD_E2E_REDIS_PORT?=6379
|
|||||||
ARGOCD_E2E_DEX_PORT?=5556
|
ARGOCD_E2E_DEX_PORT?=5556
|
||||||
ARGOCD_E2E_YARN_HOST?=localhost
|
ARGOCD_E2E_YARN_HOST?=localhost
|
||||||
ARGOCD_E2E_DISABLE_AUTH?=
|
ARGOCD_E2E_DISABLE_AUTH?=
|
||||||
ARGOCD_E2E_DIR?=/tmp/argo-e2e
|
|
||||||
|
|
||||||
ARGOCD_E2E_TEST_TIMEOUT?=90m
|
ARGOCD_E2E_TEST_TIMEOUT?=90m
|
||||||
ARGOCD_E2E_RERUN_FAILS?=5
|
|
||||||
|
|
||||||
ARGOCD_IN_CI?=false
|
ARGOCD_IN_CI?=false
|
||||||
ARGOCD_TEST_E2E?=true
|
ARGOCD_TEST_E2E?=true
|
||||||
@@ -126,11 +104,11 @@ define run-in-test-server
|
|||||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||||
|
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||||
-w ${DOCKER_WORKDIR} \
|
-w ${DOCKER_WORKDIR} \
|
||||||
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
||||||
-p 4000:4000 \
|
-p 4000:4000 \
|
||||||
-p 5000:5000 \
|
-p 5000:5000 \
|
||||||
$(DOCKER_NETWORK_ARG)\
|
|
||||||
$(PODMAN_ARGS) \
|
$(PODMAN_ARGS) \
|
||||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||||
bash -c "$(1)"
|
bash -c "$(1)"
|
||||||
@@ -151,8 +129,8 @@ define run-in-test-client
|
|||||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||||
|
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||||
-w ${DOCKER_WORKDIR} \
|
-w ${DOCKER_WORKDIR} \
|
||||||
$(DOCKER_NETWORK_ARG)\
|
|
||||||
$(PODMAN_ARGS) \
|
$(PODMAN_ARGS) \
|
||||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||||
bash -c "$(1)"
|
bash -c "$(1)"
|
||||||
@@ -169,11 +147,7 @@ PATH:=$(PATH):$(PWD)/hack
|
|||||||
DOCKER_PUSH?=false
|
DOCKER_PUSH?=false
|
||||||
IMAGE_NAMESPACE?=
|
IMAGE_NAMESPACE?=
|
||||||
# perform static compilation
|
# perform static compilation
|
||||||
DEFAULT_STATIC_BUILD:=true
|
STATIC_BUILD?=true
|
||||||
ifeq ($(IS_DARWIN),true)
|
|
||||||
DEFAULT_STATIC_BUILD:=false
|
|
||||||
endif
|
|
||||||
STATIC_BUILD?=${DEFAULT_STATIC_BUILD}
|
|
||||||
# build development images
|
# build development images
|
||||||
DEV_IMAGE?=false
|
DEV_IMAGE?=false
|
||||||
ARGOCD_GPG_ENABLED?=true
|
ARGOCD_GPG_ENABLED?=true
|
||||||
@@ -200,7 +174,7 @@ endif
|
|||||||
|
|
||||||
ifneq (${GIT_TAG},)
|
ifneq (${GIT_TAG},)
|
||||||
IMAGE_TAG=${GIT_TAG}
|
IMAGE_TAG=${GIT_TAG}
|
||||||
override LDFLAGS += -X ${PACKAGE}.gitTag=${GIT_TAG}
|
LDFLAGS += -X ${PACKAGE}.gitTag=${GIT_TAG}
|
||||||
else
|
else
|
||||||
IMAGE_TAG?=latest
|
IMAGE_TAG?=latest
|
||||||
endif
|
endif
|
||||||
@@ -215,10 +189,6 @@ ifdef IMAGE_NAMESPACE
|
|||||||
IMAGE_PREFIX=${IMAGE_NAMESPACE}/
|
IMAGE_PREFIX=${IMAGE_NAMESPACE}/
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifndef IMAGE_REGISTRY
|
|
||||||
IMAGE_REGISTRY="quay.io"
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: cli image
|
all: cli image
|
||||||
|
|
||||||
@@ -267,12 +237,8 @@ clidocsgen:
|
|||||||
actionsdocsgen:
|
actionsdocsgen:
|
||||||
hack/generate-actions-list.sh
|
hack/generate-actions-list.sh
|
||||||
|
|
||||||
.PHONY: resourceiconsgen
|
|
||||||
resourceiconsgen:
|
|
||||||
hack/generate-icons-typescript.sh
|
|
||||||
|
|
||||||
.PHONY: codegen-local
|
.PHONY: codegen-local
|
||||||
codegen-local: mod-vendor-local mockgen gogen protogen clientgen openapigen clidocsgen actionsdocsgen resourceiconsgen manifests-local notification-docs notification-catalog
|
codegen-local: mod-vendor-local mockgen gogen protogen clientgen openapigen clidocsgen actionsdocsgen manifests-local notification-docs notification-catalog
|
||||||
rm -rf vendor/
|
rm -rf vendor/
|
||||||
|
|
||||||
.PHONY: codegen-local-fast
|
.PHONY: codegen-local-fast
|
||||||
@@ -314,11 +280,12 @@ endif
|
|||||||
.PHONY: manifests-local
|
.PHONY: manifests-local
|
||||||
manifests-local:
|
manifests-local:
|
||||||
./hack/update-manifests.sh
|
./hack/update-manifests.sh
|
||||||
|
|
||||||
.PHONY: manifests
|
.PHONY: manifests
|
||||||
manifests: test-tools-image
|
manifests: test-tools-image
|
||||||
$(call run-in-test-client,make manifests-local IMAGE_REGISTRY='${IMAGE_REGISTRY}' IMAGE_NAMESPACE='${IMAGE_NAMESPACE}' IMAGE_REPOSITORY='${IMAGE_REPOSITORY}' IMAGE_TAG='${IMAGE_TAG}')
|
$(call run-in-test-client,make manifests-local IMAGE_NAMESPACE='${IMAGE_NAMESPACE}' IMAGE_TAG='${IMAGE_TAG}')
|
||||||
# consolidated binary for cli, util, server, repo-server, controller
|
|
||||||
|
|
||||||
|
# consolidated binary for cli, util, server, repo-server, controller
|
||||||
.PHONY: argocd-all
|
.PHONY: argocd-all
|
||||||
argocd-all: clean-debug
|
argocd-all: clean-debug
|
||||||
CGO_ENABLED=${CGO_FLAG} GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
CGO_ENABLED=${CGO_FLAG} GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||||
@@ -463,7 +430,7 @@ test-e2e:
|
|||||||
test-e2e-local: cli-local
|
test-e2e-local: cli-local
|
||||||
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
||||||
export GO111MODULE=off
|
export GO111MODULE=off
|
||||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=$(ARGOCD_E2E_RERUN_FAILS) PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
|
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
|
||||||
|
|
||||||
# Spawns a shell in the test server container for debugging purposes
|
# Spawns a shell in the test server container for debugging purposes
|
||||||
debug-test-server: test-tools-image
|
debug-test-server: test-tools-image
|
||||||
@@ -487,13 +454,13 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
|||||||
kubectl create ns argocd-e2e-external || true
|
kubectl create ns argocd-e2e-external || true
|
||||||
kubectl create ns argocd-e2e-external-2 || true
|
kubectl create ns argocd-e2e-external-2 || true
|
||||||
kubectl config set-context --current --namespace=argocd-e2e
|
kubectl config set-context --current --namespace=argocd-e2e
|
||||||
kustomize build test/manifests/base | kubectl apply --server-side --force-conflicts -f -
|
kustomize build test/manifests/base | kubectl apply -f -
|
||||||
kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml
|
kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml
|
||||||
# Create GPG keys and source directories
|
# Create GPG keys and source directories
|
||||||
if test -d $(ARGOCD_E2E_DIR)/app/config/gpg; then rm -rf $(ARGOCD_E2E_DIR)/app/config/gpg/*; fi
|
if test -d /tmp/argo-e2e/app/config/gpg; then rm -rf /tmp/argo-e2e/app/config/gpg/*; fi
|
||||||
mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/keys && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/keys
|
mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys
|
||||||
mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/source && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/source
|
mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source
|
||||||
mkdir -p $(ARGOCD_E2E_DIR)/app/config/plugin && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/plugin
|
mkdir -p /tmp/argo-e2e/app/config/plugin && chmod 0700 /tmp/argo-e2e/app/config/plugin
|
||||||
# create folders to hold go coverage results for each component
|
# create folders to hold go coverage results for each component
|
||||||
mkdir -p /tmp/coverage/app-controller
|
mkdir -p /tmp/coverage/app-controller
|
||||||
mkdir -p /tmp/coverage/api-server
|
mkdir -p /tmp/coverage/api-server
|
||||||
@@ -502,15 +469,13 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
|||||||
mkdir -p /tmp/coverage/notification
|
mkdir -p /tmp/coverage/notification
|
||||||
mkdir -p /tmp/coverage/commit-server
|
mkdir -p /tmp/coverage/commit-server
|
||||||
# set paths for locally managed ssh known hosts and tls certs data
|
# set paths for locally managed ssh known hosts and tls certs data
|
||||||
ARGOCD_E2E_DIR=$(ARGOCD_E2E_DIR) \
|
ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \
|
||||||
ARGOCD_SSH_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/ssh \
|
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
|
||||||
ARGOCD_TLS_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/tls \
|
ARGOCD_GPG_DATA_PATH=/tmp/argo-e2e/app/config/gpg/source \
|
||||||
ARGOCD_GPG_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/gpg/source \
|
ARGOCD_GNUPGHOME=/tmp/argo-e2e/app/config/gpg/keys \
|
||||||
ARGOCD_GNUPGHOME=$(ARGOCD_E2E_DIR)/app/config/gpg/keys \
|
|
||||||
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
||||||
ARGOCD_PLUGINCONFIGFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \
|
ARGOCD_PLUGINCONFIGFILEPATH=/tmp/argo-e2e/app/config/plugin \
|
||||||
ARGOCD_PLUGINSOCKFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \
|
ARGOCD_PLUGINSOCKFILEPATH=/tmp/argo-e2e/app/config/plugin \
|
||||||
ARGOCD_GIT_CONFIG=$(PWD)/test/e2e/fixture/gitconfig \
|
|
||||||
ARGOCD_E2E_DISABLE_AUTH=false \
|
ARGOCD_E2E_DISABLE_AUTH=false \
|
||||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||||
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
|
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
|
||||||
@@ -521,7 +486,6 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
|||||||
ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \
|
ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \
|
||||||
ARGOCD_E2E_TEST=true \
|
ARGOCD_E2E_TEST=true \
|
||||||
ARGOCD_HYDRATOR_ENABLED=true \
|
ARGOCD_HYDRATOR_ENABLED=true \
|
||||||
ARGOCD_CLUSTER_CACHE_EVENTS_PROCESSING_INTERVAL=1ms \
|
|
||||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||||
ls -lrt /tmp/coverage
|
ls -lrt /tmp/coverage
|
||||||
|
|
||||||
@@ -587,7 +551,7 @@ build-docs-local:
|
|||||||
|
|
||||||
.PHONY: build-docs
|
.PHONY: build-docs
|
||||||
build-docs:
|
build-docs:
|
||||||
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs build'
|
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install mkdocs; pip install $$(mkdocs get-deps); mkdocs build'
|
||||||
|
|
||||||
.PHONY: serve-docs-local
|
.PHONY: serve-docs-local
|
||||||
serve-docs-local:
|
serve-docs-local:
|
||||||
@@ -595,7 +559,7 @@ serve-docs-local:
|
|||||||
|
|
||||||
.PHONY: serve-docs
|
.PHONY: serve-docs
|
||||||
serve-docs:
|
serve-docs:
|
||||||
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs serve -a $$(ip route get 1 | awk '\''{print $$7}'\''):8000'
|
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install mkdocs; pip install $$(mkdocs get-deps); mkdocs serve -a $$(ip route get 1 | awk '\''{print $$7}'\''):8000'
|
||||||
|
|
||||||
# Verify that kubectl can connect to your K8s cluster from Docker
|
# Verify that kubectl can connect to your K8s cluster from Docker
|
||||||
.PHONY: verify-kube-connect
|
.PHONY: verify-kube-connect
|
||||||
@@ -620,13 +584,11 @@ install-test-tools-local:
|
|||||||
./hack/install.sh kustomize
|
./hack/install.sh kustomize
|
||||||
./hack/install.sh helm
|
./hack/install.sh helm
|
||||||
./hack/install.sh gotestsum
|
./hack/install.sh gotestsum
|
||||||
./hack/install.sh oras
|
|
||||||
|
|
||||||
# Installs all tools required for running codegen (Linux packages)
|
# Installs all tools required for running codegen (Linux packages)
|
||||||
.PHONY: install-codegen-tools-local
|
.PHONY: install-codegen-tools-local
|
||||||
install-codegen-tools-local:
|
install-codegen-tools-local:
|
||||||
./hack/install.sh codegen-tools
|
./hack/install.sh codegen-tools
|
||||||
./hack/install.sh codegen-go-tools
|
|
||||||
|
|
||||||
# Installs all tools required for running codegen (Go packages)
|
# Installs all tools required for running codegen (Go packages)
|
||||||
.PHONY: install-go-tools-local
|
.PHONY: install-go-tools-local
|
||||||
|
|||||||
7
Procfile
7
Procfile
@@ -2,13 +2,12 @@ controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run
|
|||||||
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}"
|
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}"
|
||||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v3/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v3/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
||||||
redis: hack/start-redis-with-password.sh
|
redis: hack/start-redis-with-password.sh
|
||||||
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "export PATH=./dist:\$PATH && [ -n \"\$ARGOCD_GIT_CONFIG\" ] && export GIT_CONFIG_GLOBAL=\$ARGOCD_GIT_CONFIG && export GIT_CONFIG_NOSYSTEM=1; GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||||
cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||||
commit-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/commit-server} FORCE_LOG_COLORS=1 ARGOCD_BINARY_NAME=argocd-commit-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_COMMITSERVER_PORT:-8086}"
|
commit-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/commit-server} FORCE_LOG_COLORS=1 ARGOCD_BINARY_NAME=argocd-commit-server $COMMAND --loglevel debug --port ${ARGOCD_E2E_COMMITSERVER_PORT:-8086}"
|
||||||
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
||||||
git-server: test/fixture/testrepos/start-git.sh
|
git-server: test/fixture/testrepos/start-git.sh
|
||||||
helm-registry: test/fixture/testrepos/start-helm-registry.sh
|
helm-registry: test/fixture/testrepos/start-helm-registry.sh
|
||||||
oci-registry: test/fixture/testrepos/start-authenticated-helm-registry.sh
|
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
|
||||||
dev-mounter: [ "$ARGOCD_E2E_TEST" != "true" ] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
|
|
||||||
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||||
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"
|
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"
|
||||||
@@ -3,9 +3,9 @@ header:
|
|||||||
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
|
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
|
||||||
last-updated: '2023-10-27'
|
last-updated: '2023-10-27'
|
||||||
last-reviewed: '2023-10-27'
|
last-reviewed: '2023-10-27'
|
||||||
commit-hash: 06ef059f9fc7cf9da2dfaef2a505ee1e3c693485
|
commit-hash: 74a367d10e7110209610ba3ec225539ebe5f7522
|
||||||
project-url: https://github.com/argoproj/argo-cd
|
project-url: https://github.com/argoproj/argo-cd
|
||||||
project-release: v3.3.0
|
project-release: v2.14.0
|
||||||
changelog: https://github.com/argoproj/argo-cd/releases
|
changelog: https://github.com/argoproj/argo-cd/releases
|
||||||
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
|
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
|
||||||
project-lifecycle:
|
project-lifecycle:
|
||||||
|
|||||||
314
Tiltfile
314
Tiltfile
@@ -1,314 +0,0 @@
|
|||||||
load('ext://restart_process', 'docker_build_with_restart')
|
|
||||||
load('ext://uibutton', 'cmd_button', 'location')
|
|
||||||
|
|
||||||
# add ui button in web ui to run make codegen-local (top nav)
|
|
||||||
cmd_button(
|
|
||||||
'make codegen-local',
|
|
||||||
argv=['sh', '-c', 'make codegen-local'],
|
|
||||||
location=location.NAV,
|
|
||||||
icon_name='terminal',
|
|
||||||
text='make codegen-local',
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd_button(
|
|
||||||
'make test-local',
|
|
||||||
argv=['sh', '-c', 'make test-local'],
|
|
||||||
location=location.NAV,
|
|
||||||
icon_name='science',
|
|
||||||
text='make test-local',
|
|
||||||
)
|
|
||||||
|
|
||||||
# add ui button in web ui to run make codegen-local (top nav)
|
|
||||||
cmd_button(
|
|
||||||
'make cli-local',
|
|
||||||
argv=['sh', '-c', 'make cli-local'],
|
|
||||||
location=location.NAV,
|
|
||||||
icon_name='terminal',
|
|
||||||
text='make cli-local',
|
|
||||||
)
|
|
||||||
|
|
||||||
# detect cluster architecture for build
|
|
||||||
cluster_version = decode_yaml(local('kubectl version -o yaml'))
|
|
||||||
platform = cluster_version['serverVersion']['platform']
|
|
||||||
arch = platform.split('/')[1]
|
|
||||||
|
|
||||||
# build the argocd binary on code changes
|
|
||||||
code_deps = [
|
|
||||||
'applicationset',
|
|
||||||
'cmd',
|
|
||||||
'cmpserver',
|
|
||||||
'commitserver',
|
|
||||||
'common',
|
|
||||||
'controller',
|
|
||||||
'notification-controller',
|
|
||||||
'pkg',
|
|
||||||
'reposerver',
|
|
||||||
'server',
|
|
||||||
'util',
|
|
||||||
'go.mod',
|
|
||||||
'go.sum',
|
|
||||||
]
|
|
||||||
local_resource(
|
|
||||||
'build',
|
|
||||||
'CGO_ENABLED=0 GOOS=linux GOARCH=' + arch + ' go build -gcflags="all=-N -l" -mod=readonly -o .tilt-bin/argocd_linux cmd/main.go',
|
|
||||||
deps = code_deps,
|
|
||||||
allow_parallel=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# deploy the argocd manifests
|
|
||||||
k8s_yaml(kustomize('manifests/dev-tilt'))
|
|
||||||
|
|
||||||
# build dev image
|
|
||||||
docker_build_with_restart(
|
|
||||||
'argocd',
|
|
||||||
context='.',
|
|
||||||
dockerfile='Dockerfile.tilt',
|
|
||||||
entrypoint=[
|
|
||||||
"/usr/bin/tini",
|
|
||||||
"-s",
|
|
||||||
"--",
|
|
||||||
"dlv",
|
|
||||||
"exec",
|
|
||||||
"--continue",
|
|
||||||
"--accept-multiclient",
|
|
||||||
"--headless",
|
|
||||||
"--listen=:2345",
|
|
||||||
"--api-version=2"
|
|
||||||
],
|
|
||||||
platform=platform,
|
|
||||||
live_update=[
|
|
||||||
sync('.tilt-bin/argocd_linux', '/usr/local/bin/argocd'),
|
|
||||||
],
|
|
||||||
only=[
|
|
||||||
'.tilt-bin',
|
|
||||||
'hack',
|
|
||||||
'entrypoint.sh',
|
|
||||||
],
|
|
||||||
restart_file='/tilt/.restart-proc'
|
|
||||||
)
|
|
||||||
|
|
||||||
# build image for argocd-cli jobs
|
|
||||||
docker_build(
|
|
||||||
'argocd-job',
|
|
||||||
context='.',
|
|
||||||
dockerfile='Dockerfile.tilt',
|
|
||||||
platform=platform,
|
|
||||||
only=[
|
|
||||||
'.tilt-bin',
|
|
||||||
'hack',
|
|
||||||
'entrypoint.sh',
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-server resources and port forward
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-server',
|
|
||||||
objects=[
|
|
||||||
'argocd-server:serviceaccount',
|
|
||||||
'argocd-server:role',
|
|
||||||
'argocd-server:rolebinding',
|
|
||||||
'argocd-cm:configmap',
|
|
||||||
'argocd-cmd-params-cm:configmap',
|
|
||||||
'argocd-gpg-keys-cm:configmap',
|
|
||||||
'argocd-rbac-cm:configmap',
|
|
||||||
'argocd-ssh-known-hosts-cm:configmap',
|
|
||||||
'argocd-tls-certs-cm:configmap',
|
|
||||||
'argocd-secret:secret',
|
|
||||||
'argocd-server-network-policy:networkpolicy',
|
|
||||||
'argocd-server:clusterrolebinding',
|
|
||||||
'argocd-server:clusterrole',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'8080:8080',
|
|
||||||
'9345:2345',
|
|
||||||
'8083:8083'
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track crds
|
|
||||||
k8s_resource(
|
|
||||||
new_name='cluster-resources',
|
|
||||||
objects=[
|
|
||||||
'applications.argoproj.io:customresourcedefinition',
|
|
||||||
'applicationsets.argoproj.io:customresourcedefinition',
|
|
||||||
'appprojects.argoproj.io:customresourcedefinition',
|
|
||||||
'argocd:namespace'
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-repo-server resources and port forward
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-repo-server',
|
|
||||||
objects=[
|
|
||||||
'argocd-repo-server:serviceaccount',
|
|
||||||
'argocd-repo-server-network-policy:networkpolicy',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'8081:8081',
|
|
||||||
'9346:2345',
|
|
||||||
'8084:8084'
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-redis resources and port forward
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-redis',
|
|
||||||
objects=[
|
|
||||||
'argocd-redis:serviceaccount',
|
|
||||||
'argocd-redis:role',
|
|
||||||
'argocd-redis:rolebinding',
|
|
||||||
'argocd-redis-network-policy:networkpolicy',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'6379:6379',
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-applicationset-controller resources
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-applicationset-controller',
|
|
||||||
objects=[
|
|
||||||
'argocd-applicationset-controller:serviceaccount',
|
|
||||||
'argocd-applicationset-controller-network-policy:networkpolicy',
|
|
||||||
'argocd-applicationset-controller:role',
|
|
||||||
'argocd-applicationset-controller:rolebinding',
|
|
||||||
'argocd-applicationset-controller:clusterrolebinding',
|
|
||||||
'argocd-applicationset-controller:clusterrole',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'9347:2345',
|
|
||||||
'8085:8080',
|
|
||||||
'7000:7000'
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-application-controller resources
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-application-controller',
|
|
||||||
objects=[
|
|
||||||
'argocd-application-controller:serviceaccount',
|
|
||||||
'argocd-application-controller-network-policy:networkpolicy',
|
|
||||||
'argocd-application-controller:role',
|
|
||||||
'argocd-application-controller:rolebinding',
|
|
||||||
'argocd-application-controller:clusterrolebinding',
|
|
||||||
'argocd-application-controller:clusterrole',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'9348:2345',
|
|
||||||
'8086:8082',
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-notifications-controller resources
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-notifications-controller',
|
|
||||||
objects=[
|
|
||||||
'argocd-notifications-controller:serviceaccount',
|
|
||||||
'argocd-notifications-controller-network-policy:networkpolicy',
|
|
||||||
'argocd-notifications-controller:role',
|
|
||||||
'argocd-notifications-controller:rolebinding',
|
|
||||||
'argocd-notifications-cm:configmap',
|
|
||||||
'argocd-notifications-secret:secret',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'9349:2345',
|
|
||||||
'8087:9001',
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-dex-server resources
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-dex-server',
|
|
||||||
objects=[
|
|
||||||
'argocd-dex-server:serviceaccount',
|
|
||||||
'argocd-dex-server-network-policy:networkpolicy',
|
|
||||||
'argocd-dex-server:role',
|
|
||||||
'argocd-dex-server:rolebinding',
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-commit-server resources
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-commit-server',
|
|
||||||
objects=[
|
|
||||||
'argocd-commit-server:serviceaccount',
|
|
||||||
'argocd-commit-server-network-policy:networkpolicy',
|
|
||||||
],
|
|
||||||
port_forwards=[
|
|
||||||
'9350:2345',
|
|
||||||
'8088:8087',
|
|
||||||
'8089:8086',
|
|
||||||
],
|
|
||||||
resource_deps=['build']
|
|
||||||
)
|
|
||||||
|
|
||||||
# ui dependencies
|
|
||||||
local_resource(
|
|
||||||
'node-modules',
|
|
||||||
'yarn',
|
|
||||||
dir='ui',
|
|
||||||
deps = [
|
|
||||||
'ui/package.json',
|
|
||||||
'ui/yarn.lock',
|
|
||||||
],
|
|
||||||
allow_parallel=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# docker for ui
|
|
||||||
docker_build(
|
|
||||||
'argocd-ui',
|
|
||||||
context='.',
|
|
||||||
dockerfile='Dockerfile.ui.tilt',
|
|
||||||
entrypoint=['sh', '-c', 'cd /app/ui && yarn start'],
|
|
||||||
only=['ui'],
|
|
||||||
live_update=[
|
|
||||||
sync('ui', '/app/ui'),
|
|
||||||
run('sh -c "cd /app/ui && yarn install"', trigger=['/app/ui/package.json', '/app/ui/yarn.lock']),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
# track argocd-ui resources and port forward
|
|
||||||
k8s_resource(
|
|
||||||
workload='argocd-ui',
|
|
||||||
port_forwards=[
|
|
||||||
'4000:4000',
|
|
||||||
],
|
|
||||||
resource_deps=['node-modules'],
|
|
||||||
)
|
|
||||||
|
|
||||||
# linting
|
|
||||||
local_resource(
|
|
||||||
'lint',
|
|
||||||
'make lint-local',
|
|
||||||
deps = code_deps,
|
|
||||||
allow_parallel=True,
|
|
||||||
resource_deps=['vendor']
|
|
||||||
)
|
|
||||||
|
|
||||||
local_resource(
|
|
||||||
'lint-ui',
|
|
||||||
'make lint-ui-local',
|
|
||||||
deps = [
|
|
||||||
'ui',
|
|
||||||
],
|
|
||||||
allow_parallel=True,
|
|
||||||
resource_deps=['node-modules'],
|
|
||||||
)
|
|
||||||
|
|
||||||
local_resource(
|
|
||||||
'vendor',
|
|
||||||
'go mod vendor',
|
|
||||||
deps = [
|
|
||||||
'go.mod',
|
|
||||||
'go.sum',
|
|
||||||
],
|
|
||||||
allow_parallel=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
41
USERS.md
41
USERS.md
@@ -5,10 +5,8 @@ PR with your organization name if you are using Argo CD.
|
|||||||
|
|
||||||
Currently, the following organizations are **officially** using Argo CD:
|
Currently, the following organizations are **officially** using Argo CD:
|
||||||
|
|
||||||
1. [100ms](https://www.100ms.ai/)
|
|
||||||
1. [127Labs](https://127labs.com/)
|
1. [127Labs](https://127labs.com/)
|
||||||
1. [3Rein](https://www.3rein.com/)
|
1. [3Rein](https://www.3rein.com/)
|
||||||
1. [42 School](https://42.fr/)
|
|
||||||
1. [4data](https://4data.ch/)
|
1. [4data](https://4data.ch/)
|
||||||
1. [7shifts](https://www.7shifts.com/)
|
1. [7shifts](https://www.7shifts.com/)
|
||||||
1. [Adevinta](https://www.adevinta.com/)
|
1. [Adevinta](https://www.adevinta.com/)
|
||||||
@@ -31,9 +29,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||||
1. [Ant Group](https://www.antgroup.com/)
|
1. [Ant Group](https://www.antgroup.com/)
|
||||||
1. [AppDirect](https://www.appdirect.com)
|
1. [AppDirect](https://www.appdirect.com)
|
||||||
1. [Arcadia](https://www.arcadia.io)
|
|
||||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||||
1. [Artemis Health by Nomi Health](https://www.artemishealth.com/)
|
|
||||||
1. [Arturia](https://www.arturia.com)
|
1. [Arturia](https://www.arturia.com)
|
||||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||||
1. [Augury](https://www.augury.com/)
|
1. [Augury](https://www.augury.com/)
|
||||||
@@ -43,14 +39,12 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Back Market](https://www.backmarket.com)
|
1. [Back Market](https://www.backmarket.com)
|
||||||
1. [Bajaj Finserv Health Ltd.](https://www.bajajfinservhealth.in)
|
1. [Bajaj Finserv Health Ltd.](https://www.bajajfinservhealth.in)
|
||||||
1. [Baloise](https://www.baloise.com)
|
1. [Baloise](https://www.baloise.com)
|
||||||
1. [Batumbu](https://batumbu.id)
|
|
||||||
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
||||||
1. [Beat](https://thebeat.co/en/)
|
1. [Beat](https://thebeat.co/en/)
|
||||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||||
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
||||||
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
||||||
1. [Believable Bots](https://believablebots.io)
|
1. [Believable Bots](https://believablebots.io)
|
||||||
1. [Bayer AG](https://bayer.com)
|
|
||||||
1. [BigPanda](https://bigpanda.io)
|
1. [BigPanda](https://bigpanda.io)
|
||||||
1. [BioBox Analytics](https://biobox.io)
|
1. [BioBox Analytics](https://biobox.io)
|
||||||
1. [BMW Group](https://www.bmwgroup.com/)
|
1. [BMW Group](https://www.bmwgroup.com/)
|
||||||
@@ -64,7 +58,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Camptocamp](https://camptocamp.com)
|
1. [Camptocamp](https://camptocamp.com)
|
||||||
1. [Candis](https://www.candis.io)
|
1. [Candis](https://www.candis.io)
|
||||||
1. [Capital One](https://www.capitalone.com)
|
1. [Capital One](https://www.capitalone.com)
|
||||||
1. [Capptain LTD](https://capptain.co/)
|
|
||||||
1. [CARFAX Europe](https://www.carfax.eu)
|
1. [CARFAX Europe](https://www.carfax.eu)
|
||||||
1. [CARFAX](https://www.carfax.com)
|
1. [CARFAX](https://www.carfax.com)
|
||||||
1. [Carrefour Group](https://www.carrefour.com)
|
1. [Carrefour Group](https://www.carrefour.com)
|
||||||
@@ -74,9 +67,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Chainnodes](https://chainnodes.org)
|
1. [Chainnodes](https://chainnodes.org)
|
||||||
1. [Chargetrip](https://chargetrip.com)
|
1. [Chargetrip](https://chargetrip.com)
|
||||||
1. [Chime](https://www.chime.com)
|
1. [Chime](https://www.chime.com)
|
||||||
1. [Chronicle Labs](https://chroniclelabs.org)
|
|
||||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||||
1. [Close](https://www.close.com/)
|
|
||||||
1. [Cloud Posse](https://www.cloudposse.com/)
|
1. [Cloud Posse](https://www.cloudposse.com/)
|
||||||
1. [Cloud Scale](https://cloudscaleinc.com/)
|
1. [Cloud Scale](https://cloudscaleinc.com/)
|
||||||
1. [CloudScript](https://www.cloudscript.com.br/)
|
1. [CloudScript](https://www.cloudscript.com.br/)
|
||||||
@@ -87,7 +78,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Codefresh](https://www.codefresh.io/)
|
1. [Codefresh](https://www.codefresh.io/)
|
||||||
1. [Codility](https://www.codility.com/)
|
1. [Codility](https://www.codility.com/)
|
||||||
1. [Cognizant](https://www.cognizant.com/)
|
1. [Cognizant](https://www.cognizant.com/)
|
||||||
1. [Collins Aerospace](https://www.collinsaerospace.com/)
|
|
||||||
1. [Commonbond](https://commonbond.co/)
|
1. [Commonbond](https://commonbond.co/)
|
||||||
1. [Compatio.AI](https://compatio.ai/)
|
1. [Compatio.AI](https://compatio.ai/)
|
||||||
1. [Contlo](https://contlo.com/)
|
1. [Contlo](https://contlo.com/)
|
||||||
@@ -101,18 +91,13 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Datarisk](https://www.datarisk.io/)
|
1. [Datarisk](https://www.datarisk.io/)
|
||||||
1. [Daydream](https://daydream.ing)
|
1. [Daydream](https://daydream.ing)
|
||||||
1. [Deloitte](https://www.deloitte.com/)
|
1. [Deloitte](https://www.deloitte.com/)
|
||||||
1. [Dematic](https://www.dematic.com)
|
|
||||||
1. [Deutsche Telekom AG](https://telekom.com)
|
1. [Deutsche Telekom AG](https://telekom.com)
|
||||||
1. [Deutsche Bank AG](https://www.deutsche-bank.de/)
|
|
||||||
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
||||||
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
||||||
1. [DigitalEd](https://www.digitaled.com)
|
|
||||||
1. [DigitalOcean](https://www.digitalocean.com)
|
1. [DigitalOcean](https://www.digitalocean.com)
|
||||||
1. [Divar](https://divar.ir)
|
1. [Divar](https://divar.ir)
|
||||||
1. [Divistant](https://divistant.com)
|
1. [Divistant](https://divistant.com)
|
||||||
2. [DocNetwork](https://docnetwork.org/)
|
|
||||||
1. [Dott](https://ridedott.com)
|
1. [Dott](https://ridedott.com)
|
||||||
1. [Doubble](https://www.doubble.app)
|
|
||||||
1. [Doximity](https://www.doximity.com/)
|
1. [Doximity](https://www.doximity.com/)
|
||||||
1. [EDF Renewables](https://www.edf-re.com/)
|
1. [EDF Renewables](https://www.edf-re.com/)
|
||||||
1. [edX](https://edx.org)
|
1. [edX](https://edx.org)
|
||||||
@@ -124,8 +109,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Energisme](https://energisme.com/)
|
1. [Energisme](https://energisme.com/)
|
||||||
1. [enigmo](https://enigmo.co.jp/)
|
1. [enigmo](https://enigmo.co.jp/)
|
||||||
1. [Envoy](https://envoy.com/)
|
1. [Envoy](https://envoy.com/)
|
||||||
1. [eSave](https://esave.es/)
|
|
||||||
1. [Expedia](https://www.expedia.com)
|
|
||||||
1. [Factorial](https://factorialhr.com/)
|
1. [Factorial](https://factorialhr.com/)
|
||||||
1. [Farfetch](https://www.farfetch.com)
|
1. [Farfetch](https://www.farfetch.com)
|
||||||
1. [Faro](https://www.faro.com/)
|
1. [Faro](https://www.faro.com/)
|
||||||
@@ -170,11 +153,9 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Hiya](https://hiya.com)
|
1. [Hiya](https://hiya.com)
|
||||||
1. [Honestbank](https://honestbank.com)
|
1. [Honestbank](https://honestbank.com)
|
||||||
1. [Hostinger](https://www.hostinger.com)
|
1. [Hostinger](https://www.hostinger.com)
|
||||||
1. [Hotjar](https://www.hotjar.com)
|
|
||||||
1. [IABAI](https://www.iab.ai)
|
1. [IABAI](https://www.iab.ai)
|
||||||
1. [IBM](https://www.ibm.com/)
|
1. [IBM](https://www.ibm.com/)
|
||||||
1. [Ibotta](https://home.ibotta.com)
|
1. [Ibotta](https://home.ibotta.com)
|
||||||
1. [Icelandair](https://www.icelandair.com)
|
|
||||||
1. [IFS](https://www.ifs.com)
|
1. [IFS](https://www.ifs.com)
|
||||||
1. [IITS-Consulting](https://iits-consulting.de)
|
1. [IITS-Consulting](https://iits-consulting.de)
|
||||||
1. [IllumiDesk](https://www.illumidesk.com)
|
1. [IllumiDesk](https://www.illumidesk.com)
|
||||||
@@ -184,9 +165,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Info Support](https://www.infosupport.com/)
|
1. [Info Support](https://www.infosupport.com/)
|
||||||
1. [InsideBoard](https://www.insideboard.com)
|
1. [InsideBoard](https://www.insideboard.com)
|
||||||
1. [Instruqt](https://www.instruqt.com)
|
1. [Instruqt](https://www.instruqt.com)
|
||||||
1. [Intel](https://www.intel.com)
|
|
||||||
1. [Intuit](https://www.intuit.com/)
|
1. [Intuit](https://www.intuit.com/)
|
||||||
1. [IQVIA](https://www.iqvia.com/)
|
|
||||||
1. [Jellysmack](https://www.jellysmack.com)
|
1. [Jellysmack](https://www.jellysmack.com)
|
||||||
1. [Joblift](https://joblift.com/)
|
1. [Joblift](https://joblift.com/)
|
||||||
1. [JovianX](https://www.jovianx.com/)
|
1. [JovianX](https://www.jovianx.com/)
|
||||||
@@ -238,7 +217,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [mixi Group](https://mixi.co.jp/)
|
1. [mixi Group](https://mixi.co.jp/)
|
||||||
1. [Moengage](https://www.moengage.com/)
|
1. [Moengage](https://www.moengage.com/)
|
||||||
1. [Money Forward](https://corp.moneyforward.com/en/)
|
1. [Money Forward](https://corp.moneyforward.com/en/)
|
||||||
1. [MongoDB](https://www.mongodb.com/)
|
|
||||||
1. [MOO Print](https://www.moo.com/)
|
1. [MOO Print](https://www.moo.com/)
|
||||||
1. [Mozilla](https://www.mozilla.org)
|
1. [Mozilla](https://www.mozilla.org)
|
||||||
1. [MTN Group](https://www.mtn.com/)
|
1. [MTN Group](https://www.mtn.com/)
|
||||||
@@ -281,7 +259,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Patreon](https://www.patreon.com/)
|
1. [Patreon](https://www.patreon.com/)
|
||||||
1. [PayIt](https://payitgov.com/)
|
1. [PayIt](https://payitgov.com/)
|
||||||
1. [PayPay](https://paypay.ne.jp/)
|
1. [PayPay](https://paypay.ne.jp/)
|
||||||
1. [Paystack](https://paystack.com/)
|
|
||||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||||
1. [Percona](https://percona.com/)
|
1. [Percona](https://percona.com/)
|
||||||
1. [PGS](https://www.pgs.com)
|
1. [PGS](https://www.pgs.com)
|
||||||
@@ -317,8 +294,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Relex Solutions](https://www.relexsolutions.com/)
|
1. [Relex Solutions](https://www.relexsolutions.com/)
|
||||||
1. [RightRev](https://rightrev.com/)
|
1. [RightRev](https://rightrev.com/)
|
||||||
1. [Rijkswaterstaat](https://www.rijkswaterstaat.nl/en)
|
1. [Rijkswaterstaat](https://www.rijkswaterstaat.nl/en)
|
||||||
1. Rise
|
1. [Rise](https://www.risecard.eu/)
|
||||||
1. [RISK IDENT](https://riskident.com/)
|
|
||||||
1. [Riskified](https://www.riskified.com/)
|
1. [Riskified](https://www.riskified.com/)
|
||||||
1. [Robotinfra](https://www.robotinfra.com)
|
1. [Robotinfra](https://www.robotinfra.com)
|
||||||
1. [Rocket.Chat](https://rocket.chat)
|
1. [Rocket.Chat](https://rocket.chat)
|
||||||
@@ -329,20 +305,16 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||||
1. [Sap Labs](http://sap.com)
|
1. [Sap Labs](http://sap.com)
|
||||||
1. [Sauce Labs](https://saucelabs.com/)
|
1. [Sauce Labs](https://saucelabs.com/)
|
||||||
1. [Schneider Electric](https://www.se.com)
|
|
||||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||||
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
||||||
1. [SEEK](https://seek.com.au)
|
1. [SEEK](https://seek.com.au)
|
||||||
1. [SEKAI](https://www.sekai.io/)
|
1. [SEKAI](https://www.sekai.io/)
|
||||||
1. [Semgrep](https://semgrep.com)
|
1. [Semgrep](https://semgrep.com)
|
||||||
1. [Seznam.cz](https://o-seznam.cz/)
|
|
||||||
1. [Shield](https://shield.com)
|
1. [Shield](https://shield.com)
|
||||||
1. [Shipfox](https://www.shipfox.io)
|
|
||||||
1. [Shock Media](https://www.shockmedia.nl)
|
|
||||||
1. [SI Analytics](https://si-analytics.ai)
|
1. [SI Analytics](https://si-analytics.ai)
|
||||||
1. [Sidewalk Entertainment](https://sidewalkplay.com/)
|
1. [Sidewalk Entertainment](https://sidewalkplay.com/)
|
||||||
1. [Skit](https://skit.ai/)
|
1. [Skit](https://skit.ai/)
|
||||||
1. [Skribble](https://skribble.com)
|
1. [Skribble](https://skribble.com)
|
||||||
1. [Skyscanner](https://www.skyscanner.net/)
|
1. [Skyscanner](https://www.skyscanner.net/)
|
||||||
1. [Smart Pension](https://www.smartpension.co.uk/)
|
1. [Smart Pension](https://www.smartpension.co.uk/)
|
||||||
1. [Smilee.io](https://smilee.io)
|
1. [Smilee.io](https://smilee.io)
|
||||||
@@ -351,7 +323,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Snapp](https://snapp.ir/)
|
1. [Snapp](https://snapp.ir/)
|
||||||
1. [Snyk](https://snyk.io/)
|
1. [Snyk](https://snyk.io/)
|
||||||
1. [Softway Medical](https://www.softwaymedical.fr/)
|
1. [Softway Medical](https://www.softwaymedical.fr/)
|
||||||
1. [Sophotech](https://sopho.tech)
|
|
||||||
1. [South China Morning Post (SCMP)](https://www.scmp.com/)
|
1. [South China Morning Post (SCMP)](https://www.scmp.com/)
|
||||||
1. [Speee](https://speee.jp/)
|
1. [Speee](https://speee.jp/)
|
||||||
1. [Spendesk](https://spendesk.com/)
|
1. [Spendesk](https://spendesk.com/)
|
||||||
@@ -364,13 +335,11 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Sumo Logic](https://sumologic.com/)
|
1. [Sumo Logic](https://sumologic.com/)
|
||||||
1. [Sutpc](http://www.sutpc.com/)
|
1. [Sutpc](http://www.sutpc.com/)
|
||||||
1. [Swiss Post](https://github.com/swisspost)
|
1. [Swiss Post](https://github.com/swisspost)
|
||||||
1. [Swissblock Technologies](https://swissblock.net/)
|
|
||||||
1. [Swisscom](https://www.swisscom.ch)
|
1. [Swisscom](https://www.swisscom.ch)
|
||||||
1. [Swissquote](https://github.com/swissquote)
|
1. [Swissquote](https://github.com/swissquote)
|
||||||
1. [Syncier](https://syncier.com/)
|
1. [Syncier](https://syncier.com/)
|
||||||
1. [Synergy](https://synergy.net.au)
|
1. [Synergy](https://synergy.net.au)
|
||||||
1. [Syself](https://syself.com)
|
1. [Syself](https://syself.com)
|
||||||
1. [T-ROC Global](https://trocglobal.com/)
|
|
||||||
1. [TableCheck](https://tablecheck.com/)
|
1. [TableCheck](https://tablecheck.com/)
|
||||||
1. [Tailor Brands](https://www.tailorbrands.com)
|
1. [Tailor Brands](https://www.tailorbrands.com)
|
||||||
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
||||||
@@ -385,7 +354,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Ticketmaster](https://ticketmaster.com)
|
1. [Ticketmaster](https://ticketmaster.com)
|
||||||
1. [Tiger Analytics](https://www.tigeranalytics.com/)
|
1. [Tiger Analytics](https://www.tigeranalytics.com/)
|
||||||
1. [Tigera](https://www.tigera.io/)
|
1. [Tigera](https://www.tigera.io/)
|
||||||
1. [Topicus.Education](https://topicus.nl/en/sectors/education)
|
|
||||||
1. [Toss](https://toss.im/en)
|
1. [Toss](https://toss.im/en)
|
||||||
1. [Trendyol](https://www.trendyol.com/)
|
1. [Trendyol](https://www.trendyol.com/)
|
||||||
1. [tru.ID](https://tru.id)
|
1. [tru.ID](https://tru.id)
|
||||||
@@ -413,7 +381,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Volvo Cars](https://www.volvocars.com/)
|
1. [Volvo Cars](https://www.volvocars.com/)
|
||||||
1. [Voyager Digital](https://www.investvoyager.com/)
|
1. [Voyager Digital](https://www.investvoyager.com/)
|
||||||
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
||||||
1. [Wakacje.pl](https://www.wakacje.pl/)
|
|
||||||
1. [Walkbase](https://www.walkbase.com/)
|
1. [Walkbase](https://www.walkbase.com/)
|
||||||
1. [Webstores](https://www.webstores.nl)
|
1. [Webstores](https://www.webstores.nl)
|
||||||
1. [Wehkamp](https://www.wehkamp.nl/)
|
1. [Wehkamp](https://www.wehkamp.nl/)
|
||||||
@@ -429,8 +396,8 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Yieldlab](https://www.yieldlab.de/)
|
1. [Yieldlab](https://www.yieldlab.de/)
|
||||||
1. [Youverify](https://youverify.co/)
|
1. [Youverify](https://youverify.co/)
|
||||||
1. [Yubo](https://www.yubo.live/)
|
1. [Yubo](https://www.yubo.live/)
|
||||||
1. [Yuno](https://y.uno/)
|
|
||||||
1. [ZDF](https://www.zdf.de/)
|
1. [ZDF](https://www.zdf.de/)
|
||||||
1. [Zimpler](https://www.zimpler.com/)
|
1. [Zimpler](https://www.zimpler.com/)
|
||||||
1. [ZipRecruiter](https://www.ziprecruiter.com/)
|
1. [ZipRecuiter](https://www.ziprecruiter.com/)
|
||||||
1. [ZOZO](https://corp.zozo.com/)
|
1. [ZOZO](https://corp.zozo.com/)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,6 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
|
||||||
"github.com/argoproj/argo-cd/v3/common"
|
"github.com/argoproj/argo-cd/v3/common"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
@@ -23,9 +22,8 @@ import (
|
|||||||
// requeue any related ApplicationSets.
|
// requeue any related ApplicationSets.
|
||||||
type clusterSecretEventHandler struct {
|
type clusterSecretEventHandler struct {
|
||||||
// handler.EnqueueRequestForOwner
|
// handler.EnqueueRequestForOwner
|
||||||
Log log.FieldLogger
|
Log log.FieldLogger
|
||||||
Client client.Client
|
Client client.Client
|
||||||
ApplicationSetNamespaces []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||||
@@ -70,10 +68,6 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
|||||||
|
|
||||||
h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets")
|
h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets")
|
||||||
for _, appSet := range appSetList.Items {
|
for _, appSet := range appSetList.Items {
|
||||||
if !utils.IsNamespaceAllowed(h.ApplicationSetNamespaces, appSet.GetNamespace()) {
|
|
||||||
// Ignore it as not part of the allowed list of namespaces in which to watch Appsets
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
foundClusterGenerator := false
|
foundClusterGenerator := false
|
||||||
for _, generator := range appSet.Spec.Generators {
|
for _, generator := range appSet.Spec.Generators {
|
||||||
if generator.Clusters != nil {
|
if generator.Clusters != nil {
|
||||||
|
|||||||
@@ -20,15 +20,6 @@ import (
|
|||||||
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockAddRateLimitingInterface struct {
|
|
||||||
addedItems []reconcile.Request
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add checks the type, and adds it to the internal list of received additions
|
|
||||||
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
|
|
||||||
obj.addedItems = append(obj.addedItems, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClusterEventHandler(t *testing.T) {
|
func TestClusterEventHandler(t *testing.T) {
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
err := argov1alpha1.AddToScheme(scheme)
|
err := argov1alpha1.AddToScheme(scheme)
|
||||||
@@ -137,7 +128,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "another-namespace",
|
||||||
},
|
},
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -171,37 +162,9 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRequests: []reconcile.Request{
|
expectedRequests: []reconcile.Request{
|
||||||
{NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}},
|
{NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "cluster generators in other namespaces should not match",
|
|
||||||
items: []argov1alpha1.ApplicationSet{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "my-app-set",
|
|
||||||
Namespace: "my-namespace-not-allowed",
|
|
||||||
},
|
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
|
||||||
{
|
|
||||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
secret: corev1.Secret{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Namespace: "argocd",
|
|
||||||
Name: "my-secret",
|
|
||||||
Labels: map[string]string{
|
|
||||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedRequests: []reconcile.Request{},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "non-argo cd secret should not match",
|
name: "non-argo cd secret should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
@@ -580,9 +543,8 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
|
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
|
||||||
|
|
||||||
handler := &clusterSecretEventHandler{
|
handler := &clusterSecretEventHandler{
|
||||||
Client: fakeClient,
|
Client: fakeClient,
|
||||||
Log: log.WithField("type", "createSecretEventHandler"),
|
Log: log.WithField("type", "createSecretEventHandler"),
|
||||||
ApplicationSetNamespaces: []string{"argocd"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
||||||
@@ -594,6 +556,15 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add checks the type, and adds it to the internal list of received additions
|
||||||
|
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
|
||||||
|
obj.addedItems = append(obj.addedItems, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockAddRateLimitingInterface struct {
|
||||||
|
addedItems []reconcile.Request
|
||||||
|
}
|
||||||
|
|
||||||
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
|
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
|
||||||
nested := argov1alpha1.ApplicationSetNestedGenerator{
|
nested := argov1alpha1.ApplicationSetNestedGenerator{
|
||||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
|
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
|
||||||
scmConfig := generators.NewSCMConfig("", []string{""}, true, true, nil, true)
|
scmConfig := generators.NewSCMConfig("", []string{""}, true, nil, true)
|
||||||
terminalGenerators := map[string]generators.Generator{
|
terminalGenerators := map[string]generators.Generator{
|
||||||
"List": generators.NewListGenerator(),
|
"List": generators.NewListGenerator(),
|
||||||
"Clusters": generators.NewClusterGenerator(ctx, k8sClient, appClientset, "argocd"),
|
"Clusters": generators.NewClusterGenerator(ctx, k8sClient, appClientset, "argocd"),
|
||||||
@@ -129,7 +129,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, ""}, want: generators.DefaultRequeueAfter, wantErr: assert.NoError},
|
}, ""}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||||
{name: "ClusterMatrixNested", args: args{&argov1alpha1.ApplicationSet{
|
{name: "ClusterMatrixNested", args: args{&argov1alpha1.ApplicationSet{
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -144,7 +144,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, ""}, want: generators.DefaultRequeueAfter, wantErr: assert.NoError},
|
}, ""}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||||
{name: "ListGenerator", args: args{appset: &argov1alpha1.ApplicationSet{
|
{name: "ListGenerator", args: args{appset: &argov1alpha1.ApplicationSet{
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{{List: &argov1alpha1.ListGenerator{}}},
|
Generators: []argov1alpha1.ApplicationSetGenerator{{List: &argov1alpha1.ListGenerator{}}},
|
||||||
@@ -154,7 +154,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{{ClusterDecisionResource: &argov1alpha1.DuckTypeGenerator{}}},
|
Generators: []argov1alpha1.ApplicationSetGenerator{{ClusterDecisionResource: &argov1alpha1.DuckTypeGenerator{}}},
|
||||||
},
|
},
|
||||||
}}, want: generators.DefaultRequeueAfter, wantErr: assert.NoError},
|
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||||
{name: "OverrideRequeueDuck", args: args{
|
{name: "OverrideRequeueDuck", args: args{
|
||||||
appset: &argov1alpha1.ApplicationSet{
|
appset: &argov1alpha1.ApplicationSet{
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{appv1.ResourcesFinalizerName},
|
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||||
},
|
},
|
||||||
Spec: appv1.ApplicationSpec{
|
Spec: appv1.ApplicationSpec{
|
||||||
Project: "default",
|
Project: "default",
|
||||||
@@ -72,7 +72,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{appv1.ResourcesFinalizerName},
|
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"annotation-some-key": "annotation-some-value",
|
"annotation-some-key": "annotation-some-value",
|
||||||
},
|
},
|
||||||
@@ -112,7 +112,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{appv1.ResourcesFinalizerName},
|
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||||
},
|
},
|
||||||
Spec: appv1.ApplicationSpec{
|
Spec: appv1.ApplicationSpec{
|
||||||
Project: "default",
|
Project: "default",
|
||||||
@@ -148,7 +148,7 @@ spec:
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{appv1.ResourcesFinalizerName},
|
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"annotation-some-key": "annotation-some-value",
|
"annotation-some-key": "annotation-some-value",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -86,28 +86,28 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run(cc.name, func(t *testing.T) {
|
t.Run(cc.name, func(t *testing.T) {
|
||||||
generatorMock := &genmock.Generator{}
|
generatorMock := genmock.Generator{}
|
||||||
generator := v1alpha1.ApplicationSetGenerator{
|
generator := v1alpha1.ApplicationSetGenerator{
|
||||||
List: &v1alpha1.ListGenerator{},
|
List: &v1alpha1.ListGenerator{},
|
||||||
}
|
}
|
||||||
|
|
||||||
generatorMock.EXPECT().GenerateParams(&generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||||
Return(cc.params, cc.generateParamsError)
|
Return(cc.params, cc.generateParamsError)
|
||||||
|
|
||||||
generatorMock.EXPECT().GetTemplate(&generator).
|
generatorMock.On("GetTemplate", &generator).
|
||||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
|
|
||||||
rendererMock := &rendmock.Renderer{}
|
rendererMock := rendmock.Renderer{}
|
||||||
|
|
||||||
var expectedApps []v1alpha1.Application
|
var expectedApps []v1alpha1.Application
|
||||||
|
|
||||||
if cc.generateParamsError == nil {
|
if cc.generateParamsError == nil {
|
||||||
for _, p := range cc.params {
|
for _, p := range cc.params {
|
||||||
if cc.rendererError != nil {
|
if cc.rendererError != nil {
|
||||||
rendererMock.EXPECT().RenderTemplateParams(GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)).
|
rendererMock.On("RenderTemplateParams", GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)).
|
||||||
Return(nil, cc.rendererError)
|
Return(nil, cc.rendererError)
|
||||||
} else {
|
} else {
|
||||||
rendererMock.EXPECT().RenderTemplateParams(GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)).
|
rendererMock.On("RenderTemplateParams", GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)).
|
||||||
Return(&app, nil)
|
Return(&app, nil)
|
||||||
expectedApps = append(expectedApps, app)
|
expectedApps = append(expectedApps, app)
|
||||||
}
|
}
|
||||||
@@ -115,9 +115,9 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generators := map[string]generators.Generator{
|
generators := map[string]generators.Generator{
|
||||||
"List": generatorMock,
|
"List": &generatorMock,
|
||||||
}
|
}
|
||||||
renderer := rendererMock
|
renderer := &rendererMock
|
||||||
|
|
||||||
got, reason, err := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
got, reason, err := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -200,26 +200,26 @@ func TestMergeTemplateApplications(t *testing.T) {
|
|||||||
cc := c
|
cc := c
|
||||||
|
|
||||||
t.Run(cc.name, func(t *testing.T) {
|
t.Run(cc.name, func(t *testing.T) {
|
||||||
generatorMock := &genmock.Generator{}
|
generatorMock := genmock.Generator{}
|
||||||
generator := v1alpha1.ApplicationSetGenerator{
|
generator := v1alpha1.ApplicationSetGenerator{
|
||||||
List: &v1alpha1.ListGenerator{},
|
List: &v1alpha1.ListGenerator{},
|
||||||
}
|
}
|
||||||
|
|
||||||
generatorMock.EXPECT().GenerateParams(&generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||||
Return(cc.params, nil)
|
Return(cc.params, nil)
|
||||||
|
|
||||||
generatorMock.EXPECT().GetTemplate(&generator).
|
generatorMock.On("GetTemplate", &generator).
|
||||||
Return(&cc.overrideTemplate)
|
Return(&cc.overrideTemplate)
|
||||||
|
|
||||||
rendererMock := &rendmock.Renderer{}
|
rendererMock := rendmock.Renderer{}
|
||||||
|
|
||||||
rendererMock.EXPECT().RenderTemplateParams(GetTempApplication(cc.expectedMerged), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), cc.params[0], false, []string(nil)).
|
rendererMock.On("RenderTemplateParams", GetTempApplication(cc.expectedMerged), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), cc.params[0], false, []string(nil)).
|
||||||
Return(&cc.expectedApps[0], nil)
|
Return(&cc.expectedApps[0], nil)
|
||||||
|
|
||||||
generators := map[string]generators.Generator{
|
generators := map[string]generators.Generator{
|
||||||
"List": generatorMock,
|
"List": &generatorMock,
|
||||||
}
|
}
|
||||||
renderer := rendererMock
|
renderer := &rendererMock
|
||||||
|
|
||||||
got, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
got, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -312,19 +312,19 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
|||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(cases.name, func(t *testing.T) {
|
t.Run(cases.name, func(t *testing.T) {
|
||||||
generatorMock := &genmock.Generator{}
|
generatorMock := genmock.Generator{}
|
||||||
generator := v1alpha1.ApplicationSetGenerator{
|
generator := v1alpha1.ApplicationSetGenerator{
|
||||||
PullRequest: &v1alpha1.PullRequestGenerator{},
|
PullRequest: &v1alpha1.PullRequestGenerator{},
|
||||||
}
|
}
|
||||||
|
|
||||||
generatorMock.EXPECT().GenerateParams(&generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||||
Return(cases.params, nil)
|
Return(cases.params, nil)
|
||||||
|
|
||||||
generatorMock.EXPECT().GetTemplate(&generator).
|
generatorMock.On("GetTemplate", &generator).
|
||||||
Return(&cases.template)
|
Return(&cases.template, nil)
|
||||||
|
|
||||||
generators := map[string]generators.Generator{
|
generators := map[string]generators.Generator{
|
||||||
"PullRequest": generatorMock,
|
"PullRequest": &generatorMock,
|
||||||
}
|
}
|
||||||
renderer := &utils.Render{}
|
renderer := &utils.Render{}
|
||||||
|
|
||||||
@@ -341,10 +341,10 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
|||||||
renderer,
|
renderer,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
assert.Equal(t, cases.expectedApp[0].Name, gotApp[0].Name)
|
assert.EqualValues(t, cases.expectedApp[0].ObjectMeta.Name, gotApp[0].ObjectMeta.Name)
|
||||||
assert.Equal(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision)
|
assert.EqualValues(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision)
|
||||||
assert.Equal(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace)
|
assert.EqualValues(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace)
|
||||||
assert.True(t, maps.Equal(cases.expectedApp[0].Labels, gotApp[0].Labels))
|
assert.True(t, maps.Equal(cases.expectedApp[0].ObjectMeta.Labels, gotApp[0].ObjectMeta.Labels))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: quay.io/argoprojlabs/argocd-e2e-container
|
repository: gcr.io/heptio-images/ks-guestbook-demo
|
||||||
tag: 0.1
|
tag: 0.1
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: ApplicationSet
|
|
||||||
metadata:
|
|
||||||
name: guestbook
|
|
||||||
spec:
|
|
||||||
generators:
|
|
||||||
- git:
|
|
||||||
repoURL: https://github.com/argoproj/argo-cd.git
|
|
||||||
revision: HEAD
|
|
||||||
files:
|
|
||||||
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
|
|
||||||
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/*/dev/config.json"
|
|
||||||
exclude: true
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
name: '{{cluster.name}}-guestbook'
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
source:
|
|
||||||
repoURL: https://github.com/argoproj/argo-cd.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: "applicationset/examples/git-generator-files-discovery/apps/guestbook"
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
#server: '{{cluster.address}}'
|
|
||||||
namespace: guestbook
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: ApplicationSet
|
|
||||||
metadata:
|
|
||||||
name: guestbook
|
|
||||||
spec:
|
|
||||||
goTemplate: true
|
|
||||||
goTemplateOptions: ["missingkey=error"]
|
|
||||||
generators:
|
|
||||||
- git:
|
|
||||||
repoURL: https://github.com/argoproj/argo-cd.git
|
|
||||||
revision: HEAD
|
|
||||||
files:
|
|
||||||
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
|
|
||||||
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/*/dev/config.json"
|
|
||||||
exclude: true
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
name: '{{.cluster.name}}-guestbook'
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
source:
|
|
||||||
repoURL: https://github.com/argoproj/argo-cd.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: "applicationset/examples/git-generator-files-discovery/apps/guestbook"
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: guestbook
|
|
||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
@@ -25,17 +27,21 @@ type ClusterGenerator struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
clientset kubernetes.Interface
|
clientset kubernetes.Interface
|
||||||
// namespace is the Argo CD namespace
|
// namespace is the Argo CD namespace
|
||||||
namespace string
|
namespace string
|
||||||
|
settingsManager *settings.SettingsManager
|
||||||
}
|
}
|
||||||
|
|
||||||
var render = &utils.Render{}
|
var render = &utils.Render{}
|
||||||
|
|
||||||
func NewClusterGenerator(ctx context.Context, c client.Client, clientset kubernetes.Interface, namespace string) Generator {
|
func NewClusterGenerator(ctx context.Context, c client.Client, clientset kubernetes.Interface, namespace string) Generator {
|
||||||
|
settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
|
||||||
|
|
||||||
g := &ClusterGenerator{
|
g := &ClusterGenerator{
|
||||||
Client: c,
|
Client: c,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
clientset: clientset,
|
clientset: clientset,
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
|
settingsManager: settingsManager,
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@@ -53,11 +59,11 @@ func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.Appli
|
|||||||
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
logCtx := log.WithField("applicationset", appSet.GetName()).WithField("namespace", appSet.GetNamespace())
|
logCtx := log.WithField("applicationset", appSet.GetName()).WithField("namespace", appSet.GetNamespace())
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.Clusters == nil {
|
if appSetGenerator.Clusters == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not include the local cluster in the cluster parameters IF there is a non-empty selector
|
// Do not include the local cluster in the cluster parameters IF there is a non-empty selector
|
||||||
@@ -79,10 +85,14 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
|||||||
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramHolder := ¶mHolder{isFlatMode: appSetGenerator.Clusters.FlatList}
|
res := []map[string]any{}
|
||||||
logCtx.Debugf("Using flat mode = %t for cluster generator", paramHolder.isFlatMode)
|
|
||||||
|
|
||||||
secretsFound := []corev1.Secret{}
|
secretsFound := []corev1.Secret{}
|
||||||
|
|
||||||
|
isFlatMode := appSetGenerator.Clusters.FlatList
|
||||||
|
logCtx.Debugf("Using flat mode = %t for cluster generator", isFlatMode)
|
||||||
|
clustersParams := make([]map[string]any, 0)
|
||||||
|
|
||||||
for _, cluster := range clustersFromArgoCD {
|
for _, cluster := range clustersFromArgoCD {
|
||||||
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
|
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
|
||||||
// handled by the next step.
|
// handled by the next step.
|
||||||
@@ -101,80 +111,72 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
|||||||
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramHolder.append(params)
|
if isFlatMode {
|
||||||
|
clustersParams = append(clustersParams, params)
|
||||||
|
} else {
|
||||||
|
res = append(res, params)
|
||||||
|
}
|
||||||
|
|
||||||
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each matching cluster secret (non-local clusters only)
|
// For each matching cluster secret (non-local clusters only)
|
||||||
for _, cluster := range secretsFound {
|
for _, cluster := range secretsFound {
|
||||||
params := g.getClusterParameters(cluster, appSet)
|
params := map[string]any{}
|
||||||
|
|
||||||
|
params["name"] = string(cluster.Data["name"])
|
||||||
|
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
|
||||||
|
params["server"] = string(cluster.Data["server"])
|
||||||
|
|
||||||
|
project, ok := cluster.Data["project"]
|
||||||
|
if ok {
|
||||||
|
params["project"] = string(project)
|
||||||
|
} else {
|
||||||
|
params["project"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if appSet.Spec.GoTemplate {
|
||||||
|
meta := map[string]any{}
|
||||||
|
|
||||||
|
if len(cluster.ObjectMeta.Annotations) > 0 {
|
||||||
|
meta["annotations"] = cluster.ObjectMeta.Annotations
|
||||||
|
}
|
||||||
|
if len(cluster.ObjectMeta.Labels) > 0 {
|
||||||
|
meta["labels"] = cluster.ObjectMeta.Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
params["metadata"] = meta
|
||||||
|
} else {
|
||||||
|
for key, value := range cluster.ObjectMeta.Annotations {
|
||||||
|
params["metadata.annotations."+key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range cluster.ObjectMeta.Labels {
|
||||||
|
params["metadata.labels."+key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
|
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramHolder.append(params)
|
if isFlatMode {
|
||||||
|
clustersParams = append(clustersParams, params)
|
||||||
|
} else {
|
||||||
|
res = append(res, params)
|
||||||
|
}
|
||||||
|
|
||||||
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
|
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
|
||||||
}
|
}
|
||||||
|
|
||||||
return paramHolder.consolidate(), nil
|
if isFlatMode {
|
||||||
}
|
res = append(res, map[string]any{
|
||||||
|
"clusters": clustersParams,
|
||||||
type paramHolder struct {
|
})
|
||||||
isFlatMode bool
|
|
||||||
params []map[string]any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *paramHolder) append(params map[string]any) {
|
|
||||||
p.params = append(p.params, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *paramHolder) consolidate() []map[string]any {
|
|
||||||
if p.isFlatMode {
|
|
||||||
p.params = []map[string]any{
|
|
||||||
{"clusters": p.params},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return p.params
|
return res, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (g *ClusterGenerator) getClusterParameters(cluster corev1.Secret, appSet *argoappsetv1alpha1.ApplicationSet) map[string]any {
|
|
||||||
params := map[string]any{}
|
|
||||||
|
|
||||||
params["name"] = string(cluster.Data["name"])
|
|
||||||
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
|
|
||||||
params["server"] = string(cluster.Data["server"])
|
|
||||||
|
|
||||||
project, ok := cluster.Data["project"]
|
|
||||||
if ok {
|
|
||||||
params["project"] = string(project)
|
|
||||||
} else {
|
|
||||||
params["project"] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if appSet.Spec.GoTemplate {
|
|
||||||
meta := map[string]any{}
|
|
||||||
|
|
||||||
if len(cluster.Annotations) > 0 {
|
|
||||||
meta["annotations"] = cluster.Annotations
|
|
||||||
}
|
|
||||||
if len(cluster.Labels) > 0 {
|
|
||||||
meta["labels"] = cluster.Labels
|
|
||||||
}
|
|
||||||
|
|
||||||
params["metadata"] = meta
|
|
||||||
} else {
|
|
||||||
for key, value := range cluster.Annotations {
|
|
||||||
params["metadata.annotations."+key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range cluster.Labels {
|
|
||||||
params["metadata.labels."+key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
||||||
@@ -186,7 +188,7 @@ func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerat
|
|||||||
return nil, fmt.Errorf("error converting label selector: %w", err)
|
return nil, fmt.Errorf("error converting label selector: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := g.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
if err := g.Client.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
@@ -25,18 +26,22 @@ var _ Generator = (*DuckTypeGenerator)(nil)
|
|||||||
|
|
||||||
// DuckTypeGenerator generates Applications for some or all clusters registered with ArgoCD.
|
// DuckTypeGenerator generates Applications for some or all clusters registered with ArgoCD.
|
||||||
type DuckTypeGenerator struct {
|
type DuckTypeGenerator struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dynClient dynamic.Interface
|
dynClient dynamic.Interface
|
||||||
clientset kubernetes.Interface
|
clientset kubernetes.Interface
|
||||||
namespace string // namespace is the Argo CD namespace
|
namespace string // namespace is the Argo CD namespace
|
||||||
|
settingsManager *settings.SettingsManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
|
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
|
||||||
|
settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
|
||||||
|
|
||||||
g := &DuckTypeGenerator{
|
g := &DuckTypeGenerator{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
dynClient: dynClient,
|
dynClient: dynClient,
|
||||||
clientset: clientset,
|
clientset: clientset,
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
|
settingsManager: settingsManager,
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@@ -57,12 +62,12 @@ func (g *DuckTypeGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Appl
|
|||||||
|
|
||||||
func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not likely to happen
|
// Not likely to happen
|
||||||
if appSetGenerator.ClusterDecisionResource == nil {
|
if appSetGenerator.ClusterDecisionResource == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
||||||
@@ -92,13 +97,13 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
|||||||
// Validate the fields
|
// Validate the fields
|
||||||
if kind == "" || versionIdx < 1 {
|
if kind == "" || versionIdx < 1 {
|
||||||
log.Warningf("kind=%v, resourceName=%v, versionIdx=%v", kind, resourceName, versionIdx)
|
log.Warningf("kind=%v, resourceName=%v, versionIdx=%v", kind, resourceName, versionIdx)
|
||||||
return nil, errors.New("there is a problem with the apiVersion, kind or resourceName provided")
|
return nil, errors.New("There is a problem with the apiVersion, kind or resourceName provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resourceName == "" && labelSelector.MatchLabels == nil && labelSelector.MatchExpressions == nil) ||
|
if (resourceName == "" && labelSelector.MatchLabels == nil && labelSelector.MatchExpressions == nil) ||
|
||||||
(resourceName != "" && (labelSelector.MatchExpressions != nil || labelSelector.MatchLabels != nil)) {
|
(resourceName != "" && (labelSelector.MatchExpressions != nil || labelSelector.MatchLabels != nil)) {
|
||||||
log.Warningf("You must choose either resourceName=%v, labelSelector.matchLabels=%v or labelSelect.matchExpressions=%v", resourceName, labelSelector.MatchLabels, labelSelector.MatchExpressions)
|
log.Warningf("You must choose either resourceName=%v, labelSelector.matchLabels=%v or labelSelect.matchExpressions=%v", resourceName, labelSelector.MatchLabels, labelSelector.MatchExpressions)
|
||||||
return nil, errors.New("there is a problem with the definition of the ClusterDecisionResource generator")
|
return nil, errors.New("There is a problem with the definition of the ClusterDecisionResource generator")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split up the apiVersion
|
// Split up the apiVersion
|
||||||
@@ -131,51 +136,18 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
|||||||
|
|
||||||
// Override the duck type in the status of the resource
|
// Override the duck type in the status of the resource
|
||||||
statusListKey := "clusters"
|
statusListKey := "clusters"
|
||||||
|
|
||||||
|
matchKey := cm.Data["matchKey"]
|
||||||
|
|
||||||
if cm.Data["statusListKey"] != "" {
|
if cm.Data["statusListKey"] != "" {
|
||||||
statusListKey = cm.Data["statusListKey"]
|
statusListKey = cm.Data["statusListKey"]
|
||||||
}
|
}
|
||||||
|
|
||||||
matchKey := cm.Data["matchKey"]
|
|
||||||
if matchKey == "" {
|
if matchKey == "" {
|
||||||
log.WithField("matchKey", matchKey).Warning("matchKey not found in " + cm.Name)
|
log.WithField("matchKey", matchKey).Warning("matchKey not found in " + cm.Name)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterDecisions := buildClusterDecisions(duckResources, statusListKey)
|
|
||||||
if len(clusterDecisions) == 0 {
|
|
||||||
log.Warningf("clusterDecisionResource status.%s missing", statusListKey)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res := []map[string]any{}
|
res := []map[string]any{}
|
||||||
for _, clusterDecision := range clusterDecisions {
|
|
||||||
cluster := findCluster(clustersFromArgoCD, clusterDecision, matchKey, statusListKey)
|
|
||||||
// if no cluster is found, move to the next cluster
|
|
||||||
if cluster == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// generated instance of cluster params
|
|
||||||
params := map[string]any{
|
|
||||||
"name": cluster.Name,
|
|
||||||
"server": cluster.Server,
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range clusterDecision.(map[string]any) {
|
|
||||||
params[key] = value.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range appSetGenerator.ClusterDecisionResource.Values {
|
|
||||||
collectParams(appSet, params, key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildClusterDecisions(duckResources *unstructured.UnstructuredList, statusListKey string) []any {
|
|
||||||
clusterDecisions := []any{}
|
clusterDecisions := []any{}
|
||||||
|
|
||||||
// Build the decision slice
|
// Build the decision slice
|
||||||
@@ -192,38 +164,60 @@ func buildClusterDecisions(duckResources *unstructured.UnstructuredList, statusL
|
|||||||
clusterDecisions = append(clusterDecisions, duckResource.Object["status"].(map[string]any)[statusListKey].([]any)...)
|
clusterDecisions = append(clusterDecisions, duckResource.Object["status"].(map[string]any)[statusListKey].([]any)...)
|
||||||
}
|
}
|
||||||
log.Infof("Number of decisions found: %v", len(clusterDecisions))
|
log.Infof("Number of decisions found: %v", len(clusterDecisions))
|
||||||
return clusterDecisions
|
|
||||||
}
|
|
||||||
|
|
||||||
func findCluster(clustersFromArgoCD []utils.ClusterSpecifier, cluster any, matchKey string, statusListKey string) *utils.ClusterSpecifier {
|
if len(clusterDecisions) == 0 {
|
||||||
log.Infof("cluster: %v", cluster)
|
log.Warningf("clusterDecisionResource status.%s missing", statusListKey)
|
||||||
matchValue := cluster.(map[string]any)[matchKey]
|
return nil, nil
|
||||||
if matchValue == nil || matchValue.(string) == "" {
|
|
||||||
log.Warningf("matchKey=%v not found in \"%v\" list: %v\n", matchKey, statusListKey, cluster.(map[string]any))
|
|
||||||
return nil // no match
|
|
||||||
}
|
}
|
||||||
|
for _, cluster := range clusterDecisions {
|
||||||
|
// generated instance of cluster params
|
||||||
|
params := map[string]any{}
|
||||||
|
|
||||||
strMatchValue := matchValue.(string)
|
log.Infof("cluster: %v", cluster)
|
||||||
log.WithField(matchKey, strMatchValue).Debug("validate against ArgoCD")
|
matchValue := cluster.(map[string]any)[matchKey]
|
||||||
|
if matchValue == nil || matchValue.(string) == "" {
|
||||||
for _, argoCluster := range clustersFromArgoCD {
|
log.Warningf("matchKey=%v not found in \"%v\" list: %v\n", matchKey, statusListKey, cluster.(map[string]any))
|
||||||
if argoCluster.Name == strMatchValue {
|
continue
|
||||||
log.WithField(matchKey, argoCluster.Name).Info("matched cluster in ArgoCD")
|
|
||||||
return &argoCluster
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
log.WithField(matchKey, strMatchValue).Warning("unmatched cluster in ArgoCD")
|
strMatchValue := matchValue.(string)
|
||||||
return nil
|
log.WithField(matchKey, strMatchValue).Debug("validate against ArgoCD")
|
||||||
}
|
|
||||||
|
|
||||||
func collectParams(appSet *argoprojiov1alpha1.ApplicationSet, params map[string]any, key string, value string) {
|
found := false
|
||||||
if appSet.Spec.GoTemplate {
|
|
||||||
if params["values"] == nil {
|
for _, argoCluster := range clustersFromArgoCD {
|
||||||
params["values"] = map[string]string{}
|
if argoCluster.Name == strMatchValue {
|
||||||
|
log.WithField(matchKey, argoCluster.Name).Info("matched cluster in ArgoCD")
|
||||||
|
params["name"] = argoCluster.Name
|
||||||
|
params["server"] = argoCluster.Server
|
||||||
|
|
||||||
|
found = true
|
||||||
|
break // Stop looking
|
||||||
|
}
|
||||||
}
|
}
|
||||||
params["values"].(map[string]string)[key] = value
|
|
||||||
} else {
|
if !found {
|
||||||
params["values."+key] = value
|
log.WithField(matchKey, strMatchValue).Warning("unmatched cluster in ArgoCD")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range cluster.(map[string]any) {
|
||||||
|
params[key] = value.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range appSetGenerator.ClusterDecisionResource.Values {
|
||||||
|
if appSet.Spec.GoTemplate {
|
||||||
|
if params["values"] == nil {
|
||||||
|
params["values"] = map[string]string{}
|
||||||
|
}
|
||||||
|
params["values"].(map[string]string)[key] = value
|
||||||
|
} else {
|
||||||
|
params["values."+key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]any{},
|
expected: []map[string]any{},
|
||||||
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("There is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
||||||
{
|
{
|
||||||
@@ -270,7 +270,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("There is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +455,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]any{},
|
expected: []map[string]any{},
|
||||||
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("There is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
||||||
{
|
{
|
||||||
@@ -566,7 +566,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("There is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ func TestTransForm(t *testing.T) {
|
|||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
testGenerators := map[string]Generator{
|
testGenerators := map[string]Generator{
|
||||||
"Clusters": getMockClusterGenerator(t.Context()),
|
"Clusters": getMockClusterGenerator(),
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationSetInfo := argov1alpha1.ApplicationSet{
|
applicationSetInfo := argov1alpha1.ApplicationSet{
|
||||||
@@ -260,7 +260,7 @@ func emptyTemplate() argov1alpha1.ApplicationSetTemplate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMockClusterGenerator(ctx context.Context) Generator {
|
func getMockClusterGenerator() Generator {
|
||||||
clusters := []crtclient.Object{
|
clusters := []crtclient.Object{
|
||||||
&corev1.Secret{
|
&corev1.Secret{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
@@ -342,19 +342,19 @@ func getMockClusterGenerator(ctx context.Context) Generator {
|
|||||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||||
|
|
||||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||||
return NewClusterGenerator(ctx, fakeClient, appClientset, "namespace")
|
return NewClusterGenerator(context.Background(), fakeClient, appClientset, "namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMockGitGenerator() Generator {
|
func getMockGitGenerator() Generator {
|
||||||
argoCDServiceMock := &mocks.Repos{}
|
argoCDServiceMock := mocks.Repos{}
|
||||||
argoCDServiceMock.EXPECT().GetDirectories(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
||||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "namespace")
|
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||||
return gitGenerator
|
return gitGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRelevantGenerators(t *testing.T) {
|
func TestGetRelevantGenerators(t *testing.T) {
|
||||||
testGenerators := map[string]Generator{
|
testGenerators := map[string]Generator{
|
||||||
"Clusters": getMockClusterGenerator(t.Context()),
|
"Clusters": getMockClusterGenerator(),
|
||||||
"Git": getMockGitGenerator(),
|
"Git": getMockGitGenerator(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,7 +551,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
|||||||
},
|
},
|
||||||
useGoTemplate: true,
|
useGoTemplate: true,
|
||||||
goTemplateOptions: []string{},
|
goTemplateOptions: []string{},
|
||||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template {{ index .rmap (default .override .test) }}: template: base:1:3: executing \"base\" at <index .rmap (default .override .test)>: error calling index: index of untyped nil"},
|
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template {{ index .rmap (default .override .test) }}: template: :1:3: executing \"\" at <index .rmap (default .override .test)>: error calling index: index of untyped nil"},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -28,27 +28,22 @@ type GitGenerator struct {
|
|||||||
namespace string
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGitGenerator creates a new instance of Git Generator
|
func NewGitGenerator(repos services.Repos, namespace string) Generator {
|
||||||
func NewGitGenerator(repos services.Repos, controllerNamespace string) Generator {
|
|
||||||
g := &GitGenerator{
|
g := &GitGenerator{
|
||||||
repos: repos,
|
repos: repos,
|
||||||
namespace: controllerNamespace,
|
namespace: namespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTemplate returns the ApplicationSetTemplate associated with the Git generator
|
|
||||||
// from the provided ApplicationSetGenerator. This template defines how each
|
|
||||||
// generated Argo CD Application should be rendered.
|
|
||||||
func (g *GitGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *GitGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
return &appSetGenerator.Git.Template
|
return &appSetGenerator.Git.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequeueAfter returns the duration after which the Git generator should be
|
|
||||||
// requeued for reconciliation. If RequeueAfterSeconds is set in the generator spec,
|
|
||||||
// it uses that value. Otherwise, it falls back to a default requeue interval (3 minutes).
|
|
||||||
func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
|
// Return a requeue default of 3 minutes, if no default is specified.
|
||||||
|
|
||||||
if appSetGenerator.Git.RequeueAfterSeconds != nil {
|
if appSetGenerator.Git.RequeueAfterSeconds != nil {
|
||||||
return time.Duration(*appSetGenerator.Git.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.Git.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
@@ -56,15 +51,13 @@ func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appli
|
|||||||
return getDefaultRequeueAfter()
|
return getDefaultRequeueAfter()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateParams generates a list of parameter maps for the ApplicationSet by evaluating the Git generator's configuration.
|
|
||||||
// It supports both directory-based and file-based Git generators.
|
|
||||||
func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.Git == nil {
|
if appSetGenerator.Git == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
noRevisionCache := appSet.RefreshRequired()
|
noRevisionCache := appSet.RefreshRequired()
|
||||||
@@ -74,15 +67,14 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
|||||||
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
|
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
|
||||||
// but git generator cannot be called without verifying the commit signature.
|
// but git generator cannot be called without verifying the commit signature.
|
||||||
// In this case, we skip the signature verification.
|
// In this case, we skip the signature verification.
|
||||||
// If the project is templated, we skip the commit verification
|
|
||||||
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||||
project := appSet.Spec.Template.Spec.Project
|
project := appSet.Spec.Template.Spec.Project
|
||||||
appProject := &argoprojiov1alpha1.AppProject{}
|
appProject := &argoprojiov1alpha1.AppProject{}
|
||||||
controllerNamespace := g.namespace
|
namespace := g.namespace
|
||||||
if controllerNamespace == "" {
|
if namespace == "" {
|
||||||
controllerNamespace = appSet.Namespace
|
namespace = appSet.Namespace
|
||||||
}
|
}
|
||||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: controllerNamespace}, appProject); err != nil {
|
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
|
||||||
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
||||||
}
|
}
|
||||||
// we need to verify the signature on the Git revision if GPG is enabled
|
// we need to verify the signature on the Git revision if GPG is enabled
|
||||||
@@ -101,7 +93,7 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
|||||||
case len(appSetGenerator.Git.Files) != 0:
|
case len(appSetGenerator.Git.Files) != 0:
|
||||||
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, project, appSet.Spec.GoTemplateOptions)
|
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, project, appSet.Spec.GoTemplateOptions)
|
||||||
default:
|
default:
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error generating params from git: %w", err)
|
return nil, fmt.Errorf("error generating params from git: %w", err)
|
||||||
@@ -110,10 +102,8 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateParamsForGitDirectories generates parameters for an ApplicationSet using a directory-based Git generator.
|
|
||||||
// It fetches all directories from the given Git repository and revision, optionally using a revision cache and verifying commits.
|
|
||||||
// It then filters the directories based on the generator's configuration and renders parameters for the resulting applications
|
|
||||||
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit, useGoTemplate bool, project string, goTemplateOptions []string) ([]map[string]any, error) {
|
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit, useGoTemplate bool, project string, goTemplateOptions []string) ([]map[string]any, error) {
|
||||||
|
// Directories, not files
|
||||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, project, noRevisionCache, verifyCommit)
|
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, project, noRevisionCache, verifyCommit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
||||||
@@ -137,103 +127,57 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateParamsForGitFiles generates parameters for an ApplicationSet using a file-based Git generator.
|
|
||||||
// It retrieves and processes specified files from the Git repository, supporting both YAML and JSON formats,
|
|
||||||
// and returns a list of parameter maps extracted from the content.
|
|
||||||
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit, useGoTemplate bool, project string, goTemplateOptions []string) ([]map[string]any, error) {
|
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit, useGoTemplate bool, project string, goTemplateOptions []string) ([]map[string]any, error) {
|
||||||
// fileContentMap maps absolute file paths to their byte content
|
// Get all files that match the requested path string, removing duplicates
|
||||||
fileContentMap := make(map[string][]byte)
|
allFiles := make(map[string][]byte)
|
||||||
var includePatterns []string
|
for _, requestedPath := range appSetGenerator.Git.Files {
|
||||||
var excludePatterns []string
|
files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, project, requestedPath.Path, noRevisionCache, verifyCommit)
|
||||||
|
|
||||||
for _, req := range appSetGenerator.Git.Files {
|
|
||||||
if req.Exclude {
|
|
||||||
excludePatterns = append(excludePatterns, req.Path)
|
|
||||||
} else {
|
|
||||||
includePatterns = append(includePatterns, req.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch all files from include patterns
|
|
||||||
for _, includePattern := range includePatterns {
|
|
||||||
retrievedFiles, err := g.repos.GetFiles(
|
|
||||||
context.TODO(),
|
|
||||||
appSetGenerator.Git.RepoURL,
|
|
||||||
appSetGenerator.Git.Revision,
|
|
||||||
project,
|
|
||||||
includePattern,
|
|
||||||
noRevisionCache,
|
|
||||||
verifyCommit,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for absPath, content := range retrievedFiles {
|
for filePath, content := range files {
|
||||||
fileContentMap[absPath] = content
|
allFiles[filePath] = content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now remove files matching any exclude pattern
|
// Extract the unduplicated map into a list, and sort by path to ensure a deterministic
|
||||||
for _, excludePattern := range excludePatterns {
|
// processing order in the subsequent step
|
||||||
matchingFiles, err := g.repos.GetFiles(
|
allPaths := []string{}
|
||||||
context.TODO(),
|
for path := range allFiles {
|
||||||
appSetGenerator.Git.RepoURL,
|
allPaths = append(allPaths, path)
|
||||||
appSetGenerator.Git.Revision,
|
|
||||||
project,
|
|
||||||
excludePattern,
|
|
||||||
noRevisionCache,
|
|
||||||
verifyCommit,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for absPath := range matchingFiles {
|
|
||||||
// if the file doesn't exist already and you try to delete it from the map
|
|
||||||
// the operation is a no-op. It’s safe and doesn't return an error or panic.
|
|
||||||
// Hence, we can simply try to delete the file from the path without checking
|
|
||||||
// if that file already exists in the map.
|
|
||||||
delete(fileContentMap, absPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
sort.Strings(allPaths)
|
||||||
|
|
||||||
// Get a sorted list of file paths to ensure deterministic processing order
|
// Generate params from each path, and return
|
||||||
var filePaths []string
|
res := []map[string]any{}
|
||||||
for path := range fileContentMap {
|
for _, path := range allPaths {
|
||||||
filePaths = append(filePaths, path)
|
|
||||||
}
|
|
||||||
sort.Strings(filePaths)
|
|
||||||
|
|
||||||
var allParams []map[string]any
|
|
||||||
for _, filePath := range filePaths {
|
|
||||||
// A JSON / YAML file path can contain multiple sets of parameters (ie it is an array)
|
// A JSON / YAML file path can contain multiple sets of parameters (ie it is an array)
|
||||||
paramsFromFileArray, err := g.generateParamsFromGitFile(filePath, fileContentMap[filePath], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix)
|
paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to process file '%s': %w", filePath, err)
|
return nil, fmt.Errorf("unable to process file '%s': %w", path, err)
|
||||||
}
|
}
|
||||||
allParams = append(allParams, paramsFromFileArray...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return allParams, nil
|
res = append(res, paramsArray...)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateParamsFromGitFile parses the content of a Git-tracked file and generates a slice of parameter maps.
|
|
||||||
// The file can contain a single YAML/JSON object or an array of such objects. Depending on the useGoTemplate flag,
|
|
||||||
// it either preserves structure for Go templating or flattens the objects for use as plain key-value parameters.
|
|
||||||
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, values map[string]string, useGoTemplate bool, goTemplateOptions []string, pathParamPrefix string) ([]map[string]any, error) {
|
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, values map[string]string, useGoTemplate bool, goTemplateOptions []string, pathParamPrefix string) ([]map[string]any, error) {
|
||||||
objectsFound := []map[string]any{}
|
objectsFound := []map[string]any{}
|
||||||
|
|
||||||
// First, we attempt to parse as a single object.
|
// First, we attempt to parse as an array
|
||||||
// This will also succeed for empty files.
|
err := yaml.Unmarshal(fileContent, &objectsFound)
|
||||||
singleObj := map[string]any{}
|
if err != nil {
|
||||||
err := yaml.Unmarshal(fileContent, &singleObj)
|
// If unable to parse as an array, attempt to parse as a single object
|
||||||
if err == nil {
|
singleObj := make(map[string]any)
|
||||||
objectsFound = append(objectsFound, singleObj)
|
err = yaml.Unmarshal(fileContent, &singleObj)
|
||||||
} else {
|
|
||||||
// If unable to parse as an object, try to parse as an array
|
|
||||||
err = yaml.Unmarshal(fileContent, &objectsFound)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to parse file: %w", err)
|
return nil, fmt.Errorf("unable to parse file: %w", err)
|
||||||
}
|
}
|
||||||
|
objectsFound = append(objectsFound, singleObj)
|
||||||
|
} else if len(objectsFound) == 0 {
|
||||||
|
// If file is valid but empty, add a default empty item
|
||||||
|
objectsFound = append(objectsFound, map[string]any{})
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]any{}
|
res := []map[string]any{}
|
||||||
@@ -277,7 +221,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
|||||||
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName].(string)))
|
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName].(string)))
|
||||||
params[pathParamName+".filenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName+".filename"].(string)))
|
params[pathParamName+".filenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName+".filename"].(string)))
|
||||||
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
||||||
if v != "" {
|
if len(v) > 0 {
|
||||||
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,10 +238,8 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterApps filters the list of all application paths based on inclusion and exclusion rules
|
|
||||||
// defined in GitDirectoryGeneratorItems. Each item can either include or exclude matching paths.
|
|
||||||
func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
|
func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
|
||||||
var res []string
|
res := []string{}
|
||||||
for _, appPath := range allPaths {
|
for _, appPath := range allPaths {
|
||||||
appInclude := false
|
appInclude := false
|
||||||
appExclude := false
|
appExclude := false
|
||||||
@@ -324,9 +266,6 @@ func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryG
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateParamsFromApps generates a list of parameter maps based on the given app paths.
|
|
||||||
// Each app path is converted into a parameter object with path metadata (basename, segments, etc.).
|
|
||||||
// It supports both Go templates and flat key-value parameters.
|
|
||||||
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]any, error) {
|
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]any, error) {
|
||||||
res := make([]map[string]any, len(requestedApps))
|
res := make([]map[string]any, len(requestedApps))
|
||||||
for i, a := range requestedApps {
|
for i, a := range requestedApps {
|
||||||
@@ -352,7 +291,7 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene
|
|||||||
params[pathParamName+".basename"] = path.Base(a)
|
params[pathParamName+".basename"] = path.Base(a)
|
||||||
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(a))
|
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(a))
|
||||||
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
||||||
if v != "" {
|
if len(v) > 0 {
|
||||||
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,7 +308,6 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveProjectName resolves a project name whether templated or not
|
|
||||||
func resolveProjectName(project string) string {
|
func resolveProjectName(project string) string {
|
||||||
if strings.Contains(project, "{{") {
|
if strings.Contains(project, "{{") {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -27,15 +27,15 @@ type Generator interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrEmptyAppSetGenerator = errors.New("ApplicationSet is empty")
|
EmptyAppSetGeneratorError = errors.New("ApplicationSet is empty")
|
||||||
NoRequeueAfter time.Duration
|
NoRequeueAfter time.Duration
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultRequeueAfter = 3 * time.Minute
|
DefaultRequeueAfterSeconds = 3 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDefaultRequeueAfter() time.Duration {
|
func getDefaultRequeueAfter() time.Duration {
|
||||||
// Default is 3 minutes, min is 1 second, max is 1 year
|
// Default is 3 minutes, min is 1 second, max is 1 year
|
||||||
return env.ParseDurationFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", DefaultRequeueAfter, 1*time.Second, 8760*time.Hour)
|
return env.ParseDurationFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", DefaultRequeueAfterSeconds, 1*time.Second, 8760*time.Hour)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ func Test_getDefaultRequeueAfter(t *testing.T) {
|
|||||||
requeueAfterEnv string
|
requeueAfterEnv string
|
||||||
want time.Duration
|
want time.Duration
|
||||||
}{
|
}{
|
||||||
{name: "Default", requeueAfterEnv: "", want: DefaultRequeueAfter},
|
{name: "Default", requeueAfterEnv: "", want: DefaultRequeueAfterSeconds},
|
||||||
{name: "Min", requeueAfterEnv: "1s", want: 1 * time.Second},
|
{name: "Min", requeueAfterEnv: "1s", want: 1 * time.Second},
|
||||||
{name: "Max", requeueAfterEnv: "8760h", want: 8760 * time.Hour},
|
{name: "Max", requeueAfterEnv: "8760h", want: 8760 * time.Hour},
|
||||||
{name: "Override", requeueAfterEnv: "10m", want: 10 * time.Minute},
|
{name: "Override", requeueAfterEnv: "10m", want: 10 * time.Minute},
|
||||||
{name: "LessThanMin", requeueAfterEnv: "1ms", want: DefaultRequeueAfter},
|
{name: "LessThanMin", requeueAfterEnv: "1ms", want: DefaultRequeueAfterSeconds},
|
||||||
{name: "MoreThanMax", requeueAfterEnv: "8761h", want: DefaultRequeueAfter},
|
{name: "MoreThanMax", requeueAfterEnv: "8761h", want: DefaultRequeueAfterSeconds},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ func (g *ListGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicat
|
|||||||
|
|
||||||
func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.List == nil {
|
if appSetGenerator.List == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
res := make([]map[string]any, len(appSetGenerator.List.Elements))
|
res := make([]map[string]any, len(appSetGenerator.List.Elements))
|
||||||
@@ -77,7 +77,7 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append elements from ElementsYaml to the response
|
// Append elements from ElementsYaml to the response
|
||||||
if appSetGenerator.List.ElementsYaml != "" {
|
if len(appSetGenerator.List.ElementsYaml) > 0 {
|
||||||
var yamlElements []map[string]any
|
var yamlElements []map[string]any
|
||||||
err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements)
|
err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func NewMatrixGenerator(supportedGenerators map[string]Generator) Generator {
|
|||||||
|
|
||||||
func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator.Matrix == nil {
|
if appSetGenerator.Matrix == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(appSetGenerator.Matrix.Generators) < 2 {
|
if len(appSetGenerator.Matrix.Generators) < 2 {
|
||||||
@@ -71,7 +71,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to combine string maps with merging params for the matrix generator: %w", err)
|
return nil, fmt.Errorf("failed to combine string maps with merging params for the matrix generator: %w", err)
|
||||||
}
|
}
|
||||||
res = append(res, val)
|
res = append(res, utils.ConvertToMapStringInterface(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
|
||||||
generatorsMock "github.com/argoproj/argo-cd/v3/applicationset/generators/mocks"
|
|
||||||
servicesMocks "github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
|
||||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
testCaseCopy := testCase // Since tests may run in parallel
|
testCaseCopy := testCase // Since tests may run in parallel
|
||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorsMock.Generator{}
|
genMock := &generatorMock{}
|
||||||
appSet := &v1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "set",
|
Name: "set",
|
||||||
@@ -149,7 +149,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
}
|
}
|
||||||
genMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": "app1",
|
"path": "app1",
|
||||||
"path.basename": "app1",
|
"path.basename": "app1",
|
||||||
@@ -162,7 +162,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
testCaseCopy := testCase // Since tests may run in parallel
|
testCaseCopy := testCase // Since tests may run in parallel
|
||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorsMock.Generator{}
|
genMock := &generatorMock{}
|
||||||
appSet := &v1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "set",
|
Name: "set",
|
||||||
@@ -358,7 +358,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
}
|
}
|
||||||
genMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": map[string]string{
|
"path": map[string]string{
|
||||||
"path": "app1",
|
"path": "app1",
|
||||||
@@ -375,7 +375,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +507,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
testCaseCopy := testCase // Since tests may run in parallel
|
testCaseCopy := testCase // Since tests may run in parallel
|
||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
mock := &generatorsMock.Generator{}
|
mock := &generatorMock{}
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
@@ -517,7 +517,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
SCMProvider: g.SCMProvider,
|
SCMProvider: g.SCMProvider,
|
||||||
ClusterDecisionResource: g.ClusterDecisionResource,
|
ClusterDecisionResource: g.ClusterDecisionResource,
|
||||||
}
|
}
|
||||||
mock.EXPECT().GetRequeueAfter(&gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter)
|
mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
@@ -634,7 +634,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
testCaseCopy := testCase // Since tests may run in parallel
|
testCaseCopy := testCase // Since tests may run in parallel
|
||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorsMock.Generator{}
|
genMock := &generatorMock{}
|
||||||
appSet := &v1alpha1.ApplicationSet{}
|
appSet := &v1alpha1.ApplicationSet{}
|
||||||
|
|
||||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||||
@@ -650,7 +650,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
Clusters: g.Clusters,
|
Clusters: g.Clusters,
|
||||||
}
|
}
|
||||||
genMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||||
"path.basename": "dev",
|
"path.basename": "dev",
|
||||||
@@ -662,7 +662,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
"path.basenameNormalized": "prod",
|
"path.basenameNormalized": "prod",
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
@@ -813,7 +813,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
testCaseCopy := testCase // Since tests may run in parallel
|
testCaseCopy := testCase // Since tests may run in parallel
|
||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorsMock.Generator{}
|
genMock := &generatorMock{}
|
||||||
appSet := &v1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
Spec: v1alpha1.ApplicationSetSpec{
|
Spec: v1alpha1.ApplicationSetSpec{
|
||||||
GoTemplate: true,
|
GoTemplate: true,
|
||||||
@@ -833,7 +833,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
Clusters: g.Clusters,
|
Clusters: g.Clusters,
|
||||||
}
|
}
|
||||||
genMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": map[string]string{
|
"path": map[string]string{
|
||||||
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||||
@@ -849,7 +849,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
@@ -969,7 +969,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
testCaseCopy := testCase // Since tests may run in parallel
|
testCaseCopy := testCase // Since tests may run in parallel
|
||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorsMock.Generator{}
|
genMock := &generatorMock{}
|
||||||
appSet := &v1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "set",
|
Name: "set",
|
||||||
@@ -984,7 +984,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
}
|
}
|
||||||
genMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{{
|
||||||
"foo": map[string]any{
|
"foo": map[string]any{
|
||||||
"bar": []any{
|
"bar": []any{
|
||||||
map[string]any{
|
map[string]any{
|
||||||
@@ -1009,7 +1009,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}, nil)
|
}}, nil)
|
||||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1037,6 +1037,28 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type generatorMock struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generatorMock) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
||||||
|
args := g.Called(appSetGenerator)
|
||||||
|
|
||||||
|
return args.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, appSet *v1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
|
args := g.Called(appSetGenerator, appSet)
|
||||||
|
|
||||||
|
return args.Get(0).([]map[string]any), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *generatorMock) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
|
args := g.Called(appSetGenerator)
|
||||||
|
|
||||||
|
return args.Get(0).(time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||||
// Given a matrix generator over a list generator and a git files generator, the nested git files generator should
|
// Given a matrix generator over a list generator and a git files generator, the nested git files generator should
|
||||||
// be treated as a files generator, and it should produce parameters.
|
// be treated as a files generator, and it should produce parameters.
|
||||||
@@ -1050,11 +1072,11 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
|||||||
// Now instead of checking for nil, we check whether the field is a non-empty slice. This test prevents a regression
|
// Now instead of checking for nil, we check whether the field is a non-empty slice. This test prevents a regression
|
||||||
// of that bug.
|
// of that bug.
|
||||||
|
|
||||||
listGeneratorMock := &generatorsMock.Generator{}
|
listGeneratorMock := &generatorMock{}
|
||||||
listGeneratorMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
||||||
{"some": "value"},
|
{"some": "value"},
|
||||||
}, nil)
|
}, nil)
|
||||||
listGeneratorMock.EXPECT().GetTemplate(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
|
|
||||||
gitGeneratorSpec := &v1alpha1.GitGenerator{
|
gitGeneratorSpec := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "https://git.example.com",
|
RepoURL: "https://git.example.com",
|
||||||
@@ -1063,10 +1085,10 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
repoServiceMock := &servicesMocks.Repos{}
|
repoServiceMock := &mocks.Repos{}
|
||||||
repoServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||||
"some/path.json": []byte("test: content"),
|
"some/path.json": []byte("test: content"),
|
||||||
}, nil).Maybe()
|
}, nil)
|
||||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||||
|
|
||||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dario.cat/mergo"
|
"dario.cat/mergo"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1a
|
|||||||
// GenerateParams gets the params produced by the MergeGenerator.
|
// GenerateParams gets the params produced by the MergeGenerator.
|
||||||
func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator.Merge == nil {
|
if appSetGenerator.Merge == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(appSetGenerator.Merge.Generators) < 2 {
|
if len(appSetGenerator.Merge.Generators) < 2 {
|
||||||
@@ -83,8 +83,11 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
|||||||
}
|
}
|
||||||
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
||||||
} else {
|
} else {
|
||||||
maps.Copy(baseParamSet, overrideParamSet)
|
overriddenParamSet, err := utils.CombineStringMapsAllowDuplicates(baseParamSet, overrideParamSet)
|
||||||
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error combining string maps: %w", err)
|
||||||
|
}
|
||||||
|
baseParamSetsByMergeKey[mergeKeyValue] = utils.ConvertToMapStringInterface(overriddenParamSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ func listOfMapsToSet(maps []map[string]any) (map[string]bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeGenerate(t *testing.T) {
|
func TestMergeGenerate(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
||||||
@@ -212,8 +210,6 @@ func toAPIExtensionsJSON(t *testing.T, g any) *apiextensionsv1.JSON {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
mergeKeys []string
|
mergeKeys []string
|
||||||
|
|||||||
276
applicationset/generators/mocks/Generator.go
generated
276
applicationset/generators/mocks/Generator.go
generated
@@ -1,17 +1,90 @@
|
|||||||
// Code generated by mockery; DO NOT EDIT.
|
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||||
// github.com/vektra/mockery
|
|
||||||
// template: testify
|
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
client "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
|
time "time"
|
||||||
|
|
||||||
|
v1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Generator is an autogenerated mock type for the Generator type
|
||||||
|
type Generator struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateParams provides a mock function with given fields: appSetGenerator, applicationSetInfo, _a2
|
||||||
|
func (_m *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, _a2 client.Client) ([]map[string]interface{}, error) {
|
||||||
|
ret := _m.Called(appSetGenerator, applicationSetInfo, _a2)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GenerateParams")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []map[string]interface{}
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]interface{}, error)); ok {
|
||||||
|
return rf(appSetGenerator, applicationSetInfo, _a2)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]interface{}); ok {
|
||||||
|
r0 = rf(appSetGenerator, applicationSetInfo, _a2)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]map[string]interface{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok {
|
||||||
|
r1 = rf(appSetGenerator, applicationSetInfo, _a2)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequeueAfter provides a mock function with given fields: appSetGenerator
|
||||||
|
func (_m *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
|
ret := _m.Called(appSetGenerator)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetRequeueAfter")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 time.Duration
|
||||||
|
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok {
|
||||||
|
r0 = rf(appSetGenerator)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(time.Duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemplate provides a mock function with given fields: appSetGenerator
|
||||||
|
func (_m *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
||||||
|
ret := _m.Called(appSetGenerator)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetTemplate")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *v1alpha1.ApplicationSetTemplate
|
||||||
|
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok {
|
||||||
|
r0 = rf(appSetGenerator)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// NewGenerator creates a new instance of Generator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
// NewGenerator creates a new instance of Generator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
// The first argument is typically a *testing.T value.
|
// The first argument is typically a *testing.T value.
|
||||||
func NewGenerator(t interface {
|
func NewGenerator(t interface {
|
||||||
@@ -25,194 +98,3 @@ func NewGenerator(t interface {
|
|||||||
|
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generator is an autogenerated mock type for the Generator type
|
|
||||||
type Generator struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
type Generator_Expecter struct {
|
|
||||||
mock *mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_m *Generator) EXPECT() *Generator_Expecter {
|
|
||||||
return &Generator_Expecter{mock: &_m.Mock}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateParams provides a mock function for the type Generator
|
|
||||||
func (_mock *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, client1 client.Client) ([]map[string]any, error) {
|
|
||||||
ret := _mock.Called(appSetGenerator, applicationSetInfo, client1)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GenerateParams")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 []map[string]any
|
|
||||||
var r1 error
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]any, error)); ok {
|
|
||||||
return returnFunc(appSetGenerator, applicationSetInfo, client1)
|
|
||||||
}
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]any); ok {
|
|
||||||
r0 = returnFunc(appSetGenerator, applicationSetInfo, client1)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([]map[string]any)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if returnFunc, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok {
|
|
||||||
r1 = returnFunc(appSetGenerator, applicationSetInfo, client1)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generator_GenerateParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateParams'
|
|
||||||
type Generator_GenerateParams_Call struct {
|
|
||||||
*mock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateParams is a helper method to define mock.On call
|
|
||||||
// - appSetGenerator *v1alpha1.ApplicationSetGenerator
|
|
||||||
// - applicationSetInfo *v1alpha1.ApplicationSet
|
|
||||||
// - client1 client.Client
|
|
||||||
func (_e *Generator_Expecter) GenerateParams(appSetGenerator interface{}, applicationSetInfo interface{}, client1 interface{}) *Generator_GenerateParams_Call {
|
|
||||||
return &Generator_GenerateParams_Call{Call: _e.mock.On("GenerateParams", appSetGenerator, applicationSetInfo, client1)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GenerateParams_Call) Run(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, client1 client.Client)) *Generator_GenerateParams_Call {
|
|
||||||
_c.Call.Run(func(args mock.Arguments) {
|
|
||||||
var arg0 *v1alpha1.ApplicationSetGenerator
|
|
||||||
if args[0] != nil {
|
|
||||||
arg0 = args[0].(*v1alpha1.ApplicationSetGenerator)
|
|
||||||
}
|
|
||||||
var arg1 *v1alpha1.ApplicationSet
|
|
||||||
if args[1] != nil {
|
|
||||||
arg1 = args[1].(*v1alpha1.ApplicationSet)
|
|
||||||
}
|
|
||||||
var arg2 client.Client
|
|
||||||
if args[2] != nil {
|
|
||||||
arg2 = args[2].(client.Client)
|
|
||||||
}
|
|
||||||
run(
|
|
||||||
arg0,
|
|
||||||
arg1,
|
|
||||||
arg2,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GenerateParams_Call) Return(stringToVs []map[string]any, err error) *Generator_GenerateParams_Call {
|
|
||||||
_c.Call.Return(stringToVs, err)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GenerateParams_Call) RunAndReturn(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, client1 client.Client) ([]map[string]any, error)) *Generator_GenerateParams_Call {
|
|
||||||
_c.Call.Return(run)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRequeueAfter provides a mock function for the type Generator
|
|
||||||
func (_mock *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
|
||||||
ret := _mock.Called(appSetGenerator)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetRequeueAfter")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 time.Duration
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok {
|
|
||||||
r0 = returnFunc(appSetGenerator)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Get(0).(time.Duration)
|
|
||||||
}
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generator_GetRequeueAfter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRequeueAfter'
|
|
||||||
type Generator_GetRequeueAfter_Call struct {
|
|
||||||
*mock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRequeueAfter is a helper method to define mock.On call
|
|
||||||
// - appSetGenerator *v1alpha1.ApplicationSetGenerator
|
|
||||||
func (_e *Generator_Expecter) GetRequeueAfter(appSetGenerator interface{}) *Generator_GetRequeueAfter_Call {
|
|
||||||
return &Generator_GetRequeueAfter_Call{Call: _e.mock.On("GetRequeueAfter", appSetGenerator)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GetRequeueAfter_Call) Run(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator)) *Generator_GetRequeueAfter_Call {
|
|
||||||
_c.Call.Run(func(args mock.Arguments) {
|
|
||||||
var arg0 *v1alpha1.ApplicationSetGenerator
|
|
||||||
if args[0] != nil {
|
|
||||||
arg0 = args[0].(*v1alpha1.ApplicationSetGenerator)
|
|
||||||
}
|
|
||||||
run(
|
|
||||||
arg0,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GetRequeueAfter_Call) Return(duration time.Duration) *Generator_GetRequeueAfter_Call {
|
|
||||||
_c.Call.Return(duration)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GetRequeueAfter_Call) RunAndReturn(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration) *Generator_GetRequeueAfter_Call {
|
|
||||||
_c.Call.Return(run)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTemplate provides a mock function for the type Generator
|
|
||||||
func (_mock *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
|
||||||
ret := _mock.Called(appSetGenerator)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetTemplate")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 *v1alpha1.ApplicationSetTemplate
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok {
|
|
||||||
r0 = returnFunc(appSetGenerator)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generator_GetTemplate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTemplate'
|
|
||||||
type Generator_GetTemplate_Call struct {
|
|
||||||
*mock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTemplate is a helper method to define mock.On call
|
|
||||||
// - appSetGenerator *v1alpha1.ApplicationSetGenerator
|
|
||||||
func (_e *Generator_Expecter) GetTemplate(appSetGenerator interface{}) *Generator_GetTemplate_Call {
|
|
||||||
return &Generator_GetTemplate_Call{Call: _e.mock.On("GetTemplate", appSetGenerator)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GetTemplate_Call) Run(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator)) *Generator_GetTemplate_Call {
|
|
||||||
_c.Call.Run(func(args mock.Arguments) {
|
|
||||||
var arg0 *v1alpha1.ApplicationSetGenerator
|
|
||||||
if args[0] != nil {
|
|
||||||
arg0 = args[0].(*v1alpha1.ApplicationSetGenerator)
|
|
||||||
}
|
|
||||||
run(
|
|
||||||
arg0,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GetTemplate_Call) Return(applicationSetTemplate *v1alpha1.ApplicationSetTemplate) *Generator_GetTemplate_Call {
|
|
||||||
_c.Call.Return(applicationSetTemplate)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Generator_GetTemplate_Call) RunAndReturn(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate) *Generator_GetTemplate_Call {
|
|
||||||
_c.Call.Return(run)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jeremywohl/flatten"
|
"github.com/jeremywohl/flatten"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
@@ -19,19 +20,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultPluginRequeueAfter = 30 * time.Minute
|
DefaultPluginRequeueAfterSeconds = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*PluginGenerator)(nil)
|
var _ Generator = (*PluginGenerator)(nil)
|
||||||
|
|
||||||
type PluginGenerator struct {
|
type PluginGenerator struct {
|
||||||
client client.Client
|
client client.Client
|
||||||
|
ctx context.Context
|
||||||
|
clientset kubernetes.Interface
|
||||||
namespace string
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPluginGenerator(client client.Client, namespace string) Generator {
|
func NewPluginGenerator(ctx context.Context, client client.Client, clientset kubernetes.Interface, namespace string) Generator {
|
||||||
g := &PluginGenerator{
|
g := &PluginGenerator{
|
||||||
client: client,
|
client: client,
|
||||||
|
ctx: ctx,
|
||||||
|
clientset: clientset,
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
@@ -44,7 +49,7 @@ func (g *PluginGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
|
|||||||
return time.Duration(*appSetGenerator.Plugin.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.Plugin.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultPluginRequeueAfter
|
return DefaultPluginRequeueAfterSeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
@@ -53,11 +58,11 @@ func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applic
|
|||||||
|
|
||||||
func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.Plugin == nil {
|
if appSetGenerator.Plugin == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
@@ -626,6 +628,8 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := t.Context()
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -662,9 +666,11 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
testCase.configmap.Data["baseUrl"] = fakeServer.URL
|
testCase.configmap.Data["baseUrl"] = fakeServer.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fakeClient := kubefake.NewSimpleClientset(append([]runtime.Object{}, testCase.configmap, testCase.secret)...)
|
||||||
|
|
||||||
fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build()
|
fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build()
|
||||||
|
|
||||||
pluginGenerator := NewPluginGenerator(fakeClientWithCache, "default")
|
pluginGenerator := NewPluginGenerator(ctx, fakeClientWithCache, fakeClient, "default")
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|||||||
@@ -4,23 +4,22 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/gosimple/slug"
|
"github.com/gosimple/slug"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
|
||||||
pullrequest "github.com/argoproj/argo-cd/v3/applicationset/services/pull_request"
|
pullrequest "github.com/argoproj/argo-cd/v3/applicationset/services/pull_request"
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Generator = (*PullRequestGenerator)(nil)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultPullRequestRequeueAfter = 30 * time.Minute
|
DefaultPullRequestRequeueAfterSeconds = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
type PullRequestGenerator struct {
|
type PullRequestGenerator struct {
|
||||||
@@ -45,11 +44,7 @@ func (g *PullRequestGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alph
|
|||||||
return time.Duration(*appSetGenerator.PullRequest.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.PullRequest.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultPullRequestRequeueAfter
|
return DefaultPullRequestRequeueAfterSeconds
|
||||||
}
|
|
||||||
|
|
||||||
func (g *PullRequestGenerator) GetContinueOnRepoNotFoundError(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) bool {
|
|
||||||
return appSetGenerator.PullRequest.ContinueOnRepoNotFoundError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
@@ -58,11 +53,11 @@ func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A
|
|||||||
|
|
||||||
func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.PullRequest == nil {
|
if appSetGenerator.PullRequest == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -72,15 +67,10 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
||||||
params := make([]map[string]any, 0, len(pulls))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if pullrequest.IsRepositoryNotFoundError(err) && g.GetContinueOnRepoNotFoundError(appSetGenerator) {
|
|
||||||
log.WithError(err).WithField("generator", g).
|
|
||||||
Warn("Skipping params generation for this repository since it was not found.")
|
|
||||||
return params, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("error listing repos: %w", err)
|
return nil, fmt.Errorf("error listing repos: %w", err)
|
||||||
}
|
}
|
||||||
|
params := make([]map[string]any, 0, len(pulls))
|
||||||
|
|
||||||
// In order to follow the DNS label standard as defined in RFC 1123,
|
// In order to follow the DNS label standard as defined in RFC 1123,
|
||||||
// we need to limit the 'branch' to 50 to give room to append/suffix-ing it
|
// we need to limit the 'branch' to 50 to give room to append/suffix-ing it
|
||||||
@@ -107,7 +97,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
paramMap := map[string]any{
|
paramMap := map[string]any{
|
||||||
"number": strconv.FormatInt(pull.Number, 10),
|
"number": strconv.Itoa(pull.Number),
|
||||||
"title": pull.Title,
|
"title": pull.Title,
|
||||||
"branch": pull.Branch,
|
"branch": pull.Branch,
|
||||||
"branch_slug": slug.Make(pull.Branch),
|
"branch_slug": slug.Make(pull.Branch),
|
||||||
@@ -119,15 +109,15 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
"author": pull.Author,
|
"author": pull.Author,
|
||||||
}
|
}
|
||||||
|
|
||||||
// PR lables will only be supported for Go Template appsets, since fasttemplate will be deprecated.
|
|
||||||
if applicationSetInfo != nil && applicationSetInfo.Spec.GoTemplate {
|
|
||||||
paramMap["labels"] = pull.Labels
|
|
||||||
}
|
|
||||||
|
|
||||||
err := appendTemplatedValues(appSetGenerator.PullRequest.Values, paramMap, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
err := appendTemplatedValues(appSetGenerator.PullRequest.Values, paramMap, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to append templated values: %w", err)
|
return nil, fmt.Errorf("failed to append templated values: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PR lables will only be supported for Go Template appsets, since fasttemplate will be deprecated.
|
||||||
|
if applicationSetInfo != nil && applicationSetInfo.Spec.GoTemplate {
|
||||||
|
paramMap["labels"] = pull.Labels
|
||||||
|
}
|
||||||
params = append(params, paramMap)
|
params = append(params, paramMap)
|
||||||
}
|
}
|
||||||
return params, nil
|
return params, nil
|
||||||
@@ -167,8 +157,7 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
|
return pullrequest.NewGiteaService(token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Insecure)
|
||||||
return pullrequest.NewGiteaService(token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Labels, providerConfig.Insecure)
|
|
||||||
}
|
}
|
||||||
if generatorConfig.BitbucketServer != nil {
|
if generatorConfig.BitbucketServer != nil {
|
||||||
providerConfig := generatorConfig.BitbucketServer
|
providerConfig := generatorConfig.BitbucketServer
|
||||||
@@ -224,28 +213,13 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alpha1.PullRequestGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alpha1.PullRequestGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
var metricsCtx *services.MetricsContext
|
|
||||||
var httpClient *http.Client
|
|
||||||
|
|
||||||
if g.enableGitHubAPIMetrics {
|
|
||||||
metricsCtx = &services.MetricsContext{
|
|
||||||
AppSetNamespace: applicationSetInfo.Namespace,
|
|
||||||
AppSetName: applicationSetInfo.Name,
|
|
||||||
}
|
|
||||||
httpClient = services.NewGitHubMetricsClient(metricsCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// use an app if it was configured
|
// use an app if it was configured
|
||||||
if cfg.AppSecretName != "" {
|
if cfg.AppSecretName != "" {
|
||||||
auth, err := g.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName)
|
auth, err := g.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting GitHub App secret: %w", err)
|
return nil, fmt.Errorf("error getting GitHub App secret: %w", err)
|
||||||
}
|
}
|
||||||
|
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||||
if g.enableGitHubAPIMetrics {
|
|
||||||
return pullrequest.NewGithubAppService(ctx, *auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels, httpClient)
|
|
||||||
}
|
|
||||||
return pullrequest.NewGithubAppService(ctx, *auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// always default to token, even if not set (public access)
|
// always default to token, even if not set (public access)
|
||||||
@@ -253,9 +227,5 @@ func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alph
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.enableGitHubAPIMetrics {
|
|
||||||
return pullrequest.NewGithubService(token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels, httpClient)
|
|
||||||
}
|
|
||||||
return pullrequest.NewGithubService(token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
return pullrequest.NewGithubService(token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,11 @@ import (
|
|||||||
func TestPullRequestGithubGenerateParams(t *testing.T) {
|
func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
||||||
values map[string]string
|
values map[string]string
|
||||||
expected []map[string]any
|
expected []map[string]any
|
||||||
expectedErr error
|
expectedErr error
|
||||||
applicationSet argoprojiov1alpha1.ApplicationSet
|
applicationSet argoprojiov1alpha1.ApplicationSet
|
||||||
continueOnRepoNotFoundError bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
@@ -172,30 +171,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
expected: nil,
|
expected: nil,
|
||||||
expectedErr: errors.New("error listing repos: fake error"),
|
expectedErr: errors.New("error listing repos: fake error"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
|
||||||
return pullrequest.NewFakeService(
|
|
||||||
ctx,
|
|
||||||
nil,
|
|
||||||
pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
expected: []map[string]any{},
|
|
||||||
expectedErr: nil,
|
|
||||||
continueOnRepoNotFoundError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
|
||||||
return pullrequest.NewFakeService(
|
|
||||||
ctx,
|
|
||||||
nil,
|
|
||||||
pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
expected: nil,
|
|
||||||
expectedErr: errors.New("error listing repos: repository not found"),
|
|
||||||
continueOnRepoNotFoundError: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
return pullrequest.NewFakeService(
|
return pullrequest.NewFakeService(
|
||||||
@@ -277,51 +252,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
|
||||||
return pullrequest.NewFakeService(
|
|
||||||
ctx,
|
|
||||||
[]*pullrequest.PullRequest{
|
|
||||||
{
|
|
||||||
Number: 1,
|
|
||||||
Title: "title1",
|
|
||||||
Branch: "my_branch",
|
|
||||||
TargetBranch: "master",
|
|
||||||
HeadSHA: "abcd",
|
|
||||||
Author: "testName",
|
|
||||||
Labels: []string{"preview", "preview:team1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
values: map[string]string{
|
|
||||||
"preview_env": "{{ regexFind \"(team1|team2)\" (.labels | join \",\") }}",
|
|
||||||
},
|
|
||||||
expected: []map[string]any{
|
|
||||||
{
|
|
||||||
"number": "1",
|
|
||||||
"title": "title1",
|
|
||||||
"branch": "my_branch",
|
|
||||||
"branch_slug": "my-branch",
|
|
||||||
"target_branch": "master",
|
|
||||||
"target_branch_slug": "master",
|
|
||||||
"head_sha": "abcd",
|
|
||||||
"head_short_sha": "abcd",
|
|
||||||
"head_short_sha_7": "abcd",
|
|
||||||
"author": "testName",
|
|
||||||
"labels": []string{"preview", "preview:team1"},
|
|
||||||
"values": map[string]string{"preview_env": "team1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expectedErr: nil,
|
|
||||||
applicationSet: argoprojiov1alpha1.ApplicationSet{
|
|
||||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
|
||||||
// Application set is using fasttemplate.
|
|
||||||
GoTemplate: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@@ -330,8 +260,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||||
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
|
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
|
||||||
Values: c.values,
|
Values: c.values,
|
||||||
ContinueOnRepoNotFoundError: c.continueOnRepoNotFoundError,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,8 +275,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
providerConfig *argoprojiov1alpha1.PullRequestGenerator
|
providerConfig *argoprojiov1alpha1.PullRequestGenerator
|
||||||
@@ -398,7 +325,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
|||||||
"gitea.myorg.com",
|
"gitea.myorg.com",
|
||||||
"bitbucket.myorg.com",
|
"bitbucket.myorg.com",
|
||||||
"azuredevops.myorg.com",
|
"azuredevops.myorg.com",
|
||||||
}, true, true, nil, true))
|
}, true, nil, true))
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -421,7 +348,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
|
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
|
||||||
generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, true, nil, true))
|
generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, nil, true))
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -12,7 +11,6 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider"
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
@@ -23,7 +21,7 @@ import (
|
|||||||
var _ Generator = (*SCMProviderGenerator)(nil)
|
var _ Generator = (*SCMProviderGenerator)(nil)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultSCMProviderRequeueAfter = 30 * time.Minute
|
DefaultSCMProviderRequeueAfterSeconds = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCMProviderGenerator struct {
|
type SCMProviderGenerator struct {
|
||||||
@@ -33,22 +31,20 @@ type SCMProviderGenerator struct {
|
|||||||
SCMConfig
|
SCMConfig
|
||||||
}
|
}
|
||||||
type SCMConfig struct {
|
type SCMConfig struct {
|
||||||
scmRootCAPath string
|
scmRootCAPath string
|
||||||
allowedSCMProviders []string
|
allowedSCMProviders []string
|
||||||
enableSCMProviders bool
|
enableSCMProviders bool
|
||||||
enableGitHubAPIMetrics bool
|
GitHubApps github_app_auth.Credentials
|
||||||
GitHubApps github_app_auth.Credentials
|
tokenRefStrictMode bool
|
||||||
tokenRefStrictMode bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSCMConfig(scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool, enableGitHubAPIMetrics bool, gitHubApps github_app_auth.Credentials, tokenRefStrictMode bool) SCMConfig {
|
func NewSCMConfig(scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool, gitHubApps github_app_auth.Credentials, tokenRefStrictMode bool) SCMConfig {
|
||||||
return SCMConfig{
|
return SCMConfig{
|
||||||
scmRootCAPath: scmRootCAPath,
|
scmRootCAPath: scmRootCAPath,
|
||||||
allowedSCMProviders: allowedSCMProviders,
|
allowedSCMProviders: allowedSCMProviders,
|
||||||
enableSCMProviders: enableSCMProviders,
|
enableSCMProviders: enableSCMProviders,
|
||||||
enableGitHubAPIMetrics: enableGitHubAPIMetrics,
|
GitHubApps: gitHubApps,
|
||||||
GitHubApps: gitHubApps,
|
tokenRefStrictMode: tokenRefStrictMode,
|
||||||
tokenRefStrictMode: tokenRefStrictMode,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +69,7 @@ func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alph
|
|||||||
return time.Duration(*appSetGenerator.SCMProvider.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.SCMProvider.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultSCMProviderRequeueAfter
|
return DefaultSCMProviderRequeueAfterSeconds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
@@ -122,11 +118,11 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
|
|||||||
|
|
||||||
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.SCMProvider == nil {
|
if appSetGenerator.SCMProvider == nil {
|
||||||
return nil, ErrEmptyAppSetGenerator
|
return nil, EmptyAppSetGeneratorError
|
||||||
}
|
}
|
||||||
|
|
||||||
if !g.enableSCMProviders {
|
if !g.enableSCMProviders {
|
||||||
@@ -257,7 +253,6 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
params := map[string]any{
|
params := map[string]any{
|
||||||
"organization": repo.Organization,
|
"organization": repo.Organization,
|
||||||
"repository": repo.Repository,
|
"repository": repo.Repository,
|
||||||
"repository_id": repo.RepositoryId,
|
|
||||||
"url": repo.URL,
|
"url": repo.URL,
|
||||||
"branch": repo.Branch,
|
"branch": repo.Branch,
|
||||||
"sha": repo.SHA,
|
"sha": repo.SHA,
|
||||||
@@ -278,36 +273,23 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argoprojiov1alpha1.SCMProviderGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (scm_provider.SCMProviderService, error) {
|
func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argoprojiov1alpha1.SCMProviderGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (scm_provider.SCMProviderService, error) {
|
||||||
var metricsCtx *services.MetricsContext
|
|
||||||
var httpClient *http.Client
|
|
||||||
|
|
||||||
if g.enableGitHubAPIMetrics {
|
|
||||||
metricsCtx = &services.MetricsContext{
|
|
||||||
AppSetNamespace: applicationSetInfo.Namespace,
|
|
||||||
AppSetName: applicationSetInfo.Name,
|
|
||||||
}
|
|
||||||
httpClient = services.NewGitHubMetricsClient(metricsCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if github.AppSecretName != "" {
|
if github.AppSecretName != "" {
|
||||||
auth, err := g.GitHubApps.GetAuthSecret(ctx, github.AppSecretName)
|
auth, err := g.GitHubApps.GetAuthSecret(ctx, github.AppSecretName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Github app secret: %w", err)
|
return nil, fmt.Errorf("error fetching Github app secret: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.enableGitHubAPIMetrics {
|
return scm_provider.NewGithubAppProviderFor(
|
||||||
return scm_provider.NewGithubAppProviderFor(ctx, *auth, github.Organization, github.API, github.AllBranches, httpClient)
|
*auth,
|
||||||
}
|
github.Organization,
|
||||||
return scm_provider.NewGithubAppProviderFor(ctx, *auth, github.Organization, github.API, github.AllBranches)
|
github.API,
|
||||||
|
github.AllBranches,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Github token: %w", err)
|
return nil, fmt.Errorf("error fetching Github token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.enableGitHubAPIMetrics {
|
|
||||||
return scm_provider.NewGithubProvider(github.Organization, token, github.API, github.AllBranches, httpClient)
|
|
||||||
}
|
|
||||||
return scm_provider.NewGithubProvider(github.Organization, token, github.API, github.AllBranches)
|
return scm_provider.NewGithubProvider(github.Organization, token, github.API, github.AllBranches)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestSCMProviderGenerateParams(t *testing.T) {
|
func TestSCMProviderGenerateParams(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
repos []*scm_provider.Repository
|
repos []*scm_provider.Repository
|
||||||
@@ -27,7 +25,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Organization: "myorg",
|
Organization: "myorg",
|
||||||
Repository: "repo1",
|
Repository: "repo1",
|
||||||
RepositoryId: 190320251,
|
|
||||||
URL: "git@github.com:myorg/repo1.git",
|
URL: "git@github.com:myorg/repo1.git",
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
@@ -36,7 +33,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Organization: "myorg",
|
Organization: "myorg",
|
||||||
Repository: "repo2",
|
Repository: "repo2",
|
||||||
RepositoryId: 190320252,
|
|
||||||
URL: "git@github.com:myorg/repo2.git",
|
URL: "git@github.com:myorg/repo2.git",
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
SHA: "59d0",
|
SHA: "59d0",
|
||||||
@@ -46,7 +42,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"organization": "myorg",
|
"organization": "myorg",
|
||||||
"repository": "repo1",
|
"repository": "repo1",
|
||||||
"repository_id": 190320251,
|
|
||||||
"url": "git@github.com:myorg/repo1.git",
|
"url": "git@github.com:myorg/repo1.git",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"branchNormalized": "main",
|
"branchNormalized": "main",
|
||||||
@@ -58,7 +53,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"organization": "myorg",
|
"organization": "myorg",
|
||||||
"repository": "repo2",
|
"repository": "repo2",
|
||||||
"repository_id": 190320252,
|
|
||||||
"url": "git@github.com:myorg/repo2.git",
|
"url": "git@github.com:myorg/repo2.git",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"branchNormalized": "main",
|
"branchNormalized": "main",
|
||||||
@@ -75,7 +69,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Organization: "myorg",
|
Organization: "myorg",
|
||||||
Repository: "repo3",
|
Repository: "repo3",
|
||||||
RepositoryId: 190320253,
|
|
||||||
URL: "git@github.com:myorg/repo3.git",
|
URL: "git@github.com:myorg/repo3.git",
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
@@ -90,7 +83,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"organization": "myorg",
|
"organization": "myorg",
|
||||||
"repository": "repo3",
|
"repository": "repo3",
|
||||||
"repository_id": 190320253,
|
|
||||||
"url": "git@github.com:myorg/repo3.git",
|
"url": "git@github.com:myorg/repo3.git",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"branchNormalized": "main",
|
"branchNormalized": "main",
|
||||||
@@ -103,52 +95,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Repos with and without id",
|
|
||||||
repos: []*scm_provider.Repository{
|
|
||||||
{
|
|
||||||
Organization: "myorg",
|
|
||||||
Repository: "repo4",
|
|
||||||
RepositoryId: "idaz09",
|
|
||||||
URL: "git@github.com:myorg/repo4.git",
|
|
||||||
Branch: "main",
|
|
||||||
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Organization: "myorg",
|
|
||||||
Repository: "repo5",
|
|
||||||
URL: "git@github.com:myorg/repo5.git",
|
|
||||||
Branch: "main",
|
|
||||||
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: []map[string]any{
|
|
||||||
{
|
|
||||||
"organization": "myorg",
|
|
||||||
"repository": "repo4",
|
|
||||||
"repository_id": "idaz09",
|
|
||||||
"url": "git@github.com:myorg/repo4.git",
|
|
||||||
"branch": "main",
|
|
||||||
"branchNormalized": "main",
|
|
||||||
"sha": "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
|
||||||
"short_sha": "0bc57212",
|
|
||||||
"short_sha_7": "0bc5721",
|
|
||||||
"labels": "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"organization": "myorg",
|
|
||||||
"repository": "repo5",
|
|
||||||
"repository_id": nil,
|
|
||||||
"url": "git@github.com:myorg/repo5.git",
|
|
||||||
"branch": "main",
|
|
||||||
"branchNormalized": "main",
|
|
||||||
"sha": "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
|
||||||
"short_sha": "0bc57212",
|
|
||||||
"short_sha_7": "0bc5721",
|
|
||||||
"labels": "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
@@ -187,8 +133,6 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowedSCMProvider(t *testing.T) {
|
func TestAllowedSCMProvider(t *testing.T) {
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
|
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import (
|
|||||||
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, namespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
||||||
terminalGenerators := map[string]Generator{
|
terminalGenerators := map[string]Generator{
|
||||||
"List": NewListGenerator(),
|
"List": NewListGenerator(),
|
||||||
"Clusters": NewClusterGenerator(ctx, c, k8sClient, controllerNamespace),
|
"Clusters": NewClusterGenerator(ctx, c, k8sClient, namespace),
|
||||||
"Git": NewGitGenerator(argoCDService, controllerNamespace),
|
"Git": NewGitGenerator(argoCDService, namespace),
|
||||||
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
||||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace),
|
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
||||||
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
||||||
"Plugin": NewPluginGenerator(c, controllerNamespace),
|
"Plugin": NewPluginGenerator(ctx, c, k8sClient, namespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
nestedGenerators := map[string]Generator{
|
nestedGenerators := map[string]Generator{
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func TestValueInterpolation(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
err := appendTemplatedValues(testCase.values, testCase.params, false, nil)
|
err := appendTemplatedValues(testCase.values, testCase.params, false, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, testCase.expected, testCase.params)
|
assert.EqualValues(t, testCase.expected, testCase.params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
err := appendTemplatedValues(testCase.values, testCase.params, true, nil)
|
err := appendTemplatedValues(testCase.values, testCase.params, true, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, testCase.expected, testCase.params)
|
assert.EqualValues(t, testCase.expected, testCase.params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ func NewApplicationsetMetrics(appsetLister applisters.ApplicationSetLister, apps
|
|||||||
metrics.Registry.MustRegister(reconcileHistogram)
|
metrics.Registry.MustRegister(reconcileHistogram)
|
||||||
metrics.Registry.MustRegister(appsetCollector)
|
metrics.Registry.MustRegister(appsetCollector)
|
||||||
|
|
||||||
kubectl.RegisterWithClientGo()
|
kubectlMetricsServer := kubectl.NewKubectlMetrics()
|
||||||
|
kubectlMetricsServer.RegisterWithClientGo()
|
||||||
kubectl.RegisterWithPrometheus(metrics.Registry)
|
kubectl.RegisterWithPrometheus(metrics.Registry)
|
||||||
|
|
||||||
return ApplicationsetMetrics{
|
return ApplicationsetMetrics{
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ func TestApplicationsetCollector(t *testing.T) {
|
|||||||
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||||
|
|
||||||
metrics.Registry.MustRegister(appsetCollector)
|
metrics.Registry.MustRegister(appsetCollector)
|
||||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", http.NoBody)
|
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||||
@@ -216,7 +216,7 @@ func TestObserveReconcile(t *testing.T) {
|
|||||||
|
|
||||||
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
|
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", http.NoBody)
|
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Doc for the GitHub API rate limit headers:
|
|
||||||
// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#checking-the-status-of-your-rate-limit
|
|
||||||
|
|
||||||
// Metric names as constants
|
|
||||||
const (
|
|
||||||
githubAPIRequestTotalMetricName = "argocd_github_api_requests_total"
|
|
||||||
githubAPIRequestDurationMetricName = "argocd_github_api_request_duration_seconds"
|
|
||||||
githubAPIRateLimitRemainingMetricName = "argocd_github_api_rate_limit_remaining"
|
|
||||||
githubAPIRateLimitLimitMetricName = "argocd_github_api_rate_limit_limit"
|
|
||||||
githubAPIRateLimitResetMetricName = "argocd_github_api_rate_limit_reset_seconds"
|
|
||||||
githubAPIRateLimitUsedMetricName = "argocd_github_api_rate_limit_used"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GitHubMetrics groups all metric vectors for easier injection and registration
|
|
||||||
type GitHubMetrics struct {
|
|
||||||
RequestTotal *prometheus.CounterVec
|
|
||||||
RequestDuration *prometheus.HistogramVec
|
|
||||||
RateLimitRemaining *prometheus.GaugeVec
|
|
||||||
RateLimitLimit *prometheus.GaugeVec
|
|
||||||
RateLimitReset *prometheus.GaugeVec
|
|
||||||
RateLimitUsed *prometheus.GaugeVec
|
|
||||||
}
|
|
||||||
|
|
||||||
// Factory for a new set of GitHub metrics (for tests or custom registries)
|
|
||||||
func NewGitHubMetrics() *GitHubMetrics {
|
|
||||||
return &GitHubMetrics{
|
|
||||||
RequestTotal: NewGitHubAPIRequestTotal(),
|
|
||||||
RequestDuration: NewGitHubAPIRequestDuration(),
|
|
||||||
RateLimitRemaining: NewGitHubAPIRateLimitRemaining(),
|
|
||||||
RateLimitLimit: NewGitHubAPIRateLimitLimit(),
|
|
||||||
RateLimitReset: NewGitHubAPIRateLimitReset(),
|
|
||||||
RateLimitUsed: NewGitHubAPIRateLimitUsed(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Factory functions for each metric vector
|
|
||||||
func NewGitHubAPIRequestTotal() *prometheus.CounterVec {
|
|
||||||
return prometheus.NewCounterVec(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: githubAPIRequestTotalMetricName,
|
|
||||||
Help: "Total number of GitHub API requests",
|
|
||||||
},
|
|
||||||
[]string{"method", "endpoint", "status", "appset_namespace", "appset_name"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGitHubAPIRequestDuration() *prometheus.HistogramVec {
|
|
||||||
return prometheus.NewHistogramVec(
|
|
||||||
prometheus.HistogramOpts{
|
|
||||||
Name: githubAPIRequestDurationMetricName,
|
|
||||||
Help: "GitHub API request duration in seconds",
|
|
||||||
Buckets: prometheus.DefBuckets,
|
|
||||||
},
|
|
||||||
[]string{"method", "endpoint", "appset_namespace", "appset_name"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGitHubAPIRateLimitRemaining() *prometheus.GaugeVec {
|
|
||||||
return prometheus.NewGaugeVec(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Name: githubAPIRateLimitRemainingMetricName,
|
|
||||||
Help: "The number of requests remaining in the current rate limit window",
|
|
||||||
},
|
|
||||||
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGitHubAPIRateLimitLimit() *prometheus.GaugeVec {
|
|
||||||
return prometheus.NewGaugeVec(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Name: githubAPIRateLimitLimitMetricName,
|
|
||||||
Help: "The maximum number of requests that you can make per hour",
|
|
||||||
},
|
|
||||||
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGitHubAPIRateLimitReset() *prometheus.GaugeVec {
|
|
||||||
return prometheus.NewGaugeVec(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Name: githubAPIRateLimitResetMetricName,
|
|
||||||
Help: "The time left till the current rate limit window resets, in seconds",
|
|
||||||
},
|
|
||||||
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGitHubAPIRateLimitUsed() *prometheus.GaugeVec {
|
|
||||||
return prometheus.NewGaugeVec(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Name: githubAPIRateLimitUsedMetricName,
|
|
||||||
Help: "The number of requests used in the current rate limit window",
|
|
||||||
},
|
|
||||||
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global metrics (registered with the default registry)
|
|
||||||
var globalGitHubMetrics = NewGitHubMetrics()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.Debug("Registering GitHub API AppSet metrics")
|
|
||||||
metrics.Registry.MustRegister(globalGitHubMetrics.RequestTotal)
|
|
||||||
metrics.Registry.MustRegister(globalGitHubMetrics.RequestDuration)
|
|
||||||
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitRemaining)
|
|
||||||
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitLimit)
|
|
||||||
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitReset)
|
|
||||||
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitUsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
type MetricsContext struct {
|
|
||||||
AppSetNamespace string
|
|
||||||
AppSetName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// GitHubMetricsTransport is a custom http.RoundTripper that collects GitHub API metrics
|
|
||||||
type GitHubMetricsTransport struct {
|
|
||||||
transport http.RoundTripper
|
|
||||||
metricsContext *MetricsContext
|
|
||||||
metrics *GitHubMetrics
|
|
||||||
}
|
|
||||||
|
|
||||||
// RoundTrip implements http.RoundTripper interface and collects metrics along with debug logging
|
|
||||||
func (t *GitHubMetricsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
||||||
endpoint := req.URL.Path
|
|
||||||
method := req.Method
|
|
||||||
|
|
||||||
appsetNamespace := "unknown"
|
|
||||||
appsetName := "unknown"
|
|
||||||
|
|
||||||
if t.metricsContext != nil {
|
|
||||||
appsetNamespace = t.metricsContext.AppSetNamespace
|
|
||||||
appsetName = t.metricsContext.AppSetName
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"method": method,
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"applicationset": map[string]string{"name": appsetName, "namespace": appsetNamespace},
|
|
||||||
}).Debugf("Invoking GitHub API")
|
|
||||||
|
|
||||||
startTime := time.Now()
|
|
||||||
resp, err := t.transport.RoundTrip(req)
|
|
||||||
duration := time.Since(startTime)
|
|
||||||
|
|
||||||
// Record metrics
|
|
||||||
t.metrics.RequestDuration.WithLabelValues(method, endpoint, appsetNamespace, appsetName).Observe(duration.Seconds())
|
|
||||||
|
|
||||||
status := "0"
|
|
||||||
if resp != nil {
|
|
||||||
status = strconv.Itoa(resp.StatusCode)
|
|
||||||
}
|
|
||||||
t.metrics.RequestTotal.WithLabelValues(method, endpoint, status, appsetNamespace, appsetName).Inc()
|
|
||||||
|
|
||||||
if resp != nil {
|
|
||||||
resetHumanReadableTime := ""
|
|
||||||
remainingInt := 0
|
|
||||||
limitInt := 0
|
|
||||||
usedInt := 0
|
|
||||||
resource := resp.Header.Get("X-RateLimit-Resource")
|
|
||||||
|
|
||||||
// Record rate limit metrics if available
|
|
||||||
if resetTime := resp.Header.Get("X-RateLimit-Reset"); resetTime != "" {
|
|
||||||
if resetUnix, err := strconv.ParseInt(resetTime, 10, 64); err == nil {
|
|
||||||
// Calculate seconds until reset (reset timestamp - current time)
|
|
||||||
secondsUntilReset := resetUnix - time.Now().Unix()
|
|
||||||
t.metrics.RateLimitReset.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(secondsUntilReset))
|
|
||||||
resetHumanReadableTime = time.Unix(resetUnix, 0).Local().Format("2006-01-02 15:04:05 MST")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if remaining := resp.Header.Get("X-RateLimit-Remaining"); remaining != "" {
|
|
||||||
if remainingInt, err = strconv.Atoi(remaining); err == nil {
|
|
||||||
t.metrics.RateLimitRemaining.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(remainingInt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if limit := resp.Header.Get("X-RateLimit-Limit"); limit != "" {
|
|
||||||
if limitInt, err = strconv.Atoi(limit); err == nil {
|
|
||||||
t.metrics.RateLimitLimit.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(limitInt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if used := resp.Header.Get("X-RateLimit-Used"); used != "" {
|
|
||||||
if usedInt, err = strconv.Atoi(used); err == nil {
|
|
||||||
t.metrics.RateLimitUsed.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(usedInt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"reset": resetHumanReadableTime,
|
|
||||||
"remaining": remainingInt,
|
|
||||||
"limit": limitInt,
|
|
||||||
"used": usedInt,
|
|
||||||
"resource": resource,
|
|
||||||
"applicationset": map[string]string{"name": appsetName, "namespace": appsetNamespace},
|
|
||||||
}).Debugf("GitHub API rate limit info")
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Full constructor (for tests and advanced use)
|
|
||||||
func NewGitHubMetricsTransport(
|
|
||||||
transport http.RoundTripper,
|
|
||||||
metricsContext *MetricsContext,
|
|
||||||
metrics *GitHubMetrics,
|
|
||||||
) *GitHubMetricsTransport {
|
|
||||||
return &GitHubMetricsTransport{
|
|
||||||
transport: transport,
|
|
||||||
metricsContext: metricsContext,
|
|
||||||
metrics: metrics,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default constructor
|
|
||||||
func NewDefaultGitHubMetricsTransport(transport http.RoundTripper, metricsContext *MetricsContext) *GitHubMetricsTransport {
|
|
||||||
return NewGitHubMetricsTransport(
|
|
||||||
transport,
|
|
||||||
metricsContext,
|
|
||||||
globalGitHubMetrics,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGitHubMetricsClient wraps an http.Client with metrics middleware
|
|
||||||
func NewGitHubMetricsClient(metricsContext *MetricsContext) *http.Client {
|
|
||||||
log.Debug("Creating new GitHub metrics client")
|
|
||||||
return &http.Client{
|
|
||||||
Transport: NewDefaultGitHubMetricsTransport(http.DefaultTransport, metricsContext),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Metric struct {
|
|
||||||
name string
|
|
||||||
labels []string
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
endpointLabel = "endpoint=\"/api/test\""
|
|
||||||
URL = "/api/test"
|
|
||||||
appsetNamespaceLabel = "appset_namespace=\"test-ns\""
|
|
||||||
appsetNamespace = "test-ns"
|
|
||||||
appsetName = "test-appset"
|
|
||||||
appsetNameLabel = "appset_name=\"test-appset\""
|
|
||||||
resourceLabel = "resource=\"core\""
|
|
||||||
|
|
||||||
rateLimitMetrics = []Metric{
|
|
||||||
{
|
|
||||||
name: githubAPIRateLimitRemainingMetricName,
|
|
||||||
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
|
||||||
value: "42",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: githubAPIRateLimitLimitMetricName,
|
|
||||||
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
|
||||||
value: "100",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: githubAPIRateLimitUsedMetricName,
|
|
||||||
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
|
||||||
value: "58",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: githubAPIRateLimitResetMetricName,
|
|
||||||
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
|
||||||
value: "1",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
successRequestMetrics = Metric{
|
|
||||||
name: githubAPIRequestTotalMetricName,
|
|
||||||
labels: []string{"method=\"GET\"", endpointLabel, "status=\"201\"", appsetNamespaceLabel, appsetNameLabel},
|
|
||||||
value: "1",
|
|
||||||
}
|
|
||||||
failureRequestMetrics = Metric{
|
|
||||||
name: githubAPIRequestTotalMetricName,
|
|
||||||
labels: []string{"method=\"GET\"", endpointLabel, "status=\"0\"", appsetNamespaceLabel, appsetNameLabel},
|
|
||||||
value: "1",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGitHubMetrics_CollectorApproach_Success(t *testing.T) {
|
|
||||||
metrics := NewGitHubMetrics()
|
|
||||||
reg := prometheus.NewRegistry()
|
|
||||||
reg.MustRegister(
|
|
||||||
metrics.RequestTotal,
|
|
||||||
metrics.RequestDuration,
|
|
||||||
metrics.RateLimitRemaining,
|
|
||||||
metrics.RateLimitLimit,
|
|
||||||
metrics.RateLimitReset,
|
|
||||||
metrics.RateLimitUsed,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Setup a fake HTTP server
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
w.Header().Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Unix()+1, 10))
|
|
||||||
w.Header().Set("X-RateLimit-Remaining", "42")
|
|
||||||
w.Header().Set("X-RateLimit-Limit", "100")
|
|
||||||
w.Header().Set("X-RateLimit-Used", "58")
|
|
||||||
w.Header().Set("X-RateLimit-Resource", "core")
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
_, _ = w.Write([]byte("ok"))
|
|
||||||
}))
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
metricsCtx := &MetricsContext{AppSetNamespace: appsetNamespace, AppSetName: appsetName}
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: NewGitHubMetricsTransport(
|
|
||||||
http.DefaultTransport,
|
|
||||||
metricsCtx,
|
|
||||||
metrics,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL+URL, http.NoBody)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
// Expose and scrape metrics
|
|
||||||
handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
|
||||||
server := httptest.NewServer(handler)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
req, err = http.NewRequestWithContext(ctx, http.MethodGet, server.URL, http.NoBody)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create request: %v", err)
|
|
||||||
}
|
|
||||||
resp, err = http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to scrape metrics: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
|
||||||
metricsOutput := string(body)
|
|
||||||
|
|
||||||
sort.Strings(successRequestMetrics.labels)
|
|
||||||
assert.Contains(t, metricsOutput, successRequestMetrics.name+"{"+strings.Join(successRequestMetrics.labels, ",")+"} "+successRequestMetrics.value)
|
|
||||||
|
|
||||||
for _, metric := range rateLimitMetrics {
|
|
||||||
sort.Strings(metric.labels)
|
|
||||||
assert.Contains(t, metricsOutput, metric.name+"{"+strings.Join(metric.labels, ",")+"} "+metric.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoundTripperFunc func(*http.Request) (*http.Response, error)
|
|
||||||
|
|
||||||
func (f RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
|
||||||
|
|
||||||
func TestGitHubMetrics_CollectorApproach_NoRateLimitMetricsOnNilResponse(t *testing.T) {
|
|
||||||
metrics := NewGitHubMetrics()
|
|
||||||
reg := prometheus.NewRegistry()
|
|
||||||
reg.MustRegister(
|
|
||||||
metrics.RequestTotal,
|
|
||||||
metrics.RequestDuration,
|
|
||||||
metrics.RateLimitRemaining,
|
|
||||||
metrics.RateLimitLimit,
|
|
||||||
metrics.RateLimitReset,
|
|
||||||
metrics.RateLimitUsed,
|
|
||||||
)
|
|
||||||
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: &GitHubMetricsTransport{
|
|
||||||
transport: RoundTripperFunc(func(*http.Request) (*http.Response, error) {
|
|
||||||
return nil, http.ErrServerClosed
|
|
||||||
}),
|
|
||||||
metricsContext: &MetricsContext{AppSetNamespace: appsetNamespace, AppSetName: appsetName},
|
|
||||||
metrics: metrics,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
ctx := t.Context()
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, URL, http.NoBody)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create request: %v", err)
|
|
||||||
}
|
|
||||||
_, _ = client.Do(req)
|
|
||||||
|
|
||||||
handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
|
||||||
server := httptest.NewServer(handler)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
req, err = http.NewRequestWithContext(ctx, http.MethodGet, server.URL, http.NoBody)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create request: %v", err)
|
|
||||||
}
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to scrape metrics: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
|
||||||
metricsOutput := string(body)
|
|
||||||
|
|
||||||
// Verify request metric exists with status "0"
|
|
||||||
sort.Strings(failureRequestMetrics.labels)
|
|
||||||
assert.Contains(t, metricsOutput, failureRequestMetrics.name+"{"+strings.Join(failureRequestMetrics.labels, ",")+"} "+failureRequestMetrics.value)
|
|
||||||
|
|
||||||
// Verify rate limit metrics don't exist
|
|
||||||
for _, metric := range rateLimitMetrics {
|
|
||||||
sort.Strings(metric.labels)
|
|
||||||
assert.NotContains(t, metricsOutput, metric.name+"{"+strings.Join(metric.labels, ",")+"} "+metric.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewGitHubMetricsClient(t *testing.T) {
|
|
||||||
// Test cases
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
metricsCtx *MetricsContext
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "with metrics context",
|
|
||||||
metricsCtx: &MetricsContext{
|
|
||||||
AppSetNamespace: appsetNamespace,
|
|
||||||
AppSetName: appsetName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "with nil metrics context",
|
|
||||||
metricsCtx: nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
// Create client
|
|
||||||
client := NewGitHubMetricsClient(tc.metricsCtx)
|
|
||||||
|
|
||||||
// Assert client is not nil
|
|
||||||
assert.NotNil(t, client)
|
|
||||||
|
|
||||||
// Assert transport is properly configured
|
|
||||||
transport, ok := client.Transport.(*GitHubMetricsTransport)
|
|
||||||
assert.True(t, ok, "Transport should be GitHubMetricsTransport")
|
|
||||||
|
|
||||||
// Verify transport configuration
|
|
||||||
assert.Equal(t, tc.metricsCtx, transport.metricsContext)
|
|
||||||
assert.NotNil(t, transport.metrics, "Metrics should not be nil")
|
|
||||||
assert.Equal(t, http.DefaultTransport, transport.transport, "Base transport should be http.DefaultTransport")
|
|
||||||
|
|
||||||
// Verify metrics are global metrics
|
|
||||||
assert.Equal(t, globalGitHubMetrics, transport.metrics, "Should use global metrics")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +1,35 @@
|
|||||||
package github_app
|
package github_app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/bradleyfalzon/ghinstallation/v2"
|
"github.com/bradleyfalzon/ghinstallation/v2"
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v66/github"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||||
"github.com/argoproj/argo-cd/v3/util/git"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// getInstallationClient creates a new GitHub client with the specified installation ID.
|
// Client builds a github client for the given app authentication.
|
||||||
// It also returns a ghinstallation.Transport, which can be used for git requests.
|
func Client(g github_app_auth.Authentication, url string) (*github.Client, error) {
|
||||||
func getInstallationClient(g github_app_auth.Authentication, url string, httpClient ...*http.Client) (*github.Client, error) {
|
rt, err := ghinstallation.New(http.DefaultTransport, g.Id, g.InstallationId, []byte(g.PrivateKey))
|
||||||
if g.InstallationId <= 0 {
|
|
||||||
return nil, errors.New("installation ID is required for github")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use provided HTTP client's transport or default
|
|
||||||
var transport http.RoundTripper
|
|
||||||
if len(httpClient) > 0 && httpClient[0] != nil && httpClient[0].Transport != nil {
|
|
||||||
transport = httpClient[0].Transport
|
|
||||||
} else {
|
|
||||||
transport = http.DefaultTransport
|
|
||||||
}
|
|
||||||
|
|
||||||
itr, err := ghinstallation.New(transport, g.Id, g.InstallationId, []byte(g.PrivateKey))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create GitHub installation transport: %w", err)
|
return nil, fmt.Errorf("failed to create github app install: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if url == "" {
|
if url == "" {
|
||||||
url = g.EnterpriseBaseURL
|
url = g.EnterpriseBaseURL
|
||||||
}
|
}
|
||||||
|
|
||||||
var client *github.Client
|
var client *github.Client
|
||||||
if url == "" {
|
if url == "" {
|
||||||
client = github.NewClient(&http.Client{Transport: itr})
|
httpClient := http.Client{Transport: rt}
|
||||||
return client, nil
|
client = github.NewClient(&httpClient)
|
||||||
}
|
} else {
|
||||||
|
rt.BaseURL = url
|
||||||
itr.BaseURL = url
|
httpClient := http.Client{Transport: rt}
|
||||||
client, err = github.NewClient(&http.Client{Transport: itr}).WithEnterpriseURLs(url, url)
|
client, err = github.NewClient(&httpClient).WithEnterpriseURLs(url, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create GitHub enterprise client: %w", err)
|
return nil, fmt.Errorf("failed to create github enterprise client: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client builds a github client for the given app authentication.
|
|
||||||
func Client(ctx context.Context, g github_app_auth.Authentication, url, org string, optionalHTTPClient ...*http.Client) (*github.Client, error) {
|
|
||||||
if url == "" {
|
|
||||||
url = g.EnterpriseBaseURL
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an installation ID is already provided, use it directly.
|
|
||||||
if g.InstallationId != 0 {
|
|
||||||
return getInstallationClient(g, url, optionalHTTPClient...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-discover installation ID using shared utility
|
|
||||||
// Pass optional HTTP client for metrics tracking
|
|
||||||
installationId, err := git.DiscoverGitHubAppInstallationID(ctx, g.Id, g.PrivateKey, url, org, optionalHTTPClient...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
g.InstallationId = installationId
|
|
||||||
return getInstallationClient(g, url, optionalHTTPClient...)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func (c *Client) NewRequestWithContext(ctx context.Context, method, path string,
|
|||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.token != "" {
|
if len(c.token) != 0 {
|
||||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
274
applicationset/services/mocks/Repos.go
generated
274
applicationset/services/mocks/Repos.go
generated
@@ -1,15 +1,78 @@
|
|||||||
// Code generated by mockery; DO NOT EDIT.
|
// Code generated by mockery v2.52.4. DO NOT EDIT.
|
||||||
// github.com/vektra/mockery
|
|
||||||
// template: testify
|
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
context "context"
|
||||||
|
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Repos is an autogenerated mock type for the Repos type
|
||||||
|
type Repos struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, project, noRevisionCache, verifyCommit
|
||||||
|
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool) ([]string, error) {
|
||||||
|
ret := _m.Called(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetDirectories")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []string
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) ([]string, error)); ok {
|
||||||
|
return rf(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) []string); ok {
|
||||||
|
r0 = rf(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool, bool) error); ok {
|
||||||
|
r1 = rf(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFiles provides a mock function with given fields: ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit
|
||||||
|
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error) {
|
||||||
|
ret := _m.Called(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetFiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 map[string][]byte
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool, bool) (map[string][]byte, error)); ok {
|
||||||
|
return rf(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool, bool) map[string][]byte); ok {
|
||||||
|
r0 = rf(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(map[string][]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, bool, bool) error); ok {
|
||||||
|
r1 = rf(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
// The first argument is typically a *testing.T value.
|
// The first argument is typically a *testing.T value.
|
||||||
func NewRepos(t interface {
|
func NewRepos(t interface {
|
||||||
@@ -23,206 +86,3 @@ func NewRepos(t interface {
|
|||||||
|
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repos is an autogenerated mock type for the Repos type
|
|
||||||
type Repos struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
type Repos_Expecter struct {
|
|
||||||
mock *mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_m *Repos) EXPECT() *Repos_Expecter {
|
|
||||||
return &Repos_Expecter{mock: &_m.Mock}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDirectories provides a mock function for the type Repos
|
|
||||||
func (_mock *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool) ([]string, error) {
|
|
||||||
ret := _mock.Called(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetDirectories")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 []string
|
|
||||||
var r1 error
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) ([]string, error)); ok {
|
|
||||||
return returnFunc(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
|
||||||
}
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) []string); ok {
|
|
||||||
r0 = returnFunc(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([]string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, string, bool, bool) error); ok {
|
|
||||||
r1 = returnFunc(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repos_GetDirectories_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDirectories'
|
|
||||||
type Repos_GetDirectories_Call struct {
|
|
||||||
*mock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDirectories is a helper method to define mock.On call
|
|
||||||
// - ctx context.Context
|
|
||||||
// - repoURL string
|
|
||||||
// - revision string
|
|
||||||
// - project string
|
|
||||||
// - noRevisionCache bool
|
|
||||||
// - verifyCommit bool
|
|
||||||
func (_e *Repos_Expecter) GetDirectories(ctx interface{}, repoURL interface{}, revision interface{}, project interface{}, noRevisionCache interface{}, verifyCommit interface{}) *Repos_GetDirectories_Call {
|
|
||||||
return &Repos_GetDirectories_Call{Call: _e.mock.On("GetDirectories", ctx, repoURL, revision, project, noRevisionCache, verifyCommit)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Repos_GetDirectories_Call) Run(run func(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool)) *Repos_GetDirectories_Call {
|
|
||||||
_c.Call.Run(func(args mock.Arguments) {
|
|
||||||
var arg0 context.Context
|
|
||||||
if args[0] != nil {
|
|
||||||
arg0 = args[0].(context.Context)
|
|
||||||
}
|
|
||||||
var arg1 string
|
|
||||||
if args[1] != nil {
|
|
||||||
arg1 = args[1].(string)
|
|
||||||
}
|
|
||||||
var arg2 string
|
|
||||||
if args[2] != nil {
|
|
||||||
arg2 = args[2].(string)
|
|
||||||
}
|
|
||||||
var arg3 string
|
|
||||||
if args[3] != nil {
|
|
||||||
arg3 = args[3].(string)
|
|
||||||
}
|
|
||||||
var arg4 bool
|
|
||||||
if args[4] != nil {
|
|
||||||
arg4 = args[4].(bool)
|
|
||||||
}
|
|
||||||
var arg5 bool
|
|
||||||
if args[5] != nil {
|
|
||||||
arg5 = args[5].(bool)
|
|
||||||
}
|
|
||||||
run(
|
|
||||||
arg0,
|
|
||||||
arg1,
|
|
||||||
arg2,
|
|
||||||
arg3,
|
|
||||||
arg4,
|
|
||||||
arg5,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Repos_GetDirectories_Call) Return(strings []string, err error) *Repos_GetDirectories_Call {
|
|
||||||
_c.Call.Return(strings, err)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Repos_GetDirectories_Call) RunAndReturn(run func(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool) ([]string, error)) *Repos_GetDirectories_Call {
|
|
||||||
_c.Call.Return(run)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFiles provides a mock function for the type Repos
|
|
||||||
func (_mock *Repos) GetFiles(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error) {
|
|
||||||
ret := _mock.Called(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetFiles")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 map[string][]byte
|
|
||||||
var r1 error
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool, bool) (map[string][]byte, error)); ok {
|
|
||||||
return returnFunc(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
|
||||||
}
|
|
||||||
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool, bool) map[string][]byte); ok {
|
|
||||||
r0 = returnFunc(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(map[string][]byte)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, string, string, bool, bool) error); ok {
|
|
||||||
r1 = returnFunc(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repos_GetFiles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFiles'
|
|
||||||
type Repos_GetFiles_Call struct {
|
|
||||||
*mock.Call
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFiles is a helper method to define mock.On call
|
|
||||||
// - ctx context.Context
|
|
||||||
// - repoURL string
|
|
||||||
// - revision string
|
|
||||||
// - project string
|
|
||||||
// - pattern string
|
|
||||||
// - noRevisionCache bool
|
|
||||||
// - verifyCommit bool
|
|
||||||
func (_e *Repos_Expecter) GetFiles(ctx interface{}, repoURL interface{}, revision interface{}, project interface{}, pattern interface{}, noRevisionCache interface{}, verifyCommit interface{}) *Repos_GetFiles_Call {
|
|
||||||
return &Repos_GetFiles_Call{Call: _e.mock.On("GetFiles", ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Repos_GetFiles_Call) Run(run func(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool)) *Repos_GetFiles_Call {
|
|
||||||
_c.Call.Run(func(args mock.Arguments) {
|
|
||||||
var arg0 context.Context
|
|
||||||
if args[0] != nil {
|
|
||||||
arg0 = args[0].(context.Context)
|
|
||||||
}
|
|
||||||
var arg1 string
|
|
||||||
if args[1] != nil {
|
|
||||||
arg1 = args[1].(string)
|
|
||||||
}
|
|
||||||
var arg2 string
|
|
||||||
if args[2] != nil {
|
|
||||||
arg2 = args[2].(string)
|
|
||||||
}
|
|
||||||
var arg3 string
|
|
||||||
if args[3] != nil {
|
|
||||||
arg3 = args[3].(string)
|
|
||||||
}
|
|
||||||
var arg4 string
|
|
||||||
if args[4] != nil {
|
|
||||||
arg4 = args[4].(string)
|
|
||||||
}
|
|
||||||
var arg5 bool
|
|
||||||
if args[5] != nil {
|
|
||||||
arg5 = args[5].(bool)
|
|
||||||
}
|
|
||||||
var arg6 bool
|
|
||||||
if args[6] != nil {
|
|
||||||
arg6 = args[6].(bool)
|
|
||||||
}
|
|
||||||
run(
|
|
||||||
arg0,
|
|
||||||
arg1,
|
|
||||||
arg2,
|
|
||||||
arg3,
|
|
||||||
arg4,
|
|
||||||
arg5,
|
|
||||||
arg6,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Repos_GetFiles_Call) Return(stringToBytes map[string][]byte, err error) *Repos_GetFiles_Call {
|
|
||||||
_c.Call.Return(stringToBytes, err)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_c *Repos_GetFiles_Call) RunAndReturn(run func(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error)) *Repos_GetFiles_Call {
|
|
||||||
_c.Call.Return(run)
|
|
||||||
return _c
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,15 +5,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
|
"github.com/microsoft/azure-devops-go-api/azuredevops"
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/core"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/core"
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
|
||||||
AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
|
|
||||||
AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR = "The following project does not exist"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AzureDevOpsClientFactory interface {
|
type AzureDevOpsClientFactory interface {
|
||||||
// Returns an Azure Devops Client interface.
|
// Returns an Azure Devops Client interface.
|
||||||
@@ -73,22 +70,13 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||||
}
|
}
|
||||||
|
|
||||||
pullRequests := []*PullRequest{}
|
|
||||||
|
|
||||||
azurePullRequests, err := client.GetPullRequestsByProject(ctx, args)
|
azurePullRequests, err := client.GetPullRequestsByProject(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// A standard Http 404 error is not returned for Azure DevOps,
|
|
||||||
// so checking the error message for a specific pattern.
|
|
||||||
// NOTE: Since the repos are filtered later, only existence of the project
|
|
||||||
// is relevant for AzureDevOps
|
|
||||||
if strings.Contains(err.Error(), AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR) {
|
|
||||||
// return a custom error indicating that the repository is not found,
|
|
||||||
// but also return the empty result since the decision to continue or not in this case is made by the caller
|
|
||||||
return pullRequests, NewRepositoryNotFoundError(err)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("failed to get pull requests by project: %w", err)
|
return nil, fmt.Errorf("failed to get pull requests by project: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pullRequests := []*PullRequest{}
|
||||||
|
|
||||||
for _, pr := range *azurePullRequests {
|
for _, pr := range *azurePullRequests {
|
||||||
if pr.Repository == nil ||
|
if pr.Repository == nil ||
|
||||||
pr.Repository.Name == nil ||
|
pr.Repository.Name == nil ||
|
||||||
@@ -107,7 +95,7 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
|
|
||||||
if *pr.Repository.Name == a.repo {
|
if *pr.Repository.Name == a.repo {
|
||||||
pullRequests = append(pullRequests, &PullRequest{
|
pullRequests = append(pullRequests, &PullRequest{
|
||||||
Number: int64(*pr.PullRequestId),
|
Number: *pr.PullRequestId,
|
||||||
Title: *pr.Title,
|
Title: *pr.Title,
|
||||||
Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1),
|
Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1),
|
||||||
TargetBranch: strings.Replace(*pr.TargetRefName, "refs/heads/", "", 1),
|
TargetBranch: strings.Replace(*pr.TargetRefName, "refs/heads/", "", 1),
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package pull_request
|
package pull_request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/core"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/webapi"
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/webapi"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/core"
|
||||||
|
"github.com/microsoft/azure-devops-go-api/azuredevops/git"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
azureMock "github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/azure_devops/git/mocks"
|
azureMock "github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/azure_devops/git/mocks"
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/mocks"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func createBoolPtr(x bool) *bool {
|
func createBoolPtr(x bool) *bool {
|
||||||
@@ -35,6 +35,29 @@ func createUniqueNamePtr(x string) *string {
|
|||||||
return &x
|
return &x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AzureClientFactoryMock struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AzureClientFactoryMock) GetClient(ctx context.Context) (git.Client, error) {
|
||||||
|
args := m.mock.Called(ctx)
|
||||||
|
|
||||||
|
var client git.Client
|
||||||
|
c := args.Get(0)
|
||||||
|
if c != nil {
|
||||||
|
client = c.(git.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if len(args) > 1 {
|
||||||
|
if e, ok := args.Get(1).(error); ok {
|
||||||
|
err = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
|
||||||
func TestListPullRequest(t *testing.T) {
|
func TestListPullRequest(t *testing.T) {
|
||||||
teamProject := "myorg_project"
|
teamProject := "myorg_project"
|
||||||
repoName := "myorg_project_repo"
|
repoName := "myorg_project_repo"
|
||||||
@@ -68,10 +91,10 @@ func TestListPullRequest(t *testing.T) {
|
|||||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||||
}
|
}
|
||||||
|
|
||||||
gitClientMock := &azureMock.Client{}
|
gitClientMock := azureMock.Client{}
|
||||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
|
||||||
gitClientMock.EXPECT().GetPullRequestsByProject(mock.Anything, args).Return(&pullRequestMock, nil)
|
gitClientMock.On("GetPullRequestsByProject", ctx, args).Return(&pullRequestMock, nil)
|
||||||
|
|
||||||
provider := AzureDevOpsService{
|
provider := AzureDevOpsService{
|
||||||
clientFactory: clientFactoryMock,
|
clientFactory: clientFactoryMock,
|
||||||
@@ -87,7 +110,7 @@ func TestListPullRequest(t *testing.T) {
|
|||||||
assert.Equal(t, "main", list[0].TargetBranch)
|
assert.Equal(t, "main", list[0].TargetBranch)
|
||||||
assert.Equal(t, prHeadSha, list[0].HeadSHA)
|
assert.Equal(t, prHeadSha, list[0].HeadSHA)
|
||||||
assert.Equal(t, "feat(123)", list[0].Title)
|
assert.Equal(t, "feat(123)", list[0].Title)
|
||||||
assert.Equal(t, int64(prID), list[0].Number)
|
assert.Equal(t, prID, list[0].Number)
|
||||||
assert.Equal(t, uniqueName, list[0].Author)
|
assert.Equal(t, uniqueName, list[0].Author)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,36 +236,3 @@ func TestBuildURL(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAzureDevOpsListReturnsRepositoryNotFoundError(t *testing.T) {
|
|
||||||
args := git.GetPullRequestsByProjectArgs{
|
|
||||||
Project: createStringPtr("nonexistent"),
|
|
||||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
|
||||||
}
|
|
||||||
|
|
||||||
pullRequestMock := []git.GitPullRequest{}
|
|
||||||
|
|
||||||
gitClientMock := &azureMock.Client{}
|
|
||||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
|
||||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
|
||||||
|
|
||||||
// Mock the GetPullRequestsByProject to return an error containing "404"
|
|
||||||
gitClientMock.EXPECT().GetPullRequestsByProject(mock.Anything, args).Return(&pullRequestMock,
|
|
||||||
errors.New("The following project does not exist:"))
|
|
||||||
|
|
||||||
provider := AzureDevOpsService{
|
|
||||||
clientFactory: clientFactoryMock,
|
|
||||||
project: "nonexistent",
|
|
||||||
repo: "nonexistent",
|
|
||||||
labels: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
prs, err := provider.List(t.Context())
|
|
||||||
|
|
||||||
// Should return empty pull requests list
|
|
||||||
assert.Empty(t, prs)
|
|
||||||
|
|
||||||
// Should return RepositoryNotFoundError
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ktrysmt/go-bitbucket"
|
"github.com/ktrysmt/go-bitbucket"
|
||||||
)
|
)
|
||||||
@@ -18,19 +17,10 @@ type BitbucketCloudService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BitbucketCloudPullRequest struct {
|
type BitbucketCloudPullRequest struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Source BitbucketCloudPullRequestSource `json:"source"`
|
Source BitbucketCloudPullRequestSource `json:"source"`
|
||||||
Author BitbucketCloudPullRequestAuthor `json:"author"`
|
Author BitbucketCloudPullRequestAuthor `json:"author"`
|
||||||
Destination BitbucketCloudPullRequestDestination `json:"destination"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BitbucketCloudPullRequestDestination struct {
|
|
||||||
Branch BitbucketCloudPullRequestDestinationBranch `json:"branch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BitbucketCloudPullRequestDestinationBranch struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BitbucketCloudPullRequestSource struct {
|
type BitbucketCloudPullRequestSource struct {
|
||||||
@@ -81,10 +71,7 @@ func NewBitbucketCloudServiceBasicAuth(baseURL, username, password, owner, repos
|
|||||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseURL, owner, repositorySlug, err)
|
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseURL, owner, repositorySlug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bitbucketClient, err := bitbucket.NewBasicAuth(username, password)
|
bitbucketClient := bitbucket.NewBasicAuth(username, password)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error creating BitBucket Cloud client with basic auth: %w", err)
|
|
||||||
}
|
|
||||||
bitbucketClient.SetApiBaseURL(*url)
|
bitbucketClient.SetApiBaseURL(*url)
|
||||||
|
|
||||||
return &BitbucketCloudService{
|
return &BitbucketCloudService{
|
||||||
@@ -100,13 +87,14 @@ func NewBitbucketCloudServiceBearerToken(baseURL, bearerToken, owner, repository
|
|||||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseURL, owner, repositorySlug, err)
|
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseURL, owner, repositorySlug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bitbucketClient, err := bitbucket.NewOAuthbearerToken(bearerToken)
|
bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error creating BitBucket Cloud client with oauth bearer token: %w", err)
|
|
||||||
}
|
|
||||||
bitbucketClient.SetApiBaseURL(*url)
|
bitbucketClient.SetApiBaseURL(*url)
|
||||||
|
|
||||||
return &BitbucketCloudService{client: bitbucketClient, owner: owner, repositorySlug: repositorySlug}, nil
|
return &BitbucketCloudService{
|
||||||
|
client: bitbucketClient,
|
||||||
|
owner: owner,
|
||||||
|
repositorySlug: repositorySlug,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBitbucketCloudServiceNoAuth(baseURL, owner, repositorySlug string) (PullRequestService, error) {
|
func NewBitbucketCloudServiceNoAuth(baseURL, owner, repositorySlug string) (PullRequestService, error) {
|
||||||
@@ -120,17 +108,8 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
|||||||
RepoSlug: b.repositorySlug,
|
RepoSlug: b.repositorySlug,
|
||||||
}
|
}
|
||||||
|
|
||||||
pullRequests := []*PullRequest{}
|
|
||||||
|
|
||||||
response, err := b.client.Repositories.PullRequests.Gets(opts)
|
response, err := b.client.Repositories.PullRequests.Gets(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// A standard Http 404 error is not returned for Bitbucket Cloud,
|
|
||||||
// so checking the error message for a specific pattern
|
|
||||||
if strings.Contains(err.Error(), "404 Not Found") {
|
|
||||||
// return a custom error indicating that the repository is not found,
|
|
||||||
// but also return the empty result since the decision to continue or not in this case is made by the caller
|
|
||||||
return pullRequests, NewRepositoryNotFoundError(err)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err)
|
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,14 +133,14 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
|||||||
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
|
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pullRequests := []*PullRequest{}
|
||||||
for _, pull := range pulls {
|
for _, pull := range pulls {
|
||||||
pullRequests = append(pullRequests, &PullRequest{
|
pullRequests = append(pullRequests, &PullRequest{
|
||||||
Number: int64(pull.ID),
|
Number: pull.ID,
|
||||||
Title: pull.Title,
|
Title: pull.Title,
|
||||||
Branch: pull.Source.Branch.Name,
|
Branch: pull.Source.Branch.Name,
|
||||||
TargetBranch: pull.Destination.Branch.Name,
|
HeadSHA: pull.Source.Commit.Hash,
|
||||||
HeadSHA: pull.Source.Commit.Hash,
|
Author: pull.Author.Nickname,
|
||||||
Author: pull.Author.Nickname,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func TestListPullRequestBearerTokenCloud(t *testing.T) {
|
|||||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, int64(101), pullRequests[0].Number)
|
assert.Equal(t, 101, pullRequests[0].Number)
|
||||||
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
||||||
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
||||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||||
@@ -107,7 +107,7 @@ func TestListPullRequestNoAuthCloud(t *testing.T) {
|
|||||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, int64(101), pullRequests[0].Number)
|
assert.Equal(t, 101, pullRequests[0].Number)
|
||||||
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
||||||
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
||||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||||
@@ -125,7 +125,7 @@ func TestListPullRequestBasicAuthCloud(t *testing.T) {
|
|||||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, int64(101), pullRequests[0].Number)
|
assert.Equal(t, 101, pullRequests[0].Number)
|
||||||
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
||||||
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
||||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||||
@@ -138,7 +138,7 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
|||||||
var err error
|
var err error
|
||||||
switch r.RequestURI {
|
switch r.RequestURI {
|
||||||
case "/repositories/OWNER/REPO/pullrequests/":
|
case "/repositories/OWNER/REPO/pullrequests/":
|
||||||
_, err = fmt.Fprintf(w, `{
|
_, err = io.WriteString(w, fmt.Sprintf(`{
|
||||||
"size": 2,
|
"size": 2,
|
||||||
"pagelen": 1,
|
"pagelen": 1,
|
||||||
"page": 1,
|
"page": 1,
|
||||||
@@ -177,9 +177,9 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`, r.Host)
|
}`, r.Host))
|
||||||
case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2":
|
case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2":
|
||||||
_, err = fmt.Fprintf(w, `{
|
_, err = io.WriteString(w, fmt.Sprintf(`{
|
||||||
"size": 2,
|
"size": 2,
|
||||||
"pagelen": 1,
|
"pagelen": 1,
|
||||||
"page": 2,
|
"page": 2,
|
||||||
@@ -202,7 +202,7 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`, r.Host)
|
}`, r.Host))
|
||||||
default:
|
default:
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
@@ -329,7 +329,7 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
var err error
|
var err error
|
||||||
switch r.RequestURI {
|
switch r.RequestURI {
|
||||||
case "/repositories/OWNER/REPO/pullrequests/":
|
case "/repositories/OWNER/REPO/pullrequests/":
|
||||||
_, err = fmt.Fprintf(w, `{
|
_, err = io.WriteString(w, fmt.Sprintf(`{
|
||||||
"size": 2,
|
"size": 2,
|
||||||
"pagelen": 1,
|
"pagelen": 1,
|
||||||
"page": 1,
|
"page": 1,
|
||||||
@@ -349,11 +349,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"nickname": "testName"
|
"nickname": "testName"
|
||||||
},
|
|
||||||
"destination": {
|
|
||||||
"branch": {
|
|
||||||
"name": "master"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -370,17 +365,12 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"nickname": "testName"
|
"nickname": "testName"
|
||||||
},
|
|
||||||
"destination": {
|
|
||||||
"branch": {
|
|
||||||
"name": "branch-200"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`, r.Host)
|
}`, r.Host))
|
||||||
case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2":
|
case "/repositories/OWNER/REPO/pullrequests/?pagelen=1&page=2":
|
||||||
_, err = fmt.Fprintf(w, `{
|
_, err = io.WriteString(w, fmt.Sprintf(`{
|
||||||
"size": 2,
|
"size": 2,
|
||||||
"pagelen": 1,
|
"pagelen": 1,
|
||||||
"page": 2,
|
"page": 2,
|
||||||
@@ -400,15 +390,10 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"nickname": "testName"
|
"nickname": "testName"
|
||||||
},
|
|
||||||
"destination": {
|
|
||||||
"branch": {
|
|
||||||
"name": "master"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`, r.Host)
|
}`, r.Host))
|
||||||
default:
|
default:
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
@@ -428,20 +413,18 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 2)
|
assert.Len(t, pullRequests, 2)
|
||||||
assert.Equal(t, PullRequest{
|
assert.Equal(t, PullRequest{
|
||||||
Number: 101,
|
Number: 101,
|
||||||
Title: "feat(101)",
|
Title: "feat(101)",
|
||||||
Branch: "feature-101",
|
Branch: "feature-101",
|
||||||
HeadSHA: "1a8dd249c04a",
|
HeadSHA: "1a8dd249c04a",
|
||||||
Author: "testName",
|
Author: "testName",
|
||||||
TargetBranch: "master",
|
|
||||||
}, *pullRequests[0])
|
}, *pullRequests[0])
|
||||||
assert.Equal(t, PullRequest{
|
assert.Equal(t, PullRequest{
|
||||||
Number: 102,
|
Number: 102,
|
||||||
Title: "feat(102)",
|
Title: "feat(102)",
|
||||||
Branch: "feature-102",
|
Branch: "feature-102",
|
||||||
HeadSHA: "6344d9623e3b",
|
HeadSHA: "6344d9623e3b",
|
||||||
Author: "testName",
|
Author: "testName",
|
||||||
TargetBranch: "master",
|
|
||||||
}, *pullRequests[1])
|
}, *pullRequests[1])
|
||||||
|
|
||||||
regexp = `.*2$`
|
regexp = `.*2$`
|
||||||
@@ -455,12 +438,11 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, PullRequest{
|
assert.Equal(t, PullRequest{
|
||||||
Number: 102,
|
Number: 102,
|
||||||
Title: "feat(102)",
|
Title: "feat(102)",
|
||||||
Branch: "feature-102",
|
Branch: "feature-102",
|
||||||
HeadSHA: "6344d9623e3b",
|
HeadSHA: "6344d9623e3b",
|
||||||
Author: "testName",
|
Author: "testName",
|
||||||
TargetBranch: "master",
|
|
||||||
}, *pullRequests[0])
|
}, *pullRequests[0])
|
||||||
|
|
||||||
regexp = `[\d{2}`
|
regexp = `[\d{2}`
|
||||||
@@ -472,49 +454,4 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
regexp = `feature-2[\d]{2}`
|
|
||||||
targetRegexp := `branch.*`
|
|
||||||
pullRequests, err = ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
|
||||||
{
|
|
||||||
BranchMatch: ®exp,
|
|
||||||
TargetBranchMatch: &targetRegexp,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Len(t, pullRequests, 1)
|
|
||||||
assert.Equal(t, PullRequest{
|
|
||||||
Number: 200,
|
|
||||||
Title: "feat(200)",
|
|
||||||
Branch: "feature-200",
|
|
||||||
HeadSHA: "4cf807e67a6d",
|
|
||||||
Author: "testName",
|
|
||||||
TargetBranch: "branch-200",
|
|
||||||
}, *pullRequests[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBitbucketCloudListReturnsRepositoryNotFoundError(t *testing.T) {
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
server := httptest.NewServer(mux)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
path := "/repositories/nonexistent/nonexistent/pullrequests/"
|
|
||||||
|
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
// Return 404 status to simulate repository not found
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
svc, err := NewBitbucketCloudServiceNoAuth(server.URL, "nonexistent", "nonexistent")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
prs, err := svc.List(t.Context())
|
|
||||||
|
|
||||||
// Should return empty pull requests list
|
|
||||||
assert.Empty(t, prs)
|
|
||||||
|
|
||||||
// Should return RepositoryNotFoundError
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
|
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BitbucketService struct {
|
type BitbucketService struct {
|
||||||
@@ -49,10 +49,15 @@ func NewBitbucketServiceNoAuth(ctx context.Context, url, projectKey, repositoryS
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newBitbucketService(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
func newBitbucketService(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
||||||
bbClient := services.SetupBitbucketClient(ctx, bitbucketConfig, scmRootCAPath, insecure, caCerts)
|
bitbucketConfig.BasePath = utils.NormalizeBitbucketBasePath(bitbucketConfig.BasePath)
|
||||||
|
tlsConfig := utils.GetTlsConfig(scmRootCAPath, insecure, caCerts)
|
||||||
|
bitbucketConfig.HTTPClient = &http.Client{Transport: &http.Transport{
|
||||||
|
TLSClientConfig: tlsConfig,
|
||||||
|
}}
|
||||||
|
bitbucketClient := bitbucketv1.NewAPIClient(ctx, bitbucketConfig)
|
||||||
|
|
||||||
return &BitbucketService{
|
return &BitbucketService{
|
||||||
client: bbClient,
|
client: bitbucketClient,
|
||||||
projectKey: projectKey,
|
projectKey: projectKey,
|
||||||
repositorySlug: repositorySlug,
|
repositorySlug: repositorySlug,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -67,11 +72,6 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
|
|||||||
for {
|
for {
|
||||||
response, err := b.client.DefaultApi.GetPullRequestsPage(b.projectKey, b.repositorySlug, paged)
|
response, err := b.client.DefaultApi.GetPullRequestsPage(b.projectKey, b.repositorySlug, paged)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if response != nil && response.Response != nil && response.StatusCode == http.StatusNotFound {
|
|
||||||
// return a custom error indicating that the repository is not found,
|
|
||||||
// but also return the empty result since the decision to continue or not in this case is made by the caller
|
|
||||||
return pullRequests, NewRepositoryNotFoundError(err)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.projectKey, b.repositorySlug, err)
|
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.projectKey, b.repositorySlug, err)
|
||||||
}
|
}
|
||||||
pulls, err := bitbucketv1.GetPullRequestsResponse(response)
|
pulls, err := bitbucketv1.GetPullRequestsResponse(response)
|
||||||
@@ -82,7 +82,7 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
|
|||||||
|
|
||||||
for _, pull := range pulls {
|
for _, pull := range pulls {
|
||||||
pullRequests = append(pullRequests, &PullRequest{
|
pullRequests = append(pullRequests, &PullRequest{
|
||||||
Number: int64(pull.ID),
|
Number: pull.ID,
|
||||||
Title: pull.Title,
|
Title: pull.Title,
|
||||||
Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main
|
Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main
|
||||||
TargetBranch: pull.ToRef.DisplayID,
|
TargetBranch: pull.ToRef.DisplayID,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func TestListPullRequestNoAuth(t *testing.T) {
|
|||||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, int64(101), pullRequests[0].Number)
|
assert.Equal(t, 101, pullRequests[0].Number)
|
||||||
assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title)
|
assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title)
|
||||||
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
||||||
assert.Equal(t, "master", pullRequests[0].TargetBranch)
|
assert.Equal(t, "master", pullRequests[0].TargetBranch)
|
||||||
@@ -211,7 +211,7 @@ func TestListPullRequestBasicAuth(t *testing.T) {
|
|||||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, int64(101), pullRequests[0].Number)
|
assert.Equal(t, 101, pullRequests[0].Number)
|
||||||
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
||||||
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ func TestListPullRequestBearerAuth(t *testing.T) {
|
|||||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, pullRequests, 1)
|
assert.Len(t, pullRequests, 1)
|
||||||
assert.Equal(t, int64(101), pullRequests[0].Number)
|
assert.Equal(t, 101, pullRequests[0].Number)
|
||||||
assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title)
|
assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title)
|
||||||
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
||||||
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||||
@@ -510,29 +510,3 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBitbucketServerListReturnsRepositoryNotFoundError(t *testing.T) {
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
server := httptest.NewServer(mux)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
path := "/rest/api/1.0/projects/nonexistent/repos/nonexistent/pull-requests?limit=100"
|
|
||||||
|
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
// Return 404 status to simulate repository not found
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
svc, err := NewBitbucketServiceNoAuth(t.Context(), server.URL, "nonexistent", "nonexistent", "", false, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
prs, err := svc.List(t.Context())
|
|
||||||
|
|
||||||
// Should return empty pull requests list
|
|
||||||
assert.Empty(t, prs)
|
|
||||||
|
|
||||||
// Should return RepositoryNotFoundError
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package pull_request
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// RepositoryNotFoundError represents an error when a repository is not found by a pull request provider
|
|
||||||
type RepositoryNotFoundError struct {
|
|
||||||
causingError error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *RepositoryNotFoundError) Error() string {
|
|
||||||
return e.causingError.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRepositoryNotFoundError creates a new repository not found error
|
|
||||||
func NewRepositoryNotFoundError(err error) error {
|
|
||||||
return &RepositoryNotFoundError{causingError: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRepositoryNotFoundError checks if the given error is a repository not found error
|
|
||||||
func IsRepositoryNotFoundError(err error) bool {
|
|
||||||
var repoErr *RepositoryNotFoundError
|
|
||||||
return errors.As(err, &repoErr)
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package pull_request
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRepositoryNotFoundError(t *testing.T) {
|
|
||||||
t.Run("NewRepositoryNotFoundError creates correct error type", func(t *testing.T) {
|
|
||||||
originalErr := errors.New("repository does not exist")
|
|
||||||
repoNotFoundErr := NewRepositoryNotFoundError(originalErr)
|
|
||||||
|
|
||||||
require.Error(t, repoNotFoundErr)
|
|
||||||
assert.Equal(t, "repository does not exist", repoNotFoundErr.Error())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IsRepositoryNotFoundError identifies RepositoryNotFoundError", func(t *testing.T) {
|
|
||||||
originalErr := errors.New("repository does not exist")
|
|
||||||
repoNotFoundErr := NewRepositoryNotFoundError(originalErr)
|
|
||||||
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(repoNotFoundErr))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IsRepositoryNotFoundError returns false for regular errors", func(t *testing.T) {
|
|
||||||
regularErr := errors.New("some other error")
|
|
||||||
|
|
||||||
assert.False(t, IsRepositoryNotFoundError(regularErr))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IsRepositoryNotFoundError returns false for nil error", func(t *testing.T) {
|
|
||||||
assert.False(t, IsRepositoryNotFoundError(nil))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("IsRepositoryNotFoundError works with wrapped errors", func(t *testing.T) {
|
|
||||||
originalErr := errors.New("repository does not exist")
|
|
||||||
repoNotFoundErr := NewRepositoryNotFoundError(originalErr)
|
|
||||||
wrappedErr := errors.New("wrapped: " + repoNotFoundErr.Error())
|
|
||||||
|
|
||||||
// Direct RepositoryNotFoundError should be identified
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(repoNotFoundErr))
|
|
||||||
|
|
||||||
// Wrapped string error should not be identified (this is expected behavior)
|
|
||||||
assert.False(t, IsRepositoryNotFoundError(wrappedErr))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -14,12 +14,11 @@ type GiteaService struct {
|
|||||||
client *gitea.Client
|
client *gitea.Client
|
||||||
owner string
|
owner string
|
||||||
repo string
|
repo string
|
||||||
labels []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ PullRequestService = (*GiteaService)(nil)
|
var _ PullRequestService = (*GiteaService)(nil)
|
||||||
|
|
||||||
func NewGiteaService(token, url, owner, repo string, labels []string, insecure bool) (PullRequestService, error) {
|
func NewGiteaService(token, url, owner, repo string, insecure bool) (PullRequestService, error) {
|
||||||
if token == "" {
|
if token == "" {
|
||||||
token = os.Getenv("GITEA_TOKEN")
|
token = os.Getenv("GITEA_TOKEN")
|
||||||
}
|
}
|
||||||
@@ -43,7 +42,6 @@ func NewGiteaService(token, url, owner, repo string, labels []string, insecure b
|
|||||||
client: client,
|
client: client,
|
||||||
owner: owner,
|
owner: owner,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
labels: labels,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,23 +50,14 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
State: gitea.StateOpen,
|
State: gitea.StateOpen,
|
||||||
}
|
}
|
||||||
g.client.SetContext(ctx)
|
g.client.SetContext(ctx)
|
||||||
list := []*PullRequest{}
|
prs, _, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
|
||||||
prs, resp, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
|
||||||
// return a custom error indicating that the repository is not found,
|
|
||||||
// but also returning the empty result since the decision to continue or not in this case is made by the caller
|
|
||||||
return list, NewRepositoryNotFoundError(err)
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
list := []*PullRequest{}
|
||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
if !giteaContainLabels(g.labels, pr.Labels) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
list = append(list, &PullRequest{
|
list = append(list, &PullRequest{
|
||||||
Number: int64(pr.Index),
|
Number: int(pr.Index),
|
||||||
Title: pr.Title,
|
Title: pr.Title,
|
||||||
Branch: pr.Head.Ref,
|
Branch: pr.Head.Ref,
|
||||||
TargetBranch: pr.Base.Ref,
|
TargetBranch: pr.Base.Ref,
|
||||||
@@ -80,21 +69,6 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// containLabels returns true if gotLabels contains expectedLabels
|
|
||||||
func giteaContainLabels(expectedLabels []string, gotLabels []*gitea.Label) bool {
|
|
||||||
gotLabelNamesMap := make(map[string]bool)
|
|
||||||
for i := 0; i < len(gotLabels); i++ {
|
|
||||||
gotLabelNamesMap[gotLabels[i].Name] = true
|
|
||||||
}
|
|
||||||
for _, expected := range expectedLabels {
|
|
||||||
v, ok := gotLabelNamesMap[expected]
|
|
||||||
if !v || !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the Gitea pull request label names.
|
// Get the Gitea pull request label names.
|
||||||
func getGiteaPRLabelNames(giteaLabels []*gitea.Label) []string {
|
func getGiteaPRLabelNames(giteaLabels []*gitea.Label) []string {
|
||||||
var labelNames []string
|
var labelNames []string
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
|||||||
},
|
},
|
||||||
"title": "add an empty file",
|
"title": "add an empty file",
|
||||||
"body": "",
|
"body": "",
|
||||||
"labels": [{"id": 1, "name": "label1", "color": "00aabb", "description": "foo", "url": ""}],
|
"labels": [],
|
||||||
"milestone": null,
|
"milestone": null,
|
||||||
"assignee": null,
|
"assignee": null,
|
||||||
"assignees": null,
|
"assignees": null,
|
||||||
@@ -246,64 +246,16 @@ func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGiteaContainLabels(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Name string
|
|
||||||
Labels []string
|
|
||||||
PullLabels []*gitea.Label
|
|
||||||
Expect bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "Match labels",
|
|
||||||
Labels: []string{"label1", "label2"},
|
|
||||||
PullLabels: []*gitea.Label{
|
|
||||||
{Name: "label1"},
|
|
||||||
{Name: "label2"},
|
|
||||||
{Name: "label3"},
|
|
||||||
},
|
|
||||||
Expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "Not match labels",
|
|
||||||
Labels: []string{"label1", "label4"},
|
|
||||||
PullLabels: []*gitea.Label{
|
|
||||||
{Name: "label1"},
|
|
||||||
{Name: "label2"},
|
|
||||||
{Name: "label3"},
|
|
||||||
},
|
|
||||||
Expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "No specify",
|
|
||||||
Labels: []string{},
|
|
||||||
PullLabels: []*gitea.Label{
|
|
||||||
{Name: "label1"},
|
|
||||||
{Name: "label2"},
|
|
||||||
{Name: "label3"},
|
|
||||||
},
|
|
||||||
Expect: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.Name, func(t *testing.T) {
|
|
||||||
if got := giteaContainLabels(c.Labels, c.PullLabels); got != c.Expect {
|
|
||||||
t.Errorf("expect: %v, got: %v", c.Expect, got)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGiteaList(t *testing.T) {
|
func TestGiteaList(t *testing.T) {
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
giteaMockHandler(t)(w, r)
|
giteaMockHandler(t)(w, r)
|
||||||
}))
|
}))
|
||||||
host, err := NewGiteaService("", ts.URL, "test-argocd", "pr-test", []string{"label1"}, false)
|
host, err := NewGiteaService("", ts.URL, "test-argocd", "pr-test", false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prs, err := host.List(t.Context())
|
prs, err := host.List(t.Context())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, prs, 1)
|
assert.Len(t, prs, 1)
|
||||||
assert.Equal(t, int64(1), prs[0].Number)
|
assert.Equal(t, 1, prs[0].Number)
|
||||||
assert.Equal(t, "add an empty file", prs[0].Title)
|
assert.Equal(t, "add an empty file", prs[0].Title)
|
||||||
assert.Equal(t, "test", prs[0].Branch)
|
assert.Equal(t, "test", prs[0].Branch)
|
||||||
assert.Equal(t, "main", prs[0].TargetBranch)
|
assert.Equal(t, "main", prs[0].TargetBranch)
|
||||||
@@ -339,35 +291,3 @@ func TestGetGiteaPRLabelNames(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGiteaListReturnsRepositoryNotFoundError(t *testing.T) {
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
server := httptest.NewServer(mux)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
// Handle version endpoint that Gitea client calls first
|
|
||||||
mux.HandleFunc("/api/v1/version", func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
_, _ = w.Write([]byte(`{"version":"1.17.0+dev-452-g1f0541780"}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
path := "/api/v1/repos/nonexistent/nonexistent/pulls?limit=0&page=1&state=open"
|
|
||||||
|
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
// Return 404 status to simulate repository not found
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
svc, err := NewGiteaService("", server.URL, "nonexistent", "nonexistent", []string{}, false)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
prs, err := svc.List(t.Context())
|
|
||||||
|
|
||||||
// Should return empty pull requests list
|
|
||||||
assert.Empty(t, prs)
|
|
||||||
|
|
||||||
// Should return RepositoryNotFoundError
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v66/github"
|
||||||
|
|
||||||
appsetutils "github.com/argoproj/argo-cd/v3/applicationset/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GithubService struct {
|
type GithubService struct {
|
||||||
@@ -20,28 +18,18 @@ type GithubService struct {
|
|||||||
|
|
||||||
var _ PullRequestService = (*GithubService)(nil)
|
var _ PullRequestService = (*GithubService)(nil)
|
||||||
|
|
||||||
func NewGithubService(token, url, owner, repo string, labels []string, optionalHTTPClient ...*http.Client) (PullRequestService, error) {
|
func NewGithubService(token, url, owner, repo string, labels []string) (PullRequestService, error) {
|
||||||
// Undocumented environment variable to set a default token, to be used in testing to dodge anonymous rate limits.
|
// Undocumented environment variable to set a default token, to be used in testing to dodge anonymous rate limits.
|
||||||
if token == "" {
|
if token == "" {
|
||||||
token = os.Getenv("GITHUB_TOKEN")
|
token = os.Getenv("GITHUB_TOKEN")
|
||||||
}
|
}
|
||||||
|
httpClient := &http.Client{}
|
||||||
var client *github.Client
|
var client *github.Client
|
||||||
httpClient := appsetutils.GetOptionalHTTPClient(optionalHTTPClient...)
|
|
||||||
|
|
||||||
if url == "" {
|
if url == "" {
|
||||||
if token == "" {
|
client = github.NewClient(httpClient).WithAuthToken(token)
|
||||||
client = github.NewClient(httpClient)
|
|
||||||
} else {
|
|
||||||
client = github.NewClient(httpClient).WithAuthToken(token)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
if token == "" {
|
client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url)
|
||||||
client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url)
|
|
||||||
} else {
|
|
||||||
client, err = github.NewClient(httpClient).WithAuthToken(token).WithEnterpriseURLs(url, url)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -64,11 +52,6 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
for {
|
for {
|
||||||
pulls, resp, err := g.client.PullRequests.List(ctx, g.owner, g.repo, opts)
|
pulls, resp, err := g.client.PullRequests.List(ctx, g.owner, g.repo, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
|
||||||
// return a custom error indicating that the repository is not found,
|
|
||||||
// but also returning the empty result since the decision to continue or not in this case is made by the caller
|
|
||||||
return pullRequests, NewRepositoryNotFoundError(err)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", g.owner, g.repo, err)
|
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", g.owner, g.repo, err)
|
||||||
}
|
}
|
||||||
for _, pull := range pulls {
|
for _, pull := range pulls {
|
||||||
@@ -76,7 +59,7 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pullRequests = append(pullRequests, &PullRequest{
|
pullRequests = append(pullRequests, &PullRequest{
|
||||||
Number: int64(*pull.Number),
|
Number: *pull.Number,
|
||||||
Title: *pull.Title,
|
Title: *pull.Title,
|
||||||
Branch: *pull.Head.Ref,
|
Branch: *pull.Head.Ref,
|
||||||
TargetBranch: *pull.Base.Ref,
|
TargetBranch: *pull.Base.Ref,
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
package pull_request
|
package pull_request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||||
"github.com/argoproj/argo-cd/v3/applicationset/services/internal/github_app"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/internal/github_app"
|
||||||
appsetutils "github.com/argoproj/argo-cd/v3/applicationset/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewGithubAppService(ctx context.Context, g github_app_auth.Authentication, url, owner, repo string, labels []string, optionalHTTPClient ...*http.Client) (PullRequestService, error) {
|
func NewGithubAppService(g github_app_auth.Authentication, url, owner, repo string, labels []string) (PullRequestService, error) {
|
||||||
httpClient := appsetutils.GetOptionalHTTPClient(optionalHTTPClient...)
|
client, err := github_app.Client(g, url)
|
||||||
client, err := github_app.Client(ctx, g, url, owner, httpClient)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package pull_request
|
package pull_request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-github/v69/github"
|
"github.com/google/go-github/v66/github"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -89,29 +86,3 @@ func TestGetGitHubPRLabelNames(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGitHubListReturnsRepositoryNotFoundError(t *testing.T) {
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
server := httptest.NewServer(mux)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
path := "/repos/nonexistent/nonexistent/pulls"
|
|
||||||
|
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
// Return 404 status to simulate repository not found
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
svc, err := NewGithubService("", server.URL, "nonexistent", "nonexistent", []string{}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
prs, err := svc.List(t.Context())
|
|
||||||
|
|
||||||
// Should return empty pull requests list
|
|
||||||
assert.Empty(t, prs)
|
|
||||||
|
|
||||||
// Should return RepositoryNotFoundError
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -61,15 +61,11 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
var labelsList gitlab.LabelOptions = g.labels
|
var labelsList gitlab.LabelOptions = g.labels
|
||||||
labels = &labelsList
|
labels = &labelsList
|
||||||
}
|
}
|
||||||
|
opts := &gitlab.ListProjectMergeRequestsOptions{
|
||||||
snippetsListOptions := gitlab.ExploreSnippetsOptions{
|
|
||||||
ListOptions: gitlab.ListOptions{
|
ListOptions: gitlab.ListOptions{
|
||||||
PerPage: 100,
|
PerPage: 100,
|
||||||
},
|
},
|
||||||
}
|
Labels: labels,
|
||||||
opts := &gitlab.ListProjectMergeRequestsOptions{
|
|
||||||
ListOptions: snippetsListOptions.ListOptions,
|
|
||||||
Labels: labels,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.pullRequestState != "" {
|
if g.pullRequestState != "" {
|
||||||
@@ -80,11 +76,6 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
for {
|
for {
|
||||||
mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts, gitlab.WithContext(ctx))
|
mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts, gitlab.WithContext(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp != nil && resp.StatusCode == http.StatusNotFound {
|
|
||||||
// return a custom error indicating that the repository is not found,
|
|
||||||
// but also returning the empty result since the decision to continue or not in this case is made by the caller
|
|
||||||
return pullRequests, NewRepositoryNotFoundError(err)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("error listing merge requests for project '%s': %w", g.project, err)
|
return nil, fmt.Errorf("error listing merge requests for project '%s': %w", g.project, err)
|
||||||
}
|
}
|
||||||
for _, mr := range mrs {
|
for _, mr := range mrs {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func TestList(t *testing.T) {
|
|||||||
prs, err := svc.List(t.Context())
|
prs, err := svc.List(t.Context())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, prs, 1)
|
assert.Len(t, prs, 1)
|
||||||
assert.Equal(t, int64(15442), prs[0].Number)
|
assert.Equal(t, 15442, prs[0].Number)
|
||||||
assert.Equal(t, "Draft: Use structured logging for DB load balancer", prs[0].Title)
|
assert.Equal(t, "Draft: Use structured logging for DB load balancer", prs[0].Title)
|
||||||
assert.Equal(t, "use-structured-logging-for-db-load-balancer", prs[0].Branch)
|
assert.Equal(t, "use-structured-logging-for-db-load-balancer", prs[0].Branch)
|
||||||
assert.Equal(t, "master", prs[0].TargetBranch)
|
assert.Equal(t, "master", prs[0].TargetBranch)
|
||||||
@@ -191,29 +191,3 @@ func TestListWithStateTLS(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGitLabListReturnsRepositoryNotFoundError(t *testing.T) {
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
server := httptest.NewServer(mux)
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
path := "/api/v4/projects/nonexistent/merge_requests"
|
|
||||||
|
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, _ *http.Request) {
|
|
||||||
// Return 404 status to simulate repository not found
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
_, _ = w.Write([]byte(`{"message": "404 Project Not Found"}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
svc, err := NewGitLabService("", server.URL, "nonexistent", []string{}, "", "", false, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
prs, err := svc.List(t.Context())
|
|
||||||
|
|
||||||
// Should return empty pull requests list
|
|
||||||
assert.Empty(t, prs)
|
|
||||||
|
|
||||||
// Should return RepositoryNotFoundError
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import (
|
|||||||
|
|
||||||
type PullRequest struct {
|
type PullRequest struct {
|
||||||
// Number is a number that will be the ID of the pull request.
|
// Number is a number that will be the ID of the pull request.
|
||||||
// Gitlab uses int64 for the pull request number.
|
Number int
|
||||||
Number int64
|
|
||||||
// Title of the pull request.
|
// Title of the pull request.
|
||||||
Title string
|
Title string
|
||||||
// Branch is the name of the branch from which the pull request originated.
|
// Branch is the name of the branch from which the pull request originated.
|
||||||
@@ -31,5 +30,4 @@ type PullRequestService interface {
|
|||||||
type Filter struct {
|
type Filter struct {
|
||||||
BranchMatch *regexp.Regexp
|
BranchMatch *regexp.Regexp
|
||||||
TargetBranchMatch *regexp.Regexp
|
TargetBranchMatch *regexp.Regexp
|
||||||
TitleMatch *regexp.Regexp
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user