├── .github └── CODEOWNERS ├── renovate.json ├── go.mod ├── examples └── git-mkdocs │ ├── manifests │ ├── secret.yaml │ ├── service.yaml │ ├── configmap.yaml │ ├── ingress.yaml │ └── deployment.yaml │ └── README.md ├── Makefile ├── Dockerfile ├── README.md ├── main.go ├── go.sum └── LICENSE /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @spoukke 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/padok-team/git-volume-reloader 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/go-git/go-git/v5 v5.4.2 7 | github.com/go-playground/webhooks/v6 v6.0.0-beta.3 8 | github.com/sirupsen/logrus v1.8.1 9 | ) 10 | -------------------------------------------------------------------------------- /examples/git-mkdocs/manifests/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | labels: 5 | app.kubernetes.io/instance: docs 6 | name: docs 7 | type: Opaque 8 | stringData: 9 | GITHUB_SECRET: github-secret 10 | SSH_PRIVATE_KEY: | 11 | -----BEGIN OPENSSH PRIVATE KEY----- 12 | ... 13 | -----END OPENSSH PRIVATE KEY----- 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION ?= latest 2 | IMAGE ?= ghcr.io/padok-team/git-volume-reloader:$(VERSION) 3 | 4 | .DEFAULT_TARGET = help 5 | 6 | ## help: Display list of commands 7 | .PHONY: help 8 | help: Makefile 9 | @sed -n 's|^##||p' $< | column -t -s ':' | sed -e 's|^| |' 10 | 11 | ## build: Build a container image 12 | .PHONY: build 13 | build: 14 | docker build . -t ${IMAGE} 15 | 16 | ## push: Push the container image to a registry 17 | .PHONY: push 18 | push: 19 | docker push ${IMAGE} 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16 as build 2 | 3 | WORKDIR /workspace 4 | 5 | # Download dependencies. 6 | COPY go.* . 7 | RUN go mod download 8 | 9 | # Copy the application's source code. 10 | COPY main.go . 11 | 12 | # Build the application. 13 | RUN CGO_ENABLED=0 go build -o=git-volume-reloader 14 | 15 | # ================================================ 16 | 17 | FROM alpine/git:v2.30.2 18 | 19 | LABEL org.opencontainers.image.source=https://github.com/padok-team/git-volume-reloader 20 | 21 | WORKDIR / 22 | 23 | # Copy the binary built in the previous stage. 24 | COPY --from=build /workspace/git-volume-reloader . 25 | 26 | ENTRYPOINT ["/git-volume-reloader"] 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-volume-reloader 2 | 3 | Synchronise a directory's contents with a git repository. Synchronisation is 4 | triggered by a webhook sent by the git service provider. 5 | 6 | ## Usage 7 | 8 | To package the git-volume-reloader into a container image and push the image to 9 | a container registry: 10 | 11 | ```bash 12 | make build push IMAGE=ghcr.io/padok-team/git-volume-reloader:latest 13 | ``` 14 | 15 | An example of how to deploy the git-volume-reloader as a sidecar in a Kubernetes 16 | Pod is available in the [examples/git-mkdocs](./examples/git-mkdocs/README.md) directory. 17 | 18 | ## LICENSE 19 | 20 | © 2021 [Padok](https://www.padok.fr/). 21 | 22 | Licensed under the [Apache License](https://www.apache.org/licenses/LICENSE-2.0), Version 2.0 ([LICENSE](./LICENSE)) 23 | -------------------------------------------------------------------------------- /examples/git-mkdocs/manifests/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/instance: docs 6 | app.kubernetes.io/name: docs 7 | name: docs 8 | spec: 9 | ports: 10 | - name: http 11 | port: 80 12 | protocol: TCP 13 | targetPort: http 14 | selector: 15 | app.kubernetes.io/instance: docs 16 | app.kubernetes.io/name: docs 17 | type: ClusterIP 18 | --- 19 | apiVersion: v1 20 | kind: Service 21 | metadata: 22 | labels: 23 | app.kubernetes.io/instance: docs 24 | app.kubernetes.io/name: docs 25 | name: docs-webhook 26 | spec: 27 | ports: 28 | - name: http 29 | port: 80 30 | protocol: TCP 31 | targetPort: webhook 32 | selector: 33 | app.kubernetes.io/instance: docs 34 | app.kubernetes.io/name: docs 35 | type: ClusterIP -------------------------------------------------------------------------------- /examples/git-mkdocs/manifests/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | labels: 5 | app.kubernetes.io/instance: docs 6 | name: docs 7 | data: 8 | GIT_BRANCH: refs/heads/main 9 | GIT_PROVIDER: github 10 | REPOSITORY_URL: 'git@github.com:padok-team/git-volume-reloader.git' 11 | --- 12 | apiVersion: v1 13 | kind: ConfigMap 14 | metadata: 15 | labels: 16 | app.kubernetes.io/instance: docs 17 | name: git-volume-reloader-ssh-known-hosts 18 | data: 19 | known_hosts: > 20 | github.com ssh-rsa 21 | AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== 22 | -------------------------------------------------------------------------------- /examples/git-mkdocs/manifests/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | ingress.kubernetes.io/ssl-redirect: 'true' 6 | labels: 7 | app.kubernetes.io/instance: docs 8 | app.kubernetes.io/name: docs 9 | name: docs 10 | spec: 11 | ingressClassName: nginx 12 | rules: 13 | - host: docs.padok.cloud 14 | http: 15 | paths: 16 | - path: / 17 | backend: 18 | service: 19 | name: docs 20 | port: 21 | number: 80 22 | pathType: ImplementationSpecific 23 | tls: 24 | - hosts: 25 | - docs.padok.cloud 26 | secretName: docs-cert 27 | --- 28 | apiVersion: networking.k8s.io/v1 29 | kind: Ingress 30 | metadata: 31 | annotations: 32 | ingress.kubernetes.io/ssl-redirect: 'true' 33 | labels: 34 | app.kubernetes.io/instance: docs 35 | app.kubernetes.io/name: docs 36 | name: docs-webhook 37 | spec: 38 | ingressClassName: nginx 39 | rules: 40 | - host: docs.padok.cloud 41 | http: 42 | paths: 43 | - path: /webhook 44 | backend: 45 | service: 46 | name: docs-webhook 47 | port: 48 | number: 80 49 | pathType: Exact 50 | tls: 51 | - hosts: 52 | - docs.padok.cloud 53 | secretName: docs-cert 54 | -------------------------------------------------------------------------------- /examples/git-mkdocs/manifests/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app.kubernetes.io/instance: docs 6 | app.kubernetes.io/name: docs 7 | name: docs 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app.kubernetes.io/instance: docs 13 | app.kubernetes.io/name: docs 14 | template: 15 | metadata: 16 | labels: 17 | app.kubernetes.io/instance: docs 18 | app.kubernetes.io/name: docs 19 | spec: 20 | containers: 21 | - env: 22 | - name: WORKING_DIR 23 | value: /docs 24 | - name: PORT 25 | value: '80' 26 | envFrom: 27 | - configMapRef: 28 | name: docs 29 | - secretRef: 30 | name: docs 31 | image: 'ghcr.io/padok-team/git-volume-reloader:v0.0.1' 32 | livenessProbe: 33 | httpGet: 34 | path: /healthz 35 | port: webhook 36 | initialDelaySeconds: 15 37 | name: git-volume-reloader 38 | ports: 39 | - containerPort: 80 40 | name: webhook 41 | protocol: TCP 42 | readinessProbe: 43 | httpGet: 44 | path: /healthz 45 | port: webhook 46 | initialDelaySeconds: 15 47 | resources: {} 48 | volumeMounts: 49 | - mountPath: /docs 50 | name: shared-volume 51 | - mountPath: /root/.ssh/ 52 | name: ssh-known-hosts 53 | - args: 54 | - serve 55 | - '--dev-addr' 56 | - '0.0.0.0:8080' 57 | image: 'squidfunk/mkdocs-material:7.2.2' 58 | livenessProbe: 59 | httpGet: 60 | path: / 61 | port: http 62 | initialDelaySeconds: 15 63 | name: mkdocs 64 | ports: 65 | - containerPort: 8080 66 | name: http 67 | protocol: TCP 68 | readinessProbe: 69 | httpGet: 70 | path: / 71 | port: http 72 | initialDelaySeconds: 15 73 | resources: {} 74 | volumeMounts: 75 | - mountPath: /docs 76 | name: shared-volume 77 | securityContext: {} 78 | volumes: 79 | - emptyDir: {} 80 | name: shared-volume 81 | - configMap: 82 | name: git-volume-reloader-ssh-known-hosts 83 | name: ssh-known-hosts 84 | -------------------------------------------------------------------------------- /examples/git-mkdocs/README.md: -------------------------------------------------------------------------------- 1 | # git-mkdocs 2 | 3 | - [Use Case](#use-case) 4 | - [Prerequisites](#prerequisites) 5 | - [Step 1: Prepare your git repository](#step-1-prepare-your-git-repository) 6 | - [Step 1.1: Setup a SSH key](#step-11-setup-a-ssh-key) 7 | - [Step 1.2: Setup a webhook](#step-12-setup-a-webhook) 8 | - [Step 2: Prepare your Kubernetes manifests](#step-2-prepare-your-kubernetes-manifests) 9 | - [Step 2.1 : Update the secret.yaml manifest](#step-21--update-the-secretyaml-manifest) 10 | - [Step 2.2 : Update the configmap.yaml manifest](#step-22--update-the-configmapyaml-manifest) 11 | - [Step 2.3 : Update the ingress.yaml manifest](#step-23--update-the-ingressyaml-manifest) 12 | - [Step 2.4 : Update the deployment.yaml manifest](#step-24--update-the-deploymentyaml-manifest) 13 | - [Step 3: Deploy](#step-3-deploy) 14 | 15 | ## Use Case 16 | 17 | This example deploys a static documentation website generated with [mkdocs](https://www.mkdocs.org/). The documentation's source is stored in a GitHub repository. The website regenerates itself when changes are pushed to the `main` branch. 18 | 19 | ## Prerequisites 20 | 21 | - A repository containing documentation structured for mkdocs. 22 | - A Docker an image of the `git-volume-reloader` available in a registry. Currently, no public image is available. 23 | - A domain name that resolves to your cluster's public IP address. 24 | - A valid certificate for your domain name stored in a Kubernetes secret. Alternatively, you can use [`cert-manager`](https://cert-manager.io/docs/) to generate the secret for you. 25 | 26 | ## Step 1: Prepare your git repository 27 | 28 | ### Step 1.1: Setup a SSH key 29 | 30 | Generate a SSH key pair without a passphrase. 31 | 32 | ```zsh 33 | ssh-keygen -t RSA -b 2048 34 | ``` 35 | 36 | In you documentation repository, go to **Settings > Deploy keys** and click on **Add deploy key** to add the public SSH key you just generated. Keep your private key somewhere, you will need it later. 37 | 38 | ### Step 1.2: Setup a webhook 39 | 40 | Generate a secret key. 41 | 42 | ```zsh 43 | openssl rand -base64 32 44 | ``` 45 | 46 | In you documentation repository, go to **Settings > Webhooks** and click on **Add webhook**. For the webhook parameters: 47 | 48 | - The **Payload URL** is the host of the documentation with `/webhook` appended. For example, if you want your documentation to be available on `docs.padok.cloud`, the payload URL will be `https://docs.padok.cloud/webhook`. 49 | - The **Content Type** must be set to `application/json`. 50 | - The **Secret** is the secret key you just generated above. 51 | - Leave the other parameters with their default values. 52 | 53 | ## Step 2: Prepare your Kubernetes manifests 54 | 55 | Copy the examples manifests avaialble in `./manifests`. 56 | 57 | ### Step 2.1 : Update the secret.yaml manifest 58 | 59 | - Put the webhook's secret key you generated in step 1.2 under the `GITHUB_SECRET` key of the Kubernetes secret. 60 | - Put the SSH private key you generated in step 1.1 under the `SSH_PRIVATE_KEY` key of the Kubernetes secret. 61 | 62 | ### Step 2.2 : Update the configmap.yaml manifest 63 | 64 | In the `docs` ConfigMap, update all the values. The keys are self-explanatory. 65 | 66 | ### Step 2.3 : Update the ingress.yaml manifest 67 | 68 | - Change all the `host` values to the hostname you want your documentation to be available at. 69 | - Update the `tls` field of both Ingress resources to match your TLS certificate. 70 | 71 | ### Step 2.4 : Update the deployment.yaml manifest 72 | 73 | Change the `image` of the `git-volume-reloader` container to the one you have in your registry. If needed, add an `imagePullSecrets` field in your Deployment. 74 | 75 | ## Step 3: Deploy 76 | 77 | You can now deploy your documentation. Go to the folder you put your manifests in and run: 78 | 79 | ```zsh 80 | kubectl apply -f . 81 | ``` 82 | 83 | Your documentation should be available at the URL you configured. You can also try to push a change to the documentation to the branch you configured. The documentation should update itself. 84 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/go-git/go-git/v5" 9 | "github.com/go-git/go-git/v5/plumbing" 10 | "github.com/go-git/go-git/v5/plumbing/transport/ssh" 11 | "github.com/go-playground/webhooks/v6/github" 12 | log "github.com/sirupsen/logrus" 13 | ) 14 | 15 | func main() { 16 | var ( 17 | gitBranch = os.Getenv("GIT_BRANCH") 18 | githubSecret = os.Getenv("GITHUB_SECRET") 19 | gitProvider = os.Getenv("GIT_PROVIDER") 20 | repositoryURL = os.Getenv("REPOSITORY_URL") 21 | sshPrivateKey = []byte(os.Getenv("SSH_PRIVATE_KEY")) 22 | workingDir = os.Getenv("WORKING_DIR") 23 | ) 24 | 25 | if err := checkoutGitRepository(sshPrivateKey, workingDir, repositoryURL, gitBranch); err != nil { 26 | log.Fatalf("failed to checkout branch %q of repository %q into directory %q: %w", gitBranch, repositoryURL, workingDir, err) 27 | } 28 | 29 | var webhookHandler http.HandlerFunc 30 | var err error 31 | switch gitProvider { 32 | case "github": 33 | webhookHandler, err = handleGithubWebhook(gitBranch, githubSecret, workingDir, sshPrivateKey) 34 | if err != nil { 35 | log.Fatalf("failed to prepare webhook: %w", err) 36 | } 37 | default: 38 | log.Fatalf("unknown git provider %q; current implementation only accepts \"github\"", gitProvider) 39 | } 40 | 41 | http.HandleFunc("/webhook", webhookHandler) 42 | http.HandleFunc("/healthz", healthcheck) 43 | 44 | log.Println("Listening for requests on port 80...") 45 | if err := http.ListenAndServe(":80", nil); err != nil { 46 | log.Fatalf("failed to start HTTP server: %w", err) 47 | } 48 | } 49 | 50 | func checkoutGitRepository(sshPrivateKey []byte, workingDir, repositoryURL, gitBranch string) error { 51 | publicKeys, err := ssh.NewPublicKeys("git", sshPrivateKey, "") 52 | if err != nil { 53 | return fmt.Errorf("failed to generate public keys: %w", err) 54 | } 55 | 56 | cloneOptions := git.CloneOptions{ 57 | Auth: publicKeys, 58 | URL: repositoryURL, 59 | } 60 | repo, err := git.PlainClone(workingDir, false, &cloneOptions) 61 | if err != nil { 62 | return fmt.Errorf("failed to clone repository: %w", err) 63 | } 64 | 65 | worktree, err := repo.Worktree() 66 | if err != nil { 67 | return fmt.Errorf("cloned repository is broken: %w", err) 68 | } 69 | 70 | checkoutOptions := git.CheckoutOptions{ 71 | Branch: plumbing.ReferenceName(gitBranch), 72 | } 73 | if err := worktree.Checkout(&checkoutOptions); err != nil { 74 | return fmt.Errorf("failed to checkout branch %q: %w", gitBranch, err) 75 | } 76 | 77 | return nil 78 | } 79 | 80 | func handleGithubWebhook(gitBranch, githubSecret, workingDir string, sshPrivateKey []byte) (http.HandlerFunc, error) { 81 | githubWebhook, err := github.New(github.Options.Secret(githubSecret)) 82 | if err != nil { 83 | return nil, fmt.Errorf("failed to set up GitHub webhook: %w", err) 84 | } 85 | 86 | handler := func(w http.ResponseWriter, r *http.Request) { 87 | payload, err := githubWebhook.Parse(r, github.PushEvent) 88 | switch err { 89 | case nil: // no error, do nothing. 90 | case github.ErrEventNotFound: 91 | log.Info("webhook called for non-existing event, skipping") 92 | default: 93 | log.Errorf("could not parse payload: %q", err) 94 | http.Error(w, "Invalid payload", http.StatusBadRequest) 95 | return 96 | } 97 | 98 | pushEvent := payload.(github.PushPayload) 99 | if pushEvent.Ref != gitBranch { 100 | log.Info("webhook called for ref %q, skipping", pushEvent.Ref) 101 | return 102 | } 103 | 104 | if err := updateRepository(workingDir, sshPrivateKey); err != nil { 105 | log.Errorf("failed to update repository: %w", err) 106 | http.Error(w, "Internal error", http.StatusInternalServerError) 107 | return 108 | } 109 | 110 | log.Info("repository updated following event from GitHub") 111 | } 112 | 113 | return handler, nil 114 | } 115 | 116 | func updateRepository(workingDir string, sshPrivateKey []byte) error { 117 | publicKeys, err := ssh.NewPublicKeys("git", sshPrivateKey, "") 118 | if err != nil { 119 | return fmt.Errorf("failed to generate public keys: %w", err) 120 | } 121 | 122 | repo, err := git.PlainOpen(workingDir) 123 | if err != nil { 124 | return fmt.Errorf("repository in %q directory is broken: %w", workingDir, err) 125 | } 126 | 127 | worktree, err := repo.Worktree() 128 | if err != nil { 129 | return fmt.Errorf("repository in %q directory is broken: %w", workingDir, err) 130 | } 131 | 132 | pullOptions := git.PullOptions{ 133 | Auth: publicKeys, 134 | RemoteName: "origin", 135 | } 136 | if err := worktree.Pull(&pullOptions); err != nil { 137 | return fmt.Errorf("failed to pull repository: %q", err) 138 | } 139 | 140 | return nil 141 | } 142 | 143 | func healthcheck(w http.ResponseWriter, r *http.Request) { 144 | // The HTTP server is always healthy. 145 | } 146 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 2 | github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= 3 | github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= 4 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= 5 | github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= 6 | github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= 7 | github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= 8 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= 9 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 10 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 11 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 12 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 15 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16 | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= 17 | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= 18 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 19 | github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= 20 | github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 21 | github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= 22 | github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= 23 | github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 24 | github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= 25 | github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= 26 | github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= 27 | github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= 28 | github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= 29 | github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= 30 | github.com/go-playground/webhooks/v6 v6.0.0-beta.3 h1:QtGNPdIXR+IgQilOx/KSW/uHS+zD0G1XifJELzrGIbg= 31 | github.com/go-playground/webhooks/v6 v6.0.0-beta.3/go.mod h1:GCocmfMtpJdkEOM1uG9p2nXzg1kY5X/LtvQgtPHUaaA= 32 | github.com/gogits/go-gogs-client v0.0.0-20200905025246-8bb8a50cb355/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0= 33 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 34 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 35 | github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= 36 | github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= 37 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 38 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 39 | github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 40 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= 41 | github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 42 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 43 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 44 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 45 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 46 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 47 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 48 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 49 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 50 | github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= 51 | github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= 52 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 53 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 54 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 55 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 56 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 57 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 58 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 59 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 60 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 61 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 62 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 63 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 64 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 65 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 66 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 67 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 68 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 69 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 70 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 71 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 72 | github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= 73 | github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= 74 | golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 75 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 76 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= 77 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 78 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 79 | golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= 80 | golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= 81 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 82 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 83 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 84 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 86 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 87 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 88 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 89 | golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E= 90 | golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 91 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= 92 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 93 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 94 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 95 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 96 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 97 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 98 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 99 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 100 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 101 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 102 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 103 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 104 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 105 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 106 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 107 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 108 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 109 | -------------------------------------------------------------------------------- /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 2021 Padok 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 | --------------------------------------------------------------------------------