diff --git a/.github/workflows/release-tag.yaml b/.github/workflows/release-tag.yaml index 13f5d54..3cce51f 100644 --- a/.github/workflows/release-tag.yaml +++ b/.github/workflows/release-tag.yaml @@ -144,3 +144,9 @@ jobs: - name: Inspect image run: | docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ github.repository }}:${{ steps.meta.outputs.version }} + update-crd-doc: + runs-on: ubuntu-latest + steps: + - name: Update CRD doc + run: | + curl -s https://doc.crds.dev/github.com/${{ github.repository_owner }}/{{ github.repository }}@${{ github.ref_name }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..2357909 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,20 @@ +name: Test and coverage + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 'stable' + - name: Gather dependencies + run: go mod download + - name: Run coverage + run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 9c06610..0000000 --- a/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# Set the shell to bash always -SHELL := /bin/bash - -# Look for a .env file, and if present, set make variables from it. -ifneq (,$(wildcard ./.env)) - include .env - export $(shell sed 's/=.*//' .env) -endif - -KIND_CLUSTER_NAME ?= local-dev -KUBECONFIG ?= $(HOME)/.kube/config - -VERSION := $(shell git describe --always --tags | sed 's/-/./2' | sed 's/-/./2') -ifndef VERSION -VERSION := 0.0.0 -endif - -# Tools -KIND=$(shell which kind) -LINT=$(shell which golangci-lint) -KUBECTL=$(shell which kubectl) -SED=$(shell which sed) - -.DEFAULT_GOAL := help - - -.PHONY: dev -dev: generate ## run the controller in debug mode - $(KUBECTL) apply -f crds/ -R - go run cmd/main.go -d - -.PHONY: generate -generate: tidy ## generate all CRDs - go generate ./... - -.PHONY: tidy -tidy: ## go mod tidy - go mod tidy - -.PHONY: test -test: ## go test - go test -v ./... - -.PHONY: lint -lint: ## go lint - $(LINT) run - -.PHONY: kind-up -kind-up: ## starts a KinD cluster for local development - @$(KIND) get kubeconfig --name $(KIND_CLUSTER_NAME) >/dev/null 2>&1 || $(KIND) create cluster --name=$(KIND_CLUSTER_NAME) - -.PHONY: kind-down -kind-down: ## shuts down the KinD cluster - @$(KIND) delete cluster --name=$(KIND_CLUSTER_NAME) - - -.PHONY: demo -demo: ## Run the demo examples - @$(KUBECTL) create secret generic azuredevops-endpoint --from-literal=token=$(TOKEN) || true - @$(KUBECTL) apply -f samples/claim.yaml - - -.PHONY: help -help: ## print this help - @grep -E '^[a-zA-Z\._-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/README.md b/README.md index 1e4b167..a682750 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,73 @@ # Git Provider -This is a [Krateo](https://krateoplatformops.github.io/) Provider that clones git repositories (eventually applying templates). +This is a [Krateo](https://krateo.io) Provider that clones git repositories (eventually applying templates). -## Getting Started +## Summary -You’ll need a Kubernetes cluster to run against. +- [Summary](#summary) +- [Overview](#overview) +- [Examples](#examples) +- [Configuration](#configuration) + -> You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster. +## Overview +Git Provider clones git repositories and may apply [Mustache templates](https://mustache.github.io). It then pushes the cloned and modified repository to a different location. The templating values are retrieved in a configmap referenced in the custom resource. +It provides automatic reconciliation when changes are retrieved from the original repository. -### Running on the cluster +Git Provider leverages Krateo [provider-runtime](https://docs.krateo.io/key-concepts/kco/#provider-runtime) a production-grade version of the controller-runtime. -1. Install the provider: +## Examples -```sh +### Provider Installation + +```bash $ helm repo add krateo https://charts.krateo.io $ helm repo update krateo $ helm install git-provider krateo/git-provider ``` -2. Install Instances of Custom Resources: - -```sh -$ kubectl apply -f samples/ -``` - -### Test It Out - -1. Start a local cluster using [KIND](https://sigs.k8s.io/kind): - -```sh -$ make kind-up -``` - -2. Run your provider (this will run in the foreground, so switch to a new terminal if you want to leave it running): - -```sh -$ make dev -``` - -### Modifying the API definitions -If you are editing the API definitions, generate the CRDs using: - -```sh -$ make generate +### Manifest application + +```yaml +apiVersion: git.krateo.io/v1alpha1 +kind: Repo +metadata: + name: git-azuredevops-branch-5 +spec: + enableUpdate: false + configMapKeyRef: + key: values + name: filename-replace-values + namespace: default + deletionPolicy: Delete + fromRepo: + authMethod: generic + branch: main + path: skeleton/ + usernameRef: + key: username + name: github-user + namespace: default + secretRef: + key: token + name: github-token + namespace: default + url: https://github.com/matteogastaldello/fromRepo + toRepo: + authMethod: generic + branch: test-5 + usernameRef: + key: username + name: azure-user + namespace: default + secretRef: + key: token + name: azure-token + namespace: default + url: https://matteogastaldello-org@dev.azure.com/matteogastaldello-org/teamproject/_git/repo-generated + unsupportedCapabilities: true ``` -**NOTE:** Run `make help` for more information on all potential `make` targets +## Configuration +To view the CR configuration visit [this link](https://doc.crds.dev/github.com/krateoplatformops/git-provider). \ No newline at end of file diff --git a/apis/repo/v1alpha1/types.go b/apis/repo/v1alpha1/types.go index 3cbf8c8..68aac63 100644 --- a/apis/repo/v1alpha1/types.go +++ b/apis/repo/v1alpha1/types.go @@ -8,28 +8,30 @@ import ( ) type RepoOpts struct { - // Url: the repository URL. + // Url: url of the remote repository // +immutable Url string `json:"url"` - // Path: name of the folder in the git repository - // to copy from (or to). + // Path: if in spec.fromRepo, Represents the folder to clone from. If not set the entire repository is cloned. If in spec.toRepo, represents the folder to use as destination. + // +kubebuilder:default:="/" // +optional Path *string `json:"path,omitempty"` - // Branch: in the git repository to copy from (or to). + // Branch: if in spec.fromRepo, the branch to copy from. If in spec.toRepo, represents the branch to populate; If the branch does not exist on remote is created by the provider. // +required Branch *string `json:"branch"` - // SecretRef: holds token required to git server authentication or cookie file in case of 'cookiefile' authMethod. + // SecretRef: reference to a secret that contains token required to git server authentication or cookie file in case of 'cookiefile' authMethod. SecretRef *commonv1.SecretKeySelector `json:"secretRef"` - // UsernameRef: holds username required to git server authentication. - If 'authMethod' is 'bearer' the field is ignored. If the field is not set, username is setted as 'krateoctl' + // UsernameRef: holds username required to git server authentication. - If 'authMethod' is 'bearer' or 'cookiefile' the field is ignored. If the field is not set, username is setted as 'krateoctl' // +optional UsernameRef *commonv1.SecretKeySelector `json:"usernameRef"` - // AuthMethod defines the authentication mode. One of 'basic' or 'bearer' or 'cookiefile'. + // AuthMethod: Possible values are: `generic`, `bearer`, `gitcookies`. `generic` requires `secretRef` and `usernameRef`; `generic` requires only `secretRef`; `cookiefile` requires only `secretRef` // In case of 'cookiefile' the secretRef must contain a file with the cookie. + // +kubebuilder:validation:Enum=generic;bearer;cookiefile + // +kubebuilder:default:=generic // +optional AuthMethod *string `json:"authMethod,omitempty"` @@ -56,18 +58,17 @@ type RepoSpec struct { // +optional ConfigMapKeyRef *commonv1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"` - // Insecure is useful with hand made SSL certs (default: false) + // Insecure: Insecure is useful with hand made SSL certs (default: false) // +optional Insecure *bool `json:"insecure,omitempty"` - // UnsupportedCapabilities enable Go-Git transport.UnsupportedCapabilities (default: false) - // Azure DevOps requires capabilities multi_ack / multi_ack_detailed, - // which are not fully implemented in go-git library and by default - // are included in transport.UnsupportedCapabilities. + // UnsupportedCapabilities: If `true` [capabilities not supported by any client implementation](https://github.com/go-git/go-git/blob/4fd9979d5c2940e72bdd6946fec21e02d959f0f6/plumbing/transport/common.go#L310) will not be used by the provider // +optional + // +kubebuilder:default:=false UnsupportedCapabilities *bool `json:"unsupportedCapabilities,omitempty"` - // Enable sync with origin repo. Target repo will be reconciled the changes on origin - experimental (default: false) + // EnableUpdate: If `true`, the provider performs updates on the repository specified in `toRepo` when newer commits are retrieved from `fromRepo` + // +kubebuilder:default:=false // +optional EnableUpdate *bool `json:"enableUpdate,omitempty"` } diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..afdfb5f --- /dev/null +++ b/codecov.yml @@ -0,0 +1,7 @@ +# sample regex patterns +ignore: + - "internal/controllers/repo/repo.go" + - "internal/controllers/repo/setup.go" + - "internal/controllers/git.go" + - "apis" + - "cmd/*.go" \ No newline at end of file diff --git a/crds/git.krateo.io_repoes.yaml b/crds/git.krateo.io_repoes.yaml index 4d495f2..95ca14e 100644 --- a/crds/git.krateo.io_repoes.yaml +++ b/crds/git.krateo.io_repoes.yaml @@ -82,19 +82,28 @@ spec: - namespace type: object enableUpdate: - description: 'Enable sync with origin repo. Target repo will be reconciled - the changes on origin - experimental (default: false)' + default: false + description: 'EnableUpdate: If `true`, the provider performs updates + on the repository specified in `toRepo` when newer commits are retrieved + from `fromRepo`' type: boolean fromRepo: description: 'FromRepo: repo origin to copy from' properties: authMethod: + default: generic description: |- - AuthMethod defines the authentication mode. One of 'basic' or 'bearer' or 'cookiefile'. + AuthMethod: Possible values are: `generic`, `bearer`, `gitcookies`. `generic` requires `secretRef` and `usernameRef`; `generic` requires only `secretRef`; `cookiefile` requires only `secretRef` In case of 'cookiefile' the secretRef must contain a file with the cookie. + enum: + - generic + - bearer + - cookiefile type: string branch: - description: 'Branch: in the git repository to copy from (or to).' + description: 'Branch: if in spec.fromRepo, the branch to copy + from. If in spec.toRepo, represents the branch to populate; + If the branch does not exist on remote is created by the provider.' type: string cloneFromBranch: description: |- @@ -103,13 +112,15 @@ spec: - If the parameter is not set, the branch is created empty and has no parents (no history) - `git switch --orphan branch-name` type: string path: - description: |- - Path: name of the folder in the git repository - to copy from (or to). + default: / + description: 'Path: if in spec.fromRepo, Represents the folder + to clone from. If not set the entire repository is cloned. If + in spec.toRepo, represents the folder to use as destination.' type: string secretRef: - description: 'SecretRef: holds token required to git server authentication - or cookie file in case of ''cookiefile'' authMethod.' + description: 'SecretRef: reference to a secret that contains token + required to git server authentication or cookie file in case + of ''cookiefile'' authMethod.' properties: key: description: The key to select. @@ -126,12 +137,13 @@ spec: - namespace type: object url: - description: 'Url: the repository URL.' + description: 'Url: url of the remote repository' type: string usernameRef: description: 'UsernameRef: holds username required to git server - authentication. - If ''authMethod'' is ''bearer'' the field - is ignored. If the field is not set, username is setted as ''krateoctl''' + authentication. - If ''authMethod'' is ''bearer'' or ''cookiefile'' + the field is ignored. If the field is not set, username is setted + as ''krateoctl''' properties: key: description: The key to select. @@ -153,19 +165,26 @@ spec: - url type: object insecure: - description: 'Insecure is useful with hand made SSL certs (default: - false)' + description: 'Insecure: Insecure is useful with hand made SSL certs + (default: false)' type: boolean toRepo: description: 'ToRepo: repo destination to copy to' properties: authMethod: + default: generic description: |- - AuthMethod defines the authentication mode. One of 'basic' or 'bearer' or 'cookiefile'. + AuthMethod: Possible values are: `generic`, `bearer`, `gitcookies`. `generic` requires `secretRef` and `usernameRef`; `generic` requires only `secretRef`; `cookiefile` requires only `secretRef` In case of 'cookiefile' the secretRef must contain a file with the cookie. + enum: + - generic + - bearer + - cookiefile type: string branch: - description: 'Branch: in the git repository to copy from (or to).' + description: 'Branch: if in spec.fromRepo, the branch to copy + from. If in spec.toRepo, represents the branch to populate; + If the branch does not exist on remote is created by the provider.' type: string cloneFromBranch: description: |- @@ -174,13 +193,15 @@ spec: - If the parameter is not set, the branch is created empty and has no parents (no history) - `git switch --orphan branch-name` type: string path: - description: |- - Path: name of the folder in the git repository - to copy from (or to). + default: / + description: 'Path: if in spec.fromRepo, Represents the folder + to clone from. If not set the entire repository is cloned. If + in spec.toRepo, represents the folder to use as destination.' type: string secretRef: - description: 'SecretRef: holds token required to git server authentication - or cookie file in case of ''cookiefile'' authMethod.' + description: 'SecretRef: reference to a secret that contains token + required to git server authentication or cookie file in case + of ''cookiefile'' authMethod.' properties: key: description: The key to select. @@ -197,12 +218,13 @@ spec: - namespace type: object url: - description: 'Url: the repository URL.' + description: 'Url: url of the remote repository' type: string usernameRef: description: 'UsernameRef: holds username required to git server - authentication. - If ''authMethod'' is ''bearer'' the field - is ignored. If the field is not set, username is setted as ''krateoctl''' + authentication. - If ''authMethod'' is ''bearer'' or ''cookiefile'' + the field is ignored. If the field is not set, username is setted + as ''krateoctl''' properties: key: description: The key to select. @@ -224,11 +246,10 @@ spec: - url type: object unsupportedCapabilities: - description: |- - UnsupportedCapabilities enable Go-Git transport.UnsupportedCapabilities (default: false) - Azure DevOps requires capabilities multi_ack / multi_ack_detailed, - which are not fully implemented in go-git library and by default - are included in transport.UnsupportedCapabilities. + default: false + description: 'UnsupportedCapabilities: If `true` [capabilities not + supported by any client implementation](https://github.com/go-git/go-git/blob/4fd9979d5c2940e72bdd6946fec21e02d959f0f6/plumbing/transport/common.go#L310) + will not be used by the provider' type: boolean required: - fromRepo diff --git a/go.mod b/go.mod index b901d80..f6f9ecc 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,14 @@ toolchain go1.23.1 require ( github.com/cbroglie/mustache v1.4.0 github.com/go-git/go-billy/v5 v5.5.0 + github.com/go-git/go-git v4.7.0+incompatible + github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 github.com/go-git/go-git/v5 v5.12.0 github.com/krateoplatformops/provider-runtime v0.9.0 - github.com/lucasepe/dotenv v0.1.0 github.com/pkg/errors v0.9.1 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/stoewer/go-strcase v1.3.0 + github.com/stretchr/testify v1.9.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 @@ -34,6 +36,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fatih/color v1.17.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -59,6 +62,8 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.17.9 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -66,14 +71,17 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.20.2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.2.2 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/src-d/gcfg v1.4.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -91,7 +99,10 @@ require ( golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/src-d/go-git.v4 v4.13.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 203f871..fb197ac 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,13 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -24,6 +27,8 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo= github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,24 +39,29 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcej github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA= +github.com/go-git/go-git v4.7.0+incompatible/go.mod h1:6+421e08gnZWn30y26Vchf7efgYLe4dl5OQbBSUXShE= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= @@ -78,6 +88,7 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -94,10 +105,12 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -105,9 +118,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -115,8 +130,6 @@ github.com/krateoplatformops/provider-runtime v0.9.0 h1:ZvgJbfmv4Zx+Z/a4sat6xF88 github.com/krateoplatformops/provider-runtime v0.9.0/go.mod h1:A0OKDAXE9KnX1GyhZH0UpZhpn15xQANoc4KVYLsfZM0= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lucasepe/dotenv v0.1.0 h1:w9JoOcuic7Hn4BhvP+rGk6UJgOsHTWb2KkJGGtuwZ7I= -github.com/lucasepe/dotenv v0.1.0/go.mod h1:1kAFKdDhPXR+pkTgBfjSOmKzCf+wrY29OP727UhJ5rA= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -124,6 +137,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -139,8 +153,11 @@ github.com/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -154,11 +171,13 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -168,9 +187,12 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -184,6 +206,7 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -195,7 +218,9 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -214,6 +239,7 @@ golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -234,7 +260,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -259,6 +287,7 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -270,6 +299,7 @@ golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -288,6 +318,7 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -295,6 +326,12 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= diff --git a/internal/clients/git/fakeRepo.go b/internal/clients/git/fakeRepo.go new file mode 100644 index 0000000..bab8a58 --- /dev/null +++ b/internal/clients/git/fakeRepo.go @@ -0,0 +1,74 @@ +package git + +import ( + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/memfs" + fixtures "github.com/go-git/go-git-fixtures/v4" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/cache" + "github.com/go-git/go-git/v5/storage/filesystem" +) + +type BaseSuite struct { + fixtures.Suite + Repository *git.Repository + cache map[string]*git.Repository +} + +func (s *BaseSuite) SetUpSuite() { + s.BuildBasicRepository() + + s.cache = make(map[string]*git.Repository) +} + +// func (s *BaseSuite) TearDownSuite() { +// s.Suite.TearDownSuite() +// } + +func (s *BaseSuite) BuildBasicRepository() { + f := fixtures.Basic().One() + s.Repository = s.NewRepository(f) +} + +// NewRepository returns a new repository using the .git folder, if the fixture +// is tagged as worktree the filesystem from fixture is used, otherwise a new +// memfs filesystem is used as worktree. +func (s *BaseSuite) NewRepository(f *fixtures.Fixture) *git.Repository { + var worktree, dotgit billy.Filesystem + if f.Is("worktree") { + r, err := PlainOpen(f.Worktree().Root()) + if err != nil { + panic(err) + } + + return r + } + + dotgit = f.DotGit() + worktree = memfs.New() + + st := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault()) + + r, err := git.Open(st, worktree) + if err != nil { + panic(err) + } + + return r +} + +// PlainOpen opens a git repository from the given path. It detects if the +// repository is bare or a normal one. If the path doesn't contain a valid +// repository ErrRepositoryNotExists is returned +func PlainOpen(path string) (*git.Repository, error) { + return git.PlainOpenWithOptions(path, &git.PlainOpenOptions{}) +} + +func (s *BaseSuite) GetBasicLocalRepositoryURL() string { + fixture := fixtures.Basic().One() + return s.GetLocalRepositoryURL(fixture) +} + +func (s *BaseSuite) GetLocalRepositoryURL(f *fixtures.Fixture) string { + return f.DotGit().Root() +} diff --git a/internal/clients/git/git_test.go b/internal/clients/git/git_test.go index f167c46..c9a73c3 100644 --- a/internal/clients/git/git_test.go +++ b/internal/clients/git/git_test.go @@ -1,100 +1,159 @@ -//go:build integration -// +build integration - package git import ( - "io" "os" - "strings" "testing" - "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/lucasepe/dotenv" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/storage/memory" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestClone(t *testing.T) { - setEnv() +func TestIsInGitCommitHistory(t *testing.T) { - repo, err := Clone(CloneOptions{ - URL: os.Getenv("URL"), - Auth: &http.BasicAuth{ - Username: "krateoctl", - Password: os.Getenv("TOKEN"), - }, - Insecure: true, - UnsupportedCapabilities: true, - }) - if err != nil { - t.Fatal(err) + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() + + opts := ListOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), } - all, err := repo.FS().ReadDir("/") + hash := "0123456789abcdef0123456789abcdef01234567" + + exists, err := IsInGitCommitHistory(opts, hash) if err != nil { - t.Fatal(err) + t.Errorf("Error checking commit history: %v", err) } - for _, el := range all { - t.Log(el.Name()) + if exists { + t.Logf("Commit %s exists in the Git repository", hash) + } else { + t.Logf("Commit %s does not exist in the Git repository", hash) } } -func TestWriteBytes(t *testing.T) { - setEnv() +func TestGetLatestCommitRemote(t *testing.T) { + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() repo, err := Clone(CloneOptions{ - URL: os.Getenv("URL"), - Auth: &http.BasicAuth{ - Username: "krateoctl", - Password: os.Getenv("TOKEN"), - }, - Insecure: true, - UnsupportedCapabilities: true, + URL: baseRepo.GetBasicLocalRepositoryURL(), }) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) - out, err := repo.FS().Create("JUST_TO_TEST_A_ISSUE.md") - if err != nil { - t.Fatal(err) - } + _, err = repo.FS().OpenFile("README.md", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) - defer func() { - if e := out.Close(); e != nil { - err = e - } - }() + commit, err := GetLatestCommitRemote(ListOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + Branch: "master", + }) + require.NoError(t, err) + expected := "6ecf0ef2c2dffb796033e5a02219af86ec6584e5" + assert.Equal(t, expected, *commit) - in := strings.NewReader("Please be patient...") - if _, err = io.Copy(out, in); err != nil { - t.Fatal(err) - } + commit, err = GetLatestCommitRemote(ListOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + Branch: "branch", + }) + require.NoError(t, err) + expected = "e8d3ffab552895c19b9fcf7aa264d277cde33881" + assert.Equal(t, expected, *commit) +} - all, err := repo.FS().ReadDir("/") - if err != nil { - t.Fatal(err) - } +func TestPush(t *testing.T) { + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() - for _, el := range all { - t.Log(el.Name()) - } + repo, err := Clone(CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + }) + require.NoError(t, err) - t.Logf("current branch: %s", repo.CurrentBranch()) + err = repo.Push("origin", "master", false) + require.ErrorIs(t, err, git.NoErrAlreadyUpToDate) - commitId, err := repo.Commit(".", "first commit") - if err != nil { - t.Fatal(err) - } - t.Logf("commitId: %s", commitId) + _, err = repo.FS().OpenFile("README.md", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) - err = repo.Push("origin", repo.CurrentBranch(), true) - if err != nil { - t.Fatal(err) - } + repo.storer.IndexStorage = memory.IndexStorage{} + _, err = repo.Commit(".", "Initial commit", &IndexOptions{ + OriginRepo: repo, + FromPath: "/", + ToPath: "/", + }) + require.NoError(t, err) + + err = repo.Push("origin", "test", false) + require.NoError(t, err) } -func setEnv() { - env, _ := dotenv.FromFile("../../../.env") - dotenv.PutInEnv(env, false) +func TestPull(t *testing.T) { + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() + + repo, err := Clone(CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + Branch: "master", + }) + require.NoError(t, err) + + err = Pull(repo, false) + require.NoError(t, err) +} + +func TestBranch(t *testing.T) { + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() + + repo, err := Clone(CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + Branch: "master", + }) + require.NoError(t, err) + + err = repo.Branch("test", &CreateOpt{ + Create: true, + Orphan: false, + }) + require.NoError(t, err) + + err = repo.Branch("test-orphan", &CreateOpt{ + Create: true, + Orphan: true, + }) + require.NoError(t, err) + + err = repo.Branch("test", nil) + require.NoError(t, err) +} + +func TestCurrentBranch(t *testing.T) { + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() + + repo, err := Clone(CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + Branch: "master", + }) + require.NoError(t, err) + + branch := repo.CurrentBranch() + assert.Equal(t, "master", branch) +} + +func TestGetLatestCommit(t *testing.T) { + baseRepo := BaseSuite{} + baseRepo.BuildBasicRepository() + + repo, err := Clone(CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + }) + require.NoError(t, err) + + commit, err := repo.GetLatestCommit("master") + require.NoError(t, err) + expected := "6ecf0ef2c2dffb796033e5a02219af86ec6584e5" + assert.Equal(t, expected, commit) } diff --git a/internal/controllers/repo/copier_test.go b/internal/controllers/repo/copier_test.go new file mode 100644 index 0000000..5d95c9c --- /dev/null +++ b/internal/controllers/repo/copier_test.go @@ -0,0 +1,42 @@ +package repo + +import ( + "os" + "testing" + + "github.com/krateoplatformops/git-provider/internal/clients/git" + "github.com/stretchr/testify/require" +) + +func TestCopier(t *testing.T) { + baseRepo := git.BaseSuite{} + baseRepo.BuildBasicRepository() + origin, err := git.Clone(git.CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + }) + require.NoError(t, err) + + f, _ := origin.FS().OpenFile(".krateoignore", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) + f.Write([]byte("*")) + f.Close() + _, err = origin.FS().OpenFile("file1.txt", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) + _, err = origin.FS().OpenFile("file2.txt", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) + + targetRepo := git.BaseSuite{} + targetRepo.BuildBasicRepository() + target, err := git.Clone(git.CloneOptions{ + URL: targetRepo.GetBasicLocalRepositoryURL(), + }) + require.NoError(t, err) + + co := newCopier(origin, target, "/", "/") + err = loadIgnoreTargetFiles("/", co) + require.NoError(t, err) + err = loadIgnoreFileEventually(co) + require.NoError(t, err) + err = co.copyDir("/", "/") + require.NoError(t, err) +} diff --git a/internal/controllers/repo/repo.go b/internal/controllers/repo/repo.go index 8b00eb9..dd0262c 100644 --- a/internal/controllers/repo/repo.go +++ b/internal/controllers/repo/repo.go @@ -4,12 +4,8 @@ import ( "context" "encoding/json" "fmt" - "io" - "path/filepath" "strings" - "github.com/cbroglie/mustache" - "github.com/go-git/go-billy/v5" "github.com/pkg/errors" commonv1 "github.com/krateoplatformops/provider-runtime/apis/common/v1" @@ -27,8 +23,6 @@ import ( "github.com/krateoplatformops/git-provider/internal/clients/git" "github.com/krateoplatformops/git-provider/internal/ptr" - gi "github.com/sabhiram/go-gitignore" - corev1 "k8s.io/api/core/v1" ) @@ -358,82 +352,3 @@ func (e *external) SyncRepos(ctx context.Context, cr *repov1alpha1.Repo, commitM } return nil } - -func createRenderFuncs(co *copier, values interface{}) { - co.renderFunc = func(in io.Reader, out io.Writer) error { - bin, err := io.ReadAll(in) - if err != nil { - return fmt.Errorf("failed to read file: %w", err) - } - tmpl, err := mustache.ParseString(string(bin)) - if err != nil { - return fmt.Errorf("failed to parse template: %w", err) - } - - return tmpl.FRender(out, values) - } - co.renderFileNames = func(src string) (string, error) { - tmpl, err := mustache.ParseString(src) - if err != nil { - return "", fmt.Errorf("failed to parse file names: %w", err) - } - - s, err := tmpl.Render(values) - if err != nil { - return "", fmt.Errorf("failed to render file names: %w", err) - } - return s, nil - } - -} - -func loadIgnoreFileEventually(co *copier) error { - fp, err := co.fromRepo.FS().Open(".krateoignore") - if err != nil { - return err - } - defer fp.Close() - - bs, err := io.ReadAll(fp) - if err != nil { - return err - } - - lines := strings.Split(string(bs), "\n") - - co.krateoIgnore = gi.CompileIgnoreLines(lines...) - - return nil -} - -func loadFilesIntoArray(fs billy.Filesystem, dir string, flist *[]string) error { - files, err := fs.ReadDir(dir) - if err != nil { - return err - } - - for _, file := range files { - if file.IsDir() { - err := loadFilesIntoArray(fs, filepath.Join(dir, file.Name()), flist) - if err != nil { - return err - } - } else { - absPath := filepath.Join(dir, file.Name()) - *flist = append(*flist, absPath) - } - } - - return nil -} - -func loadIgnoreTargetFiles(srcPath string, co *copier) error { - fs := co.toRepo.FS() - var flist []string - err := loadFilesIntoArray(fs, srcPath, &flist) - if err != nil { - return err - } - co.targetIgnore = gi.CompileIgnoreLines(flist...) - return nil -} diff --git a/internal/controllers/repo/setup.go b/internal/controllers/repo/setup.go index ec896f5..d8d21b1 100644 --- a/internal/controllers/repo/setup.go +++ b/internal/controllers/repo/setup.go @@ -2,10 +2,8 @@ package repo import ( "context" - "strings" repov1alpha1 "github.com/krateoplatformops/git-provider/apis/repo/v1alpha1" - "github.com/krateoplatformops/git-provider/internal/ptr" "github.com/krateoplatformops/provider-runtime/pkg/reconciler" "github.com/krateoplatformops/provider-runtime/pkg/controller" @@ -17,9 +15,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/go-git/go-git/v5/plumbing/transport" - githttp "github.com/go-git/go-git/v5/plumbing/transport/http" - "github.com/pkg/errors" ) @@ -73,95 +68,3 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (reconcile rec: c.recorder, }, nil } - -type externalClientOpts struct { - Insecure bool - UnsupportedCapabilities bool - FromRepoCreds transport.AuthMethod - ToRepoCreds transport.AuthMethod - FromRepoCookieFile []byte - ToRepoCookieFile []byte -} - -func loadExternalClientOpts(ctx context.Context, kc client.Client, cr *repov1alpha1.Repo) (*externalClientOpts, error) { - var fromRepoCookie, toRepoCookie []byte - fromRepoCreds, err := getRepoCredentials(ctx, kc, cr.Spec.FromRepo) - if err != nil { - return nil, errors.Wrapf(err, "retrieving from repo credentials") - } - fromRepoCookie = nil - if fromRepoCreds == nil { - fromRepoCookie, err = getRepoCookies(ctx, kc, cr.Spec.FromRepo) - if err != nil { - return nil, errors.Wrapf(err, "retrieving from repo cookies") - } - } - - toRepoCreds, err := getRepoCredentials(ctx, kc, cr.Spec.ToRepo) - if err != nil { - return nil, errors.Wrapf(err, "retrieving to repo credentials") - } - if toRepoCreds == nil { - toRepoCookie, err = getRepoCookies(ctx, kc, cr.Spec.ToRepo) - if err != nil { - return nil, errors.Wrapf(err, "retrieving to repo cookies") - } - } - - // fmt.Println("ToRepoCookieFile", string(toRepoCookie)) - - return &externalClientOpts{ - Insecure: ptr.BoolFromPtr(cr.Spec.Insecure), - UnsupportedCapabilities: ptr.BoolFromPtr(cr.Spec.UnsupportedCapabilities), - FromRepoCreds: fromRepoCreds, - ToRepoCreds: toRepoCreds, - FromRepoCookieFile: fromRepoCookie, - ToRepoCookieFile: toRepoCookie, - }, nil -} - -func getRepoCookies(ctx context.Context, k client.Client, opts repov1alpha1.RepoOpts) ([]byte, error) { - if opts.SecretRef == nil { - return nil, nil - } - - sec, err := resource.GetSecret(ctx, k, opts.SecretRef) - - return []byte(sec), err -} - -// getRepoCredentials returns the from repo credentials stored in a secret. -func getRepoCredentials(ctx context.Context, k client.Client, opts repov1alpha1.RepoOpts) (transport.AuthMethod, error) { - if opts.SecretRef == nil { - return nil, nil - } - - token, err := resource.GetSecret(ctx, k, opts.SecretRef) - if err != nil { - return nil, err - } - - authMethod := ptr.StringFromPtr(opts.AuthMethod) - if strings.EqualFold(authMethod, "bearer") { - return &githttp.TokenAuth{ - Token: token, - }, nil - } - - if strings.EqualFold(authMethod, "cookiefile") { - return nil, nil - } - - username := "krateoctl" - if opts.UsernameRef != nil { - username, err = resource.GetSecret(ctx, k, opts.UsernameRef) - if err != nil { - return nil, err - } - } - - return &githttp.BasicAuth{ - Username: username, - Password: token, - }, nil -} diff --git a/internal/controllers/repo/utils.go b/internal/controllers/repo/utils.go new file mode 100644 index 0000000..07ff12f --- /dev/null +++ b/internal/controllers/repo/utils.go @@ -0,0 +1,186 @@ +package repo + +import ( + "context" + "io" + "path/filepath" + "strings" + + githttp "github.com/go-git/go-git/v5/plumbing/transport/http" + repov1alpha1 "github.com/krateoplatformops/git-provider/apis/repo/v1alpha1" + + "github.com/cbroglie/mustache" + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-git/plumbing/transport" + "github.com/krateoplatformops/git-provider/internal/ptr" + "github.com/krateoplatformops/provider-runtime/pkg/resource" + "github.com/pkg/errors" + gi "github.com/sabhiram/go-gitignore" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type externalClientOpts struct { + Insecure bool + UnsupportedCapabilities bool + FromRepoCreds transport.AuthMethod + ToRepoCreds transport.AuthMethod + FromRepoCookieFile []byte + ToRepoCookieFile []byte +} + +func loadExternalClientOpts(ctx context.Context, kc client.Client, cr *repov1alpha1.Repo) (*externalClientOpts, error) { + var fromRepoCookie, toRepoCookie []byte + fromRepoCreds, err := getRepoCredentials(ctx, kc, cr.Spec.FromRepo) + if err != nil { + return nil, errors.Wrapf(err, "retrieving from repo credentials") + } + fromRepoCookie = nil + if fromRepoCreds == nil { + fromRepoCookie, err = getRepoCookies(ctx, kc, cr.Spec.FromRepo) + if err != nil { + return nil, errors.Wrapf(err, "retrieving from repo cookies") + } + } + + toRepoCreds, err := getRepoCredentials(ctx, kc, cr.Spec.ToRepo) + if err != nil { + return nil, errors.Wrapf(err, "retrieving to repo credentials") + } + if toRepoCreds == nil { + toRepoCookie, err = getRepoCookies(ctx, kc, cr.Spec.ToRepo) + if err != nil { + return nil, errors.Wrapf(err, "retrieving to repo cookies") + } + } + + // fmt.Println("ToRepoCookieFile", string(toRepoCookie)) + + return &externalClientOpts{ + Insecure: ptr.BoolFromPtr(cr.Spec.Insecure), + UnsupportedCapabilities: ptr.BoolFromPtr(cr.Spec.UnsupportedCapabilities), + FromRepoCreds: fromRepoCreds, + ToRepoCreds: toRepoCreds, + FromRepoCookieFile: fromRepoCookie, + ToRepoCookieFile: toRepoCookie, + }, nil +} + +func getRepoCookies(ctx context.Context, k client.Client, opts repov1alpha1.RepoOpts) ([]byte, error) { + if opts.SecretRef == nil { + return nil, nil + } + + sec, err := resource.GetSecret(ctx, k, opts.SecretRef) + + return []byte(sec), err +} + +// getRepoCredentials returns the from repo credentials stored in a secret. +func getRepoCredentials(ctx context.Context, k client.Client, opts repov1alpha1.RepoOpts) (transport.AuthMethod, error) { + if opts.SecretRef == nil { + return nil, nil + } + + token, err := resource.GetSecret(ctx, k, opts.SecretRef) + if err != nil { + return nil, err + } + + authMethod := ptr.StringFromPtr(opts.AuthMethod) + if strings.EqualFold(authMethod, "bearer") { + return &githttp.TokenAuth{ + Token: token, + }, nil + } + + if strings.EqualFold(authMethod, "cookiefile") { + return nil, nil + } + + username := "krateoctl" + if opts.UsernameRef != nil { + username, err = resource.GetSecret(ctx, k, opts.UsernameRef) + if err != nil { + return nil, err + } + } + + return &githttp.BasicAuth{ + Username: username, + Password: token, + }, nil +} + +func createRenderFuncs(co *copier, values interface{}) { + co.renderFunc = func(in io.Reader, out io.Writer) error { + bin, err := io.ReadAll(in) + if err != nil { + return err + } + tmpl, err := mustache.ParseString(string(bin)) + if err != nil { + return err + } + + return tmpl.FRender(out, values) + } + co.renderFileNames = func(src string) (string, error) { + tmpl, err := mustache.ParseString(src) + if err != nil { + return "", err + } + return tmpl.Render(values) + } + +} + +func loadIgnoreFileEventually(co *copier) error { + fp, err := co.fromRepo.FS().Open(".krateoignore") + if err != nil { + return err + } + defer fp.Close() + + bs, err := io.ReadAll(fp) + if err != nil { + return err + } + + lines := strings.Split(string(bs), "\n") + + co.krateoIgnore = gi.CompileIgnoreLines(lines...) + + return nil +} + +func loadFilesIntoArray(fs billy.Filesystem, dir string, flist *[]string) error { + files, err := fs.ReadDir(dir) + if err != nil { + return err + } + + for _, file := range files { + if file.IsDir() { + err := loadFilesIntoArray(fs, filepath.Join(dir, file.Name()), flist) + if err != nil { + return err + } + } else { + absPath := filepath.Join(dir, file.Name()) + *flist = append(*flist, absPath) + } + } + + return nil +} + +func loadIgnoreTargetFiles(srcPath string, co *copier) error { + fs := co.toRepo.FS() + var flist []string + err := loadFilesIntoArray(fs, srcPath, &flist) + if err != nil { + return err + } + co.targetIgnore = gi.CompileIgnoreLines(flist...) + return nil +} diff --git a/internal/controllers/repo/utils_test.go b/internal/controllers/repo/utils_test.go new file mode 100644 index 0000000..49a8d4e --- /dev/null +++ b/internal/controllers/repo/utils_test.go @@ -0,0 +1,202 @@ +package repo + +import ( + "context" + "os" + "testing" + + "github.com/go-git/go-billy/v5/memfs" + githttp "github.com/go-git/go-git/v5/plumbing/transport/http" + repov1alpha1 "github.com/krateoplatformops/git-provider/apis/repo/v1alpha1" + "github.com/krateoplatformops/git-provider/internal/clients/git" + "github.com/krateoplatformops/git-provider/internal/ptr" + commonv1 "github.com/krateoplatformops/provider-runtime/apis/common/v1" + gi "github.com/sabhiram/go-gitignore" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestLoadExternalClientOpts(t *testing.T) { + ctx := context.TODO() + kc := fake.NewFakeClient() + + // Create the Repo object + + cr := &repov1alpha1.Repo{ + Spec: repov1alpha1.RepoSpec{ + FromRepo: repov1alpha1.RepoOpts{ + AuthMethod: ptr.PtrTo("bearer"), + SecretRef: &commonv1.SecretKeySelector{ + Key: "token", + Reference: commonv1.Reference{ + Name: "from-repo-secret", + Namespace: "default", + }, + }, + }, + ToRepo: repov1alpha1.RepoOpts{ + AuthMethod: ptr.PtrTo("generic"), + SecretRef: &commonv1.SecretKeySelector{ + Key: "token", + Reference: commonv1.Reference{ + Name: "to-repo-secret", + Namespace: "default", + }, + }, + }, + Insecure: ptr.PtrTo(true), + UnsupportedCapabilities: ptr.PtrTo(false), + }, + } + + // Create the secret objects + fromRepoSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "from-repo-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "token": []byte("from-repo-token"), + }, + } + toRepoSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "to-repo-secret", + Namespace: "default", + }, + Data: map[string][]byte{ + "token": []byte("to-repo-token"), + }, + } + + // Create the secrets in the cluster + require.NoError(t, kc.Create(ctx, fromRepoSecret)) + require.NoError(t, kc.Create(ctx, toRepoSecret)) + + opts, err := loadExternalClientOpts(ctx, kc, cr) + require.NoError(t, err) + + expectedOpts := &externalClientOpts{ + Insecure: true, + UnsupportedCapabilities: false, + FromRepoCreds: &githttp.TokenAuth{ + Token: "from-repo-token", + }, + ToRepoCreds: &githttp.BasicAuth{ + Username: "krateoctl", + Password: "to-repo-token", + }, + FromRepoCookieFile: nil, + ToRepoCookieFile: nil, + } + + assert.Equal(t, expectedOpts, opts) +} + +func TestGetRepoCookies(t *testing.T) { + ctx := context.TODO() + kc := fake.NewFakeClient() + + // Create the secret object + secretData := map[string][]byte{ + "cookie": []byte("repo-cookie"), + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "repo-secret", + Namespace: "default", + }, + Data: secretData, + } + + // Create the secret in the cluster + require.NoError(t, kc.Create(ctx, secret)) + + opts := repov1alpha1.RepoOpts{ + SecretRef: &commonv1.SecretKeySelector{ + Key: "cookie", + Reference: commonv1.Reference{ + Name: "repo-secret", + Namespace: "default", + }, + }, + } + + cookies, err := getRepoCookies(ctx, kc, opts) + require.NoError(t, err) + + expectedCookies := []byte("repo-cookie") + assert.Equal(t, expectedCookies, cookies) +} + +func TestLoadIgnoreTargetFiles(t *testing.T) { + + baseRepo := git.BaseSuite{} + baseRepo.BuildBasicRepository() + origin, err := git.Clone(git.CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + }) + require.NoError(t, err) + + targetRepo := git.BaseSuite{} + targetRepo.BuildBasicRepository() + target, err := git.Clone(git.CloneOptions{ + URL: baseRepo.GetBasicLocalRepositoryURL(), + }) + require.NoError(t, err) + + co := newCopier(origin, target, "/", "/") + + srcPath := "/path/to/source" + + err = loadIgnoreTargetFiles(srcPath, co) + require.NoError(t, err) + + expectedIgnore := gi.CompileIgnoreLines() + co.targetIgnore = expectedIgnore + + assert.Equal(t, expectedIgnore, co.targetIgnore) +} +func TestLoadFilesIntoArray(t *testing.T) { + fs := memfs.New() + + // Create some test files + err := fs.MkdirAll("/path/to/dir", 0755) + require.NoError(t, err) + + f1, err := fs.OpenFile("/path/to/file1.txt", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) + _, err = f1.Write([]byte("file1")) + require.NoError(t, err) + err = f1.Close() + require.NoError(t, err) + + f2, err := fs.OpenFile("/path/to/file2.txt", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) + _, err = f2.Write([]byte("file2")) + require.NoError(t, err) + err = f2.Close() + require.NoError(t, err) + + f3, err := fs.OpenFile("/path/to/dir/file3.txt", os.O_RDWR|os.O_CREATE, 0644) + require.NoError(t, err) + _, err = f3.Write([]byte("file3")) + require.NoError(t, err) + err = f3.Close() + require.NoError(t, err) + + var flist []string + err = loadFilesIntoArray(fs, "/path", &flist) + require.NoError(t, err) + + expectedFiles := []string{ + "/path/to/file1.txt", + "/path/to/file2.txt", + "/path/to/dir/file3.txt", + } + + assert.ElementsMatch(t, expectedFiles, flist) +} diff --git a/samples/repo.yaml b/samples/repo.yaml index 6326892..bbc4991 100644 --- a/samples/repo.yaml +++ b/samples/repo.yaml @@ -1,8 +1,9 @@ apiVersion: git.krateo.io/v1alpha1 kind: Repo metadata: - name: git-user + name: git-azuredevops-branch-5 spec: + enableUpdate: false configMapKeyRef: key: values name: filename-replace-values @@ -33,5 +34,5 @@ spec: key: username name: git-username namespace: default - url: https://github.com/matteogastaldello/toRepo + url: https://matteogastaldello-org@dev.azure.com/matteogastaldello-org/teamproject/_git/repo-generated unsupportedCapabilities: true diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..e8187aa --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,9 @@ +#!/bin/bash + + +# Deploy +# kubectl apply -f manifests/ns.yaml +# kubectl apply -f manifests/sa.yaml +# kubectl apply -f manifests/deployment.yaml +# kubectl apply -f manifests/registration.yaml +# kubectl apply -f manifests/service.yaml diff --git a/scripts/kind-down.sh b/scripts/kind-down.sh index 346675e..f3d0cff 100755 --- a/scripts/kind-down.sh +++ b/scripts/kind-down.sh @@ -1,3 +1,3 @@ #!/bin/bash -kind delete cluster \ No newline at end of file +kind delete cluster diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..f639ef1 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +go test ./... -coverprofile cover.out +go tool cover -func cover.out +rm -f cover.out