0
0
mirror of https://github.com/thegeeklab/wp-git-clone.git synced 2024-11-21 04:00:40 +00:00

initial commit

This commit is contained in:
Robert Kaussow 2023-12-23 00:59:23 +01:00
commit 378831f327
Signed by: xoxys
GPG Key ID: 4E692A2EAECC03C0
40 changed files with 2450 additions and 0 deletions

4
.dictionary Normal file
View File

@ -0,0 +1,4 @@
userid
url
roomid
wp-git-clone

71
.github/settings.yml vendored Normal file
View File

@ -0,0 +1,71 @@
repository:
name: wp-git-clone
description: Woodpecker CI plugin to clone git repositories
homepage: https://woodpecker-plugins.geekdocs.de/plugins/wp-git-clone
topics: woodpecker-ci, woodpecker, woodpecker-plugin
private: false
has_issues: true
has_wiki: false
has_downloads: true
default_branch: main
allow_squash_merge: true
allow_merge_commit: true
allow_rebase_merge: true
labels:
- name: bug
color: d73a4a
description: Something isn't working
- name: documentation
color: 0075ca
description: Improvements or additions to documentation
- name: duplicate
color: cfd3d7
description: This issue or pull request already exists
- name: enhancement
color: a2eeef
description: New feature or request
- name: good first issue
color: 7057ff
description: Good for newcomers
- name: help wanted
color: 008672
description: Extra attention is needed
- name: invalid
color: e4e669
description: This doesn't seem right
- name: question
color: d876e3
description: Further information is requested
- name: wontfix
color: ffffff
description: This will not be worked on
branches:
- name: main
protection:
required_pull_request_reviews: null
required_status_checks:
strict: false
contexts:
- ci/woodpecker/pr/test
- ci/woodpecker/pr/build-package
- ci/woodpecker/pr/build-container
- ci/woodpecker/pr/docs
enforce_admins: false
required_linear_history: true
restrictions: null
- name: docs
protection:
required_pull_request_reviews: null
required_status_checks: null
enforce_admins: true
required_linear_history: true
restrictions:
apps: []
users: []
teams:
- bot

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/dist
/release
/wp-git-clone*
docs/data/data-raw.yaml
coverage.out
CHANGELOG.md

47
.gitsv/config.yml Normal file
View File

@ -0,0 +1,47 @@
---
version: "1.1"
versioning:
update-major: []
update-minor: [feat]
update-patch: [fix, perf, refactor, chore, test, ci, docs]
tag:
pattern: "v%d.%d.%d"
release-notes:
sections:
- name: Features
commit-types: [feat]
section-type: commits
- name: Bug Fixes
commit-types: [fix]
section-type: commits
- name: Performance Improvements
commit-types: [perf]
section-type: commits
- name: Code Refactoring
commit-types: [refactor]
section-type: commits
- name: Others
commit-types: [chore]
section-type: commits
- name: Testing
commit-types: [test]
section-type: commits
- name: CI Pipeline
commit-types: [ci]
section-type: commits
- name: Documentation
commit-types: [docs]
section-type: commits
- name: BREAKING CHANGES
section-type: breaking-changes
commit-message:
footer:
issue:
key: issue
add-value-prefix: "#"
issue:
regex: "#?[0-9]+"

104
.golangci.yml Normal file
View File

@ -0,0 +1,104 @@
linters:
enable-all: false
disable-all: true
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- unused
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
- contextcheck
- decorder
- dogsled
- dupl
- dupword
- durationcheck
- errchkjson
- errname
- errorlint
- execinquery
- exhaustive
- exportloopref
- forcetypeassert
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- goerr113
- gofmt
- gofumpt
- goheader
- goimports
- gomnd
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- grouper
- importas
- interfacebloat
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- misspell
- musttag
- nakedret
- nestif
- nilerr
- nilnil
- nlreturn
- noctx
- nolintlint
- nonamedreturns
- nosprintfhostport
- prealloc
- predeclared
- promlinter
- reassign
- revive
# - rowserrcheck
# - sqlclosecheck
# - structcheck
- stylecheck
- tagliatelle
- tenv
- testableexamples
- thelper
- tparallel
- unconvert
- unparam
- usestdlibvars
# - wastedassign
- whitespace
- wsl
- zerologlint
fast: false
run:
timeout: 3m
linters-settings:
gofumpt:
extra-rules: true
ireturn:
allow:
- anon
- error
- empty
- stdlib
- ^github\.com\/cenkalti\/backoff\/\S+\.BackOff$

1
.lycheeignore Normal file
View File

@ -0,0 +1 @@
https://hub.docker.com/r/thegeeklab/*

6
.markdownlint.yml Normal file
View File

@ -0,0 +1,6 @@
---
default: True
MD013: False
MD041: False
MD004:
style: dash

2
.prettierignore Normal file
View File

@ -0,0 +1,2 @@
*.tpl.md
LICENSE

View File

