mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-16 21:38:48 +01:00
Compare commits
61 Commits
release-3.
...
v3.1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96797ba846 | ||
|
|
b46a57ab82 | ||
|
|
2b3df7f5a8 | ||
|
|
4ef56634b4 | ||
|
|
cb9574597e | ||
|
|
468870f65d | ||
|
|
cfeed49105 | ||
|
|
c21141a51f | ||
|
|
0415c60af9 | ||
|
|
9a3235ef92 | ||
|
|
3320f1ed7a | ||
|
|
20dd73af34 | ||
|
|
206d57b0de | ||
|
|
c1467b81bc | ||
|
|
791b036d98 | ||
|
|
60c62a944b | ||
|
|
fe6efec8f4 | ||
|
|
6de4f7739b | ||
|
|
ed9149beea | ||
|
|
20447f7f57 | ||
|
|
7982a74600 | ||
|
|
b3ad040b2c | ||
|
|
30d8ce66e2 | ||
|
|
fa342d153e | ||
|
|
c140eb27f8 | ||
|
|
70dde2c27b | ||
|
|
eb72a0bd3b | ||
|
|
fdd099181c | ||
|
|
a0f065316b | ||
|
|
b22566d001 | ||
|
|
03c4ad854c | ||
|
|
a9a0c7bf57 | ||
|
|
1c1c17663e | ||
|
|
61d2a05fd0 | ||
|
|
64e94af771 | ||
|
|
269e213889 | ||
|
|
26c63b9283 | ||
|
|
1ad527f094 | ||
|
|
b7d39b57fc | ||
|
|
9f18ff1fcd | ||
|
|
66d06c0b23 | ||
|
|
007a5aea6e | ||
|
|
36cc2d1b86 | ||
|
|
9db5e25e03 | ||
|
|
8eaccb7ea0 | ||
|
|
fed347da29 | ||
|
|
1cbd28cb30 | ||
|
|
6fe5ec794a | ||
|
|
6142c5bf56 | ||
|
|
563d45b8db | ||
|
|
37f2793e95 | ||
|
|
7224a15502 | ||
|
|
dd675feb33 | ||
|
|
c215dbf202 | ||
|
|
75f7016d89 | ||
|
|
4a7e581080 | ||
|
|
320f46f06b | ||
|
|
d69639a073 | ||
|
|
b75f532a91 | ||
|
|
200dc1d499 | ||
|
|
3f1d9bf5a2 |
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,7 +2,7 @@
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ['bug', 'triage/pending']
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
@@ -10,9 +10,9 @@ assignees: ''
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] 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 pasted the output of `argocd version`.
|
||||
* [ ] 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 pasted the output of `argocd version`.
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
name: Enhancement proposal
|
||||
about: Propose an enhancement for this project
|
||||
title: ''
|
||||
labels: ['enhancement', 'triage/pending']
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
about: This is a request for adding a new tool for setting up a dev environment.
|
||||
title: ''
|
||||
labels: ['component:dev-env', 'triage/pending']
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] 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 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
|
||||
- [ ] I have given a motivation why this should be added
|
||||
* [ ] 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 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
|
||||
* [ ] I have given a motivation why this should be added
|
||||
|
||||
### The proposer
|
||||
|
||||
@@ -24,7 +24,7 @@ Checklist:
|
||||
|
||||
### 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)
|
||||
|
||||
|
||||
85
.github/ISSUE_TEMPLATE/release.md
vendored
85
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -9,79 +9,18 @@ assignees: ''
|
||||
Target RC1 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)
|
||||
- [ ] 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)
|
||||
- [ ] 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)
|
||||
- [ ] 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 RC1 release
|
||||
- [ ] Confirm that tweet and blog post are ready
|
||||
- [ ] 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)
|
||||
- [ ] After creating the RC, open a documentation PR for the next minor version using [this](../../docs/operator-manual/templates/minor_version_upgrade.md) template.
|
||||
|
||||
## 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!
|
||||
```
|
||||
- [ ] Create new release branch
|
||||
- [ ] Add the release branch to ReadTheDocs
|
||||
- [ ] Confirm that tweet and blog post are ready
|
||||
- [ ] Trigger the release
|
||||
- [ ] After the release is finished, publish tweet and blog post
|
||||
- [ ] Post in #argo-cd and #argo-announcements with lots of emojis announcing the release and requesting help testing
|
||||
- [ ] Monitor support channels for issues, cherry-picking bugfixes and docs fixes as appropriate (or delegate this task to an Approver and coordinate timing)
|
||||
- [ ] 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)
|
||||
- [ ] 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)
|
||||
- [ ] 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)
|
||||
- [ ] (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
|
||||
about: Propose adding security-related logs or tagging existing logs with security fields
|
||||
title: 'seclog: [Event Description]'
|
||||
labels: ['security', 'triage/pending']
|
||||
assignees: ''
|
||||
title: "seclog: [Event Description]"
|
||||
labels: security-log
|
||||
assignees: notfromstatefarm
|
||||
---
|
||||
|
||||
# Event to be logged
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
16
.github/configs/renovate-config.js
vendored
16
.github/configs/renovate-config.js
vendored
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
platform: 'github',
|
||||
gitAuthor: 'renovate[bot] <renovate[bot]@users.noreply.github.com>',
|
||||
autodiscover: false,
|
||||
allowPostUpgradeCommandTemplating: true,
|
||||
allowedPostUpgradeCommands: ["make mockgen"],
|
||||
binarySource: 'install',
|
||||
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"
|
||||
]
|
||||
}
|
||||
26
.github/pr-title-checker-config.json
vendored
26
.github/pr-title-checker-config.json
vendored
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"LABEL": {
|
||||
"name": "title needs formatting",
|
||||
"color": "EEEEEE"
|
||||
},
|
||||
"CHECKS": {
|
||||
"prefixes": ["[Bot] docs: "],
|
||||
"regexp": "^(refactor|feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
},
|
||||
"MESSAGES": {
|
||||
"success": "PR title is valid",
|
||||
"failure": "PR title is invalid",
|
||||
"notice": "PR Title needs to pass regex '^(refactor|feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
"LABEL": {
|
||||
"name": "title needs formatting",
|
||||
"color": "EEEEEE"
|
||||
},
|
||||
"CHECKS": {
|
||||
"prefixes": ["[Bot] docs: "],
|
||||
"regexp": "^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
},
|
||||
"MESSAGES": {
|
||||
"success": "PR title is valid",
|
||||
"failure": "PR title is invalid",
|
||||
"notice": "PR Title needs to pass regex '^(feat|fix|docs|test|ci|chore)!?(\\(.*\\))?!?:.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
* [ ] 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 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?
|
||||
|
||||
1
.github/workflows/README.md
vendored
1
.github/workflows/README.md
vendored
@@ -11,7 +11,6 @@
|
||||
| release.yaml | Build images, cli-binaries, provenances, and post actions |
|
||||
| scorecard.yaml | Generate scorecard for supply-chain security |
|
||||
| update-snyk.yaml | Scheduled snyk reports |
|
||||
| stale.yaml | Labels stale issues and PRs |
|
||||
|
||||
# Reusable workflows
|
||||
|
||||
|
||||
8
.github/workflows/bump-major-version.yaml
vendored
8
.github/workflows/bump-major-version.yaml
vendored
@@ -10,10 +10,10 @@ jobs:
|
||||
contents: write # for peter-evans/create-pull-request to create branch
|
||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||
name: Automatically update major version
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- 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
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
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 }}"
|
||||
|
||||
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-24.04
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||
with:
|
||||
app-id: ${{ secrets.CHERRYPICK_APP_ID }}
|
||||
private-key: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
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-24.04
|
||||
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 }}
|
||||
158
.github/workflows/ci-build.yaml
vendored
158
.github/workflows/ci-build.yaml
vendored
@@ -14,7 +14,7 @@ on:
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.26.0'
|
||||
GOLANG_VERSION: '1.24.6'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -25,14 +25,14 @@ permissions:
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
backend: ${{ steps.filter.outputs.backend_any_changed }}
|
||||
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
|
||||
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5
|
||||
id: filter
|
||||
with:
|
||||
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
||||
@@ -50,14 +50,14 @@ jobs:
|
||||
check-go:
|
||||
name: Ensure Go modules synchronicity
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
@@ -67,21 +67,22 @@ jobs:
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code -- .
|
||||
|
||||
build-go:
|
||||
name: Build & cache Go code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -97,27 +98,27 @@ jobs:
|
||||
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
||||
name: Lint Go code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||
with:
|
||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint/v2 versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||
version: v2.11.3
|
||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||
version: v2.1.6
|
||||
args: --verbose
|
||||
|
||||
test-go:
|
||||
name: Run unit tests for Go packages
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
@@ -128,11 +129,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -152,7 +153,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -173,7 +174,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
@@ -181,7 +182,7 @@ jobs:
|
||||
test-go-race:
|
||||
name: Run unit tests with -race for Go packages
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
@@ -192,11 +193,11 @@ jobs:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -216,7 +217,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -237,7 +238,7 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-race-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
@@ -245,21 +246,20 @@ jobs:
|
||||
codegen:
|
||||
name: Check changes to generated code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.docs == 'true'}}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
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
|
||||
run: |
|
||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||
@@ -271,14 +271,12 @@ jobs:
|
||||
# We need to vendor go modules for codegen yet
|
||||
go mod download
|
||||
go mod vendor -v
|
||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
- name: Install toolchain for codegen
|
||||
run: |
|
||||
make install-codegen-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/${{ github.event.repository.name }}
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
# We install kustomize in the dist directory
|
||||
- name: Add dist to PATH
|
||||
run: |
|
||||
@@ -289,33 +287,31 @@ jobs:
|
||||
export GOPATH=$(go env GOPATH)
|
||||
git checkout -- go.mod go.sum
|
||||
make codegen-local
|
||||
# generalizing repo name for forks: ${{ github.event.repository.name }}
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/${{ github.event.repository.name }}
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
- name: Check nothing has changed
|
||||
run: |
|
||||
set -xo pipefail
|
||||
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/${{ github.event.repository.name }}
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
|
||||
build-ui:
|
||||
name: Build, test & lint UI code
|
||||
# We run UI logic for backend changes so that we have a complete set of coverage documents to send to codecov.
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
# renovate: datasource=node-version packageName=node versioning=node
|
||||
node-version: '22.9.0'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -338,18 +334,18 @@ jobs:
|
||||
working-directory: ui/
|
||||
|
||||
shellcheck:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- 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
|
||||
shellcheck -e SC2086 -e SC2046 -e SC2068 -e SC2206 -e SC2048 -e SC2059 -e SC2154 -e SC2034 -e SC2016 -e SC2128 -e SC1091 -e SC2207 $(find . -type f -name '*.sh') | tee sc.log
|
||||
test ! -s sc.log
|
||||
|
||||
analyze:
|
||||
name: Process & analyze test artifacts
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- test-go
|
||||
- build-ui
|
||||
@@ -357,15 +353,14 @@ jobs:
|
||||
- test-e2e
|
||||
env:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
codecov_secret: ${{ secrets.CODECOV_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -373,12 +368,12 @@ jobs:
|
||||
run: |
|
||||
rm -rf ui/node_modules/argo-ui/node_modules
|
||||
- name: Get e2e code coverage
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: e2e-code-coverage
|
||||
- name: Get unit test code coverage
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
@@ -390,52 +385,52 @@ jobs:
|
||||
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
|
||||
- name: Upload code coverage information to codecov.io
|
||||
# Only run when the workflow is for upstream (PR target or push is in argoproj/argo-cd).
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
|
||||
with:
|
||||
files: test-results/full-coverage.out
|
||||
fail_ci_if_error: true
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Upload test results to Codecov
|
||||
# Codecov uploads test results to Codecov.io on upstream master branch.
|
||||
if: github.repository == 'argoproj/argo-cd' && github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
|
||||
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
|
||||
with:
|
||||
files: test-results/junit.xml
|
||||
file: test-results/junit.xml
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
report_type: test_results
|
||||
- name: Perform static code analysis using SonarCloud
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7.0.0
|
||||
uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf # v5.2.0
|
||||
if: env.sonar_secret != ''
|
||||
test-e2e:
|
||||
name: Run end-to-end tests
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ${{ github.repository == 'argoproj/argo-cd' && 'oracle-vm-16cpu-64gb-x86-64' || 'ubuntu-24.04' }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# 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.
|
||||
k3s:
|
||||
- version: v1.35.0
|
||||
latest: true
|
||||
- version: v1.34.2
|
||||
latest: false
|
||||
- version: v1.33.1
|
||||
latest: false
|
||||
latest: true
|
||||
- version: v1.32.1
|
||||
latest: false
|
||||
- version: v1.31.0
|
||||
latest: false
|
||||
- version: v1.30.4
|
||||
latest: false
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GOPATH: /home/runner/go
|
||||
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_IN_CI: 'true'
|
||||
ARGOCD_E2E_APISERVER_PORT: '8088'
|
||||
@@ -452,14 +447,11 @@ jobs:
|
||||
swap-storage: false
|
||||
tool-cache: false
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Set GOPATH
|
||||
run: |
|
||||
echo "GOPATH=$HOME/go" >> $GITHUB_ENV
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
run: |
|
||||
sudo pkill mono || true
|
||||
@@ -470,19 +462,19 @@ jobs:
|
||||
set -x
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
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 chown $(whoami) $HOME/.kube/config
|
||||
sudo chown runner $HOME/.kube/config
|
||||
sudo chmod go-r $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
- name: Add ~/go/bin to PATH
|
||||
run: |
|
||||
echo "$HOME/go/bin" >> $GITHUB_PATH
|
||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||
- name: Add /usr/local/bin to PATH
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
@@ -502,13 +494,13 @@ jobs:
|
||||
git config --global user.email "john.doe@example.com"
|
||||
- name: Pull Docker image required for tests
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.45.0
|
||||
docker pull ghcr.io/dexidp/dex:v2.43.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
|
||||
run: |
|
||||
mkdir -p dist
|
||||
chown $(whoami) dist
|
||||
chown runner dist
|
||||
- name: Run E2E server and wait for it being available
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
@@ -534,13 +526,13 @@ jobs:
|
||||
goreman run stop-all || echo "goreman trouble"
|
||||
sleep 30
|
||||
- name: Upload e2e coverage report
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: /tmp/coverage
|
||||
if: ${{ matrix.k3s.latest }}
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
@@ -558,7 +550,7 @@ jobs:
|
||||
needs:
|
||||
- test-e2e
|
||||
- changes
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- run: |
|
||||
result="${{ needs.test-e2e.result }}"
|
||||
|
||||
17
.github/workflows/codeql.yml
vendored
17
.github/workflows/codeql.yml
vendored
@@ -6,18 +6,7 @@ on:
|
||||
branches-ignore:
|
||||
- 'dependabot/**'
|
||||
- 'cherry-pick-*'
|
||||
|
||||
# Skip CodeQL for documentation-only changes
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
|
||||
pull_request:
|
||||
# Skip CodeQL for documentation-only changes
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- '**/*.md'
|
||||
- 'docs/assets/**'
|
||||
schedule:
|
||||
- cron: '0 19 * * 0'
|
||||
|
||||
@@ -37,14 +26,14 @@ jobs:
|
||||
if: github.repository == 'argoproj/argo-cd' || vars.enable_codeql
|
||||
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
|
||||
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
|
||||
24
.github/workflows/image-reuse.yaml
vendored
24
.github/workflows/image-reuse.yaml
vendored
@@ -51,32 +51,32 @@ jobs:
|
||||
contents: read
|
||||
packages: write # Used to push images to `ghcr.io` if used.
|
||||
id-token: write # Needed to create an OIDC token for keyless signing
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
image-digest: ${{ steps.image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ github.ref_type == 'tag'}}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
if: ${{ github.ref_type != 'tag'}}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
cache: false
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0
|
||||
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
|
||||
|
||||
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Setup tags for container image as a CSV type
|
||||
run: |
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
|
||||
- name: Login to Quay.io
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.quay_username }}
|
||||
@@ -111,7 +111,7 @@ jobs:
|
||||
if: ${{ inputs.quay_image_name && inputs.push }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.ghcr_username }}
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
||||
|
||||
- name: Login to dockerhub Container Registry
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
username: ${{ secrets.docker_username }}
|
||||
password: ${{ secrets.docker_password }}
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
|
||||
- name: Build and push container image
|
||||
id: image
|
||||
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 #v7.0.0
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platforms }}
|
||||
|
||||
64
.github/workflows/image.yaml
vendored
64
.github/workflows/image.yaml
vendored
@@ -19,49 +19,16 @@ jobs:
|
||||
set-vars:
|
||||
permissions:
|
||||
contents: read
|
||||
# Always run to calculate variables - other jobs check outputs
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
image-tag: ${{ steps.image.outputs.tag}}
|
||||
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:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
|
||||
- name: Set image tag and names
|
||||
run: |
|
||||
# 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
|
||||
- name: Set image tag for ghcr
|
||||
run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
id: image
|
||||
|
||||
- name: Determine image platforms to use
|
||||
@@ -81,12 +48,12 @@ jobs:
|
||||
contents: read
|
||||
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.
|
||||
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
|
||||
with:
|
||||
# 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
|
||||
go-version: 1.26.0
|
||||
go-version: 1.24.6
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: false
|
||||
|
||||
@@ -96,14 +63,14 @@ jobs:
|
||||
contents: read
|
||||
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.
|
||||
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
|
||||
with:
|
||||
quay_image_name: ${{ needs.set-vars.outputs.quay_image_name }}
|
||||
ghcr_image_name: ${{ needs.set-vars.outputs.ghcr_image_name }}
|
||||
quay_image_name: quay.io/argoproj/argocd:latest
|
||||
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)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.26.0
|
||||
go-version: 1.24.6
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: true
|
||||
secrets:
|
||||
@@ -114,17 +81,16 @@ jobs:
|
||||
|
||||
build-and-publish-provenance: # Push attestations to GHCR, latest image is polluting quay.io
|
||||
needs:
|
||||
- set-vars
|
||||
- build-and-publish
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
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)
|
||||
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
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ${{ needs.set-vars.outputs.ghcr_provenance_image }}
|
||||
image: ghcr.io/argoproj/argo-cd/argocd
|
||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||
registry-username: ${{ github.actor }}
|
||||
secrets:
|
||||
@@ -138,9 +104,9 @@ jobs:
|
||||
contents: write # for git to push upgrade commit if not already deployed
|
||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
|
||||
12
.github/workflows/init-release.yaml
vendored
12
.github/workflows/init-release.yaml
vendored
@@ -20,16 +20,10 @@ jobs:
|
||||
contents: write # for peter-evans/create-pull-request to create branch
|
||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||
name: Automatically generate version and manifests on ${{ inputs.TARGET_BRANCH }}
|
||||
runs-on: ubuntu-24.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' }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -70,7 +64,7 @@ jobs:
|
||||
git stash pop
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||
|
||||
6
.github/workflows/pr-title-check.yml
vendored
6
.github/workflows/pr-title-check.yml
vendored
@@ -1,9 +1,11 @@
|
||||
name: "Lint PR"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
# IMPORTANT: No checkout actions, scripts, or builds should be added to this workflow. Permissions should always be used
|
||||
# with extreme caution. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
permissions: {}
|
||||
|
||||
# PR updates can happen in quick succession leading to this
|
||||
@@ -19,7 +21,7 @@ jobs:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
name: Validate PR Title
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: thehanimo/pr-title-checker@7fbfe05602bdd86f926d3fb3bccb6f3aed43bc70 # v1.4.3
|
||||
with:
|
||||
|
||||
146
.github/workflows/release.yaml
vendored
146
.github/workflows/release.yaml
vendored
@@ -11,99 +11,38 @@ permissions: {}
|
||||
|
||||
env:
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.26.0' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
GOLANG_VERSION: '1.24.6' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
|
||||
jobs:
|
||||
argocd-image:
|
||||
needs: [setup-variables]
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
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
|
||||
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)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.26.0
|
||||
go-version: 1.24.6
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||
push: true
|
||||
secrets:
|
||||
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||
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-24.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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
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:
|
||||
needs: [setup-variables, argocd-image]
|
||||
needs: [argocd-image]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
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)
|
||||
# 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
|
||||
with:
|
||||
image: ${{ needs.setup-variables.outputs.provenance_image }}
|
||||
image: quay.io/argoproj/argocd
|
||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||
secrets:
|
||||
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||
@@ -111,20 +50,18 @@ jobs:
|
||||
|
||||
goreleaser:
|
||||
needs:
|
||||
- setup-variables
|
||||
- argocd-image
|
||||
- argocd-image-provenance
|
||||
permissions:
|
||||
contents: write # used for uploading assets
|
||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
GORELEASER_MAKE_LATEST: ${{ needs.setup-variables.outputs.is_latest_release }}
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
hashes: ${{ steps.hash.outputs.hashes }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -133,7 +70,7 @@ jobs:
|
||||
run: git fetch --force --tags
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
cache: false
|
||||
@@ -159,7 +96,7 @@ jobs:
|
||||
tool-cache: false
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
id: run-goreleaser
|
||||
with:
|
||||
version: latest
|
||||
@@ -168,8 +105,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
||||
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
|
||||
id: hash
|
||||
@@ -186,12 +121,12 @@ jobs:
|
||||
echo "hashes=$hashes" >> $GITHUB_OUTPUT
|
||||
|
||||
goreleaser-provenance:
|
||||
needs: [goreleaser, setup-variables]
|
||||
needs: [goreleaser]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment
|
||||
id-token: write # Needed for provenance signing and ID
|
||||
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
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
@@ -204,22 +139,21 @@ jobs:
|
||||
needs:
|
||||
- argocd-image
|
||||
- goreleaser
|
||||
- setup-variables
|
||||
permissions:
|
||||
contents: write # Needed for release uploads
|
||||
outputs:
|
||||
hashes: ${{ steps.sbom-hash.outputs.hashes }}
|
||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
||||
runs-on: ubuntu-24.04
|
||||
hashes: ${{ steps.sbom-hash.outputs.hashes}}
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
cache: false
|
||||
@@ -235,7 +169,7 @@ jobs:
|
||||
# managers (gomod, yarn, npm).
|
||||
PROJECT_FOLDERS: '.,./ui'
|
||||
# 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: |
|
||||
yarn install --cwd ./ui
|
||||
go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION
|
||||
@@ -264,7 +198,7 @@ jobs:
|
||||
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload SBOM
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
@@ -272,12 +206,12 @@ jobs:
|
||||
/tmp/sbom.tar.gz
|
||||
|
||||
sbom-provenance:
|
||||
needs: [generate-sbom, setup-variables]
|
||||
needs: [generate-sbom]
|
||||
permissions:
|
||||
actions: read # for detecting the Github Actions environment
|
||||
id-token: write # Needed for provenance signing and ID
|
||||
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
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
@@ -287,20 +221,17 @@ jobs:
|
||||
|
||||
post-release:
|
||||
needs:
|
||||
- setup-variables
|
||||
- argocd-image
|
||||
- goreleaser
|
||||
- generate-sbom
|
||||
permissions:
|
||||
contents: write # Needed to push commit to update stable tag
|
||||
pull-requests: write # Needed to create PR for VERSION update.
|
||||
if: github.repository == 'argoproj/argo-cd' || needs.setup-variables.outputs.allow_fork_release == 'true'
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
TAG_STABLE: ${{ needs.setup-variables.outputs.is_latest_release }}
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -311,6 +242,27 @@ jobs:
|
||||
git config --global user.email 'ci@argoproj.com'
|
||||
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
|
||||
run: |
|
||||
git tag -f stable ${{ github.ref_name }}
|
||||
@@ -344,7 +296,7 @@ jobs:
|
||||
if: ${{ env.UPDATE_VERSION == 'true' }}
|
||||
|
||||
- name: Create PR to update VERSION on master branch
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
commit-message: Bump version in master
|
||||
title: 'chore: Bump version in master'
|
||||
|
||||
32
.github/workflows/renovate.yaml
vendored
32
.github/workflows/renovate.yaml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Renovate
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
workflow_dispatch: {}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
runs-on: ubuntu-24.04
|
||||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 6.0.2
|
||||
|
||||
- name: Self-hosted Renovate
|
||||
uses: renovatebot/github-action@abd08c7549b2a864af5df4a2e369c43f035a6a9d #46.1.5
|
||||
with:
|
||||
configurationFile: .github/configs/renovate-config.js
|
||||
token: '${{ steps.get_token.outputs.token }}'
|
||||
env:
|
||||
LOG_LEVEL: 'debug'
|
||||
RENOVATE_REPOSITORIES: '${{ github.repository }}'
|
||||
8
.github/workflows/scorecard.yaml
vendored
8
.github/workflows/scorecard.yaml
vendored
@@ -17,7 +17,7 @@ permissions: read-all
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecards analysis
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
@@ -30,12 +30,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||
with:
|
||||
results_file: results.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
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
33
.github/workflows/stale.yaml
vendored
33
.github/workflows/stale.yaml
vendored
@@ -1,33 +0,0 @@
|
||||
name: "Label stale issues and PRs"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" #Runs midnight 12AM UTC
|
||||
|
||||
#Added Recommended permissions
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
stale-issue-message: >
|
||||
This issue has been marked as stale because it has had no activity for 90 days. Please comment if this is still relevant.
|
||||
|
||||
stale-pr-message: >
|
||||
This pull request has been marked as stale because it has had no activity for 90 days. Please comment if this is still relevant.
|
||||
|
||||
days-before-stale: 90
|
||||
days-before-close: -1 # Auto-close diabled
|
||||
|
||||
exempt-issue-labels: >
|
||||
bug, security, breaking/high, breaking/medium, breaking/low
|
||||
|
||||
# General configuration
|
||||
operations-per-run: 200
|
||||
remove-stale-when-updated: true #Remove stale label when issue/pr is updated
|
||||
4
.github/workflows/update-snyk.yaml
vendored
4
.github/workflows/update-snyk.yaml
vendored
@@ -14,10 +14,10 @@ jobs:
|
||||
pull-requests: write
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
name: Update Snyk report in the docs directory
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,7 +20,6 @@ node_modules/
|
||||
.kube/
|
||||
./test/cmp/*.sock
|
||||
.envrc.remote
|
||||
.mirrord/
|
||||
.*.swp
|
||||
rerunreport.txt
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ linters:
|
||||
- govet
|
||||
- importas
|
||||
- misspell
|
||||
- modernize
|
||||
- noctx
|
||||
- perfsprint
|
||||
- revive
|
||||
- staticcheck
|
||||
@@ -60,6 +58,7 @@ linters:
|
||||
- commentedOutCode
|
||||
- deferInLoop
|
||||
- exitAfterDefer
|
||||
- exposedSyncMutex
|
||||
- hugeParam
|
||||
- importShadow
|
||||
- paramTypeCombine # Leave disabled, there are too many failures to be worth fixing.
|
||||
@@ -122,13 +121,6 @@ linters:
|
||||
- pkg: github.com/argoproj/argo-cd/v3/util/io
|
||||
alias: utilio
|
||||
|
||||
modernize:
|
||||
disable:
|
||||
# Suggest replacing omitempty with omitzero for struct fields.
|
||||
- omitzero
|
||||
# Simplify code by using go1.26's new(expr). - generates lots of false positives.
|
||||
- newexpr
|
||||
|
||||
nolintlint:
|
||||
require-specific: true
|
||||
|
||||
|
||||
@@ -49,14 +49,13 @@ archives:
|
||||
- argocd-cli
|
||||
name_template: |-
|
||||
{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}
|
||||
formats: [binary]
|
||||
formats: [ binary ]
|
||||
|
||||
checksum:
|
||||
name_template: 'cli_checksums.txt'
|
||||
algorithm: sha256
|
||||
|
||||
release:
|
||||
make_latest: '{{ .Env.GORELEASER_MAKE_LATEST }}'
|
||||
prerelease: auto
|
||||
draft: false
|
||||
header: |
|
||||
@@ -66,14 +65,14 @@ release:
|
||||
|
||||
```shell
|
||||
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:
|
||||
|
||||
```shell
|
||||
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
|
||||
@@ -81,13 +80,13 @@ 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.
|
||||
|
||||
## 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/announcing-argo-cd-v3-1-f4389bc783c8)
|
||||
|
||||
## 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.
|
||||
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>
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
dir: '{{.InterfaceDir}}/mocks'
|
||||
structname: '{{.InterfaceName}}'
|
||||
filename: '{{.InterfaceName}}.go'
|
||||
include-auto-generated: true # Needed since mockery 3.6.1
|
||||
pkgname: mocks
|
||||
|
||||
template-data:
|
||||
unroll-variadic: true
|
||||
|
||||
packages:
|
||||
github.com/argoproj/argo-cd/v3/applicationset/generators:
|
||||
interfaces:
|
||||
@@ -9,15 +14,17 @@ packages:
|
||||
interfaces:
|
||||
Repos: {}
|
||||
github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider:
|
||||
config:
|
||||
dir: applicationset/services/scm_provider/aws_codecommit/mocks
|
||||
interfaces:
|
||||
AWSCodeCommitClient: {}
|
||||
AWSTaggingClient: {}
|
||||
AzureDevOpsClientFactory: {}
|
||||
github.com/argoproj/argo-cd/v3/applicationset/utils:
|
||||
interfaces:
|
||||
Renderer: {}
|
||||
github.com/argoproj/argo-cd/v3/commitserver/apiclient:
|
||||
interfaces:
|
||||
Clientset: {}
|
||||
CommitServiceClient: {}
|
||||
github.com/argoproj/argo-cd/v3/commitserver/commit:
|
||||
interfaces:
|
||||
@@ -25,16 +32,9 @@ packages:
|
||||
github.com/argoproj/argo-cd/v3/controller/cache:
|
||||
interfaces:
|
||||
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: {}
|
||||
@@ -44,9 +44,9 @@ packages:
|
||||
AppProjectInterface: {}
|
||||
github.com/argoproj/argo-cd/v3/reposerver/apiclient:
|
||||
interfaces:
|
||||
RepoServerServiceClient: {}
|
||||
RepoServerService_GenerateManifestWithFilesClient: {}
|
||||
github.com/argoproj/argo-cd/v3/server/broadcast:
|
||||
RepoServerServiceClient: {}
|
||||
github.com/argoproj/argo-cd/v3/server/application:
|
||||
interfaces:
|
||||
Broadcaster: {}
|
||||
github.com/argoproj/argo-cd/v3/server/extension:
|
||||
@@ -60,37 +60,26 @@ packages:
|
||||
github.com/argoproj/argo-cd/v3/util/db:
|
||||
interfaces:
|
||||
ArgoDB: {}
|
||||
RepoCredsDB: {}
|
||||
github.com/argoproj/argo-cd/v3/util/git:
|
||||
interfaces:
|
||||
Client: {}
|
||||
github.com/argoproj/argo-cd/v3/util/helm:
|
||||
interfaces:
|
||||
Client: {}
|
||||
github.com/argoproj/argo-cd/v3/util/oci:
|
||||
interfaces:
|
||||
Client: {}
|
||||
github.com/argoproj/argo-cd/v3/util/io:
|
||||
interfaces:
|
||||
TempPaths: {}
|
||||
github.com/argoproj/argo-cd/v3/util/notification/argocd:
|
||||
interfaces:
|
||||
Service: {}
|
||||
github.com/argoproj/argo-cd/v3/util/oci:
|
||||
interfaces:
|
||||
Client: {}
|
||||
github.com/argoproj/argo-cd/v3/util/workloadidentity:
|
||||
interfaces:
|
||||
TokenProvider: {}
|
||||
github.com/argoproj/argo-cd/gitops-engine/pkg/cache:
|
||||
interfaces:
|
||||
ClusterCache: {}
|
||||
github.com/argoproj/argo-cd/gitops-engine/pkg/diff:
|
||||
interfaces:
|
||||
ServerSideDryRunner: {}
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops/v7/git:
|
||||
config:
|
||||
dir: applicationset/services/scm_provider/azure_devops/git/mocks
|
||||
interfaces:
|
||||
Client: {}
|
||||
pkgname: mocks
|
||||
structname: '{{.InterfaceName}}'
|
||||
template-data:
|
||||
unroll-variadic: true
|
||||
|
||||
@@ -12,9 +12,3 @@
|
||||
/.github/** @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
|
||||
|
||||
# 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
|
||||
|
||||
27
Dockerfile
27
Dockerfile
@@ -1,10 +1,10 @@
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:25.10@sha256:4a9232cc47bf99defcc8860ef6222c99773330367fcecbf21ba2edb0b810a31e
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab
|
||||
####################################################################################################
|
||||
# Builder 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
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.26.0@sha256:fb612b7831d53a89cbc0aaa7855b69ad7b0caf603715860cf538df854d047b84 AS builder
|
||||
FROM docker.io/library/golang:1.24.6@sha256:2c89c41fb9efc3807029b59af69645867cfe978d2b877d475be0d72f6c6ce6f6 AS builder
|
||||
|
||||
WORKDIR /tmp
|
||||
|
||||
@@ -16,6 +16,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
unzip \
|
||||
fcgiwrap \
|
||||
git \
|
||||
git-lfs \
|
||||
make \
|
||||
wget \
|
||||
gcc \
|
||||
@@ -28,8 +29,7 @@ COPY hack/install.sh hack/tool-versions.sh ./
|
||||
COPY hack/installers installers
|
||||
|
||||
RUN ./install.sh helm && \
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize && \
|
||||
./install.sh git-lfs
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||
|
||||
####################################################################################################
|
||||
# Argo CD Base - used as the base for both the release and dev argocd images
|
||||
@@ -50,10 +50,10 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
||||
chmod g=u /home/argocd && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install --no-install-recommends -y \
|
||||
git tini ca-certificates gpg gpg-agent tzdata connect-proxy openssh-client && \
|
||||
apt-get install -y \
|
||||
git git-lfs tini gpg tzdata connect-proxy && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY hack/gpg-wrapper.sh \
|
||||
hack/git-verify-wrapper.sh \
|
||||
@@ -61,7 +61,6 @@ COPY hack/gpg-wrapper.sh \
|
||||
/usr/local/bin/
|
||||
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/git-lfs /usr/local/bin/git-lfs
|
||||
|
||||
# keep uid_entrypoint.sh for backward compatibility
|
||||
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
||||
@@ -80,19 +79,13 @@ RUN mkdir -p tls && \
|
||||
|
||||
ENV USER=argocd
|
||||
|
||||
# Disable gRPC service config lookups via DNS TXT records to prevent excessive
|
||||
# DNS queries for _grpc_config.<hostname> which can cause timeouts in dual-stack
|
||||
# environments. This can be overridden via argocd-cmd-params-cm ConfigMap.
|
||||
# See https://github.com/argoproj/argo-cd/issues/24991
|
||||
ENV GRPC_ENABLE_TXT_SERVICE_CONFIG=false
|
||||
|
||||
USER $ARGOCD_USER_ID
|
||||
WORKDIR /home/argocd
|
||||
|
||||
####################################################################################################
|
||||
# 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
|
||||
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
||||
@@ -110,13 +103,11 @@ 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
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26.0@sha256:fb612b7831d53a89cbc0aaa7855b69ad7b0caf603715860cf538df854d047b84 AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.24.6@sha256:2c89c41fb9efc3807029b59af69645867cfe978d2b877d475be0d72f6c6ce6f6 AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
COPY go.* ./
|
||||
RUN mkdir -p gitops-engine
|
||||
COPY gitops-engine/go.* ./gitops-engine
|
||||
RUN go mod download
|
||||
|
||||
# Perform the build
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/golang:1.26.0@sha256:fb612b7831d53a89cbc0aaa7855b69ad7b0caf603715860cf538df854d047b84
|
||||
FROM docker.io/library/golang:1.24.1@sha256:c5adecdb7b3f8c5ca3c88648a861882849cc8b02fed68ece31e25de88ad13418
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -11,6 +11,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
unzip \
|
||||
fcgiwrap \
|
||||
git \
|
||||
git-lfs \
|
||||
make \
|
||||
wget \
|
||||
gcc \
|
||||
@@ -27,8 +28,7 @@ COPY hack/install.sh hack/tool-versions.sh ./
|
||||
COPY hack/installers installers
|
||||
|
||||
RUN ./install.sh helm && \
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize && \
|
||||
./install.sh git-lfs
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||
|
||||
COPY hack/gpg-wrapper.sh \
|
||||
hack/git-verify-wrapper.sh \
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# Argo CD Maintainers
|
||||
|
||||
This document lists the maintainers of the Argo CD project.
|
||||
|
||||
## Maintainers
|
||||
|
||||
| Maintainer | GitHub ID | Project Roles | Affiliation |
|
||||
|---------------------------|---------------------------------------------------------|----------------------|-------------------------------------------------|
|
||||
| Zach Aller | [zachaller](https://github.com/zachaller) | Reviewer | [Intuit](https://www.github.com/intuit/) |
|
||||
| Leonardo Luz Almeida | [leoluz](https://github.com/leoluz) | Approver | [Intuit](https://www.github.com/intuit/) |
|
||||
| Chetan Banavikalmutt | [chetan-rns](https://github.com/chetan-rns) | Reviewer | [Red Hat](https://redhat.com/) |
|
||||
| Keith Chong | [keithchong](https://github.com/keithchong) | Approver | [Red Hat](https://redhat.com/) |
|
||||
| Alex Collins | [alexec](https://github.com/alexec) | Approver | [Intuit](https://www.github.com/intuit/) |
|
||||
| Michael Crenshaw | [crenshaw-dev](https://github.com/crenshaw-dev) | Lead | [Intuit](https://www.github.com/intuit/) |
|
||||
| Soumya Ghosh Dastidar | [gdsoumya](https://github.com/gdsoumya) | Approver | [Akuity](https://akuity.io/) |
|
||||
| Eugene Doudine | [dudinea](https://github.com/dudinea) | Reviewer | [Octopus Deploy](https://octopus.com/) |
|
||||
| Jann Fischer | [jannfis](https://github.com/jannfis) | Approver | [Red Hat](https://redhat.com/) |
|
||||
| Dan Garfield | [todaywasawesome](https://github.com/todaywasawesome) | Approver(docs) | [Octopus Deploy](https://octopus.com/) |
|
||||
| Alexandre Gaudreault | [agaudreault](https://github.com/agaudreault) | Approver | [Intuit](https://www.github.com/intuit/) |
|
||||
| Christian Hernandez | [christianh814](https://github.com/christianh814) | Reviewer(docs) | [Akuity](https://akuity.io/) |
|
||||
| Peter Jiang | [pjiang-dev](https://github.com/pjiang-dev) | Approver(docs) | [Intuit](https://www.intuit.com/) |
|
||||
| Andrii Korotkov | [andrii-korotkov](https://github.com/andrii-korotkov) | Reviewer | [Verkada](https://www.verkada.com/) |
|
||||
| Pasha Kostohrys | [pasha-codefresh](https://github.com/pasha-codefresh) | Approver | [Codefresh](https://www.github.com/codefresh/) |
|
||||
| Nitish Kumar | [nitishfy](https://github.com/nitishfy) | Approver(cli,docs) | [Akuity](https://akuity.io/) |
|
||||
| Justin Marquis | [34fathombelow](https://github.com/34fathombelow) | Approver(docs/ci) | [Akuity](https://akuity.io/) |
|
||||
| Alexander Matyushentsev | [alexmt](https://github.com/alexmt) | Lead | [Akuity](https://akuity.io/) |
|
||||
| Nicholas Morey | [morey-tech](https://github.com/morey-tech) | Reviewer(docs) | [Akuity](https://akuity.io/) |
|
||||
| Papapetrou Patroklos | [ppapapetrou76](https://github.com/ppapapetrou76) | Approver(docs,cli) | [Octopus Deploy](https://octopus.com/) |
|
||||
| Blake Pettersson | [blakepettersson](https://github.com/blakepettersson) | Approver | [Akuity](https://akuity.io/) |
|
||||
| Ishita Sequeira | [ishitasequeira](https://github.com/ishitasequeira) | Approver | [Red Hat](https://redhat.com/) |
|
||||
| Ashutosh Singh | [ashutosh16](https://github.com/ashutosh16) | Approver(docs) | [Intuit](https://www.github.com/intuit/) |
|
||||
| Linghao Su | [linghaoSu](https://github.com/linghaoSu) | Reviewer | [DaoCloud](https://daocloud.io) |
|
||||
| Jesse Suen | [jessesuen](https://github.com/jessesuen) | Approver | [Akuity](https://akuity.io/) |
|
||||
| Yuan Tang | [terrytangyuan](https://github.com/terrytangyuan) | Reviewer | [Red Hat](https://redhat.com/) |
|
||||
| William Tam | [wtam2018](https://github.com/wtam2018) | Reviewer | [Red Hat](https://redhat.com/) |
|
||||
| Ryan Umstead | [rumstead](https://github.com/rumstead) | Approver | [Black Rock](https://www.github.com/blackrock/) |
|
||||
| Regina Voloshin | [reggie-k](https://github.com/reggie-k) | Approver | [Octopus Deploy](https://octopus.com/) |
|
||||
| Hong Wang | [wanghong230](https://github.com/wanghong230) | Reviewer | [Akuity](https://akuity.io/) |
|
||||
| Jonathan West | [jgwest](https://github.com/jgwest) | Approver | [Red Hat](https://redhat.com/) |
|
||||
| Jaewoo Choi | [choejwoo](https://github.com/choejwoo) | Reviewer | [Hyundai-Autoever](https://www.hyundai-autoever.com/eng/) |
|
||||
| Alexy Mantha | [alexymantha](https://github.com/alexymantha) | Reviewer | GoTo |
|
||||
| Kanika Rana | [ranakan19](https://github.com/ranakan19) | Reviewer | [Red Hat](https://redhat.com/) |
|
||||
| Jonathan Winters | [jwinters01](https://github.com/jwinters01) | Reviewer | [Intuit](https://www.github.com/intuit/) |
|
||||
119
Makefile
119
Makefile
@@ -43,21 +43,10 @@ endif
|
||||
DOCKER_SRCDIR?=$(GOPATH)/src
|
||||
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
|
||||
|
||||
# pointing to python 3.12 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yaml
|
||||
MKDOCS_DOCKER_IMAGE?=python:3.12-alpine
|
||||
# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml
|
||||
MKDOCS_DOCKER_IMAGE?=python:3.7-alpine
|
||||
MKDOCS_RUN_ARGS?=
|
||||
|
||||
# Configuration for building argocd-test-tools image
|
||||
@@ -76,15 +65,15 @@ ARGOCD_E2E_REDIS_PORT?=6379
|
||||
ARGOCD_E2E_DEX_PORT?=5556
|
||||
ARGOCD_E2E_YARN_HOST?=localhost
|
||||
ARGOCD_E2E_DISABLE_AUTH?=
|
||||
ARGOCD_E2E_DIR?=/tmp/argo-e2e
|
||||
|
||||
ARGOCD_E2E_TEST_TIMEOUT?=90m
|
||||
ARGOCD_E2E_RERUN_FAILS?=5
|
||||
|
||||
ARGOCD_IN_CI?=false
|
||||
ARGOCD_TEST_E2E?=true
|
||||
ARGOCD_BIN_MODE?=true
|
||||
|
||||
ARGOCD_LINT_GOGC?=20
|
||||
|
||||
# Depending on where we are (legacy or non-legacy pwd), we need to use
|
||||
# different Docker volume mounts for our source tree
|
||||
LEGACY_PATH=$(GOPATH)/src/github.com/argoproj/argo-cd
|
||||
@@ -124,11 +113,11 @@ define run-in-test-server
|
||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||
-w ${DOCKER_WORKDIR} \
|
||||
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
||||
-p 4000:4000 \
|
||||
-p 5000:5000 \
|
||||
$(DOCKER_NETWORK_ARG)\
|
||||
$(PODMAN_ARGS) \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
@@ -144,12 +133,13 @@ define run-in-test-client
|
||||
-e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) \
|
||||
-e GITHUB_TOKEN \
|
||||
-e GOCACHE=/tmp/go-build-cache \
|
||||
-e ARGOCD_LINT_GOGC=$(ARGOCD_LINT_GOGC) \
|
||||
-v ${DOCKER_SRC_MOUNT} \
|
||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||
-w ${DOCKER_WORKDIR} \
|
||||
$(DOCKER_NETWORK_ARG)\
|
||||
$(PODMAN_ARGS) \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
@@ -197,40 +187,19 @@ endif
|
||||
|
||||
ifneq (${GIT_TAG},)
|
||||
IMAGE_TAG=${GIT_TAG}
|
||||
override LDFLAGS += -X ${PACKAGE}.gitTag=${GIT_TAG}
|
||||
LDFLAGS += -X ${PACKAGE}.gitTag=${GIT_TAG}
|
||||
else
|
||||
IMAGE_TAG?=latest
|
||||
endif
|
||||
|
||||
# defaults for building images and manifests
|
||||
ifeq (${DOCKER_PUSH},true)
|
||||
ifndef IMAGE_NAMESPACE
|
||||
$(error IMAGE_NAMESPACE must be set to push images (e.g. IMAGE_NAMESPACE=argoproj))
|
||||
endif
|
||||
endif
|
||||
|
||||
# Consruct prefix for docker image
|
||||
# Note: keeping same logic as in hacks/update_manifests.sh
|
||||
ifdef IMAGE_REGISTRY
|
||||
ifdef IMAGE_NAMESPACE
|
||||
IMAGE_PREFIX=${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/
|
||||
else
|
||||
$(error IMAGE_NAMESPACE must be set when IMAGE_REGISTRY is set (e.g. IMAGE_NAMESPACE=argoproj))
|
||||
endif
|
||||
else
|
||||
ifdef IMAGE_NAMESPACE
|
||||
# for backwards compatibility with the old way like IMAGE_NAMESPACE='quay.io/argoproj'
|
||||
IMAGE_PREFIX=${IMAGE_NAMESPACE}/
|
||||
else
|
||||
# Neither namespace nor registry given - apply the default values
|
||||
IMAGE_REGISTRY="quay.io"
|
||||
IMAGE_NAMESPACE="argoproj"
|
||||
IMAGE_PREFIX=${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef IMAGE_REPOSITORY
|
||||
IMAGE_REPOSITORY=argocd
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
@@ -281,12 +250,8 @@ clidocsgen:
|
||||
actionsdocsgen:
|
||||
hack/generate-actions-list.sh
|
||||
|
||||
.PHONY: resourceiconsgen
|
||||
resourceiconsgen:
|
||||
hack/generate-icons-typescript.sh
|
||||
|
||||
.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/
|
||||
|
||||
.PHONY: codegen-local-fast
|
||||
@@ -328,11 +293,12 @@ endif
|
||||
.PHONY: manifests-local
|
||||
manifests-local:
|
||||
./hack/update-manifests.sh
|
||||
|
||||
.PHONY: manifests
|
||||
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}')
|
||||
# consolidated binary for cli, util, server, repo-server, controller
|
||||
$(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
|
||||
.PHONY: argocd-all
|
||||
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
|
||||
@@ -353,7 +319,7 @@ controller:
|
||||
build-ui:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui .
|
||||
find ./ui/dist -type f -not -name gitkeep -delete
|
||||
$(DOCKER) run -u $(CONTAINER_UID):$(CONTAINER_GID) -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
$(DOCKER) run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -363,23 +329,23 @@ ifeq ($(DEV_IMAGE), true)
|
||||
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
|
||||
image: build-ui
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base .
|
||||
GOOS=linux GOARCH=$(TARGET_ARCH:linux/%=%) GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
CGO_ENABLED=${CGO_FLAG} GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-cmp-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-dex
|
||||
cp Dockerfile.dev dist
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)$(IMAGE_REPOSITORY):$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
else
|
||||
image:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)$(IMAGE_REPOSITORY):$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
|
||||
endif
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)$(IMAGE_REPOSITORY):$(IMAGE_TAG) ; fi
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: armimage
|
||||
armimage:
|
||||
$(DOCKER) build -t $(IMAGE_PREFIX)(IMAGE_REPOSITORY):$(IMAGE_TAG)-arm .
|
||||
$(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
|
||||
|
||||
.PHONY: builder-image
|
||||
builder-image:
|
||||
@@ -411,7 +377,9 @@ lint: test-tools-image
|
||||
.PHONY: lint-local
|
||||
lint-local:
|
||||
golangci-lint --version
|
||||
golangci-lint run --fix --verbose
|
||||
# NOTE: If you get a "Killed" OOM message, try reducing the value of GOGC
|
||||
# See https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --fix --verbose
|
||||
|
||||
.PHONY: lint-ui
|
||||
lint-ui: test-tools-image
|
||||
@@ -443,24 +411,12 @@ test: test-tools-image
|
||||
|
||||
# Run all unit tests (local version)
|
||||
.PHONY: test-local
|
||||
test-local: test-gitops-engine
|
||||
# run if TEST_MODULE is empty or does not point to gitops-engine tests
|
||||
ifneq ($(if $(TEST_MODULE),,ALL)$(filter-out github.com/argoproj/argo-cd/gitops-engine% ./gitops-engine%,$(TEST_MODULE)),)
|
||||
test-local:
|
||||
if test "$(TEST_MODULE)" = ""; then \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results"; \
|
||||
else \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results" "$(TEST_MODULE)"; \
|
||||
fi
|
||||
endif
|
||||
|
||||
# Run gitops-engine unit tests
|
||||
.PHONY: test-gitops-engine
|
||||
test-gitops-engine:
|
||||
# run if TEST_MODULE is empty or points to gitops-engine tests
|
||||
ifneq ($(if $(TEST_MODULE),,ALL)$(filter github.com/argoproj/argo-cd/gitops-engine% ./gitops-engine%,$(TEST_MODULE)),)
|
||||
mkdir -p $(PWD)/test-results
|
||||
cd gitops-engine && go test -race -cover ./... -args -test.gocoverdir="$(PWD)/test-results"
|
||||
endif
|
||||
|
||||
.PHONY: test-race
|
||||
test-race: test-tools-image
|
||||
@@ -487,7 +443,7 @@ test-e2e:
|
||||
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
|
||||
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
|
||||
debug-test-server: test-tools-image
|
||||
@@ -511,13 +467,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-2 || true
|
||||
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
|
||||
# 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
|
||||
mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/keys && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/keys
|
||||
mkdir -p $(ARGOCD_E2E_DIR)/app/config/gpg/source && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/gpg/source
|
||||
mkdir -p $(ARGOCD_E2E_DIR)/app/config/plugin && chmod 0700 $(ARGOCD_E2E_DIR)/app/config/plugin
|
||||
if test -d /tmp/argo-e2e/app/config/gpg; then rm -rf /tmp/argo-e2e/app/config/gpg/*; fi
|
||||
mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys
|
||||
mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source
|
||||
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
|
||||
mkdir -p /tmp/coverage/app-controller
|
||||
mkdir -p /tmp/coverage/api-server
|
||||
@@ -526,15 +482,13 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
||||
mkdir -p /tmp/coverage/notification
|
||||
mkdir -p /tmp/coverage/commit-server
|
||||
# set paths for locally managed ssh known hosts and tls certs data
|
||||
ARGOCD_E2E_DIR=$(ARGOCD_E2E_DIR) \
|
||||
ARGOCD_SSH_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/ssh \
|
||||
ARGOCD_TLS_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/tls \
|
||||
ARGOCD_GPG_DATA_PATH=$(ARGOCD_E2E_DIR)/app/config/gpg/source \
|
||||
ARGOCD_GNUPGHOME=$(ARGOCD_E2E_DIR)/app/config/gpg/keys \
|
||||
ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \
|
||||
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
|
||||
ARGOCD_GPG_DATA_PATH=/tmp/argo-e2e/app/config/gpg/source \
|
||||
ARGOCD_GNUPGHOME=/tmp/argo-e2e/app/config/gpg/keys \
|
||||
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
||||
ARGOCD_PLUGINCONFIGFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \
|
||||
ARGOCD_PLUGINSOCKFILEPATH=$(ARGOCD_E2E_DIR)/app/config/plugin \
|
||||
ARGOCD_GIT_CONFIG=$(PWD)/test/e2e/fixture/gitconfig \
|
||||
ARGOCD_PLUGINCONFIGFILEPATH=/tmp/argo-e2e/app/config/plugin \
|
||||
ARGOCD_PLUGINSOCKFILEPATH=/tmp/argo-e2e/app/config/plugin \
|
||||
ARGOCD_E2E_DISABLE_AUTH=false \
|
||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
|
||||
@@ -611,7 +565,7 @@ build-docs-local:
|
||||
|
||||
.PHONY: 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
|
||||
serve-docs-local:
|
||||
@@ -619,7 +573,7 @@ serve-docs-local:
|
||||
|
||||
.PHONY: 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
|
||||
.PHONY: verify-kube-connect
|
||||
@@ -650,7 +604,6 @@ install-test-tools-local:
|
||||
.PHONY: install-codegen-tools-local
|
||||
install-codegen-tools-local:
|
||||
./hack/install.sh codegen-tools
|
||||
./hack/install.sh codegen-go-tools
|
||||
|
||||
# Installs all tools required for running codegen (Go packages)
|
||||
.PHONY: install-go-tools-local
|
||||
|
||||
8
Procfile
8
Procfile
@@ -1,14 +1,14 @@
|
||||
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/app-controller} HOSTNAME=testappcontroller-1 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-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --commit-server localhost:${ARGOCD_E2E_COMMITSERVER_PORT:-8086} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'} --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:v2.45.0" 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
|
||||
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}"
|
||||
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'
|
||||
git-server: test/fixture/testrepos/start-git.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}"
|
||||
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'}"
|
||||
@@ -13,7 +13,6 @@
|
||||
[](https://twitter.com/argoproj)
|
||||
[](https://argoproj.github.io/community/join-slack)
|
||||
[](https://www.linkedin.com/company/argoproj/)
|
||||
[](https://bsky.app/profile/argoproj.bsky.social)
|
||||
|
||||
# Argo CD - Declarative Continuous Delivery for Kubernetes
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ header:
|
||||
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
|
||||
last-updated: '2023-10-27'
|
||||
last-reviewed: '2023-10-27'
|
||||
commit-hash: 814db444c36503851dc3d45cf9c44394821ca1a4
|
||||
commit-hash: 226a670fe6b3c6769ff6d18e6839298a58e4577d
|
||||
project-url: https://github.com/argoproj/argo-cd
|
||||
project-release: v3.4.0
|
||||
project-release: v3.1.0
|
||||
changelog: https://github.com/argoproj/argo-cd/releases
|
||||
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
|
||||
project-lifecycle:
|
||||
|
||||
17
SECURITY.md
17
SECURITY.md
@@ -48,23 +48,6 @@ otherwise very intrusive, and there's a workaround available, we may decide to
|
||||
provide a forward-fix only, e.g. to be released the next minor release, instead
|
||||
of releasing it within a patch branch for the currently supported releases.
|
||||
|
||||
## Dependency Upgrade Policy
|
||||
|
||||
Argo CD relies on certain binaries and libraries that might appear in security scanners.
|
||||
|
||||
Upgrading certain dependencies, such as Helm, Kustomize, and git, may have negative impacts
|
||||
on users, as they may include breaking changes or changes in behavior. For this reason,
|
||||
we will only upgrade to new patch versions within the same minor version series within
|
||||
a supported Argo CD version. For example, if we are currently on Helm 3.12.0 and Argo CD
|
||||
3.4.0, we will only upgrade to Helm 3.12.x within Argo CD 3.4.x, and not to Helm 3.13.0
|
||||
or later.
|
||||
|
||||
If there is a critical, _exploitable_ vulnerability in a dependency that will not be fixed
|
||||
in a patch release, we will evaluate the impact of the vulnerability and the risk of not
|
||||
upgrading the dependency. We ask that, if you believe a version bump is justified, please
|
||||
open an issue _describing how the vulnerability is exploitable in the context of Argo CD_,
|
||||
and we will evaluate it and decide whether or not to upgrade the dependency.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security related bug in Argo CD, we kindly ask you for responsible
|
||||
|
||||
41
Tiltfile
41
Tiltfile
@@ -1,10 +1,6 @@
|
||||
load('ext://restart_process', 'docker_build_with_restart')
|
||||
load('ext://uibutton', 'cmd_button', 'location')
|
||||
|
||||
# tilt version should be >= v0.37.0 for k8s_server_side_apply tilt-dev/tilt#6680
|
||||
update_settings(
|
||||
k8s_server_side_apply="true",
|
||||
)
|
||||
# add ui button in web ui to run make codegen-local (top nav)
|
||||
cmd_button(
|
||||
'make codegen-local',
|
||||
@@ -14,14 +10,6 @@ cmd_button(
|
||||
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',
|
||||
@@ -56,7 +44,6 @@ 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,
|
||||
ignore = ['**/*_test.go'],
|
||||
allow_parallel=True,
|
||||
)
|
||||
|
||||
@@ -65,7 +52,7 @@ k8s_yaml(kustomize('manifests/dev-tilt'))
|
||||
|
||||
# build dev image
|
||||
docker_build_with_restart(
|
||||
'quay.io/argoproj/argocd:latest',
|
||||
'argocd',
|
||||
context='.',
|
||||
dockerfile='Dockerfile.tilt',
|
||||
entrypoint=[
|
||||
@@ -82,7 +69,7 @@ docker_build_with_restart(
|
||||
],
|
||||
platform=platform,
|
||||
live_update=[
|
||||
sync('.tilt-bin/argocd_linux', '/usr/local/bin/argocd'),
|
||||
sync('.tilt-bin/argocd_linux_amd64', '/usr/local/bin/argocd'),
|
||||
],
|
||||
only=[
|
||||
'.tilt-bin',
|
||||
@@ -128,7 +115,6 @@ k8s_resource(
|
||||
'9345:2345',
|
||||
'8083:8083'
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track crds
|
||||
@@ -154,7 +140,6 @@ k8s_resource(
|
||||
'9346:2345',
|
||||
'8084:8084'
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track argocd-redis resources and port forward
|
||||
@@ -169,7 +154,6 @@ k8s_resource(
|
||||
port_forwards=[
|
||||
'6379:6379',
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track argocd-applicationset-controller resources
|
||||
@@ -188,7 +172,6 @@ k8s_resource(
|
||||
'8085:8080',
|
||||
'7000:7000'
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track argocd-application-controller resources
|
||||
@@ -206,7 +189,6 @@ k8s_resource(
|
||||
'9348:2345',
|
||||
'8086:8082',
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track argocd-notifications-controller resources
|
||||
@@ -224,7 +206,6 @@ k8s_resource(
|
||||
'9349:2345',
|
||||
'8087:9001',
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track argocd-dex-server resources
|
||||
@@ -236,7 +217,6 @@ k8s_resource(
|
||||
'argocd-dex-server:role',
|
||||
'argocd-dex-server:rolebinding',
|
||||
],
|
||||
resource_deps=['build']
|
||||
)
|
||||
|
||||
# track argocd-commit-server resources
|
||||
@@ -251,19 +231,6 @@ k8s_resource(
|
||||
'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
|
||||
@@ -285,7 +252,6 @@ k8s_resource(
|
||||
port_forwards=[
|
||||
'4000:4000',
|
||||
],
|
||||
resource_deps=['node-modules'],
|
||||
)
|
||||
|
||||
# linting
|
||||
@@ -294,7 +260,6 @@ local_resource(
|
||||
'make lint-local',
|
||||
deps = code_deps,
|
||||
allow_parallel=True,
|
||||
resource_deps=['vendor']
|
||||
)
|
||||
|
||||
local_resource(
|
||||
@@ -304,7 +269,6 @@ local_resource(
|
||||
'ui',
|
||||
],
|
||||
allow_parallel=True,
|
||||
resource_deps=['node-modules'],
|
||||
)
|
||||
|
||||
local_resource(
|
||||
@@ -314,6 +278,5 @@ local_resource(
|
||||
'go.mod',
|
||||
'go.sum',
|
||||
],
|
||||
allow_parallel=True,
|
||||
)
|
||||
|
||||
|
||||
51
USERS.md
51
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:
|
||||
|
||||
1. [100ms](https://www.100ms.ai/)
|
||||
1. [127Labs](https://127labs.com/)
|
||||
1. [3Rein](https://3reinmedia.com/)
|
||||
1. [42 School](https://42.fr/)
|
||||
1. [3Rein](https://www.3rein.com/)
|
||||
1. [4data](https://4data.ch/)
|
||||
1. [7shifts](https://www.7shifts.com/)
|
||||
1. [Adevinta](https://www.adevinta.com/)
|
||||
@@ -31,8 +29,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [Ant Group](https://www.antgroup.com/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arcadia](https://arcadia.io/)
|
||||
1. [Arctiq Inc.](https://arctiq.com/)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
1. [Artemis Health by Nomi Health](https://www.artemishealth.com/)
|
||||
1. [Arturia](https://www.arturia.com)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||
@@ -43,7 +40,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Back Market](https://www.backmarket.com)
|
||||
1. [Bajaj Finserv Health Ltd.](https://www.bajajfinservhealth.in)
|
||||
1. [Baloise](https://www.baloise.com)
|
||||
1. [Batumbu](https://batumbu.id)
|
||||
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
||||
1. [Beat](https://thebeat.co/en/)
|
||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||
@@ -64,7 +60,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Camptocamp](https://camptocamp.com)
|
||||
1. [Candis](https://www.candis.io)
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
1. [Capptain LTD](https://capptain.co/)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [CARFAX](https://www.carfax.com)
|
||||
1. [Carrefour Group](https://www.carrefour.com)
|
||||
@@ -75,9 +70,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Chargetrip](https://chargetrip.com)
|
||||
1. [Chime](https://www.chime.com)
|
||||
1. [Chronicle Labs](https://chroniclelabs.org)
|
||||
1. [C.H.Robinson ](https://www.chrobinson.com)
|
||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||
1. [Close](https://www.close.com/)
|
||||
1. [Cloud Posse](https://www.cloudposse.com/)
|
||||
1. [Cloud Scale](https://cloudscaleinc.com/)
|
||||
1. [CloudScript](https://www.cloudscript.com.br/)
|
||||
@@ -88,7 +81,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Codefresh](https://www.codefresh.io/)
|
||||
1. [Codility](https://www.codility.com/)
|
||||
1. [Cognizant](https://www.cognizant.com/)
|
||||
1. [Collins Aerospace](https://www.collinsaerospace.com/)
|
||||
1. [Commonbond](https://commonbond.co/)
|
||||
1. [Compatio.AI](https://compatio.ai/)
|
||||
1. [Contlo](https://contlo.com/)
|
||||
@@ -102,7 +94,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Datarisk](https://www.datarisk.io/)
|
||||
1. [Daydream](https://daydream.ing)
|
||||
1. [Deloitte](https://www.deloitte.com/)
|
||||
1. [Dematic](https://www.dematic.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/)
|
||||
@@ -111,7 +102,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [DigitalOcean](https://www.digitalocean.com)
|
||||
1. [Divar](https://divar.ir)
|
||||
1. [Divistant](https://divistant.com)
|
||||
2. [DocNetwork](https://docnetwork.org/)
|
||||
1. [Dott](https://ridedott.com)
|
||||
1. [Doubble](https://www.doubble.app)
|
||||
1. [Doximity](https://www.doximity.com/)
|
||||
@@ -126,7 +116,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [enigmo](https://enigmo.co.jp/)
|
||||
1. [Envoy](https://envoy.com/)
|
||||
1. [eSave](https://esave.es/)
|
||||
1. [Expedia](https://www.expedia.com)
|
||||
1. [Factorial](https://factorialhr.com/)
|
||||
1. [Farfetch](https://www.farfetch.com)
|
||||
1. [Faro](https://www.faro.com/)
|
||||
@@ -148,14 +137,14 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Getir](https://getir.com)
|
||||
1. [GetYourGuide](https://www.getyourguide.com/)
|
||||
1. [Gitpod](https://www.gitpod.io)
|
||||
1. [Gllue](https://www.gllue.com/)
|
||||
1. [Gllue](https://gllue.com)
|
||||
1. [gloat](https://gloat.com/)
|
||||
1. [GLOBIS](https://globis.com)
|
||||
1. [Glovo](https://www.glovoapp.com)
|
||||
1. [GlueOps](https://glueops.dev)
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Gojek](https://www.gojek.io/)
|
||||
1. [GoTo Financial](https://www.gotocompany.com/en/products/goto-financial)
|
||||
1. [GoTo Financial](https://gotofinancial.com/)
|
||||
1. [GoTo](https://www.goto.com/)
|
||||
1. [Greenpass](https://www.greenpass.com.br/)
|
||||
1. [Gridfuse](https://gridfuse.com/)
|
||||
@@ -166,29 +155,25 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Healy](https://www.healyworld.net)
|
||||
1. [Helio](https://helio.exchange)
|
||||
1. [hetao101](https://www.hetao101.com/)
|
||||
1. [Hetki](https://hetki.io/)
|
||||
1. [Hetki](https://hetki.ai)
|
||||
1. [hipages](https://hipages.com.au/)
|
||||
1. [Hiya](https://hiya.com)
|
||||
1. [Honestbank](https://honestbank.com)
|
||||
1. [Hostinger](https://www.hostinger.com)
|
||||
1. [Hotjar](https://www.hotjar.com)
|
||||
1. [IABAI](https://www.iab.ai)
|
||||
1. [IBM](https://www.ibm.com/)
|
||||
1. [Ibotta](https://home.ibotta.com)
|
||||
1. [Icelandair](https://www.icelandair.com)
|
||||
1. [IFS](https://www.ifs.com)
|
||||
1. [IITS-Consulting](https://iits-consulting.de)
|
||||
1. [IllumiDesk](https://illumichat.com/)
|
||||
1. [Imagine Learning](https://www.imaginelearning.com/)
|
||||
1. [IllumiDesk](https://www.illumidesk.com)
|
||||
1. [imaware](https://imaware.health)
|
||||
1. [Indeed](https://indeed.com)
|
||||
1. [Index Exchange](https://www.indexexchange.com/)
|
||||
1. [Info Support](https://www.infosupport.com/)
|
||||
1. [InsideBoard](https://www.insideboard.com)
|
||||
1. [Instruqt](https://www.instruqt.com)
|
||||
1. [Intel](https://www.intel.com)
|
||||
1. [Intuit](https://www.intuit.com/)
|
||||
1. [IQVIA](https://www.iqvia.com/)
|
||||
1. [Jellysmack](https://www.jellysmack.com)
|
||||
1. [Joblift](https://joblift.com/)
|
||||
1. [JovianX](https://www.jovianx.com/)
|
||||
@@ -210,11 +195,10 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Kurly](https://www.kurly.com/)
|
||||
1. [Kvist](https://kvistsolutions.com)
|
||||
1. [Kyriba](https://www.kyriba.com/)
|
||||
1. [Lattice](https://lattice.com)
|
||||
1. [LeFigaro](https://www.lefigaro.fr/)
|
||||
1. [Lely](https://www.lely.com/)
|
||||
1. [LexisNexis](https://www.lexisnexis.com/)
|
||||
1. [Lian Chu Securities](https://www.lczq.com/)
|
||||
1. [Lian Chu Securities](https://lczq.com)
|
||||
1. [Liatrio](https://www.liatrio.com)
|
||||
1. [Lightricks](https://www.lightricks.com/)
|
||||
1. [Loom](https://www.loom.com/)
|
||||
@@ -241,14 +225,12 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [mixi Group](https://mixi.co.jp/)
|
||||
1. [Moengage](https://www.moengage.com/)
|
||||
1. [Money Forward](https://corp.moneyforward.com/en/)
|
||||
1. [MongoDB](https://www.mongodb.com/)
|
||||
1. [MOO Print](https://www.moo.com/)
|
||||
1. [Mozilla](https://www.mozilla.org)
|
||||
1. [MTN Group](https://www.mtn.com/)
|
||||
1. [Municipality of The Hague](https://www.denhaag.nl/)
|
||||
1. [My Job Glasses](https://myjobglasses.com)
|
||||
1. [Natura &Co](https://ri.natura.com.br/)
|
||||
1. [Netease Cloud Music](https://music.163.com/)
|
||||
1. [Natura &Co](https://naturaeco.com/)
|
||||
1. [Nethopper](https://nethopper.io)
|
||||
1. [New Relic](https://newrelic.com/)
|
||||
1. [Nextbasket](https://nextbasket.com)
|
||||
@@ -278,7 +260,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Orbital Insight](https://orbitalinsight.com/)
|
||||
1. [Oscar Health Insurance](https://hioscar.com/)
|
||||
1. [Outpost24](https://outpost24.com/)
|
||||
1. [p3r](https://persona.atlus.com/p3r/)
|
||||
1. [p3r](https://www.p3r.one/)
|
||||
1. [Packlink](https://www.packlink.com/)
|
||||
1. [PagerDuty](https://www.pagerduty.com/)
|
||||
1. [Pandosearch](https://www.pandosearch.com/en/home)
|
||||
@@ -321,8 +303,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Relex Solutions](https://www.relexsolutions.com/)
|
||||
1. [RightRev](https://rightrev.com/)
|
||||
1. [Rijkswaterstaat](https://www.rijkswaterstaat.nl/en)
|
||||
1. Rise
|
||||
1. [RISK IDENT](https://riskident.com/)
|
||||
1. [Rise](https://www.risecard.eu/)
|
||||
1. [Riskified](https://www.riskified.com/)
|
||||
1. [Robotinfra](https://www.robotinfra.com)
|
||||
1. [Rocket.Chat](https://rocket.chat)
|
||||
@@ -332,7 +313,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Salad Technologies](https://salad.com/)
|
||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [SAP Signavio](https://www.signavio.com)
|
||||
1. [Sauce Labs](https://saucelabs.com/)
|
||||
1. [Schneider Electric](https://www.se.com)
|
||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||
@@ -340,10 +320,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [SEEK](https://seek.com.au)
|
||||
1. [SEKAI](https://www.sekai.io/)
|
||||
1. [Semgrep](https://semgrep.com)
|
||||
1. [Seznam.cz](https://o-seznam.cz/)
|
||||
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. [Sidewalk Entertainment](https://sidewalkplay.com/)
|
||||
1. [Skit](https://skit.ai/)
|
||||
@@ -356,7 +333,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Snapp](https://snapp.ir/)
|
||||
1. [Snyk](https://snyk.io/)
|
||||
1. [Softway Medical](https://www.softwaymedical.fr/)
|
||||
1. [Sophotech](https://sopho.tech)
|
||||
1. [South China Morning Post (SCMP)](https://www.scmp.com/)
|
||||
1. [Speee](https://speee.jp/)
|
||||
1. [Spendesk](https://spendesk.com/)
|
||||
@@ -390,7 +366,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Ticketmaster](https://ticketmaster.com)
|
||||
1. [Tiger Analytics](https://www.tigeranalytics.com/)
|
||||
1. [Tigera](https://www.tigera.io/)
|
||||
1. [Topicus.Education](https://topicus.nl/en/sectors/education)
|
||||
1. [Toss](https://toss.im/en)
|
||||
1. [Trendyol](https://www.trendyol.com/)
|
||||
1. [tru.ID](https://tru.id)
|
||||
@@ -420,18 +395,18 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
||||
1. [Wakacje.pl](https://www.wakacje.pl/)
|
||||
1. [Walkbase](https://www.walkbase.com/)
|
||||
1. [Webstores](https://www.friday.nl/)
|
||||
1. [Webstores](https://www.webstores.nl)
|
||||
1. [Wehkamp](https://www.wehkamp.nl/)
|
||||
1. [WeMaintain](https://www.wemaintain.com/)
|
||||
1. [WeMo Scooter](https://www.wemoscooter.com/)
|
||||
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
|
||||
1. [Witick](https://www.witik.io/en/)
|
||||
1. [Witick](https://witick.io/)
|
||||
1. [Wolffun Game](https://www.wolffungame.com/)
|
||||
1. [WooliesX](https://wooliesx.com.au/)
|
||||
1. [Woolworths Group](https://www.woolworthsgroup.com.au/)
|
||||
1. [WSpot](https://www.wspot.com.br/)
|
||||
1. [X3M ads](https://x3mads.com)
|
||||
1. [Yieldlab](https://virtualminds.com/)
|
||||
1. [Yieldlab](https://www.yieldlab.de/)
|
||||
1. [Youverify](https://youverify.co/)
|
||||
1. [Yubo](https://www.yubo.live/)
|
||||
1. [Yuno](https://y.uno/)
|
||||
|
||||
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/event"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
"github.com/argoproj/argo-cd/v3/common"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
@@ -23,9 +22,8 @@ import (
|
||||
// requeue any related ApplicationSets.
|
||||
type clusterSecretEventHandler struct {
|
||||
// handler.EnqueueRequestForOwner
|
||||
Log log.FieldLogger
|
||||
Client client.Client
|
||||
ApplicationSetNamespaces []string
|
||||
Log log.FieldLogger
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
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")
|
||||
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
|
||||
for _, generator := range appSet.Spec.Generators {
|
||||
if generator.Clusters != nil {
|
||||
|
||||
@@ -137,7 +137,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-app-set",
|
||||
Namespace: "argocd",
|
||||
Namespace: "another-namespace",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
@@ -171,37 +171,9 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
},
|
||||
},
|
||||
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",
|
||||
items: []argov1alpha1.ApplicationSet{
|
||||
@@ -580,9 +552,8 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
|
||||
|
||||
handler := &clusterSecretEventHandler{
|
||||
Client: fakeClient,
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
ApplicationSetNamespaces: []string{"argocd"},
|
||||
Client: fakeClient,
|
||||
Log: log.WithField("type", "createSecretEventHandler"),
|
||||
}
|
||||
|
||||
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
appsetmetrics "github.com/argoproj/argo-cd/v3/applicationset/metrics"
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||
)
|
||||
|
||||
func TestRequeueAfter(t *testing.T) {
|
||||
@@ -58,17 +57,12 @@ func TestRequeueAfter(t *testing.T) {
|
||||
}
|
||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
|
||||
scmConfig := generators.NewSCMConfig("", []string{""}, true, true, nil, true)
|
||||
clusterInformer, err := settings.NewClusterInformer(appClientset, "argocd")
|
||||
require.NoError(t, err)
|
||||
|
||||
defer startAndSyncInformer(t, clusterInformer)()
|
||||
|
||||
terminalGenerators := map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
"Clusters": generators.NewClusterGenerator(k8sClient, "argocd"),
|
||||
"Clusters": generators.NewClusterGenerator(ctx, k8sClient, appClientset, "argocd"),
|
||||
"Git": generators.NewGitGenerator(mockServer, "namespace"),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd", clusterInformer),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, scmConfig),
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
||||
},
|
||||
SyncPolicy: &appv1.SyncPolicy{
|
||||
Automated: &appv1.SyncPolicyAutomated{
|
||||
Prune: new(true),
|
||||
Prune: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -172,7 +172,7 @@ spec:
|
||||
},
|
||||
SyncPolicy: &appv1.SyncPolicy{
|
||||
Automated: &appv1.SyncPolicyAutomated{
|
||||
Prune: new(true),
|
||||
Prune: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -86,28 +86,28 @@ func TestGenerateApplications(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
generatorMock := &genmock.Generator{}
|
||||
generatorMock := genmock.Generator{}
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
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)
|
||||
|
||||
generatorMock.EXPECT().GetTemplate(&generator).
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
rendererMock := &rendmock.Renderer{}
|
||||
rendererMock := rendmock.Renderer{}
|
||||
|
||||
var expectedApps []v1alpha1.Application
|
||||
|
||||
if cc.generateParamsError == nil {
|
||||
for _, p := range cc.params {
|
||||
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)
|
||||
} 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)
|
||||
expectedApps = append(expectedApps, app)
|
||||
}
|
||||
@@ -115,9 +115,9 @@ func TestGenerateApplications(t *testing.T) {
|
||||
}
|
||||
|
||||
generators := map[string]generators.Generator{
|
||||
"List": generatorMock,
|
||||
"List": &generatorMock,
|
||||
}
|
||||
renderer := rendererMock
|
||||
renderer := &rendererMock
|
||||
|
||||
got, reason, err := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -200,26 +200,26 @@ func TestMergeTemplateApplications(t *testing.T) {
|
||||
cc := c
|
||||
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
generatorMock := &genmock.Generator{}
|
||||
generatorMock := genmock.Generator{}
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
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)
|
||||
|
||||
generatorMock.EXPECT().GetTemplate(&generator).
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
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)
|
||||
|
||||
generators := map[string]generators.Generator{
|
||||
"List": generatorMock,
|
||||
"List": &generatorMock,
|
||||
}
|
||||
renderer := rendererMock
|
||||
renderer := &rendererMock
|
||||
|
||||
got, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -312,19 +312,19 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
t.Run(cases.name, func(t *testing.T) {
|
||||
generatorMock := &genmock.Generator{}
|
||||
generatorMock := genmock.Generator{}
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
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)
|
||||
|
||||
generatorMock.EXPECT().GetTemplate(&generator).
|
||||
Return(&cases.template)
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
Return(&cases.template, nil)
|
||||
|
||||
generators := map[string]generators.Generator{
|
||||
"PullRequest": generatorMock,
|
||||
"PullRequest": &generatorMock,
|
||||
}
|
||||
renderer := &utils.Render{}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
@@ -21,15 +22,19 @@ var _ Generator = (*ClusterGenerator)(nil)
|
||||
// ClusterGenerator generates Applications for some or all clusters registered with ArgoCD.
|
||||
type ClusterGenerator struct {
|
||||
client.Client
|
||||
ctx context.Context
|
||||
clientset kubernetes.Interface
|
||||
// namespace is the Argo CD namespace
|
||||
namespace string
|
||||
}
|
||||
|
||||
var render = &utils.Render{}
|
||||
|
||||
func NewClusterGenerator(c client.Client, namespace string) Generator {
|
||||
func NewClusterGenerator(ctx context.Context, c client.Client, clientset kubernetes.Interface, namespace string) Generator {
|
||||
g := &ClusterGenerator{
|
||||
Client: c,
|
||||
ctx: ctx,
|
||||
clientset: clientset,
|
||||
namespace: namespace,
|
||||
}
|
||||
return g
|
||||
@@ -59,107 +64,113 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
||||
// - Since local clusters do not have secrets, they do not have labels to match against
|
||||
ignoreLocalClusters := len(appSetGenerator.Clusters.Selector.MatchExpressions) > 0 || len(appSetGenerator.Clusters.Selector.MatchLabels) > 0
|
||||
|
||||
// Get cluster secrets using the cached controller-runtime client
|
||||
// ListCluster will include the local cluster in the list of clusters
|
||||
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing clusters: %w", err)
|
||||
}
|
||||
|
||||
if clustersFromArgoCD == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
clusterSecrets, err := g.getSecretsByClusterName(logCtx, appSetGenerator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
||||
}
|
||||
|
||||
paramHolder := ¶mHolder{isFlatMode: appSetGenerator.Clusters.FlatList}
|
||||
logCtx.Debugf("Using flat mode = %t for cluster generator", paramHolder.isFlatMode)
|
||||
res := []map[string]any{}
|
||||
|
||||
// Convert map values to slice to check for an in-cluster secret
|
||||
secretsList := make([]corev1.Secret, 0, len(clusterSecrets))
|
||||
for _, secret := range clusterSecrets {
|
||||
secretsList = append(secretsList, 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 {
|
||||
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
|
||||
// handled by the next step.
|
||||
if secretForCluster, exists := clusterSecrets[cluster.Name]; exists {
|
||||
secretsFound = append(secretsFound, secretForCluster)
|
||||
} else if !ignoreLocalClusters {
|
||||
// If there is no secret for the cluster, it's the local cluster, so handle it here.
|
||||
params := map[string]any{}
|
||||
params["name"] = cluster.Name
|
||||
params["nameNormalized"] = cluster.Name
|
||||
params["server"] = cluster.Server
|
||||
params["project"] = ""
|
||||
|
||||
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
||||
}
|
||||
|
||||
if isFlatMode {
|
||||
clustersParams = append(clustersParams, params)
|
||||
} else {
|
||||
res = append(res, params)
|
||||
}
|
||||
|
||||
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
||||
}
|
||||
}
|
||||
|
||||
// For each matching cluster secret (non-local clusters only)
|
||||
for _, cluster := range clusterSecrets {
|
||||
params := g.getClusterParameters(cluster, appSet)
|
||||
for _, cluster := range secretsFound {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
// Add the in-cluster last if it doesn't have a secret, and we're not ignoring in-cluster
|
||||
if !ignoreLocalClusters && !utils.SecretsContainInClusterCredentials(secretsList) {
|
||||
params := map[string]any{}
|
||||
params["name"] = argoappsetv1alpha1.KubernetesInClusterName
|
||||
params["nameNormalized"] = argoappsetv1alpha1.KubernetesInClusterName
|
||||
params["server"] = argoappsetv1alpha1.KubernetesInternalAPIServerAddr
|
||||
params["project"] = ""
|
||||
|
||||
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
||||
}
|
||||
|
||||
paramHolder.append(params)
|
||||
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
||||
if isFlatMode {
|
||||
res = append(res, map[string]any{
|
||||
"clusters": clustersParams,
|
||||
})
|
||||
}
|
||||
|
||||
return paramHolder.consolidate(), nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
||||
@@ -171,7 +182,7 @@ func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerat
|
||||
return nil, fmt.Errorf("error converting label selector: %w", err)
|
||||
}
|
||||
|
||||
if err := g.List(context.Background(), clusterSecretList, client.InNamespace(g.namespace), client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
||||
if err := g.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
||||
|
||||
@@ -7,9 +7,12 @@ import (
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
|
||||
@@ -296,15 +299,23 @@ func TestGenerateParams(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// convert []client.Object to []runtime.Object, for use by kubefake package
|
||||
runtimeClusters := []runtime.Object{}
|
||||
for _, clientCluster := range clusters {
|
||||
runtimeClusters = append(runtimeClusters, clientCluster)
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
cl := &possiblyErroringFakeCtrlRuntimeClient{
|
||||
fakeClient,
|
||||
testCase.clientError,
|
||||
}
|
||||
|
||||
clusterGenerator := NewClusterGenerator(cl, "namespace")
|
||||
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -325,25 +336,12 @@ func TestGenerateParams(t *testing.T) {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assertEqualParamsFlat(t, testCase.expected, got, testCase.isFlatMode)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertEqualParamsFlat(t *testing.T, expected, got []map[string]any, isFlatMode bool) {
|
||||
t.Helper()
|
||||
if isFlatMode && len(expected) == 1 && len(got) == 1 {
|
||||
expectedClusters, ok1 := expected[0]["clusters"].([]map[string]any)
|
||||
gotClusters, ok2 := got[0]["clusters"].([]map[string]any)
|
||||
if ok1 && ok2 {
|
||||
assert.ElementsMatch(t, expectedClusters, gotClusters)
|
||||
return
|
||||
}
|
||||
}
|
||||
assert.ElementsMatch(t, expected, got)
|
||||
}
|
||||
|
||||
func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
clusters := []client.Object{
|
||||
&corev1.Secret{
|
||||
@@ -839,15 +837,23 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// convert []client.Object to []runtime.Object, for use by kubefake package
|
||||
runtimeClusters := []runtime.Object{}
|
||||
for _, clientCluster := range clusters {
|
||||
runtimeClusters = append(runtimeClusters, clientCluster)
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
cl := &possiblyErroringFakeCtrlRuntimeClient{
|
||||
fakeClient,
|
||||
testCase.clientError,
|
||||
}
|
||||
|
||||
clusterGenerator := NewClusterGenerator(cl, "namespace")
|
||||
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -870,7 +876,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assertEqualParamsFlat(t, testCase.expected, got, testCase.isFlatMode)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,27 +19,24 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||
)
|
||||
|
||||
var _ Generator = (*DuckTypeGenerator)(nil)
|
||||
|
||||
// DuckTypeGenerator generates Applications for some or all clusters registered with ArgoCD.
|
||||
type DuckTypeGenerator struct {
|
||||
ctx context.Context
|
||||
dynClient dynamic.Interface
|
||||
clientset kubernetes.Interface
|
||||
namespace string // namespace is the Argo CD namespace
|
||||
clusterInformer *settings.ClusterInformer
|
||||
ctx context.Context
|
||||
dynClient dynamic.Interface
|
||||
clientset kubernetes.Interface
|
||||
namespace string // namespace is the Argo CD namespace
|
||||
}
|
||||
|
||||
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string, clusterInformer *settings.ClusterInformer) Generator {
|
||||
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
|
||||
g := &DuckTypeGenerator{
|
||||
ctx: ctx,
|
||||
dynClient: dynClient,
|
||||
clientset: clientset,
|
||||
namespace: namespace,
|
||||
clusterInformer: clusterInformer,
|
||||
ctx: ctx,
|
||||
dynClient: dynClient,
|
||||
clientset: clientset,
|
||||
namespace: namespace,
|
||||
}
|
||||
return g
|
||||
}
|
||||
@@ -68,7 +65,8 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
return nil, ErrEmptyAppSetGenerator
|
||||
}
|
||||
|
||||
clustersFromArgoCD, err := utils.ListClusters(g.clusterInformer)
|
||||
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
||||
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing clusters: %w", err)
|
||||
}
|
||||
|
||||
@@ -11,13 +11,11 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic/fake"
|
||||
dynfake "k8s.io/client-go/dynamic/fake"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v3/test"
|
||||
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -292,14 +290,9 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
||||
Resource: "ducks",
|
||||
}: "DuckList"}
|
||||
|
||||
fakeDynClient := fake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||
|
||||
clusterInformer, err := settings.NewClusterInformer(appClientset, "namespace")
|
||||
require.NoError(t, err)
|
||||
|
||||
defer test.StartInformer(clusterInformer)()
|
||||
|
||||
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace", clusterInformer)
|
||||
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -593,14 +586,9 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
||||
Resource: "ducks",
|
||||
}: "DuckList"}
|
||||
|
||||
fakeDynClient := fake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||
|
||||
clusterInformer, err := settings.NewClusterInformer(appClientset, "namespace")
|
||||
require.NoError(t, err)
|
||||
|
||||
defer test.StartInformer(clusterInformer)()
|
||||
|
||||
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace", clusterInformer)
|
||||
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
||||
@@ -124,7 +124,7 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet
|
||||
}
|
||||
|
||||
func flattenParameters(in map[string]any) (map[string]string, error) {
|
||||
flat, err := flatten.Flatten(normalizeMapForFlatten(in), "", flatten.DotStyle)
|
||||
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error flatenning parameters: %w", err)
|
||||
}
|
||||
@@ -137,31 +137,6 @@ func flattenParameters(in map[string]any) (map[string]string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// normalizeMapForFlatten recursively converts map[string]string values to
|
||||
// map[string]interface{} so that flatten.Flatten can recurse into them.
|
||||
// flatten.Flatten only recurses into map[string]interface{} and []interface{};
|
||||
// map[string]string is treated as an opaque leaf, causing individual keys
|
||||
// (e.g. metadata.labels.environment) to be absent from the flat output and
|
||||
// silently breaking post-generator selector matching in GoTemplate mode.
|
||||
func normalizeMapForFlatten(in map[string]any) map[string]any {
|
||||
out := make(map[string]any, len(in))
|
||||
for k, v := range in {
|
||||
switch cast := v.(type) {
|
||||
case map[string]string:
|
||||
inner := make(map[string]any, len(cast))
|
||||
for ik, iv := range cast {
|
||||
inner[ik] = iv
|
||||
}
|
||||
out[k] = inner
|
||||
case map[string]any:
|
||||
out[k] = normalizeMapForFlatten(cast)
|
||||
default:
|
||||
out[k] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) (argoprojiov1alpha1.ApplicationSetTemplate, error) {
|
||||
// Make a copy of the value from `GetTemplate()` before merge, rather than copying directly into
|
||||
// the provided parameter (which will touch the original resource object returned by client-go)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -15,6 +16,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
crtclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
)
|
||||
@@ -249,231 +252,6 @@ func TestTransForm(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransFormGoTemplate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
selector *metav1.LabelSelector
|
||||
values map[string]string
|
||||
expected []map[string]any
|
||||
}{
|
||||
{
|
||||
name: "server filter",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"server": "https://production-01.example.com"},
|
||||
},
|
||||
expected: []map[string]any{{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "label MatchLabels",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"metadata.labels.environment": "staging"},
|
||||
},
|
||||
expected: []map[string]any{{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "label NotIn matchExpression",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "metadata.labels.environment",
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
Values: []string{"staging"},
|
||||
},
|
||||
},
|
||||
},
|
||||
// staging-01 is excluded; production-01, some-really-long-server-url, and
|
||||
// in-cluster (no labels) are all included because NotIn vacuously passes
|
||||
// when the key is absent.
|
||||
expected: []map[string]any{
|
||||
{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "some-really-long-server-url",
|
||||
"nameNormalized": "some-really-long-server-url",
|
||||
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// in-cluster (local cluster) has no labels; NotIn vacuously passes.
|
||||
"name": "in-cluster",
|
||||
"nameNormalized": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
"project": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "slash key in label",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
|
||||
},
|
||||
expected: []map[string]any{
|
||||
{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "some-really-long-server-url",
|
||||
"nameNormalized": "some-really-long-server-url",
|
||||
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "values filter",
|
||||
selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"values.env": "staging"},
|
||||
},
|
||||
values: map[string]string{"env": "{{.metadata.labels.environment}}"},
|
||||
expected: []map[string]any{{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]any{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"env": "staging",
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
testGenerators := map[string]Generator{
|
||||
"Clusters": getMockClusterGenerator(),
|
||||
}
|
||||
|
||||
applicationSetInfo := argov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
results, err := Transform(
|
||||
argov1alpha1.ApplicationSetGenerator{
|
||||
Selector: testCase.selector,
|
||||
Clusters: &argov1alpha1.ClusterGenerator{
|
||||
Selector: metav1.LabelSelector{},
|
||||
Template: argov1alpha1.ApplicationSetTemplate{},
|
||||
Values: testCase.values,
|
||||
},
|
||||
},
|
||||
testGenerators,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func emptyTemplate() argov1alpha1.ApplicationSetTemplate {
|
||||
return argov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
@@ -557,14 +335,20 @@ func getMockClusterGenerator() Generator {
|
||||
Type: corev1.SecretType("Opaque"),
|
||||
},
|
||||
}
|
||||
runtimeClusters := []runtime.Object{}
|
||||
for _, clientCluster := range clusters {
|
||||
runtimeClusters = append(runtimeClusters, clientCluster)
|
||||
}
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
return NewClusterGenerator(fakeClient, "namespace")
|
||||
return NewClusterGenerator(context.Background(), fakeClient, appClientset, "namespace")
|
||||
}
|
||||
|
||||
func getMockGitGenerator() Generator {
|
||||
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)
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "namespace")
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||
return gitGenerator
|
||||
}
|
||||
|
||||
@@ -753,7 +537,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
{name: "Error templating", args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "foo",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "path/{{ index .rmap (default .override .test) }}"}},
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "bar/"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"git_test": "{{ toPrettyJson . }}",
|
||||
@@ -767,7 +551,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "failed to replace parameters in generator: failed to execute go template path/{{ index .rmap (default .override .test) }}: template: base:1:8: 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 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -781,178 +565,3 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpolateGeneratorValuesHandling(t *testing.T) {
|
||||
applicationSetTemplate := argov1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: argov1alpha1.ApplicationSetTemplateMeta{
|
||||
Labels: map[string]string{},
|
||||
Annotations: map[string]string{},
|
||||
Finalizers: []string{},
|
||||
},
|
||||
Spec: argov1alpha1.ApplicationSpec{
|
||||
IgnoreDifferences: argov1alpha1.IgnoreDifferences{},
|
||||
Info: []argov1alpha1.Info{},
|
||||
Sources: argov1alpha1.ApplicationSources{},
|
||||
},
|
||||
}
|
||||
type args struct {
|
||||
requestedGenerator *argov1alpha1.ApplicationSetGenerator
|
||||
params map[string]any
|
||||
useGoTemplate bool
|
||||
goTemplateOptions []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want argov1alpha1.ApplicationSetGenerator
|
||||
expectedErrStr string
|
||||
}{
|
||||
{
|
||||
name: "Generator with values set",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{.name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
// must stay not interpolated
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
{
|
||||
name: "Generator with no values set",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{.name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
goTemplateOptions: []string{},
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
{
|
||||
name: "Generator with values set without go templates",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{
|
||||
"appname": "{{ .path.basenameNormalized }}",
|
||||
},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
{
|
||||
name: "Generator without values set and no go templates",
|
||||
args: args{
|
||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/{{name}}.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
params: map[string]any{
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: argov1alpha1.ApplicationSetGenerator{
|
||||
Git: &argov1alpha1.GitGenerator{
|
||||
RepoURL: "https://somewhere.com/per-cluster/in-cluster.git",
|
||||
Files: []argov1alpha1.GitFileGeneratorItem{{Path: "somedir/*"}},
|
||||
Revision: "main",
|
||||
Values: map[string]string{},
|
||||
Directories: []argov1alpha1.GitDirectoryGeneratorItem{},
|
||||
Template: applicationSetTemplate,
|
||||
},
|
||||
},
|
||||
expectedErrStr: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := InterpolateGenerator(tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions)
|
||||
if tt.expectedErrStr != "" {
|
||||
require.EqualError(t, err, tt.expectedErrStr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tt.want.Git.Values, got.Git.Values)
|
||||
assert.Equal(t, tt.want.Git.Revision, got.Git.Revision)
|
||||
assert.Equal(t, tt.want.Git.RepoURL, got.Git.RepoURL)
|
||||
assert.Equal(t, tt.want.Git.Files, got.Git.Files)
|
||||
assert.Equal(t, tt.want.Git.Directories, got.Git.Directories)
|
||||
assert.Equal(t, tt.want.Git.Template, got.Git.Template)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package generators
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -169,7 +168,9 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maps.Copy(fileContentMap, retrievedFiles)
|
||||
for absPath, content := range retrievedFiles {
|
||||
fileContentMap[absPath] = content
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove files matching any exclude pattern
|
||||
@@ -221,18 +222,19 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
|
||||
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{}
|
||||
|
||||
// First, we attempt to parse as a single object.
|
||||
// This will also succeed for empty files.
|
||||
singleObj := map[string]any{}
|
||||
err := yaml.Unmarshal(fileContent, &singleObj)
|
||||
if err == nil {
|
||||
objectsFound = append(objectsFound, singleObj)
|
||||
} else {
|
||||
// If unable to parse as an object, try to parse as an array
|
||||
err = yaml.Unmarshal(fileContent, &objectsFound)
|
||||
// First, we attempt to parse as an array
|
||||
err := yaml.Unmarshal(fileContent, &objectsFound)
|
||||
if err != nil {
|
||||
// If unable to parse as an array, attempt to parse as a single object
|
||||
singleObj := make(map[string]any)
|
||||
err = yaml.Unmarshal(fileContent, &singleObj)
|
||||
if err != nil {
|
||||
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{}
|
||||
@@ -241,7 +243,9 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
params := map[string]any{}
|
||||
|
||||
if useGoTemplate {
|
||||
maps.Copy(params, objectFound)
|
||||
for k, v := range objectFound {
|
||||
params[k] = v
|
||||
}
|
||||
|
||||
paramPath := map[string]any{}
|
||||
|
||||
@@ -313,7 +317,7 @@ func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryG
|
||||
appExclude = true
|
||||
}
|
||||
}
|
||||
// Whenever there is a path with exclude: true it won't be included, even if it is included in a different path pattern
|
||||
// Whenever there is a path with exclude: true it wont be included, even if it is included in a different path pattern
|
||||
if appInclude && !appExclude {
|
||||
res = append(res, appPath)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||
@@ -110,18 +111,13 @@ foo:
|
||||
{
|
||||
name: "file parameters are added to params with go template",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{
|
||||
"somekey": "{{.path.basename}}",
|
||||
},
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: true,
|
||||
},
|
||||
want: []map[string]any{
|
||||
{
|
||||
"values": map[string]string{
|
||||
"somekey": "dir",
|
||||
},
|
||||
"foo": map[string]any{
|
||||
"bar": "baz",
|
||||
},
|
||||
@@ -169,41 +165,6 @@ foo:
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path parameter are prefixed with go template and values",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{
|
||||
"somekey": "{{.myRepo.path.basename}}",
|
||||
},
|
||||
useGoTemplate: true,
|
||||
pathParamPrefix: "myRepo",
|
||||
},
|
||||
want: []map[string]any{
|
||||
{
|
||||
"foo": map[string]any{
|
||||
"bar": "baz",
|
||||
},
|
||||
"myRepo": map[string]any{
|
||||
"path": map[string]any{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"somekey": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -359,11 +320,11 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
argoCDServiceMock.EXPECT().GetDirectories(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -396,6 +357,8 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -660,11 +623,11 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
argoCDServiceMock.EXPECT().GetDirectories(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -697,6 +660,8 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -860,7 +825,7 @@ func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
},
|
||||
repoPathsError: nil,
|
||||
expected: []map[string]any{},
|
||||
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []map[string]interface {}"),
|
||||
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
|
||||
},
|
||||
{
|
||||
name: "test JSON array",
|
||||
@@ -1017,16 +982,6 @@ cluster:
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "test empty YAML array",
|
||||
files: []v1alpha1.GitFileGeneratorItem{{Path: "**/config.yaml"}},
|
||||
repoFileContents: map[string][]byte{
|
||||
"cluster-config/production/config.yaml": []byte(`[]`),
|
||||
},
|
||||
repoPathsError: nil,
|
||||
expected: []map[string]any{},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range cases {
|
||||
@@ -1035,11 +990,11 @@ cluster:
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1071,6 +1026,8 @@ cluster:
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1364,7 +1321,7 @@ env: testing
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
// IMPORTANT: we try to get the files from the repo server that matches the patterns
|
||||
// If we find those files also satisfy the exclude pattern, we remove them from map
|
||||
@@ -1372,16 +1329,18 @@ env: testing
|
||||
// With the below mock setup, we make sure that if the GetFiles() function gets called
|
||||
// for a include or exclude pattern, it should always return the includeFiles or excludeFiles.
|
||||
for _, pattern := range testCaseCopy.excludePattern {
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.
|
||||
On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.excludeFiles, testCaseCopy.repoPathsError)
|
||||
}
|
||||
|
||||
for _, pattern := range testCaseCopy.includePattern {
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.
|
||||
On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.includeFiles, testCaseCopy.repoPathsError)
|
||||
}
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1413,6 +1372,8 @@ env: testing
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1701,7 +1662,7 @@ env: testing
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
// IMPORTANT: we try to get the files from the repo server that matches the patterns
|
||||
// If we find those files also satisfy the exclude pattern, we remove them from map
|
||||
@@ -1709,16 +1670,18 @@ env: testing
|
||||
// With the below mock setup, we make sure that if the GetFiles() function gets called
|
||||
// for a include or exclude pattern, it should always return the includeFiles or excludeFiles.
|
||||
for _, pattern := range testCaseCopy.excludePattern {
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.
|
||||
On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.excludeFiles, testCaseCopy.repoPathsError)
|
||||
}
|
||||
|
||||
for _, pattern := range testCaseCopy.includePattern {
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.
|
||||
On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.includeFiles, testCaseCopy.repoPathsError)
|
||||
}
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1750,6 +1713,8 @@ env: testing
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1933,23 +1898,25 @@ func TestGitGeneratorParamsFromFilesWithExcludeOptionGoTemplate(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
// IMPORTANT: we try to get the files from the repo server that matches the patterns
|
||||
// If we find those files also satisfy the exclude pattern, we remove them from map
|
||||
// This is generally done by the g.repos.GetFiles() function.
|
||||
// With the below mock setup, we make sure that if the GetFiles() function gets called
|
||||
// for a include or exclude pattern, it should always return the includeFiles or excludeFiles.
|
||||
for _, pattern := range testCaseCopy.excludePattern {
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.
|
||||
On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.excludeFiles, testCaseCopy.repoPathsError)
|
||||
}
|
||||
|
||||
for _, pattern := range testCaseCopy.includePattern {
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.
|
||||
On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, pattern, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.includeFiles, testCaseCopy.repoPathsError)
|
||||
}
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1981,6 +1948,8 @@ func TestGitGeneratorParamsFromFilesWithExcludeOptionGoTemplate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2091,7 +2060,7 @@ func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) {
|
||||
},
|
||||
repoPathsError: nil,
|
||||
expected: []map[string]any{},
|
||||
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []map[string]interface {}"),
|
||||
expectedError: errors.New("error generating params from git: unable to process file 'cluster-config/production/config.json': unable to parse file: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"),
|
||||
},
|
||||
{
|
||||
name: "test JSON array",
|
||||
@@ -2300,11 +2269,11 @@ cluster:
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
applicationSetInfo := v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -2336,6 +2305,8 @@ cluster:
|
||||
require.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2462,7 +2433,7 @@ func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: []map[string]any{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedProject: new("project"),
|
||||
expectedProject: ptr.To("project"),
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
@@ -2496,12 +2467,12 @@ func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: []map[string]any{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedProject: new(""),
|
||||
expectedProject: ptr.To(""),
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
argoCDServiceMock := mocks.NewRepos(t)
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
if testCase.callGetDirectories {
|
||||
var project any
|
||||
@@ -2511,9 +2482,9 @@ func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
project = mock.Anything
|
||||
}
|
||||
|
||||
argoCDServiceMock.EXPECT().GetDirectories(mock.Anything, mock.Anything, mock.Anything, project, mock.Anything, mock.Anything).Return(testCase.repoApps, testCase.repoPathsError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, project, mock.Anything, mock.Anything).Return(testCase.repoApps, testCase.repoPathsError)
|
||||
}
|
||||
gitGenerator := NewGitGenerator(argoCDServiceMock, "argocd")
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "argocd")
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
@@ -2529,5 +2500,7 @@ func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testCase.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,16 @@ import (
|
||||
corev1 "k8s.io/api/core/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/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
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"
|
||||
)
|
||||
|
||||
@@ -135,7 +136,7 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
testCaseCopy := testCase // Since tests may run in parallel
|
||||
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
genMock := &generatorsMock.Generator{}
|
||||
genMock := &generatorMock{}
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -148,7 +149,7 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
Git: g.Git,
|
||||
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.basename": "app1",
|
||||
@@ -161,7 +162,7 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
},
|
||||
}, nil)
|
||||
|
||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
|
||||
@@ -342,7 +343,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
testCaseCopy := testCase // Since tests may run in parallel
|
||||
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
genMock := &generatorsMock.Generator{}
|
||||
genMock := &generatorMock{}
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -357,7 +358,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
Git: g.Git,
|
||||
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": "app1",
|
||||
@@ -374,7 +375,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
},
|
||||
}, nil)
|
||||
|
||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
|
||||
@@ -506,7 +507,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
testCaseCopy := testCase // Since tests may run in parallel
|
||||
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
mock := &generatorsMock.Generator{}
|
||||
mock := &generatorMock{}
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||
@@ -516,7 +517,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
SCMProvider: g.SCMProvider,
|
||||
ClusterDecisionResource: g.ClusterDecisionResource,
|
||||
}
|
||||
mock.EXPECT().GetRequeueAfter(&gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter)
|
||||
mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil)
|
||||
}
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
@@ -623,27 +624,33 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||
Type: corev1.SecretType("Opaque"),
|
||||
},
|
||||
}
|
||||
// convert []client.Object to []runtime.Object, for use by kubefake package
|
||||
runtimeClusters := []runtime.Object{}
|
||||
for _, clientCluster := range clusters {
|
||||
runtimeClusters = append(runtimeClusters, clientCluster)
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCaseCopy := testCase // Since tests may run in parallel
|
||||
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
genMock := &generatorsMock.Generator{}
|
||||
genMock := &generatorMock{}
|
||||
appSet := &v1alpha1.ApplicationSet{}
|
||||
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
cl := &possiblyErroringFakeCtrlRuntimeClient{
|
||||
fakeClient,
|
||||
testCase.clientError,
|
||||
}
|
||||
clusterGenerator := NewClusterGenerator(cl, "namespace")
|
||||
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
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.basename": "dev",
|
||||
@@ -655,7 +662,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||
"path.basenameNormalized": "prod",
|
||||
},
|
||||
}, nil)
|
||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
@@ -796,31 +803,37 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
Type: corev1.SecretType("Opaque"),
|
||||
},
|
||||
}
|
||||
// convert []client.Object to []runtime.Object, for use by kubefake package
|
||||
runtimeClusters := []runtime.Object{}
|
||||
for _, clientCluster := range clusters {
|
||||
runtimeClusters = append(runtimeClusters, clientCluster)
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCaseCopy := testCase // Since tests may run in parallel
|
||||
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
genMock := &generatorsMock.Generator{}
|
||||
genMock := &generatorMock{}
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
},
|
||||
}
|
||||
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
cl := &possiblyErroringFakeCtrlRuntimeClient{
|
||||
fakeClient,
|
||||
testCase.clientError,
|
||||
}
|
||||
clusterGenerator := NewClusterGenerator(cl, "namespace")
|
||||
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
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": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||
@@ -836,7 +849,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
@@ -956,7 +969,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
testCaseCopy := testCase // Since tests may run in parallel
|
||||
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
genMock := &generatorsMock.Generator{}
|
||||
genMock := &generatorMock{}
|
||||
appSet := &v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -971,7 +984,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
Git: g.Git,
|
||||
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{
|
||||
"bar": []any{
|
||||
map[string]any{
|
||||
@@ -996,7 +1009,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}}, nil)
|
||||
genMock.EXPECT().GetTemplate(&gitGeneratorSpec).
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
|
||||
@@ -1024,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) {
|
||||
// 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.
|
||||
@@ -1037,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
|
||||
// of that bug.
|
||||
|
||||
listGeneratorMock := &generatorsMock.Generator{}
|
||||
listGeneratorMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
||||
listGeneratorMock := &generatorMock{}
|
||||
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
||||
{"some": "value"},
|
||||
}, nil)
|
||||
listGeneratorMock.EXPECT().GetTemplate(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
gitGeneratorSpec := &v1alpha1.GitGenerator{
|
||||
RepoURL: "https://git.example.com",
|
||||
@@ -1050,10 +1085,10 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
repoServiceMock := &servicesMocks.Repos{}
|
||||
repoServiceMock.EXPECT().GetFiles(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
repoServiceMock := &mocks.Repos{}
|
||||
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"),
|
||||
}, nil).Maybe()
|
||||
}, nil)
|
||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||
@@ -1100,85 +1135,3 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
"test": "content",
|
||||
}}, params)
|
||||
}
|
||||
|
||||
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator_go_templates_values(t *testing.T) {
|
||||
// Given a matrix generator over a list generator and a git generator with values,
|
||||
// that contain a template that refers to got generator output parameters.
|
||||
// This tests for a specific bug where the second generator in the matrix
|
||||
// failed to evaluate value templates that referred to generator output parameters.
|
||||
|
||||
listGeneratorMock := &generatorsMock.Generator{}
|
||||
listGeneratorMock.EXPECT().GenerateParams(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
||||
{"some": "value"},
|
||||
}, nil)
|
||||
listGeneratorMock.EXPECT().GetTemplate(mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
gitGeneratorSpec := &v1alpha1.GitGenerator{
|
||||
RepoURL: "https://git.example.com",
|
||||
Files: []v1alpha1.GitFileGeneratorItem{
|
||||
{Path: "some/path.json"},
|
||||
},
|
||||
Values: map[string]string{
|
||||
"foo": "{{.path.basename}}",
|
||||
},
|
||||
}
|
||||
|
||||
repoServiceMock := &servicesMocks.Repos{}
|
||||
repoServiceMock.EXPECT().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"),
|
||||
}, nil).Maybe()
|
||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||
"List": listGeneratorMock,
|
||||
"Git": gitGenerator,
|
||||
})
|
||||
|
||||
matrixGeneratorSpec := &v1alpha1.MatrixGenerator{
|
||||
Generators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
List: &v1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{
|
||||
{
|
||||
Raw: []byte(`{"some": "value"}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Git: gitGeneratorSpec,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := v1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
params, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||
Matrix: matrixGeneratorSpec,
|
||||
}, &v1alpha1.ApplicationSet{
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
},
|
||||
}, client)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []map[string]any{{
|
||||
"path": map[string]any{
|
||||
"basename": "some",
|
||||
"basenameNormalized": "some",
|
||||
"filename": "path.json",
|
||||
"filenameNormalized": "path.json",
|
||||
"path": "some",
|
||||
"segments": []string{"some"},
|
||||
},
|
||||
"some": "value",
|
||||
"test": "content",
|
||||
"values": map[string]string{
|
||||
"foo": "some",
|
||||
},
|
||||
}}, params)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -116,7 +115,9 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
params := map[string]any{}
|
||||
|
||||
if useGoTemplate {
|
||||
maps.Copy(params, objectFound)
|
||||
for k, v := range objectFound {
|
||||
params[k] = v
|
||||
}
|
||||
} else {
|
||||
flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"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"
|
||||
@@ -19,6 +18,8 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
var _ Generator = (*PullRequestGenerator)(nil)
|
||||
|
||||
const (
|
||||
DefaultPullRequestRequeueAfter = 30 * time.Minute
|
||||
)
|
||||
@@ -48,10 +49,6 @@ func (g *PullRequestGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alph
|
||||
return DefaultPullRequestRequeueAfter
|
||||
}
|
||||
|
||||
func (g *PullRequestGenerator) GetContinueOnRepoNotFoundError(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) bool {
|
||||
return appSetGenerator.PullRequest.ContinueOnRepoNotFoundError
|
||||
}
|
||||
|
||||
func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||
return &appSetGenerator.PullRequest.Template
|
||||
}
|
||||
@@ -72,15 +69,10 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
}
|
||||
|
||||
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
||||
params := make([]map[string]any, 0, len(pulls))
|
||||
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)
|
||||
}
|
||||
params := make([]map[string]any, 0, len(pulls))
|
||||
|
||||
// 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
|
||||
@@ -96,12 +88,18 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
var shortSHALength int
|
||||
var shortSHALength7 int
|
||||
for _, pull := range pulls {
|
||||
shortSHALength = min(len(pull.HeadSHA), 8)
|
||||
shortSHALength = 8
|
||||
if len(pull.HeadSHA) < 8 {
|
||||
shortSHALength = len(pull.HeadSHA)
|
||||
}
|
||||
|
||||
shortSHALength7 = min(len(pull.HeadSHA), 7)
|
||||
shortSHALength7 = 7
|
||||
if len(pull.HeadSHA) < 7 {
|
||||
shortSHALength7 = len(pull.HeadSHA)
|
||||
}
|
||||
|
||||
paramMap := map[string]any{
|
||||
"number": strconv.FormatInt(pull.Number, 10),
|
||||
"number": strconv.Itoa(pull.Number),
|
||||
"title": pull.Title,
|
||||
"branch": pull.Branch,
|
||||
"branch_slug": slug.Make(pull.Branch),
|
||||
@@ -113,15 +111,15 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
"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)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
return params, nil
|
||||
@@ -237,9 +235,9 @@ func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alph
|
||||
}
|
||||
|
||||
if g.enableGitHubAPIMetrics {
|
||||
return pullrequest.NewGithubAppService(ctx, *auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels, httpClient)
|
||||
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels, httpClient)
|
||||
}
|
||||
return pullrequest.NewGithubAppService(ctx, *auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||
}
|
||||
|
||||
// always default to token, even if not set (public access)
|
||||
|
||||
@@ -16,12 +16,11 @@ import (
|
||||
func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
cases := []struct {
|
||||
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
||||
values map[string]string
|
||||
expected []map[string]any
|
||||
expectedErr error
|
||||
applicationSet argoprojiov1alpha1.ApplicationSet
|
||||
continueOnRepoNotFoundError bool
|
||||
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
||||
values map[string]string
|
||||
expected []map[string]any
|
||||
expectedErr error
|
||||
applicationSet argoprojiov1alpha1.ApplicationSet
|
||||
}{
|
||||
{
|
||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||
@@ -172,30 +171,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
expected: nil,
|
||||
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) {
|
||||
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 {
|
||||
@@ -330,8 +260,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
}
|
||||
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
|
||||
Values: c.values,
|
||||
ContinueOnRepoNotFoundError: c.continueOnRepoNotFoundError,
|
||||
Values: c.values,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -106,8 +105,10 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
|
||||
return nil
|
||||
}
|
||||
|
||||
if slices.Contains(allowedScmProviders, url) {
|
||||
return nil
|
||||
for _, allowedScmProvider := range allowedScmProviders {
|
||||
if url == allowedScmProvider {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
@@ -243,9 +244,15 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
var shortSHALength int
|
||||
var shortSHALength7 int
|
||||
for _, repo := range repos {
|
||||
shortSHALength = min(len(repo.SHA), 8)
|
||||
shortSHALength = 8
|
||||
if len(repo.SHA) < 8 {
|
||||
shortSHALength = len(repo.SHA)
|
||||
}
|
||||
|
||||
shortSHALength7 = min(len(repo.SHA), 7)
|
||||
shortSHALength7 = 7
|
||||
if len(repo.SHA) < 7 {
|
||||
shortSHALength7 = len(repo.SHA)
|
||||
}
|
||||
|
||||
params := map[string]any{
|
||||
"organization": repo.Organization,
|
||||
@@ -289,9 +296,9 @@ func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argop
|
||||
}
|
||||
|
||||
if g.enableGitHubAPIMetrics {
|
||||
return scm_provider.NewGithubAppProviderFor(ctx, *auth, github.Organization, github.API, github.AllBranches, httpClient)
|
||||
return scm_provider.NewGithubAppProviderFor(*auth, github.Organization, github.API, github.AllBranches, httpClient)
|
||||
}
|
||||
return scm_provider.NewGithubAppProviderFor(ctx, *auth, github.Organization, github.API, github.AllBranches)
|
||||
return scm_provider.NewGithubAppProviderFor(*auth, github.Organization, github.API, github.AllBranches)
|
||||
}
|
||||
|
||||
token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
|
||||
@@ -8,16 +8,15 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||
)
|
||||
|
||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig, clusterInformer *settings.ClusterInformer) map[string]Generator {
|
||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
||||
terminalGenerators := map[string]Generator{
|
||||
"List": NewListGenerator(),
|
||||
"Clusters": NewClusterGenerator(c, controllerNamespace),
|
||||
"Clusters": NewClusterGenerator(ctx, c, k8sClient, controllerNamespace),
|
||||
"Git": NewGitGenerator(argoCDService, controllerNamespace),
|
||||
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace, clusterInformer),
|
||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace),
|
||||
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
||||
"Plugin": NewPluginGenerator(c, controllerNamespace),
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package generators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
)
|
||||
|
||||
func appendTemplatedValues(values map[string]string, params map[string]any, useGoTemplate bool, goTemplateOptions []string) error {
|
||||
@@ -27,7 +26,9 @@ func appendTemplatedValues(values map[string]string, params map[string]any, useG
|
||||
}
|
||||
}
|
||||
|
||||
maps.Copy(params, tmp)
|
||||
for key, value := range tmp {
|
||||
params[key] = value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -151,9 +151,9 @@ spec:
|
||||
func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet {
|
||||
var results []argoappv1.ApplicationSet
|
||||
|
||||
appsetRawYamls := strings.SplitSeq(fakeAppsetYAML, "---")
|
||||
appsetRawYamls := strings.Split(fakeAppsetYAML, "---")
|
||||
|
||||
for appsetRawYaml := range appsetRawYamls {
|
||||
for _, appsetRawYaml := range appsetRawYamls {
|
||||
var appset argoappv1.ApplicationSet
|
||||
err := yaml.Unmarshal([]byte(appsetRawYaml), &appset)
|
||||
if err != nil {
|
||||
@@ -174,7 +174,7 @@ func TestApplicationsetCollector(t *testing.T) {
|
||||
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||
|
||||
metrics.Registry.MustRegister(appsetCollector)
|
||||
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "/metrics", http.NoBody)
|
||||
req, err := http.NewRequest(http.MethodGet, "/metrics", http.NoBody)
|
||||
require.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||
@@ -216,7 +216,7 @@ func TestObserveReconcile(t *testing.T) {
|
||||
|
||||
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", http.NoBody)
|
||||
require.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||
|
||||
@@ -97,9 +97,7 @@ func TestGitHubMetrics_CollectorApproach_Success(t *testing.T) {
|
||||
),
|
||||
}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL+URL, http.NoBody)
|
||||
req, _ := http.NewRequest(http.MethodGet, ts.URL+URL, http.NoBody)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
@@ -111,11 +109,7 @@ func TestGitHubMetrics_CollectorApproach_Success(t *testing.T) {
|
||||
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)
|
||||
resp, err = http.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to scrape metrics: %v", err)
|
||||
}
|
||||
@@ -157,23 +151,15 @@ func TestGitHubMetrics_CollectorApproach_NoRateLimitMetricsOnNilResponse(t *test
|
||||
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)
|
||||
}
|
||||
req, _ := http.NewRequest(http.MethodGet, URL, http.NoBody)
|
||||
_, _ = 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)
|
||||
resp, err := http.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to scrape metrics: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package github_app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -10,65 +8,40 @@ import (
|
||||
"github.com/google/go-github/v69/github"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||
"github.com/argoproj/argo-cd/v3/util/git"
|
||||
appsetutils "github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
)
|
||||
|
||||
// getInstallationClient creates a new GitHub client with the specified installation ID.
|
||||
// It also returns a ghinstallation.Transport, which can be used for git requests.
|
||||
func getInstallationClient(g github_app_auth.Authentication, url string, httpClient ...*http.Client) (*github.Client, error) {
|
||||
if g.InstallationId <= 0 {
|
||||
return nil, errors.New("installation ID is required for github")
|
||||
func getOptionalHTTPClientAndTransport(optionalHTTPClient ...*http.Client) (*http.Client, http.RoundTripper) {
|
||||
httpClient := appsetutils.GetOptionalHTTPClient(optionalHTTPClient...)
|
||||
if len(optionalHTTPClient) > 0 && optionalHTTPClient[0] != nil && optionalHTTPClient[0].Transport != nil {
|
||||
// will either use the provided custom httpClient and it's transport
|
||||
return httpClient, optionalHTTPClient[0].Transport
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, fmt.Errorf("failed to create GitHub installation transport: %w", err)
|
||||
}
|
||||
|
||||
if url == "" {
|
||||
url = g.EnterpriseBaseURL
|
||||
}
|
||||
|
||||
var client *github.Client
|
||||
if url == "" {
|
||||
client = github.NewClient(&http.Client{Transport: itr})
|
||||
return client, nil
|
||||
}
|
||||
|
||||
itr.BaseURL = url
|
||||
client, err = github.NewClient(&http.Client{Transport: itr}).WithEnterpriseURLs(url, url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GitHub enterprise client: %w", err)
|
||||
}
|
||||
return client, nil
|
||||
// or the default httpClient and transport
|
||||
return httpClient, http.DefaultTransport
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func Client(g github_app_auth.Authentication, url string, optionalHTTPClient ...*http.Client) (*github.Client, error) {
|
||||
httpClient, transport := getOptionalHTTPClientAndTransport(optionalHTTPClient...)
|
||||
|
||||
rt, err := ghinstallation.New(transport, g.Id, g.InstallationId, []byte(g.PrivateKey))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create github app install: %w", err)
|
||||
}
|
||||
if url == "" {
|
||||
url = g.EnterpriseBaseURL
|
||||
}
|
||||
|
||||
// If an installation ID is already provided, use it directly.
|
||||
if g.InstallationId != 0 {
|
||||
return getInstallationClient(g, url, optionalHTTPClient...)
|
||||
var client *github.Client
|
||||
httpClient.Transport = rt
|
||||
if url == "" {
|
||||
client = github.NewClient(httpClient)
|
||||
} else {
|
||||
rt.BaseURL = url
|
||||
client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create github enterprise client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 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...)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package pull_request
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
|
||||
@@ -11,10 +10,7 @@ import (
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
||||
)
|
||||
|
||||
const (
|
||||
AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
|
||||
AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR = "The following project does not exist"
|
||||
)
|
||||
const AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
|
||||
|
||||
type AzureDevOpsClientFactory interface {
|
||||
// Returns an Azure Devops Client interface.
|
||||
@@ -74,22 +70,13 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||
}
|
||||
|
||||
pullRequests := []*PullRequest{}
|
||||
|
||||
azurePullRequests, err := client.GetPullRequestsByProject(ctx, args)
|
||||
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)
|
||||
}
|
||||
|
||||
pullRequests := []*PullRequest{}
|
||||
|
||||
for _, pr := range *azurePullRequests {
|
||||
if pr.Repository == nil ||
|
||||
pr.Repository.Name == nil ||
|
||||
@@ -108,7 +95,7 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
|
||||
if *pr.Repository.Name == a.repo {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: int64(*pr.PullRequestId),
|
||||
Number: *pr.PullRequestId,
|
||||
Title: *pr.Title,
|
||||
Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1),
|
||||
TargetBranch: strings.Replace(*pr.TargetRefName, "refs/heads/", "", 1),
|
||||
@@ -137,7 +124,13 @@ func convertLabels(tags *[]core.WebApiTagDefinition) []string {
|
||||
// containAzureDevOpsLabels returns true if gotLabels contains expectedLabels
|
||||
func containAzureDevOpsLabels(expectedLabels []string, gotLabels []string) bool {
|
||||
for _, expected := range expectedLabels {
|
||||
found := slices.Contains(gotLabels, expected)
|
||||
found := false
|
||||
for _, got := range gotLabels {
|
||||
if expected == got {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package pull_request
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/core"
|
||||
@@ -12,9 +12,51 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
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 {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createStringPtr(x string) *string {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createIntPtr(x int) *int {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createLabelsPtr(x []core.WebApiTagDefinition) *[]core.WebApiTagDefinition {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createUniqueNamePtr(x string) *string {
|
||||
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) {
|
||||
teamProject := "myorg_project"
|
||||
repoName := "myorg_project_repo"
|
||||
@@ -26,19 +68,19 @@ func TestListPullRequest(t *testing.T) {
|
||||
|
||||
pullRequestMock := []git.GitPullRequest{
|
||||
{
|
||||
PullRequestId: new(prID),
|
||||
Title: new(prTitle),
|
||||
SourceRefName: new("refs/heads/feature-branch"),
|
||||
TargetRefName: new("refs/heads/main"),
|
||||
PullRequestId: createIntPtr(prID),
|
||||
Title: createStringPtr(prTitle),
|
||||
SourceRefName: createStringPtr("refs/heads/feature-branch"),
|
||||
TargetRefName: createStringPtr("refs/heads/main"),
|
||||
LastMergeSourceCommit: &git.GitCommitRef{
|
||||
CommitId: new(prHeadSha),
|
||||
CommitId: createStringPtr(prHeadSha),
|
||||
},
|
||||
Labels: &[]core.WebApiTagDefinition{},
|
||||
Repository: &git.GitRepository{
|
||||
Name: new(repoName),
|
||||
Name: createStringPtr(repoName),
|
||||
},
|
||||
CreatedBy: &webapi.IdentityRef{
|
||||
UniqueName: new(uniqueName + "@example.com"),
|
||||
UniqueName: createUniqueNamePtr(uniqueName + "@example.com"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -48,10 +90,10 @@ func TestListPullRequest(t *testing.T) {
|
||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||
}
|
||||
|
||||
gitClientMock := &azureMock.Client{}
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
||||
gitClientMock.EXPECT().GetPullRequestsByProject(mock.Anything, args).Return(&pullRequestMock, nil)
|
||||
gitClientMock := azureMock.Client{}
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
|
||||
gitClientMock.On("GetPullRequestsByProject", ctx, args).Return(&pullRequestMock, nil)
|
||||
|
||||
provider := AzureDevOpsService{
|
||||
clientFactory: clientFactoryMock,
|
||||
@@ -67,7 +109,7 @@ func TestListPullRequest(t *testing.T) {
|
||||
assert.Equal(t, "main", list[0].TargetBranch)
|
||||
assert.Equal(t, prHeadSha, list[0].HeadSHA)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -79,27 +121,27 @@ func TestConvertLabes(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "empty labels",
|
||||
gotLabels: &[]core.WebApiTagDefinition{},
|
||||
gotLabels: createLabelsPtr([]core.WebApiTagDefinition{}),
|
||||
expectedLabels: []string{},
|
||||
},
|
||||
{
|
||||
name: "nil labels",
|
||||
gotLabels: nil,
|
||||
gotLabels: createLabelsPtr(nil),
|
||||
expectedLabels: []string{},
|
||||
},
|
||||
{
|
||||
name: "one label",
|
||||
gotLabels: &[]core.WebApiTagDefinition{
|
||||
{Name: new("label1"), Active: new(true)},
|
||||
},
|
||||
gotLabels: createLabelsPtr([]core.WebApiTagDefinition{
|
||||
{Name: createStringPtr("label1"), Active: createBoolPtr(true)},
|
||||
}),
|
||||
expectedLabels: []string{"label1"},
|
||||
},
|
||||
{
|
||||
name: "two label",
|
||||
gotLabels: &[]core.WebApiTagDefinition{
|
||||
{Name: new("label1"), Active: new(true)},
|
||||
{Name: new("label2"), Active: new(true)},
|
||||
},
|
||||
gotLabels: createLabelsPtr([]core.WebApiTagDefinition{
|
||||
{Name: createStringPtr("label1"), Active: createBoolPtr(true)},
|
||||
{Name: createStringPtr("label2"), Active: createBoolPtr(true)},
|
||||
}),
|
||||
expectedLabels: []string{"label1", "label2"},
|
||||
},
|
||||
}
|
||||
@@ -193,36 +235,3 @@ func TestBuildURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAzureDevOpsListReturnsRepositoryNotFoundError(t *testing.T) {
|
||||
args := git.GetPullRequestsByProjectArgs{
|
||||
Project: new("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"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/ktrysmt/go-bitbucket"
|
||||
)
|
||||
@@ -81,10 +80,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)
|
||||
}
|
||||
|
||||
bitbucketClient, err := bitbucket.NewBasicAuth(username, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating BitBucket Cloud client with basic auth: %w", err)
|
||||
}
|
||||
bitbucketClient := bitbucket.NewBasicAuth(username, password)
|
||||
bitbucketClient.SetApiBaseURL(*url)
|
||||
|
||||
return &BitbucketCloudService{
|
||||
@@ -100,13 +96,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)
|
||||
}
|
||||
|
||||
bitbucketClient, err := bitbucket.NewOAuthbearerToken(bearerToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating BitBucket Cloud client with oauth bearer token: %w", err)
|
||||
}
|
||||
bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken)
|
||||
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) {
|
||||
@@ -120,17 +117,8 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
||||
RepoSlug: b.repositorySlug,
|
||||
}
|
||||
|
||||
pullRequests := []*PullRequest{}
|
||||
|
||||
response, err := b.client.Repositories.PullRequests.Gets(opts)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -154,9 +142,10 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
||||
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
|
||||
}
|
||||
|
||||
pullRequests := []*PullRequest{}
|
||||
for _, pull := range pulls {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: int64(pull.ID),
|
||||
Number: pull.ID,
|
||||
Title: pull.Title,
|
||||
Branch: pull.Source.Branch.Name,
|
||||
TargetBranch: pull.Destination.Branch.Name,
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestListPullRequestBearerTokenCloud(t *testing.T) {
|
||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
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, "feature/foo-bar", pullRequests[0].Branch)
|
||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||
@@ -107,7 +107,7 @@ func TestListPullRequestNoAuthCloud(t *testing.T) {
|
||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
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, "feature/foo-bar", pullRequests[0].Branch)
|
||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||
@@ -125,7 +125,7 @@ func TestListPullRequestBasicAuthCloud(t *testing.T) {
|
||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
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, "feature/foo-bar", pullRequests[0].Branch)
|
||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||
@@ -492,29 +492,3 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package pull_request
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -67,11 +66,6 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
|
||||
for {
|
||||
response, err := b.client.DefaultApi.GetPullRequestsPage(b.projectKey, b.repositorySlug, paged)
|
||||
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)
|
||||
}
|
||||
pulls, err := bitbucketv1.GetPullRequestsResponse(response)
|
||||
@@ -82,7 +76,7 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
|
||||
|
||||
for _, pull := range pulls {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: int64(pull.ID),
|
||||
Number: pull.ID,
|
||||
Title: pull.Title,
|
||||
Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main
|
||||
TargetBranch: pull.ToRef.DisplayID,
|
||||
|
||||
@@ -68,7 +68,7 @@ func TestListPullRequestNoAuth(t *testing.T) {
|
||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
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, "feature-ABC-123", pullRequests[0].Branch)
|
||||
assert.Equal(t, "master", pullRequests[0].TargetBranch)
|
||||
@@ -211,7 +211,7 @@ func TestListPullRequestBasicAuth(t *testing.T) {
|
||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
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, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func TestListPullRequestBearerAuth(t *testing.T) {
|
||||
pullRequests, err := ListPullRequests(t.Context(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
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, "feature-ABC-123", pullRequests[0].Branch)
|
||||
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||
@@ -268,6 +268,7 @@ func TestListPullRequestTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defaultHandler(t)(w, r)
|
||||
@@ -509,29 +510,3 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
})
|
||||
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))
|
||||
})
|
||||
}
|
||||
@@ -52,23 +52,17 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
State: gitea.StateOpen,
|
||||
}
|
||||
g.client.SetContext(ctx)
|
||||
list := []*PullRequest{}
|
||||
prs, resp, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
|
||||
prs, _, err := g.client.ListRepoPullRequests(g.owner, g.repo, opts)
|
||||
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
|
||||
}
|
||||
|
||||
list := []*PullRequest{}
|
||||
for _, pr := range prs {
|
||||
if !giteaContainLabels(g.labels, pr.Labels) {
|
||||
continue
|
||||
}
|
||||
list = append(list, &PullRequest{
|
||||
Number: int64(pr.Index),
|
||||
Number: int(pr.Index),
|
||||
Title: pr.Title,
|
||||
Branch: pr.Head.Ref,
|
||||
TargetBranch: pr.Base.Ref,
|
||||
@@ -83,7 +77,7 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
// containLabels returns true if gotLabels contains expectedLabels
|
||||
func giteaContainLabels(expectedLabels []string, gotLabels []*gitea.Label) bool {
|
||||
gotLabelNamesMap := make(map[string]bool)
|
||||
for i := range gotLabels {
|
||||
for i := 0; i < len(gotLabels); i++ {
|
||||
gotLabelNamesMap[gotLabels[i].Name] = true
|
||||
}
|
||||
for _, expected := range expectedLabels {
|
||||
|
||||
@@ -303,7 +303,7 @@ func TestGiteaList(t *testing.T) {
|
||||
prs, err := host.List(t.Context())
|
||||
require.NoError(t, err)
|
||||
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, "test", prs[0].Branch)
|
||||
assert.Equal(t, "main", prs[0].TargetBranch)
|
||||
@@ -339,35 +339,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)
|
||||
}
|
||||
|
||||
@@ -64,11 +64,6 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
for {
|
||||
pulls, resp, err := g.client.PullRequests.List(ctx, g.owner, g.repo, opts)
|
||||
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)
|
||||
}
|
||||
for _, pull := range pulls {
|
||||
@@ -76,7 +71,7 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
continue
|
||||
}
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: int64(*pull.Number),
|
||||
Number: *pull.Number,
|
||||
Title: *pull.Title,
|
||||
Branch: *pull.Head.Ref,
|
||||
TargetBranch: *pull.Base.Ref,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package pull_request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||
@@ -9,9 +8,9 @@ import (
|
||||
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, optionalHTTPClient ...*http.Client) (PullRequestService, error) {
|
||||
httpClient := appsetutils.GetOptionalHTTPClient(optionalHTTPClient...)
|
||||
client, err := github_app.Client(ctx, g, url, owner, httpClient)
|
||||
client, err := github_app.Client(g, url, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package pull_request
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v69/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func toPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestContainLabels(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
@@ -21,9 +22,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "Match labels",
|
||||
Labels: []string{"label1", "label2"},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
@@ -31,9 +32,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "Not match labels",
|
||||
Labels: []string{"label1", "label4"},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
},
|
||||
Expect: false,
|
||||
},
|
||||
@@ -41,9 +42,9 @@ func TestContainLabels(t *testing.T) {
|
||||
Name: "No specify",
|
||||
Labels: []string{},
|
||||
PullLabels: []*github.Label{
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
},
|
||||
Expect: true,
|
||||
},
|
||||
@@ -66,9 +67,9 @@ func TestGetGitHubPRLabelNames(t *testing.T) {
|
||||
{
|
||||
Name: "PR has labels",
|
||||
PullLabels: []*github.Label{
|
||||
{Name: new("label1")},
|
||||
{Name: new("label2")},
|
||||
{Name: new("label3")},
|
||||
{Name: toPtr("label1")},
|
||||
{Name: toPtr("label2")},
|
||||
{Name: toPtr("label3")},
|
||||
},
|
||||
ExpectedResult: []string{"label1", "label2", "label3"},
|
||||
},
|
||||
@@ -85,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
|
||||
labels = &labelsList
|
||||
}
|
||||
|
||||
snippetsListOptions := gitlab.ExploreSnippetsOptions{
|
||||
opts := &gitlab.ListProjectMergeRequestsOptions{
|
||||
ListOptions: gitlab.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
opts := &gitlab.ListProjectMergeRequestsOptions{
|
||||
ListOptions: snippetsListOptions.ListOptions,
|
||||
Labels: labels,
|
||||
Labels: labels,
|
||||
}
|
||||
|
||||
if g.pullRequestState != "" {
|
||||
@@ -80,11 +76,6 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
for {
|
||||
mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts, gitlab.WithContext(ctx))
|
||||
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)
|
||||
}
|
||||
for _, mr := range mrs {
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestList(t *testing.T) {
|
||||
prs, err := svc.List(t.Context())
|
||||
require.NoError(t, err)
|
||||
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, "use-structured-logging-for-db-load-balancer", prs[0].Branch)
|
||||
assert.Equal(t, "master", prs[0].TargetBranch)
|
||||
@@ -158,6 +158,7 @@ func TestListWithStateTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
writeMRListResponse(t, w)
|
||||
@@ -190,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 {
|
||||
// Number is a number that will be the ID of the pull request.
|
||||
// Gitlab uses int64 for the pull request number.
|
||||
Number int64
|
||||
Number int
|
||||
// Title of the pull request.
|
||||
Title string
|
||||
// Branch is the name of the branch from which the pull request originated.
|
||||
@@ -31,5 +30,4 @@ type PullRequestService interface {
|
||||
type Filter struct {
|
||||
BranchMatch *regexp.Regexp
|
||||
TargetBranchMatch *regexp.Regexp
|
||||
TitleMatch *regexp.Regexp
|
||||
}
|
||||
|
||||
@@ -25,12 +25,6 @@ func compileFilters(filters []argoprojiov1alpha1.PullRequestGeneratorFilter) ([]
|
||||
return nil, fmt.Errorf("error compiling TargetBranchMatch regexp %q: %w", *filter.TargetBranchMatch, err)
|
||||
}
|
||||
}
|
||||
if filter.TitleMatch != nil {
|
||||
outFilter.TitleMatch, err = regexp.Compile(*filter.TitleMatch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error compiling TitleMatch regexp %q: %w", *filter.TitleMatch, err)
|
||||
}
|
||||
}
|
||||
outFilters = append(outFilters, outFilter)
|
||||
}
|
||||
return outFilters, nil
|
||||
@@ -43,9 +37,6 @@ func matchFilter(pullRequest *PullRequest, filter *Filter) bool {
|
||||
if filter.TargetBranchMatch != nil && !filter.TargetBranchMatch.MatchString(pullRequest.TargetBranch) {
|
||||
return false
|
||||
}
|
||||
if filter.TitleMatch != nil && !filter.TitleMatch.MatchString(pullRequest.Title) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func strp(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestFilterBranchMatchBadRegexp(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
t.Context(),
|
||||
@@ -26,7 +30,7 @@ func TestFilterBranchMatchBadRegexp(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: new("("),
|
||||
BranchMatch: strp("("),
|
||||
},
|
||||
}
|
||||
_, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -74,7 +78,7 @@ func TestFilterBranchMatch(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: new("w"),
|
||||
BranchMatch: strp("w"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -124,7 +128,7 @@ func TestFilterTargetBranchMatch(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
TargetBranchMatch: new("1"),
|
||||
TargetBranchMatch: strp("1"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -133,110 +137,6 @@ func TestFilterTargetBranchMatch(t *testing.T) {
|
||||
assert.Equal(t, "two", pullRequests[0].Branch)
|
||||
}
|
||||
|
||||
func TestFilterTitleMatch(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
t.Context(),
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one - filter",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two - ignore",
|
||||
Branch: "two",
|
||||
TargetBranch: "branch1",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
Title: "[filter] PR three",
|
||||
Branch: "three",
|
||||
TargetBranch: "branch2",
|
||||
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name3",
|
||||
},
|
||||
{
|
||||
Number: 4,
|
||||
Title: "[ignore] PR four",
|
||||
Branch: "four",
|
||||
TargetBranch: "branch3",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
TitleMatch: new("\\[filter]"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.Equal(t, "three", pullRequests[0].Branch)
|
||||
}
|
||||
|
||||
func TestMultiFilterOrWithTitle(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
t.Context(),
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one - filter",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two - ignore",
|
||||
Branch: "two",
|
||||
TargetBranch: "branch1",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
Title: "[filter] PR three",
|
||||
Branch: "three",
|
||||
TargetBranch: "branch2",
|
||||
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name3",
|
||||
},
|
||||
{
|
||||
Number: 4,
|
||||
Title: "[ignore] PR four",
|
||||
Branch: "four",
|
||||
TargetBranch: "branch3",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
TitleMatch: new("\\[filter]"),
|
||||
},
|
||||
{
|
||||
TitleMatch: new("- filter"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 2)
|
||||
assert.Equal(t, "one", pullRequests[0].Branch)
|
||||
assert.Equal(t, "three", pullRequests[1].Branch)
|
||||
}
|
||||
|
||||
func TestMultiFilterOr(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
t.Context(),
|
||||
@@ -278,10 +178,10 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: new("w"),
|
||||
BranchMatch: strp("w"),
|
||||
},
|
||||
{
|
||||
BranchMatch: new("r"),
|
||||
BranchMatch: strp("r"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
@@ -292,7 +192,7 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
assert.Equal(t, "four", pullRequests[2].Branch)
|
||||
}
|
||||
|
||||
func TestMultiFilterOrWithTargetBranchFilterOrWithTitleFilter(t *testing.T) {
|
||||
func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
t.Context(),
|
||||
[]*PullRequest{
|
||||
@@ -328,41 +228,24 @@ func TestMultiFilterOrWithTargetBranchFilterOrWithTitleFilter(t *testing.T) {
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
{
|
||||
Number: 5,
|
||||
Title: "PR title is different than branch name",
|
||||
Branch: "five",
|
||||
TargetBranch: "branch3",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name5",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: new("w"),
|
||||
TargetBranchMatch: new("1"),
|
||||
BranchMatch: strp("w"),
|
||||
TargetBranchMatch: strp("1"),
|
||||
},
|
||||
{
|
||||
BranchMatch: new("r"),
|
||||
TargetBranchMatch: new("3"),
|
||||
},
|
||||
{
|
||||
TitleMatch: new("two"),
|
||||
},
|
||||
{
|
||||
BranchMatch: new("five"),
|
||||
TitleMatch: new("PR title is different than branch name"),
|
||||
BranchMatch: strp("r"),
|
||||
TargetBranchMatch: strp("3"),
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(t.Context(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 3)
|
||||
assert.Len(t, pullRequests, 2)
|
||||
assert.Equal(t, "two", pullRequests[0].Branch)
|
||||
assert.Equal(t, "four", pullRequests[1].Branch)
|
||||
assert.Equal(t, "five", pullRequests[2].Branch)
|
||||
assert.Equal(t, "PR title is different than branch name", pullRequests[2].Title)
|
||||
}
|
||||
|
||||
func TestNoFilters(t *testing.T) {
|
||||
|
||||
@@ -10,19 +10,16 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
rgsatypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
codecommittypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/arn"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -35,16 +32,16 @@ const (
|
||||
// AWSCodeCommitClient is a lean facade to the codecommitiface.CodeCommitAPI
|
||||
// it helps to reduce the mockery generated code.
|
||||
type AWSCodeCommitClient interface {
|
||||
ListRepositories(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)
|
||||
GetRepository(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error)
|
||||
ListBranches(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error)
|
||||
GetFolder(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error)
|
||||
ListRepositoriesWithContext(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)
|
||||
GetRepositoryWithContext(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)
|
||||
ListBranchesWithContext(aws.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)
|
||||
GetFolderWithContext(aws.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)
|
||||
}
|
||||
|
||||
// AWSTaggingClient is a lean facade to the resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI
|
||||
// it helps to reduce the mockery generated code.
|
||||
type AWSTaggingClient interface {
|
||||
GetResources(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)
|
||||
GetResourcesWithContext(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)
|
||||
}
|
||||
|
||||
type AWSCodeCommitProvider struct {
|
||||
@@ -76,7 +73,7 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
}
|
||||
|
||||
for _, repoName := range repoNames {
|
||||
repo, err := p.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{
|
||||
repo, err := p.codeCommitClient.GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{
|
||||
RepositoryName: aws.String(repoName),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -88,7 +85,7 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
log.Warnf("codecommit returned invalid response for repository %s, skipped", repoName)
|
||||
continue
|
||||
}
|
||||
if aws.ToString(repo.RepositoryMetadata.DefaultBranch) == "" {
|
||||
if aws.StringValue(repo.RepositoryMetadata.DefaultBranch) == "" {
|
||||
// if a codecommit repo doesn't have default branch, it's uninitialized. not going to bother with it.
|
||||
log.Warnf("repository %s does not have default branch, skipped", repoName)
|
||||
continue
|
||||
@@ -97,11 +94,11 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
switch cloneProtocol {
|
||||
// default to SSH if unspecified (i.e. if "").
|
||||
case "", "ssh":
|
||||
url = aws.ToString(repo.RepositoryMetadata.CloneUrlSsh)
|
||||
url = aws.StringValue(repo.RepositoryMetadata.CloneUrlSsh)
|
||||
case "https":
|
||||
url = aws.ToString(repo.RepositoryMetadata.CloneUrlHttp)
|
||||
url = aws.StringValue(repo.RepositoryMetadata.CloneUrlHttp)
|
||||
case "https-fips":
|
||||
url, err = getCodeCommitFIPSEndpoint(aws.ToString(repo.RepositoryMetadata.CloneUrlHttp))
|
||||
url, err = getCodeCommitFIPSEndpoint(aws.StringValue(repo.RepositoryMetadata.CloneUrlHttp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("https-fips is provided but repoUrl can't be transformed to FIPS endpoint: %w", err)
|
||||
}
|
||||
@@ -111,13 +108,13 @@ func (p *AWSCodeCommitProvider) ListRepos(ctx context.Context, cloneProtocol str
|
||||
repos = append(repos, &Repository{
|
||||
// there's no "organization" level at codecommit.
|
||||
// we are just using AWS accountId for now.
|
||||
Organization: aws.ToString(repo.RepositoryMetadata.AccountId),
|
||||
Repository: aws.ToString(repo.RepositoryMetadata.RepositoryName),
|
||||
Organization: aws.StringValue(repo.RepositoryMetadata.AccountId),
|
||||
Repository: aws.StringValue(repo.RepositoryMetadata.RepositoryName),
|
||||
URL: url,
|
||||
Branch: aws.ToString(repo.RepositoryMetadata.DefaultBranch),
|
||||
Branch: aws.StringValue(repo.RepositoryMetadata.DefaultBranch),
|
||||
// we could propagate repo tag keys, but without value not sure if it's any useful.
|
||||
Labels: []string{},
|
||||
RepositoryId: aws.ToString(repo.RepositoryMetadata.RepositoryId),
|
||||
RepositoryId: aws.StringValue(repo.RepositoryMetadata.RepositoryId),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -145,9 +142,13 @@ func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repositor
|
||||
FolderPath: aws.String(parentPath),
|
||||
RepositoryName: aws.String(repo.Repository),
|
||||
}
|
||||
output, err := p.codeCommitClient.GetFolder(ctx, input)
|
||||
output, err := p.codeCommitClient.GetFolderWithContext(ctx, input)
|
||||
if err != nil {
|
||||
if hasAwsError(err) {
|
||||
if hasAwsError(err,
|
||||
codecommit.ErrCodeRepositoryDoesNotExistException,
|
||||
codecommit.ErrCodeCommitDoesNotExistException,
|
||||
codecommit.ErrCodeFolderDoesNotExistException,
|
||||
) {
|
||||
return false, nil
|
||||
}
|
||||
// unhandled exception, propagate out
|
||||
@@ -156,22 +157,22 @@ func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repositor
|
||||
|
||||
// anything that matches.
|
||||
for _, submodule := range output.SubModules {
|
||||
if basePath == aws.ToString(submodule.RelativePath) {
|
||||
if basePath == aws.StringValue(submodule.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, subpath := range output.SubFolders {
|
||||
if basePath == aws.ToString(subpath.RelativePath) {
|
||||
if basePath == aws.StringValue(subpath.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, subpath := range output.Files {
|
||||
if basePath == aws.ToString(subpath.RelativePath) {
|
||||
if basePath == aws.StringValue(subpath.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, subpath := range output.SymbolicLinks {
|
||||
if basePath == aws.ToString(subpath.RelativePath) {
|
||||
if basePath == aws.StringValue(subpath.RelativePath) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
@@ -181,7 +182,7 @@ func (p *AWSCodeCommitProvider) RepoHasPath(ctx context.Context, repo *Repositor
|
||||
func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repository) ([]*Repository, error) {
|
||||
repos := make([]*Repository, 0)
|
||||
if !p.allBranches {
|
||||
output, err := p.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{
|
||||
output, err := p.codeCommitClient.GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{
|
||||
RepositoryName: aws.String(repo.Repository),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -191,7 +192,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
Organization: repo.Organization,
|
||||
Repository: repo.Repository,
|
||||
URL: repo.URL,
|
||||
Branch: aws.ToString(output.RepositoryMetadata.DefaultBranch),
|
||||
Branch: aws.StringValue(output.RepositoryMetadata.DefaultBranch),
|
||||
RepositoryId: repo.RepositoryId,
|
||||
Labels: repo.Labels,
|
||||
// getting SHA of the branch requires a separate GetBranch call.
|
||||
@@ -203,7 +204,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
RepositoryName: aws.String(repo.Repository),
|
||||
}
|
||||
for {
|
||||
output, err := p.codeCommitClient.ListBranches(ctx, input)
|
||||
output, err := p.codeCommitClient.ListBranchesWithContext(ctx, input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -212,7 +213,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
Organization: repo.Organization,
|
||||
Repository: repo.Repository,
|
||||
URL: repo.URL,
|
||||
Branch: branch,
|
||||
Branch: aws.StringValue(branch),
|
||||
RepositoryId: repo.RepositoryId,
|
||||
Labels: repo.Labels,
|
||||
// getting SHA of the branch requires a separate GetBranch call.
|
||||
@@ -221,7 +222,7 @@ func (p *AWSCodeCommitProvider) GetBranches(ctx context.Context, repo *Repositor
|
||||
})
|
||||
}
|
||||
input.NextToken = output.NextToken
|
||||
if aws.ToString(output.NextToken) == "" {
|
||||
if aws.StringValue(output.NextToken) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -240,32 +241,32 @@ func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, er
|
||||
listReposInput := &codecommit.ListRepositoriesInput{}
|
||||
var output *codecommit.ListRepositoriesOutput
|
||||
for {
|
||||
output, err = p.codeCommitClient.ListRepositories(ctx, listReposInput)
|
||||
output, err = p.codeCommitClient.ListRepositoriesWithContext(ctx, listReposInput)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
for _, repo := range output.Repositories {
|
||||
repoNames = append(repoNames, aws.ToString(repo.RepositoryName))
|
||||
repoNames = append(repoNames, aws.StringValue(repo.RepositoryName))
|
||||
}
|
||||
listReposInput.NextToken = output.NextToken
|
||||
if aws.ToString(output.NextToken) == "" {
|
||||
if aws.StringValue(output.NextToken) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Debugf("tag filer is specified, calling tagging api to list repos")
|
||||
discoveryInput := &resourcegroupstaggingapi.GetResourcesInput{
|
||||
ResourceTypeFilters: []string{resourceTypeCodeCommitRepository},
|
||||
ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}),
|
||||
TagFilters: tagFilters,
|
||||
}
|
||||
var output *resourcegroupstaggingapi.GetResourcesOutput
|
||||
for {
|
||||
output, err = p.taggingClient.GetResources(ctx, discoveryInput)
|
||||
output, err = p.taggingClient.GetResourcesWithContext(ctx, discoveryInput)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
for _, resource := range output.ResourceTagMappingList {
|
||||
repoArn := aws.ToString(resource.ResourceARN)
|
||||
repoArn := aws.StringValue(resource.ResourceARN)
|
||||
log.Debugf("discovered codecommit repo with arn %s", repoArn)
|
||||
repoName, extractErr := getCodeCommitRepoName(repoArn)
|
||||
if extractErr != nil {
|
||||
@@ -275,7 +276,7 @@ func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, er
|
||||
repoNames = append(repoNames, repoName)
|
||||
}
|
||||
discoveryInput.PaginationToken = output.PaginationToken
|
||||
if aws.ToString(output.PaginationToken) == "" {
|
||||
if aws.StringValue(output.PaginationToken) == "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -283,20 +284,19 @@ func (p *AWSCodeCommitProvider) listRepoNames(ctx context.Context) ([]string, er
|
||||
return repoNames, err
|
||||
}
|
||||
|
||||
// getTagFilters filters by tag
|
||||
func (p *AWSCodeCommitProvider) getTagFilters() []rgsatypes.TagFilter {
|
||||
filters := make(map[string]rgsatypes.TagFilter)
|
||||
func (p *AWSCodeCommitProvider) getTagFilters() []*resourcegroupstaggingapi.TagFilter {
|
||||
filters := make(map[string]*resourcegroupstaggingapi.TagFilter)
|
||||
for _, tagFilter := range p.tagFilters {
|
||||
filter := filters[tagFilter.Key]
|
||||
if filter.Key == nil {
|
||||
filter = rgsatypes.TagFilter{
|
||||
filter, hasKey := filters[tagFilter.Key]
|
||||
if !hasKey {
|
||||
filter = &resourcegroupstaggingapi.TagFilter{
|
||||
Key: aws.String(tagFilter.Key),
|
||||
}
|
||||
filters[tagFilter.Key] = filter
|
||||
}
|
||||
if tagFilter.Value != "" {
|
||||
filter.Values = append(filter.Values, tagFilter.Value)
|
||||
filter.Values = append(filter.Values, aws.String(tagFilter.Value))
|
||||
}
|
||||
filters[tagFilter.Key] = filter
|
||||
}
|
||||
return slices.Collect(maps.Values(filters))
|
||||
}
|
||||
@@ -326,15 +326,12 @@ func getCodeCommitFIPSEndpoint(repoURL string) (string, error) {
|
||||
return strings.Replace(repoURL, prefixGitURLHTTPS, prefixGitURLHTTPSFIPS, 1), nil
|
||||
}
|
||||
|
||||
func hasAwsError(err error) bool {
|
||||
// Check for common CodeCommit exceptions using SDK v2 typed errors
|
||||
var repoNotFound *codecommittypes.RepositoryDoesNotExistException
|
||||
var commitNotFound *codecommittypes.CommitDoesNotExistException
|
||||
var folderNotFound *codecommittypes.FolderDoesNotExistException
|
||||
|
||||
return errors.As(err, &repoNotFound) ||
|
||||
errors.As(err, &commitNotFound) ||
|
||||
errors.As(err, &folderNotFound)
|
||||
func hasAwsError(err error, codes ...string) bool {
|
||||
var awsErr awserr.Error
|
||||
if errors.As(err, &awsErr) {
|
||||
return slices.Contains(codes, awsErr.Code())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// toAbsolutePath transforms a path input to absolute path, as required by AWS CodeCommit
|
||||
@@ -346,32 +343,37 @@ func toAbsolutePath(path string) string {
|
||||
return filepath.ToSlash(filepath.Join("/", path)) //nolint:gocritic // Prepend slash to have an absolute path
|
||||
}
|
||||
|
||||
// createAWSDiscoveryClients creates AWS clients
|
||||
func createAWSDiscoveryClients(ctx context.Context, role string, region string) (*resourcegroupstaggingapi.Client, *codecommit.Client, error) {
|
||||
var configOpts []func(*config.LoadOptions) error
|
||||
if region != "" {
|
||||
log.Debugf("region %s is provided for AWS CodeCommit discovery", region)
|
||||
configOpts = append(configOpts, config.WithRegion(region))
|
||||
} else {
|
||||
log.Debugf("region is not provided for AWS CodeCommit discovery, using pod region")
|
||||
}
|
||||
// Load the default config based on config options
|
||||
cfg, err := config.LoadDefaultConfig(ctx, configOpts...)
|
||||
func createAWSDiscoveryClients(_ context.Context, role string, region string) (*resourcegroupstaggingapi.ResourceGroupsTaggingAPI, *codecommit.CodeCommit, error) {
|
||||
podSession, err := session.NewSession()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error loading default config: %w", err)
|
||||
return nil, nil, fmt.Errorf("error creating new AWS pod session: %w", err)
|
||||
}
|
||||
client := sts.NewFromConfig(cfg)
|
||||
discoverySession := podSession
|
||||
// assume role if provided - this allows cross account CodeCommit repo discovery.
|
||||
if role != "" {
|
||||
log.Debugf("role %s is provided for AWS CodeCommit discovery", role)
|
||||
assumeRoleCreds := stscreds.NewAssumeRoleProvider(client, role)
|
||||
cfg.Credentials = aws.NewCredentialsCache(assumeRoleCreds)
|
||||
assumeRoleCreds := stscreds.NewCredentials(podSession, role)
|
||||
discoverySession, err = session.NewSession(&aws.Config{
|
||||
Credentials: assumeRoleCreds,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating new AWS discovery session: %w", err)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("role is not provided for AWS CodeCommit discovery, using pod role")
|
||||
}
|
||||
// use region explicitly if provided - this allows cross region CodeCommit repo discovery.
|
||||
if region != "" {
|
||||
log.Debugf("region %s is provided for AWS CodeCommit discovery", region)
|
||||
discoverySession = discoverySession.Copy(&aws.Config{
|
||||
Region: aws.String(region),
|
||||
})
|
||||
} else {
|
||||
log.Debugf("region is not provided for AWS CodeCommit discovery, using pod region")
|
||||
}
|
||||
|
||||
taggingClient := resourcegroupstaggingapi.NewFromConfig(cfg)
|
||||
codeCommitClient := codecommit.NewFromConfig(cfg)
|
||||
taggingClient := resourcegroupstaggingapi.New(discoverySession)
|
||||
codeCommitClient := codecommit.New(discoverySession)
|
||||
|
||||
return taggingClient, codeCommitClient, nil
|
||||
}
|
||||
|
||||
387
applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go
generated
Normal file
387
applicationset/services/scm_provider/aws_codecommit/mocks/AWSCodeCommitClient.go
generated
Normal file
@@ -0,0 +1,387 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewAWSCodeCommitClient creates a new instance of AWSCodeCommitClient. 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.
|
||||
func NewAWSCodeCommitClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AWSCodeCommitClient {
|
||||
mock := &AWSCodeCommitClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient is an autogenerated mock type for the AWSCodeCommitClient type
|
||||
type AWSCodeCommitClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AWSCodeCommitClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AWSCodeCommitClient) EXPECT() *AWSCodeCommitClient_Expecter {
|
||||
return &AWSCodeCommitClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetFolderWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetFolderWithContext(v aws.Context, getFolderInput *codecommit.GetFolderInput, options ...request.Option) (*codecommit.GetFolderOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, getFolderInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetFolderWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetFolderOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)); ok {
|
||||
return returnFunc(v, getFolderInput, options...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetFolderInput, ...request.Option) *codecommit.GetFolderOutput); ok {
|
||||
r0 = returnFunc(v, getFolderInput, options...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.GetFolderOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.GetFolderInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, getFolderInput, options...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetFolderWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFolderWithContext'
|
||||
type AWSCodeCommitClient_GetFolderWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetFolderWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// - getFolderInput *codecommit.GetFolderInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetFolderWithContext(v interface{}, getFolderInput interface{}, options ...interface{}) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
return &AWSCodeCommitClient_GetFolderWithContext_Call{Call: _e.mock.On("GetFolderWithContext",
|
||||
append([]interface{}{v, getFolderInput}, options...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Run(run func(v aws.Context, getFolderInput *codecommit.GetFolderInput, options ...request.Option)) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
}
|
||||
var arg1 *codecommit.GetFolderInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.GetFolderInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Return(getFolderOutput *codecommit.GetFolderOutput, err error) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
_c.Call.Return(getFolderOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) RunAndReturn(run func(v aws.Context, getFolderInput *codecommit.GetFolderInput, options ...request.Option) (*codecommit.GetFolderOutput, error)) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetRepositoryWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetRepositoryWithContext(v aws.Context, getRepositoryInput *codecommit.GetRepositoryInput, options ...request.Option) (*codecommit.GetRepositoryOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, getRepositoryInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRepositoryWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetRepositoryOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)); ok {
|
||||
return returnFunc(v, getRepositoryInput, options...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) *codecommit.GetRepositoryOutput); ok {
|
||||
r0 = returnFunc(v, getRepositoryInput, options...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.GetRepositoryOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.GetRepositoryInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, getRepositoryInput, options...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetRepositoryWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepositoryWithContext'
|
||||
type AWSCodeCommitClient_GetRepositoryWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetRepositoryWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// - getRepositoryInput *codecommit.GetRepositoryInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetRepositoryWithContext(v interface{}, getRepositoryInput interface{}, options ...interface{}) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
return &AWSCodeCommitClient_GetRepositoryWithContext_Call{Call: _e.mock.On("GetRepositoryWithContext",
|
||||
append([]interface{}{v, getRepositoryInput}, options...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Run(run func(v aws.Context, getRepositoryInput *codecommit.GetRepositoryInput, options ...request.Option)) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
}
|
||||
var arg1 *codecommit.GetRepositoryInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.GetRepositoryInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Return(getRepositoryOutput *codecommit.GetRepositoryOutput, err error) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
_c.Call.Return(getRepositoryOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) RunAndReturn(run func(v aws.Context, getRepositoryInput *codecommit.GetRepositoryInput, options ...request.Option) (*codecommit.GetRepositoryOutput, error)) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListBranchesWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListBranchesWithContext(v aws.Context, listBranchesInput *codecommit.ListBranchesInput, options ...request.Option) (*codecommit.ListBranchesOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, listBranchesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListBranchesWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListBranchesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)); ok {
|
||||
return returnFunc(v, listBranchesInput, options...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListBranchesInput, ...request.Option) *codecommit.ListBranchesOutput); ok {
|
||||
r0 = returnFunc(v, listBranchesInput, options...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.ListBranchesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.ListBranchesInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, listBranchesInput, options...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListBranchesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBranchesWithContext'
|
||||
type AWSCodeCommitClient_ListBranchesWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListBranchesWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// - listBranchesInput *codecommit.ListBranchesInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListBranchesWithContext(v interface{}, listBranchesInput interface{}, options ...interface{}) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
return &AWSCodeCommitClient_ListBranchesWithContext_Call{Call: _e.mock.On("ListBranchesWithContext",
|
||||
append([]interface{}{v, listBranchesInput}, options...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Run(run func(v aws.Context, listBranchesInput *codecommit.ListBranchesInput, options ...request.Option)) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
}
|
||||
var arg1 *codecommit.ListBranchesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.ListBranchesInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Return(listBranchesOutput *codecommit.ListBranchesOutput, err error) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
_c.Call.Return(listBranchesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) RunAndReturn(run func(v aws.Context, listBranchesInput *codecommit.ListBranchesInput, options ...request.Option) (*codecommit.ListBranchesOutput, error)) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListRepositoriesWithContext provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListRepositoriesWithContext(v aws.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, options ...request.Option) (*codecommit.ListRepositoriesOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, listRepositoriesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRepositoriesWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListRepositoriesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)); ok {
|
||||
return returnFunc(v, listRepositoriesInput, options...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) *codecommit.ListRepositoriesOutput); ok {
|
||||
r0 = returnFunc(v, listRepositoriesInput, options...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.ListRepositoriesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *codecommit.ListRepositoriesInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, listRepositoriesInput, options...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListRepositoriesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepositoriesWithContext'
|
||||
type AWSCodeCommitClient_ListRepositoriesWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListRepositoriesWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// - listRepositoriesInput *codecommit.ListRepositoriesInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListRepositoriesWithContext(v interface{}, listRepositoriesInput interface{}, options ...interface{}) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
return &AWSCodeCommitClient_ListRepositoriesWithContext_Call{Call: _e.mock.On("ListRepositoriesWithContext",
|
||||
append([]interface{}{v, listRepositoriesInput}, options...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Run(run func(v aws.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, options ...request.Option)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
}
|
||||
var arg1 *codecommit.ListRepositoriesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.ListRepositoriesInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Return(listRepositoriesOutput *codecommit.ListRepositoriesOutput, err error) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
_c.Call.Return(listRepositoriesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) RunAndReturn(run func(v aws.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, options ...request.Option) (*codecommit.ListRepositoriesOutput, error)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
126
applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go
generated
Normal file
126
applicationset/services/scm_provider/aws_codecommit/mocks/AWSTaggingClient.go
generated
Normal file
@@ -0,0 +1,126 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewAWSTaggingClient creates a new instance of AWSTaggingClient. 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.
|
||||
func NewAWSTaggingClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AWSTaggingClient {
|
||||
mock := &AWSTaggingClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// AWSTaggingClient is an autogenerated mock type for the AWSTaggingClient type
|
||||
type AWSTaggingClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AWSTaggingClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AWSTaggingClient) EXPECT() *AWSTaggingClient_Expecter {
|
||||
return &AWSTaggingClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetResourcesWithContext provides a mock function for the type AWSTaggingClient
|
||||
func (_mock *AWSTaggingClient) GetResourcesWithContext(v aws.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, options ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||
// request.Option
|
||||
_va := make([]interface{}, len(options))
|
||||
for _i := range options {
|
||||
_va[_i] = options[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, v, getResourcesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetResourcesWithContext")
|
||||
}
|
||||
|
||||
var r0 *resourcegroupstaggingapi.GetResourcesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok {
|
||||
return returnFunc(v, getResourcesInput, options...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) *resourcegroupstaggingapi.GetResourcesOutput); ok {
|
||||
r0 = returnFunc(v, getResourcesInput, options...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*resourcegroupstaggingapi.GetResourcesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(aws.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) error); ok {
|
||||
r1 = returnFunc(v, getResourcesInput, options...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSTaggingClient_GetResourcesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetResourcesWithContext'
|
||||
type AWSTaggingClient_GetResourcesWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetResourcesWithContext is a helper method to define mock.On call
|
||||
// - v aws.Context
|
||||
// - getResourcesInput *resourcegroupstaggingapi.GetResourcesInput
|
||||
// - options ...request.Option
|
||||
func (_e *AWSTaggingClient_Expecter) GetResourcesWithContext(v interface{}, getResourcesInput interface{}, options ...interface{}) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
return &AWSTaggingClient_GetResourcesWithContext_Call{Call: _e.mock.On("GetResourcesWithContext",
|
||||
append([]interface{}{v, getResourcesInput}, options...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Run(run func(v aws.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, options ...request.Option)) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 aws.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(aws.Context)
|
||||
}
|
||||
var arg1 *resourcegroupstaggingapi.GetResourcesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*resourcegroupstaggingapi.GetResourcesInput)
|
||||
}
|
||||
var arg2 []request.Option
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Return(getResourcesOutput *resourcegroupstaggingapi.GetResourcesOutput, err error) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
_c.Call.Return(getResourcesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) RunAndReturn(run func(v aws.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, options ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -5,17 +5,14 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
codecommittypes "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
|
||||
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
|
||||
rgsatypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/mocks"
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/aws_codecommit/mocks"
|
||||
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -37,7 +34,7 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
repositories []*awsCodeCommitTestRepository
|
||||
cloneProtocol string
|
||||
tagFilters []*v1alpha1.TagFilter
|
||||
expectTagFilters []rgsatypes.TagFilter
|
||||
expectTagFilters []*resourcegroupstaggingapi.TagFilter
|
||||
listRepositoryError error
|
||||
expectOverallError bool
|
||||
expectListAtCodeCommit bool
|
||||
@@ -60,8 +57,8 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
{Key: "key1", Value: "value2"},
|
||||
{Key: "key2"},
|
||||
},
|
||||
expectTagFilters: []rgsatypes.TagFilter{
|
||||
{Key: aws.String("key1"), Values: []string{"value1", "value2"}},
|
||||
expectTagFilters: []*resourcegroupstaggingapi.TagFilter{
|
||||
{Key: aws.String("key1"), Values: aws.StringSlice([]string{"value1", "value2"})},
|
||||
{Key: aws.String("key2")},
|
||||
},
|
||||
expectOverallError: false,
|
||||
@@ -83,7 +80,7 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
tagFilters: []*v1alpha1.TagFilter{
|
||||
{Key: "key1"},
|
||||
},
|
||||
expectTagFilters: []rgsatypes.TagFilter{
|
||||
expectTagFilters: []*resourcegroupstaggingapi.TagFilter{
|
||||
{Key: aws.String("key1")},
|
||||
},
|
||||
expectOverallError: false,
|
||||
@@ -163,12 +160,12 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
codeCommitClient := mocks.NewAWSCodeCommitClient(t)
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := t.Context()
|
||||
codecommitRepoNameIdPairs := make([]codecommittypes.RepositoryNameIdPair, 0)
|
||||
resourceTaggings := make([]rgsatypes.ResourceTagMapping, 0)
|
||||
codecommitRepoNameIdPairs := make([]*codecommit.RepositoryNameIdPair, 0)
|
||||
resourceTaggings := make([]*resourcegroupstaggingapi.ResourceTagMapping, 0)
|
||||
validRepositories := make([]*awsCodeCommitTestRepository, 0)
|
||||
|
||||
for _, repo := range testCase.repositories {
|
||||
repoMetadata := &codecommittypes.RepositoryMetadata{
|
||||
repoMetadata := &codecommit.RepositoryMetadata{
|
||||
AccountId: aws.String(repo.accountId),
|
||||
Arn: aws.String(repo.arn),
|
||||
CloneUrlHttp: aws.String("https://git-codecommit.us-east-1.amazonaws.com/v1/repos/" + repo.name),
|
||||
@@ -180,13 +177,14 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
if repo.getRepositoryNilMetadata {
|
||||
repoMetadata = nil
|
||||
}
|
||||
codeCommitClient.EXPECT().GetRepository(mock.Anything, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: repoMetadata}, repo.getRepositoryError).Maybe()
|
||||
codecommitRepoNameIdPairs = append(codecommitRepoNameIdPairs, codecommittypes.RepositoryNameIdPair{
|
||||
codeCommitClient.
|
||||
On("GetRepositoryWithContext", ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: repoMetadata}, repo.getRepositoryError)
|
||||
codecommitRepoNameIdPairs = append(codecommitRepoNameIdPairs, &codecommit.RepositoryNameIdPair{
|
||||
RepositoryId: aws.String(repo.id),
|
||||
RepositoryName: aws.String(repo.name),
|
||||
})
|
||||
resourceTaggings = append(resourceTaggings, rgsatypes.ResourceTagMapping{
|
||||
resourceTaggings = append(resourceTaggings, &resourcegroupstaggingapi.ResourceTagMapping{
|
||||
ResourceARN: aws.String(repo.arn),
|
||||
})
|
||||
if repo.valid {
|
||||
@@ -195,18 +193,20 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
}
|
||||
|
||||
if testCase.expectListAtCodeCommit {
|
||||
codeCommitClient.EXPECT().ListRepositories(mock.Anything, &codecommit.ListRepositoriesInput{}).
|
||||
codeCommitClient.
|
||||
On("ListRepositoriesWithContext", ctx, &codecommit.ListRepositoriesInput{}).
|
||||
Return(&codecommit.ListRepositoriesOutput{
|
||||
Repositories: codecommitRepoNameIdPairs,
|
||||
}, testCase.listRepositoryError).Maybe()
|
||||
}, testCase.listRepositoryError)
|
||||
} else {
|
||||
taggingClient.EXPECT().GetResources(mock.Anything, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{
|
||||
TagFilters: testCase.expectTagFilters,
|
||||
ResourceTypeFilters: []string{resourceTypeCodeCommitRepository},
|
||||
}))).
|
||||
taggingClient.
|
||||
On("GetResourcesWithContext", ctx, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{
|
||||
TagFilters: testCase.expectTagFilters,
|
||||
ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}),
|
||||
}))).
|
||||
Return(&resourcegroupstaggingapi.GetResourcesOutput{
|
||||
ResourceTagMappingList: resourceTaggings,
|
||||
}, testCase.listRepositoryError).Maybe()
|
||||
}, testCase.listRepositoryError)
|
||||
}
|
||||
|
||||
provider := &AWSCodeCommitProvider{
|
||||
@@ -252,7 +252,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "lib/config.yaml",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
Files: []codecommittypes.File{
|
||||
Files: []*codecommit.File{
|
||||
{RelativePath: aws.String("config.yaml")},
|
||||
},
|
||||
},
|
||||
@@ -264,7 +264,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "lib/config",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
SubFolders: []codecommittypes.Folder{
|
||||
SubFolders: []*codecommit.Folder{
|
||||
{RelativePath: aws.String("config")},
|
||||
},
|
||||
},
|
||||
@@ -276,7 +276,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "/lib/submodule/",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
SubModules: []codecommittypes.SubModule{
|
||||
SubModules: []*codecommit.SubModule{
|
||||
{RelativePath: aws.String("submodule")},
|
||||
},
|
||||
},
|
||||
@@ -288,7 +288,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "./lib/service.json",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
SymbolicLinks: []codecommittypes.SymbolicLink{
|
||||
SymbolicLinks: []*codecommit.SymbolicLink{
|
||||
{RelativePath: aws.String("service.json")},
|
||||
},
|
||||
},
|
||||
@@ -300,16 +300,16 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
path: "no-match.json",
|
||||
expectedGetFolderPath: "/",
|
||||
getFolderOutput: &codecommit.GetFolderOutput{
|
||||
Files: []codecommittypes.File{
|
||||
Files: []*codecommit.File{
|
||||
{RelativePath: aws.String("config.yaml")},
|
||||
},
|
||||
SubFolders: []codecommittypes.Folder{
|
||||
SubFolders: []*codecommit.Folder{
|
||||
{RelativePath: aws.String("config")},
|
||||
},
|
||||
SubModules: []codecommittypes.SubModule{
|
||||
SubModules: []*codecommit.SubModule{
|
||||
{RelativePath: aws.String("submodule")},
|
||||
},
|
||||
SymbolicLinks: []codecommittypes.SymbolicLink{
|
||||
SymbolicLinks: []*codecommit.SymbolicLink{
|
||||
{RelativePath: aws.String("service.json")},
|
||||
},
|
||||
},
|
||||
@@ -320,7 +320,7 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
name: "RepoHasPath when parent folder not found",
|
||||
path: "lib/submodule",
|
||||
expectedGetFolderPath: "/lib",
|
||||
getFolderError: &codecommittypes.FolderDoesNotExistException{},
|
||||
getFolderError: &codecommit.FolderDoesNotExistException{},
|
||||
expectOverallError: false,
|
||||
},
|
||||
{
|
||||
@@ -350,12 +350,13 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := t.Context()
|
||||
if testCase.expectedGetFolderPath != "" {
|
||||
codeCommitClient.EXPECT().GetFolder(mock.Anything, &codecommit.GetFolderInput{
|
||||
CommitSpecifier: aws.String(branch),
|
||||
FolderPath: aws.String(testCase.expectedGetFolderPath),
|
||||
RepositoryName: aws.String(repoName),
|
||||
}).
|
||||
Return(testCase.getFolderOutput, testCase.getFolderError).Maybe()
|
||||
codeCommitClient.
|
||||
On("GetFolderWithContext", ctx, &codecommit.GetFolderInput{
|
||||
CommitSpecifier: aws.String(branch),
|
||||
FolderPath: aws.String(testCase.expectedGetFolderPath),
|
||||
RepositoryName: aws.String(repoName),
|
||||
}).
|
||||
Return(testCase.getFolderOutput, testCase.getFolderError)
|
||||
}
|
||||
provider := &AWSCodeCommitProvider{
|
||||
codeCommitClient: codeCommitClient,
|
||||
@@ -422,18 +423,18 @@ func TestAWSCodeCommitGetBranches(t *testing.T) {
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := t.Context()
|
||||
if testCase.allBranches {
|
||||
branches := make([]string, len(testCase.branches))
|
||||
copy(branches, testCase.branches)
|
||||
codeCommitClient.EXPECT().ListBranches(mock.Anything, &codecommit.ListBranchesInput{
|
||||
RepositoryName: aws.String(name),
|
||||
}).
|
||||
Return(&codecommit.ListBranchesOutput{Branches: branches}, testCase.apiError).Maybe()
|
||||
codeCommitClient.
|
||||
On("ListBranchesWithContext", ctx, &codecommit.ListBranchesInput{
|
||||
RepositoryName: aws.String(name),
|
||||
}).
|
||||
Return(&codecommit.ListBranchesOutput{Branches: aws.StringSlice(testCase.branches)}, testCase.apiError)
|
||||
} else {
|
||||
codeCommitClient.EXPECT().GetRepository(mock.Anything, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommittypes.RepositoryMetadata{
|
||||
codeCommitClient.
|
||||
On("GetRepositoryWithContext", ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommit.RepositoryMetadata{
|
||||
AccountId: aws.String(organization),
|
||||
DefaultBranch: aws.String(defaultBranch),
|
||||
}}, testCase.apiError).Maybe()
|
||||
}}, testCase.apiError)
|
||||
}
|
||||
provider := &AWSCodeCommitProvider{
|
||||
codeCommitClient: codeCommitClient,
|
||||
@@ -475,22 +476,8 @@ func TestAWSCodeCommitGetBranches(t *testing.T) {
|
||||
func equalIgnoringTagFilterOrder(expected *resourcegroupstaggingapi.GetResourcesInput) func(*resourcegroupstaggingapi.GetResourcesInput) bool {
|
||||
return func(actual *resourcegroupstaggingapi.GetResourcesInput) bool {
|
||||
sort.Slice(actual.TagFilters, func(i, j int) bool {
|
||||
keyI := ""
|
||||
keyJ := ""
|
||||
if actual.TagFilters[i].Key != nil {
|
||||
keyI = *actual.TagFilters[i].Key
|
||||
}
|
||||
if actual.TagFilters[j].Key != nil {
|
||||
keyJ = *actual.TagFilters[j].Key
|
||||
}
|
||||
return keyI < keyJ
|
||||
return *actual.TagFilters[i].Key < *actual.TagFilters[j].Key
|
||||
})
|
||||
// Ignore unexported fields in AWS SDK v2 types
|
||||
return cmp.Equal(expected, actual,
|
||||
cmpopts.IgnoreUnexported(
|
||||
rgsatypes.TagFilter{},
|
||||
resourcegroupstaggingapi.GetResourcesInput{},
|
||||
),
|
||||
)
|
||||
return cmp.Equal(expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package scm_provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
@@ -9,14 +10,18 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
|
||||
azureGit "github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
||||
|
||||
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 s(input string) *string {
|
||||
return ptr.To(input)
|
||||
}
|
||||
|
||||
func TestAzureDevopsRepoHasPath(t *testing.T) {
|
||||
organization := "myorg"
|
||||
teamProject := "myorg_project"
|
||||
@@ -46,12 +51,12 @@ func TestAzureDevopsRepoHasPath(t *testing.T) {
|
||||
{
|
||||
name: "RepoHasPath when no path found returns false",
|
||||
pathFound: false,
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: new(AzureDevOpsErrorsTypeKeyValues.GitItemNotFound)},
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: s(AzureDevOpsErrorsTypeKeyValues.GitItemNotFound)},
|
||||
},
|
||||
{
|
||||
name: "RepoHasPath when unknown Azure DevOps WrappedError occurs returns error",
|
||||
pathFound: false,
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: new("OtherAzureDevopsException")},
|
||||
azureDevopsError: azuredevops.WrappedError{TypeKey: s("OtherAzureDevopsException")},
|
||||
returnError: true,
|
||||
errorMessage: "failed to check for path existence",
|
||||
},
|
||||
@@ -73,13 +78,13 @@ func TestAzureDevopsRepoHasPath(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
gitClientMock := &azureMock.Client{}
|
||||
gitClientMock := azureMock.Client{}
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, testCase.clientError)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, testCase.clientError)
|
||||
|
||||
repoId := &uuid
|
||||
gitClientMock.EXPECT().GetItem(mock.Anything, azureGit.GetItemArgs{Project: &teamProject, Path: &path, VersionDescriptor: &azureGit.GitVersionDescriptor{Version: &branchName}, RepositoryId: repoId}).Return(nil, testCase.azureDevopsError)
|
||||
gitClientMock.On("GetItem", ctx, azureGit.GetItemArgs{Project: &teamProject, Path: &path, VersionDescriptor: &azureGit.GitVersionDescriptor{Version: &branchName}, RepositoryId: repoId}).Return(nil, testCase.azureDevopsError)
|
||||
|
||||
provider := AzureDevOpsProvider{organization: organization, teamProject: teamProject, clientFactory: clientFactoryMock}
|
||||
|
||||
@@ -119,12 +124,12 @@ func TestGetDefaultBranchOnDisabledRepo(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "azure devops error when disabled repo causes empty return value",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
shouldReturnError: false,
|
||||
},
|
||||
{
|
||||
name: "azure devops error with unknown error type returns error",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new("OtherError")},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s("OtherError")},
|
||||
shouldReturnError: true,
|
||||
},
|
||||
{
|
||||
@@ -138,12 +143,12 @@ func TestGetDefaultBranchOnDisabledRepo(t *testing.T) {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
uuid := uuid.New().String()
|
||||
|
||||
gitClientMock := azureMock.NewClient(t)
|
||||
gitClientMock := azureMock.Client{}
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
|
||||
|
||||
gitClientMock.EXPECT().GetBranch(mock.Anything, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &defaultBranch}).Return(nil, testCase.azureDevOpsError)
|
||||
gitClientMock.On("GetBranch", ctx, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &defaultBranch}).Return(nil, testCase.azureDevOpsError)
|
||||
|
||||
repo := &Repository{Organization: organization, Repository: repoName, RepositoryId: uuid, Branch: defaultBranch}
|
||||
|
||||
@@ -157,6 +162,8 @@ func TestGetDefaultBranchOnDisabledRepo(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.Empty(t, branches)
|
||||
|
||||
gitClientMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -176,12 +183,12 @@ func TestGetAllBranchesOnDisabledRepo(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "azure devops error when disabled repo causes empty return value",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s(AzureDevOpsErrorsTypeKeyValues.GitRepositoryNotFound)},
|
||||
shouldReturnError: false,
|
||||
},
|
||||
{
|
||||
name: "azure devops error with unknown error type returns error",
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: new("OtherError")},
|
||||
azureDevOpsError: azuredevops.WrappedError{TypeKey: s("OtherError")},
|
||||
shouldReturnError: true,
|
||||
},
|
||||
{
|
||||
@@ -195,12 +202,12 @@ func TestGetAllBranchesOnDisabledRepo(t *testing.T) {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
uuid := uuid.New().String()
|
||||
|
||||
gitClientMock := azureMock.NewClient(t)
|
||||
gitClientMock := azureMock.Client{}
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
|
||||
|
||||
gitClientMock.EXPECT().GetBranches(mock.Anything, azureGit.GetBranchesArgs{RepositoryId: &repoName, Project: &teamProject}).Return(nil, testCase.azureDevOpsError)
|
||||
gitClientMock.On("GetBranches", ctx, azureGit.GetBranchesArgs{RepositoryId: &repoName, Project: &teamProject}).Return(nil, testCase.azureDevOpsError)
|
||||
|
||||
repo := &Repository{Organization: organization, Repository: repoName, RepositoryId: uuid, Branch: defaultBranch}
|
||||
|
||||
@@ -214,6 +221,8 @@ func TestGetAllBranchesOnDisabledRepo(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.Empty(t, branches)
|
||||
|
||||
gitClientMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -229,15 +238,15 @@ func TestAzureDevOpsGetDefaultBranchStripsRefsName(t *testing.T) {
|
||||
strippedBranchName := "somebranch"
|
||||
defaultBranch := fmt.Sprintf("refs/heads/%v", strippedBranchName)
|
||||
|
||||
branchReturn := &azureGit.GitBranchStats{Name: &strippedBranchName, Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}}
|
||||
branchReturn := &azureGit.GitBranchStats{Name: &strippedBranchName, Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}}
|
||||
repo := &Repository{Organization: organization, Repository: repoName, RepositoryId: uuid, Branch: defaultBranch}
|
||||
|
||||
gitClientMock := &azureMock.Client{}
|
||||
gitClientMock := azureMock.Client{}
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
|
||||
|
||||
gitClientMock.EXPECT().GetBranch(mock.Anything, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &strippedBranchName}).Return(branchReturn, nil).Maybe()
|
||||
gitClientMock.On("GetBranch", ctx, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &strippedBranchName}).Return(branchReturn, nil)
|
||||
|
||||
provider := AzureDevOpsProvider{organization: organization, teamProject: teamProject, clientFactory: clientFactoryMock, allBranches: false}
|
||||
branches, err := provider.GetBranches(ctx, repo)
|
||||
@@ -268,7 +277,7 @@ func TestAzureDevOpsGetBranchesDefultBranchOnly(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "GetBranches AllBranches false when single branch returned returns branch",
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}},
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}},
|
||||
},
|
||||
{
|
||||
name: "GetBranches AllBranches false when request fails returns error and empty result",
|
||||
@@ -280,18 +289,18 @@ func TestAzureDevOpsGetBranchesDefultBranchOnly(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "GetBranches AllBranches false when branch returned with long commit SHA",
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: new("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
expectedBranch: &azureGit.GitBranchStats{Name: &defaultBranch, Commit: &azureGit.GitCommitRef{CommitId: s("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
gitClientMock := &azureMock.Client{}
|
||||
gitClientMock := azureMock.Client{}
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, testCase.clientError)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, testCase.clientError)
|
||||
|
||||
gitClientMock.EXPECT().GetBranch(mock.Anything, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &defaultBranch}).Return(testCase.expectedBranch, testCase.getBranchesAPIError)
|
||||
gitClientMock.On("GetBranch", ctx, azureGit.GetBranchArgs{RepositoryId: &repoName, Project: &teamProject, Name: &defaultBranch}).Return(testCase.expectedBranch, testCase.getBranchesAPIError)
|
||||
|
||||
repo := &Repository{Organization: organization, Repository: repoName, RepositoryId: uuid, Branch: defaultBranch}
|
||||
|
||||
@@ -339,7 +348,7 @@ func TestAzureDevopsGetBranches(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "GetBranches when single branch returned returns this branch info",
|
||||
expectedBranches: &[]azureGit.GitBranchStats{{Name: new("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}}},
|
||||
expectedBranches: &[]azureGit.GitBranchStats{{Name: s("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}}},
|
||||
allBranches: true,
|
||||
},
|
||||
{
|
||||
@@ -360,9 +369,9 @@ func TestAzureDevopsGetBranches(t *testing.T) {
|
||||
{
|
||||
name: "GetBranches when multiple branches returned returns branch info for all branches",
|
||||
expectedBranches: &[]azureGit.GitBranchStats{
|
||||
{Name: new("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: new("abc123233223")}},
|
||||
{Name: new("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: new("4334")}},
|
||||
{Name: new("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: new("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
{Name: s("feature-feat1"), Commit: &azureGit.GitCommitRef{CommitId: s("abc123233223")}},
|
||||
{Name: s("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: s("4334")}},
|
||||
{Name: s("feature/feat2"), Commit: &azureGit.GitCommitRef{CommitId: s("53863052ADF24229AB72154B4D83DAB7")}},
|
||||
},
|
||||
allBranches: true,
|
||||
},
|
||||
@@ -370,12 +379,12 @@ func TestAzureDevopsGetBranches(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
gitClientMock := &azureMock.Client{}
|
||||
gitClientMock := azureMock.Client{}
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, testCase.clientError)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, testCase.clientError)
|
||||
|
||||
gitClientMock.EXPECT().GetBranches(mock.Anything, azureGit.GetBranchesArgs{RepositoryId: &repoName, Project: &teamProject}).Return(testCase.expectedBranches, testCase.getBranchesAPIError)
|
||||
gitClientMock.On("GetBranches", ctx, azureGit.GetBranchesArgs{RepositoryId: &repoName, Project: &teamProject}).Return(testCase.expectedBranches, testCase.getBranchesAPIError)
|
||||
|
||||
repo := &Repository{Organization: organization, Repository: repoName, RepositoryId: uuid}
|
||||
|
||||
@@ -418,6 +427,7 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
teamProject := "myorg_project"
|
||||
|
||||
uuid := uuid.New()
|
||||
ctx := t.Context()
|
||||
|
||||
repoId := &uuid
|
||||
|
||||
@@ -429,12 +439,12 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "ListRepos when single repo found returns repo info",
|
||||
repositories: []azureGit.GitRepository{{Name: new("repo1"), DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{Name: s("repo1"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}},
|
||||
expectedNumberOfRepos: 1,
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no default branch returns empty list",
|
||||
repositories: []azureGit.GitRepository{{Name: new("repo2"), RemoteUrl: new("https://remoteurl.u"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{Name: s("repo2"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when Azure DevOps request fails returns error",
|
||||
@@ -442,24 +452,24 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no name returns empty list",
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u"), Id: repoId}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no remote URL returns empty list",
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: new("main"), Name: new("repo_name"), Id: repoId}},
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: s("main"), Name: s("repo_name"), Id: repoId}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when repo has no ID returns empty list",
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: new("main"), Name: new("repo_name"), RemoteUrl: new("https://remoteurl.u")}},
|
||||
repositories: []azureGit.GitRepository{{DefaultBranch: s("main"), Name: s("repo_name"), RemoteUrl: s("https://remoteurl.u")}},
|
||||
},
|
||||
{
|
||||
name: "ListRepos when multiple repos returned returns list of eligible repos only",
|
||||
repositories: []azureGit.GitRepository{
|
||||
{Name: new("returned1"), DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u"), Id: repoId},
|
||||
{Name: new("missing_default_branch"), RemoteUrl: new("https://remoteurl.u"), Id: repoId},
|
||||
{DefaultBranch: new("missing_name"), RemoteUrl: new("https://remoteurl.u"), Id: repoId},
|
||||
{Name: new("missing_remote_url"), DefaultBranch: new("main"), Id: repoId},
|
||||
{Name: new("missing_id"), DefaultBranch: new("main"), RemoteUrl: new("https://remoteurl.u")},
|
||||
{Name: s("returned1"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u"), Id: repoId},
|
||||
{Name: s("missing_default_branch"), RemoteUrl: s("https://remoteurl.u"), Id: repoId},
|
||||
{DefaultBranch: s("missing_name"), RemoteUrl: s("https://remoteurl.u"), Id: repoId},
|
||||
{Name: s("missing_remote_url"), DefaultBranch: s("main"), Id: repoId},
|
||||
{Name: s("missing_id"), DefaultBranch: s("main"), RemoteUrl: s("https://remoteurl.u")},
|
||||
},
|
||||
expectedNumberOfRepos: 1,
|
||||
},
|
||||
@@ -467,15 +477,15 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
gitClientMock := azureMock.NewClient(t)
|
||||
gitClientMock.EXPECT().GetRepositories(mock.Anything, azureGit.GetRepositoriesArgs{Project: new(teamProject)}).Return(&testCase.repositories, testCase.getRepositoriesError)
|
||||
gitClientMock := azureMock.Client{}
|
||||
gitClientMock.On("GetRepositories", ctx, azureGit.GetRepositoriesArgs{Project: s(teamProject)}).Return(&testCase.repositories, testCase.getRepositoriesError)
|
||||
|
||||
clientFactoryMock := &mocks.AzureDevOpsClientFactory{}
|
||||
clientFactoryMock.EXPECT().GetClient(mock.Anything).Return(gitClientMock, nil)
|
||||
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock)
|
||||
|
||||
provider := AzureDevOpsProvider{organization: organization, teamProject: teamProject, clientFactory: clientFactoryMock}
|
||||
|
||||
repositories, err := provider.ListRepos(t.Context(), "https")
|
||||
repositories, err := provider.ListRepos(ctx, "https")
|
||||
|
||||
if testCase.getRepositoriesError != nil {
|
||||
require.Error(t, err, "Expected an error from test case %v", testCase.name)
|
||||
@@ -487,6 +497,31 @@ func TestGetAzureDevopsRepositories(t *testing.T) {
|
||||
assert.NotEmpty(t, repositories)
|
||||
assert.Len(t, repositories, testCase.expectedNumberOfRepos)
|
||||
}
|
||||
|
||||
gitClientMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type AzureClientFactoryMock struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (m *AzureClientFactoryMock) GetClient(ctx context.Context) (azureGit.Client, error) {
|
||||
args := m.mock.Called(ctx)
|
||||
|
||||
var client azureGit.Client
|
||||
c := args.Get(0)
|
||||
if c != nil {
|
||||
client = c.(azureGit.Client)
|
||||
}
|
||||
|
||||
var err error
|
||||
if len(args) > 1 {
|
||||
if e, ok := args.Get(1).(error); ok {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
|
||||
return client, err
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func (c *ExtendedClient) GetContents(repo *Repository, path string) (bool, error
|
||||
urlStr += fmt.Sprintf("/repositories/%s/%s/src/%s/%s?format=meta", c.owner, repo.Repository, repo.SHA, path)
|
||||
body := strings.NewReader("")
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, urlStr, body)
|
||||
req, err := http.NewRequest(http.MethodGet, urlStr, body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -53,12 +53,8 @@ func (c *ExtendedClient) GetContents(repo *Repository, path string) (bool, error
|
||||
var _ SCMProviderService = &BitBucketCloudProvider{}
|
||||
|
||||
func NewBitBucketCloudProvider(owner string, user string, password string, allBranches bool) (*BitBucketCloudProvider, error) {
|
||||
bitbucketClient, err := bitbucket.NewBasicAuth(user, password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating BitBucket Cloud client with basic auth: %w", err)
|
||||
}
|
||||
client := &ExtendedClient{
|
||||
bitbucketClient,
|
||||
bitbucket.NewBasicAuth(user, password),
|
||||
user,
|
||||
password,
|
||||
owner,
|
||||
|
||||
@@ -445,6 +445,7 @@ func TestListReposTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defaultHandler(t)(w, r)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package scm_provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||
@@ -9,9 +8,9 @@ import (
|
||||
appsetutils "github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||
)
|
||||
|
||||
func NewGithubAppProviderFor(ctx context.Context, g github_app_auth.Authentication, organization string, url string, allBranches bool, optionalHTTPClient ...*http.Client) (*GithubProvider, error) {
|
||||
func NewGithubAppProviderFor(g github_app_auth.Authentication, organization string, url string, allBranches bool, optionalHTTPClient ...*http.Client) (*GithubProvider, error) {
|
||||
httpClient := appsetutils.GetOptionalHTTPClient(optionalHTTPClient...)
|
||||
client, err := github_app.Client(ctx, g, url, organization, httpClient)
|
||||
client, err := github_app.Client(g, url, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -76,13 +76,8 @@ func (g *GitlabProvider) GetBranches(ctx context.Context, repo *Repository) ([]*
|
||||
}
|
||||
|
||||
func (g *GitlabProvider) ListRepos(_ context.Context, cloneProtocol string) ([]*Repository, error) {
|
||||
snippetsListOptions := gitlab.ExploreSnippetsOptions{
|
||||
ListOptions: gitlab.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
opt := &gitlab.ListGroupProjectsOptions{
|
||||
ListOptions: snippetsListOptions.ListOptions,
|
||||
ListOptions: gitlab.ListOptions{PerPage: 100},
|
||||
IncludeSubGroups: &g.includeSubgroups,
|
||||
WithShared: &g.includeSharedProjects,
|
||||
Topic: &g.topic,
|
||||
@@ -178,13 +173,8 @@ func (g *GitlabProvider) listBranches(_ context.Context, repo *Repository) ([]gi
|
||||
return branches, nil
|
||||
}
|
||||
// Otherwise, scrape the ListBranches API.
|
||||
snippetsListOptions := gitlab.ExploreSnippetsOptions{
|
||||
ListOptions: gitlab.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
opt := &gitlab.ListBranchesOptions{
|
||||
ListOptions: snippetsListOptions.ListOptions,
|
||||
ListOptions: gitlab.ListOptions{PerPage: 100},
|
||||
}
|
||||
for {
|
||||
gitlabBranches, resp, err := g.client.Branches.ListBranches(repo.RepositoryId, opt)
|
||||
|
||||
@@ -1100,7 +1100,7 @@ func TestGitlabListRepos(t *testing.T) {
|
||||
url: "git@gitlab.com:test-argocd-proton/argocd.git",
|
||||
filters: []v1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
LabelMatch: new("test-topic"),
|
||||
LabelMatch: strp("test-topic"),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1301,6 +1301,7 @@ func TestGetBranchesTLS(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gitlabMockHandler(t)(w, r)
|
||||
|
||||
@@ -1,387 +0,0 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/codecommit"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewAWSCodeCommitClient creates a new instance of AWSCodeCommitClient. 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.
|
||||
func NewAWSCodeCommitClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AWSCodeCommitClient {
|
||||
mock := &AWSCodeCommitClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient is an autogenerated mock type for the AWSCodeCommitClient type
|
||||
type AWSCodeCommitClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AWSCodeCommitClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AWSCodeCommitClient) EXPECT() *AWSCodeCommitClient_Expecter {
|
||||
return &AWSCodeCommitClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetFolder provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetFolder(context1 context.Context, getFolderInput *codecommit.GetFolderInput, fns ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, context1, getFolderInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetFolder")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetFolderOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error)); ok {
|
||||
return returnFunc(context1, getFolderInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) *codecommit.GetFolderOutput); ok {
|
||||
r0 = returnFunc(context1, getFolderInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.GetFolderOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.GetFolderInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, getFolderInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetFolder_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFolder'
|
||||
type AWSCodeCommitClient_GetFolder_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetFolder is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - getFolderInput *codecommit.GetFolderInput
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetFolder(context1 interface{}, getFolderInput interface{}, fns ...interface{}) *AWSCodeCommitClient_GetFolder_Call {
|
||||
return &AWSCodeCommitClient_GetFolder_Call{Call: _e.mock.On("GetFolder",
|
||||
append([]interface{}{context1, getFolderInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolder_Call) Run(run func(context1 context.Context, getFolderInput *codecommit.GetFolderInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_GetFolder_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.GetFolderInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.GetFolderInput)
|
||||
}
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolder_Call) Return(getFolderOutput *codecommit.GetFolderOutput, err error) *AWSCodeCommitClient_GetFolder_Call {
|
||||
_c.Call.Return(getFolderOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolder_Call) RunAndReturn(run func(context1 context.Context, getFolderInput *codecommit.GetFolderInput, fns ...func(*codecommit.Options)) (*codecommit.GetFolderOutput, error)) *AWSCodeCommitClient_GetFolder_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetRepository provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) GetRepository(context1 context.Context, getRepositoryInput *codecommit.GetRepositoryInput, fns ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, context1, getRepositoryInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRepository")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetRepositoryOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error)); ok {
|
||||
return returnFunc(context1, getRepositoryInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) *codecommit.GetRepositoryOutput); ok {
|
||||
r0 = returnFunc(context1, getRepositoryInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.GetRepositoryOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.GetRepositoryInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, getRepositoryInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetRepository_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepository'
|
||||
type AWSCodeCommitClient_GetRepository_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetRepository is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - getRepositoryInput *codecommit.GetRepositoryInput
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetRepository(context1 interface{}, getRepositoryInput interface{}, fns ...interface{}) *AWSCodeCommitClient_GetRepository_Call {
|
||||
return &AWSCodeCommitClient_GetRepository_Call{Call: _e.mock.On("GetRepository",
|
||||
append([]interface{}{context1, getRepositoryInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepository_Call) Run(run func(context1 context.Context, getRepositoryInput *codecommit.GetRepositoryInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_GetRepository_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.GetRepositoryInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.GetRepositoryInput)
|
||||
}
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepository_Call) Return(getRepositoryOutput *codecommit.GetRepositoryOutput, err error) *AWSCodeCommitClient_GetRepository_Call {
|
||||
_c.Call.Return(getRepositoryOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepository_Call) RunAndReturn(run func(context1 context.Context, getRepositoryInput *codecommit.GetRepositoryInput, fns ...func(*codecommit.Options)) (*codecommit.GetRepositoryOutput, error)) *AWSCodeCommitClient_GetRepository_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListBranches provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListBranches(context1 context.Context, listBranchesInput *codecommit.ListBranchesInput, fns ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, context1, listBranchesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListBranches")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListBranchesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error)); ok {
|
||||
return returnFunc(context1, listBranchesInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) *codecommit.ListBranchesOutput); ok {
|
||||
r0 = returnFunc(context1, listBranchesInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.ListBranchesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.ListBranchesInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, listBranchesInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListBranches_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBranches'
|
||||
type AWSCodeCommitClient_ListBranches_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListBranches is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - listBranchesInput *codecommit.ListBranchesInput
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListBranches(context1 interface{}, listBranchesInput interface{}, fns ...interface{}) *AWSCodeCommitClient_ListBranches_Call {
|
||||
return &AWSCodeCommitClient_ListBranches_Call{Call: _e.mock.On("ListBranches",
|
||||
append([]interface{}{context1, listBranchesInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranches_Call) Run(run func(context1 context.Context, listBranchesInput *codecommit.ListBranchesInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_ListBranches_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.ListBranchesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.ListBranchesInput)
|
||||
}
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranches_Call) Return(listBranchesOutput *codecommit.ListBranchesOutput, err error) *AWSCodeCommitClient_ListBranches_Call {
|
||||
_c.Call.Return(listBranchesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranches_Call) RunAndReturn(run func(context1 context.Context, listBranchesInput *codecommit.ListBranchesInput, fns ...func(*codecommit.Options)) (*codecommit.ListBranchesOutput, error)) *AWSCodeCommitClient_ListBranches_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListRepositories provides a mock function for the type AWSCodeCommitClient
|
||||
func (_mock *AWSCodeCommitClient) ListRepositories(context1 context.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, fns ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error) {
|
||||
// func(*codecommit.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, context1, listRepositoriesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRepositories")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListRepositoriesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)); ok {
|
||||
return returnFunc(context1, listRepositoriesInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) *codecommit.ListRepositoriesOutput); ok {
|
||||
r0 = returnFunc(context1, listRepositoriesInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*codecommit.ListRepositoriesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *codecommit.ListRepositoriesInput, ...func(*codecommit.Options)) error); ok {
|
||||
r1 = returnFunc(context1, listRepositoriesInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListRepositories_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepositories'
|
||||
type AWSCodeCommitClient_ListRepositories_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListRepositories is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - listRepositoriesInput *codecommit.ListRepositoriesInput
|
||||
// - fns ...func(*codecommit.Options)
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListRepositories(context1 interface{}, listRepositoriesInput interface{}, fns ...interface{}) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
return &AWSCodeCommitClient_ListRepositories_Call{Call: _e.mock.On("ListRepositories",
|
||||
append([]interface{}{context1, listRepositoriesInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositories_Call) Run(run func(context1 context.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, fns ...func(*codecommit.Options))) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *codecommit.ListRepositoriesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*codecommit.ListRepositoriesInput)
|
||||
}
|
||||
var arg2 []func(*codecommit.Options)
|
||||
variadicArgs := make([]func(*codecommit.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(func(*codecommit.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositories_Call) Return(listRepositoriesOutput *codecommit.ListRepositoriesOutput, err error) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
_c.Call.Return(listRepositoriesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositories_Call) RunAndReturn(run func(context1 context.Context, listRepositoriesInput *codecommit.ListRepositoriesInput, fns ...func(*codecommit.Options)) (*codecommit.ListRepositoriesOutput, error)) *AWSCodeCommitClient_ListRepositories_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewAWSTaggingClient creates a new instance of AWSTaggingClient. 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.
|
||||
func NewAWSTaggingClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AWSTaggingClient {
|
||||
mock := &AWSTaggingClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// AWSTaggingClient is an autogenerated mock type for the AWSTaggingClient type
|
||||
type AWSTaggingClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AWSTaggingClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AWSTaggingClient) EXPECT() *AWSTaggingClient_Expecter {
|
||||
return &AWSTaggingClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetResources provides a mock function for the type AWSTaggingClient
|
||||
func (_mock *AWSTaggingClient) GetResources(context1 context.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, fns ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||
// func(*resourcegroupstaggingapi.Options)
|
||||
_va := make([]interface{}, len(fns))
|
||||
for _i := range fns {
|
||||
_va[_i] = fns[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, context1, getResourcesInput)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _mock.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetResources")
|
||||
}
|
||||
|
||||
var r0 *resourcegroupstaggingapi.GetResourcesOutput
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok {
|
||||
return returnFunc(context1, getResourcesInput, fns...)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) *resourcegroupstaggingapi.GetResourcesOutput); ok {
|
||||
r0 = returnFunc(context1, getResourcesInput, fns...)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*resourcegroupstaggingapi.GetResourcesOutput)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...func(*resourcegroupstaggingapi.Options)) error); ok {
|
||||
r1 = returnFunc(context1, getResourcesInput, fns...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSTaggingClient_GetResources_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetResources'
|
||||
type AWSTaggingClient_GetResources_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetResources is a helper method to define mock.On call
|
||||
// - context1 context.Context
|
||||
// - getResourcesInput *resourcegroupstaggingapi.GetResourcesInput
|
||||
// - fns ...func(*resourcegroupstaggingapi.Options)
|
||||
func (_e *AWSTaggingClient_Expecter) GetResources(context1 interface{}, getResourcesInput interface{}, fns ...interface{}) *AWSTaggingClient_GetResources_Call {
|
||||
return &AWSTaggingClient_GetResources_Call{Call: _e.mock.On("GetResources",
|
||||
append([]interface{}{context1, getResourcesInput}, fns...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResources_Call) Run(run func(context1 context.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, fns ...func(*resourcegroupstaggingapi.Options))) *AWSTaggingClient_GetResources_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
var arg1 *resourcegroupstaggingapi.GetResourcesInput
|
||||
if args[1] != nil {
|
||||
arg1 = args[1].(*resourcegroupstaggingapi.GetResourcesInput)
|
||||
}
|
||||
var arg2 []func(*resourcegroupstaggingapi.Options)
|
||||
variadicArgs := make([]func(*resourcegroupstaggingapi.Options), len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(func(*resourcegroupstaggingapi.Options))
|
||||
}
|
||||
}
|
||||
arg2 = variadicArgs
|
||||
run(
|
||||
arg0,
|
||||
arg1,
|
||||
arg2...,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResources_Call) Return(getResourcesOutput *resourcegroupstaggingapi.GetResourcesOutput, err error) *AWSTaggingClient_GetResources_Call {
|
||||
_c.Call.Return(getResourcesOutput, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResources_Call) RunAndReturn(run func(context1 context.Context, getResourcesInput *resourcegroupstaggingapi.GetResourcesInput, fns ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error)) *AWSTaggingClient_GetResources_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// Code generated by mockery; DO NOT EDIT.
|
||||
// github.com/vektra/mockery
|
||||
// template: testify
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NewAzureDevOpsClientFactory creates a new instance of AzureDevOpsClientFactory. 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.
|
||||
func NewAzureDevOpsClientFactory(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AzureDevOpsClientFactory {
|
||||
mock := &AzureDevOpsClientFactory{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// AzureDevOpsClientFactory is an autogenerated mock type for the AzureDevOpsClientFactory type
|
||||
type AzureDevOpsClientFactory struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AzureDevOpsClientFactory_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AzureDevOpsClientFactory) EXPECT() *AzureDevOpsClientFactory_Expecter {
|
||||
return &AzureDevOpsClientFactory_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetClient provides a mock function for the type AzureDevOpsClientFactory
|
||||
func (_mock *AzureDevOpsClientFactory) GetClient(ctx context.Context) (git.Client, error) {
|
||||
ret := _mock.Called(ctx)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetClient")
|
||||
}
|
||||
|
||||
var r0 git.Client
|
||||
var r1 error
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context) (git.Client, error)); ok {
|
||||
return returnFunc(ctx)
|
||||
}
|
||||
if returnFunc, ok := ret.Get(0).(func(context.Context) git.Client); ok {
|
||||
r0 = returnFunc(ctx)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(git.Client)
|
||||
}
|
||||
}
|
||||
if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||
r1 = returnFunc(ctx)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AzureDevOpsClientFactory_GetClient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetClient'
|
||||
type AzureDevOpsClientFactory_GetClient_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetClient is a helper method to define mock.On call
|
||||
// - ctx context.Context
|
||||
func (_e *AzureDevOpsClientFactory_Expecter) GetClient(ctx interface{}) *AzureDevOpsClientFactory_GetClient_Call {
|
||||
return &AzureDevOpsClientFactory_GetClient_Call{Call: _e.mock.On("GetClient", ctx)}
|
||||
}
|
||||
|
||||
func (_c *AzureDevOpsClientFactory_GetClient_Call) Run(run func(ctx context.Context)) *AzureDevOpsClientFactory_GetClient_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
var arg0 context.Context
|
||||
if args[0] != nil {
|
||||
arg0 = args[0].(context.Context)
|
||||
}
|
||||
run(
|
||||
arg0,
|
||||
)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AzureDevOpsClientFactory_GetClient_Call) Return(client git.Client, err error) *AzureDevOpsClientFactory_GetClient_Call {
|
||||
_c.Call.Return(client, err)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AzureDevOpsClientFactory_GetClient_Call) RunAndReturn(run func(ctx context.Context) (git.Client, error)) *AzureDevOpsClientFactory_GetClient_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
@@ -59,7 +58,13 @@ func matchFilter(ctx context.Context, provider SCMProviderService, repo *Reposit
|
||||
}
|
||||
|
||||
if filter.LabelMatch != nil {
|
||||
found := slices.ContainsFunc(repo.Labels, filter.LabelMatch.MatchString)
|
||||
found := false
|
||||
for _, label := range repo.Labels {
|
||||
if filter.LabelMatch.MatchString(label) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func strp(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestFilterRepoMatch(t *testing.T) {
|
||||
provider := &MockProvider{
|
||||
Repos: []*Repository{
|
||||
@@ -29,7 +33,7 @@ func TestFilterRepoMatch(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: new("n|hr"),
|
||||
RepositoryMatch: strp("n|hr"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -58,7 +62,7 @@ func TestFilterLabelMatch(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
LabelMatch: new("^prod-.*$"),
|
||||
LabelMatch: strp("^prod-.*$"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -127,7 +131,7 @@ func TestFilterRepoMatchBadRegexp(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: new("("),
|
||||
RepositoryMatch: strp("("),
|
||||
},
|
||||
}
|
||||
_, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -144,7 +148,7 @@ func TestFilterLabelMatchBadRegexp(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
LabelMatch: new("("),
|
||||
LabelMatch: strp("("),
|
||||
},
|
||||
}
|
||||
_, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -178,7 +182,7 @@ func TestFilterBranchMatch(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
BranchMatch: new("w"),
|
||||
BranchMatch: strp("w"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -209,8 +213,8 @@ func TestMultiFilterAnd(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: new("w"),
|
||||
LabelMatch: new("^prod-.*$"),
|
||||
RepositoryMatch: strp("w"),
|
||||
LabelMatch: strp("^prod-.*$"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
@@ -238,10 +242,10 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
}
|
||||
filters := []argoprojiov1alpha1.SCMProviderGeneratorFilter{
|
||||
{
|
||||
RepositoryMatch: new("e"),
|
||||
RepositoryMatch: strp("e"),
|
||||
},
|
||||
{
|
||||
LabelMatch: new("^prod-.*$"),
|
||||
LabelMatch: strp("^prod-.*$"),
|
||||
},
|
||||
}
|
||||
repos, err := ListRepos(t.Context(), provider, filters, "")
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// NewCacheSyncingClient returns a client that wraps the given client and syncs the cache after each Create, Update, Patch, or Delete operation on Application objects.
|
||||
func NewCacheSyncingClient(c client.Client, cache ctrlcache.Cache) client.Client {
|
||||
res := &cacheSyncingClient{Client: c, storesByNs: make(map[string]k8scache.Store)}
|
||||
// The k8s controller runtime's cache does not expose a way to get the underlying store, so we have to use reflection to access it.
|
||||
// This is necessary to keep the cache in sync with the client operations.
|
||||
field := reflect.ValueOf(cache).Elem().FieldByName("namespaceToCache")
|
||||
if field.Kind() == reflect.Map {
|
||||
namespaceToCache := *(*map[string]ctrlcache.Cache)(unsafe.Pointer(field.UnsafeAddr()))
|
||||
res.getNSCache = func(_ context.Context, obj client.Object) (ctrlcache.Cache, error) {
|
||||
res, ok := namespaceToCache[obj.GetNamespace()]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cache for namespace %s not found", obj.GetNamespace())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
} else {
|
||||
res.getNSCache = func(_ context.Context, _ client.Object) (ctrlcache.Cache, error) {
|
||||
return cache, nil
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type cacheSyncingClient struct {
|
||||
client.Client
|
||||
getNSCache func(ctx context.Context, obj client.Object) (ctrlcache.Cache, error)
|
||||
|
||||
storesByNs map[string]k8scache.Store
|
||||
storesByNsLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) getStore(ctx context.Context, obj client.Object) (k8scache.Store, error) {
|
||||
c.storesByNsLock.RLock()
|
||||
store, ok := c.storesByNs[obj.GetNamespace()]
|
||||
c.storesByNsLock.RUnlock()
|
||||
if ok {
|
||||
return store, nil
|
||||
}
|
||||
|
||||
store, err := c.retrieveStore(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.storesByNsLock.Lock()
|
||||
c.storesByNs[obj.GetNamespace()] = store
|
||||
c.storesByNsLock.Unlock()
|
||||
|
||||
return store, nil
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) retrieveStore(ctx context.Context, obj client.Object) (k8scache.Store, error) {
|
||||
nsCache, err := c.getNSCache(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get namespace cache: %w", err)
|
||||
}
|
||||
|
||||
informer, err := nsCache.GetInformerForKind(ctx, application.ApplicationSchemaGroupVersionKind)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get informer: %w", err)
|
||||
}
|
||||
indexInformer, ok := informer.(k8scache.SharedIndexInformer)
|
||||
if !ok {
|
||||
return nil, errors.New("informer is not a SharedIndexInformer")
|
||||
}
|
||||
|
||||
return indexInformer.GetStore(), nil
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) execAndSyncCache(ctx context.Context, op func() error, obj client.Object, deleteObj bool) error {
|
||||
// execute the operation first and only sync cache if it succeeds
|
||||
if err := op(); err != nil {
|
||||
return err
|
||||
}
|
||||
// sync cache for applications only
|
||||
if _, ok := obj.(*application.Application); !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger := log.WithField("namespace", obj.GetNamespace()).WithField("name", obj.GetName())
|
||||
store, err := c.getStore(ctx, obj)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get cache store: %v", err)
|
||||
} else {
|
||||
if deleteObj {
|
||||
err = store.Delete(obj)
|
||||
} else {
|
||||
err = store.Update(obj)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
logger.Errorf("failed to sync cache for object: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Create(ctx, obj, opts...)
|
||||
}, obj, false)
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Update(ctx, obj, opts...)
|
||||
}, obj, false)
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Patch(ctx, obj, patch, opts...)
|
||||
}, obj, false)
|
||||
}
|
||||
|
||||
func (c *cacheSyncingClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
|
||||
return c.execAndSyncCache(ctx, func() error {
|
||||
return c.Client.Delete(ctx, obj, opts...)
|
||||
}, obj, true)
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
k8scache "k8s.io/client-go/tools/cache"
|
||||
ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
type fakeMultiNamespaceCache struct {
|
||||
//nolint:unused
|
||||
namespaceToCache map[string]ctrlcache.Cache
|
||||
|
||||
ctrlcache.Cache
|
||||
k8scache.SharedIndexInformer
|
||||
|
||||
Store k8scache.Store
|
||||
}
|
||||
|
||||
func (f *fakeMultiNamespaceCache) GetInformerForKind(_ context.Context, _ schema.GroupVersionKind, _ ...ctrlcache.InformerGetOption) (ctrlcache.Informer, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *fakeMultiNamespaceCache) GetStore() k8scache.Store {
|
||||
return f.Store
|
||||
}
|
||||
|
||||
func newClient(objs ...client.Object) (*cacheSyncingClient, k8scache.Store, error) {
|
||||
scheme := runtime.NewScheme()
|
||||
if err := application.AddToScheme(scheme); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
store := k8scache.NewStore(func(obj any) (string, error) {
|
||||
return obj.(client.Object).GetName(), nil
|
||||
})
|
||||
for _, obj := range objs {
|
||||
if err := store.Add(obj); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
c := &cacheSyncingClient{
|
||||
Client: fake.NewClientBuilder().WithScheme(scheme).WithObjects(objs...).Build(),
|
||||
storesByNs: map[string]k8scache.Store{},
|
||||
getNSCache: func(_ context.Context, _ client.Object) (ctrlcache.Cache, error) {
|
||||
return &fakeMultiNamespaceCache{Store: store}, nil
|
||||
},
|
||||
}
|
||||
return c, store, nil
|
||||
}
|
||||
|
||||
func TestCreateSyncsCache(t *testing.T) {
|
||||
c, store, err := newClient()
|
||||
require.NoError(t, err)
|
||||
|
||||
app := &application.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "argocd"},
|
||||
}
|
||||
require.NoError(t, c.Create(context.Background(), app))
|
||||
|
||||
require.Contains(t, store.List(), app)
|
||||
}
|
||||
|
||||
func TestUpdateSyncsCache(t *testing.T) {
|
||||
app := &application.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "argocd",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
c, store, err := newClient(app)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedApp := app.DeepCopy()
|
||||
updatedApp.Labels["foo"] = "bar-UPDATED"
|
||||
require.NoError(t, c.Update(context.Background(), updatedApp))
|
||||
|
||||
updated, _, err := store.GetByKey("test")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "bar-UPDATED", updated.(*application.Application).Labels["foo"])
|
||||
}
|
||||
|
||||
func TestDeleteSyncsCache(t *testing.T) {
|
||||
app := &application.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "argocd",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
c, store, err := newClient(app)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, c.Delete(context.Background(), app))
|
||||
|
||||
require.Empty(t, store.List())
|
||||
}
|
||||
|
||||
func TestNewClientDoesNotCrashWithMultiNamespaceCache(_ *testing.T) {
|
||||
_ = NewCacheSyncingClient(nil, &fakeMultiNamespaceCache{})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user