├── .changelog.yml ├── .editorconfig ├── .gitignore ├── .golangci.yml ├── .hadolint.yml ├── .markdownlint.yaml ├── .pre-commit-config.yaml ├── .prettierrc ├── .woodpecker ├── release.yml └── test-release.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── defaults.go ├── docker └── Dockerfile.multiarch ├── docs.md ├── flags.go ├── git.svg ├── go.mod ├── go.sum ├── main.go ├── plugin.go ├── plugin_test.go ├── renovate.json ├── types.go ├── umask.go ├── umask_win.go └── utils.go /.changelog.yml: -------------------------------------------------------------------------------- 1 | # config for https://gitea.com/gitea/changelog to generate CHANGELOG.md 2 | 3 | # The full repository name 4 | repo: woodpecker-ci/plugin-git 5 | 6 | # Service type (gitea or github) 7 | service: github 8 | 9 | # Changelog groups and which labeled PRs to add to each group 10 | groups: 11 | - 12 | name: BREAKING 13 | labels: 14 | - breaking 15 | - 16 | name: FEATURES 17 | labels: 18 | - feature 19 | - 20 | name: SECURITY 21 | labels: 22 | - security 23 | - 24 | name: BUGFIXES 25 | labels: 26 | - bug 27 | - 28 | name: ENHANCEMENTS 29 | labels: 30 | - enhancement 31 | - refactor 32 | - ui 33 | - 34 | name: TESTING 35 | labels: 36 | - tests 37 | - 38 | name: TRANSLATION 39 | labels: 40 | - kind/translation 41 | - 42 | name: BUILD 43 | labels: 44 | - kind/build 45 | - kind/lint 46 | - 47 | name: DOCUMENTATION 48 | labels: 49 | - documentation 50 | - 51 | name: MISC 52 | default: true 53 | 54 | # regex indicating which labels to skip for the changelog 55 | skip-labels: skip-changelog|backport 56 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.go] 13 | indent_style = tab 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [Makefile] 19 | indent_style = tab 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | release/ 27 | vendor/ 28 | 29 | coverage.out 30 | plugin-git 31 | 32 | .vscode 33 | .idea 34 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters-settings: 2 | gofmt: 3 | simplify: true 4 | misspell: 5 | locale: US 6 | gofumpt: 7 | extra-rules: true 8 | forbidigo: 9 | forbid: 10 | - context\.WithCancel$ 11 | - ^print.*$ 12 | - panic 13 | errorlint: 14 | errorf-multi: true 15 | 16 | linters: 17 | disable-all: true 18 | enable: 19 | - bidichk 20 | - errcheck 21 | - gofmt 22 | - goimports 23 | - gosimple 24 | - govet 25 | - ineffassign 26 | - misspell 27 | - revive 28 | - staticcheck 29 | - typecheck 30 | - unused 31 | - whitespace 32 | - gofumpt 33 | - errorlint 34 | - forbidigo 35 | - zerologlint 36 | 37 | run: 38 | timeout: 5m 39 | -------------------------------------------------------------------------------- /.hadolint.yml: -------------------------------------------------------------------------------- 1 | ignored: 2 | - DL3018 # pin versions in Dockerfile 3 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # markdownlint YAML configuration 2 | # https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml 3 | 4 | # Default state for all rules 5 | default: true 6 | 7 | # Path to configuration file to extend 8 | extends: null 9 | 10 | # MD003/heading-style/header-style - Heading style 11 | MD003: 12 | # Heading style 13 | style: 'atx' 14 | 15 | # MD004/ul-style - Unordered list style 16 | MD004: 17 | style: 'dash' 18 | 19 | # MD007/ul-indent - Unordered list indentation 20 | MD007: 21 | # Spaces for indent 22 | indent: 2 23 | # Whether to indent the first level of the list 24 | start_indented: false 25 | 26 | # MD009/no-trailing-spaces - Trailing spaces 27 | MD009: 28 | # Spaces for line break 29 | br_spaces: 2 30 | # Allow spaces for empty lines in list items 31 | list_item_empty_lines: false 32 | # Include unnecessary breaks 33 | strict: false 34 | 35 | # MD010/no-hard-tabs - Hard tabs 36 | MD010: 37 | # Include code blocks 38 | code_blocks: true 39 | 40 | # MD012/no-multiple-blanks - Multiple consecutive blank lines 41 | MD012: 42 | # Consecutive blank lines 43 | maximum: 1 44 | 45 | # MD013/line-length - Line length 46 | MD013: 47 | # Number of characters 48 | line_length: 500 49 | # Number of characters for headings 50 | heading_line_length: 100 51 | # Number of characters for code blocks 52 | code_block_line_length: 80 53 | # Include code blocks 54 | code_blocks: false 55 | # Include tables 56 | tables: false 57 | # Include headings 58 | headings: true 59 | # Include headings 60 | headers: true 61 | # Strict length checking 62 | strict: false 63 | # Stern length checking 64 | stern: false 65 | 66 | # MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines 67 | MD022: 68 | # Blank lines above heading 69 | lines_above: 1 70 | # Blank lines below heading 71 | lines_below: 1 72 | 73 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content 74 | MD024: 75 | # Only check sibling headings 76 | allow_different_nesting: true 77 | 78 | # MD025/single-title/single-h1 - Multiple top-level headings in the same document 79 | MD025: 80 | # Heading level 81 | level: 1 82 | # RegExp for matching title in front matter 83 | front_matter_title: "^\\s*title\\s*[:=]" 84 | 85 | # MD026/no-trailing-punctuation - Trailing punctuation in heading 86 | MD026: 87 | # Punctuation characters 88 | punctuation: '.,;:!。,;:!' 89 | 90 | # MD029/ol-prefix - Ordered list item prefix 91 | MD029: 92 | # List style 93 | style: 'one_or_ordered' 94 | 95 | # MD030/list-marker-space - Spaces after list markers 96 | MD030: 97 | # Spaces for single-line unordered list items 98 | ul_single: 1 99 | # Spaces for single-line ordered list items 100 | ol_single: 1 101 | # Spaces for multi-line unordered list items 102 | ul_multi: 1 103 | # Spaces for multi-line ordered list items 104 | ol_multi: 1 105 | 106 | # MD033/no-inline-html - Inline HTML 107 | MD033: 108 | # Allowed elements 109 | allowed_elements: [details, summary, img, a, br, p] 110 | 111 | # MD035/hr-style - Horizontal rule style 112 | MD035: 113 | # Horizontal rule style 114 | style: '---' 115 | 116 | # MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading 117 | MD036: 118 | # Punctuation characters 119 | punctuation: '.,;:!?。,;:!?' 120 | 121 | # MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading 122 | MD041: 123 | # Heading level 124 | level: 1 125 | # RegExp for matching title in front matter 126 | front_matter_title: "^\\s*title\\s*[:=]" 127 | 128 | # MD044/proper-names - Proper names should have the correct capitalization 129 | MD044: 130 | # List of proper names 131 | # names: 132 | # Include code blocks 133 | code_blocks: false 134 | 135 | # MD046/code-block-style - Code block style 136 | MD046: 137 | # Block style 138 | style: 'fenced' 139 | 140 | # MD048/code-fence-style - Code fence style 141 | MD048: 142 | # Code fence syle 143 | style: 'backtick' 144 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: meta 3 | hooks: 4 | - id: check-hooks-apply 5 | - id: check-useless-excludes 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v5.0.0 8 | hooks: 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | - repo: https://github.com/golangci/golangci-lint 13 | rev: v2.1.6 14 | hooks: 15 | - id: golangci-lint 16 | - repo: https://github.com/igorshubovych/markdownlint-cli 17 | rev: v0.45.0 18 | hooks: 19 | - id: markdownlint 20 | exclude: '^CHANGELOG.md$' 21 | language_version: 22.15.1 22 | - repo: https://github.com/mrtazz/checkmake 23 | rev: 0.2.2 24 | hooks: 25 | - id: checkmake 26 | - repo: https://github.com/hadolint/hadolint 27 | rev: v2.13.1-beta 28 | hooks: 29 | - id: hadolint 30 | 31 | ci: 32 | autofix_commit_msg: | 33 | [pre-commit.ci] auto fixes from pre-commit.com hooks [CI SKIP] 34 | 35 | for more information, see https://pre-commit.ci 36 | autofix_prs: true 37 | autoupdate_branch: '' 38 | autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate' 39 | autoupdate_schedule: monthly 40 | # NB: hadolint not included in pre-commit.ci 41 | skip: [check-hooks-apply, check-useless-excludes, hadolint, golangci-lint] 42 | submodules: false 43 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 120, 6 | "tabWidth": 2, 7 | "endOfLine": "lf" 8 | } 9 | -------------------------------------------------------------------------------- /.woodpecker/release.yml: -------------------------------------------------------------------------------- 1 | when: 2 | event: push 3 | branch: ${CI_REPO_DEFAULT_BRANCH} 4 | 5 | steps: 6 | release: 7 | image: woodpeckerci/plugin-ready-release-go:3.2.0 8 | pull: true 9 | settings: 10 | release_branch: ${CI_REPO_DEFAULT_BRANCH} 11 | forge_type: github 12 | git_email: woodpecker-bot@obermui.de 13 | github_token: 14 | from_secret: GITHUB_TOKEN 15 | -------------------------------------------------------------------------------- /.woodpecker/test-release.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | - &golang 'golang:1.24-alpine' 3 | - &platforms 'linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/riscv64,linux/s390x' 4 | # vars used on push / tag events only 5 | - publish_logins: &publish_logins 6 | # Default DockerHub login 7 | - registry: https://index.docker.io/v1/ 8 | username: woodpeckerbot 9 | password: 10 | from_secret: docker_password 11 | # Additional Quay.IO login 12 | - registry: https://quay.io 13 | username: 'woodpeckerci+wp_ci' 14 | password: 15 | from_secret: QUAY_IO_TOKEN 16 | - &publish_repos 'woodpeckerci/plugin-git,quay.io/woodpeckerci/plugin-git' 17 | 18 | steps: 19 | vendor: 20 | image: *golang 21 | commands: 22 | - go mod vendor 23 | 24 | lint: 25 | image: *golang 26 | depends_on: vendor 27 | commands: 28 | - apk add make 29 | - make vet 30 | - make formatcheck 31 | when: 32 | - event: pull_request 33 | - event: push 34 | branch: renovate/* 35 | 36 | test: 37 | image: *golang 38 | depends_on: vendor 39 | commands: 40 | - apk add make git-lfs 41 | - make test 42 | when: 43 | - event: pull_request 44 | - event: push 45 | branch: renovate/* 46 | 47 | build-dryrun: 48 | image: woodpeckerci/plugin-docker-buildx:6.0.1 49 | depends_on: 50 | - lint 51 | - test 52 | settings: 53 | repo: test/repo 54 | dockerfile: ./docker/Dockerfile.multiarch 55 | dry_run: true 56 | platforms: *platforms 57 | tags: latest 58 | when: 59 | - event: pull_request 60 | - event: push 61 | branch: renovate/* 62 | 63 | release-next: 64 | image: woodpeckerci/plugin-docker-buildx:6.0.1 65 | depends_on: vendor 66 | settings: 67 | repo: *publish_repos 68 | dockerfile: ./docker/Dockerfile.multiarch 69 | platforms: *platforms 70 | tags: next 71 | logins: *publish_logins 72 | when: 73 | branch: ${CI_REPO_DEFAULT_BRANCH} 74 | event: push 75 | 76 | release-tag: 77 | image: woodpeckerci/plugin-docker-buildx:6.0.1 78 | depends_on: vendor 79 | settings: 80 | repo: *publish_repos 81 | dockerfile: ./docker/Dockerfile.multiarch 82 | platforms: *platforms 83 | auto_tag: true 84 | # remove line below if you can read it on a release branch and it's not the latest release branch 85 | tags: latest 86 | logins: *publish_logins 87 | when: 88 | event: tag 89 | 90 | build-binaries: 91 | image: *golang 92 | depends_on: vendor 93 | commands: 94 | - apk add make 95 | - make release 96 | when: 97 | event: tag 98 | 99 | release-binaries: 100 | image: woodpeckerci/plugin-release:0.2.5 101 | depends_on: build-binaries 102 | settings: 103 | api_key: 104 | from_secret: github_token 105 | files: 106 | - release/* 107 | title: ${CI_COMMIT_TAG##v} 108 | when: 109 | event: tag 110 | 111 | when: 112 | - event: pull_request 113 | - event: tag 114 | - event: push 115 | branch: 116 | - ${CI_REPO_DEFAULT_BRANCH} 117 | - renovate/* 118 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.6.5](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.6.5) - 2025-05-31 4 | 5 | ### ❤️ Thanks to all contributors! ❤️ 6 | 7 | @qwerty287 8 | 9 | ### 🐛 Bug Fixes 10 | 11 | - Always chmod workspace [[#240](https://github.com/woodpecker-ci/plugin-git/pull/240)] 12 | - Fix submodule override flag [[#241](https://github.com/woodpecker-ci/plugin-git/pull/241)] 13 | - Fix windows build [[#239](https://github.com/woodpecker-ci/plugin-git/pull/239)] 14 | 15 | ### 📦️ Dependency 16 | 17 | - chore(deps): update alpine docker tag to v3.22 [[#244](https://github.com/woodpecker-ci/plugin-git/pull/244)] 18 | 19 | ## [2.6.4](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.6.4) - 2025-05-27 20 | 21 | ### ❤️ Thanks to all contributors! ❤️ 22 | 23 | @qwerty287, @xoxys 24 | 25 | ### 🐛 Bug Fixes 26 | 27 | - Set umask to 0 before cloning [[#236](https://github.com/woodpecker-ci/plugin-git/pull/236)] 28 | 29 | ### 📚 Documentation 30 | 31 | - Clarify SSH key docs [[#235](https://github.com/woodpecker-ci/plugin-git/pull/235)] 32 | - Explain usage of chmod more detailed [[#216](https://github.com/woodpecker-ci/plugin-git/pull/216)] 33 | 34 | ### 📦️ Dependency 35 | 36 | - chore(deps): update pre-commit hook igorshubovych/markdownlint-cli to v0.45.0 [[#234](https://github.com/woodpecker-ci/plugin-git/pull/234)] 37 | - fix(deps): update module github.com/urfave/cli/v3 to v3.3.3 [[#233](https://github.com/woodpecker-ci/plugin-git/pull/233)] 38 | - chore(deps): update dependency go to v1.24.3 [[#232](https://github.com/woodpecker-ci/plugin-git/pull/232)] 39 | - chore(deps): update pre-commit hook golangci/golangci-lint to v2.1.6 [[#230](https://github.com/woodpecker-ci/plugin-git/pull/230)] 40 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v6.0.1 [[#229](https://github.com/woodpecker-ci/plugin-git/pull/229)] 41 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v6 [[#228](https://github.com/woodpecker-ci/plugin-git/pull/228)] 42 | - fix(deps): update module github.com/urfave/cli/v3 to v3.3.2 [[#227](https://github.com/woodpecker-ci/plugin-git/pull/227)] 43 | - fix(deps): update module github.com/urfave/cli/v3 to v3.3.1 [[#226](https://github.com/woodpecker-ci/plugin-git/pull/226)] 44 | - chore(deps): update pre-commit hook golangci/golangci-lint to v2.1.5 [[#225](https://github.com/woodpecker-ci/plugin-git/pull/225)] 45 | - fix(deps): update module github.com/urfave/cli/v2 to v3 [[#220](https://github.com/woodpecker-ci/plugin-git/pull/220)] 46 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3.2.0 [[#223](https://github.com/woodpecker-ci/plugin-git/pull/223)] 47 | - chore(deps): update pre-commit hook golangci/golangci-lint to v2.1.2 [[#222](https://github.com/woodpecker-ci/plugin-git/pull/222)] 48 | - chore(deps): update pre-commit hook golangci/golangci-lint to v2.1.1 [[#221](https://github.com/woodpecker-ci/plugin-git/pull/221)] 49 | - chore(deps): update pre-commit hook golangci/golangci-lint to v2 [[#219](https://github.com/woodpecker-ci/plugin-git/pull/219)] 50 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3.1.4 [[#218](https://github.com/woodpecker-ci/plugin-git/pull/218)] 51 | 52 | ## [2.6.3](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.6.3) - 2025-03-27 53 | 54 | ### ❤️ Thanks to all contributors! ❤️ 55 | 56 | @xoxys 57 | 58 | ### 🐛 Bug Fixes 59 | 60 | - Ensure fs mode for path is set correctly [[#215](https://github.com/woodpecker-ci/plugin-git/pull/215)] 61 | 62 | ### 📦️ Dependency 63 | 64 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.2.2 [[#214](https://github.com/woodpecker-ci/plugin-git/pull/214)] 65 | - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.5 [[#213](https://github.com/woodpecker-ci/plugin-git/pull/213)] 66 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.64.8 [[#212](https://github.com/woodpecker-ci/plugin-git/pull/212)] 67 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.64.7 [[#211](https://github.com/woodpecker-ci/plugin-git/pull/211)] 68 | - fix(deps): update module github.com/urfave/cli/v2 to v2.27.6 [[#210](https://github.com/woodpecker-ci/plugin-git/pull/210)] 69 | 70 | ### Misc 71 | 72 | - [pre-commit.ci] pre-commit autoupdate [[#208](https://github.com/woodpecker-ci/plugin-git/pull/208)] 73 | 74 | ## [2.6.2](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.6.2) - 2025-02-26 75 | 76 | ### ❤️ Thanks to all contributors! ❤️ 77 | 78 | @pat-s 79 | 80 | ### Misc 81 | 82 | - Use alpine release to fix tag fetching [[#206](https://github.com/woodpecker-ci/plugin-git/pull/206)] 83 | 84 | ## [2.6.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.6.1) - 2025-02-21 85 | 86 | ### ❤️ Thanks to all contributors! ❤️ 87 | 88 | @miry 89 | 90 | ### 📦️ Dependency 91 | 92 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.64.5 [[#204](https://github.com/woodpecker-ci/plugin-git/pull/204)] 93 | - chore(deps): update golang docker tag to v1.24 [[#203](https://github.com/woodpecker-ci/plugin-git/pull/203)] 94 | - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.4 [[#202](https://github.com/woodpecker-ci/plugin-git/pull/202)] 95 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.2.1 [[#201](https://github.com/woodpecker-ci/plugin-git/pull/201)] 96 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.2.0 [[#200](https://github.com/woodpecker-ci/plugin-git/pull/200)] 97 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3.1.3 [[#198](https://github.com/woodpecker-ci/plugin-git/pull/198)] 98 | - chore(deps): update pre-commit hook igorshubovych/markdownlint-cli to v0.44.0 [[#199](https://github.com/woodpecker-ci/plugin-git/pull/199)] 99 | - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.3 [[#196](https://github.com/woodpecker-ci/plugin-git/pull/196)] 100 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.63.4 [[#195](https://github.com/woodpecker-ci/plugin-git/pull/195)] 101 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3.1.1 [[#194](https://github.com/woodpecker-ci/plugin-git/pull/194)] 102 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3.1.0 [[#192](https://github.com/woodpecker-ci/plugin-git/pull/192)] 103 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.1.0 [[#191](https://github.com/woodpecker-ci/plugin-git/pull/191)] 104 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3 [[#190](https://github.com/woodpecker-ci/plugin-git/pull/190)] 105 | - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.2 [[#189](https://github.com/woodpecker-ci/plugin-git/pull/189)] 106 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.62.2 [[#188](https://github.com/woodpecker-ci/plugin-git/pull/188)] 107 | - chore(deps): update pre-commit hook igorshubovych/markdownlint-cli to v0.43.0 [[#186](https://github.com/woodpecker-ci/plugin-git/pull/186)] 108 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.62.0 [[#184](https://github.com/woodpecker-ci/plugin-git/pull/184)] 109 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v2.1.1 [[#183](https://github.com/woodpecker-ci/plugin-git/pull/183)] 110 | - fix(deps): update module github.com/adrg/xdg to v0.5.3 [[#182](https://github.com/woodpecker-ci/plugin-git/pull/182)] 111 | - fix(deps): update module github.com/adrg/xdg to v0.5.2 [[#181](https://github.com/woodpecker-ci/plugin-git/pull/181)] 112 | - fix(deps): update module github.com/urfave/cli/v2 to v2.27.5 [[#180](https://github.com/woodpecker-ci/plugin-git/pull/180)] 113 | - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5 [[#179](https://github.com/woodpecker-ci/plugin-git/pull/179)] 114 | - chore(deps): update pre-commit hook pre-commit/pre-commit-hooks to v5 [[#177](https://github.com/woodpecker-ci/plugin-git/pull/177)] 115 | - chore(deps): update pre-commit hook igorshubovych/markdownlint-cli to v0.42.0 [[#176](https://github.com/woodpecker-ci/plugin-git/pull/176)] 116 | - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v2 [[#174](https://github.com/woodpecker-ci/plugin-git/pull/174)] 117 | 118 | ### Misc 119 | 120 | - Add docs about object-format option [[#185](https://github.com/woodpecker-ci/plugin-git/pull/185)] 121 | - [pre-commit.ci] pre-commit autoupdate [[#178](https://github.com/woodpecker-ci/plugin-git/pull/178)] 122 | 123 | ## [2.6.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.6.0) - 2024-09-20 124 | 125 | ### ❤️ Thanks to all contributors! ❤️ 126 | 127 | @6543 128 | 129 | ### ✨ Features 130 | 131 | - Support sha256 git repos [[#173](https://github.com/woodpecker-ci/plugin-git/pull/173)] 132 | 133 | ### Misc 134 | 135 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.61.0 [[#171](https://github.com/woodpecker-ci/plugin-git/pull/171)] 136 | - [pre-commit.ci] pre-commit autoupdate [[#172](https://github.com/woodpecker-ci/plugin-git/pull/172)] 137 | - chore(deps): update golang docker tag to v1.23 [[#169](https://github.com/woodpecker-ci/plugin-git/pull/169)] 138 | - chore(deps): update pre-commit hook golangci/golangci-lint to v1.60.1 [[#170](https://github.com/woodpecker-ci/plugin-git/pull/170)] 139 | - fix(deps): update module github.com/urfave/cli/v2 to v2.27.4 [[#166](https://github.com/woodpecker-ci/plugin-git/pull/166)] 140 | - [pre-commit.ci] pre-commit autoupdate [[#164](https://github.com/woodpecker-ci/plugin-git/pull/164)] 141 | - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.1 [[#165](https://github.com/woodpecker-ci/plugin-git/pull/165)] 142 | - fix(deps): update module github.com/urfave/cli/v2 to v2.27.3 [[#162](https://github.com/woodpecker-ci/plugin-git/pull/162)] 143 | 144 | ## [2.5.2](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.5.2) - 2024-07-26 145 | 146 | ### ❤️ Thanks to all contributors! ❤️ 147 | 148 | @j04n-f 149 | 150 | ### 🐛 Bug Fixes 151 | 152 | - Fetch using short commit SHA [[#160](https://github.com/woodpecker-ci/plugin-git/pull/160)] 153 | 154 | ## [2.5.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.5.1) - 2024-07-13 155 | 156 | ### ❤️ Thanks to all contributors! ❤️ 157 | 158 | @christoph-heiss, @qwerty287 159 | 160 | ### Misc 161 | 162 | - fix(deps): update module github.com/adrg/xdg to v0.5.0 [[#157](https://github.com/woodpecker-ci/plugin-git/pull/157)] 163 | - Explain setting `depth` to `0` effect [[#156](https://github.com/woodpecker-ci/plugin-git/pull/156)] 164 | - docs: fix typo in plugin feature description [[#154](https://github.com/woodpecker-ci/plugin-git/pull/154)] 165 | - Update pre-commit hook golangci/golangci-lint to v1.59.1 [[#153](https://github.com/woodpecker-ci/plugin-git/pull/153)] 166 | - Use `release` plugin [[#152](https://github.com/woodpecker-ci/plugin-git/pull/152)] 167 | - Update pre-commit non-major [[#150](https://github.com/woodpecker-ci/plugin-git/pull/150)] 168 | - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.1.2 [[#149](https://github.com/woodpecker-ci/plugin-git/pull/149)] 169 | - Update pre-commit hook golangci/golangci-lint to v1.58.2 [[#148](https://github.com/woodpecker-ci/plugin-git/pull/148)] 170 | - Update pre-commit non-major [[#144](https://github.com/woodpecker-ci/plugin-git/pull/144)] 171 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v4 [[#146](https://github.com/woodpecker-ci/plugin-git/pull/146)] 172 | - Update module github.com/urfave/cli/v2 to v2.27.2 [[#143](https://github.com/woodpecker-ci/plugin-git/pull/143)] 173 | - Update woodpeckerci/plugin-github-release Docker tag to v1.2.0 [[#142](https://github.com/woodpecker-ci/plugin-git/pull/142)] 174 | - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.1.1 [[#141](https://github.com/woodpecker-ci/plugin-git/pull/141)] 175 | - Update pre-commit hook pre-commit/pre-commit-hooks to v4.6.0 [[#140](https://github.com/woodpecker-ci/plugin-git/pull/140)] 176 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.2.1 [[#139](https://github.com/woodpecker-ci/plugin-git/pull/139)] 177 | - Update pre-commit hook golangci/golangci-lint to v1.57.2 [[#138](https://github.com/woodpecker-ci/plugin-git/pull/138)] 178 | - Update pre-commit hook golangci/golangci-lint to v1.57.1 [[#137](https://github.com/woodpecker-ci/plugin-git/pull/137)] 179 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.2.0 [[#136](https://github.com/woodpecker-ci/plugin-git/pull/136)] 180 | - Update woodpeckerci/plugin-github-release Docker tag to v1.1.2 [[#135](https://github.com/woodpecker-ci/plugin-git/pull/135)] 181 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.1.0 [[#132](https://github.com/woodpecker-ci/plugin-git/pull/132)] 182 | - Update golang Docker tag to v1.22 [[#131](https://github.com/woodpecker-ci/plugin-git/pull/131)] 183 | - [pre-commit.ci] pre-commit autoupdate [[#130](https://github.com/woodpecker-ci/plugin-git/pull/130)] 184 | - Renovate: Use org config [[#129](https://github.com/woodpecker-ci/plugin-git/pull/129)] 185 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.0.1 [[#128](https://github.com/woodpecker-ci/plugin-git/pull/128)] 186 | 187 | ## [2.5.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.5.0) - 2024-01-27 188 | 189 | ### ❤️ Thanks to all contributors! ❤️ 190 | 191 | @6543, @mhmdanas, @qwerty287 192 | 193 | ### 📈 Enhancement 194 | 195 | - Clone ref if `ref` setting is set [[#117](https://github.com/woodpecker-ci/plugin-git/pull/117)] 196 | - make add ".exe" on windows builds [[#112](https://github.com/woodpecker-ci/plugin-git/pull/112)] 197 | 198 | ### 📚 Documentation 199 | 200 | - Correct config options' names in `docs.md` [[#124](https://github.com/woodpecker-ci/plugin-git/pull/124)] 201 | - Add logo [[#123](https://github.com/woodpecker-ci/plugin-git/pull/123)] 202 | - Document SSH settings [[#116](https://github.com/woodpecker-ci/plugin-git/pull/116)] 203 | 204 | ### Misc 205 | 206 | - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.1.0 [[#127](https://github.com/woodpecker-ci/plugin-git/pull/127)] 207 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v3 [[#126](https://github.com/woodpecker-ci/plugin-git/pull/126)] 208 | - Use cleartext user [[#125](https://github.com/woodpecker-ci/plugin-git/pull/125)] 209 | - Fix `depends_on`, take 2 [[#120](https://github.com/woodpecker-ci/plugin-git/pull/120)] 210 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v2.3.0 [[#122](https://github.com/woodpecker-ci/plugin-git/pull/122)] 211 | - [pre-commit.ci] pre-commit autoupdate [[#121](https://github.com/woodpecker-ci/plugin-git/pull/121)] 212 | - Fix `depends_on` [[#119](https://github.com/woodpecker-ci/plugin-git/pull/119)] 213 | - Use `depends_on` [[#118](https://github.com/woodpecker-ci/plugin-git/pull/118)] 214 | - Update module github.com/urfave/cli/v2 to v2.27.1 [[#115](https://github.com/woodpecker-ci/plugin-git/pull/115)] 215 | - Update module github.com/urfave/cli/v2 to v2.27.0 [[#114](https://github.com/woodpecker-ci/plugin-git/pull/114)] 216 | - Update module github.com/urfave/cli/v2 to v2.26.0 [[#110](https://github.com/woodpecker-ci/plugin-git/pull/110)] 217 | - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.0.3 [[#109](https://github.com/woodpecker-ci/plugin-git/pull/109)] 218 | 219 | ## [2.4.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.4.0) - 2023-11-21 220 | 221 | ### ❤️ Thanks to all contributors! ❤️ 222 | 223 | @renovate[bot], @6543, @pat-s 224 | 225 | ### 📈 Enhancement 226 | 227 | - Shallow clone submodules by default [[#106](https://github.com/woodpecker-ci/plugin-git/pull/106)] 228 | - add precommit and linters [[#103](https://github.com/woodpecker-ci/plugin-git/pull/103)] 229 | 230 | ### Misc 231 | 232 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v2.2.1 [[#107](https://github.com/woodpecker-ci/plugin-git/pull/107)] 233 | 234 | ## [2.3.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.3.1) - 2023-11-11 235 | 236 | ### ❤️ Thanks to all contributors! ❤️ 237 | 238 | @renovate[bot] 239 | 240 | ### Misc 241 | 242 | - Update woodpeckerci/plugin-ready-release-go Docker tag to v1 [[#104](https://github.com/woodpecker-ci/plugin-git/pull/104)] 243 | 244 | ## [2.3.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.3.0) - 2023-11-08 245 | 246 | ### ❤️ Thanks to all contributors! ❤️ 247 | 248 | @renovate[bot], @6543, @pat-s, @crapStone 249 | 250 | ### 📈 Enhancement 251 | 252 | - move flags into own file [[#100](https://github.com/woodpecker-ci/plugin-git/pull/100)] 253 | 254 | ### 📚 Documentation 255 | 256 | - Use correct ref spec [[#97](https://github.com/woodpecker-ci/plugin-git/pull/97)] 257 | 258 | ### Misc 259 | 260 | - Update woodpeckerci/plugin-ready-release-go Docker tag to v0.7.0 [[#102](https://github.com/woodpecker-ci/plugin-git/pull/102)] 261 | - make sure setting safe-directory does not fail [[#101](https://github.com/woodpecker-ci/plugin-git/pull/101)] 262 | - Plugin github-release: inject the secret as an env var [[#96](https://github.com/woodpecker-ci/plugin-git/pull/96)] 263 | - Update woodpeckerci/plugin-docker-buildx Docker tag to v2.2.0 [[#99](https://github.com/woodpecker-ci/plugin-git/pull/99)] 264 | 265 | ## [2.2.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.2.0) - 2023-10-05 266 | 267 | ### ❤️ Thanks to all contributors! ❤️ 268 | 269 | @renovate[bot], @pat-s, @qwerty287, @fracai, @RayaneB75 270 | 271 | ### ✨ Features 272 | 273 | - Add option to use SSH for cloning repo [[#75](https://github.com/woodpecker-ci/plugin-git/pull/75)] 274 | 275 | ### 📈 Enhancement 276 | 277 | - Use woodpecker plugin instead of drone one [[#91](https://github.com/woodpecker-ci/plugin-git/pull/91)] 278 | - Add renovate config [[#89](https://github.com/woodpecker-ci/plugin-git/pull/89)] 279 | 280 | ### 🐛 Bug Fixes 281 | 282 | - Fix renovate labels [[#92](https://github.com/woodpecker-ci/plugin-git/pull/92)] 283 | 284 | ### 📚 Documentation 285 | 286 | - docs for remote, refs, sha, path [[#88](https://github.com/woodpecker-ci/plugin-git/pull/88)] 287 | 288 | ### Misc 289 | 290 | - Update golang Docker tag to v1.21 [[#94](https://github.com/woodpecker-ci/plugin-git/pull/94)] 291 | 292 | ## [2.1.2](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.1.2) - 2023-09-05 293 | 294 | ### ❤️ Thanks to all contributors! ❤️ 295 | 296 | @qwerty287 297 | 298 | ### 🐛 Bug Fixes 299 | 300 | - Fix SHA checkout on PRs [[#84](https://github.com/woodpecker-ci/plugin-git/pull/84)] 301 | 302 | ## [2.1.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.1.1) - 2023-08-13 303 | 304 | ### ❤️ Thanks to all contributors! ❤️ 305 | 306 | @qwerty287, @6543 307 | 308 | ### 🐛 Bug Fixes 309 | 310 | - Always checkout by SHA [[#76](https://github.com/woodpecker-ci/plugin-git/pull/76)] 311 | 312 | ### Misc 313 | 314 | - Cleanups and updates [[#78](https://github.com/woodpecker-ci/plugin-git/pull/78)] 315 | - Publish to quay.io too [[#74](https://github.com/woodpecker-ci/plugin-git/pull/74)] 316 | 317 | ## [2.1.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/2.1.0) - 2023-07-23 318 | 319 | ### ❤️ Thanks to all contributors! ❤️ 320 | 321 | @anbraten, @pat-s, @qwerty287, @ambroisie, @6543 322 | 323 | ### 📈 Enhancement 324 | 325 | - Add git `safe.directory` support [[#70](https://github.com/woodpecker-ci/plugin-git/pull/70)] 326 | - Use new env vars [[#71](https://github.com/woodpecker-ci/plugin-git/pull/71)] 327 | - Add os.Environ() for git commands environments [[#67](https://github.com/woodpecker-ci/plugin-git/pull/67)] 328 | 329 | ### 📚 Documentation 330 | 331 | - Change plugin name to "Git Clone" [[#61](https://github.com/woodpecker-ci/plugin-git/pull/61)] 332 | 333 | ### Misc 334 | 335 | - Add release helper [[#72](https://github.com/woodpecker-ci/plugin-git/pull/72)] 336 | 337 | ## [v2.0.3](https://github.com/woodpecker-ci/plugin-git/releases/tag/v2.0.3) - 2022-12-29 338 | 339 | - BUGFIXES 340 | - Fix write .netrc location with home var (#57) 341 | 342 | ## [v2.0.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/v2.0.1) - 2022-12-21 343 | 344 | - BUGFIXES 345 | - Fix dockerfile to build correct go binary (#54) 346 | - Do not set GIT_TERMINAL_PROMPT=0 for git (#52) 347 | 348 | ## [v2.0.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v2.0.0) - 2022-11-14 349 | 350 | - FEATURES 351 | - Partial clone by default (#48) 352 | - BUGFIXES 353 | - Make home var settable (#47) 354 | - ENHANCEMENTS 355 | - Publish semver images (#50) 356 | 357 | ## [v1.6.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.6.1) - 2022-11-06 358 | 359 | - BUGFIXES 360 | - Explicite set and check for home dir (#46) 361 | 362 | ## [v1.6.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.6.0) - 2022-10-13 363 | 364 | - BUGFIXES 365 | - Handle git-lfs separately (#40) 366 | - ENHANCEMENTS 367 | - if no branch info is set, fallback to default repo branch (#41) 368 | 369 | ## [v1.5.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.5.0) - 2022-10-06 370 | 371 | - ENHANCEMENTS 372 | - Release binarys (#37) 373 | - Use ref to checkout if no commit sha is set (#36) 374 | - Fix tests (#35) 375 | - MISC 376 | - Update urfave/cli to v2.17.1 (#38) 377 | - Use built-in log instead of logrus (#34) 378 | 379 | ## [v1.4.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.4.0) - 2022-08-30 380 | 381 | - ENHANCEMENTS 382 | - Auto enable tags clone if it's ci event is 'tag' (#30) 383 | - Support more architectures (#29) 384 | 385 | ## [v1.3.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.3.0) - 2022-08-15 386 | 387 | - FEATURES 388 | - Add option to Change branch name for checkout (#28) 389 | 390 | ## [v1.2.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.2.0) - 2022-05-25 391 | 392 | - FEATURES 393 | - Add git-lfs (#21) 394 | - Custom ssl certs for git (#19) 395 | - ENHANCEMENTS 396 | - Add an `lfs` setting which lets you disable Git LFS (#24) 397 | - DOCUMENTATION 398 | - Add docs page (#23) 399 | 400 | ## [v1.1.2](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.1.2) - 2022-01-30 401 | 402 | - BUGFIXES 403 | - Fix empty login/password in netrc (#20) 404 | 405 | ## [v1.1.1](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.1.1) - 2021-12-23 406 | 407 | - BUGFIXES 408 | - Fix version info (#13) 409 | 410 | ## [v1.1.0](https://github.com/woodpecker-ci/plugin-git/releases/tag/v1.1.0) - 2021-12-18 411 | 412 | - FEATURES 413 | - Add ppc64le support (#8) 414 | - BUGFIXES 415 | - Regognize "CI\_*" EnvVars (#6) 416 | - ENHANCEMENTS 417 | - Multiarch build (#8) 418 | - MISC 419 | - Upgrade urfave/cli to v2 (#5) 420 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOFILES_NOVENDOR = $(shell find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./.git/*") 2 | GO_PACKAGES ?= $(shell go list ./... | grep -v /vendor/) 3 | 4 | GOOS ?= linux 5 | GOARCH ?= amd64 6 | TARGETOS ?= $(GOOS) 7 | TARGETARCH ?= $(GOARCH) 8 | 9 | BIN_SUFFIX := 10 | ifeq ($(TARGETOS),windows) 11 | BIN_SUFFIX := .exe 12 | endif 13 | 14 | VERSION ?= next 15 | ifneq ($(CI_COMMIT_TAG),) 16 | VERSION := $(CI_COMMIT_TAG:v%=%) 17 | endif 18 | 19 | # append commit-sha to next version 20 | BUILD_VERSION := $(VERSION) 21 | ifeq ($(BUILD_VERSION),next) 22 | CI_COMMIT_SHA ?= $(shell git rev-parse HEAD) 23 | BUILD_VERSION := $(shell echo "next-$(shell echo ${CI_COMMIT_SHA} | head -c 8)") 24 | endif 25 | 26 | LDFLAGS := -s -w -extldflags "-static" -X main.version=${BUILD_VERSION} 27 | 28 | .PHONY: all 29 | all: build 30 | 31 | .PHONY: vendor 32 | vendor: 33 | go mod tidy 34 | go mod vendor 35 | 36 | formatcheck: 37 | @([ -z "$(shell gofmt -d $(GOFILES_NOVENDOR) | head)" ]) || (echo "Source is unformatted"; exit 1) 38 | 39 | format: 40 | @gofmt -w ${GOFILES_NOVENDOR} 41 | 42 | .PHONY: clean 43 | clean: 44 | go clean -i ./... 45 | rm -rf release/ 46 | 47 | .PHONY: vet 48 | vet: 49 | @echo "Running go vet..." 50 | CGO_ENABLED=0 go vet $(GO_PACKAGES) 51 | 52 | .PHONY: test 53 | test: 54 | CGO_ENABLED=0 go test -cover ./... 55 | # we can not use "-race" as test trigger write to os.stdout 56 | 57 | build: 58 | CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -ldflags '${LDFLAGS}' -o release/plugin-git${BIN_SUFFIX} 59 | 60 | .PHONY: version 61 | version: 62 | @echo ${BUILD_VERSION} 63 | 64 | release-binaries: 65 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/linux-amd64_plugin-git 66 | GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/linux-arm64_plugin-git 67 | GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/linux-arm_plugin-git 68 | GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/windows-amd64_plugin-git.exe 69 | GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/windows-arm64_plugin-git.exe 70 | GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/darwin-amd64_plugin-git 71 | GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -ldflags '${LDFLAGS}' -o release/darwin-arm64_plugin-git 72 | 73 | release-tarball: 74 | mkdir -p release 75 | tar -cvzf release/plugin-git-src-$(BUILD_VERSION).tar.gz \ 76 | vendor/ \ 77 | *.go \ 78 | go.??? \ 79 | LICENSE \ 80 | Makefile 81 | 82 | release-checksums: 83 | # generate shas for tar files 84 | (cd release/; sha256sum *plugin-git* > checksums.txt) 85 | 86 | .PHONY: release 87 | release: release-binaries release-tarball 88 | $(MAKE) release-checksums 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plugin-git 2 | 3 |

