├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── ask-question.md │ ├── break-fix.md │ └── feature-request.md └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── VERSION ├── app.go ├── assets ├── app_test_go_backup ├── config │ ├── Caddyfile │ ├── post_cmd_exec │ │ └── Caddyfile │ ├── routeless │ │ └── Caddyfile │ ├── ssh │ │ └── Caddyfile │ └── webhook │ │ └── Caddyfile └── docs │ └── images │ └── logo.png ├── caddyfile.go ├── caddyfile_test.go ├── go.mod ├── go.sum ├── pkg ├── errors │ ├── config.go │ └── errors.go └── service │ ├── config.go │ ├── endpoint.go │ ├── manager.go │ ├── repo.go │ └── status.go └── plugin.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [greenpau] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ask-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask a question! 3 | about: There are no stupid questions! It is this project's documentation that needs improvement. Show ❤️️, give 🌟 4 | title: 'question: CHANGE_ME' 5 | labels: 'need triage' 6 | assignees: 'greenpau' 7 | 8 | --- 9 | 10 | > A clear and concise description of what you want to accomplish. 11 | 12 | CHANGE_ME 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/break-fix.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Things are not working! 3 | about: You think you are doing the right thing, but it is not working as expected. 4 | title: 'breakfix: CHANGE_ME' 5 | labels: 'need triage' 6 | assignees: 'greenpau' 7 | 8 | --- 9 | 10 | **Describe the issue** 11 | 12 | A clear and concise description of what the issue is. 13 | 14 | **Configuration** 15 | 16 | Paste full `Caddyfile` below: 17 | 18 | ``` 19 | Paste configuration here ... 20 | ``` 21 | 22 | **Version Information** 23 | 24 | Provide output of `caddy list-modules -versions | grep git` below: 25 | 26 | ``` 27 | Paste output here ... 28 | ``` 29 | 30 | **Expected behavior** 31 | 32 | Describe expected behavior. 33 | 34 | **Additional context** 35 | 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: You understand that some functionality/feature is not available and you want it added. 4 | title: 'feature: CHANGE_ME' 5 | labels: 'need triage' 6 | assignees: 'greenpau' 7 | 8 | --- 9 | 10 | > A clear and concise description of what you want the system to do. 11 | 12 | CHANGE_ME 13 | 14 | > What are the Caddyfile directives that need to be added. 15 | 16 | Add Caddyfile directive: 17 | 18 | ``` 19 | git { 20 | repo foo { 21 | 22 | } 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: build 3 | 4 | on: 5 | workflow_dispatch: {} 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | core: 15 | strategy: 16 | matrix: 17 | go-version: [1.20.x, 1.21.x] 18 | platform: [ubuntu-latest] 19 | name: Build 20 | runs-on: ${{ matrix.platform }} 21 | env: 22 | GOBIN: /home/runner/.local/bin 23 | steps: 24 | - name: Install Go 25 | uses: actions/setup-go@v3 26 | with: 27 | go-version: ${{ matrix.go-version }} 28 | id: go 29 | - name: Check out code into the Go module directory 30 | uses: actions/checkout@v3 31 | - name: Amend Environment Path 32 | run: | 33 | mkdir -p /home/runner/.local/bin 34 | echo "/home/runner/.local/bin" >> $GITHUB_PATH 35 | - name: Setup Environment 36 | run: | 37 | mkdir -p .coverage 38 | echo "*** Current Directory ***" 39 | pwd 40 | echo "*** Environment Variables ***" 41 | env | sort 42 | echo "*** Executable Path ***" 43 | echo "$PATH" | tr ':' '\n' 44 | echo "*** Workspace Files ***" 45 | find . 46 | which make 47 | - name: Install prerequisites 48 | run: | 49 | sudo apt-get --assume-yes install make 50 | sudo apt-get --assume-yes install libnss3-tools 51 | sudo apt-get update 52 | - name: Install Go modules 53 | run: | 54 | make dep 55 | go mod tidy 56 | go mod verify 57 | go mod download 58 | - name: Validate prerequisites 59 | run: | 60 | echo "*** Local binaries ***" 61 | find /home/runner/.local/bin 62 | - name: Run tests 63 | run: | 64 | make test || true 65 | make test 66 | - name: Generate coverage report 67 | run: make coverage 68 | - name: Upload coverage report 69 | uses: actions/upload-artifact@v3 70 | with: 71 | name: Test Coverage Report 72 | path: .coverage/coverage.html 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/** 2 | bin/** 3 | tmp/** 4 | .coverage/** 5 | .doc/** 6 | *TODO* 7 | 123* 8 | 9 | # Binaries for programs and plugins 10 | *.exe 11 | *.exe~ 12 | *.dll 13 | *.so 14 | *.dylib 15 | 16 | # Test binary, built with `go test -c` 17 | *.test 18 | 19 | # Output of the go coverage tool, specifically when used with LiteIDE 20 | *.out 21 | 22 | # Dependency directories (remove the comment below to include it) 23 | # vendor/ 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test ctest covdir bindir coverage docs linter qtest clean dep release logo license 2 | PLUGIN_NAME="caddy-git" 3 | PLUGIN_VERSION:=$(shell cat VERSION | head -1) 4 | GIT_COMMIT:=$(shell git describe --dirty --always) 5 | GIT_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD -- | head -1) 6 | LATEST_GIT_COMMIT:=$(shell git log --format="%H" -n 1 | head -1) 7 | BUILD_USER:=$(shell whoami) 8 | BUILD_DATE:=$(shell date +"%Y-%m-%d") 9 | BUILD_DIR:=$(shell pwd) 10 | CADDY_VERSION="v2.7.4" 11 | 12 | all: info 13 | @mkdir -p bin/ 14 | @rm -rf ./bin/caddy 15 | @rm -rf ../xcaddy-$(PLUGIN_NAME)/* 16 | @mkdir -p ../xcaddy-$(PLUGIN_NAME) && cd ../xcaddy-$(PLUGIN_NAME) && \ 17 | xcaddy build $(CADDY_VERSION) --output ../$(PLUGIN_NAME)/bin/caddy \ 18 | --with github.com/greenpau/caddy-git@$(LATEST_GIT_COMMIT)=$(BUILD_DIR) 19 | @#--with github.com/go-git/go-git/v5@v5.3.0=/home/greenpau/dev/go/src/github.com/go-git/go-git 20 | @#bin/caddy run -config assets/config/Caddyfile 21 | @for f in `find ./assets -type f -name 'Caddyfile'`; do bin/caddy fmt --overwrite $$f; done 22 | 23 | info: 24 | @echo "DEBUG: Version: $(PLUGIN_VERSION), Branch: $(GIT_BRANCH), Revision: $(GIT_COMMIT)" 25 | @echo "DEBUG: Build on $(BUILD_DATE) by $(BUILD_USER)" 26 | 27 | linter: 28 | @echo "DEBUG: running lint checks" 29 | @golint -set_exit_status ./... 30 | @echo "DEBUG: completed $@" 31 | 32 | test: covdir linter 33 | @echo "DEBUG: running tests" 34 | @go test -v -coverprofile=.coverage/coverage.out ./... 35 | @echo "DEBUG: completed $@" 36 | 37 | ctest: covdir linter 38 | @echo "DEBUG: running tests" 39 | @time richgo test -v -coverprofile=.coverage/coverage.out ./... 40 | @echo "DEBUG: completed $@" 41 | 42 | covdir: 43 | @echo "DEBUG: creating .coverage/ directory" 44 | @mkdir -p .coverage 45 | @echo "DEBUG: completed $@" 46 | 47 | bindir: 48 | @echo "DEBUG: creating bin/ directory" 49 | @mkdir -p bin/ 50 | @echo "DEBUG: completed $@" 51 | 52 | coverage: covdir 53 | @echo "DEBUG: running coverage" 54 | @go tool cover -html=.coverage/coverage.out -o .coverage/coverage.html 55 | @go test -covermode=count -coverprofile=.coverage/coverage.out ./... 56 | @go tool cover -func=.coverage/coverage.out | grep -v "100.0" 57 | @echo "DEBUG: completed $@" 58 | 59 | clean: 60 | @rm -rf .coverage/ 61 | @rm -rf bin/ 62 | @echo "DEBUG: completed $@" 63 | 64 | qtest: covdir 65 | @echo "DEBUG: perform quick tests ..." 66 | @#go test -v -coverprofile=.coverage/coverage.out -run TestApp ./*.go 67 | @#go test -v -coverprofile=.coverage/coverage.out -run TestParseCaddyfile ./*.go 68 | @time richgo test -v -coverprofile=.coverage/coverage.out -run TestParseCaddyfileAppConfig ./*.go 69 | @#go test -v -coverprofile=.coverage/coverage.out -run Test* ./pkg/services/... 70 | @go tool cover -html=.coverage/coverage.out -o .coverage/coverage.html 71 | @go tool cover -func=.coverage/coverage.out | grep -v "100.0" 72 | @echo "DEBUG: completed $@" 73 | 74 | dep: 75 | @echo "Making dependencies check ..." 76 | @go install golang.org/x/lint/golint@latest 77 | @go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest 78 | @go install github.com/greenpau/versioned/cmd/versioned@latest 79 | @go install github.com/kyoh86/richgo@latest 80 | 81 | release: 82 | @echo "Making release" 83 | @go mod tidy 84 | @go mod verify 85 | @if [ $(GIT_BRANCH) != "main" ]; then echo "cannot release to non-main branch $(GIT_BRANCH)" && false; fi 86 | @git diff-index --quiet HEAD -- || ( echo "git directory is dirty, commit changes first" && false ) 87 | @versioned -patch 88 | @echo "Patched version" 89 | @git add VERSION 90 | @git commit -m "released v`cat VERSION | head -1`" 91 | @git tag -a v`cat VERSION | head -1` -m "v`cat VERSION | head -1`" 92 | @git push 93 | @git push --tags 94 | @@echo "If necessary, run the following commands:" 95 | @echo " git push --delete origin v$(PLUGIN_VERSION)" 96 | @echo " git tag --delete v$(PLUGIN_VERSION)" 97 | 98 | logo: 99 | @mkdir -p assets/docs/images 100 | @gm convert -background black -font Bookman-Demi \ 101 | -size 640x320 "xc:black" \ 102 | -pointsize 72 \ 103 | -draw "fill white gravity center text 0,0 'caddy\ngit'" \ 104 | assets/docs/images/logo.png 105 | 106 | license: 107 | @for f in `find ./ -type f -name '*.go'`; do versioned -addlicense -copyright="Paul Greenberg greenpau@outlook.com" -year=2022 -filepath=$$f; done 108 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | --- 2 | reviewers: 3 | - greenpau 4 | 5 | approvers: 6 | - greenpau 7 | 8 | features: 9 | - comments 10 | - reviewers 11 | - aliases 12 | - branches 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # caddy-git 2 | 3 | 4 | 5 | 6 | 7 | 8 | Git Plugin for [Caddy v2](https://github.com/caddyserver/caddy). 9 | 10 | Inspired by [this comment](https://github.com/vrongmeal/caddygit/pull/5#issuecomment-1010440830). 11 | 12 | Please ask questions either here or via LinkedIn. I am happy to help you! @greenpau 13 | 14 | Please see other plugins: 15 | * [caddy-security](https://github.com/greenpau/caddy-security) 16 | * [caddy-trace](https://github.com/greenpau/caddy-trace) 17 | * [caddy-systemd](https://github.com/greenpau/caddy-systemd) 18 | 19 | 20 | ## Table of Contents 21 | 22 | * [Overview](#overview) 23 | * [Getting Started](#getting-started) 24 | 25 | 26 | 27 | ## Overview 28 | 29 | The `caddy-git` allows updating a directory backed by a git repo. 30 | 31 | ## Getting Started 32 | 33 | Configuration examples: 34 | * [Public repo over HTTPS](./assets/config/Caddyfile) 35 | * [Private or public repo over SSH with key-based authentication](./assets/config/ssh/Caddyfile) 36 | * [Repo with Webhooks](./assets/config/webhook/Caddyfile) 37 | * [Repo with post pull execution scripts](./assets/config/post_cmd_exec/Caddyfile) 38 | * [Routeless config](./assets/config/routeless/Caddyfile) 39 | 40 | For example, the following configuration sets up a definition for `authp.github.io` 41 | repo. The request to `authp.myfiosgateway.com/update/authp.github.io` trigger 42 | `git pull` of the `authp.github.io` repository. 43 | 44 | ``` 45 | { 46 | git { 47 | repo authp.github.io { 48 | base_dir /tmp 49 | url https://github.com/authp/authp.github.io.git 50 | branch gh-pages 51 | post pull exec { 52 | name Pager 53 | command /usr/bin/echo 54 | args "pulled authp.github.io repo" 55 | } 56 | } 57 | } 58 | } 59 | 60 | authp.myfiosgateway.com { 61 | route /version* { 62 | respond * "1.0.0" 200 63 | } 64 | route /update/authp.github.io { 65 | git update repo authp.github.io 66 | } 67 | route { 68 | file_server { 69 | root /tmp/authp.github.io 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | The cloning of the repository happens on startup. Additionally, the cloning 76 | happens when `/update/authp.github.io` is being hit. 77 | 78 | ``` 79 | curl https://authp.myfiosgateway.com/update/authp.github.io 80 | ``` 81 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.0.9 -------------------------------------------------------------------------------- /app.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package git 16 | 17 | import ( 18 | "fmt" 19 | "github.com/caddyserver/caddy/v2" 20 | "github.com/greenpau/caddy-git/pkg/service" 21 | "go.uber.org/zap" 22 | ) 23 | 24 | var ( 25 | appName = "git" 26 | 27 | // Interface guards 28 | _ caddy.Provisioner = (*App)(nil) 29 | _ caddy.Module = (*App)(nil) 30 | _ caddy.App = (*App)(nil) 31 | ) 32 | 33 | func init() { 34 | caddy.RegisterModule(App{}) 35 | } 36 | 37 | // App implements git repository manager. 38 | type App struct { 39 | Name string `json:"-"` 40 | Config *service.Config `json:"config,omitempty"` 41 | manager *service.Manager 42 | logger *zap.Logger 43 | } 44 | 45 | // CaddyModule returns the Caddy module information. 46 | func (App) CaddyModule() caddy.ModuleInfo { 47 | return caddy.ModuleInfo{ 48 | ID: caddy.ModuleID(appName), 49 | New: func() caddy.Module { return new(App) }, 50 | } 51 | } 52 | 53 | // Provision sets up the repo manager. 54 | func (app *App) Provision(ctx caddy.Context) error { 55 | app.Name = appName 56 | app.logger = ctx.Logger(app) 57 | 58 | app.logger.Info( 59 | "provisioning app instance", 60 | zap.String("app", app.Name), 61 | ) 62 | 63 | manager, err := service.NewManager(app.Config, app.logger) 64 | if err != nil { 65 | app.logger.Error( 66 | "failed configuring app instance", 67 | zap.String("app", app.Name), 68 | zap.Error(err), 69 | ) 70 | return err 71 | } 72 | app.manager = manager 73 | 74 | app.logger.Info( 75 | "provisioned app instance", 76 | zap.String("app", app.Name), 77 | ) 78 | return nil 79 | } 80 | 81 | // Start starts the App. 82 | func (app App) Start() error { 83 | app.logger.Debug( 84 | "starting git repo manager", 85 | zap.String("app", app.Name), 86 | ) 87 | 88 | if msgs := app.manager.Start(); msgs != nil { 89 | for _, msg := range msgs { 90 | app.logger.Error( 91 | "failed managing git repo", 92 | zap.String("app", app.Name), 93 | zap.String("repo", msg.Repository), 94 | zap.Error(msg.Error), 95 | ) 96 | } 97 | return fmt.Errorf("git repo manager failed to start") 98 | } 99 | 100 | app.logger.Debug( 101 | "started git repo manager", 102 | zap.String("app", app.Name), 103 | ) 104 | 105 | return nil 106 | } 107 | 108 | // Stop stops the App. 109 | func (app App) Stop() error { 110 | app.logger.Debug( 111 | "stopping git repo manager", 112 | zap.String("app", app.Name), 113 | ) 114 | 115 | if msgs := app.manager.Stop(); msgs != nil { 116 | for _, msg := range msgs { 117 | app.logger.Error( 118 | "failed stoppint git repo manager", 119 | zap.String("app", app.Name), 120 | zap.String("repo", msg.Repository), 121 | zap.Error(msg.Error), 122 | ) 123 | } 124 | return fmt.Errorf("git repo manager failed to stop properly") 125 | } 126 | 127 | app.logger.Debug( 128 | "stopped git repo manager", 129 | zap.String("app", app.Name), 130 | ) 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /assets/app_test_go_backup: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package git 16 | 17 | import ( 18 | "fmt" 19 | "github.com/caddyserver/caddy/v2/caddytest" 20 | "io/ioutil" 21 | "net/http" 22 | "testing" 23 | "time" 24 | ) 25 | 26 | var ( 27 | scheme string = "https" 28 | host string = "127.0.0.1" 29 | securePort string = "8443" 30 | ) 31 | 32 | func initCaddyTester(t *testing.T, configFile string) (*caddytest.Tester, map[string]string, error) { 33 | hostPort := fmt.Sprintf("%s:%s", host, securePort) 34 | baseURL := fmt.Sprintf("%s://%s", scheme, hostPort) 35 | configContent, err := ioutil.ReadFile(configFile) 36 | if err != nil { 37 | return nil, nil, err 38 | } 39 | 40 | tester := caddytest.NewTester(t) 41 | tester.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error { 42 | // Do not follow redirects. 43 | return http.ErrUseLastResponse 44 | } 45 | tester.InitServer(string(configContent), "caddyfile") 46 | 47 | params := make(map[string]string) 48 | params["base_url"] = baseURL 49 | params["version_path"] = fmt.Sprintf("%s/version", baseURL) 50 | return tester, params, nil 51 | } 52 | 53 | func TestApp(t *testing.T) { 54 | tester, config, err := initCaddyTester(t, "assets/config/Caddyfile") 55 | if err != nil { 56 | t.Fatalf("failed to init caddy tester instance: %v", err) 57 | } 58 | resp, respBody := tester.AssertGetResponse(config["version_path"], 200, "1.0.0") 59 | t.Logf("%v", resp) 60 | t.Logf("%v", respBody) 61 | time.Sleep(1 * time.Second) 62 | } 63 | -------------------------------------------------------------------------------- /assets/config/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | debug 3 | local_certs 4 | http_port 8080 5 | https_port 8443 6 | 7 | git { 8 | repo authp.github.io { 9 | base_dir {$HOME}/tmp 10 | url https://github.com/authp/authp.github.io.git 11 | branch gh-pages 12 | } 13 | } 14 | } 15 | 16 | 127.0.0.1, localhost { 17 | route /version* { 18 | respond * "1.0.0" 200 19 | } 20 | route /update/authp.github.io { 21 | git update repo authp.github.io 22 | } 23 | route { 24 | file_server { 25 | root {$HOME}/tmp/authp.github.io 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /assets/config/post_cmd_exec/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | debug 3 | local_certs 4 | http_port 8080 5 | https_port 8443 6 | 7 | git { 8 | repo authp.github.io { 9 | base_dir {$HOME}/tmp 10 | url https://github.com/authp/authp.github.io.git 11 | branch gh-pages 12 | post pull exec { 13 | name Pager 14 | command /usr/local/bin/pager 15 | args "pulled authp.github.io repo" 16 | } 17 | } 18 | } 19 | } 20 | 21 | 127.0.0.1, localhost { 22 | route /version* { 23 | respond * "1.0.0" 200 24 | } 25 | route /update/authp.github.io { 26 | git update repo authp.github.io 27 | } 28 | route { 29 | file_server { 30 | root {$HOME}/tmp/authp.github.io 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /assets/config/routeless/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | debug 3 | local_certs 4 | http_port 8080 5 | https_port 8443 6 | 7 | order git before respond 8 | 9 | git { 10 | repo authp.github.io { 11 | base_dir {$HOME}/tmp/authp/gitcfg/ 12 | url https://github.com/authp/authp.github.io.git 13 | branch gh-pages 14 | post pull exec { 15 | name Pager 16 | command /usr/bin/echo 17 | args "pulled authp.github.io repo" 18 | } 19 | } 20 | } 21 | } 22 | 23 | 127.0.0.1, localhost { 24 | respond /version* "1.0.0" 200 25 | git /update/authp.github.io update repo authp.github.io 26 | root * {$HOME}/tmp/authp/gitcfg/authp.github.io 27 | file_server 28 | } 29 | -------------------------------------------------------------------------------- /assets/config/ssh/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | debug 3 | local_certs 4 | http_port 8080 5 | https_port 8443 6 | 7 | git { 8 | repo authp.github.io { 9 | base_dir {$HOME}/tmp/ssh 10 | url git@github.com:authp/authp.github.io.git 11 | # auth key {$HOME}/.ssh/id_rsa passphrase {env.MY_SSH_KEY_PASSPHRASE} 12 | # auth key {$HOME}/.ssh/id_rsa passphrase {env.MY_SSH_KEY_PASSPHRASE} no_strict_host_key_check 13 | auth key {$HOME}/.ssh/id_rsa 14 | branch gh-pages 15 | } 16 | } 17 | } 18 | 19 | 127.0.0.1, localhost { 20 | route /version* { 21 | respond * "1.0.0" 200 22 | } 23 | route /update/authp.github.io { 24 | git update repo authp.github.io 25 | } 26 | route { 27 | file_server { 28 | root {$HOME}/tmp/ssh/authp.github.io 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /assets/config/webhook/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | debug 3 | local_certs 4 | http_port 8080 5 | https_port 8443 6 | 7 | git { 8 | repo authp.github.io { 9 | base_dir {$HOME}/tmp 10 | url https://github.com/authp/authp.github.io.git 11 | # webhook Github X-Hub-Signature-256 {env.MY_GITHUB_WEBHOOK_KEY} 12 | webhook Gitlab X-Gitlab-Token barbaz 13 | branch gh-pages 14 | } 15 | } 16 | } 17 | 18 | 127.0.0.1, localhost { 19 | route /version* { 20 | respond * "1.0.0" 200 21 | } 22 | route /update/authp.github.io { 23 | git update repo authp.github.io 24 | } 25 | route { 26 | file_server { 27 | root {$HOME}/tmp/authp.github.io 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /assets/docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greenpau/caddy-git/e1241f5a070ca449ad4bdbc376099cae465f331d/assets/docs/images/logo.png -------------------------------------------------------------------------------- /caddyfile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package git 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "github.com/caddyserver/caddy/v2" 21 | "github.com/caddyserver/caddy/v2/caddyconfig" 22 | "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" 23 | "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" 24 | "github.com/caddyserver/caddy/v2/modules/caddyhttp" 25 | "github.com/greenpau/caddy-git/pkg/service" 26 | "strconv" 27 | "strings" 28 | ) 29 | 30 | func init() { 31 | httpcaddyfile.RegisterGlobalOption("git", parseCaddyfileAppConfig) 32 | httpcaddyfile.RegisterDirective("git", getRouteFromParseCaddyfileHandlerConfig) 33 | } 34 | 35 | // parseCaddyfileAppConfig sets up a repo manager. 36 | // 37 | // Syntax: 38 | // 39 | // git { 40 | // repo { 41 | // base_dir 42 | // url 43 | // auth key [passphrase ] [no_strict_host_key_check] 44 | // auth username password 45 | // webhook
46 | // branch 47 | // depth 1 48 | // update every 49 | // } 50 | 51 | // parseCaddyfileHandlerConfig configures repo update handler. 52 | // 53 | // Syntax: 54 | // 55 | // route /update { 56 | // git update repo 57 | // } 58 | 59 | const badRepl string = "ERROR_BAD_REPL" 60 | 61 | var argRules = map[string]argRule{ 62 | "base_dir": argRule{Min: 1, Max: 1}, 63 | "url": argRule{Min: 1, Max: 1}, 64 | "auth": argRule{Min: 2, Max: 255}, 65 | "branch": argRule{Min: 1, Max: 1}, 66 | "depth": argRule{Min: 1, Max: 1}, 67 | "update": argRule{Min: 1, Max: 255}, 68 | "webhook": argRule{Min: 3, Max: 3}, 69 | "post": argRule{Min: 2, Max: 2}, 70 | } 71 | 72 | type argRule struct { 73 | Min int 74 | Max int 75 | } 76 | 77 | func parseCaddyfileAppConfig(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) { 78 | repl := caddy.NewReplacer() 79 | app := new(App) 80 | app.Config = service.NewConfig() 81 | 82 | if !d.Next() { 83 | return nil, d.ArgErr() 84 | } 85 | 86 | for d.NextBlock(0) { 87 | switch d.Val() { 88 | case "repo": 89 | args := d.RemainingArgs() 90 | if len(args) != 1 { 91 | return nil, d.ArgErr() 92 | } 93 | rc := service.NewRepositoryConfig() 94 | rc.Name = args[0] 95 | for nesting := d.Nesting(); d.NextBlock(nesting); { 96 | k := d.Val() 97 | v := findReplace(repl, d.RemainingArgs()) 98 | if _, exists := argRules[k]; exists { 99 | if err := validateArg(k, v); err != nil { 100 | return nil, d.Errf("%s", err) 101 | } 102 | } 103 | switch k { 104 | case "base_dir": 105 | rc.BaseDir = v[0] 106 | case "url": 107 | rc.Address = v[0] 108 | case "auth": 109 | authCfg := &service.AuthConfig{} 110 | switch v[0] { 111 | case "key": 112 | if len(v) < 2 { 113 | return nil, d.Errf("malformed %q directive: %v", k, v) 114 | } 115 | authCfg.KeyPath = v[1] 116 | if len(v) > 2 { 117 | if v[2] == "passphrase" { 118 | authCfg.KeyPassphrase = v[3] 119 | } 120 | } 121 | case "username": 122 | if len(v) < 4 { 123 | return nil, d.Errf("malformed %q directive", k) 124 | } 125 | authCfg.Username = v[1] 126 | if v[2] == "password" { 127 | authCfg.Password = v[3] 128 | } 129 | } 130 | if findString(v, "no_strict_host_key_check") { 131 | authCfg.StrictHostKeyCheckingDisabled = true 132 | } 133 | rc.Auth = authCfg 134 | case "webhook": 135 | whCfg := &service.WebhookConfig{ 136 | Name: v[0], 137 | Header: v[1], 138 | Secret: v[2], 139 | } 140 | rc.Webhooks = append(rc.Webhooks, whCfg) 141 | case "branch": 142 | rc.Branch = v[0] 143 | case "depth": 144 | if n, err := strconv.Atoi(v[0]); err == nil { 145 | rc.Depth = n 146 | } else { 147 | return nil, d.Errf("%s value %q is not integer", k, v[0]) 148 | } 149 | // return nil, d.Errf("the depth directive is disabled due to the issue with github.com/go-git/go-git") 150 | case "post": 151 | switch { 152 | case strings.Join(v, " ") == "pull exec": 153 | ppeCfg := &service.ExecConfig{} 154 | for nesting := d.Nesting(); d.NextBlock(nesting); { 155 | nk := d.Val() 156 | nargs := findReplace(repl, d.RemainingArgs()) 157 | switch nk { 158 | case "name": 159 | ppeCfg.Name = nargs[0] 160 | case "command": 161 | ppeCfg.Command = nargs[0] 162 | case "args": 163 | ppeCfg.Args = nargs 164 | default: 165 | return nil, d.Errf("malformed %q directive: %v", nk, nargs) 166 | } 167 | } 168 | rc.PostPullExec = append(rc.PostPullExec, ppeCfg) 169 | default: 170 | return nil, d.Errf("malformed %q directive: %v", k, v) 171 | } 172 | case "update": 173 | if len(v) != 2 { 174 | return nil, d.Errf("malformed %q directive: %v", k, v) 175 | } 176 | if v[0] != "every" { 177 | return nil, d.Errf("malformed %q directive: %v", k, v) 178 | } 179 | if n, err := strconv.Atoi(v[1]); err == nil { 180 | rc.UpdateInterval = n 181 | } else { 182 | return nil, d.Errf("%s value %q is not integer", k, v[0]) 183 | } 184 | default: 185 | return nil, d.Errf("unsupported %q key", k) 186 | } 187 | } 188 | if err := app.Config.AddRepository(rc); err != nil { 189 | return nil, d.Err(err.Error()) 190 | } 191 | default: 192 | return nil, d.ArgErr() 193 | } 194 | } 195 | 196 | return httpcaddyfile.App{ 197 | Name: appName, 198 | Value: caddyconfig.JSON(app, nil), 199 | }, nil 200 | } 201 | 202 | func validateArg(k string, v []string) error { 203 | r, exists := argRules[k] 204 | if !exists { 205 | return nil 206 | } 207 | if r.Min > len(v) { 208 | return fmt.Errorf("too few args for %q directive (config: %d, min: %d)", k, len(v), r.Min) 209 | } 210 | if r.Max < len(v) { 211 | return fmt.Errorf("too many args for %q directive (config: %d, max: %d", k, len(v), r.Max) 212 | } 213 | return nil 214 | } 215 | 216 | func parseCaddyfileHandlerConfig(h httpcaddyfile.Helper) (*service.Endpoint, error) { 217 | endpoint := &service.Endpoint{} 218 | 219 | for h.Next() { 220 | args := h.RemainingArgs() 221 | strArgs := strings.Join(args, " ") 222 | if !strings.Contains(strArgs, "update repo ") { 223 | return nil, h.Errf("unsupported config: git %s", strArgs) 224 | } 225 | switch { 226 | case args[0] == "update" && args[1] == "repo": 227 | if len(args) != 3 { 228 | return nil, h.Errf("malformed config: git %s", strArgs) 229 | } 230 | endpoint.Path = "*" 231 | endpoint.RepositoryName = args[2] 232 | case args[1] == "update" && args[2] == "repo": 233 | if len(args) != 4 { 234 | return nil, h.Errf("malformed config: git %s", strArgs) 235 | } 236 | endpoint.Path = args[0] 237 | endpoint.RepositoryName = args[3] 238 | default: 239 | return nil, h.Errf("malformed config: git %s", strArgs) 240 | } 241 | } 242 | 243 | h.Reset() 244 | h.Next() 245 | return endpoint, nil 246 | } 247 | 248 | func getRouteFromParseCaddyfileHandlerConfig(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) { 249 | endpoint, err := parseCaddyfileHandlerConfig(h) 250 | if err != nil { 251 | return nil, err 252 | } 253 | pathMatcher := caddy.ModuleMap{ 254 | "path": h.JSON(caddyhttp.MatchPath{endpoint.Path}), 255 | } 256 | route := caddyhttp.Route{ 257 | HandlersRaw: []json.RawMessage{ 258 | caddyconfig.JSONModuleObject(&Middleware{Endpoint: endpoint}, "handler", "git", nil), 259 | }, 260 | } 261 | subroute := new(caddyhttp.Subroute) 262 | subroute.Routes = append([]caddyhttp.Route{route}, subroute.Routes...) 263 | return h.NewRoute(pathMatcher, subroute), nil 264 | 265 | } 266 | 267 | func findReplace(repl *caddy.Replacer, arr []string) (output []string) { 268 | for _, item := range arr { 269 | output = append(output, repl.ReplaceAll(item, badRepl)) 270 | } 271 | return output 272 | } 273 | 274 | func findString(arr []string, s string) bool { 275 | for _, x := range arr { 276 | if x == s { 277 | return true 278 | } 279 | } 280 | return false 281 | } 282 | -------------------------------------------------------------------------------- /caddyfile_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package git 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "testing" 21 | 22 | "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" 23 | "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" 24 | "github.com/google/go-cmp/cmp" 25 | ) 26 | 27 | const tf string = "Testfile" 28 | 29 | func TestParseCaddyfileAppConfig(t *testing.T) { 30 | testcases := []struct { 31 | name string 32 | d *caddyfile.Dispenser 33 | want string 34 | shouldErr bool 35 | err error 36 | }{ 37 | { 38 | name: "test parse repo config", 39 | d: caddyfile.NewTestDispenser(` 40 | git { 41 | repo authp.github.io { 42 | base_dir /tmp 43 | url https://github.com/authp/authp.github.io.git 44 | branch gh-pages 45 | } 46 | }`), 47 | want: `{ 48 | "config": { 49 | "repositories": [ 50 | { 51 | "address": "https://github.com/authp/authp.github.io.git", 52 | "base_dir": "/tmp", 53 | "branch": "gh-pages", 54 | "name": "authp.github.io" 55 | } 56 | ] 57 | } 58 | }`, 59 | }, 60 | { 61 | name: "test parse repo config with webhooks", 62 | d: caddyfile.NewTestDispenser(` 63 | git { 64 | repo authp.github.io { 65 | base_dir /tmp 66 | url https://github.com/authp/authp.github.io.git 67 | webhook Github X-Hub-Signature-256 foobar 68 | webhook Gitlab X-Gitlab-Token barbaz 69 | branch gh-pages 70 | } 71 | }`), 72 | want: `{ 73 | "config": { 74 | "repositories": [ 75 | { 76 | "address": "https://github.com/authp/authp.github.io.git", 77 | "base_dir": "/tmp", 78 | "branch": "gh-pages", 79 | "name": "authp.github.io", 80 | "webhooks": [ 81 | { 82 | "name": "Github", 83 | "header": "X-Hub-Signature-256", 84 | "secret": "foobar" 85 | }, 86 | { 87 | "name": "Gitlab", 88 | "header": "X-Gitlab-Token", 89 | "secret": "barbaz" 90 | } 91 | ] 92 | } 93 | ] 94 | } 95 | }`, 96 | }, 97 | { 98 | name: "test parse repo config with post pull cmd", 99 | d: caddyfile.NewTestDispenser(` 100 | git { 101 | repo authp.github.io { 102 | base_dir /tmp 103 | url https://github.com/authp/authp.github.io.git 104 | branch gh-pages 105 | post pull exec { 106 | name Pager 107 | command /usr/local/bin/pager 108 | args "pulled authp.github.io repo" 109 | } 110 | } 111 | }`), 112 | want: `{ 113 | "config": { 114 | "repositories": [ 115 | { 116 | "address": "https://github.com/authp/authp.github.io.git", 117 | "base_dir": "/tmp", 118 | "branch": "gh-pages", 119 | "name": "authp.github.io", 120 | "post_pull_exec": [ 121 | { 122 | "name": "Pager", 123 | "command": "/usr/local/bin/pager", 124 | "args": ["pulled authp.github.io repo"] 125 | } 126 | ] 127 | } 128 | ] 129 | } 130 | }`, 131 | }, 132 | { 133 | name: "test parse ssh config with key-based auth", 134 | d: caddyfile.NewTestDispenser(` 135 | git { 136 | repo authp.github.io { 137 | base_dir /tmp 138 | url git@github.com:authp/authp.github.io.git 139 | auth key ~/.ssh/id_rsa 140 | branch gh-pages 141 | } 142 | }`), 143 | want: `{ 144 | "config": { 145 | "repositories": [ 146 | { 147 | "address": "git@github.com:authp/authp.github.io.git", 148 | "base_dir": "/tmp", 149 | "branch": "gh-pages", 150 | "name": "authp.github.io", 151 | "auth": { 152 | "key_path": "~/.ssh/id_rsa" 153 | } 154 | } 155 | ] 156 | } 157 | }`, 158 | }, 159 | { 160 | name: "test parse ssh config with key-based auth and key passphrase", 161 | d: caddyfile.NewTestDispenser(` 162 | git { 163 | repo authp.github.io { 164 | base_dir /tmp 165 | url git@github.com:authp/authp.github.io.git 166 | auth key ~/.ssh/id_rsa passphrase foobar 167 | branch gh-pages 168 | } 169 | }`), 170 | want: `{ 171 | "config": { 172 | "repositories": [ 173 | { 174 | "address": "git@github.com:authp/authp.github.io.git", 175 | "base_dir": "/tmp", 176 | "branch": "gh-pages", 177 | "name": "authp.github.io", 178 | "auth": { 179 | "key_path": "~/.ssh/id_rsa", 180 | "key_passphrase": "foobar" 181 | } 182 | } 183 | ] 184 | } 185 | }`, 186 | }, 187 | { 188 | name: "test parse ssh config with username password auth", 189 | d: caddyfile.NewTestDispenser(` 190 | git { 191 | repo authp.github.io { 192 | base_dir /tmp 193 | url git@github.com:authp/authp.github.io.git 194 | auth username foo password bar 195 | branch gh-pages 196 | } 197 | }`), 198 | want: `{ 199 | "config": { 200 | "repositories": [ 201 | { 202 | "address": "git@github.com:authp/authp.github.io.git", 203 | "base_dir": "/tmp", 204 | "branch": "gh-pages", 205 | "name": "authp.github.io", 206 | "auth": { 207 | "username": "foo", 208 | "password": "bar" 209 | } 210 | } 211 | ] 212 | } 213 | }`, 214 | }, 215 | { 216 | name: "test parse config with unsupported bar key", 217 | d: caddyfile.NewTestDispenser(` 218 | git { 219 | repo bar { 220 | bar baz 221 | } 222 | }`), 223 | shouldErr: true, 224 | err: fmt.Errorf("%s:%d - Error during parsing: unsupported %q key, import chain: ['']", tf, 4, "bar"), 225 | }, 226 | { 227 | name: "test parse config with too few arg for repo arg", 228 | d: caddyfile.NewTestDispenser(` 229 | git { 230 | repo foo { 231 | url 232 | } 233 | }`), 234 | shouldErr: true, 235 | err: fmt.Errorf("%s:%d - Error during parsing: too few args for %q directive (config: 0, min: 1), import chain: ['']", tf, 4, "url"), 236 | }, 237 | } 238 | for _, tc := range testcases { 239 | t.Run(tc.name, func(t *testing.T) { 240 | app, err := parseCaddyfileAppConfig(tc.d, nil) 241 | if err != nil { 242 | if !tc.shouldErr { 243 | t.Fatalf("expected success, got: %v", err) 244 | } 245 | if diff := cmp.Diff(err.Error(), tc.err.Error()); diff != "" { 246 | t.Fatalf("unexpected error: %v, want: %v", err, tc.err) 247 | } 248 | return 249 | } 250 | if tc.shouldErr { 251 | t.Fatalf("unexpected success, want: %v", tc.err) 252 | } 253 | got := unpack(t, string(app.(httpcaddyfile.App).Value)) 254 | want := unpack(t, tc.want) 255 | 256 | if diff := cmp.Diff(want, got); diff != "" { 257 | t.Errorf("parseCaddyfileAppConfig() mismatch (-want +got):\n%s", diff) 258 | } 259 | }) 260 | } 261 | } 262 | 263 | func unpack(t *testing.T, s string) (m map[string]interface{}) { 264 | if err := json.Unmarshal([]byte(s), &m); err != nil { 265 | t.Fatalf("failed to parse %q: %v", s, err) 266 | } 267 | return m 268 | } 269 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/greenpau/caddy-git 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/caddyserver/caddy/v2 v2.7.4 7 | github.com/go-git/go-git/v5 v5.8.1 8 | github.com/google/go-cmp v0.5.9 9 | go.uber.org/zap v1.25.0 10 | golang.org/x/crypto v0.12.0 11 | ) 12 | 13 | require ( 14 | cloud.google.com/go/compute v1.23.0 // indirect 15 | cloud.google.com/go/iam v1.1.1 // indirect 16 | dario.cat/mergo v1.0.0 // indirect 17 | filippo.io/edwards25519 v1.0.0 // indirect 18 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect 19 | github.com/Masterminds/goutils v1.1.1 // indirect 20 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 21 | github.com/Masterminds/sprig/v3 v3.2.3 // indirect 22 | github.com/Microsoft/go-winio v0.6.1 // indirect 23 | github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect 24 | github.com/acomagu/bufpipe v1.0.4 // indirect 25 | github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect 26 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect 27 | github.com/beorn7/perks v1.0.1 // indirect 28 | github.com/caddyserver/certmagic v0.19.2 // indirect 29 | github.com/cespare/xxhash v1.1.0 // indirect 30 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 31 | github.com/chzyer/readline v1.5.1 // indirect 32 | github.com/cloudflare/circl v1.3.3 // indirect 33 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 34 | github.com/dgraph-io/badger v1.6.2 // indirect 35 | github.com/dgraph-io/badger/v2 v2.2007.4 // indirect 36 | github.com/dgraph-io/ristretto v0.1.1 // indirect 37 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect 38 | github.com/dustin/go-humanize v1.0.1 // indirect 39 | github.com/emirpasic/gods v1.18.1 // indirect 40 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 41 | github.com/go-git/go-billy/v5 v5.4.1 // indirect 42 | github.com/go-kit/kit v0.13.0 // indirect 43 | github.com/go-kit/log v0.2.1 // indirect 44 | github.com/go-logfmt/logfmt v0.6.0 // indirect 45 | github.com/go-sql-driver/mysql v1.7.1 // indirect 46 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 47 | github.com/golang/glog v1.1.2 // indirect 48 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 49 | github.com/golang/mock v1.6.0 // indirect 50 | github.com/golang/protobuf v1.5.3 // indirect 51 | github.com/golang/snappy v0.0.4 // indirect 52 | github.com/google/cel-go v0.17.0 // indirect 53 | github.com/google/pprof v0.0.0-20230901174712-0191c66da455 // indirect 54 | github.com/google/uuid v1.3.1 // indirect 55 | github.com/huandu/xstrings v1.4.0 // indirect 56 | github.com/imdario/mergo v0.3.16 // indirect 57 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 58 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 59 | github.com/jackc/pgconn v1.14.1 // indirect 60 | github.com/jackc/pgio v1.0.0 // indirect 61 | github.com/jackc/pgpassfile v1.0.0 // indirect 62 | github.com/jackc/pgproto3/v2 v2.3.2 // indirect 63 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 64 | github.com/jackc/pgtype v1.14.0 // indirect 65 | github.com/jackc/pgx/v4 v4.18.1 // indirect 66 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 67 | github.com/kevinburke/ssh_config v1.2.0 // indirect 68 | github.com/klauspost/compress v1.16.7 // indirect 69 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect 70 | github.com/libdns/libdns v0.2.1 // indirect 71 | github.com/manifoldco/promptui v0.9.0 // indirect 72 | github.com/mattn/go-colorable v0.1.13 // indirect 73 | github.com/mattn/go-isatty v0.0.19 // indirect 74 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 75 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 76 | github.com/mholt/acmez v1.2.0 // indirect 77 | github.com/micromdm/scep/v2 v2.1.0 // indirect 78 | github.com/miekg/dns v1.1.55 // indirect 79 | github.com/mitchellh/copystructure v1.2.0 // indirect 80 | github.com/mitchellh/go-ps v1.0.0 // indirect 81 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 82 | github.com/onsi/ginkgo/v2 v2.12.0 // indirect 83 | github.com/pjbgf/sha1cd v0.3.0 // indirect 84 | github.com/pkg/errors v0.9.1 // indirect 85 | github.com/prometheus/client_golang v1.16.0 // indirect 86 | github.com/prometheus/client_model v0.4.0 // indirect 87 | github.com/prometheus/common v0.44.0 // indirect 88 | github.com/prometheus/procfs v0.11.1 // indirect 89 | github.com/quic-go/qpack v0.4.0 // indirect 90 | github.com/quic-go/qtls-go1-20 v0.3.3 // indirect 91 | github.com/quic-go/quic-go v0.38.1 // indirect 92 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 93 | github.com/sergi/go-diff v1.3.1 // indirect 94 | github.com/shopspring/decimal v1.3.1 // indirect 95 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 96 | github.com/skeema/knownhosts v1.2.0 // indirect 97 | github.com/slackhq/nebula v1.7.2 // indirect 98 | github.com/smallstep/certificates v0.24.3-rc1 // indirect 99 | github.com/smallstep/nosql v0.6.0 // indirect 100 | github.com/smallstep/truststore v0.12.1 // indirect 101 | github.com/spf13/cast v1.5.1 // indirect 102 | github.com/spf13/cobra v1.7.0 // indirect 103 | github.com/spf13/pflag v1.0.5 // indirect 104 | github.com/stoewer/go-strcase v1.3.0 // indirect 105 | github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // indirect 106 | github.com/urfave/cli v1.22.14 // indirect 107 | github.com/xanzy/ssh-agent v0.3.3 // indirect 108 | github.com/zeebo/blake3 v0.2.3 // indirect 109 | go.etcd.io/bbolt v1.3.7 // indirect 110 | go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect 111 | go.step.sm/cli-utils v0.8.0 // indirect 112 | go.step.sm/crypto v0.35.0 // indirect 113 | go.step.sm/linkedca v0.20.0 // indirect 114 | go.uber.org/goleak v1.2.1 // indirect 115 | go.uber.org/multierr v1.11.0 // indirect 116 | golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect 117 | golang.org/x/mod v0.12.0 // indirect 118 | golang.org/x/net v0.14.0 // indirect 119 | golang.org/x/sys v0.12.0 // indirect 120 | golang.org/x/term v0.12.0 // indirect 121 | golang.org/x/text v0.13.0 // indirect 122 | golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect 123 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect 124 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 125 | google.golang.org/grpc v1.57.0 // indirect 126 | google.golang.org/protobuf v1.31.0 // indirect 127 | gopkg.in/square/go-jose.v2 v2.6.0 // indirect 128 | gopkg.in/warnings.v0 v0.1.2 // indirect 129 | gopkg.in/yaml.v3 v3.0.1 // indirect 130 | howett.net/plist v1.0.0 // indirect 131 | ) 132 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= 2 | cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= 3 | cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= 4 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 5 | cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= 6 | cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= 7 | cloud.google.com/go/kms v1.15.1 h1:HUC3fAoepH3RpcQXiJhXWWYizjQ5r7YjI7SO9ZbHf9s= 8 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 9 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 10 | filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= 11 | filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= 12 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= 13 | github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= 14 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 15 | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 16 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 17 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 18 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 19 | github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 20 | github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= 21 | github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 22 | github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= 23 | github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= 24 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 25 | github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= 26 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 27 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 28 | github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 29 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 30 | github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= 31 | github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 32 | github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= 33 | github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 34 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 35 | github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= 36 | github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= 37 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 38 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 39 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw= 40 | github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 41 | github.com/aws/aws-sdk-go v1.44.318 h1:Yl66rpbQHFUbxe9JBKLcvOvRivhVgP6+zH0b9KzARX8= 42 | github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 43 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 44 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 45 | github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= 46 | github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 47 | github.com/caddyserver/caddy/v2 v2.7.4 h1:J8nisjdOxnYHXlorUKXY75Gr6iBfudfoGhrJ8t7/flI= 48 | github.com/caddyserver/caddy/v2 v2.7.4/go.mod h1:/OH2g/56QCSCajEWsFa8kjwacziG/YFxeWgKacnK6KE= 49 | github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYPMQ2/ui0= 50 | github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8= 51 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 52 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 53 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 54 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 55 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 56 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 57 | github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= 58 | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= 59 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 60 | github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= 61 | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= 62 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 63 | github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= 64 | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= 65 | github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= 66 | github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 67 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 68 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 69 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 70 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 71 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 72 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 73 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 74 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 75 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 76 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 77 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 78 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 79 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 80 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 81 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 82 | github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= 83 | github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= 84 | github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o= 85 | github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= 86 | github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 87 | github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= 88 | github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= 89 | github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= 90 | github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 91 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= 92 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 93 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 94 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 95 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 96 | github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= 97 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 98 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 99 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 100 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 101 | github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= 102 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 103 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 104 | github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= 105 | github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= 106 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= 107 | github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= 108 | github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= 109 | github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 110 | github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= 111 | github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= 112 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 113 | github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= 114 | github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 115 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 116 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 117 | github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 118 | github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 119 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 120 | github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 121 | github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 122 | github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 123 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 124 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 125 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 126 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 127 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 128 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 129 | github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= 130 | github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= 131 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 132 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 133 | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 134 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 135 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 136 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 137 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 138 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 139 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 140 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 141 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 142 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 143 | github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= 144 | github.com/google/cel-go v0.17.0 h1:o8fqHUcM+0g5prwg4pHZR+EMdef2RBumnEYefCXAyPQ= 145 | github.com/google/cel-go v0.17.0/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= 146 | github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= 147 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 148 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 149 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 150 | github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= 151 | github.com/google/go-tpm-tools v0.3.12 h1:hpWglH4RaZnGVbgOK3IThI5K++jnFvjQ94EIN34xrUU= 152 | github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus= 153 | github.com/google/pprof v0.0.0-20230901174712-0191c66da455 h1:YhRUmI1ttDC4sxKY2V62BTI8hCXnyZBV9h38eAanInE= 154 | github.com/google/pprof v0.0.0-20230901174712-0191c66da455/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= 155 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 156 | github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= 157 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 158 | github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 159 | github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 160 | github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= 161 | github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= 162 | github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 163 | github.com/gorilla/mux v1.4.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 164 | github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda/go.mod h1:MyndkAZd5rUMdNogn35MWXBX1UiBigrU8eTj8DoAC2c= 165 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 166 | github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 167 | github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= 168 | github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 169 | github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 170 | github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= 171 | github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 172 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 173 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 174 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 175 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 176 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 177 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 178 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 179 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 180 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 181 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 182 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 183 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 184 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 185 | github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= 186 | github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= 187 | github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= 188 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 189 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 190 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 191 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 192 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 193 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 194 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 195 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 196 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 197 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 198 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 199 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 200 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 201 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 202 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 203 | github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= 204 | github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 205 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 206 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 207 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 208 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 209 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 210 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 211 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 212 | github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= 213 | github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 214 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 215 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 216 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 217 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 218 | github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= 219 | github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= 220 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 221 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 222 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 223 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 224 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 225 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 226 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 227 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 228 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 229 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 230 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 231 | github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= 232 | github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= 233 | github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 234 | github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= 235 | github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= 236 | github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 237 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 238 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 239 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 240 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 241 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 242 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 243 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 244 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 245 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 246 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 247 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 248 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 249 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 250 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 251 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 252 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= 253 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 254 | github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= 255 | github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= 256 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 257 | github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= 258 | github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= 259 | github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= 260 | github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 261 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 262 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 263 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 264 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 265 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 266 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 267 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 268 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 269 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 270 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 271 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 272 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 273 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 274 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 275 | github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= 276 | github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= 277 | github.com/micromdm/scep/v2 v2.1.0 h1:2fS9Rla7qRR266hvUoEauBJ7J6FhgssEiq2OkSKXmaU= 278 | github.com/micromdm/scep/v2 v2.1.0/go.mod h1:BkF7TkPPhmgJAMtHfP+sFTKXmgzNJgLQlvvGoOExBcc= 279 | github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= 280 | github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 281 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 282 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 283 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 284 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 285 | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= 286 | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= 287 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 288 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 289 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 290 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 291 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 292 | github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= 293 | github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= 294 | github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= 295 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 296 | github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= 297 | github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= 298 | github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= 299 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 300 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 301 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 302 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 303 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 304 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 305 | github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= 306 | github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= 307 | github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= 308 | github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= 309 | github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= 310 | github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= 311 | github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= 312 | github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= 313 | github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= 314 | github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 315 | github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM= 316 | github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= 317 | github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE= 318 | github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4= 319 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 320 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 321 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 322 | github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= 323 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 324 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 325 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 326 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 327 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 328 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 329 | github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E= 330 | github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= 331 | github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= 332 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 333 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 334 | github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= 335 | github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 336 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 337 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 338 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 339 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 340 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 341 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 342 | github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= 343 | github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= 344 | github.com/slackhq/nebula v1.7.2 h1:Rko1Mlksz/nC0c919xjGpB8uOSrTJ5e6KPgZx+lVfYw= 345 | github.com/slackhq/nebula v1.7.2/go.mod h1:cnaoahkUipDs1vrNoIszyp0QPRIQN9Pm68ppQEW1Fhg= 346 | github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= 347 | github.com/smallstep/certificates v0.24.3-rc1 h1:mN6Hdymg7kfhjmAHAucTZAOXTaC24Rig1XXgBR+2n9M= 348 | github.com/smallstep/certificates v0.24.3-rc1/go.mod h1:PQLGm1SXGrNjvb4v+WyvVVlroSF3ZOZso1K4hpaARr8= 349 | github.com/smallstep/go-attestation v0.4.4-0.20230509120429-e17291421738 h1:h+cZgVniTaE0uuRMdxTThLaJeuxsv4aas6oStz6f5VQ= 350 | github.com/smallstep/nosql v0.6.0 h1:ur7ysI8s9st0cMXnTvB8tA3+x5Eifmkb6hl4uqNV5jc= 351 | github.com/smallstep/nosql v0.6.0/go.mod h1:jOXwLtockXORUPPZ2MCUcIkGR6w0cN1QGZniY9DITQA= 352 | github.com/smallstep/truststore v0.12.1 h1:guLUKkc1UlsXeS3t6BuVMa4leOOpdiv02PCRTiy1WdY= 353 | github.com/smallstep/truststore v0.12.1/go.mod h1:M4mebeNy28KusGX3lJxpLARIktLcyqBOrj3ZiZ46pqw= 354 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 355 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 356 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 357 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 358 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 359 | github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 360 | github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= 361 | github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= 362 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 363 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 364 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 365 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 366 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 367 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 368 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 369 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 370 | github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= 371 | github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= 372 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 373 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 374 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 375 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 376 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 377 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 378 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 379 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 380 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 381 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 382 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 383 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 384 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 385 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 386 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 387 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 388 | github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 h1:8rUlviSVOEe7TMk7W0gIPrW8MqEzYfZHpsNWSf8s2vg= 389 | github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= 390 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 391 | github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= 392 | github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= 393 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 394 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 395 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 396 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 397 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 398 | github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= 399 | github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= 400 | github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= 401 | github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= 402 | github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= 403 | github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= 404 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 405 | go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= 406 | go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 407 | go.mozilla.org/pkcs7 v0.0.0-20210730143726-725912489c62/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= 408 | go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= 409 | go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= 410 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 411 | go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ= 412 | go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4= 413 | go.step.sm/crypto v0.35.0 h1:0N6ks5n1sdv4+biJMUTdqHjpTBKKN9zNqqBdOJIyHe4= 414 | go.step.sm/crypto v0.35.0/go.mod h1:sBsrpVReoxmiLexbWL+vQRxZd6Gq4YBj/IRSUH+DZe4= 415 | go.step.sm/linkedca v0.20.0 h1:bH41rvyDm3nSSJ5xgGsKUZOpzJcq5x2zacMIeqtq9oI= 416 | go.step.sm/linkedca v0.20.0/go.mod h1:eybHw6ZTpuFmkUQnTBRWM2SPIGaP0VbYeo1bupfPT70= 417 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 418 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 419 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 420 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 421 | go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= 422 | go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 423 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 424 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 425 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 426 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 427 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 428 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 429 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 430 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 431 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 432 | go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= 433 | go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= 434 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 435 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 436 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 437 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 438 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 439 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 440 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 441 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 442 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 443 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 444 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 445 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 446 | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 447 | golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 448 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 449 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 450 | golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= 451 | golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 452 | golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= 453 | golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= 454 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 455 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 456 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 457 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 458 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 459 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 460 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= 461 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 462 | golang.org/x/net v0.0.0-20170726083632-f5079bd7f6f7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 463 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 464 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 465 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 466 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 467 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 468 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 469 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 470 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 471 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 472 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 473 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 474 | golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= 475 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 476 | golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= 477 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 478 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 479 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 480 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 481 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 482 | golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= 483 | golang.org/x/sys v0.0.0-20170728174421-0f826bdd13b5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 484 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 485 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 486 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 487 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 488 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 489 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 490 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 491 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 492 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 493 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 494 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 495 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 496 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 497 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 498 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 499 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 500 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 501 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 502 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 503 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 504 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 505 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 506 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 507 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 508 | golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 509 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 510 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 511 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 512 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 513 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= 514 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 515 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 516 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 517 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 518 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 519 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 520 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 521 | golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= 522 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 523 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 524 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 525 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 526 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 527 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 528 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 529 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 530 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 531 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 532 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 533 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 534 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 535 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 536 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 537 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 538 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 539 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 540 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 541 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 542 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 543 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 544 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 545 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 546 | golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= 547 | golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= 548 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 549 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 550 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 551 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 552 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 553 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 554 | google.golang.org/api v0.135.0 h1:6Vgfj6uPMXcyy66waYWBwmkeNB+9GmUlJDOzkukPQYQ= 555 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 556 | google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= 557 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= 558 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 559 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 560 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 561 | google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= 562 | google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= 563 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 564 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 565 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 566 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 567 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 568 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 569 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 570 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 571 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 572 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 573 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 574 | gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= 575 | gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= 576 | gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 577 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 578 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 579 | gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= 580 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 581 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 582 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 583 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 584 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 585 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 586 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 587 | howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= 588 | howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= 589 | -------------------------------------------------------------------------------- /pkg/errors/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errors 16 | 17 | // Config-related errors. 18 | const ( 19 | ErrRepositoryConfigNil StandardError = "repository config is nil" 20 | ErrRepositoryConfigNameEmpty StandardError = "repository config name is empty" 21 | ErrRepositoryConfigExists StandardError = "repository config %q name already exists" 22 | ErrRepositoryConfigAddressEmpty StandardError = "repository config address is empty" 23 | ErrRepositoryConfigAddressUnsupported StandardError = "repository config address %q is unsupported" 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errors 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | ) 21 | 22 | // StandardError is a standard error. 23 | type StandardError string 24 | 25 | func (e StandardError) Error() string { 26 | return string(e) 27 | } 28 | 29 | // WithArgs accepts errors with parameters. 30 | func (e StandardError) WithArgs(v ...interface{}) error { 31 | var hasErr, hasNil bool 32 | for _, vv := range v { 33 | switch err := vv.(type) { 34 | case error: 35 | if err == nil { 36 | return nil 37 | } 38 | hasErr = true 39 | case nil: 40 | hasNil = true 41 | } 42 | } 43 | 44 | if hasNil && !hasErr { 45 | return nil 46 | } 47 | 48 | err := advErr{ 49 | err: fmt.Errorf("%w", e), 50 | v: v, 51 | } 52 | 53 | return err 54 | } 55 | 56 | // advErr is an error with parameters. 57 | type advErr struct { 58 | err error 59 | v []interface{} 60 | } 61 | 62 | // Error returns error string. 63 | func (e advErr) Error() string { 64 | return fmt.Sprintf(e.err.Error(), e.v...) 65 | } 66 | 67 | // Unwrap returns unwrapped error. 68 | func (e advErr) Unwrap() error { 69 | return errors.Unwrap(e.err) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/service/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package service 16 | 17 | import ( 18 | "github.com/greenpau/caddy-git/pkg/errors" 19 | "strings" 20 | ) 21 | 22 | // Config is a configuration of Manager. 23 | type Config struct { 24 | Repositories []*RepositoryConfig `json:"repositories,omitempty"` 25 | repoMap map[string]*RepositoryConfig 26 | } 27 | 28 | // AuthConfig is authentication configuration in RepositoryConfig. 29 | type AuthConfig struct { 30 | Username string `json:"username,omitempty"` 31 | Password string `json:"password,omitempty"` 32 | KeyPath string `json:"key_path,omitempty"` 33 | KeyPassphrase string `json:"key_passphrase,omitempty"` 34 | StrictHostKeyCheckingDisabled bool `json:"strict_host_key_checking_disabled,omitempty"` 35 | } 36 | 37 | // WebhookConfig is a webhook configuration in RepositoryConfig. 38 | type WebhookConfig struct { 39 | Name string `json:"name,omitempty"` 40 | Header string `json:"header,omitempty"` 41 | Secret string `json:"secret,omitempty"` 42 | } 43 | 44 | // ExecConfig is an execution script configuration in RepositoryConfig. 45 | type ExecConfig struct { 46 | Name string `json:"name,omitempty"` 47 | Command string `json:"command,omitempty"` 48 | Args []string `json:"args,omitempty"` 49 | } 50 | 51 | // RepositoryConfig is a configuration of Repository. 52 | type RepositoryConfig struct { 53 | // The alias for the Repository. 54 | Name string `json:"name,omitempty"` 55 | // The address of the Repository. 56 | Address string `json:"address,omitempty"` 57 | // The directory where the Repository is being stored locally. 58 | BaseDir string `json:"base_dir,omitempty"` 59 | Branch string `json:"branch,omitempty"` 60 | Depth int `json:"depth,omitempty"` 61 | // The interval at which repository updates automatically. 62 | UpdateInterval int `json:"update_interval,omitempty"` 63 | Auth *AuthConfig `json:"auth,omitempty"` 64 | Webhooks []*WebhookConfig `json:"webhooks,omitempty"` 65 | PostPullExec []*ExecConfig `json:"post_pull_exec,omitempty"` 66 | transport string `json:"transport,omitempty"` 67 | } 68 | 69 | // NewConfig returns an instance of Config. 70 | func NewConfig() *Config { 71 | return &Config{ 72 | repoMap: make(map[string]*RepositoryConfig), 73 | } 74 | } 75 | 76 | // NewRepositoryConfig returns an instance of RepositoryConfig. 77 | func NewRepositoryConfig() *RepositoryConfig { 78 | return &RepositoryConfig{} 79 | } 80 | 81 | // AddRepository adds a repository entry to Config. 82 | func (cfg *Config) AddRepository(rc *RepositoryConfig) error { 83 | 84 | if rc == nil { 85 | return errors.ErrRepositoryConfigNil 86 | } 87 | rc.Name = strings.TrimSpace(rc.Name) 88 | if rc.Name == "" { 89 | return errors.ErrRepositoryConfigNameEmpty 90 | } 91 | if _, exists := cfg.repoMap[rc.Name]; exists { 92 | return errors.ErrRepositoryConfigExists.WithArgs(rc.Name) 93 | } 94 | if err := rc.validate(); err != nil { 95 | return err 96 | } 97 | cfg.Repositories = append(cfg.Repositories, rc) 98 | cfg.repoMap[rc.Name] = rc 99 | return nil 100 | } 101 | 102 | func (rc *RepositoryConfig) validate() error { 103 | if rc.Address == "" { 104 | return errors.ErrRepositoryConfigAddressEmpty 105 | } 106 | if !strings.HasSuffix(rc.Address, ".git") { 107 | return errors.ErrRepositoryConfigAddressUnsupported.WithArgs(rc.Address) 108 | } 109 | 110 | switch { 111 | case strings.HasPrefix(rc.Address, "https://"), strings.HasPrefix(rc.Address, "http://"): 112 | rc.transport = "http" 113 | default: 114 | rc.transport = "ssh" 115 | } 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /pkg/service/endpoint.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package service 16 | 17 | import ( 18 | "context" 19 | "crypto/hmac" 20 | "crypto/sha256" 21 | "encoding/hex" 22 | "encoding/json" 23 | "fmt" 24 | "go.uber.org/zap" 25 | "io/ioutil" 26 | "net/http" 27 | "strings" 28 | "sync" 29 | "time" 30 | ) 31 | 32 | // Endpoint handles git management requests. 33 | type Endpoint struct { 34 | mu sync.Mutex 35 | Name string `json:"-"` 36 | Path string `json:"path,omitempty" xml:"path,omitempty" yaml:"path,omitempty"` 37 | RepositoryName string 38 | logger *zap.Logger 39 | startedAt time.Time 40 | } 41 | 42 | // SetLogger add logger to Endpoint. 43 | func (m *Endpoint) SetLogger(logger *zap.Logger) { 44 | m.logger = logger 45 | } 46 | 47 | // Provision configures the instance of Endpoint. 48 | func (m *Endpoint) Provision() error { 49 | m.startedAt = time.Now().UTC() 50 | m.Name = "git-" + m.RepositoryName 51 | 52 | m.logger.Info( 53 | "provisioned plugin instance", 54 | zap.String("instance_name", m.Name), 55 | zap.String("path", m.Path), 56 | zap.Time("started_at", m.startedAt), 57 | ) 58 | return nil 59 | } 60 | 61 | // Validate implements caddy.Validator. 62 | func (m *Endpoint) Validate() error { 63 | m.logger.Info( 64 | "validated plugin instance", 65 | zap.String("instance_name", m.Name), 66 | ) 67 | return nil 68 | } 69 | 70 | // ServeHTTP serves git management requests. 71 | func (m *Endpoint) ServeHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 72 | m.logger.Debug( 73 | "received update git repo request", 74 | zap.String("repo_name", m.RepositoryName), 75 | ) 76 | 77 | resp := make(map[string]interface{}) 78 | repo, exists := manager.repos[m.RepositoryName] 79 | if !exists { 80 | resp["status_code"] = http.StatusInternalServerError 81 | m.logger.Warn("repo not found", zap.String("repo_name", m.RepositoryName)) 82 | return m.respondHTTP(ctx, w, r, resp) 83 | } 84 | 85 | if len(repo.Config.Webhooks) > 0 { 86 | // Inspect HTTP headers for webhooks. 87 | var authorized bool 88 | for _, webhook := range repo.Config.Webhooks { 89 | hdr := r.Header.Get(webhook.Header) 90 | if hdr == "" { 91 | continue 92 | } 93 | 94 | var authFailed bool 95 | var authFailMessage string 96 | 97 | switch webhook.Header { 98 | case "X-Hub-Signature-256", strings.ToUpper("X-Hub-Signature-256"): 99 | if r.Method != "POST" { 100 | authFailed = true 101 | authFailMessage = "non-POST request" 102 | break 103 | } 104 | hdrParts := strings.SplitN(hdr, "=", 2) 105 | if len(hdrParts) != 2 { 106 | authFailed = true 107 | authFailMessage = fmt.Sprintf("malformed %s header", webhook.Header) 108 | break 109 | } 110 | if hdrParts[0] != "sha256" { 111 | authFailMessage = fmt.Sprintf("malformed %s header, sha256 not found", webhook.Header) 112 | } 113 | if err := validateSignature(r, strings.TrimSpace(hdrParts[1]), webhook.Secret); err != nil { 114 | authFailed = true 115 | authFailMessage = fmt.Sprintf("signature validation failed: %v", err) 116 | } 117 | default: 118 | if hdr != webhook.Secret { 119 | authFailed = true 120 | authFailMessage = "auth header value mismatch" 121 | } 122 | } 123 | 124 | if authFailed { 125 | resp["status_code"] = http.StatusUnauthorized 126 | m.logger.Warn( 127 | "webhook authentication failed", 128 | zap.String("repo_name", repo.Config.Name), 129 | zap.String("webhook_header", webhook.Header), 130 | zap.String("error", authFailMessage), 131 | ) 132 | return m.respondHTTP(ctx, w, r, resp) 133 | } 134 | 135 | authorized = true 136 | break 137 | } 138 | 139 | if !authorized { 140 | resp["status_code"] = http.StatusUnauthorized 141 | m.logger.Warn( 142 | "webhook authentication failed", 143 | zap.String("repo_name", repo.Config.Name), 144 | zap.String("error", "auth header not found"), 145 | ) 146 | return m.respondHTTP(ctx, w, r, resp) 147 | } 148 | } 149 | 150 | if err := repo.update(); err != nil { 151 | m.logger.Warn("failed updating repo", zap.String("repo_name", repo.Config.Name), zap.Error(err)) 152 | resp["status_code"] = http.StatusInternalServerError 153 | return m.respondHTTP(ctx, w, r, resp) 154 | } 155 | 156 | resp["status_code"] = http.StatusOK 157 | return m.respondHTTP(ctx, w, r, resp) 158 | } 159 | 160 | func (m *Endpoint) respondHTTP(ctx context.Context, w http.ResponseWriter, r *http.Request, data map[string]interface{}) error { 161 | b, _ := json.Marshal(data) 162 | if code, exists := data["status_code"]; exists { 163 | w.WriteHeader(code.(int)) 164 | } else { 165 | w.WriteHeader(http.StatusInternalServerError) 166 | } 167 | w.Write(b) 168 | return nil 169 | } 170 | 171 | func validateSignature(r *http.Request, wantSig, secret string) error { 172 | if wantSig == "" { 173 | return fmt.Errorf("empty signature") 174 | } 175 | if len(wantSig) != 64 { 176 | return fmt.Errorf("malformed sha256 hash, length %d", len(wantSig)) 177 | } 178 | 179 | respBody, err := ioutil.ReadAll(r.Body) 180 | if err != nil { 181 | return fmt.Errorf("failed reading request body") 182 | } 183 | h := hmac.New(sha256.New, []byte(secret)) 184 | h.Write(respBody) 185 | gotSig := hex.EncodeToString(h.Sum(nil)) 186 | if wantSig != gotSig { 187 | return fmt.Errorf("signature mismatch") 188 | } 189 | return nil 190 | } 191 | -------------------------------------------------------------------------------- /pkg/service/manager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package service 16 | 17 | import ( 18 | "go.uber.org/zap" 19 | "sync" 20 | ) 21 | 22 | var manager *Manager 23 | 24 | // Manager manages git repositories 25 | type Manager struct { 26 | mu sync.Mutex 27 | repos map[string]*Repository 28 | started bool 29 | logger *zap.Logger 30 | } 31 | 32 | // NewManager parses config and creates Manager instance. 33 | func NewManager(cfg *Config, logger *zap.Logger) (*Manager, error) { 34 | m := &Manager{ 35 | repos: make(map[string]*Repository), 36 | logger: logger, 37 | } 38 | manager = m 39 | for _, rc := range cfg.Repositories { 40 | if err := rc.validate(); err != nil { 41 | return nil, err 42 | } 43 | r, _ := NewRepository(rc) 44 | r.logger = logger 45 | m.repos[rc.Name] = r 46 | if err := r.update(); err != nil { 47 | m.logger.Error("failed managing repo", zap.String("repo_name", rc.Name), zap.Error(err)) 48 | return nil, err 49 | } 50 | m.logger.Debug("registered and synced repo", zap.String("repo_name", rc.Name)) 51 | if rc.UpdateInterval > 0 { 52 | go autoUpdater(r) 53 | } 54 | } 55 | return m, nil 56 | } 57 | 58 | // Start starts Manager. 59 | func (m *Manager) Start() []*Status { 60 | m.mu.Lock() 61 | defer m.mu.Unlock() 62 | return nil 63 | } 64 | 65 | // Stop stops Manager. 66 | func (m *Manager) Stop() []*Status { 67 | m.mu.Lock() 68 | defer m.mu.Unlock() 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /pkg/service/repo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package service 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "github.com/go-git/go-git/v5" 21 | "github.com/go-git/go-git/v5/plumbing" 22 | "github.com/go-git/go-git/v5/plumbing/transport" 23 | "github.com/go-git/go-git/v5/plumbing/transport/http" 24 | "github.com/go-git/go-git/v5/plumbing/transport/ssh" 25 | "go.uber.org/zap" 26 | cryptossh "golang.org/x/crypto/ssh" 27 | "os" 28 | "os/exec" 29 | "path" 30 | "path/filepath" 31 | "strings" 32 | "sync" 33 | "time" 34 | ) 35 | 36 | // Repository is a configuration for a command or app. 37 | type Repository struct { 38 | Config *RepositoryConfig `json:"config,omitempty"` 39 | mu sync.Mutex 40 | logger *zap.Logger 41 | lastUpdated time.Time 42 | updating bool 43 | } 44 | 45 | // NewRepository returns an instance of Repository. 46 | func NewRepository(rc *RepositoryConfig) (*Repository, error) { 47 | r := &Repository{ 48 | Config: rc, 49 | } 50 | return r, nil 51 | } 52 | 53 | func (r *Repository) update() error { 54 | if r.updating { 55 | return nil 56 | } 57 | 58 | r.mu.Lock() 59 | defer r.mu.Unlock() 60 | r.updating = true 61 | defer func() { 62 | r.updating = false 63 | }() 64 | 65 | err := r.runUpdate() 66 | if err != nil { 67 | return err 68 | } 69 | 70 | if len(r.Config.PostPullExec) > 0 { 71 | r.runPostPullExec() 72 | } 73 | 74 | return nil 75 | } 76 | 77 | func (r *Repository) runPostPullExec() { 78 | for _, entry := range r.Config.PostPullExec { 79 | var stdout, stderr bytes.Buffer 80 | switch { 81 | case entry.Command != "": 82 | cmd := exec.Command(entry.Command, entry.Args...) 83 | cmd.Stdout = &stdout 84 | cmd.Stderr = &stderr 85 | if err := cmd.Run(); err != nil { 86 | r.logger.Warn( 87 | "failed executing post-pull command", 88 | zap.String("repo_name", r.Config.Name), 89 | zap.String("error", fmt.Sprintf("%v", cmd.Stderr)), 90 | ) 91 | continue 92 | } 93 | r.logger.Debug( 94 | "executed post-pull command", 95 | zap.String("repo_name", r.Config.Name), 96 | zap.String("stdout", fmt.Sprintf("%v", cmd.Stdout)), 97 | zap.String("stderr", fmt.Sprintf("%v", cmd.Stderr)), 98 | ) 99 | } 100 | } 101 | } 102 | 103 | func (r *Repository) runUpdate() error { 104 | r.Config.BaseDir = expandDir(r.Config.BaseDir) 105 | 106 | baseDirExists, err := dirExists(r.Config.BaseDir) 107 | if err != nil { 108 | return err 109 | } 110 | if !baseDirExists { 111 | if err := os.MkdirAll(r.Config.BaseDir, 0700); err != nil { 112 | return err 113 | } 114 | } 115 | 116 | repoDir := path.Join(r.Config.BaseDir, r.Config.Name) 117 | repoDirExists, err := dirExists(repoDir) 118 | if err != nil { 119 | return err 120 | } 121 | if !repoDirExists { 122 | // Clone the repository. 123 | opts := &git.CloneOptions{} 124 | if err := configureCloneOptions(r.Config, opts); err != nil { 125 | return err 126 | } 127 | if _, err := git.PlainClone(repoDir, false, opts); err != nil { 128 | return err 129 | } 130 | } 131 | 132 | // Pull the repository. 133 | repoDir, err = filepath.Abs(repoDir) 134 | if err != nil { 135 | return err 136 | } 137 | repo, err := git.PlainOpen(repoDir) 138 | if err != nil { 139 | return err 140 | } 141 | w, err := repo.Worktree() 142 | if err != nil { 143 | return err 144 | } 145 | 146 | opts := &git.PullOptions{} 147 | if err := configurePullOptions(r.Config, opts); err != nil { 148 | return err 149 | } 150 | if err := w.Pull(opts); err != nil { 151 | if err == git.NoErrAlreadyUpToDate { 152 | r.logger.Debug( 153 | "repo is already up to date", 154 | zap.String("repo_name", r.Config.Name), 155 | ) 156 | return nil 157 | } 158 | return err 159 | } 160 | ref, err := repo.Head() 161 | if err != nil { 162 | return err 163 | } 164 | commit, err := repo.CommitObject(ref.Hash()) 165 | if err != nil { 166 | return err 167 | } 168 | 169 | r.logger.Debug( 170 | "pulled latest commit", 171 | zap.String("repo_name", r.Config.Name), 172 | zap.Any("commit", commit.Hash.String()), 173 | ) 174 | return nil 175 | } 176 | 177 | func dirExists(s string) (bool, error) { 178 | if s == "" { 179 | return true, nil 180 | } 181 | _, err := os.Stat(s) 182 | if os.IsNotExist(err) { 183 | return false, nil 184 | } 185 | return true, err 186 | 187 | } 188 | 189 | func configureCloneOptions(cfg *RepositoryConfig, opts *git.CloneOptions) error { 190 | opts.URL = cfg.Address 191 | trAuthMethod, err := configureAuthOptions(cfg) 192 | if err != nil { 193 | return err 194 | } 195 | opts.Auth = trAuthMethod 196 | if cfg.Depth > 0 { 197 | opts.Depth = cfg.Depth 198 | } 199 | if cfg.Branch != "" { 200 | opts.ReferenceName = plumbing.NewBranchReferenceName(cfg.Branch) 201 | } 202 | return nil 203 | } 204 | 205 | func configurePullOptions(cfg *RepositoryConfig, opts *git.PullOptions) error { 206 | opts.RemoteName = "origin" 207 | trAuthMethod, err := configureAuthOptions(cfg) 208 | if err != nil { 209 | return err 210 | } 211 | opts.Auth = trAuthMethod 212 | if cfg.Depth > 0 { 213 | opts.Depth = cfg.Depth 214 | } 215 | if cfg.Branch != "" { 216 | opts.ReferenceName = plumbing.NewBranchReferenceName(cfg.Branch) 217 | opts.SingleBranch = true 218 | } 219 | return nil 220 | } 221 | 222 | func configureAuthOptions(cfg *RepositoryConfig) (transport.AuthMethod, error) { 223 | if cfg.Auth == nil { 224 | return nil, nil 225 | } 226 | cfg.Auth.KeyPath = expandDir(cfg.Auth.KeyPath) 227 | 228 | switch cfg.transport { 229 | case "http": 230 | // Configure authentication for HTTP/S. 231 | switch { 232 | case cfg.Auth.Username != "": 233 | return &http.BasicAuth{ 234 | Username: cfg.Auth.Username, 235 | Password: cfg.Auth.Password, 236 | }, nil 237 | } 238 | case "ssh": 239 | // Configure authentication for SSH. 240 | switch { 241 | case cfg.Auth.KeyPath != "": 242 | var publicKeysUser string 243 | switch { 244 | case strings.Contains(cfg.Address, "@"): 245 | cfgAddressArr := strings.SplitN(cfg.Address, "@", 2) 246 | publicKeysUser = cfgAddressArr[0] 247 | case cfg.Auth.Username != "": 248 | publicKeysUser = cfg.Auth.Username 249 | } 250 | 251 | if publicKeysUser == "" { 252 | publicKeysUser = "git" 253 | } 254 | 255 | publicKeys, err := ssh.NewPublicKeysFromFile(publicKeysUser, cfg.Auth.KeyPath, cfg.Auth.KeyPassphrase) 256 | if err != nil { 257 | return nil, err 258 | } 259 | if cfg.Auth.StrictHostKeyCheckingDisabled { 260 | publicKeys.HostKeyCallbackHelper = ssh.HostKeyCallbackHelper{ 261 | HostKeyCallback: cryptossh.InsecureIgnoreHostKey(), 262 | } 263 | } 264 | return publicKeys, nil 265 | case cfg.Auth.Username != "": 266 | password := &ssh.Password{ 267 | User: cfg.Auth.Username, 268 | Password: cfg.Auth.Password, 269 | } 270 | if cfg.Auth.StrictHostKeyCheckingDisabled { 271 | password.HostKeyCallbackHelper = ssh.HostKeyCallbackHelper{ 272 | HostKeyCallback: cryptossh.InsecureIgnoreHostKey(), 273 | } 274 | } 275 | return password, nil 276 | } 277 | } 278 | return nil, nil 279 | } 280 | 281 | func expandDir(s string) string { 282 | if s == "" || !strings.HasPrefix(s, "~") { 283 | return s 284 | } 285 | hd, err := os.UserHomeDir() 286 | if err != nil { 287 | return s 288 | } 289 | output := hd + s[1:] 290 | return output 291 | } 292 | 293 | func autoUpdater(r *Repository) { 294 | r.logger.Debug( 295 | "auto-update enabled", 296 | zap.String("repo_name", r.Config.Name), 297 | zap.Int("interval", r.Config.UpdateInterval), 298 | ) 299 | intervals := time.NewTicker(time.Second * time.Duration(r.Config.UpdateInterval)) 300 | defer intervals.Stop() 301 | for range intervals.C { 302 | if r == nil { 303 | break 304 | } 305 | if err := r.update(); err != nil { 306 | r.logger.Error("failed auto-updating repo", zap.String("repo_name", r.Config.Name), zap.Error(err)) 307 | continue 308 | } 309 | r.logger.Debug("auto-updated repo", zap.String("repo_name", r.Config.Name)) 310 | } 311 | return 312 | } 313 | -------------------------------------------------------------------------------- /pkg/service/status.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package service 16 | 17 | // Status represent the last recorded status of a git repository. 18 | type Status struct { 19 | Repository string `json:"repository,omitempty"` 20 | Error error `json:"error,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /plugin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package git 16 | 17 | import ( 18 | "net/http" 19 | 20 | "github.com/caddyserver/caddy/v2" 21 | "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" 22 | "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" 23 | "github.com/caddyserver/caddy/v2/modules/caddyhttp" 24 | "github.com/greenpau/caddy-git/pkg/service" 25 | ) 26 | 27 | func init() { 28 | caddy.RegisterModule(Middleware{}) 29 | } 30 | 31 | // Middleware implements git repository manager. 32 | type Middleware struct { 33 | Endpoint *service.Endpoint `json:"endpoint,omitempty"` 34 | } 35 | 36 | // CaddyModule returns the Caddy module information. 37 | func (Middleware) CaddyModule() caddy.ModuleInfo { 38 | return caddy.ModuleInfo{ 39 | ID: "http.handlers.git", 40 | New: func() caddy.Module { return new(Middleware) }, 41 | } 42 | } 43 | 44 | // UnmarshalCaddyfile unmarshals a Caddyfile. 45 | func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) (err error) { 46 | endpoint, err := parseCaddyfileHandlerConfig(httpcaddyfile.Helper{Dispenser: d}) 47 | if err != nil { 48 | return err 49 | } 50 | m.Endpoint = endpoint 51 | return nil 52 | } 53 | 54 | // Provision provisions git repository endpoint. 55 | func (m *Middleware) Provision(ctx caddy.Context) error { 56 | m.Endpoint.SetLogger(ctx.Logger(m)) 57 | return m.Endpoint.Provision() 58 | } 59 | 60 | // Validate implements caddy.Validator. 61 | func (m *Middleware) Validate() error { 62 | return m.Endpoint.Validate() 63 | } 64 | 65 | // ServeHTTP performs git repository management tasks. 66 | func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, _ caddyhttp.Handler) error { 67 | return m.Endpoint.ServeHTTP(r.Context(), w, r) 68 | } 69 | 70 | // Interface guards 71 | var ( 72 | _ caddy.Provisioner = (*Middleware)(nil) 73 | _ caddy.Validator = (*Middleware)(nil) 74 | _ caddyhttp.MiddlewareHandler = (*Middleware)(nil) 75 | _ caddyfile.Unmarshaler = (*Middleware)(nil) 76 | ) 77 | --------------------------------------------------------------------------------