@ -0,0 +1,66 @@
---
when:
- event: [pull_request, tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: dryrun
image: quay.io/thegeeklab/wp-docker-buildx:2
settings:
containerfile: Containerfile.multiarch
dry_run: true
platforms:
- linux/amd64
- linux/arm64
provenance: false
repo: ${CI_REPO}
when:
- event: [pull_request]
publish-dockerhub:
group: container
image: quay.io/thegeeklab/wp-docker-buildx:2
settings:
auto_tag: true
containerfile: Containerfile.multiarch
password:
from_secret: docker_password
platforms:
- linux/amd64
- linux/arm64
provenance: false
repo: ${CI_REPO}
username:
from_secret: docker_username
when:
- event: [tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
publish-quay:
group: container
image: quay.io/thegeeklab/wp-docker-buildx:2
settings:
auto_tag: true
containerfile: Containerfile.multiarch
password:
from_secret: quay_password
platforms:
- linux/amd64
- linux/arm64
provenance: false
registry: quay.io
repo: quay.io/${CI_REPO}
username:
from_secret: quay_username
when:
- event: [tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
depends_on:
- test

View File

@ -0,0 +1,41 @@
---
when:
- event: [pull_request, tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: build
image: docker.io/techknowlogick/xgo:go-1.21.x
commands:
- ln -s $(pwd) /source
- make release
- name: executable
image: quay.io/thegeeklab/alpine-tools
commands:
- $(find dist/ -executable -type f -iname ${CI_REPO_NAME}-linux-amd64) --help
- name: changelog
image: quay.io/thegeeklab/git-sv
commands:
- git sv current-version
- git sv release-notes -t ${CI_COMMIT_TAG:-next} -o CHANGELOG.md
- cat CHANGELOG.md
- name: publish-github
image: docker.io/plugins/github-release
settings:
api_key:
from_secret: github_token
files:
- dist/*
note: CHANGELOG.md
overwrite: true
title: ${CI_COMMIT_TAG}
when:
- event: [tag]
depends_on:
- test

79
.woodpecker/docs.yml Normal file
View File

@ -0,0 +1,79 @@
---
when:
- event: [pull_request, tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: markdownlint
image: quay.io/thegeeklab/markdownlint-cli
group: test
commands:
- markdownlint 'README.md' 'CONTRIBUTING.md'
- name: spellcheck
image: quay.io/thegeeklab/alpine-tools
group: test
commands:
- spellchecker --files '_docs/**/*.md' 'README.md' 'CONTRIBUTING.md' -d .dictionary -p spell indefinite-article syntax-urls
environment:
FORCE_COLOR: "true"
- name: link-validation
image: docker.io/lycheeverse/lychee
group: test
commands:
- lychee --no-progress --format detailed _docs/content README.md
- name: publish
image: quay.io/thegeeklab/wp-git-action
settings:
action:
- pages
author_email: bot@thegeeklab.de
author_name: thegeeklab-bot
branch: docs
message: "[skip ci] auto-update documentation"
netrc_password:
from_secret: github_token
pages_directory: _docs/
when:
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
- name: pushrm-dockerhub
image: docker.io/chko/docker-pushrm:1
secrets:
- source: docker_password
target: DOCKER_PASS
- source: docker_username
target: DOCKER_USER
environment:
PUSHRM_FILE: README.md
PUSHRM_SHORT: Woodpecker CI plugin to clone git repositories
PUSHRM_TARGET: ${CI_REPO}
when:
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
status: [success]
- name: pushrm-quay
image: docker.io/chko/docker-pushrm:1
secrets:
- source: quay_token
target: APIKEY__QUAY_IO
environment:
PUSHRM_FILE: README.md
PUSHRM_TARGET: quay.io/${CI_REPO}
when:
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
status: [success]
depends_on:
- build-package
- build-container

26
.woodpecker/notify.yml Normal file
View File

@ -0,0 +1,26 @@
---
when:
- event: [tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
runs_on: [success, failure]
steps:
- name: matrix
image: quay.io/thegeeklab/wp-matrix
settings:
homeserver:
from_secret: matrix_homeserver
password:
from_secret: matrix_password
roomid:
from_secret: matrix_roomid
username:
from_secret: matrix_username
when:
- status: [success, failure]
depends_on:
- docs

17
.woodpecker/test.yml Normal file
View File

@ -0,0 +1,17 @@
---
when:
- event: [pull_request, tag]
- event: [push, manual]
branch:
- ${CI_REPO_DEFAULT_BRANCH}
steps:
- name: lint
image: docker.io/library/golang:1.21
commands:
- make lint
- name: test
image: docker.io/library/golang:1.21
commands:
- make test

31
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,31 @@
# Contributing
## Security
If you think you have found a **security issue**, please do not mention it in this repository.
Instead, send an email to `security@thegeeklab.de` with as many details as possible so it can be handled confidential.
## Bug Reports and Feature Requests
If you have found a **bug** or have a **feature request** please use the search first in case a similar issue already exists.
If not, please create an issue in this repository
## Code
If you would like to fix a bug or implement a feature, please fork the repository and create a Pull Request.
Before you start any Pull Request, it is recommended that you create an issue to discuss first if you have any
doubts about requirement or implementation. That way you can be sure that the maintainer(s) agree on what to change and how,
and you can hopefully get a quick merge afterwards.
Pull Requests can only be merged once all status checks are green.
## Do not force push to your Pull Request branch
Please do not force push to your Pull Requests branch after you have created your Pull Request, as doing so makes it harder for us to review your work.
Pull Requests will always be squashed by us when we merge your work. Commit as many times as you need in your Pull Request branch.
## Re-requesting a review
Please do not ping your reviewer(s) by mentioning them in a new comment. Instead, use the re-request review functionality.
Read more about this in the [GitHub docs, Re-requesting a review](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request#re-requesting-a-review).

25
Containerfile.multiarch Normal file
View File

@ -0,0 +1,25 @@
FROM --platform=$BUILDPLATFORM golang:1.21@sha256:672a2286da3ee7a854c3e0a56e0838918d0dbb1c18652992930293312de898a6 as build
ARG TARGETOS
ARG TARGETARCH
ADD . /src
WORKDIR /src
RUN make build
FROM alpine:3.19@sha256:51b67269f354137895d43f3b3d810bfacd3945438e94dc5ac55fdac340352f48
LABEL maintainer="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.authors="Robert Kaussow <mail@thegeeklab.de>"
LABEL org.opencontainers.image.title="wp-git-clone"
LABEL org.opencontainers.image.url="https://github.com/thegeeklab/wp-git-clone"
LABEL org.opencontainers.image.source="https://github.com/thegeeklab/wp-git-clone"
LABEL org.opencontainers.image.documentation="https://github.com/thegeeklab/wp-git-clone"
RUN apk --update add --no-cache git openssh curl git-lfs && \
rm -rf /var/cache/apk/* && \
rm -rf /tmp/*
COPY --from=build /src/dist/wp-git-clone /bin/wp-git-clone
ENTRYPOINT [ "/bin/wp-git-clone" ]

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2022 Robert Kaussow <mail@thegeeklab.de>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

107
Makefile Normal file
View File

@ -0,0 +1,107 @@
# renovate: datasource=github-releases depName=mvdan/gofumpt
GOFUMPT_PACKAGE_VERSION := v0.5.0
# renovate: datasource=github-releases depName=golangci/golangci-lint
GOLANGCI_LINT_PACKAGE_VERSION := v1.55.2
EXECUTABLE := wp-git-clone
DIST := dist
DIST_DIRS := $(DIST)
IMPORT := github.com/thegeeklab/$(EXECUTABLE)
GO ?= go
CWD ?= $(shell pwd)
PACKAGES ?= $(shell go list ./...)
SOURCES ?= $(shell find . -name "*.go" -type f)
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@$(GOFUMPT_PACKAGE_VERSION)
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_PACKAGE_VERSION)
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest
GENERATE ?=
XGO_VERSION := go-1.21.x
XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64
TARGETOS ?= linux
TARGETARCH ?= amd64
ifneq ("$(TARGETVARIANT)","")
GOARM ?= $(subst v,,$(TARGETVARIANT))
endif
TAGS ?= netgo
ifndef VERSION
ifneq ($(CI_COMMIT_TAG),)
VERSION ?= $(subst v,,$(CI_COMMIT_TAG))
else
VERSION ?= $(shell git rev-parse --short HEAD)
endif
endif
ifndef DATE
DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%S%z")
endif
LDFLAGS += -s -w -X "main.BuildVersion=$(VERSION)" -X "main.BuildDate=$(DATE)"
.PHONY: all
all: clean build
.PHONY: clean
clean:
$(GO) clean -i ./...
rm -rf $(DIST_DIRS)
.PHONY: fmt
fmt:
$(GO) run $(GOFUMPT_PACKAGE) -extra -w $(SOURCES)
.PHONY: golangci-lint
golangci-lint:
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: lint
lint: golangci-lint
.PHONY: generate
generate:
$(GO) generate $(GENERATE)
.PHONY: generate-docs
generate-docs:
$(GO) generate ./cmd/$(EXECUTABLE)/flags.go
.PHONY: test
test:
$(GO) run $(GOTESTSUM_PACKAGE) --no-color=false -- -coverprofile=coverage.out $(PACKAGES)
.PHONY: build
build: $(DIST)/$(EXECUTABLE)
$(DIST)/$(EXECUTABLE): $(SOURCES)
GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) GOARM=$(GOARM) $(GO) build -v -tags '$(TAGS)' -ldflags '-extldflags "-static" $(LDFLAGS)' -o $@ ./cmd/$(EXECUTABLE)
$(DIST_DIRS):
mkdir -p $(DIST_DIRS)
.PHONY: xgo
xgo: | $(DIST_DIRS)
$(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -v -ldflags '-extldflags "-static" $(LDFLAGS)' -tags '$(TAGS)' -targets '$(XGO_TARGETS)' -out $(EXECUTABLE) --pkg cmd/$(EXECUTABLE) .
cp /build/* $(CWD)/$(DIST)
ls -l $(CWD)/$(DIST)
.PHONY: checksum
checksum:
cd $(DIST); $(foreach file,$(wildcard $(DIST)/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
ls -l $(CWD)/$(DIST)
.PHONY: release
release: xgo checksum
.PHONY: deps
deps:
$(GO) mod download
$(GO) install $(GOFUMPT_PACKAGE)
$(GO) install $(GOLANGCI_LINT_PACKAGE)
$(GO) install $(XGO_PACKAGE)
$(GO) install $(GOTESTSUM_PACKAGE)

21
README.md Normal file
View File

@ -0,0 +1,21 @@
# wp-git-clone
Woodpecker CI plugin to clone git repositories
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/wp-git-clone/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/wp-git-clone)
[![Docker Hub](https://img.shields.io/badge/dockerhub-latest-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/thegeeklab/wp-git-clone)
[![Quay.io](https://img.shields.io/badge/quay-latest-blue.svg?logo=docker&logoColor=white)](https://quay.io/repository/thegeeklab/wp-git-clone)
[![Go Report Card](https://goreportcard.com/badge/github.com/thegeeklab/wp-git-clone)](https://goreportcard.com/report/github.com/thegeeklab/wp-git-clone)
[![GitHub contributors](https://img.shields.io/github/contributors/thegeeklab/wp-git-clone)](https://github.com/thegeeklab/wp-git-clone/graphs/contributors)
[![Source: GitHub](https://img.shields.io/badge/source-github-blue.svg?logo=github&logoColor=white)](https://github.com/thegeeklab/wp-git-clone)
[![License: Apache-2.0](https://img.shields.io/github/license/thegeeklab/wp-git-clone)](https://github.com/thegeeklab/wp-git-clone/blob/main/LICENSE)
Woodpecker CI plugin to clone git repositories. You can find the full documentation at [https://woodpecker-plugins.geekdocs.de](https://woodpecker-plugins.geekdocs.de/plugins/wp-git-clone).
## Contributors
Special thanks to all [contributors](https://github.com/thegeeklab/wp-git-clone/graphs/contributors). If you would like to contribute, please see the [instructions](https://github.com/thegeeklab/wp-git-clone/blob/main/CONTRIBUTING.md).
## License
This project is licensed under the Apache-2.0 License - see the [LICENSE](https://github.com/thegeeklab/wp-git-clone/blob/main/LICENSE) file for details.

57
cmd/wp-git-clone/docs.go Normal file
View File

@ -0,0 +1,57 @@
//go:build generate
// +build generate
package main
import (
"bytes"
"embed"
"fmt"
"os"
"text/template"
"github.com/thegeeklab/wp-git-clone/plugin"
"github.com/thegeeklab/wp-plugin-go/docs"
wp "github.com/thegeeklab/wp-plugin-go/plugin"
"github.com/urfave/cli/v2"
)
//go:embed templates/docs-data.yaml.tmpl
var yamlTemplate embed.FS
func main() {
settings := &plugin.Settings{}
app := &cli.App{
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
}
out, err := toYAML(app)
if err != nil {
panic(err)
}
fi, err := os.Create("../../docs/data/data-raw.yaml")
if err != nil {
panic(err)
}
defer fi.Close()
if _, err := fi.WriteString(out); err != nil {
panic(err)
}
}
func toYAML(app *cli.App) (string, error) {
var w bytes.Buffer
yamlTmpl, err := template.New("docs").ParseFS(yamlTemplate, "templates/docs-data.yaml.tmpl")
if err != nil {
fmt.Println(yamlTmpl)
return "", err
}
if err := yamlTmpl.ExecuteTemplate(&w, "docs-data.yaml.tmpl", docs.GetTemplateData(app)); err != nil {
return "", err
}
return w.String(), nil
}

183
cmd/wp-git-clone/flags.go Normal file
View File

@ -0,0 +1,183 @@
// Copyright (c) 2020, the Drone Plugins project authors.
// Copyright (c) 2021, Robert Kaussow <mail@thegeeklab.de>
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file.
package main
import (
"github.com/thegeeklab/wp-git-clone/plugin"
"github.com/thegeeklab/wp-plugin-go/types"
"github.com/urfave/cli/v2"
)
// settingsFlags has the cli.Flags for the plugin.Settings.
//
//go:generate go run docs.go flags.go
func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "remote",
Usage: "git remote url",
EnvVars: []string{"PLUGIN_REMOTE", "CI_REPO_CLONE_URL"},
Destination: &settings.Repo.RemoteURL,
Category: category,
},
&cli.StringFlag{
Name: "remote-ssh",
Usage: "git clone ssh url",
EnvVars: []string{"PLUGIN_REMOTE_SSH", "CI_REPO_CLONE_SSH_URL"},
Destination: &settings.Repo.RemoteURL,
Category: category,
},
&cli.StringFlag{
Name: "workdir",
Usage: "path to clone git repository",
EnvVars: []string{"PLUGIN_WORKDIR", "CI_WORKSPACE"},
Destination: &settings.WorkDir,
Category: category,
},
&cli.StringFlag{
Name: "sha",
Usage: "git commit sha",
EnvVars: []string{"PLUGIN_COMMIT_SHA", "CI_COMMIT_SHA"},
Destination: &settings.Repo.CommitSha,
Category: category,
},
&cli.StringFlag{
Name: "ref",
Value: "refs/heads/main",
Usage: "git commit ref",
EnvVars: []string{"PLUGIN_COMMIT_REF", "CI_COMMIT_REF"},
Destination: &settings.Repo.CommitRef,
Category: category,
},
&cli.StringFlag{
Name: "event",
Value: "push",
Usage: "pipeline event",
EnvVars: []string{"CI_PIPELINE_EVENT"},
Destination: &settings.Pipeline.Event,
Category: category,
},
&cli.StringFlag{
Name: "netrc.machine",
Usage: "netrc machine",
EnvVars: []string{"CI_NETRC_MACHINE"},
Destination: &settings.Netrc.Machine,
Category: category,
},
&cli.StringFlag{
Name: "netrc.username",
Usage: "netrc username",
EnvVars: []string{"CI_NETRC_USERNAME"},
Destination: &settings.Netrc.Password,
Category: category,
},
&cli.StringFlag{
Name: "netrc.password",
Usage: "netrc password",
EnvVars: []string{"CI_NETRC_PASSWORD"},
Destination: &settings.Netrc.Password,
Category: category,
},
&cli.IntFlag{
Name: "depth",
Usage: "clone depth",
EnvVars: []string{"PLUGIN_DEPTH"},
Destination: &settings.Depth,
Category: category,
},
&cli.BoolFlag{
Name: "recursive",
Usage: "clone submodules",
EnvVars: []string{"PLUGIN_RECURSIVE"},
Value: true,
Destination: &settings.Recursive,
Category: category,
},
&cli.BoolFlag{
Name: "tags",
Usage: "clone tags, if not explicitly set and event is tag its default is true else false",
EnvVars: []string{"PLUGIN_TAGS"},
Destination: &settings.Tags,
Category: category,
},
&cli.BoolFlag{
Name: "insecure-ssl-verify",
Usage: "set SSL verification of the remote machine",
EnvVars: []string{"PLUGIN_INSECURE_SSL_VERIFY"},
Destination: &settings.Repo.InsecureSSLVerify,
Value: false,
Category: category,
},
&cli.BoolFlag{
Name: "submodule-update-remote",
Usage: "update remote submodules",
EnvVars: []string{"PLUGIN_SUBMODULES_UPDATE_REMOTE", "PLUGIN_SUBMODULE_UPDATE_REMOTE"},
Destination: &settings.Repo.SubmoduleRemote,
Category: category,
},
&cli.GenericFlag{
Name: "submodule-override",
Usage: "json map of submodule overrides",
EnvVars: []string{"PLUGIN_SUBMODULE_OVERRIDE"},
Value: &types.MapFlag{},
Category: category,
},
&cli.BoolFlag{
Name: "submodule-partial",
Usage: "update submodules via partial clone",
EnvVars: []string{"PLUGIN_SUBMODULES_PARTIAL", "PLUGIN_SUBMODULE_PARTIAL"},
Value: true,
Destination: &settings.Repo.SubmodulePartial,
Category: category,
},
&cli.BoolFlag{
Name: "lfs",
Usage: "whether to retrieve LFS content if available",
EnvVars: []string{"PLUGIN_LFS"},
Value: true,
Destination: &settings.Lfs,
Category: category,
},
&cli.StringFlag{
Name: "branch",
Usage: "change branch name",
EnvVars: []string{"PLUGIN_BRANCH", "CI_COMMIT_BRANCH", "CI_REPO_DEFAULT_BRANCH"},
Destination: &settings.Repo.Branch,
Category: category,
},
&cli.BoolFlag{
Name: "partial",
Usage: "enable/disable partial clone",
EnvVars: []string{"PLUGIN_PARTIAL"},
Value: false,
Destination: &settings.Partial,
Category: category,
},
&cli.StringFlag{
Name: "safe-directory",
Usage: "define/replace safe directories",
EnvVars: []string{"PLUGIN_SAFE_DIRECTORY", "CI_WORKSPACE"},
Destination: &settings.Repo.SafeDirectory,
Category: category,
},
&cli.BoolFlag{
Name: "use-ssh",
Usage: "using ssh for git clone",
EnvVars: []string{"PLUGIN_USE_SSH"},
Value: false,
Destination: &settings.UseSSH,
Category: category,
},
&cli.StringFlag{
Name: "ssh-key",
Usage: "ssh key for ssh clone",
EnvVars: []string{"PLUGIN_SSH_KEY"},
Destination: &settings.SSHKey,
Category: category,
},
}
}

34
cmd/wp-git-clone/main.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright (c) 2020, the Drone Plugins project authors.
// Copyright (c) 2021, Robert Kaussow <mail@thegeeklab.de>
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file.
package main
import (
"fmt"
"github.com/thegeeklab/wp-git-clone/plugin"
wp "github.com/thegeeklab/wp-plugin-go/plugin"
)
//nolint:gochecknoglobals
var (
BuildVersion = "devel"
BuildDate = "00000000"
)
func main() {
settings := &plugin.Settings{}
options := wp.Options{
Name: "wp-git-clone",
Description: "Clone git repository",
Version: BuildVersion,
VersionMetadata: fmt.Sprintf("date=%s", BuildDate),
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
}
plugin.New(options, settings).Run()
}

View File

@ -0,0 +1,14 @@
---
{{- if .GlobalArgs }}
properties:
{{- range $v := .GlobalArgs }}
- name: {{ $v.Name }}
{{- with $v.Description }}
description: |
{{ . }}
{{- end }}
{{- with $v.Default }}
defaultvalue: {{ . }}
{{- end }}
{{ end -}}
{{ end -}}

66
docs/content/_index.md Normal file
View File

@ -0,0 +1,66 @@
---
title: wp-git-clone
---
[![Build Status](https://ci.thegeeklab.de/api/badges/thegeeklab/wp-git-clone/status.svg)](https://ci.thegeeklab.de/repos/thegeeklab/wp-git-clone)
[![Docker Hub](https://img.shields.io/badge/dockerhub-latest-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/thegeeklab/wp-git-clone)
[![Quay.io](https://img.shields.io/badge/quay-latest-blue.svg?logo=docker&logoColor=white)](https://quay.io/repository/thegeeklab/wp-git-clone)
[![Go Report Card](https://goreportcard.com/badge/github.com/thegeeklab/wp-git-clone)](https://goreportcard.com/report/github.com/thegeeklab/wp-git-clone)
[![GitHub contributors](https://img.shields.io/github/contributors/thegeeklab/wp-git-clone)](https://github.com/thegeeklab/wp-git-clone/graphs/contributors)
[![Source: GitHub](https://img.shields.io/badge/source-github-blue.svg?logo=github&logoColor=white)](https://github.com/thegeeklab/wp-git-clone)
[![License: Apache-2.0](https://img.shields.io/github/license/thegeeklab/wp-git-clone)](https://github.com/thegeeklab/wp-git-clone/blob/main/LICENSE)
Woodpecker CI plugin to clone git repositories.
<!-- prettier-ignore-start -->
<!-- spellchecker-disable -->
{{< toc >}}
<!-- spellchecker-enable -->
<!-- prettier-ignore-end -->
## Usage
```YAML
clone:
git:
image: quay.io/thegeeklab/wp-git-clone
settings:
depth: 50
lfs: false
```
### Parameters
<!-- prettier-ignore-start -->
<!-- spellchecker-disable -->
{{< propertylist name=wp-git-clone.data sort=name >}}
<!-- spellchecker-enable -->
<!-- prettier-ignore-end -->
## Build
Build the binary with the following command:
```Shell
make build
```
Build the container image with the following command:
```Shell
docker build --file Containerfile.multiarch --tag thegeeklab/wp-git-clone .
```
## Test
```Shell
docker run --rm \
-e CI_REPO_CLONE_URL=https://github.com/octocat/Hello-World.git \
-e CI_PIPELINE_EVENT=push \
-e CI_COMMIT_SHA=553c2077f0edc3d5dc5d17262f6aa498e69d6f8e \
-e CI_COMMIT_REF=refs/heads/master \
-e CI_WORKSPACE=/tmp/wp_git_testrepo \
-v $(pwd):/build:z \
-w /build \
quay.io/thegeeklab/wp-git-clone
```

100
docs/data/data.yaml Normal file
View File

@ -0,0 +1,100 @@
---
properties:
- name: branch
description: |
Change branch name.
- name: ci_netrc_machine
description: |
Netrc machine.
- name: ci_netrc_password
description: |
Netrc password.
- name: ci_netrc_username
description: |
Metrc username.
- name: ci_pipeline_event
description: |
Pipeline event.
defaultvalue: "push"
- name: commit_ref
description: |
Git commit ref.
defaultvalue: "refs/heads/main"
- name: commit_sha
description: |
Git commit sha.
- name: depth
description: |
Clone depth.
defaultvalue: 0
- name: insecure_ssl_verify
description: |
Set SSL verification of the remote machine.
defaultvalue: false
- name: lfs
description: |
Whether to retrieve LFS content if available.
defaultvalue: true
- name: partial
description: |
Enable/disable partial clone.
defaultvalue: false
- name: recursive
description: |
Clone submodules.
defaultvalue: true
- name: remote
description: |
Git remote HTTP clone url.
- name: remote_ssh
description: |
Git remote SSH clone url.
- name: safe_directory
description: |
Define/replace safe directories.
- name: ssh_key
description: |
SSH key for ssh clone.
- name: submodule_override
description: |
JSON map of submodule overrides.
- name: submodules_partial
description: |
Update submodules via partial clone (`depth=1`).
defaultvalue: true
- name: submodules_update_remote
description: |
Update remote submodules.
defaultvalue: false
- name: tags
description: |
Clone tags, if not explicitly set and event is tag its default is `true` else `false`.
defaultvalue: false
- name: use_ssh
description: |
Using ssh for git clone.
defaultvalue: false
- name: workdir
description: |
Path to clone git repository.

92
git/clone.go Normal file
View File

@ -0,0 +1,92 @@
package git
import (
"fmt"
"golang.org/x/sys/execabs"
)
// FetchSource fetches the source from remote.
func FetchSource(ref string, tags bool, depth int, filter string) *execabs.Cmd {
tagsOption := "--no-tags"
if tags {
tagsOption = "--tags"
}
args := []string{
"fetch",
tagsOption,
}
if depth != 0 {
args = append(args, fmt.Sprintf("--depth=%d", depth))
}
if filter != "" {
args = append(args, "--filter="+filter)
}
args = append(args, "origin")
args = append(args, fmt.Sprintf("+%s:", ref))
return execabs.Command(
gitBin,
args...,
)
}
// FetchLFS fetches lfs.
func FetchLFS() *execabs.Cmd {
args := []string{
"lfs",
"fetch",
}
return execabs.Command(
gitBin,
args...,
)
}
// CheckoutHead handles head checkout.
func CheckoutHead() *execabs.Cmd {
args := []string{
"checkout",
"-qf",
"FETCH_HEAD",
}
return execabs.Command(
gitBin,
args...,
)
}
// CheckoutSha handles commit checkout.
func CheckoutSha(repo Repository) *execabs.Cmd {
args := []string{
"reset",
"--hard",
"-q",
repo.CommitSha,
}
return execabs.Command(
gitBin,
args...,
)
}
// CheckoutLFS handles commit checkout.
func CheckoutLFS() *execabs.Cmd {
args := []string{
"lfs",
"checkout",
}
return execabs.Command(
gitBin,
args...,
)
}

66
git/clone_test.go Normal file
View File

@ -0,0 +1,66 @@
package git
import (
"testing"
)
// TestFetch tests if the arguments to `git fetch` are constructed properly.
func TestFetch(t *testing.T) {
testdata := []struct {
ref string
tags bool
depth int
exp []string
}{
{
"refs/heads/master",
false,
0,
[]string{
"/usr/bin/git",
"fetch",
"--no-tags",
"origin",
"+refs/heads/master:",
},
},
{
"refs/heads/master",
false,
50,
[]string{
"/usr/bin/git",
"fetch",
"--no-tags",
"--depth=50",
"origin",
"+refs/heads/master:",
},
},
{
"refs/heads/master",
true,
100,
[]string{
"/usr/bin/git",
"fetch",
"--tags",
"--depth=100",
"origin",
"+refs/heads/master:",
},
},
}
for _, td := range testdata {
c := FetchSource(td.ref, td.tags, td.depth, "")
if len(c.Args) != len(td.exp) {
t.Errorf("Expected: %s, got %s", td.exp, c.Args)
}
for i := range c.Args {
if c.Args[i] != td.exp[i] {
t.Errorf("Expected: %s, got %s", td.exp, c.Args)
}
}
}
}

70
git/config.go Normal file
View File

@ -0,0 +1,70 @@
package git
import (
"fmt"
"strconv"
"golang.org/x/sys/execabs"
)
// ConfigSSLVerify disables globally the git ssl verification.
func ConfigSSLVerify(repo Repository) *execabs.Cmd {
args := []string{
"config",
"--local",
"http.sslVerify",
strconv.FormatBool(repo.InsecureSSLVerify),
}
return execabs.Command(
gitBin,
args...,
)
}
// ConfigSafeDirectory disables globally the git ssl verification.
func ConfigSafeDirectory(repo Repository) *execabs.Cmd {
args := []string{
"config",
"--local",
"--replace-all",
"safe.directory",
repo.SafeDirectory,
}
return execabs.Command(
gitBin,
args...,
)
}
// ConfigRemapSubmodule returns a git command that, when executed configures git to
// remap submodule urls.
func ConfigRemapSubmodule(name, url string) *execabs.Cmd {
args := []string{
"config",
"--local",
fmt.Sprintf("submodule.%s.url", name),
url,
}
return execabs.Command(
gitBin,
args...,
)
}
// ConfigSSHCommand sets custom SSH key.
func ConfigSSHCommand(sshKey string) *execabs.Cmd {
args := []string{
"config",
"--local",
"core.sshCommand",
"ssh -i " + sshKey,
}
return execabs.Command(
gitBin,
args...,
)
}

27
git/init.go Normal file
View File

@ -0,0 +1,27 @@
package git
import (
"golang.org/x/sys/execabs"
)
// RemoteRemove drops the defined remote from a git repo.
func Init(repo Repository) *execabs.Cmd {
args := []string{
"init",
}
if repo.Branch != "" {
args = []string{
"init",
"-b",
repo.Branch,
}
}
cmd := execabs.Command(
gitBin,
args...,
)
return cmd
}

20
git/remote.go Normal file
View File

@ -0,0 +1,20 @@
package git
import (
"golang.org/x/sys/execabs"
)
// RemoteAdd adds an additional remote to a git repo.
func RemoteAdd(url string) *execabs.Cmd {
args := []string{
"remote",
"add",
"origin",
url,
}
return execabs.Command(
gitBin,
args...,
)
}

30
git/submodule.go Normal file
View File

@ -0,0 +1,30 @@
package git
import (
"golang.org/x/sys/execabs"
)
// SubmoduleUpdate recursively initializes and updates submodules.
func SubmoduleUpdate(repo Repository) *execabs.Cmd {
args := []string{
"submodule",
"update",
"--init",
"--recursive",
}
if repo.SubmodulePartial {
args = append(args, "--depth=1", "--recommend-shallow")
}
cmd := execabs.Command(
gitBin,
args...,
)
if repo.SubmoduleRemote {
cmd.Args = append(cmd.Args, "--remote")
}
return cmd
}

100
git/submodule_test.go Normal file
View File

@ -0,0 +1,100 @@
package git
import (
"testing"
)
// TestUpdateSubmodules tests if the arguments to `git submodule update`
// are constructed properly.
func TestUpdateSubmodules(t *testing.T) {
tests := []struct {
partial bool
exp []string
}{
{
false,
[]string{
"/usr/bin/git",
"submodule",
"update",
"--init",
"--recursive",
},
},
{
true,
[]string{
"/usr/bin/git",
"submodule",
"update",
"--init",
"--recursive",
"--depth=1",
"--recommend-shallow",
},
},
}
for _, tt := range tests {
repo := Repository{
SubmoduleRemote: false,
SubmodulePartial: tt.partial,
}
c := SubmoduleUpdate(repo)
if len(c.Args) != len(tt.exp) {
t.Errorf("Expected: %s, got %s", tt.exp, c.Args)
}
for i := range c.Args {
if c.Args[i] != tt.exp[i] {
t.Errorf("Expected: %s, got %s", tt.exp, c.Args)
}
}
}
}
// TestUpdateSubmodules tests if the arguments to `git submodule update`
// are constructed properly.
func TestUpdateSubmodulesRemote(t *testing.T) {
tests := []struct {
exp []string
}{
{
[]string{
"/usr/bin/git",
"submodule",
"update",
"--init",
"--recursive",
"--remote",
},
},
{
[]string{
"/usr/bin/git",
"submodule",
"update",
"--init",
"--recursive",
"--remote",
},
},
}
for _, tt := range tests {
repo := Repository{
SubmoduleRemote: true,
SubmodulePartial: false,
}
c := SubmoduleUpdate(repo)
if len(c.Args) != len(tt.exp) {
t.Errorf("Expected: %s, got %s", tt.exp, c.Args)
}
for i := range c.Args {
if c.Args[i] != tt.exp[i] {
t.Errorf("Expected: %s, got %s", tt.exp, c.Args)
}
}
}
}

18
git/type.go Normal file
View File

@ -0,0 +1,18 @@
package git
const gitBin = "/usr/bin/git"
type Repository struct {
RemoteURL string
RemoteSSH string
Branch string
CommitSha string
CommitRef string
Submodules map[string]string
SubmoduleRemote bool
SubmodulePartial bool
InsecureSSLVerify bool
SafeDirectory string
InitExists bool
}

47
git/utils.go Normal file
View File

@ -0,0 +1,47 @@
package git
import (
"fmt"
"os"
"os/user"
"path/filepath"
)
const (
netrcFile = `
machine %s
login %s
password %s
`
)
const (
strictFilePerm = 0o600
)
// WriteNetrc writes the netrc file.
func WriteNetrc(machine, login, password string) error {
netrcContent := fmt.Sprintf(
netrcFile,
machine,
login,
password,
)
home := "/root"
if currentUser, err := user.Current(); err == nil {
home = currentUser.HomeDir
}
netpath := filepath.Join(
home,
".netrc",
)
return os.WriteFile(
netpath,
[]byte(netrcContent),
strictFilePerm,
)
}

32
go.mod Normal file
View File

@ -0,0 +1,32 @@
module github.com/thegeeklab/wp-git-clone
go 1.21
require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/rs/zerolog v1.31.0
github.com/thegeeklab/wp-plugin-go v1.3.0
github.com/urfave/cli/v2 v2.26.0
golang.org/x/sys v0.15.0
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/google/uuid v1.1.1 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/net v0.19.0 // indirect
)

99
go.sum Normal file
View File

@ -0,0 +1,99 @@
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/thegeeklab/wp-plugin-go v1.3.0 h1:Yhz0dTDwXcbeaoyoPOXNDf2RzSoUixHuE+ML27cYy2E=
github.com/thegeeklab/wp-plugin-go v1.3.0/go.mod h1:EUOH6XJ8G/bcd6gmM6a6vLeta+keyX9ul1Es0F0r2Lc=
github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI=
github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

207
plugin/impl.go Normal file
View File

@ -0,0 +1,207 @@
// Copyright (c) 2023, Robert Kaussow <mail@thegeeklab.de>
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file.
package plugin
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/rs/zerolog/log"
"github.com/thegeeklab/wp-git-clone/git"
"github.com/thegeeklab/wp-plugin-go/types"
"golang.org/x/sys/execabs"
)
const (
daemonBackoffMaxRetries = 3
daemonBackoffInitialInterval = 2 * time.Second
daemonBackoffMultiplier = 3.5
)
var (
ErrGitCloneDestintionNotValid = errors.New("destination not valid")
ErrTypeAssertionFailed = errors.New("type assertion failed")
)
//nolint:revive
func (p *Plugin) run(ctx context.Context) error {
if err := p.FlagsFromContext(); err != nil {
return fmt.Errorf("validation failed: %w", err)
}
if err := p.Validate(); err != nil {
return fmt.Errorf("validation failed: %w", err)
}
if err := p.Execute(); err != nil {
return fmt.Errorf("execution failed: %w", err)
}
return nil
}
// Validate handles the settings validation of the plugin.
func (p *Plugin) Validate() error {
if p.Settings.WorkDir == "" {
var err error
if p.Settings.WorkDir, err = os.Getwd(); err != nil {
return err
}
}
if p.Settings.Pipeline.Event == "tag" && !p.Settings.Tags {
// tags clone not explicit set but pipeline is triggered by a tag
// auto set tags cloning to true
p.Settings.Tags = true
}
if p.Settings.Tags && p.Settings.Partial {
log.Warn().Msg("ignore partial clone as tags are fetched")
// if tag fetching is enabled per event or setting, disable partial clone
p.Settings.Partial = false
}
if p.Settings.Partial {
p.Settings.Depth = 1
p.Settings.Filter = "tree:0"
}
return nil
}
// Execute provides the implementation of the plugin.
func (p *Plugin) Execute() error {
cmds := make([]*execabs.Cmd, 0)
if err := os.Setenv("GIT_TERMINAL_PROMPT", "0"); err != nil {
return err
}
// prevents git-lfs from retrieving any LFS files
if err := os.Setenv("GIT_LFS_SKIP_SMUDGE", "1"); err != nil {
return err
}
// Handle init
initPath := filepath.Join(p.Settings.WorkDir, ".git")
if err := os.MkdirAll(p.Settings.WorkDir, os.ModePerm); err != nil {
return err
}
//nolint:nestif
if _, err := os.Stat(initPath); os.IsNotExist(err) {
if err := p.execCmd(git.Init(p.Settings.Repo), new(bytes.Buffer)); err != nil {
return err
}
if p.Settings.UseSSH {
cmds = append(cmds, git.RemoteAdd(p.Settings.Repo.RemoteSSH))
if p.Settings.SSHKey != "" {
cmds = append(cmds, git.ConfigSSHCommand(p.Settings.SSHKey))
}
} else {
cmds = append(cmds, git.RemoteAdd(p.Settings.Repo.RemoteURL))
}
}
cmds = append(cmds, git.ConfigSSLVerify(p.Settings.Repo))
if err := git.WriteNetrc(p.Settings.Netrc.Machine, p.Settings.Netrc.Login, p.Settings.Netrc.Password); err != nil {
return err
}
// Handle clone
if p.Settings.Repo.CommitSha == "" {
// fetch and checkout by ref
log.Info().Msg("no commit information: using head checkout")
cmds = append(cmds, git.FetchSource(p.Settings.Repo.CommitRef, p.Settings.Tags, p.Settings.Depth, p.Settings.Filter))
cmds = append(cmds, git.CheckoutHead())
} else {
cmds = append(cmds, git.FetchSource(p.Settings.Repo.CommitSha, p.Settings.Tags, p.Settings.Depth, p.Settings.Filter))
cmds = append(cmds, git.CheckoutSha(p.Settings.Repo))
}
for name, submoduleURL := range p.Settings.Repo.Submodules {
cmds = append(cmds, git.ConfigRemapSubmodule(name, submoduleURL))
}
if p.Settings.Recursive {
cmds = append(cmds, git.SubmoduleUpdate(p.Settings.Repo))
}
if p.Settings.Lfs {
cmds = append(cmds, git.FetchLFS())
cmds = append(cmds, git.CheckoutLFS())
}
for _, cmd := range cmds {
log.Debug().Msgf("+ %s", strings.Join(cmd.Args, " "))
buf := new(bytes.Buffer)
err := p.execCmd(cmd, buf)
switch {
case err != nil && shouldRetry(buf.String()):
backoffOps := func() error {
// copy the original command
//nolint:gosec
retry := execabs.Command(cmd.Args[0], cmd.Args[1:]...)
retry.Dir = cmd.Dir
retry.Env = cmd.Env
retry.Stdout = os.Stdout
retry.Stderr = os.Stderr
trace(cmd)
return cmd.Run()
}
backoffLog := func(err error, delay time.Duration) {
log.Error().Msgf("failed to find remote ref: %v: retry in %s", err, delay.Truncate(time.Second))
}
if err := backoff.RetryNotify(backoffOps, newBackoff(daemonBackoffMaxRetries), backoffLog); err != nil {
return err
}
case err != nil:
return err
}
}
return nil
}
func (p *Plugin) FlagsFromContext() error {
submodules, ok := p.Context.Generic("submodule-override").(*types.MapFlag)
if !ok {
return fmt.Errorf("%w: failed to read submodule-override input", ErrTypeAssertionFailed)
}
p.Settings.Repo.Submodules = submodules.Get()
return nil
}
func (p *Plugin) execCmd(cmd *execabs.Cmd, buf *bytes.Buffer) error {
cmd.Env = os.Environ()
cmd.Stdout = io.MultiWriter(os.Stdout, buf)
cmd.Stderr = io.MultiWriter(os.Stderr, buf)
cmd.Dir = p.Settings.WorkDir
fmt.Println(cmd.Dir)
return cmd.Run()
}

243
plugin/impl_test.go Normal file
View File

@ -0,0 +1,243 @@
package plugin
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/thegeeklab/wp-git-clone/git"
)
type testCommit struct {
name string
path string
clone string
event string
commit string
ref string
file string
data string
dataSize int64
recursive bool
lfs bool
}
// TestClone tests the ability to clone a specific commit into
// a fresh, empty directory every time.
func TestClone(t *testing.T) {
for _, tt := range getCommits() {
dir := setup()
defer teardown(dir)
plugin := Plugin{
Settings: &Settings{
Repo: git.Repository{
RemoteURL: tt.clone,
CommitRef: tt.ref,
CommitSha: tt.commit,
},
Pipeline: Pipeline{
Event: tt.event,
},
WorkDir: filepath.Join(dir, tt.path),
Recursive: tt.recursive,
Lfs: tt.lfs,
},
}
if err := plugin.Execute(); err != nil {
t.Errorf("Expected successful clone. Got error. %s.", err)
}
if tt.data != "" {
data := readFile(plugin.Settings.WorkDir, tt.file)
if data != tt.data {
t.Errorf("Expected %s to contain [%s]. Got [%s].", tt.file, tt.data, data)
}
}
if tt.dataSize != 0 {
size := getFileSize(plugin.Settings.WorkDir, tt.file)
if size != tt.dataSize {
t.Errorf("Expected %s size to be [%d]. Got [%d].", tt.file, tt.dataSize, size)
}
}
}
}
// TestCloneNonEmpty tests the ability to clone a specific commit into
// a non-empty directory. This is useful if the git workspace is cached
// and re-stored for every workflow.
func TestCloneNonEmpty(t *testing.T) {
dir := setup()
defer teardown(dir)
for _, tt := range getCommits() {
plugin := Plugin{
Settings: &Settings{
Repo: git.Repository{
RemoteURL: tt.clone,
CommitRef: tt.ref,
CommitSha: tt.commit,
},
Pipeline: Pipeline{
Event: tt.event,
},
WorkDir: filepath.Join(dir, tt.path),
Recursive: tt.recursive,
Lfs: tt.lfs,
},
}
fmt.Println(plugin.Settings.Repo.CommitSha, tt.commit, fmt.Sprintf("%q", tt.data))
if err := plugin.Execute(); err != nil {
t.Errorf("Expected successful clone. Got error. %s.", err)
}
if tt.data != "" {
data := readFile(plugin.Settings.WorkDir, tt.file)
if data != tt.data {
t.Errorf("Expected %s to contain [%q]. Got [%q].", tt.file, tt.data, data)
break
}
}
if tt.dataSize != 0 {
size := getFileSize(plugin.Settings.WorkDir, tt.file)
if size != tt.dataSize {
t.Errorf("Expected %s size to be [%d]. Got [%d].", tt.file, tt.dataSize, size)
}
}
}
}
// helper function that will setup a temporary workspace
// to which we can clone the repositroy.
func setup() string {
dir, _ := os.MkdirTemp("/tmp", "plugin_git_test_")
_ = os.Mkdir(dir, os.ModePerm)
return dir
}
// helper function to delete the temporary workspace.
func teardown(dir string) {
os.RemoveAll(dir)
}
// helper function to read a file in the temporary worskapce.
func readFile(dir, file string) string {
filename := filepath.Join(dir, file)
fmt.Println(filename)
data, _ := os.ReadFile(filename)
return string(data)
}
func getFileSize(dir, file string) int64 {
filename := filepath.Join(dir, file)
fi, _ := os.Stat(filename)
return fi.Size()
}
func getCommits() []testCommit {
return []testCommit{
{
name: "first commit",
path: "octocat/Hello-World",
clone: "https://github.com/octocat/Hello-World.git",
event: "push",
commit: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e",
ref: "refs/heads/master",
file: "README",
data: "Hello World!",
},
{
name: "head commit",
path: "octocat/Hello-World",
clone: "https://github.com/octocat/Hello-World.git",
event: "push",
commit: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
ref: "refs/heads/master",
file: "README",
data: "Hello World!\n",
},
{
name: "pull request commit",
path: "octocat/Hello-World",
clone: "https://github.com/octocat/Hello-World.git",
event: "pull_request",
commit: "762941318ee16e59dabbacb1b4049eec22f0d303",
ref: "refs/pull/6/merge",
file: "README",
data: "Hello World!\n",
},
{
name: "branch",
path: "octocat/Hello-World",
clone: "https://github.com/octocat/Hello-World.git",
event: "push",
commit: "b3cbd5bbd7e81436d2eee04537ea2b4c0cad4cdf",
ref: "refs/heads/test",
file: "CONTRIBUTING.md",
data: "## Contributing\n",
},
{
name: "tags",
path: "github/mime-types",
clone: "https://github.com/github/mime-types.git",
event: "tag",
commit: "bf68d60215a167c935bc5976b7d06a7ffb290926",
ref: "refs/tags/v1.17",
file: ".gitignore",
data: "*.swp\n*~\n.rake_tasks~\nhtml\ndoc\npkg\npublish\ncoverage\n",
},
{
name: "submodules",
path: "test-assets/woodpecker-git-test-submodule",
clone: "https://github.com/test-assets/woodpecker-git-test-submodule.git",
event: "push",
commit: "cc020eb6aaa601c13ca7b0d5db9d1ca694e7a003",
ref: "refs/heads/main",
file: "Hello-World/README",
data: "Hello World!\n",
recursive: true,
},
{
name: "checkout with ref only",
path: "octocat/Hello-World",
clone: "https://github.com/octocat/Hello-World.git",
event: "push",
// commit: "a11fb45a696bf1d696fc9ab2c733f8f123aa4cf5",
ref: "pull/2403/head",
file: "README",
data: "Hello World!\n\nsomething is changed!\n",
},
// test lfs, please do not change order, otherwise TestCloneNonEmpty will fail ###
{
name: "checkout with lfs skip",
path: "test-assets/woodpecker-git-test-lfs",
clone: "https://github.com/test-assets/woodpecker-git-test-lfs.git",
event: "push",
commit: "69d4dadb4c2899efb73c0095bb58a6454d133cef",
ref: "refs/heads/main",
file: "4M.bin",
dataSize: 132,
},
{
name: "checkout with lfs",
path: "test-assets/woodpecker-git-test-lfs",
clone: "https://github.com/test-assets/woodpecker-git-test-lfs.git",
event: "push",
commit: "69d4dadb4c2899efb73c0095bb58a6454d133cef",
ref: "refs/heads/main",
file: "4M.bin",
dataSize: 4194304,
lfs: true,
},
}
}

56
plugin/plugin.go Normal file
View File

@ -0,0 +1,56 @@
// Copyright (c) 2023, Robert Kaussow <mail@thegeeklab.de>
// Use of this source code is governed by an Apache 2.0 license that can be
// found in the LICENSE file.
package plugin
import (
"github.com/thegeeklab/wp-git-clone/git"
wp "github.com/thegeeklab/wp-plugin-go/plugin"
)
// Plugin implements provide the plugin.
type Plugin struct {
*wp.Plugin
Settings *Settings
}
type Pipeline struct {
Event string
Number int
}
type Netrc struct {
Machine string
Login string
Password string
}
// Settings for the plugin.
type Settings struct {
Depth int
Recursive bool
Tags bool
Lfs bool
Partial bool
Filter string
UseSSH bool
SSHKey string
WorkDir string
Pipeline Pipeline
Netrc Netrc
Repo git.Repository
}
func New(options wp.Options, settings *Settings) *Plugin {
p := &Plugin{}
options.Execute = p.run
p.Plugin = wp.New(options)
p.Settings = settings
return p
}

28
plugin/utils.go Normal file
View File

@ -0,0 +1,28 @@
package plugin
import (
"fmt"
"os"
"strings"
"github.com/cenkalti/backoff/v4"
"golang.org/x/sys/execabs"
)
// shouldRetry returns true if the command should be re-executed. Currently
// this only returns true if the remote ref does not exist.
func shouldRetry(s string) bool {
return strings.Contains(s, "find remote ref")
}
func newBackoff(maxRetries uint64) backoff.BackOff {
b := backoff.NewExponentialBackOff()
b.InitialInterval = daemonBackoffInitialInterval
b.Multiplier = daemonBackoffMultiplier
return backoff.WithMaxRetries(b, maxRetries)
}
func trace(cmd *execabs.Cmd) {
fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}

4
renovate.json Normal file
View File

@ -0,0 +1,4 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>thegeeklab/renovate-presets:golang"]
}