4 | 5 | Build Status 6 | 7 | 8 | Discord chat 9 | 10 | 11 | Go Report Card 12 | 13 | 14 | GoDoc 15 | 16 | 17 | Docker pulls 18 | 19 | 20 | License: Apache-2.0 21 | 22 |

23 | 24 | Woodpecker plugin to clone `git` repositories. For the usage information and a listing of the available options please take a look at [the docs](https://woodpecker-ci.org/plugins/Git%20Clone). 25 | The docs are also available in [`docs.md` in this repository](docs.md). 26 | 27 | ## Build 28 | 29 | Build the binary with the following command: 30 | 31 | ```console 32 | export GOOS=linux 33 | export GOARCH=amd64 34 | export CGO_ENABLED=0 35 | export GO111MODULE=on 36 | 37 | go build -v -a -tags netgo -o release/linux/amd64/plugin-git 38 | ``` 39 | 40 | ## Docker 41 | 42 | Build the Docker image with the following command: 43 | 44 | ```console 45 | docker buildx build \ 46 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 47 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 48 | --platform linux/amd64 --output type=docker \ 49 | --file docker/Dockerfile.multiarch --tag woodpeckerci/plugin-git . 50 | ``` 51 | 52 | *The platform linux/amd64 should be replaced by the correct platform.* 53 | 54 | This will build the image and load it into docker so the image can be used locally. 55 | [More information on the output formats can be found in docker buildx doc](https://docs.docker.com/engine/reference/commandline/buildx_build/#output). 56 | 57 | ## Usage 58 | 59 | Clone a commit: 60 | 61 | ```console 62 | docker run --rm \ 63 | -e CI_REPO_REMOTE=https://github.com/garyburd/redigo.git \ 64 | -e CI_WORKSPACE=/go/src/github.com/garyburd/redigo \ 65 | -e CI_BUILD_EVENT=push \ 66 | -e CI_COMMIT_SHA=d8dbe4d94f15fe89232e0402c6e8a0ddf21af3ab \ 67 | -e CI_COMMIT_REF=refs/heads/master \ 68 | woodpeckerci/plugin-git 69 | ``` 70 | 71 | Clone a pull request: 72 | 73 | ```console 74 | docker run --rm \ 75 | -e CI_REPO_REMOTE=https://github.com/garyburd/redigo.git \ 76 | -e CI_WORKSPACE=/go/src/github.com/garyburd/redigo \ 77 | -e CI_BUILD_EVENT=pull_request \ 78 | -e CI_COMMIT_SHA=3b4642018d177bf5fecc5907e7f341a2b5c12b8a \ 79 | -e CI_COMMIT_REF=refs/pull/74/head \ 80 | woodpeckerci/plugin-git 81 | ``` 82 | 83 | Clone a tag: 84 | 85 | ```console 86 | docker run --rm \ 87 | -e CI_REPO_REMOTE=https://github.com/garyburd/redigo.git \ 88 | -e CI_WORKSPACE=/go/src/github.com/garyburd/redigo \ 89 | -e CI_BUILD_EVENT=tag \ 90 | -e CI_COMMIT_SHA=3b4642018d177bf5fecc5907e7f341a2b5c12b8a \ 91 | -e CI_COMMIT_REF=refs/tags/74/head \ 92 | woodpeckerci/plugin-git 93 | ``` 94 | 95 | ## Build arguments 96 | 97 | ### HOME 98 | 99 | The docker image can be build using `--build-arg HOME=`. 100 | This will create the directory for the custom home and set the custom home as the default value for the `home` plugin setting (see [the plugin docs](./docs.md) for more information about this setting). 101 | -------------------------------------------------------------------------------- /defaults.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/adrg/xdg" 7 | "github.com/urfave/cli/v3" 8 | ) 9 | 10 | func SetDefaults(c *cli.Command, p *Plugin) { 11 | if p.Pipeline.Event == "tag" && !c.IsSet("tags") { 12 | // tags clone not explicit set but pipeline is triggered by a tag 13 | // auto set tags cloning to true 14 | p.Config.Tags = true 15 | } 16 | 17 | if c.IsSet("tags") && c.IsSet("partial") { 18 | fmt.Println("WARNING: ignore partial clone as tags are fetched") 19 | } 20 | 21 | if p.Config.Tags && p.Config.Partial { 22 | // if tag fetching is enabled per event or setting, disable partial clone 23 | p.Config.Partial = false 24 | } 25 | 26 | if p.Config.Partial { 27 | p.Config.Depth = 1 28 | p.Config.filter = "tree:0" 29 | } 30 | 31 | if len(p.Config.Home) == 0 { 32 | // fallback to system home 33 | p.Config.Home = xdg.Home 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docker/Dockerfile.multiarch: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM golang:1.24 AS build 2 | ARG TARGETOS TARGETARCH 3 | 4 | WORKDIR /src 5 | COPY . . 6 | RUN --mount=type=cache,target=/root/.cache/go-build \ 7 | --mount=type=cache,target=/go/pkg \ 8 | make build 9 | 10 | FROM alpine:3.22 11 | ARG HOME=/app 12 | 13 | ENV GODEBUG=netdns=go 14 | ENV PLUGIN_HOME=$HOME 15 | 16 | RUN mkdir -p $HOME && apk add --no-cache ca-certificates git openssh curl git-lfs 17 | 18 | COPY --from=build src/release/plugin-git /bin/ 19 | ENTRYPOINT ["/bin/plugin-git"] 20 | -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Git Clone 3 | icon: https://raw.githubusercontent.com/woodpecker-ci/plugin-git/main/git.svg 4 | description: This is the default plugin for the clone step. 5 | author: Woodpecker Authors 6 | tags: [git, clone] 7 | containerImage: woodpeckerci/plugin-git 8 | containerImageUrl: https://hub.docker.com/r/woodpeckerci/plugin-git 9 | url: https://github.com/woodpecker-ci/plugin-git 10 | --- 11 | 12 | # plugin-git 13 | 14 | This plugin is automatically introduced into your pipeline as the first step. 15 | Its purpose is to clone your Git repository. 16 | 17 | ## Features 18 | 19 | - Git LFS support is enabled by default. 20 | - Fetch tags when needed. 21 | - Adjust submodules. 22 | 23 | ## Overriding Settings 24 | 25 | You can manually define your `clone` step in order to change plugin or override some of the default settings. 26 | Consult [the `clone` section of the pipeline documentation][workflowClone] for more information; 27 | this documentation page only describes this plugin. 28 | 29 | ```yaml 30 | clone: 31 | git: 32 | image: woodpeckerci/plugin-git 33 | settings: 34 | depth: 50 35 | lfs: false 36 | ``` 37 | 38 | ## Settings 39 | 40 | | Settings Name | Default | Description | 41 | | ------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 42 | | `depth` | _none_ | If specified, uses git's `--depth` option to create a shallow clone with a limited number of commits, overwritten by `partial`. Setting it to `0` disables shallow cloning | 43 | | `lfs` | `true` | Set this to `false` to disable retrieval of LFS files | 44 | | `recursive` | `false` | Clones submodules recursively | 45 | | `skip-verify` | `false` | Skips the SSL verification | 46 | | `tags` | `false` (except on tag event) | Fetches tags when set to true, default is false if event is not tag else true | 47 | | `submodule-override` | _none_ | Override submodule urls | 48 | | `submodule-update-remote` | `false` | Pass the --remote flag to git submodule update | 49 | | `submodule-partial` | `true` | Update submodules via partial clone (depth=1) | 50 | | `custom-ssl-path` | _none_ | Set path to custom cert | 51 | | `custom-ssl-url` | _none_ | Set url to custom cert | 52 | | `backoff` | `5sec` | Change backoff duration | 53 | | `attempts` | `5` | Change backoff attempts | 54 | | `branch` | $CI_COMMIT_BRANCH | Change branch name to checkout to | 55 | | `partial` | `true` (except if tags are fetched) | Only fetch the one commit and it's blob objects to resolve all files, overwrite depth with 1 | 56 | | `home` | | Change HOME var for commands executed, fail if it does not exist | 57 | | `remote` | $CI_REPO_CLONE_URL | Set the git remote url | 58 | | `remote-ssh` | $CI_REPO_CLONE_SSH_URL | Set the git SSH remote url | 59 | | `object-format` | detected from commit SHA | Set the object format for Git initialization. Supported values: `sha1`, `sha256`. | 60 | | `sha` | $CI_COMMIT_SHA | git commit hash to retrieve | 61 | | `ref` | _none_ | Set the git reference to retrieve | 62 | | `path` | $CI_WORKSPACE | Set destination path to clone to | 63 | | `use-ssh` | `false` | Clone using SSH | 64 | | `ssh-key` | _none_ | path to SSH key for SSH clone | 65 | 66 | [workflowClone]: https://woodpecker-ci.org/docs/usage/workflow-syntax#clone 67 | -------------------------------------------------------------------------------- /flags.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/urfave/cli/v3" 7 | ) 8 | 9 | var globalFlags = []cli.Flag{ 10 | &cli.StringFlag{ 11 | Name: "remote", 12 | Usage: "git remote url", 13 | Sources: cli.EnvVars("PLUGIN_REMOTE", "CI_REPO_CLONE_URL"), 14 | }, 15 | &cli.StringFlag{ 16 | Name: "remote-ssh", 17 | Usage: "git clone ssh url", 18 | Sources: cli.EnvVars("PLUGIN_REMOTE_SSH", "CI_REPO_CLONE_SSH_URL"), 19 | }, 20 | &cli.StringFlag{ 21 | Name: "object-format", 22 | Usage: "specify the object format (hash) to be used on init. if not set it is autodetect by the commit sha.", 23 | Sources: cli.EnvVars("PLUGIN_OBJECT_FORMAT"), 24 | }, 25 | &cli.StringFlag{ 26 | Name: "path", 27 | Usage: "git clone path", 28 | Sources: cli.EnvVars("PLUGIN_PATH", "CI_WORKSPACE"), 29 | }, 30 | &cli.StringFlag{ 31 | Name: "sha", 32 | Usage: "git commit sha", 33 | Sources: cli.EnvVars("PLUGIN_SHA", "CI_COMMIT_SHA"), 34 | }, 35 | &cli.StringFlag{ 36 | Name: "ref", 37 | Usage: "git commit ref", 38 | Sources: cli.EnvVars("PLUGIN_REF"), 39 | }, 40 | &cli.StringFlag{ 41 | Name: "event", 42 | Value: "push", 43 | Usage: "pipeline event", 44 | Sources: cli.EnvVars("CI_PIPELINE_EVENT"), 45 | }, 46 | &cli.StringFlag{ 47 | Name: "netrc.machine", 48 | Usage: "netrc machine", 49 | Sources: cli.EnvVars("CI_NETRC_MACHINE"), 50 | }, 51 | &cli.StringFlag{ 52 | Name: "netrc.username", 53 | Usage: "netrc username", 54 | Sources: cli.EnvVars("CI_NETRC_USERNAME"), 55 | }, 56 | &cli.StringFlag{ 57 | Name: "netrc.password", 58 | Usage: "netrc password", 59 | Sources: cli.EnvVars("CI_NETRC_PASSWORD"), 60 | }, 61 | &cli.IntFlag{ 62 | Name: "depth", 63 | Usage: "clone depth", 64 | Sources: cli.EnvVars("PLUGIN_DEPTH"), 65 | }, 66 | &cli.BoolFlag{ 67 | Name: "recursive", 68 | Usage: "clone submodules", 69 | Sources: cli.EnvVars("PLUGIN_RECURSIVE"), 70 | Value: true, 71 | }, 72 | &cli.BoolFlag{ 73 | Name: "tags", 74 | Usage: "clone tags, if not explicitly set and event is tag its default is true else false", 75 | Sources: cli.EnvVars("PLUGIN_TAGS"), 76 | }, 77 | &cli.BoolFlag{ 78 | Name: "skip-verify", 79 | Usage: "skip tls verification", 80 | Sources: cli.EnvVars("PLUGIN_SKIP_VERIFY"), 81 | }, 82 | &cli.StringFlag{ 83 | Name: "custom-cert", 84 | Usage: "path or url to custom cert", 85 | Sources: cli.EnvVars("PLUGIN_CUSTOM_SSL_PATH", "PLUGIN_CUSTOM_SSL_URL"), 86 | }, 87 | &cli.BoolFlag{ 88 | Name: "submodule-update-remote", 89 | Usage: "update remote submodules", 90 | Sources: cli.EnvVars("PLUGIN_SUBMODULES_UPDATE_REMOTE", "PLUGIN_SUBMODULE_UPDATE_REMOTE"), 91 | }, 92 | &cli.StringFlag{ 93 | Name: "submodule-override", 94 | Usage: "json map of submodule overrides", 95 | Sources: cli.EnvVars("PLUGIN_SUBMODULE_OVERRIDE"), 96 | }, 97 | &cli.BoolFlag{ 98 | Name: "submodule-partial", 99 | Usage: "update submodules via partial clone (depth=1) (default)", 100 | Sources: cli.EnvVars("PLUGIN_SUBMODULES_PARTIAL", "PLUGIN_SUBMODULE_PARTIAL"), 101 | Value: true, 102 | }, 103 | &cli.DurationFlag{ 104 | Name: "backoff", 105 | Usage: "backoff duration", 106 | Sources: cli.EnvVars("PLUGIN_BACKOFF"), 107 | Value: 5 * time.Second, 108 | }, 109 | &cli.IntFlag{ 110 | Name: "backoff-attempts", 111 | Usage: "backoff attempts", 112 | Sources: cli.EnvVars("PLUGIN_ATTEMPTS"), 113 | Value: 5, 114 | }, 115 | &cli.BoolFlag{ 116 | Name: "lfs", 117 | Usage: "whether to retrieve LFS content if available", 118 | Sources: cli.EnvVars("PLUGIN_LFS"), 119 | Value: true, 120 | }, 121 | &cli.StringFlag{ 122 | Name: "env-file", 123 | Usage: "source env file", 124 | }, 125 | &cli.StringFlag{ 126 | Name: "branch", 127 | Usage: "Change branch name", 128 | Sources: cli.EnvVars("PLUGIN_BRANCH", "CI_COMMIT_BRANCH", "CI_REPO_DEFAULT_BRANCH"), 129 | }, 130 | &cli.BoolFlag{ 131 | Name: "partial", 132 | Usage: "Enable/Disable Partial clone", 133 | Sources: cli.EnvVars("PLUGIN_PARTIAL"), 134 | Value: true, 135 | }, 136 | &cli.StringFlag{ 137 | Name: "home", 138 | Usage: "Change home directory", 139 | Sources: cli.EnvVars("PLUGIN_HOME"), 140 | }, 141 | &cli.StringFlag{ 142 | Name: "safe-directory", 143 | Usage: "Define/replace safe directories", 144 | Sources: cli.EnvVars("PLUGIN_SAFE_DIRECTORY", "CI_WORKSPACE"), 145 | }, 146 | &cli.BoolFlag{ 147 | Name: "use-ssh", 148 | Usage: "Using ssh for git clone", 149 | Sources: cli.EnvVars("PLUGIN_USE_SSH"), 150 | Value: false, 151 | }, 152 | &cli.StringFlag{ 153 | Name: "ssh-key", 154 | Usage: "SSH key for ssh clone", 155 | Sources: cli.EnvVars("PLUGIN_SSH_KEY"), 156 | }, 157 | } 158 | -------------------------------------------------------------------------------- /git.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/woodpecker-ci/plugin-git 2 | 3 | go 1.22 4 | 5 | toolchain go1.24.4 6 | 7 | require ( 8 | github.com/adrg/xdg v0.5.3 9 | github.com/joho/godotenv v1.5.1 10 | github.com/urfave/cli/v3 v3.3.3 11 | ) 12 | 13 | require golang.org/x/sys v0.26.0 // indirect 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= 2 | github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 6 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 10 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 11 | github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I= 12 | github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= 13 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 14 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 15 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 16 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | "github.com/joho/godotenv" 9 | "github.com/urfave/cli/v3" 10 | ) 11 | 12 | var version = "0.0.0+0" 13 | 14 | func main() { 15 | app := cli.Command{} 16 | app.Name = "git plugin" 17 | app.Usage = "git plugin" 18 | app.Action = run 19 | app.Version = version 20 | app.Flags = globalFlags 21 | 22 | ctx := context.Background() 23 | 24 | if err := app.Run(ctx, os.Args); err != nil { 25 | log.Fatal(err) 26 | } 27 | } 28 | 29 | func run(ctx context.Context, c *cli.Command) error { 30 | if c.String("env-file") != "" { 31 | _ = godotenv.Load(c.String("env-file")) 32 | } 33 | 34 | plugin := Plugin{ 35 | Repo: Repo{ 36 | Clone: c.String("remote"), 37 | CloneSSH: c.String("remote-ssh"), 38 | ObjectFormat: c.String("object-format"), 39 | }, 40 | Pipeline: Pipeline{ 41 | Commit: c.String("sha"), 42 | Event: c.String("event"), 43 | Path: c.String("path"), 44 | Ref: c.String("ref"), 45 | }, 46 | Netrc: Netrc{ 47 | Login: c.String("netrc.username"), 48 | Machine: c.String("netrc.machine"), 49 | Password: c.String("netrc.password"), 50 | }, 51 | Config: Config{ 52 | Depth: c.Int("depth"), 53 | Tags: c.Bool("tags"), 54 | Recursive: c.Bool("recursive"), 55 | SkipVerify: c.Bool("skip-verify"), 56 | CustomCert: c.String("custom-cert"), 57 | SubmoduleRemote: c.Bool("submodule-update-remote"), 58 | Submodules: c.String("submodule-override"), 59 | SubmodulePartial: c.Bool("submodule-partial"), 60 | Lfs: c.Bool("lfs"), 61 | Branch: c.String("branch"), 62 | Partial: c.Bool("partial"), 63 | Home: c.String("home"), 64 | SafeDirectory: c.String("safe-directory"), 65 | UseSSH: c.Bool("use-ssh"), 66 | SSHKey: c.String("ssh-key"), 67 | }, 68 | Backoff: Backoff{ 69 | Attempts: c.Int("backoff-attempts"), 70 | Duration: c.Duration("backoff"), 71 | }, 72 | } 73 | 74 | SetDefaults(c, &plugin) 75 | 76 | return plugin.Exec() 77 | } 78 | -------------------------------------------------------------------------------- /plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | type Plugin struct { 18 | Repo Repo 19 | Pipeline Pipeline 20 | Netrc Netrc 21 | Config Config 22 | Backoff Backoff 23 | } 24 | 25 | const customCertTmpPath = "/tmp/customCert.pem" 26 | 27 | var defaultEnvVars = []string{ 28 | // do not set GIT_TERMINAL_PROMPT=0, otherwise git won't load credentials from ".netrc" 29 | "GIT_LFS_SKIP_SMUDGE=1", // prevents git-lfs from retrieving any LFS files 30 | } 31 | 32 | func (p Plugin) Exec() error { 33 | // set umask to 0 so cloned files are 34 | // accessible from non-root containers 35 | umask() 36 | 37 | if p.Pipeline.Path != "" { 38 | err := os.MkdirAll(p.Pipeline.Path, 0o777) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | 44 | // make sure pipeline workspace is always writable for anyone 45 | os.Chmod(p.Pipeline.Path, 0o777) 46 | 47 | err := writeNetrc(p.Config.Home, p.Netrc.Machine, p.Netrc.Login, p.Netrc.Password) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | // set vars from exec environment 53 | defaultEnvVars = append(os.Environ(), defaultEnvVars...) 54 | 55 | // alter home var for all commands exec afterwards 56 | if err := setHome(p.Config.Home); err != nil { 57 | return err 58 | } 59 | 60 | var cmds []*exec.Cmd 61 | 62 | if p.Config.SkipVerify { 63 | cmds = append(cmds, skipVerify()) 64 | } else if p.Config.CustomCert != "" { 65 | certCmd := customCertHandler(p.Config.CustomCert) 66 | if certCmd != nil { 67 | cmds = append(cmds, certCmd) 68 | } 69 | } 70 | 71 | // autodetect object-format if not set 72 | if p.Repo.ObjectFormat == "" { 73 | p.Repo.ObjectFormat = "sha1" 74 | if len(p.Pipeline.Commit) == 64 { 75 | p.Repo.ObjectFormat = "sha256" 76 | } 77 | } 78 | 79 | if isDirEmpty(filepath.Join(p.Pipeline.Path, ".git")) { 80 | cmds = append(cmds, initGit(p.Config.Branch, p.Repo.ObjectFormat)) 81 | cmds = append(cmds, safeDirectory(p.Config.SafeDirectory)) 82 | if p.Config.UseSSH { 83 | // If env var PLUGIN_USE_SSH is set to true, use SSH instead of HTTPS 84 | cmds = append(cmds, remote(p.Repo.CloneSSH)) 85 | if p.Config.SSHKey != "" { 86 | // If env var PLUGIN_SSH_KEY is set, use it as the SSH key 87 | cmds = append(cmds, sshKeyHandler(p.Config.SSHKey)) 88 | } 89 | } else { 90 | cmds = append(cmds, remote(p.Repo.Clone)) 91 | } 92 | 93 | } 94 | 95 | if p.Pipeline.Ref != "" { 96 | // fetch and checkout by ref 97 | fmt.Println("using head checkout") 98 | cmds = append(cmds, fetch(p.Pipeline.Ref, p.Config.Tags, p.Config.Depth, p.Config.filter)) 99 | cmds = append(cmds, checkoutHead()) 100 | } else if len(p.Pipeline.Commit) != 40 && len(p.Pipeline.Commit) != 64 { 101 | // fetch requires full SHA1 (40 chars) or SHA256 (64 chars) commits (unambiguous reference) 102 | // for short SHA1 or SHA256, fetch and switch the branch before commit reset 103 | if p.Config.Branch == "" { 104 | return fmt.Errorf("short commit SHA1 checkout requires a branch") 105 | } 106 | cmds = append(cmds, fetch(p.Config.Branch, p.Config.Tags, p.Config.Depth, p.Config.filter)) 107 | cmds = append(cmds, switchBranch(p.Config.Branch)) 108 | cmds = append(cmds, checkoutSha(p.Pipeline.Commit)) 109 | } else { 110 | // fetch and checkout by commit sha 111 | cmds = append(cmds, fetch(p.Pipeline.Commit, p.Config.Tags, p.Config.Depth, p.Config.filter)) 112 | cmds = append(cmds, checkoutSha(p.Pipeline.Commit)) 113 | } 114 | 115 | if p.Config.Submodules != "" { 116 | var submoduleOverrides map[string]string 117 | err = json.Unmarshal([]byte(p.Config.Submodules), &submoduleOverrides) 118 | if err != nil { 119 | return fmt.Errorf("could not parse submodule_override map: %v", err) 120 | } 121 | for name, submoduleUrl := range submoduleOverrides { 122 | cmds = append(cmds, remapSubmodule(name, submoduleUrl)) 123 | } 124 | } 125 | 126 | if p.Config.Recursive { 127 | cmds = append(cmds, updateSubmodules(p.Config.SubmoduleRemote, p.Config.SubmodulePartial)) 128 | } 129 | 130 | if p.Config.Lfs { 131 | cmds = append(cmds, 132 | fetchLFS(), 133 | checkoutLFS()) 134 | } 135 | 136 | for _, cmd := range cmds { 137 | buf := new(bytes.Buffer) 138 | cmd.Dir = p.Pipeline.Path 139 | cmd.Stdout = io.MultiWriter(os.Stdout, buf) 140 | cmd.Stderr = io.MultiWriter(os.Stderr, buf) 141 | trace(cmd) 142 | err := cmd.Run() 143 | switch { 144 | case err != nil && shouldRetry(buf.String()): 145 | err = retryExec(cmd, p.Backoff.Duration, p.Backoff.Attempts) 146 | if err != nil { 147 | return err 148 | } 149 | case err != nil: 150 | return err 151 | } 152 | } 153 | 154 | return nil 155 | } 156 | 157 | func customCertHandler(certPath string) *exec.Cmd { 158 | if IsUrl(certPath) { 159 | if downloadCert(certPath) { 160 | return setCustomCert(customCertTmpPath) 161 | } else { 162 | fmt.Printf("Failed to download custom ssl cert. Ignoring...\n") 163 | return nil 164 | } 165 | } 166 | return setCustomCert(certPath) 167 | } 168 | 169 | func IsUrl(str string) bool { 170 | u, err := url.Parse(str) 171 | return err == nil && u.Scheme != "" && u.Host != "" 172 | } 173 | 174 | func downloadCert(url string) (retStatus bool) { 175 | resp, err := http.Get(url) 176 | if err != nil { 177 | fmt.Printf("Failed to download %s\n", err) 178 | return false 179 | } 180 | defer func(Body io.ReadCloser) { 181 | err := Body.Close() 182 | if err != nil { 183 | retStatus = false 184 | } 185 | }(resp.Body) 186 | 187 | out, err := os.Create(customCertTmpPath) 188 | if err != nil { 189 | fmt.Printf("Failed to create file %s\n", customCertTmpPath) 190 | return false 191 | } 192 | defer func(out *os.File) { 193 | err := out.Close() 194 | if err != nil { 195 | retStatus = false 196 | } 197 | }(out) 198 | 199 | _, err = io.Copy(out, resp.Body) 200 | if err != nil { 201 | fmt.Printf("Failed to copy cert to %s\n", customCertTmpPath) 202 | return false 203 | } 204 | return true 205 | } 206 | 207 | // shouldRetry returns true if the command should be re-executed. Currently 208 | // this only returns true if the remote ref does not exist. 209 | func shouldRetry(s string) bool { 210 | return strings.Contains(s, "find remote ref") 211 | } 212 | 213 | // retryExec is a helper function that retries a command. 214 | func retryExec(cmd *exec.Cmd, backoff time.Duration, retries int) (err error) { 215 | for i := 0; i < retries; i++ { 216 | // signal intent to retry 217 | fmt.Printf("retry in %v\n", backoff) 218 | 219 | // wait 5 seconds before retry 220 | <-time.After(backoff) 221 | 222 | // copy the original command 223 | retry := exec.Command(cmd.Args[0], cmd.Args[1:]...) 224 | retry.Dir = cmd.Dir 225 | retry.Env = cmd.Env 226 | retry.Stdout = os.Stdout 227 | retry.Stderr = os.Stderr 228 | trace(retry) 229 | err = retry.Run() 230 | if err == nil { 231 | return 232 | } 233 | } 234 | return 235 | } 236 | 237 | func appendEnv(cmd *exec.Cmd, env ...string) *exec.Cmd { 238 | cmd.Env = append(cmd.Env, env...) 239 | return cmd 240 | } 241 | 242 | // Creates an empty git repository. 243 | func initGit(branch, objectFormat string) *exec.Cmd { 244 | if branch == "" { 245 | return appendEnv(exec.Command("git", "init", "--object-format", objectFormat), defaultEnvVars...) 246 | } 247 | return appendEnv(exec.Command("git", "init", "--object-format", objectFormat, "-b", branch), defaultEnvVars...) 248 | } 249 | 250 | func safeDirectory(safeDirectory string) *exec.Cmd { 251 | return appendEnv(exec.Command("git", "config", "--global", "--replace-all", "safe.directory", safeDirectory), defaultEnvVars...) 252 | } 253 | 254 | // Use custom SSH Key thanks to core.sshCommand 255 | func sshKeyHandler(sshKey string) *exec.Cmd { 256 | return appendEnv(exec.Command("git", "config", "core.sshCommand", "ssh -i "+sshKey), defaultEnvVars...) 257 | } 258 | 259 | // Sets the remote origin for the repository. 260 | func remote(remote string) *exec.Cmd { 261 | return appendEnv(exec.Command( 262 | "git", 263 | "remote", 264 | "add", 265 | "origin", 266 | remote, 267 | ), defaultEnvVars...) 268 | } 269 | 270 | // Checkout executes a git checkout command. 271 | func checkoutHead() *exec.Cmd { 272 | return appendEnv(exec.Command( 273 | "git", 274 | "checkout", 275 | "-qf", 276 | "FETCH_HEAD", 277 | ), defaultEnvVars...) 278 | } 279 | 280 | // Checkout executes a git checkout command. 281 | func checkoutSha(commit string) *exec.Cmd { 282 | return appendEnv(exec.Command( 283 | "git", 284 | "reset", 285 | "--hard", 286 | "-q", 287 | commit, 288 | ), defaultEnvVars...) 289 | } 290 | 291 | // Switch executes a git switch command. 292 | func switchBranch(branch string) *exec.Cmd { 293 | return appendEnv(exec.Command( 294 | "git", 295 | "switch", 296 | "-q", 297 | branch, 298 | ), defaultEnvVars...) 299 | } 300 | 301 | func fetchLFS() *exec.Cmd { 302 | return appendEnv(exec.Command( 303 | "git", "lfs", 304 | "fetch", 305 | ), defaultEnvVars...) 306 | } 307 | 308 | func checkoutLFS() *exec.Cmd { 309 | return appendEnv(exec.Command( 310 | "git", "lfs", 311 | "checkout", 312 | ), defaultEnvVars...) 313 | } 314 | 315 | // fetch returns git command that fetches from origin. If tags is true 316 | // then tags will be fetched. 317 | func fetch(ref string, tags bool, depth int, filter string) *exec.Cmd { 318 | tags_option := "--no-tags" 319 | if tags { 320 | tags_option = "--tags" 321 | } 322 | cmd := exec.Command( 323 | "git", 324 | "fetch", 325 | tags_option, 326 | ) 327 | if depth != 0 { 328 | cmd.Args = append(cmd.Args, fmt.Sprintf("--depth=%d", depth)) 329 | } 330 | if filter != "" { 331 | cmd.Args = append(cmd.Args, "--filter="+filter) 332 | } 333 | cmd.Args = append(cmd.Args, "origin") 334 | cmd.Args = append(cmd.Args, fmt.Sprintf("+%s:", ref)) 335 | 336 | return appendEnv(cmd, defaultEnvVars...) 337 | } 338 | 339 | // updateSubmodules recursively initializes and updates submodules. 340 | func updateSubmodules(remote, partial bool) *exec.Cmd { 341 | args := []string{"submodule", "update", "--init", "--recursive"} 342 | if partial { 343 | args = append(args, "--depth=1", "--recommend-shallow") 344 | } 345 | cmd := exec.Command("git", args...) 346 | 347 | if remote { 348 | cmd.Args = append(cmd.Args, "--remote") 349 | } 350 | 351 | return appendEnv(cmd, defaultEnvVars...) 352 | } 353 | 354 | // skipVerify returns a git command that, when executed configures git to skip 355 | // ssl verification. This should may be used with self-signed certificates. 356 | func skipVerify() *exec.Cmd { 357 | return appendEnv(exec.Command( 358 | "git", 359 | "config", 360 | "--global", 361 | "http.sslVerify", 362 | "false", 363 | ), defaultEnvVars...) 364 | } 365 | 366 | func setCustomCert(path string) *exec.Cmd { 367 | return appendEnv(exec.Command( 368 | "git", 369 | "config", 370 | "--global", 371 | "http.sslCAInfo", 372 | path, 373 | ), defaultEnvVars...) 374 | } 375 | 376 | // remapSubmodule returns a git command that, when executed configures git to 377 | // remap submodule urls. 378 | func remapSubmodule(name, url string) *exec.Cmd { 379 | name = fmt.Sprintf("submodule.%s.url", name) 380 | return appendEnv(exec.Command( 381 | "git", 382 | "config", 383 | "--global", 384 | name, 385 | url, 386 | ), defaultEnvVars...) 387 | } 388 | 389 | func setHome(home string) error { 390 | // make sure home dir exist and is set 391 | homeExist, err := pathExists(home) 392 | if err != nil { 393 | return err 394 | } 395 | if !homeExist { 396 | return fmt.Errorf("home directory '%s' do not exist", home) 397 | } 398 | defaultEnvVars = append(defaultEnvVars, "HOME="+home) 399 | 400 | return nil 401 | } 402 | -------------------------------------------------------------------------------- /plugin_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | ) 8 | 9 | // commits is a list of commits of different types (push, pull request, tag) 10 | // to help us verify that this clone plugin can handle multiple commit types. 11 | var commits = []struct { 12 | path string 13 | clone string 14 | event string 15 | branch string 16 | commit string 17 | ref string 18 | file string 19 | data string 20 | dataSize int64 21 | recursive bool 22 | lfs bool 23 | }{ 24 | // first commit 25 | { 26 | path: "octocat/Hello-World", 27 | clone: "https://github.com/octocat/Hello-World.git", 28 | event: "push", 29 | branch: "master", 30 | commit: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e", 31 | ref: "", 32 | file: "README", 33 | data: "Hello World!", 34 | }, 35 | // head commit 36 | { 37 | path: "octocat/Hello-World", 38 | clone: "https://github.com/octocat/Hello-World.git", 39 | event: "push", 40 | branch: "master", 41 | commit: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", 42 | ref: "", 43 | file: "README", 44 | data: "Hello World!\n", 45 | }, 46 | // pull request commit 47 | { 48 | path: "octocat/Hello-World", 49 | clone: "https://github.com/octocat/Hello-World.git", 50 | event: "pull_request", 51 | branch: "master", 52 | commit: "762941318ee16e59dabbacb1b4049eec22f0d303", 53 | ref: "", 54 | file: "README", 55 | data: "Hello World!\n", 56 | }, 57 | // branch 58 | { 59 | path: "octocat/Hello-World", 60 | clone: "https://github.com/octocat/Hello-World.git", 61 | event: "push", 62 | branch: "test", 63 | commit: "b3cbd5bbd7e81436d2eee04537ea2b4c0cad4cdf", 64 | ref: "", 65 | file: "CONTRIBUTING.md", 66 | data: "## Contributing\n", 67 | }, 68 | // tags 69 | { 70 | path: "github/mime-types", 71 | clone: "https://github.com/github/mime-types.git", 72 | event: "tag", 73 | branch: "master", 74 | commit: "bf68d60215a167c935bc5976b7d06a7ffb290926", 75 | ref: "", 76 | file: ".gitignore", 77 | data: "*.swp\n*~\n.rake_tasks~\nhtml\ndoc\npkg\npublish\ncoverage\n", 78 | }, 79 | // submodules 80 | { 81 | path: "test-assets/woodpecker-git-test-submodule", 82 | clone: "https://github.com/test-assets/woodpecker-git-test-submodule.git", 83 | event: "push", 84 | branch: "main", 85 | commit: "cc020eb6aaa601c13ca7b0d5db9d1ca694e7a003", 86 | ref: "", 87 | file: "Hello-World/README", 88 | data: "Hello World!\n", 89 | recursive: true, 90 | }, 91 | // checkout with ref only 92 | { 93 | path: "octocat/Hello-World", 94 | clone: "https://github.com/octocat/Hello-World.git", 95 | event: "push", 96 | // commit: "a11fb45a696bf1d696fc9ab2c733f8f123aa4cf5", 97 | ref: "pull/2403/head", 98 | file: "README", 99 | data: "Hello World!\n\nsomething is changed!\n", 100 | }, 101 | // checkout with short SHA! 102 | { 103 | path: "octocat/Hello-World", 104 | clone: "https://github.com/octocat/Hello-World.git", 105 | event: "pull_request", 106 | branch: "test", 107 | commit: "7629413", 108 | ref: "", 109 | file: "README", 110 | data: "Hello World!\n", 111 | }, 112 | // ### test lfs, please do not change order, otherwise TestCloneNonEmpty will fail ### 113 | // checkout with lfs skip 114 | { 115 | path: "test-assets/woodpecker-git-test-lfs", 116 | clone: "https://github.com/test-assets/woodpecker-git-test-lfs.git", 117 | event: "push", 118 | commit: "69d4dadb4c2899efb73c0095bb58a6454d133cef", 119 | ref: "", 120 | file: "4M.bin", 121 | dataSize: 132, 122 | }, 123 | // checkout with lfs 124 | { 125 | path: "test-assets/woodpecker-git-test-lfs", 126 | clone: "https://github.com/test-assets/woodpecker-git-test-lfs.git", 127 | event: "push", 128 | commit: "69d4dadb4c2899efb73c0095bb58a6454d133cef", 129 | ref: "", 130 | file: "4M.bin", 131 | dataSize: 4194304, 132 | lfs: true, 133 | }, 134 | } 135 | 136 | // TestClone tests the ability to clone a specific commit into 137 | // a fresh, empty directory every time. 138 | func TestClone(t *testing.T) { 139 | for _, c := range commits { 140 | dir := setup() 141 | defer teardown(dir) 142 | 143 | plugin := Plugin{ 144 | Repo: Repo{ 145 | Clone: c.clone, 146 | }, 147 | Pipeline: Pipeline{ 148 | Path: filepath.Join(dir, c.path), 149 | Commit: c.commit, 150 | Event: c.event, 151 | Ref: c.ref, 152 | }, 153 | Config: Config{ 154 | Recursive: c.recursive, 155 | Lfs: c.lfs, 156 | Home: "/tmp", 157 | Branch: c.branch, 158 | }, 159 | } 160 | 161 | if err := plugin.Exec(); err != nil { 162 | t.Errorf("Expected successful clone. Got error. %s.", err) 163 | } 164 | 165 | if c.data != "" { 166 | data := readFile(plugin.Pipeline.Path, c.file) 167 | if data != c.data { 168 | t.Errorf("Expected %s to contain [%s]. Got [%s].", c.file, c.data, data) 169 | } 170 | } 171 | 172 | if c.dataSize != 0 { 173 | size := getFileSize(plugin.Pipeline.Path, c.file) 174 | if size != c.dataSize { 175 | t.Errorf("Expected %s size to be [%d]. Got [%d].", c.file, c.dataSize, size) 176 | } 177 | } 178 | 179 | } 180 | } 181 | 182 | // TestCloneNonEmpty tests the ability to clone a specific commit into 183 | // a non-empty directory. This is useful if the git workspace is cached 184 | // and re-stored for every workflow. 185 | func TestCloneNonEmpty(t *testing.T) { 186 | dir := setup() 187 | defer teardown(dir) 188 | 189 | for _, c := range commits { 190 | 191 | plugin := Plugin{ 192 | Repo: Repo{ 193 | Clone: c.clone, 194 | }, 195 | Pipeline: Pipeline{ 196 | Path: filepath.Join(dir, c.path), 197 | Commit: c.commit, 198 | Event: c.event, 199 | Ref: c.ref, 200 | }, 201 | Config: Config{ 202 | Recursive: c.recursive, 203 | Lfs: c.lfs, 204 | Home: "/tmp", 205 | Branch: c.branch, 206 | }, 207 | } 208 | 209 | if err := plugin.Exec(); err != nil { 210 | t.Errorf("Expected successful clone. Got error. %s.", err) 211 | } 212 | 213 | if c.data != "" { 214 | data := readFile(plugin.Pipeline.Path, c.file) 215 | if data != c.data { 216 | t.Errorf("Expected %s to contain [%s]. Got [%s].", c.file, c.data, data) 217 | break 218 | } 219 | } 220 | 221 | if c.dataSize != 0 { 222 | size := getFileSize(plugin.Pipeline.Path, c.file) 223 | if size != c.dataSize { 224 | t.Errorf("Expected %s size to be [%d]. Got [%d].", c.file, c.dataSize, size) 225 | } 226 | } 227 | } 228 | } 229 | 230 | // TestFetch tests if the arguments to `git fetch` are constructed properly. 231 | func TestFetch(t *testing.T) { 232 | testdata := []struct { 233 | ref string 234 | tags bool 235 | depth int 236 | exp []string 237 | }{ 238 | { 239 | "refs/heads/master", 240 | false, 241 | 0, 242 | []string{ 243 | "git", 244 | "fetch", 245 | "--no-tags", 246 | "origin", 247 | "+refs/heads/master:", 248 | }, 249 | }, 250 | { 251 | "refs/heads/master", 252 | false, 253 | 50, 254 | []string{ 255 | "git", 256 | "fetch", 257 | "--no-tags", 258 | "--depth=50", 259 | "origin", 260 | "+refs/heads/master:", 261 | }, 262 | }, 263 | { 264 | "refs/heads/master", 265 | true, 266 | 100, 267 | []string{ 268 | "git", 269 | "fetch", 270 | "--tags", 271 | "--depth=100", 272 | "origin", 273 | "+refs/heads/master:", 274 | }, 275 | }, 276 | } 277 | for _, td := range testdata { 278 | c := fetch(td.ref, td.tags, td.depth, "") 279 | if len(c.Args) != len(td.exp) { 280 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 281 | } 282 | for i := range c.Args { 283 | if c.Args[i] != td.exp[i] { 284 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 285 | } 286 | } 287 | } 288 | } 289 | 290 | // TestUpdateSubmodules tests if the arguments to `git submodule update` 291 | // are constructed properly. 292 | func TestUpdateSubmodules(t *testing.T) { 293 | testdata := []struct { 294 | partial bool 295 | exp []string 296 | }{ 297 | { 298 | false, 299 | []string{ 300 | "git", 301 | "submodule", 302 | "update", 303 | "--init", 304 | "--recursive", 305 | }, 306 | }, 307 | { 308 | true, 309 | []string{ 310 | "git", 311 | "submodule", 312 | "update", 313 | "--init", 314 | "--recursive", 315 | "--depth=1", 316 | "--recommend-shallow", 317 | }, 318 | }, 319 | } 320 | for _, td := range testdata { 321 | c := updateSubmodules(false, td.partial) 322 | if len(c.Args) != len(td.exp) { 323 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 324 | } 325 | for i := range c.Args { 326 | if c.Args[i] != td.exp[i] { 327 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 328 | } 329 | } 330 | } 331 | } 332 | 333 | func TestCustomCertUrl(t *testing.T) { 334 | testdata := []struct { 335 | exp []string 336 | }{ 337 | { 338 | []string{ 339 | "git", 340 | "config", 341 | "--global", 342 | "http.sslCAInfo", 343 | customCertTmpPath, 344 | }, 345 | }, 346 | } 347 | for _, td := range testdata { 348 | c := customCertHandler("http://example.com") 349 | if len(c.Args) != len(td.exp) { 350 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 351 | } 352 | for i := range c.Args { 353 | if c.Args[i] != td.exp[i] { 354 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 355 | } 356 | } 357 | } 358 | } 359 | 360 | func TestCustomCertFile(t *testing.T) { 361 | testdata := []struct { 362 | exp []string 363 | }{ 364 | { 365 | []string{ 366 | "git", 367 | "config", 368 | "--global", 369 | "http.sslCAInfo", 370 | "/etc/ssl/my-cert.pem", 371 | }, 372 | }, 373 | } 374 | 375 | for _, td := range testdata { 376 | c := customCertHandler("/etc/ssl/my-cert.pem") 377 | if len(c.Args) != len(td.exp) { 378 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 379 | } 380 | for i := range c.Args { 381 | if c.Args[i] != td.exp[i] { 382 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 383 | } 384 | } 385 | } 386 | } 387 | 388 | func TestSwitchBranch(t *testing.T) { 389 | testdata := []struct { 390 | exp []string 391 | }{ 392 | { 393 | []string{ 394 | "git", 395 | "switch", 396 | "-q", 397 | "test", 398 | }, 399 | }, 400 | } 401 | 402 | for _, td := range testdata { 403 | c := switchBranch("test") 404 | if len(c.Args) != len(td.exp) { 405 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 406 | } 407 | for i := range c.Args { 408 | if c.Args[i] != td.exp[i] { 409 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 410 | } 411 | } 412 | } 413 | } 414 | 415 | // TestUpdateSubmodules tests if the arguments to `git submodule update` 416 | // are constructed properly. 417 | func TestUpdateSubmodulesRemote(t *testing.T) { 418 | testdata := []struct { 419 | exp []string 420 | }{ 421 | { 422 | []string{ 423 | "git", 424 | "submodule", 425 | "update", 426 | "--init", 427 | "--recursive", 428 | "--remote", 429 | }, 430 | }, 431 | { 432 | []string{ 433 | "git", 434 | "submodule", 435 | "update", 436 | "--init", 437 | "--recursive", 438 | "--remote", 439 | }, 440 | }, 441 | } 442 | for _, td := range testdata { 443 | c := updateSubmodules(true, false) 444 | if len(c.Args) != len(td.exp) { 445 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 446 | } 447 | for i := range c.Args { 448 | if c.Args[i] != td.exp[i] { 449 | t.Errorf("Expected: %s, got %s", td.exp, c.Args) 450 | } 451 | } 452 | } 453 | } 454 | 455 | // helper function that will setup a temporary workspace. 456 | // to which we can clone the repositroy 457 | func setup() string { 458 | dir, _ := os.MkdirTemp("/tmp", "plugin_git_test_") 459 | os.Mkdir(dir, 0o777) 460 | return dir 461 | } 462 | 463 | // helper function to delete the temporary workspace. 464 | func teardown(dir string) { 465 | os.RemoveAll(dir) 466 | } 467 | 468 | // helper function to read a file in the temporary worskapce. 469 | func readFile(dir, file string) string { 470 | filename := filepath.Join(dir, file) 471 | data, _ := os.ReadFile(filename) 472 | return string(data) 473 | } 474 | 475 | func getFileSize(dir, file string) int64 { 476 | filename := filepath.Join(dir, file) 477 | fi, _ := os.Stat(filename) 478 | return fi.Size() 479 | } 480 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>woodpecker-ci/renovate-config"] 4 | } 5 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | type ( 9 | Repo struct { 10 | Clone string 11 | CloneSSH string 12 | ObjectFormat string 13 | } 14 | 15 | Pipeline struct { 16 | Path string 17 | Event string 18 | Number int 19 | Commit string 20 | Ref string 21 | } 22 | 23 | Netrc struct { 24 | Machine string 25 | Login string 26 | Password string 27 | } 28 | 29 | Config struct { 30 | Depth int 31 | Recursive bool 32 | SkipVerify bool 33 | Tags bool 34 | Submodules string 35 | SubmoduleRemote bool 36 | SubmodulePartial bool 37 | CustomCert string 38 | Lfs bool 39 | Branch string 40 | Home string 41 | Partial bool 42 | filter string 43 | SafeDirectory string 44 | UseSSH bool 45 | SSHKey string 46 | } 47 | 48 | Backoff struct { 49 | Attempts int 50 | Duration time.Duration 51 | } 52 | ) 53 | 54 | // below are special types used for unmarshaling structured data 55 | // from environment variable or command line args. 56 | 57 | type MapFlag struct { 58 | parts map[string]string 59 | } 60 | 61 | func (m *MapFlag) Get() map[string]string { 62 | return m.parts 63 | } 64 | 65 | func (m *MapFlag) Set(value string) error { 66 | m.parts = map[string]string{} 67 | return json.Unmarshal([]byte(value), &m.parts) 68 | } 69 | 70 | func (m *MapFlag) String() (s string) { 71 | return 72 | } 73 | -------------------------------------------------------------------------------- /umask.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package main 4 | 5 | import "syscall" 6 | 7 | func umask() { 8 | syscall.Umask(0) 9 | } 10 | -------------------------------------------------------------------------------- /umask_win.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package main 4 | 5 | func umask() { 6 | } 7 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // trace writes the command in the programs stdout for debug purposes. 13 | // the command is wrapped in xml tags for easy parsing. 14 | func trace(cmd *exec.Cmd) { 15 | fmt.Printf("+ %s\n", strings.Join(cmd.Args, " ")) 16 | } 17 | 18 | // pathExists returns whether the given file or directory exists or not 19 | func pathExists(path string) (bool, error) { 20 | _, err := os.Stat(path) 21 | if err == nil { 22 | return true, nil 23 | } 24 | if os.IsNotExist(err) { 25 | return false, nil 26 | } 27 | return true, err 28 | } 29 | 30 | // helper function returns true if directory dir is empty. 31 | func isDirEmpty(dir string) bool { 32 | f, err := os.Open(dir) 33 | if err != nil { 34 | return true 35 | } 36 | defer f.Close() 37 | 38 | _, err = f.Readdir(1) 39 | return err == io.EOF 40 | } 41 | 42 | // helper function to write a netrc file. 43 | func writeNetrc(home, machine, login, password string) error { 44 | if machine == "" || (login == "" && password == "") { 45 | return nil 46 | } 47 | out := fmt.Sprintf( 48 | netrcFile, 49 | machine, 50 | login, 51 | password, 52 | ) 53 | 54 | path := filepath.Join(home, ".netrc") 55 | return os.WriteFile(path, []byte(out), 0o600) 56 | } 57 | 58 | const netrcFile = ` 59 | machine %s 60 | login %s 61 | password %s 62 | ` 63 | --------------------------------------------------------------------------------