├── .drone.yml ├── .github ├── issue_template.md ├── pull_request_template.md └── settings.yml ├── .github_changelog_generator ├── .gitignore ├── .harness ├── eventPR.yaml ├── eventPush.yaml ├── eventTag.yaml └── harness.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── card.go ├── cmd ├── drone-acr │ └── main.go ├── drone-docker │ ├── custom_string_slice.go │ └── main.go ├── drone-ecr │ ├── main.go │ └── main_test.go ├── drone-gar │ └── main.go ├── drone-gcr │ └── main.go └── drone-heroku │ └── main.go ├── daemon.go ├── daemon_win.go ├── docker.go ├── docker ├── acr │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm64 │ ├── Dockerfile.windows.amd64.1809 │ ├── Dockerfile.windows.amd64.ltsc2022 │ └── manifest.tmpl ├── docker │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm64 │ ├── Dockerfile.windows.amd64.1809 │ ├── Dockerfile.windows.amd64.ltsc2022 │ └── manifest.tmpl ├── ecr │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm64 │ ├── Dockerfile.windows.amd64.1809 │ ├── Dockerfile.windows.amd64.ltsc2022 │ └── manifest.tmpl ├── gar │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm64 │ ├── Dockerfile.windows.amd64.1809 │ ├── Dockerfile.windows.amd64.ltsc2022 │ └── manifest.tmpl ├── gcr │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm64 │ ├── Dockerfile.windows.amd64.1809 │ ├── Dockerfile.windows.amd64.ltsc2022 │ └── manifest.tmpl └── heroku │ ├── Dockerfile.linux.amd64 │ ├── Dockerfile.linux.arm64 │ └── manifest.tmpl ├── docker_test.go ├── docs ├── card.data.json ├── card.json └── index.json ├── git-hooks ├── .gitleaksignore ├── README.md ├── hooks │ ├── git-leaks-pre-commit.sh │ ├── git-leaks.sh │ ├── pre-commit │ └── pre-push └── install.sh ├── go.mod ├── go.sum ├── internal ├── docker │ ├── config.go │ └── config_test.go └── gcp │ └── tokenutil.go ├── scripts └── windows │ ├── latest.ps1 │ └── tag.ps1 ├── tags.go └── tags_test.go /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | repository: 2 | name: drone-docker 3 | description: Drone plugin for publishing Docker images 4 | homepage: http://plugins.drone.io/drone-plugins/drone-docker 5 | topics: drone, drone-plugin 6 | 7 | private: false 8 | has_issues: true 9 | has_wiki: false 10 | has_downloads: false 11 | 12 | default_branch: master 13 | 14 | allow_squash_merge: true 15 | allow_merge_commit: true 16 | allow_rebase_merge: true 17 | 18 | labels: 19 | - name: bug 20 | color: d73a4a 21 | description: Something isn't working 22 | - name: duplicate 23 | color: cfd3d7 24 | description: This issue or pull request already exists 25 | - name: enhancement 26 | color: a2eeef 27 | description: New feature or request 28 | - name: good first issue 29 | color: 7057ff 30 | description: Good for newcomers 31 | - name: help wanted 32 | color: 008672 33 | description: Extra attention is needed 34 | - name: invalid 35 | color: e4e669 36 | description: This doesn't seem right 37 | - name: question 38 | color: d876e3 39 | description: Further information is requested 40 | - name: renovate 41 | color: e99695 42 | description: Automated action from Renovate 43 | - name: wontfix 44 | color: ffffff 45 | description: This will not be worked on 46 | 47 | teams: 48 | - name: Admins 49 | permission: admin 50 | 51 | branches: 52 | - name: master 53 | protection: 54 | required_pull_request_reviews: 55 | required_approving_review_count: 1 56 | dismiss_stale_reviews: false 57 | require_code_owner_reviews: false 58 | dismissal_restrictions: 59 | teams: 60 | - Admins 61 | required_status_checks: 62 | strict: true 63 | contexts: 64 | - continuous-integration/drone/pr 65 | enforce_admins: false 66 | restrictions: 67 | users: [] 68 | teams: 69 | - Admins 70 | -------------------------------------------------------------------------------- /.github_changelog_generator: -------------------------------------------------------------------------------- 1 | since-tag=v19.03.8 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | release 2 | coverage.out 3 | vendor 4 | .vscode/ 5 | Dockerfile 6 | -------------------------------------------------------------------------------- /.harness/eventPR.yaml: -------------------------------------------------------------------------------- 1 | inputSet: 2 | name: event-PR 3 | tags: {} 4 | identifier: eventPR 5 | orgIdentifier: default 6 | projectIdentifier: Drone_Plugins 7 | pipeline: 8 | identifier: dronedockerharness 9 | properties: 10 | ci: 11 | codebase: 12 | build: 13 | type: PR 14 | spec: 15 | number: <+trigger.prNumber> 16 | -------------------------------------------------------------------------------- /.harness/eventPush.yaml: -------------------------------------------------------------------------------- 1 | inputSet: 2 | name: event-Push 3 | tags: {} 4 | identifier: eventPush 5 | orgIdentifier: default 6 | projectIdentifier: Drone_Plugins 7 | pipeline: 8 | identifier: dronedockerharness 9 | properties: 10 | ci: 11 | codebase: 12 | build: 13 | type: branch 14 | spec: 15 | branch: <+trigger.branch> 16 | -------------------------------------------------------------------------------- /.harness/eventTag.yaml: -------------------------------------------------------------------------------- 1 | inputSet: 2 | name: event-Tag 3 | tags: {} 4 | identifier: eventTag 5 | orgIdentifier: default 6 | projectIdentifier: Drone_Plugins 7 | pipeline: 8 | identifier: dronedockerharness 9 | properties: 10 | ci: 11 | codebase: 12 | build: 13 | type: tag 14 | spec: 15 | tag: <+trigger.tag> 16 | -------------------------------------------------------------------------------- /.harness/harness.yaml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | orgIdentifier: default 3 | tags: {} 4 | properties: 5 | ci: 6 | codebase: 7 | connectorRef: GitHub_Drone_Plugins_Org 8 | repoName: drone-docker 9 | build: <+input> 10 | sparseCheckout: [] 11 | stages: 12 | - stage: 13 | name: Test 14 | identifier: Test 15 | description: "" 16 | type: CI 17 | spec: 18 | cloneCodebase: true 19 | caching: 20 | enabled: false 21 | paths: [] 22 | platform: 23 | os: Linux 24 | arch: Amd64 25 | runtime: 26 | type: Cloud 27 | spec: {} 28 | execution: 29 | steps: 30 | - step: 31 | type: Run 32 | name: GO VET 33 | identifier: Run_1 34 | spec: 35 | connectorRef: Plugins_Docker_Hub_Connector 36 | image: golang:1.23.0 37 | shell: Sh 38 | command: go vet ./... 39 | - step: 40 | type: Run 41 | name: GO TEST 42 | identifier: Run_2 43 | spec: 44 | connectorRef: Plugins_Docker_Hub_Connector 45 | image: golang:1.23.0 46 | shell: Sh 47 | command: go test -cover ./... 48 | - parallel: 49 | - stage: 50 | name: linux-amd64 51 | identifier: linamd64 52 | description: "" 53 | type: CI 54 | spec: 55 | cloneCodebase: true 56 | caching: 57 | enabled: false 58 | paths: [] 59 | platform: 60 | os: Linux 61 | arch: Amd64 62 | runtime: 63 | type: Cloud 64 | spec: {} 65 | execution: 66 | steps: 67 | - step: 68 | type: Run 69 | name: Build Binary 70 | identifier: Build_Push 71 | spec: 72 | connectorRef: Plugins_Docker_Hub_Connector 73 | image: golang:1.23.0 74 | shell: Sh 75 | command: go build -a -tags netgo -o release/linux/amd64/drone-<+matrix.repo> ./cmd/drone-<+matrix.repo> 76 | envVariables: 77 | CGO_ENABLED: "0" 78 | strategy: 79 | matrix: 80 | repo: 81 | - docker 82 | - gcr 83 | - gar 84 | - ecr 85 | - heroku 86 | - acr 87 | - step: 88 | type: Plugin 89 | name: Build and Push on Tag 90 | identifier: Docker_Build_and_Push 91 | spec: 92 | connectorRef: Plugins_Docker_Hub_Connector 93 | image: plugins/docker 94 | settings: 95 | username: drone 96 | password: <+secrets.getValue("Plugins_Docker_Hub_Pat")> 97 | repo: plugins/<+matrix.repo> 98 | dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64 99 | auto_tag: "true" 100 | auto_tag_suffix: linux-amd64 101 | when: 102 | stageStatus: Success 103 | condition: <+codebase.build.type> == "tag" 104 | strategy: 105 | matrix: 106 | repo: 107 | - docker 108 | - gcr 109 | - gar 110 | - ecr 111 | - heroku 112 | - acr 113 | - step: 114 | type: BuildAndPushDockerRegistry 115 | name: Build and Push on Branch 116 | identifier: BuildAndPushDockerRegistry_1 117 | spec: 118 | connectorRef: Plugins_Docker_Hub_Connector 119 | repo: plugins/<+matrix.repo> 120 | tags: 121 | - linux-amd64 122 | caching: false 123 | dockerfile: docker/<+matrix.repo>/Dockerfile.linux.amd64 124 | when: 125 | stageStatus: Success 126 | condition: <+codebase.build.type> == "branch" 127 | strategy: 128 | matrix: 129 | repo: 130 | - docker 131 | - gcr 132 | - gar 133 | - ecr 134 | - heroku 135 | - acr 136 | - stage: 137 | name: linux-arm64 138 | identifier: linarm64 139 | description: "" 140 | type: CI 141 | spec: 142 | cloneCodebase: true 143 | caching: 144 | enabled: false 145 | paths: [] 146 | platform: 147 | os: Linux 148 | arch: Arm64 149 | runtime: 150 | type: Cloud 151 | spec: {} 152 | execution: 153 | steps: 154 | - step: 155 | type: Run 156 | name: Build Binary 157 | identifier: buildpush 158 | spec: 159 | connectorRef: Plugins_Docker_Hub_Connector 160 | image: golang:1.23.0 161 | shell: Sh 162 | command: go build -a -tags netgo -o release/linux/arm64/drone-<+matrix.repo> ./cmd/drone-<+matrix.repo> 163 | envVariables: 164 | CGO_ENABLED: "0" 165 | strategy: 166 | matrix: 167 | repo: 168 | - docker 169 | - gcr 170 | - gar 171 | - ecr 172 | - heroku 173 | - acr 174 | - step: 175 | type: Plugin 176 | name: Build and Push on Tag 177 | identifier: Docker_Build_and_Push 178 | spec: 179 | connectorRef: Plugins_Docker_Hub_Connector 180 | image: plugins/docker 181 | settings: 182 | username: drone 183 | password: <+secrets.getValue("Plugins_Docker_Hub_Pat")> 184 | repo: plugins/<+matrix.repo> 185 | dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64 186 | auto_tag: "true" 187 | auto_tag_suffix: linux-arm64 188 | when: 189 | stageStatus: Success 190 | condition: <+codebase.build.type> == "tag" 191 | strategy: 192 | matrix: 193 | repo: 194 | - docker 195 | - gcr 196 | - gar 197 | - ecr 198 | - heroku 199 | - acr 200 | - step: 201 | type: BuildAndPushDockerRegistry 202 | name: Build and Push on Branch 203 | identifier: BuildAndPushDockerRegistry_1 204 | spec: 205 | connectorRef: Plugins_Docker_Hub_Connector 206 | repo: plugins/<+matrix.repo> 207 | tags: 208 | - linux-arm64 209 | caching: false 210 | dockerfile: docker/<+matrix.repo>/Dockerfile.linux.arm64 211 | when: 212 | stageStatus: Success 213 | condition: <+codebase.build.type> == "branch" 214 | strategy: 215 | matrix: 216 | repo: 217 | - docker 218 | - gcr 219 | - gar 220 | - ecr 221 | - heroku 222 | - acr 223 | - stage: 224 | name: win-1809-amd64 225 | identifier: win1809amd64 226 | description: "" 227 | type: CI 228 | spec: 229 | cloneCodebase: true 230 | caching: 231 | enabled: true 232 | infrastructure: 233 | type: VM 234 | spec: 235 | type: Pool 236 | spec: 237 | poolName: windows-2019 238 | os: Windows 239 | execution: 240 | steps: 241 | - step: 242 | type: Run 243 | name: Build Binary 244 | identifier: go_build 245 | spec: 246 | connectorRef: Plugins_Docker_Hub_Connector 247 | image: golang:1.23.0 248 | shell: Sh 249 | command: |- 250 | # disable cgo 251 | export CGO_ENABLED=0 252 | 253 | go build -o release/windows/amd64/drone-<+matrix.repo>.exe ./cmd/drone-<+matrix.repo> 254 | strategy: 255 | matrix: 256 | repo: 257 | - docker 258 | - gcr 259 | - gar 260 | - ecr 261 | - acr 262 | - step: 263 | type: Plugin 264 | name: Build and Push on Tag 265 | identifier: Docker_Build_and_Push1 266 | spec: 267 | connectorRef: Plugins_Docker_Hub_Connector 268 | image: plugins/docker 269 | settings: 270 | username: drone 271 | password: <+secrets.getValue("Plugins_Docker_Hub_Pat")> 272 | repo: plugins/<+matrix.repo> 273 | dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.1809 274 | auto_tag: "true" 275 | auto_tag_suffix: windows-1809-amd64 276 | when: 277 | stageStatus: Success 278 | condition: <+codebase.build.type> == "tag" 279 | strategy: 280 | matrix: 281 | repo: 282 | - docker 283 | - gcr 284 | - gar 285 | - ecr 286 | - acr 287 | - step: 288 | type: BuildAndPushDockerRegistry 289 | name: Build and Push on Branch 290 | identifier: BuildAndPushDockerRegistry_2 291 | spec: 292 | connectorRef: Plugins_Docker_Hub_Connector 293 | repo: plugins/<+matrix.repo> 294 | tags: 295 | - windows-1809-amd64 296 | caching: false 297 | dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.1809 298 | when: 299 | stageStatus: Success 300 | condition: <+codebase.build.type> == "branch" 301 | strategy: 302 | matrix: 303 | repo: 304 | - docker 305 | - gcr 306 | - gar 307 | - ecr 308 | - acr 309 | delegateSelectors: 310 | - windows-vm 311 | - stage: 312 | name: win-ltsc2022-amd64 313 | identifier: winamd64 314 | description: "" 315 | type: CI 316 | spec: 317 | cloneCodebase: true 318 | caching: 319 | enabled: false 320 | paths: [] 321 | platform: 322 | os: Windows 323 | arch: Amd64 324 | runtime: 325 | type: Cloud 326 | spec: {} 327 | execution: 328 | steps: 329 | - step: 330 | type: Run 331 | name: Build Binary -ltsc2022 332 | identifier: build_amd64ltsc2022 333 | spec: 334 | connectorRef: Plugins_Docker_Hub_Connector 335 | image: golang:1.23.0 336 | shell: Sh 337 | command: |- 338 | # disable cgo 339 | export CGO_ENABLED=0 340 | 341 | go build -o release/windows/amd64/drone-<+matrix.repo>.exe ./cmd/drone-<+matrix.repo> 342 | envVariables: 343 | CGO_ENABLED: "0" 344 | strategy: 345 | matrix: 346 | repo: 347 | - docker 348 | - gcr 349 | - gar 350 | - ecr 351 | - acr 352 | - step: 353 | type: Plugin 354 | name: Build and Push on Tag 355 | identifier: Docker_Build_and_Push1 356 | spec: 357 | connectorRef: Plugins_Docker_Hub_Connector 358 | image: plugins/docker 359 | settings: 360 | username: drone 361 | password: <+secrets.getValue("Plugins_Docker_Hub_Pat")> 362 | repo: plugins/<+matrix.repo> 363 | dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.ltsc2022 364 | auto_tag: "true" 365 | auto_tag_suffix: windows-ltsc2022-amd64 366 | when: 367 | stageStatus: Success 368 | condition: <+codebase.build.type> == "tag" 369 | strategy: 370 | matrix: 371 | repo: 372 | - docker 373 | - gcr 374 | - gar 375 | - ecr 376 | - acr 377 | - step: 378 | type: BuildAndPushDockerRegistry 379 | name: Build and Push on Branch 380 | identifier: BuildAndPushDockerRegistry_2 381 | spec: 382 | connectorRef: Plugins_Docker_Hub_Connector 383 | repo: plugins/<+matrix.repo> 384 | tags: 385 | - windows-ltsc2022-amd64 386 | caching: false 387 | dockerfile: docker/<+matrix.repo>/Dockerfile.windows.amd64.ltsc2022 388 | when: 389 | stageStatus: Success 390 | condition: <+codebase.build.type> == "branch" 391 | strategy: 392 | matrix: 393 | repo: 394 | - docker 395 | - gcr 396 | - gar 397 | - ecr 398 | - acr 399 | buildIntelligence: 400 | enabled: false 401 | - stage: 402 | name: Manifest and Release 403 | identifier: Manifest 404 | description: "" 405 | type: CI 406 | spec: 407 | cloneCodebase: true 408 | caching: 409 | enabled: false 410 | paths: [] 411 | execution: 412 | steps: 413 | - step: 414 | type: Plugin 415 | name: Manifest 416 | identifier: Plugin_1 417 | spec: 418 | connectorRef: Plugins_Docker_Hub_Connector 419 | image: plugins/manifest 420 | settings: 421 | username: drone 422 | password: <+secrets.getValue("Plugins_Docker_Hub_Pat")> 423 | auto_tag: "true" 424 | ignore_missing: "true" 425 | spec: docker/<+matrix.repo>/manifest.tmpl 426 | when: 427 | stageStatus: Success 428 | condition: <+codebase.build.type> == "tag" || <+codebase.build.type> == "branch" 429 | strategy: 430 | matrix: 431 | repo: 432 | - docker 433 | - gcr 434 | - gar 435 | - ecr 436 | - heroku 437 | - acr 438 | platform: 439 | os: Linux 440 | arch: Amd64 441 | runtime: 442 | type: Cloud 443 | spec: {} 444 | identifier: dronedockerharness 445 | projectIdentifier: Drone_Plugins 446 | name: drone-docker-harness 447 | allowStageExecutions: true 448 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v20.14.5](https://github.com/drone-plugins/drone-docker/tree/v20.14.5) (2023-09-13) 4 | 5 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.14.4...v20.14.5) 6 | 7 | **Implemented enhancements:** 8 | 9 | - Allow gcr authentication with workload identity [\#383](https://github.com/drone-plugins/drone-docker/pull/383) ([dhpollack](https://github.com/dhpollack)) 10 | 11 | **Fixed bugs:** 12 | 13 | - \[fix\]: \[ci-9254\]: go version upgrade to 1.21 [\#401](https://github.com/drone-plugins/drone-docker/pull/401) ([abhay084](https://github.com/abhay084)) 14 | - Revert "Add support for AAD auth for docker-acr" [\#398](https://github.com/drone-plugins/drone-docker/pull/398) ([tphoney](https://github.com/tphoney)) 15 | 16 | **Closed issues:** 17 | 18 | - Remove deprecated support of label-schema in favor of OCI [\#396](https://github.com/drone-plugins/drone-docker/issues/396) 19 | 20 | **Merged pull requests:** 21 | 22 | - Add support for AAD auth for docker-acr [\#395](https://github.com/drone-plugins/drone-docker/pull/395) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness)) 23 | 24 | ## [v20.14.4](https://github.com/drone-plugins/drone-docker/tree/v20.14.4) (2023-05-16) 25 | 26 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.14.3...v20.14.4) 27 | 28 | **Fixed bugs:** 29 | 30 | - fix: Use unique build name for build and tag [\#390](https://github.com/drone-plugins/drone-docker/pull/390) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness)) 31 | 32 | **Merged pull requests:** 33 | 34 | - v20.14.4 prep [\#391](https://github.com/drone-plugins/drone-docker/pull/391) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness)) 35 | 36 | ## [v20.14.3](https://github.com/drone-plugins/drone-docker/tree/v20.14.3) (2023-05-04) 37 | 38 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.14.2...v20.14.3) 39 | 40 | **Merged pull requests:** 41 | 42 | - Write artifacts to input artifact file [\#389](https://github.com/drone-plugins/drone-docker/pull/389) ([raghavharness](https://github.com/raghavharness)) 43 | 44 | ## [v20.14.2](https://github.com/drone-plugins/drone-docker/tree/v20.14.2) (2023-04-18) 45 | 46 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.14.1...v20.14.2) 47 | 48 | **Merged pull requests:** 49 | 50 | - fix windows drone yml [\#388](https://github.com/drone-plugins/drone-docker/pull/388) ([shubham149](https://github.com/shubham149)) 51 | 52 | ## [v20.14.1](https://github.com/drone-plugins/drone-docker/tree/v20.14.1) (2023-01-30) 53 | 54 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.14.0...v20.14.1) 55 | 56 | **Implemented enhancements:** 57 | 58 | - Add option to mount host ssh agent \(--ssh\) [\#382](https://github.com/drone-plugins/drone-docker/pull/382) ([tphoney](https://github.com/tphoney)) 59 | 60 | **Fixed bugs:** 61 | 62 | - windows 1809 docker build pin [\#384](https://github.com/drone-plugins/drone-docker/pull/384) ([tphoney](https://github.com/tphoney)) 63 | - \(maint\) move to harness.drone.io [\#381](https://github.com/drone-plugins/drone-docker/pull/381) ([tphoney](https://github.com/tphoney)) 64 | 65 | ## [v20.14.0](https://github.com/drone-plugins/drone-docker/tree/v20.14.0) (2022-11-17) 66 | 67 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.13.0...v20.14.0) 68 | 69 | **Implemented enhancements:** 70 | 71 | - Add support for docker --platform flag [\#376](https://github.com/drone-plugins/drone-docker/pull/376) ([tphoney](https://github.com/tphoney)) 72 | 73 | **Fixed bugs:** 74 | 75 | - Use full path to docker when creating card [\#373](https://github.com/drone-plugins/drone-docker/pull/373) ([donny-dont](https://github.com/donny-dont)) 76 | 77 | **Merged pull requests:** 78 | 79 | - \(maint\) prep for v20.14.0 [\#377](https://github.com/drone-plugins/drone-docker/pull/377) ([tphoney](https://github.com/tphoney)) 80 | 81 | ## [v20.13.0](https://github.com/drone-plugins/drone-docker/tree/v20.13.0) (2022-06-08) 82 | 83 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.12.0...v20.13.0) 84 | 85 | **Implemented enhancements:** 86 | 87 | - update docker linux amd64/arm64 to 20.10.14 [\#365](https://github.com/drone-plugins/drone-docker/pull/365) ([tphoney](https://github.com/tphoney)) 88 | 89 | **Merged pull requests:** 90 | 91 | - v20.13.0 prep [\#367](https://github.com/drone-plugins/drone-docker/pull/367) ([tphoney](https://github.com/tphoney)) 92 | 93 | ## [v20.12.0](https://github.com/drone-plugins/drone-docker/tree/v20.12.0) (2022-05-16) 94 | 95 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.11.0...v20.12.0) 96 | 97 | **Implemented enhancements:** 98 | 99 | - Add support for multiple Buildkit secrets with env vars or files as source [\#359](https://github.com/drone-plugins/drone-docker/pull/359) ([ste93cry](https://github.com/ste93cry)) 100 | - \(DRON-237\) cards add link to image repo, minor cleanup [\#358](https://github.com/drone-plugins/drone-docker/pull/358) ([tphoney](https://github.com/tphoney)) 101 | - \(DRON-232\) enable build-kit for secrets consumption [\#356](https://github.com/drone-plugins/drone-docker/pull/356) ([tphoney](https://github.com/tphoney)) 102 | 103 | **Fixed bugs:** 104 | 105 | - \(fix\) Update card.json with UX [\#355](https://github.com/drone-plugins/drone-docker/pull/355) ([tphoney](https://github.com/tphoney)) 106 | 107 | **Merged pull requests:** 108 | 109 | - prep for v20.12.0 [\#363](https://github.com/drone-plugins/drone-docker/pull/363) ([tphoney](https://github.com/tphoney)) 110 | 111 | ## [v20.11.0](https://github.com/drone-plugins/drone-docker/tree/v20.11.0) (2022-01-19) 112 | 113 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.10.9.1...v20.11.0) 114 | 115 | **Merged pull requests:** 116 | 117 | - new release to fix window semver error [\#354](https://github.com/drone-plugins/drone-docker/pull/354) ([eoinmcafee00](https://github.com/eoinmcafee00)) 118 | - \(feat\) publish docker data to create drone card [\#347](https://github.com/drone-plugins/drone-docker/pull/347) ([eoinmcafee00](https://github.com/eoinmcafee00)) 119 | 120 | ## [v20.10.9.1](https://github.com/drone-plugins/drone-docker/tree/v20.10.9.1) (2022-01-13) 121 | 122 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v20.10.9...v20.10.9.1) 123 | 124 | **Implemented enhancements:** 125 | 126 | - Serialize windows 1809 pipelines [\#348](https://github.com/drone-plugins/drone-docker/pull/348) ([shubham149](https://github.com/shubham149)) 127 | - Support for windows images for tags [\#346](https://github.com/drone-plugins/drone-docker/pull/346) ([shubham149](https://github.com/shubham149)) 128 | 129 | **Fixed bugs:** 130 | 131 | - Fix ECR & GCR docker publish on windows [\#352](https://github.com/drone-plugins/drone-docker/pull/352) ([shubham149](https://github.com/shubham149)) 132 | - Fix windows docker builds [\#351](https://github.com/drone-plugins/drone-docker/pull/351) ([shubham149](https://github.com/shubham149)) 133 | - Fix powershell script to publish windows images [\#350](https://github.com/drone-plugins/drone-docker/pull/350) ([shubham149](https://github.com/shubham149)) 134 | 135 | **Merged pull requests:** 136 | 137 | - release prep for 20.10.9.1 [\#353](https://github.com/drone-plugins/drone-docker/pull/353) ([eoinmcafee00](https://github.com/eoinmcafee00)) 138 | 139 | ## [v20.10.9](https://github.com/drone-plugins/drone-docker/tree/v20.10.9) (2021-11-03) 140 | 141 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v19.03.9...v20.10.9) 142 | 143 | **Merged pull requests:** 144 | 145 | - bump to version 20.10.9: [\#342](https://github.com/drone-plugins/drone-docker/pull/342) ([eoinmcafee00](https://github.com/eoinmcafee00)) 146 | - Upgrade Docker dind to 20.10.9 for 64bit platforms [\#334](https://github.com/drone-plugins/drone-docker/pull/334) ([gzm0](https://github.com/gzm0)) 147 | 148 | ## [v19.03.9](https://github.com/drone-plugins/drone-docker/tree/v19.03.9) (2021-10-13) 149 | 150 | [Full Changelog](https://github.com/drone-plugins/drone-docker/compare/v19.03.8...v19.03.9) 151 | 152 | **Implemented enhancements:** 153 | 154 | - adding support for externalId [\#333](https://github.com/drone-plugins/drone-docker/pull/333) ([jimsheldon](https://github.com/jimsheldon)) 155 | - Add support for automatic opencontainer labels [\#313](https://github.com/drone-plugins/drone-docker/pull/313) ([codrut-fc](https://github.com/codrut-fc)) 156 | - add custom seccomp profile [\#312](https://github.com/drone-plugins/drone-docker/pull/312) ([xoxys](https://github.com/xoxys)) 157 | - ECR: adding setting to enable image scanning while repo creation [\#300](https://github.com/drone-plugins/drone-docker/pull/300) ([rvoitenko](https://github.com/rvoitenko)) 158 | 159 | **Fixed bugs:** 160 | 161 | - Revert "Update seccomp to 20.10 docker" [\#325](https://github.com/drone-plugins/drone-docker/pull/325) ([bradrydzewski](https://github.com/bradrydzewski)) 162 | 163 | **Closed issues:** 164 | 165 | - Enable auth against multiple registries [\#324](https://github.com/drone-plugins/drone-docker/issues/324) 166 | - Parameter add\_host not work [\#318](https://github.com/drone-plugins/drone-docker/issues/318) 167 | - support customized Dockerfile name ? [\#315](https://github.com/drone-plugins/drone-docker/issues/315) 168 | - Tag wrongly gets parsed as octal [\#311](https://github.com/drone-plugins/drone-docker/issues/311) 169 | - Support TLS 1.3 [\#310](https://github.com/drone-plugins/drone-docker/issues/310) 170 | - Can pugin-docker access workspace content directly? [\#307](https://github.com/drone-plugins/drone-docker/issues/307) 171 | 172 | **Merged pull requests:** 173 | 174 | - \(maint\) bump git to 1.13 for build and test [\#338](https://github.com/drone-plugins/drone-docker/pull/338) ([tphoney](https://github.com/tphoney)) 175 | - \(maint\) v19.03.9 release prep [\#337](https://github.com/drone-plugins/drone-docker/pull/337) ([tphoney](https://github.com/tphoney)) 176 | - \(maint\) CI, remove the dry run steps, due to rate limiting [\#323](https://github.com/drone-plugins/drone-docker/pull/323) ([tphoney](https://github.com/tphoney)) 177 | - Update seccomp to 20.10 docker [\#322](https://github.com/drone-plugins/drone-docker/pull/322) ([techknowlogick](https://github.com/techknowlogick)) 178 | 179 | 180 | 181 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 182 | -------------------------------------------------------------------------------- /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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # drone-docker 2 | 3 | [![Build Status](http://cloud.drone.io/api/badges/drone-plugins/drone-docker/status.svg)](http://cloud.drone.io/drone-plugins/drone-docker) 4 | [![Gitter chat](https://badges.gitter.im/drone/drone.png)](https://gitter.im/drone/drone) 5 | [![Join the discussion at https://discourse.drone.io](https://img.shields.io/badge/discourse-forum-orange.svg)](https://discourse.drone.io) 6 | [![Drone questions at https://stackoverflow.com](https://img.shields.io/badge/drone-stackoverflow-orange.svg)](https://stackoverflow.com/questions/tagged/drone.io) 7 | [![](https://images.microbadger.com/badges/image/plugins/docker.svg)](https://microbadger.com/images/plugins/docker "Get your own image badge on microbadger.com") 8 | [![Go Doc](https://godoc.org/github.com/drone-plugins/drone-docker?status.svg)](http://godoc.org/github.com/drone-plugins/drone-docker) 9 | [![Go Report](https://goreportcard.com/badge/github.com/drone-plugins/drone-docker)](https://goreportcard.com/report/github.com/drone-plugins/drone-docker) 10 | 11 | Drone plugin uses Docker-in-Docker to build and publish Docker images to a container registry. For the usage information and a listing of the available options please take a look at [the docs](http://plugins.drone.io/drone-plugins/drone-docker/). 12 | 13 | ### Git Leaks 14 | 15 | Run the following script to install git-leaks support to this repo. 16 | ``` 17 | chmod +x ./git-hooks/install.sh 18 | ./git-hooks/install.sh 19 | ``` 20 | 21 | ## Build 22 | 23 | Build the binaries with the following commands: 24 | 25 | ```console 26 | export GOOS=linux 27 | export GOARCH=amd64 28 | export CGO_ENABLED=0 29 | export GO111MODULE=on 30 | 31 | go build -v -a -tags netgo -o release/linux/amd64/drone-docker ./cmd/drone-docker 32 | go build -v -a -tags netgo -o release/linux/amd64/drone-gcr ./cmd/drone-gcr 33 | go build -v -a -tags netgo -o release/linux/amd64/drone-ecr ./cmd/drone-ecr 34 | go build -v -a -tags netgo -o release/linux/amd64/drone-acr ./cmd/drone-acr 35 | go build -v -a -tags netgo -o release/linux/amd64/drone-heroku ./cmd/drone-heroku 36 | go build -v -a -tags netgo -o release/linux/amd64/drone-gar ./cmd/drone-gar 37 | ``` 38 | 39 | ## Docker 40 | 41 | Build the Docker images with the following commands: 42 | 43 | ```console 44 | docker build \ 45 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 46 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 47 | --file docker/docker/Dockerfile.linux.amd64 --tag plugins/docker . 48 | 49 | docker build \ 50 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 51 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 52 | --file docker/gcr/Dockerfile.linux.amd64 --tag plugins/gcr . 53 | 54 | docker build \ 55 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 56 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 57 | --file docker/ecr/Dockerfile.linux.amd64 --tag plugins/ecr . 58 | 59 | docker build \ 60 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 61 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 62 | --file docker/acr/Dockerfile.linux.amd64 --tag plugins/acr . 63 | 64 | docker build \ 65 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 66 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 67 | --file docker/heroku/Dockerfile.linux.amd64 --tag plugins/heroku . 68 | 69 | docker build \ 70 | --label org.label-schema.build-date=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ 71 | --label org.label-schema.vcs-ref=$(git rev-parse --short HEAD) \ 72 | --file docker/gar/Dockerfile.linux.amd64 --tag plugins/gar . 73 | ``` 74 | 75 | ## Usage 76 | 77 | > Notice: Be aware that the Docker plugin currently requires privileged capabilities, otherwise the integrated Docker daemon is not able to start. 78 | 79 | ### Using Docker buildkit Secrets 80 | 81 | ```yaml 82 | kind: pipeline 83 | name: default 84 | 85 | steps: 86 | - name: build dummy docker file and publish 87 | image: plugins/docker 88 | pull: never 89 | settings: 90 | repo: tphoney/test 91 | tags: latest 92 | secret: id=mysecret,src=secret-file 93 | username: 94 | from_secret: docker_username 95 | password: 96 | from_secret: docker_password 97 | ``` 98 | 99 | Using a dockerfile that references the secret-file 100 | 101 | ```bash 102 | # syntax=docker/dockerfile:1.2 103 | 104 | FROM alpine 105 | 106 | # shows secret from default secret location: 107 | RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret 108 | ``` 109 | 110 | and a secret file called secret-file 111 | 112 | ``` 113 | COOL BANANAS 114 | ``` 115 | 116 | 117 | ### Running from the CLI 118 | 119 | ```console 120 | docker run --rm \ 121 | -e PLUGIN_TAG=latest \ 122 | -e PLUGIN_REPO=octocat/hello-world \ 123 | -e DRONE_COMMIT_SHA=d8dbe4d94f15fe89232e0402c6e8a0ddf21af3ab \ 124 | -v $(pwd):$(pwd) \ 125 | -w $(pwd) \ 126 | --privileged \ 127 | plugins/docker --dry-run 128 | ``` 129 | 130 | ### GAR (Google Artifact Registry) 131 | 132 | ```yaml 133 | kind: pipeline 134 | name: default 135 | type: docker 136 | 137 | steps: 138 | - name: push-to-gar 139 | image: plugins/gar 140 | pull: never 141 | settings: 142 | tag: latest 143 | repo: project-id/repo/image-name 144 | location: us 145 | json_key: 146 | from_secret: gcr_json_key 147 | ``` 148 | 149 | ### GAR (Google Artifact Registry) using workload identity (OIDC) 150 | 151 | ```yaml 152 | steps: 153 | - name: push-to-gar 154 | image: plugins/gar 155 | pull: never 156 | settings: 157 | tag: latest 158 | repo: project-id/repo/image-name 159 | location: europe 160 | project_number: project-number 161 | pool_id: workload identity pool id 162 | provider_id: workload identity provider id 163 | service_account_email: service account email 164 | oidc_token_id: 165 | from_secret: token 166 | ``` 167 | 168 | ## Developer Notes 169 | 170 | - When updating the base image, you will need to update for each architecture and OS. 171 | - Arm32 base images are no longer being updated. 172 | 173 | ## Release procedure 174 | 175 | Run the changelog generator. 176 | 177 | ```BASH 178 | docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator -u drone-plugins -p drone-docker -t 179 | ``` 180 | 181 | You can generate a token by logging into your GitHub account and going to Settings -> Personal access tokens. 182 | 183 | Next we tag the PR's with the fixes or enhancements labels. If the PR does not fufil the requirements, do not add a label. 184 | 185 | Run the changelog generator again with the future version according to semver. 186 | 187 | ```BASH 188 | docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator -u drone-plugins -p drone-docker -t --future-release v1.0.0 189 | ``` 190 | 191 | Create your pull request for the release. Get it merged then tag the release. 192 | 193 | -------------------------------------------------------------------------------- /card.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "os/exec" 11 | "path" 12 | "strings" 13 | "time" 14 | 15 | "github.com/drone/drone-go/drone" 16 | 17 | "github.com/inhies/go-bytesize" 18 | ) 19 | 20 | func (p Plugin) writeCard() error { 21 | cmd := exec.Command(dockerExe, "inspect", p.Build.TempTag) 22 | data, err := cmd.CombinedOutput() 23 | if err != nil { 24 | return err 25 | } 26 | 27 | out := Card{} 28 | if err := json.Unmarshal(data, &out); err != nil { 29 | return err 30 | } 31 | 32 | inspect := out[0] 33 | inspect.SizeString = fmt.Sprint(bytesize.New(float64(inspect.Size))) 34 | inspect.VirtualSizeString = fmt.Sprint(bytesize.New(float64(inspect.VirtualSize))) 35 | inspect.Time = fmt.Sprint(inspect.Metadata.LastTagTime.Format(time.RFC3339)) 36 | // change slice of tags to slice of TagStruct 37 | var sliceTagStruct []TagStruct 38 | for _, tag := range inspect.RepoTags { 39 | sliceTagStruct = append(sliceTagStruct, TagStruct{Tag: tag}) 40 | } 41 | inspect.ParsedRepoTags = sliceTagStruct[1:] // remove the first tag which is always "hash:latest" 42 | // create the url from repo and registry 43 | inspect.URL = mapRegistryToURL(p.Daemon.Registry, p.Build.Repo) 44 | cardData, _ := json.Marshal(inspect) 45 | 46 | card := drone.CardInput{ 47 | Schema: "https://drone-plugins.github.io/drone-docker/card.json", 48 | Data: cardData, 49 | } 50 | 51 | writeCard(p.CardPath, &card) 52 | return nil 53 | } 54 | 55 | func writeCard(path string, card interface{}) { 56 | data, _ := json.Marshal(card) 57 | switch { 58 | case path == "/dev/stdout": 59 | writeCardTo(os.Stdout, data) 60 | case path == "/dev/stderr": 61 | writeCardTo(os.Stderr, data) 62 | case path != "": 63 | ioutil.WriteFile(path, data, 0644) 64 | } 65 | } 66 | 67 | func writeCardTo(out io.Writer, data []byte) { 68 | encoded := base64.StdEncoding.EncodeToString(data) 69 | io.WriteString(out, "\u001B]1338;") 70 | io.WriteString(out, encoded) 71 | io.WriteString(out, "\u001B]0m") 72 | io.WriteString(out, "\n") 73 | } 74 | 75 | func mapRegistryToURL(registry, repo string) (url string) { 76 | url = "https://" 77 | var domain string 78 | if strings.Contains(registry, "amazonaws.com") { 79 | domain = "gallery.ecr.aws/" 80 | } else if strings.Contains(registry, "gcr.io") { 81 | domain = "console.cloud.google.com/gcr/images" 82 | } else { 83 | // default to docker hub 84 | domain = "hub.docker.com/r/" 85 | } 86 | url = path.Join(url, domain, repo) 87 | return url 88 | } 89 | -------------------------------------------------------------------------------- /cmd/drone-acr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "encoding/json" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "strings" 15 | 16 | "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" 17 | "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 18 | "github.com/joho/godotenv" 19 | "github.com/pkg/errors" 20 | "github.com/sirupsen/logrus" 21 | 22 | docker "github.com/drone-plugins/drone-docker" 23 | ) 24 | 25 | type subscriptionUrlResponse struct { 26 | Value []struct { 27 | ID string `json:"id"` 28 | } `json:"value"` 29 | } 30 | 31 | const ( 32 | acrCertFile = "acr-cert.pem" 33 | azSubscriptionApiVersion = "2021-04-01" 34 | azSubscriptionBaseUrl = "https://management.azure.com/subscriptions/" 35 | basePublicUrl = "https://portal.azure.com/#view/Microsoft_Azure_ContainerRegistries/TagMetadataBlade/registryId/" 36 | defaultUsername = "00000000-0000-0000-0000-000000000000" 37 | 38 | // Environment variable names for Azure Environment Credential 39 | clientIdEnv = "AZURE_CLIENT_ID" 40 | clientSecretKeyEnv = "AZURE_CLIENT_SECRET" 41 | tenantKeyEnv = "AZURE_TENANT_ID" 42 | certPathEnv = "AZURE_CLIENT_CERTIFICATE_PATH" 43 | ) 44 | 45 | var ( 46 | acrCertPath = filepath.Join(os.TempDir(), acrCertFile) 47 | ) 48 | 49 | func main() { 50 | // Load env-file if it exists first 51 | if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { 52 | godotenv.Load(env) 53 | } 54 | 55 | var ( 56 | repo = getenv("PLUGIN_REPO") 57 | registry = getenv("PLUGIN_REGISTRY") 58 | 59 | // If these credentials are provided, they will be directly used 60 | // for docker login 61 | username = getenv("SERVICE_PRINCIPAL_CLIENT_ID") 62 | password = getenv("SERVICE_PRINCIPAL_CLIENT_SECRET") 63 | 64 | // Service principal credentials 65 | clientId = getenv("CLIENT_ID") 66 | clientSecret = getenv("CLIENT_SECRET") 67 | clientCert = getenv("CLIENT_CERTIFICATE") 68 | tenantId = getenv("TENANT_ID") 69 | subscriptionId = getenv("SUBSCRIPTION_ID") 70 | publicUrl = getenv("DAEMON_REGISTRY") 71 | ) 72 | 73 | // default registry value 74 | if registry == "" { 75 | registry = "azurecr.io" 76 | } 77 | 78 | // Get auth if username and password is not specified 79 | if username == "" && password == "" { 80 | // docker login credentials are not provided 81 | var err error 82 | username = defaultUsername 83 | password, publicUrl, err = getAuth(clientId, clientSecret, clientCert, tenantId, subscriptionId, registry) 84 | if err != nil { 85 | logrus.Fatal(err) 86 | } 87 | } 88 | 89 | // must use the fully qualified repo name. If the 90 | // repo name does not have the registry prefix we 91 | // should prepend. 92 | if !strings.HasPrefix(repo, registry) { 93 | repo = fmt.Sprintf("%s/%s", registry, repo) 94 | } 95 | 96 | os.Setenv("PLUGIN_REPO", repo) 97 | os.Setenv("PLUGIN_REGISTRY", registry) 98 | os.Setenv("DOCKER_USERNAME", username) 99 | os.Setenv("DOCKER_PASSWORD", password) 100 | os.Setenv("PLUGIN_REGISTRY_TYPE", "ACR") 101 | if publicUrl != "" { 102 | // Set this env variable if public URL for artifact is available 103 | // If not, we will fall back to registry url 104 | os.Setenv("ARTIFACT_REGISTRY", publicUrl) 105 | } 106 | 107 | // invoke the base docker plugin binary 108 | cmd := exec.Command(docker.GetDroneDockerExecCmd()) 109 | cmd.Stdout = os.Stdout 110 | cmd.Stderr = os.Stderr 111 | err := cmd.Run() 112 | if err != nil { 113 | logrus.Fatal(err) 114 | } 115 | } 116 | 117 | func getAuth(clientId, clientSecret, clientCert, tenantId, subscriptionId, registry string) (string, string, error) { 118 | // Verify inputs 119 | if tenantId == "" { 120 | return "", "", fmt.Errorf("tenantId cannot be empty for AAD authentication") 121 | } 122 | if clientId == "" { 123 | return "", "", fmt.Errorf("clientId cannot be empty for AAD authentication") 124 | } 125 | if clientSecret == "" && clientCert == "" { 126 | return "", "", fmt.Errorf("one of client secret or client cert should be defined") 127 | } 128 | 129 | // Setup cert 130 | if clientCert != "" { 131 | err := setupACRCert(clientCert, acrCertPath) 132 | if err != nil { 133 | errors.Wrap(err, "failed to push setup cert file") 134 | } 135 | } 136 | 137 | // Get AZ env 138 | if err := os.Setenv(clientIdEnv, clientId); err != nil { 139 | return "", "", errors.Wrap(err, "failed to set env variable client Id") 140 | } 141 | if err := os.Setenv(clientSecretKeyEnv, clientSecret); err != nil { 142 | return "", "", errors.Wrap(err, "failed to set env variable client secret") 143 | } 144 | if err := os.Setenv(tenantKeyEnv, tenantId); err != nil { 145 | return "", "", errors.Wrap(err, "failed to set env variable tenant Id") 146 | } 147 | if err := os.Setenv(certPathEnv, acrCertPath); err != nil { 148 | return "", "", errors.Wrap(err, "failed to set env variable cert path") 149 | } 150 | env, err := azidentity.NewEnvironmentCredential(nil) 151 | if err != nil { 152 | return "", "", errors.Wrap(err, "failed to get env credentials from azure") 153 | } 154 | os.Unsetenv(clientIdEnv) 155 | os.Unsetenv(clientSecretKeyEnv) 156 | os.Unsetenv(tenantKeyEnv) 157 | os.Unsetenv(certPathEnv) 158 | 159 | // Fetch AAD token 160 | policy := policy.TokenRequestOptions{ 161 | Scopes: []string{"https://management.azure.com/.default"}, 162 | } 163 | aadToken, err := env.GetToken(context.Background(), policy) 164 | if err != nil { 165 | return "", "", errors.Wrap(err, "failed to fetch access token") 166 | } 167 | 168 | // Get public URL for artifacts 169 | publicUrl, err := getPublicUrl(aadToken.Token, registry, subscriptionId) 170 | if err != nil { 171 | // execution should not fail because of this error 172 | fmt.Fprintf(os.Stderr, "failed to get public url with error: %s\n", err) 173 | } 174 | 175 | // Fetch token 176 | ACRToken, err := fetchACRToken(tenantId, aadToken.Token, registry) 177 | if err != nil { 178 | return "", "", errors.Wrap(err, "failed to fetch ACR token") 179 | } 180 | return ACRToken, publicUrl, nil 181 | } 182 | 183 | func fetchACRToken(tenantId, token, registry string) (string, error) { 184 | // oauth exchange 185 | formData := url.Values{ 186 | "grant_type": {"access_token"}, 187 | "service": {registry}, 188 | "tenant": {tenantId}, 189 | "access_token": {token}, 190 | } 191 | jsonResponse, err := http.PostForm(fmt.Sprintf("https://%s/oauth2/exchange", registry), formData) 192 | if err != nil || jsonResponse == nil { 193 | return "", errors.Wrap(err, "failed to fetch ACR token") 194 | } 195 | 196 | // fetch token from response 197 | var response map[string]interface{} 198 | err = json.NewDecoder(jsonResponse.Body).Decode(&response) 199 | if err != nil { 200 | return "", errors.Wrap(err, "failed to decode oauth exchange response") 201 | } 202 | 203 | // Parse the refresh_token from the response 204 | if t, found := response["refresh_token"]; found { 205 | if refreshToken, ok := t.(string); ok { 206 | return refreshToken, nil 207 | } 208 | return "", errors.New("failed to cast refresh token from acr") 209 | } 210 | return "", errors.Wrap(err, "refresh token not found in response of oauth exchange call") 211 | } 212 | 213 | func setupACRCert(cert, certPath string) error { 214 | decoded, err := base64.StdEncoding.DecodeString(cert) 215 | if err != nil { 216 | return errors.Wrap(err, "failed to base64 decode ACR certificate") 217 | } 218 | err = ioutil.WriteFile(certPath, decoded, 0644) 219 | if err != nil { 220 | return errors.Wrap(err, "failed to write ACR certificate") 221 | } 222 | return nil 223 | } 224 | 225 | func getPublicUrl(token, registryUrl, subscriptionId string) (string, error) { 226 | if len(subscriptionId) == 0 || registryUrl == "" { 227 | return "", nil 228 | } 229 | 230 | registry := strings.Split(registryUrl, ".")[0] 231 | filter := fmt.Sprintf("resourceType eq 'Microsoft.ContainerRegistry/registries' and name eq '%s'", registry) 232 | params := url.Values{} 233 | params.Add("$filter", filter) 234 | params.Add("api-version", azSubscriptionApiVersion) 235 | params.Add("$select", "id") 236 | url := azSubscriptionBaseUrl + subscriptionId + "/resources?" + params.Encode() 237 | 238 | client := &http.Client{} 239 | req, err := http.NewRequest("GET", url, nil) 240 | if err != nil { 241 | fmt.Println(err) 242 | return "", errors.Wrap(err, "failed to create request for getting container registry setting") 243 | } 244 | 245 | req.Header.Add("Authorization", "Bearer "+token) 246 | res, err := client.Do(req) 247 | if err != nil { 248 | fmt.Println(err) 249 | return "", errors.Wrap(err, "failed to send request for getting container registry setting") 250 | } 251 | defer res.Body.Close() 252 | 253 | var response subscriptionUrlResponse 254 | err = json.NewDecoder(res.Body).Decode(&response) 255 | if err != nil { 256 | return "", errors.Wrap(err, "failed to send request for getting container registry setting") 257 | } 258 | if len(response.Value) == 0 { 259 | return "", errors.New("no id present for base url") 260 | } 261 | return basePublicUrl + encodeParam(response.Value[0].ID), nil 262 | } 263 | 264 | func encodeParam(s string) string { 265 | return url.QueryEscape(s) 266 | } 267 | 268 | func getenv(key ...string) (s string) { 269 | for _, k := range key { 270 | s = os.Getenv(k) 271 | if s != "" { 272 | return 273 | } 274 | } 275 | return 276 | } 277 | -------------------------------------------------------------------------------- /cmd/drone-docker/custom_string_slice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // CustomStringSliceFlag is like a regular StringSlice flag but with 8 | // semicolon as a delimiter 9 | type CustomStringSliceFlag struct { 10 | Value []string 11 | } 12 | 13 | func (f *CustomStringSliceFlag) GetValue() []string { 14 | if f.Value == nil { 15 | return make([]string, 0) 16 | } 17 | return f.Value 18 | } 19 | 20 | func (f *CustomStringSliceFlag) String() string { 21 | if f.Value == nil { 22 | return "" 23 | } 24 | return strings.Join(f.Value, ";") 25 | } 26 | 27 | func (f *CustomStringSliceFlag) Set(v string) error { 28 | for _, s := range strings.Split(v, ";") { 29 | s = strings.TrimSpace(s) 30 | f.Value = append(f.Value, s) 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /cmd/drone-docker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "strings" 7 | 8 | "github.com/dchest/uniuri" 9 | "github.com/joho/godotenv" 10 | "github.com/sirupsen/logrus" 11 | "github.com/urfave/cli" 12 | 13 | docker "github.com/drone-plugins/drone-docker" 14 | "github.com/drone-plugins/drone-plugin-lib/drone" 15 | ) 16 | 17 | var ( 18 | version = "unknown" 19 | ) 20 | 21 | func main() { 22 | // Load env-file if it exists first 23 | if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { 24 | godotenv.Load(env) 25 | } 26 | 27 | app := cli.NewApp() 28 | app.Name = "docker plugin" 29 | app.Usage = "docker plugin" 30 | app.Action = run 31 | app.Version = version 32 | app.Flags = []cli.Flag{ 33 | cli.BoolFlag{ 34 | Name: "dry-run", 35 | Usage: "dry run disables docker push", 36 | EnvVar: "PLUGIN_DRY_RUN", 37 | }, 38 | cli.StringFlag{ 39 | Name: "remote.url", 40 | Usage: "git remote url", 41 | EnvVar: "DRONE_REMOTE_URL", 42 | }, 43 | cli.StringFlag{ 44 | Name: "commit.sha", 45 | Usage: "git commit sha", 46 | EnvVar: "DRONE_COMMIT_SHA", 47 | Value: "00000000", 48 | }, 49 | cli.StringFlag{ 50 | Name: "commit.ref", 51 | Usage: "git commit ref", 52 | EnvVar: "DRONE_COMMIT_REF", 53 | }, 54 | cli.StringFlag{ 55 | Name: "daemon.mirror", 56 | Usage: "docker daemon registry mirror", 57 | EnvVar: "PLUGIN_MIRROR,DOCKER_PLUGIN_MIRROR", 58 | }, 59 | cli.StringFlag{ 60 | Name: "daemon.storage-driver", 61 | Usage: "docker daemon storage driver", 62 | EnvVar: "PLUGIN_STORAGE_DRIVER", 63 | }, 64 | cli.StringFlag{ 65 | Name: "daemon.storage-path", 66 | Usage: "docker daemon storage path", 67 | Value: "/var/lib/docker", 68 | EnvVar: "PLUGIN_STORAGE_PATH", 69 | }, 70 | cli.StringFlag{ 71 | Name: "daemon.bip", 72 | Usage: "docker daemon bride ip address", 73 | EnvVar: "PLUGIN_BIP", 74 | }, 75 | cli.StringFlag{ 76 | Name: "daemon.mtu", 77 | Usage: "docker daemon custom mtu setting", 78 | EnvVar: "PLUGIN_MTU", 79 | }, 80 | cli.StringSliceFlag{ 81 | Name: "daemon.dns", 82 | Usage: "docker daemon dns server", 83 | EnvVar: "PLUGIN_CUSTOM_DNS", 84 | }, 85 | cli.StringSliceFlag{ 86 | Name: "daemon.dns-search", 87 | Usage: "docker daemon dns search domains", 88 | EnvVar: "PLUGIN_CUSTOM_DNS_SEARCH", 89 | }, 90 | cli.BoolFlag{ 91 | Name: "daemon.insecure", 92 | Usage: "docker daemon allows insecure registries", 93 | EnvVar: "PLUGIN_INSECURE", 94 | }, 95 | cli.BoolFlag{ 96 | Name: "daemon.ipv6", 97 | Usage: "docker daemon IPv6 networking", 98 | EnvVar: "PLUGIN_IPV6", 99 | }, 100 | cli.BoolFlag{ 101 | Name: "daemon.experimental", 102 | Usage: "docker daemon Experimental mode", 103 | EnvVar: "PLUGIN_EXPERIMENTAL", 104 | }, 105 | cli.BoolFlag{ 106 | Name: "daemon.debug", 107 | Usage: "docker daemon executes in debug mode", 108 | EnvVar: "PLUGIN_DEBUG,DOCKER_LAUNCH_DEBUG", 109 | }, 110 | cli.BoolFlag{ 111 | Name: "daemon.off", 112 | Usage: "don't start the docker daemon", 113 | EnvVar: "PLUGIN_DAEMON_OFF", 114 | }, 115 | cli.StringFlag{ 116 | Name: "dockerfile", 117 | Usage: "build dockerfile", 118 | Value: "Dockerfile", 119 | EnvVar: "PLUGIN_DOCKERFILE", 120 | }, 121 | cli.StringFlag{ 122 | Name: "context", 123 | Usage: "build context", 124 | Value: ".", 125 | EnvVar: "PLUGIN_CONTEXT", 126 | }, 127 | cli.StringSliceFlag{ 128 | Name: "tags", 129 | Usage: "build tags", 130 | Value: &cli.StringSlice{"latest"}, 131 | EnvVar: "PLUGIN_TAG,PLUGIN_TAGS", 132 | FilePath: ".tags", 133 | }, 134 | cli.BoolFlag{ 135 | Name: "tags.auto", 136 | Usage: "default build tags", 137 | EnvVar: "PLUGIN_DEFAULT_TAGS,PLUGIN_AUTO_TAG", 138 | }, 139 | cli.StringFlag{ 140 | Name: "tags.suffix", 141 | Usage: "default build tags with suffix", 142 | EnvVar: "PLUGIN_DEFAULT_SUFFIX,PLUGIN_AUTO_TAG_SUFFIX", 143 | }, 144 | cli.StringSliceFlag{ 145 | Name: "args", 146 | Usage: "build args", 147 | EnvVar: "PLUGIN_BUILD_ARGS", 148 | }, 149 | cli.StringSliceFlag{ 150 | Name: "args-from-env", 151 | Usage: "build args", 152 | EnvVar: "PLUGIN_BUILD_ARGS_FROM_ENV", 153 | }, 154 | cli.GenericFlag{ 155 | Name: "args-new", 156 | Usage: "build args new", 157 | EnvVar: "PLUGIN_BUILD_ARGS_NEW", 158 | Value: new(CustomStringSliceFlag), 159 | }, 160 | cli.BoolFlag{ 161 | Name: "plugin-multiple-build-agrs", 162 | Usage: "plugin multiple build agrs", 163 | EnvVar: "PLUGIN_MULTIPLE_BUILD_ARGS", 164 | }, 165 | cli.BoolFlag{ 166 | Name: "quiet", 167 | Usage: "quiet docker build", 168 | EnvVar: "PLUGIN_QUIET", 169 | }, 170 | cli.StringFlag{ 171 | Name: "target", 172 | Usage: "build target", 173 | EnvVar: "PLUGIN_TARGET", 174 | }, 175 | cli.StringSliceFlag{ 176 | Name: "cache-from", 177 | Usage: "images to consider as cache sources", 178 | EnvVar: "PLUGIN_CACHE_FROM", 179 | }, 180 | cli.BoolFlag{ 181 | Name: "squash", 182 | Usage: "squash the layers at build time", 183 | EnvVar: "PLUGIN_SQUASH", 184 | }, 185 | cli.BoolTFlag{ 186 | Name: "pull-image", 187 | Usage: "force pull base image at build time", 188 | EnvVar: "PLUGIN_PULL_IMAGE", 189 | }, 190 | cli.BoolFlag{ 191 | Name: "compress", 192 | Usage: "compress the build context using gzip", 193 | EnvVar: "PLUGIN_COMPRESS", 194 | }, 195 | cli.StringFlag{ 196 | Name: "repo", 197 | Usage: "docker repository", 198 | EnvVar: "PLUGIN_REPO", 199 | }, 200 | cli.StringSliceFlag{ 201 | Name: "custom-labels", 202 | Usage: "additional k=v labels", 203 | EnvVar: "PLUGIN_CUSTOM_LABELS", 204 | }, 205 | cli.StringSliceFlag{ 206 | Name: "label-schema", 207 | Usage: "label-schema labels", 208 | EnvVar: "PLUGIN_LABEL_SCHEMA", 209 | }, 210 | cli.BoolTFlag{ 211 | Name: "auto-label", 212 | Usage: "auto-label true|false", 213 | EnvVar: "PLUGIN_AUTO_LABEL", 214 | }, 215 | cli.StringFlag{ 216 | Name: "link", 217 | Usage: "link https://example.com/org/repo-name", 218 | EnvVar: "PLUGIN_REPO_LINK,DRONE_REPO_LINK", 219 | }, 220 | cli.StringFlag{ 221 | Name: "docker.registry", 222 | Usage: "docker registry", 223 | Value: "https://index.docker.io/v1/", 224 | EnvVar: "PLUGIN_REGISTRY,DOCKER_REGISTRY", 225 | }, 226 | cli.StringFlag{ 227 | Name: "docker.username", 228 | Usage: "docker username", 229 | EnvVar: "PLUGIN_USERNAME,DOCKER_USERNAME", 230 | }, 231 | cli.StringFlag{ 232 | Name: "docker.password", 233 | Usage: "docker password", 234 | EnvVar: "PLUGIN_PASSWORD,DOCKER_PASSWORD", 235 | }, 236 | cli.StringFlag{ 237 | Name: "docker.baseimageusername", 238 | Usage: "Docker username for base image registry", 239 | EnvVar: "PLUGIN_DOCKER_USERNAME,PLUGIN_BASE_IMAGE_USERNAME,DOCKER_BASE_IMAGE_USERNAME", 240 | }, 241 | cli.StringFlag{ 242 | Name: "docker.baseimagepassword", 243 | Usage: "Docker password for base image registry", 244 | EnvVar: "PLUGIN_DOCKER_PASSWORD,PLUGIN_BASE_IMAGE_PASSWORD,DOCKER_BASE_IMAGE_PASSWORD", 245 | }, 246 | cli.StringFlag{ 247 | Name: "docker.baseimageregistry", 248 | Usage: "Docker registry for base image registry", 249 | EnvVar: "PLUGIN_DOCKER_REGISTRY,PLUGIN_BASE_IMAGE_REGISTRY,DOCKER_BASE_IMAGE_REGISTRY", 250 | }, 251 | cli.StringFlag{ 252 | Name: "docker.email", 253 | Usage: "docker email", 254 | EnvVar: "PLUGIN_EMAIL,DOCKER_EMAIL", 255 | }, 256 | cli.StringFlag{ 257 | Name: "docker.config", 258 | Usage: "docker json dockerconfig content", 259 | EnvVar: "PLUGIN_CONFIG,DOCKER_PLUGIN_CONFIG", 260 | }, 261 | cli.BoolTFlag{ 262 | Name: "docker.purge", 263 | Usage: "docker should cleanup images", 264 | EnvVar: "PLUGIN_PURGE", 265 | }, 266 | cli.StringFlag{ 267 | Name: "repo.branch", 268 | Usage: "repository default branch", 269 | EnvVar: "DRONE_REPO_BRANCH", 270 | }, 271 | cli.BoolFlag{ 272 | Name: "no-cache", 273 | Usage: "do not use cached intermediate containers", 274 | EnvVar: "PLUGIN_NO_CACHE", 275 | }, 276 | cli.StringSliceFlag{ 277 | Name: "add-host", 278 | Usage: "additional host:IP mapping", 279 | EnvVar: "PLUGIN_ADD_HOST", 280 | }, 281 | cli.StringFlag{ 282 | Name: "secret", 283 | Usage: "secret key value pair eg id=MYSECRET", 284 | EnvVar: "PLUGIN_SECRET", 285 | }, 286 | cli.StringSliceFlag{ 287 | Name: "secrets-from-env", 288 | Usage: "secret key value pair eg secret_name=secret", 289 | EnvVar: "PLUGIN_SECRETS_FROM_ENV", 290 | }, 291 | cli.StringSliceFlag{ 292 | Name: "secrets-from-file", 293 | Usage: "secret key value pairs eg secret_name=/path/to/secret", 294 | EnvVar: "PLUGIN_SECRETS_FROM_FILE", 295 | }, 296 | cli.StringFlag{ 297 | Name: "drone-card-path", 298 | Usage: "card path location to write to", 299 | EnvVar: "DRONE_CARD_PATH", 300 | }, 301 | cli.StringFlag{ 302 | Name: "platform", 303 | Usage: "platform value to pass to docker", 304 | EnvVar: "PLUGIN_PLATFORM", 305 | }, 306 | cli.StringFlag{ 307 | Name: "ssh-agent-key", 308 | Usage: "ssh agent key to use", 309 | EnvVar: "PLUGIN_SSH_AGENT_KEY", 310 | }, 311 | cli.StringFlag{ 312 | Name: "artifact-file", 313 | Usage: "Artifact file location that will be generated by the plugin. This file will include information of docker images that are uploaded by the plugin.", 314 | EnvVar: "PLUGIN_ARTIFACT_FILE", 315 | }, 316 | cli.StringFlag{ 317 | Name: "registry-type", 318 | Usage: "registry type", 319 | EnvVar: "PLUGIN_REGISTRY_TYPE", 320 | }, 321 | cli.StringFlag{ 322 | Name: "access-token", 323 | Usage: "access token", 324 | EnvVar: "ACCESS_TOKEN", 325 | }, 326 | } 327 | 328 | if err := app.Run(os.Args); err != nil { 329 | logrus.Fatal(err) 330 | } 331 | } 332 | 333 | func run(c *cli.Context) error { 334 | registryType := drone.Docker 335 | if c.String("registry-type") != "" { 336 | registryType = drone.RegistryType(c.String("registry-type")) 337 | } 338 | 339 | plugin := docker.Plugin{ 340 | Dryrun: c.Bool("dry-run"), 341 | Cleanup: c.BoolT("docker.purge"), 342 | Login: docker.Login{ 343 | Registry: c.String("docker.registry"), 344 | Username: c.String("docker.username"), 345 | Password: c.String("docker.password"), 346 | Email: c.String("docker.email"), 347 | Config: c.String("docker.config"), 348 | AccessToken: c.String("access-token"), 349 | }, 350 | CardPath: c.String("drone-card-path"), 351 | ArtifactFile: c.String("artifact-file"), 352 | Build: docker.Build{ 353 | Remote: c.String("remote.url"), 354 | Name: c.String("commit.sha"), 355 | TempTag: generateTempTag(), 356 | Dockerfile: c.String("dockerfile"), 357 | Context: c.String("context"), 358 | Tags: c.StringSlice("tags"), 359 | Args: c.StringSlice("args"), 360 | ArgsEnv: c.StringSlice("args-from-env"), 361 | ArgsNew: c.Generic("args-new").(*CustomStringSliceFlag).GetValue(), 362 | IsMultipleBuildArgs: c.Bool("plugin-multiple-build-agrs"), 363 | Target: c.String("target"), 364 | Squash: c.Bool("squash"), 365 | Pull: c.BoolT("pull-image"), 366 | CacheFrom: c.StringSlice("cache-from"), 367 | Compress: c.Bool("compress"), 368 | Repo: c.String("repo"), 369 | Labels: c.StringSlice("custom-labels"), 370 | LabelSchema: c.StringSlice("label-schema"), 371 | AutoLabel: c.BoolT("auto-label"), 372 | Link: c.String("link"), 373 | NoCache: c.Bool("no-cache"), 374 | Secret: c.String("secret"), 375 | SecretEnvs: c.StringSlice("secrets-from-env"), 376 | SecretFiles: c.StringSlice("secrets-from-file"), 377 | AddHost: c.StringSlice("add-host"), 378 | Quiet: c.Bool("quiet"), 379 | Platform: c.String("platform"), 380 | SSHAgentKey: c.String("ssh-agent-key"), 381 | }, 382 | Daemon: docker.Daemon{ 383 | Registry: c.String("docker.registry"), 384 | Mirror: c.String("daemon.mirror"), 385 | StorageDriver: c.String("daemon.storage-driver"), 386 | StoragePath: c.String("daemon.storage-path"), 387 | Insecure: c.Bool("daemon.insecure"), 388 | Disabled: c.Bool("daemon.off"), 389 | IPv6: c.Bool("daemon.ipv6"), 390 | Debug: c.Bool("daemon.debug"), 391 | Bip: c.String("daemon.bip"), 392 | DNS: c.StringSlice("daemon.dns"), 393 | DNSSearch: c.StringSlice("daemon.dns-search"), 394 | MTU: c.String("daemon.mtu"), 395 | Experimental: c.Bool("daemon.experimental"), 396 | RegistryType: registryType, 397 | }, 398 | BaseImageRegistry: c.String("docker.baseimageregistry"), 399 | BaseImageUsername: c.String("docker.baseimageusername"), 400 | BaseImagePassword: c.String("docker.baseimagepassword"), 401 | } 402 | 403 | if c.Bool("tags.auto") { 404 | if docker.UseDefaultTag( // return true if tag event or default branch 405 | c.String("commit.ref"), 406 | c.String("repo.branch"), 407 | ) { 408 | tag, err := docker.DefaultTagSuffix( 409 | c.String("commit.ref"), 410 | c.String("tags.suffix"), 411 | ) 412 | if err != nil { 413 | logrus.Printf("cannot build docker image for %s, invalid semantic version", c.String("commit.ref")) 414 | return err 415 | } 416 | plugin.Build.Tags = tag 417 | } else { 418 | logrus.Printf("skipping automated docker build for %s", c.String("commit.ref")) 419 | return nil 420 | } 421 | } 422 | 423 | return plugin.Exec() 424 | } 425 | 426 | func generateTempTag() string { 427 | return strings.ToLower(uniuri.New()) 428 | } 429 | 430 | func GetExecCmd() string { 431 | if runtime.GOOS == "windows" { 432 | return "C:/bin/drone-docker.exe" 433 | } 434 | 435 | return "drone-docker" 436 | } 437 | -------------------------------------------------------------------------------- /cmd/drone-ecr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "strconv" 11 | "strings" 12 | 13 | "github.com/joho/godotenv" 14 | "github.com/sirupsen/logrus" 15 | 16 | "github.com/aws/aws-sdk-go/aws" 17 | "github.com/aws/aws-sdk-go/aws/awserr" 18 | "github.com/aws/aws-sdk-go/aws/credentials/stscreds" 19 | "github.com/aws/aws-sdk-go/aws/session" 20 | "github.com/aws/aws-sdk-go/service/ecr" 21 | 22 | docker "github.com/drone-plugins/drone-docker" 23 | ) 24 | 25 | type ecrAPI interface { 26 | DescribeImages(*ecr.DescribeImagesInput) (*ecr.DescribeImagesOutput, error) 27 | } 28 | 29 | const defaultRegion = "us-east-1" 30 | 31 | func main() { 32 | // Load env-file if it exists first 33 | if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { 34 | godotenv.Load(env) 35 | } 36 | 37 | var ( 38 | repo = getenv("PLUGIN_REPO") 39 | registry = getenv("PLUGIN_REGISTRY") 40 | region = getenv("PLUGIN_REGION", "ECR_REGION", "AWS_REGION") 41 | key = getenv("PLUGIN_ACCESS_KEY", "ECR_ACCESS_KEY", "AWS_ACCESS_KEY_ID") 42 | secret = getenv("PLUGIN_SECRET_KEY", "ECR_SECRET_KEY", "AWS_SECRET_ACCESS_KEY") 43 | create = parseBoolOrDefault(false, getenv("PLUGIN_CREATE_REPOSITORY", "ECR_CREATE_REPOSITORY")) 44 | lifecyclePolicy = getenv("PLUGIN_LIFECYCLE_POLICY") 45 | repositoryPolicy = getenv("PLUGIN_REPOSITORY_POLICY") 46 | assumeRole = getenv("PLUGIN_ASSUME_ROLE") 47 | externalId = getenv("PLUGIN_EXTERNAL_ID") 48 | scanOnPush = parseBoolOrDefault(false, getenv("PLUGIN_SCAN_ON_PUSH")) 49 | idToken = os.Getenv("PLUGIN_OIDC_TOKEN_ID") 50 | skipPushIfTagExists = parseBoolOrDefault(false, getenv("PLUGIN_SKIP_PUSH_IF_TAG_EXISTS")) 51 | ) 52 | 53 | // set the region 54 | if region == "" { 55 | region = defaultRegion 56 | } 57 | 58 | os.Setenv("AWS_REGION", region) 59 | 60 | if key != "" && secret != "" { 61 | os.Setenv("AWS_ACCESS_KEY_ID", key) 62 | os.Setenv("AWS_SECRET_ACCESS_KEY", secret) 63 | } 64 | 65 | sess, err := session.NewSession(&aws.Config{Region: ®ion}) 66 | if err != nil { 67 | log.Fatal(fmt.Sprintf("error creating aws session: %v", err)) 68 | } 69 | 70 | svc := getECRClient(sess, assumeRole, externalId, idToken) 71 | username, password, defaultRegistry, err := getAuthInfo(svc) 72 | 73 | if registry == "" { 74 | registry = defaultRegistry 75 | } 76 | 77 | if err != nil { 78 | log.Fatal(fmt.Sprintf("error getting ECR auth: %v", err)) 79 | } 80 | 81 | if !strings.HasPrefix(repo, registry) { 82 | repo = fmt.Sprintf("%s/%s", registry, repo) 83 | } 84 | 85 | if create { 86 | err = ensureRepoExists(svc, trimHostname(repo, registry), scanOnPush) 87 | if err != nil { 88 | log.Fatal(fmt.Sprintf("error creating ECR repo: %v", err)) 89 | } 90 | err = updateImageScannningConfig(svc, trimHostname(repo, registry), scanOnPush) 91 | if err != nil { 92 | log.Fatal(fmt.Sprintf("error updating scan on push for ECR repo: %v", err)) 93 | } 94 | } 95 | 96 | if lifecyclePolicy != "" { 97 | p, err := ioutil.ReadFile(lifecyclePolicy) 98 | if err != nil { 99 | log.Fatal(err) 100 | } 101 | if err := uploadLifeCyclePolicy(svc, string(p), trimHostname(repo, registry)); err != nil { 102 | log.Fatal(fmt.Sprintf("error uploading ECR lifecycle policy: %v", err)) 103 | } 104 | } 105 | 106 | if repositoryPolicy != "" { 107 | p, err := ioutil.ReadFile(repositoryPolicy) 108 | if err != nil { 109 | log.Fatal(err) 110 | } 111 | if err := uploadRepositoryPolicy(svc, string(p), trimHostname(repo, registry)); err != nil { 112 | log.Fatal(fmt.Sprintf("error uploading ECR repository policy. %v", err)) 113 | } 114 | } 115 | 116 | os.Setenv("PLUGIN_REPO", repo) 117 | os.Setenv("PLUGIN_REGISTRY", registry) 118 | os.Setenv("DOCKER_USERNAME", username) 119 | os.Setenv("DOCKER_PASSWORD", password) 120 | os.Setenv("PLUGIN_REGISTRY_TYPE", "ECR") 121 | 122 | // Skip if tag already exits for both mutable and immutable repos 123 | if skipPushIfTagExists { 124 | tagInput := getenv("PLUGIN_TAG", "PLUGIN_TAGS") 125 | var tags []string 126 | if tagInput == "" { 127 | tags = []string{"latest"} 128 | } else { 129 | for _, t := range strings.Split(tagInput, ",") { 130 | trimmed := strings.TrimSpace(t) 131 | if trimmed != "" { 132 | tags = append(tags, trimmed) 133 | } 134 | } 135 | } 136 | 137 | repositoryName := trimHostname(repo, registry) 138 | for _, t := range tags { 139 | exists, err := tagExists(svc, repositoryName, t) 140 | if err != nil { 141 | logrus.Fatalf("Error checking if image exists for tag %s: %v", t, err) 142 | } 143 | if exists { 144 | logrus.Infof("%s:%s: Image tag exists. Skipping push.", repo, t) 145 | os.Exit(0) 146 | } 147 | } 148 | } 149 | 150 | // invoke the base docker plugin binary 151 | cmd := exec.Command(docker.GetDroneDockerExecCmd()) 152 | cmd.Stdout = os.Stdout 153 | cmd.Stderr = os.Stderr 154 | if err = cmd.Run(); err != nil { 155 | logrus.Fatal(err) 156 | } 157 | } 158 | 159 | func trimHostname(repo, registry string) string { 160 | repo = strings.TrimPrefix(repo, registry) 161 | repo = strings.TrimLeft(repo, "/") 162 | return repo 163 | } 164 | 165 | func ensureRepoExists(svc *ecr.ECR, name string, scanOnPush bool) (err error) { 166 | input := &ecr.CreateRepositoryInput{} 167 | input.SetRepositoryName(name) 168 | input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush}) 169 | _, err = svc.CreateRepository(input) 170 | if err != nil { 171 | if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ecr.ErrCodeRepositoryAlreadyExistsException { 172 | // eat it, we skip checking for existing to save two requests 173 | err = nil 174 | } 175 | } 176 | 177 | return 178 | } 179 | 180 | func updateImageScannningConfig(svc *ecr.ECR, name string, scanOnPush bool) (err error) { 181 | input := &ecr.PutImageScanningConfigurationInput{} 182 | input.SetRepositoryName(name) 183 | input.SetImageScanningConfiguration(&ecr.ImageScanningConfiguration{ScanOnPush: &scanOnPush}) 184 | _, err = svc.PutImageScanningConfiguration(input) 185 | 186 | return err 187 | } 188 | 189 | func uploadLifeCyclePolicy(svc *ecr.ECR, lifecyclePolicy string, name string) (err error) { 190 | input := &ecr.PutLifecyclePolicyInput{} 191 | input.SetLifecyclePolicyText(lifecyclePolicy) 192 | input.SetRepositoryName(name) 193 | _, err = svc.PutLifecyclePolicy(input) 194 | 195 | return err 196 | } 197 | 198 | func uploadRepositoryPolicy(svc *ecr.ECR, repositoryPolicy string, name string) (err error) { 199 | input := &ecr.SetRepositoryPolicyInput{} 200 | input.SetPolicyText(repositoryPolicy) 201 | input.SetRepositoryName(name) 202 | _, err = svc.SetRepositoryPolicy(input) 203 | 204 | return err 205 | } 206 | 207 | func getAuthInfo(svc *ecr.ECR) (username, password, registry string, err error) { 208 | var result *ecr.GetAuthorizationTokenOutput 209 | var decoded []byte 210 | 211 | result, err = svc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{}) 212 | if err != nil { 213 | return 214 | } 215 | 216 | auth := result.AuthorizationData[0] 217 | token := *auth.AuthorizationToken 218 | decoded, err = base64.StdEncoding.DecodeString(token) 219 | if err != nil { 220 | return 221 | } 222 | 223 | registry = strings.TrimPrefix(*auth.ProxyEndpoint, "https://") 224 | creds := strings.Split(string(decoded), ":") 225 | username = creds[0] 226 | password = creds[1] 227 | return 228 | } 229 | 230 | func parseBoolOrDefault(defaultValue bool, s string) (result bool) { 231 | var err error 232 | result, err = strconv.ParseBool(s) 233 | if err != nil { 234 | result = defaultValue 235 | } 236 | 237 | return 238 | } 239 | 240 | func getenv(key ...string) (s string) { 241 | for _, k := range key { 242 | s = os.Getenv(k) 243 | if s != "" { 244 | return 245 | } 246 | } 247 | return 248 | } 249 | 250 | func getECRClient(sess *session.Session, role string, externalId string, idToken string) *ecr.ECR { 251 | if role == "" { 252 | return ecr.New(sess) 253 | } 254 | 255 | if idToken != "" { 256 | tempFile, err := os.CreateTemp("/tmp", "idToken-*.jwt") 257 | if err != nil { 258 | log.Fatalf("Failed to create temporary file: %v", err) 259 | } 260 | defer tempFile.Close() 261 | 262 | if err := os.Chmod(tempFile.Name(), 0600); err != nil { 263 | log.Fatalf("Failed to set file permissions: %v", err) 264 | } 265 | 266 | if _, err := tempFile.WriteString(idToken); err != nil { 267 | log.Fatalf("Failed to write ID token to temporary file: %v", err) 268 | } 269 | 270 | // Create credentials using the path to the ID token file 271 | creds := stscreds.NewWebIdentityCredentials(sess, role, "", tempFile.Name()) 272 | return ecr.New(sess, &aws.Config{Credentials: creds}) 273 | } else if externalId != "" { 274 | return ecr.New(sess, &aws.Config{ 275 | Credentials: stscreds.NewCredentials(sess, role, func(p *stscreds.AssumeRoleProvider) { 276 | p.ExternalID = &externalId 277 | }), 278 | }) 279 | } else { 280 | return ecr.New(sess, &aws.Config{ 281 | Credentials: stscreds.NewCredentials(sess, role), 282 | }) 283 | } 284 | } 285 | 286 | func tagExists(svc ecrAPI, repository, tag string) (bool, error) { 287 | input := &ecr.DescribeImagesInput{ 288 | RepositoryName: aws.String(repository), 289 | ImageIds: []*ecr.ImageIdentifier{ 290 | {ImageTag: aws.String(tag)}, 291 | }, 292 | } 293 | output, err := svc.DescribeImages(input) 294 | if err != nil { 295 | if aerr, ok := err.(awserr.Error); ok && aerr.Code() == "ImageNotFoundException" { 296 | return false, nil 297 | } 298 | return false, err 299 | } 300 | return len(output.ImageDetails) > 0, nil 301 | } 302 | -------------------------------------------------------------------------------- /cmd/drone-ecr/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func TestTrimHostname(t *testing.T) { 6 | registry := "000000000000.dkr.ecr.us-east-1.amazonaws.com" 7 | // map full repo path to expected repo name 8 | repos := map[string]string{ 9 | registry + "/repo": "repo", 10 | registry + "/namespace/repo": "namespace/repo", 11 | registry + "/namespace/namespace/repo": "namespace/namespace/repo", 12 | } 13 | 14 | for repo, name := range repos { 15 | splitName := trimHostname(repo, registry) 16 | if splitName != name { 17 | t.Errorf("%s is not equal to %s.", splitName, name) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cmd/drone-gar/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "fmt" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "path" 11 | "strconv" 12 | "strings" 13 | 14 | docker "github.com/drone-plugins/drone-docker" 15 | "github.com/drone-plugins/drone-docker/internal/gcp" 16 | 17 | "github.com/joho/godotenv" 18 | "github.com/sirupsen/logrus" 19 | "golang.org/x/oauth2" 20 | "golang.org/x/oauth2/google" 21 | ) 22 | 23 | type Config struct { 24 | Repo string 25 | Registry string 26 | Password string 27 | WorkloadIdentity bool 28 | Username string 29 | AccessToken string 30 | } 31 | 32 | type staticTokenSource struct { 33 | token *oauth2.Token 34 | } 35 | 36 | func (s *staticTokenSource) Token() (*oauth2.Token, error) { 37 | return s.token, nil 38 | } 39 | 40 | func loadConfig() Config { 41 | // Default username 42 | username := "_json_key" 43 | var config Config 44 | 45 | // Load env-file if it exists 46 | if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { 47 | if err := godotenv.Load(env); err != nil { 48 | log.Fatalf("Error loading .env file: %v", err) 49 | } 50 | } 51 | 52 | idToken := getenv("PLUGIN_OIDC_TOKEN_ID") 53 | projectId := getenv("PLUGIN_PROJECT_NUMBER") 54 | poolId := getenv("PLUGIN_POOL_ID") 55 | providerId := getenv("PLUGIN_PROVIDER_ID") 56 | serviceAccountEmail := getenv("PLUGIN_SERVICE_ACCOUNT_EMAIL") 57 | 58 | if idToken != "" && projectId != "" && poolId != "" && providerId != "" && serviceAccountEmail != "" { 59 | federalToken, err := gcp.GetFederalToken(idToken, projectId, poolId, providerId) 60 | if err != nil { 61 | logrus.Fatalf("Error (getFederalToken): %s", err) 62 | } 63 | accessToken, err := gcp.GetGoogleCloudAccessToken(federalToken, serviceAccountEmail) 64 | if err != nil { 65 | logrus.Fatalf("Error (getGoogleCloudAccessToken): %s", err) 66 | } 67 | config.AccessToken = accessToken 68 | } else { 69 | password := getenv( 70 | "PLUGIN_JSON_KEY", 71 | "GCR_JSON_KEY", 72 | "GOOGLE_CREDENTIALS", 73 | "TOKEN", 74 | ) 75 | config.WorkloadIdentity = parseBoolOrDefault(false, getenv("PLUGIN_WORKLOAD_IDENTITY")) 76 | config.Username, config.Password = setUsernameAndPassword(username, password, config.WorkloadIdentity) 77 | } 78 | 79 | location := getenv("PLUGIN_LOCATION") 80 | repo := getenv("PLUGIN_REPO") 81 | 82 | registry := getenv("PLUGIN_REGISTRY") 83 | if registry == "" { 84 | registry = fmt.Sprintf("%s-docker.pkg.dev", location) 85 | } 86 | 87 | if !strings.HasPrefix(repo, registry) { 88 | repo = path.Join(registry, repo) 89 | } 90 | config.Repo = repo 91 | config.Registry = registry 92 | return config 93 | } 94 | 95 | func main() { 96 | config := loadConfig() 97 | if config.AccessToken != "" { 98 | os.Setenv("ACCESS_TOKEN", config.AccessToken) 99 | } else if config.Username != "" && config.Password != "" { 100 | os.Setenv("DOCKER_USERNAME", config.Username) 101 | os.Setenv("DOCKER_PASSWORD", config.Password) 102 | } 103 | 104 | os.Setenv("PLUGIN_REPO", config.Repo) 105 | os.Setenv("PLUGIN_REGISTRY", config.Registry) 106 | 107 | // invoke the base docker plugin binary 108 | cmd := exec.Command(docker.GetDroneDockerExecCmd()) 109 | cmd.Stdout = os.Stdout 110 | cmd.Stderr = os.Stderr 111 | err := cmd.Run() 112 | if err != nil { 113 | logrus.Fatal(err) 114 | } 115 | } 116 | 117 | func getOauthToken(data []byte) (s string) { 118 | scopes := []string{ 119 | "https://www.googleapis.com/auth/cloud-platform", 120 | } 121 | ctx := context.Background() 122 | credentials, err := google.CredentialsFromJSON(ctx, data, scopes...) 123 | if err == nil { 124 | token, err := credentials.TokenSource.Token() 125 | if err == nil { 126 | return token.AccessToken 127 | } 128 | } 129 | return 130 | } 131 | 132 | func setUsernameAndPassword(user string, pass string, workloadIdentity bool) (u string, p string) { 133 | // decode the token if base64 encoded 134 | decoded, err := base64.StdEncoding.DecodeString(pass) 135 | if err == nil { 136 | pass = string(decoded) 137 | } 138 | // get oauth token and set username if using workload identity 139 | if workloadIdentity { 140 | data := []byte(pass) 141 | pass = getOauthToken(data) 142 | user = "oauth2accesstoken" 143 | } 144 | return user, pass 145 | } 146 | 147 | func parseBoolOrDefault(defaultValue bool, s string) (result bool) { 148 | var err error 149 | result, err = strconv.ParseBool(s) 150 | if err != nil { 151 | result = defaultValue 152 | } 153 | 154 | return 155 | } 156 | 157 | func getenv(key ...string) (s string) { 158 | for _, k := range key { 159 | s = os.Getenv(k) 160 | if s != "" { 161 | return 162 | } 163 | } 164 | return 165 | } 166 | -------------------------------------------------------------------------------- /cmd/drone-gcr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "log" 7 | "os" 8 | "os/exec" 9 | "path" 10 | "strconv" 11 | "strings" 12 | 13 | docker "github.com/drone-plugins/drone-docker" 14 | "github.com/drone-plugins/drone-docker/internal/gcp" 15 | 16 | "github.com/joho/godotenv" 17 | "github.com/sirupsen/logrus" 18 | "golang.org/x/oauth2/google" 19 | ) 20 | 21 | type Config struct { 22 | Repo string 23 | Registry string 24 | Password string 25 | WorkloadIdentity bool 26 | Username string 27 | AccessToken string 28 | } 29 | 30 | func loadConfig() Config { 31 | // Default username 32 | username := "_json_key" 33 | var config Config 34 | 35 | // Load env-file if it exists 36 | if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { 37 | if err := godotenv.Load(env); err != nil { 38 | log.Fatalf("Error loading .env file: %v", err) 39 | } 40 | } 41 | 42 | idToken := getenv("PLUGIN_OIDC_TOKEN_ID") 43 | projectId := getenv("PLUGIN_PROJECT_NUMBER") 44 | poolId := getenv("PLUGIN_POOL_ID") 45 | providerId := getenv("PLUGIN_PROVIDER_ID") 46 | serviceAccountEmail := getenv("PLUGIN_SERVICE_ACCOUNT_EMAIL") 47 | 48 | if idToken != "" && projectId != "" && poolId != "" && providerId != "" && serviceAccountEmail != "" { 49 | federalToken, err := gcp.GetFederalToken(idToken, projectId, poolId, providerId) 50 | if err != nil { 51 | logrus.Fatalf("Error (getFederalToken): %s", err) 52 | } 53 | accessToken, err := gcp.GetGoogleCloudAccessToken(federalToken, serviceAccountEmail) 54 | if err != nil { 55 | logrus.Fatalf("Error (getGoogleCloudAccessToken): %s", err) 56 | } 57 | config.AccessToken = accessToken 58 | } else { 59 | password := getenv( 60 | "PLUGIN_JSON_KEY", 61 | "GCR_JSON_KEY", 62 | "GOOGLE_CREDENTIALS", 63 | "TOKEN", 64 | ) 65 | config.WorkloadIdentity = parseBoolOrDefault(false, getenv("PLUGIN_WORKLOAD_IDENTITY")) 66 | config.Username, config.Password = setUsernameAndPassword(username, password, config.WorkloadIdentity) 67 | } 68 | 69 | repo := getenv("PLUGIN_REPO") 70 | registryType := getenv("PLUGIN_REGISTRY_TYPE") 71 | if registryType == "" { 72 | registryType = "GCR" 73 | } 74 | 75 | registry := getenv("PLUGIN_REGISTRY") 76 | if registry == "" { 77 | registry = "gcr.io" 78 | } 79 | 80 | if !strings.HasPrefix(repo, registry) { 81 | repo = path.Join(registry, repo) 82 | } 83 | config.Repo = repo 84 | config.Registry = registry 85 | return config 86 | } 87 | 88 | func main() { 89 | config := loadConfig() 90 | if config.AccessToken != "" { 91 | os.Setenv("ACCESS_TOKEN", config.AccessToken) 92 | } else if config.Username != "" && config.Password != "" { 93 | os.Setenv("DOCKER_USERNAME", config.Username) 94 | os.Setenv("DOCKER_PASSWORD", config.Password) 95 | } 96 | 97 | os.Setenv("PLUGIN_REPO", config.Repo) 98 | os.Setenv("PLUGIN_REGISTRY", config.Registry) 99 | 100 | // invoke the base docker plugin binary 101 | cmd := exec.Command(docker.GetDroneDockerExecCmd()) 102 | cmd.Stdout = os.Stdout 103 | cmd.Stderr = os.Stderr 104 | err := cmd.Run() 105 | if err != nil { 106 | logrus.Fatal(err) 107 | } 108 | } 109 | 110 | func getOauthToken(data []byte) (s string) { 111 | scopes := []string{ 112 | "https://www.googleapis.com/auth/cloud-platform", 113 | } 114 | ctx := context.Background() 115 | credentials, err := google.CredentialsFromJSON(ctx, data, scopes...) 116 | if err == nil { 117 | token, err := credentials.TokenSource.Token() 118 | if err == nil { 119 | return token.AccessToken 120 | } 121 | } 122 | return 123 | } 124 | 125 | func setUsernameAndPassword(user string, pass string, workloadIdentity bool) (u string, p string) { 126 | // decode the token if base64 encoded 127 | decoded, err := base64.StdEncoding.DecodeString(pass) 128 | if err == nil { 129 | pass = string(decoded) 130 | } 131 | // get oauth token and set username if using workload identity 132 | if workloadIdentity { 133 | data := []byte(pass) 134 | pass = getOauthToken(data) 135 | user = "oauth2accesstoken" 136 | } 137 | return user, pass 138 | } 139 | 140 | func parseBoolOrDefault(defaultValue bool, s string) (result bool) { 141 | var err error 142 | result, err = strconv.ParseBool(s) 143 | if err != nil { 144 | result = defaultValue 145 | } 146 | 147 | return 148 | } 149 | 150 | func getenv(key ...string) (s string) { 151 | for _, k := range key { 152 | s = os.Getenv(k) 153 | if s != "" { 154 | return 155 | } 156 | } 157 | return 158 | } 159 | -------------------------------------------------------------------------------- /cmd/drone-heroku/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "path" 7 | 8 | "github.com/joho/godotenv" 9 | ) 10 | 11 | func main() { 12 | // Load env-file if it exists first 13 | if env := os.Getenv("PLUGIN_ENV_FILE"); env != "" { 14 | godotenv.Load(env) 15 | } 16 | 17 | var ( 18 | registry = "registry.heroku.com" 19 | process = getenv("PLUGIN_PROCESS_TYPE") 20 | app = getenv("PLUGIN_APP") 21 | email = getenv("PLUGIN_EMAIL", "HEROKU_EMAIL") 22 | key = getenv("PLUGIN_API_KEY", "HEROKU_API_KEY") 23 | ) 24 | 25 | if process == "" { 26 | process = "web" 27 | } 28 | 29 | os.Setenv("PLUGIN_REGISTRY", registry) 30 | os.Setenv("PLUGIN_REPO", path.Join(registry, app, process)) 31 | 32 | os.Setenv("DOCKER_PASSWORD", key) 33 | os.Setenv("DOCKER_USERNAME", email) 34 | os.Setenv("DOCKER_EMAIL", email) 35 | 36 | cmd := exec.Command("drone-docker") 37 | cmd.Stdout = os.Stdout 38 | cmd.Stderr = os.Stderr 39 | err := cmd.Run() 40 | if err != nil { 41 | os.Exit(1) 42 | } 43 | } 44 | 45 | func getenv(key ...string) (s string) { 46 | for _, k := range key { 47 | s = os.Getenv(k) 48 | if s != "" { 49 | return 50 | } 51 | } 52 | return 53 | } 54 | -------------------------------------------------------------------------------- /daemon.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package docker 5 | 6 | import ( 7 | "io" 8 | "os" 9 | ) 10 | 11 | const dockerExe = "/usr/local/bin/docker" 12 | const dockerdExe = "/usr/local/bin/dockerd" 13 | const dockerHome = "/root/.docker/" 14 | 15 | func (p Plugin) startDaemon() { 16 | cmd := commandDaemon(p.Daemon) 17 | if p.Daemon.Debug { 18 | cmd.Stdout = os.Stdout 19 | cmd.Stderr = os.Stderr 20 | } else { 21 | cmd.Stdout = io.Discard 22 | cmd.Stderr = io.Discard 23 | } 24 | go func() { 25 | trace(cmd) 26 | cmd.Run() 27 | }() 28 | } 29 | -------------------------------------------------------------------------------- /daemon_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package docker 4 | 5 | const dockerExe = "C:\\bin\\docker.exe" 6 | const dockerdExe = "" 7 | const dockerHome = "C:\\ProgramData\\docker\\" 8 | 9 | func (p Plugin) startDaemon() { 10 | // this is a no-op on windows 11 | } 12 | -------------------------------------------------------------------------------- /docker.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "runtime" 10 | "strings" 11 | "time" 12 | 13 | "github.com/drone-plugins/drone-docker/internal/docker" 14 | "github.com/drone-plugins/drone-plugin-lib/drone" 15 | ) 16 | 17 | type ( 18 | // Daemon defines Docker daemon parameters. 19 | Daemon struct { 20 | Registry string // Docker registry 21 | Mirror string // Docker registry mirror 22 | Insecure bool // Docker daemon enable insecure registries 23 | StorageDriver string // Docker daemon storage driver 24 | StoragePath string // Docker daemon storage path 25 | Disabled bool // DOcker daemon is disabled (already running) 26 | Debug bool // Docker daemon started in debug mode 27 | Bip string // Docker daemon network bridge IP address 28 | DNS []string // Docker daemon dns server 29 | DNSSearch []string // Docker daemon dns search domain 30 | MTU string // Docker daemon mtu setting 31 | IPv6 bool // Docker daemon IPv6 networking 32 | Experimental bool // Docker daemon enable experimental mode 33 | RegistryType drone.RegistryType // Docker registry type 34 | } 35 | 36 | // Login defines Docker login parameters. 37 | Login struct { 38 | Registry string // Docker registry address 39 | Username string // Docker registry username 40 | Password string // Docker registry password 41 | Email string // Docker registry email 42 | Config string // Docker Auth Config 43 | AccessToken string // External Access Token 44 | } 45 | 46 | // Build defines Docker build parameters. 47 | Build struct { 48 | Remote string // Git remote URL 49 | Name string // Docker build using default named tag 50 | TempTag string // Temporary tag used during docker build 51 | Dockerfile string // Docker build Dockerfile 52 | Context string // Docker build context 53 | Tags []string // Docker build tags 54 | Args []string // Docker build args 55 | ArgsEnv []string // Docker build args from env 56 | ArgsNew []string // docker build args which has comma seperated values 57 | IsMultipleBuildArgs bool // env variable for fall back to old build args 58 | Target string // Docker build target 59 | Squash bool // Docker build squash 60 | Pull bool // Docker build pull 61 | CacheFrom []string // Docker build cache-from 62 | Compress bool // Docker build compress 63 | Repo string // Docker build repository 64 | LabelSchema []string // label-schema Label map 65 | AutoLabel bool // auto-label bool 66 | Labels []string // Label map 67 | Link string // Git repo link 68 | NoCache bool // Docker build no-cache 69 | Secret string // secret keypair 70 | SecretEnvs []string // Docker build secrets with env var as source 71 | SecretFiles []string // Docker build secrets with file as source 72 | AddHost []string // Docker build add-host 73 | Quiet bool // Docker build quiet 74 | Platform string // Docker build platform 75 | SSHAgentKey string // Docker build ssh agent key 76 | SSHKeyPath string // Docker build ssh key path 77 | } 78 | 79 | // Plugin defines the Docker plugin parameters. 80 | Plugin struct { 81 | Login Login // Docker login configuration 82 | Build Build // Docker build configuration 83 | Daemon Daemon // Docker daemon configuration 84 | Dryrun bool // Docker push is skipped 85 | Cleanup bool // Docker purge is enabled 86 | CardPath string // Card path to write file to 87 | ArtifactFile string // Artifact path to write file to 88 | BaseImageRegistry string // Docker registry to pull base image 89 | BaseImageUsername string // Docker registry username to pull base image 90 | BaseImagePassword string // Docker registry password to pull base image 91 | } 92 | 93 | Card []struct { 94 | ID string `json:"Id"` 95 | RepoTags []string `json:"RepoTags"` 96 | ParsedRepoTags []TagStruct `json:"ParsedRepoTags"` 97 | RepoDigests []interface{} `json:"RepoDigests"` 98 | Parent string `json:"Parent"` 99 | Comment string `json:"Comment"` 100 | Created time.Time `json:"Created"` 101 | Container string `json:"Container"` 102 | DockerVersion string `json:"DockerVersion"` 103 | Author string `json:"Author"` 104 | Architecture string `json:"Architecture"` 105 | Os string `json:"Os"` 106 | Size int `json:"Size"` 107 | VirtualSize int `json:"VirtualSize"` 108 | Metadata struct { 109 | LastTagTime time.Time `json:"LastTagTime"` 110 | } `json:"Metadata"` 111 | SizeString string 112 | VirtualSizeString string 113 | Time string 114 | URL string `json:"URL"` 115 | } 116 | TagStruct struct { 117 | Tag string `json:"Tag"` 118 | } 119 | ) 120 | 121 | // Exec executes the plugin step 122 | func (p Plugin) Exec() error { 123 | // start the Docker daemon server 124 | if !p.Daemon.Disabled { 125 | p.startDaemon() 126 | } 127 | 128 | // poll the docker daemon until it is started. This ensures the daemon is 129 | // ready to accept connections before we proceed. 130 | for i := 0; ; i++ { 131 | cmd := commandInfo() 132 | err := cmd.Run() 133 | if err == nil { 134 | break 135 | } 136 | if i == 15 { 137 | fmt.Println("Unable to reach Docker Daemon after 15 attempts.") 138 | break 139 | } 140 | time.Sleep(time.Second * 1) 141 | } 142 | 143 | // for debugging purposes, log the type of authentication 144 | // credentials that have been provided. 145 | switch { 146 | case p.Login.Password != "" && p.Login.Config != "": 147 | fmt.Println("Detected registry credentials and registry credentials file") 148 | case p.Login.Password != "": 149 | fmt.Println("Detected registry credentials") 150 | case p.Login.Config != "": 151 | fmt.Println("Detected registry credentials file") 152 | case p.Login.AccessToken != "": 153 | fmt.Println("Detected access token") 154 | default: 155 | fmt.Println("Registry credentials or Docker config not provided. Guest mode enabled.") 156 | } 157 | 158 | // create Auth Config File 159 | if p.Login.Config != "" { 160 | os.MkdirAll(dockerHome, 0600) 161 | 162 | path := filepath.Join(dockerHome, "config.json") 163 | err := os.WriteFile(path, []byte(p.Login.Config), 0600) 164 | if err != nil { 165 | return fmt.Errorf("Error writing config.json: %s", err) 166 | } 167 | } 168 | 169 | // instead of writing to config file directly, using docker's login func 170 | // is better to integrate with various credential helpers, 171 | // it also handles different registry specific logic in a better way, 172 | // as opposed to config write where different registries need to be addressed differently. 173 | // It handles any changes in the authentication process across different Docker versions. 174 | 175 | if p.BaseImageRegistry != "" { 176 | if p.BaseImageUsername == "" { 177 | fmt.Printf("Username cannot be empty. The base image connector requires authenticated access. Please either use an authenticated connector, or remove the base image connector.") 178 | } 179 | if p.BaseImagePassword == "" { 180 | fmt.Printf("Password cannot be empty. The base image connector requires authenticated access. Please either use an authenticated connector, or remove the base image connector.") 181 | } 182 | var baseConnectorLogin Login 183 | baseConnectorLogin.Registry = p.BaseImageRegistry 184 | baseConnectorLogin.Username = p.BaseImageUsername 185 | baseConnectorLogin.Password = p.BaseImagePassword 186 | 187 | cmd := commandLogin(baseConnectorLogin) 188 | 189 | raw, err := cmd.CombinedOutput() 190 | if err != nil { 191 | out := string(raw) 192 | out = strings.Replace(out, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.", "", -1) 193 | fmt.Println(out) 194 | return fmt.Errorf("Error authenticating base connector: exit status 1") 195 | } 196 | } 197 | 198 | // login to the Docker registry 199 | if p.Login.Password != "" { 200 | cmd := commandLogin(p.Login) 201 | raw, err := cmd.CombinedOutput() 202 | if err != nil { 203 | out := string(raw) 204 | out = strings.Replace(out, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.", "", -1) 205 | fmt.Println(out) 206 | return fmt.Errorf("error authenticating: exit status 1") 207 | } 208 | } else if p.Login.AccessToken != "" { 209 | cmd := commandLoginAccessToken(p.Login, p.Login.AccessToken) 210 | output, err := cmd.CombinedOutput() 211 | if err != nil { 212 | return fmt.Errorf("error logging in to Docker registry: %s", err) 213 | } 214 | if strings.Contains(string(output), "Login Succeeded") { 215 | fmt.Println("Login successful") 216 | } else { 217 | return fmt.Errorf("login did not succeed") 218 | } 219 | } 220 | 221 | if p.Build.Squash && !p.Daemon.Experimental { 222 | fmt.Println("Squash build flag is only available when Docker deamon is started with experimental flag. Ignoring...") 223 | p.Build.Squash = false 224 | } 225 | 226 | // add proxy build args 227 | addProxyBuildArgs(&p.Build) 228 | 229 | var cmds []*exec.Cmd 230 | cmds = append(cmds, commandVersion()) // docker version 231 | cmds = append(cmds, commandInfo()) // docker info 232 | 233 | // pre-pull cache images 234 | for _, img := range p.Build.CacheFrom { 235 | cmds = append(cmds, commandPull(img)) 236 | } 237 | 238 | // setup for using ssh agent (https://docs.docker.com/develop/develop-images/build_enhancements/#using-ssh-to-access-private-data-in-builds) 239 | if p.Build.SSHAgentKey != "" { 240 | var sshErr error 241 | p.Build.SSHKeyPath, sshErr = writeSSHPrivateKey(p.Build.SSHAgentKey) 242 | if sshErr != nil { 243 | return sshErr 244 | } 245 | } 246 | 247 | cmds = append(cmds, commandBuild(p.Build)) // docker build 248 | 249 | for _, tag := range p.Build.Tags { 250 | cmds = append(cmds, commandTag(p.Build, tag)) // docker tag 251 | 252 | if !p.Dryrun { 253 | cmds = append(cmds, commandPush(p.Build, tag)) // docker push 254 | } 255 | } 256 | 257 | // execute all commands in batch mode. 258 | for _, cmd := range cmds { 259 | cmd.Stdout = os.Stdout 260 | cmd.Stderr = os.Stderr 261 | trace(cmd) 262 | 263 | err := cmd.Run() 264 | if err != nil && isCommandPull(cmd.Args) { 265 | fmt.Printf("Could not pull cache-from image %s. Ignoring...\n", cmd.Args[2]) 266 | } else if err != nil && isCommandPrune(cmd.Args) { 267 | fmt.Printf("Could not prune system containers. Ignoring...\n") 268 | } else if err != nil && isCommandRmi(cmd.Args) { 269 | fmt.Printf("Could not remove image %s. Ignoring...\n", cmd.Args[2]) 270 | } else if err != nil { 271 | return err 272 | } 273 | } 274 | 275 | // output the adaptive card 276 | if err := p.writeCard(); err != nil { 277 | fmt.Printf("Could not create adaptive card. %s\n", err) 278 | } 279 | 280 | if p.ArtifactFile != "" { 281 | if digest, err := getDigest(p.Build.TempTag); err == nil { 282 | if err = drone.WritePluginArtifactFile(p.Daemon.RegistryType, p.ArtifactFile, p.Daemon.Registry, p.Build.Repo, digest, p.Build.Tags); err != nil { 283 | fmt.Printf("failed to write plugin artifact file at path: %s with error: %s\n", p.ArtifactFile, err) 284 | } 285 | } else { 286 | fmt.Printf("Could not fetch the digest. %s\n", err) 287 | } 288 | } 289 | 290 | // execute cleanup routines in batch mode 291 | if p.Cleanup { 292 | // clear the slice 293 | cmds = nil 294 | 295 | cmds = append(cmds, commandRmi(p.Build.TempTag)) // docker rmi 296 | cmds = append(cmds, commandPrune()) // docker system prune -f 297 | 298 | for _, cmd := range cmds { 299 | cmd.Stdout = os.Stdout 300 | cmd.Stderr = os.Stderr 301 | trace(cmd) 302 | } 303 | } 304 | 305 | return nil 306 | } 307 | 308 | // helper function to set the credentials 309 | func setDockerAuth(username, password, registry, baseImageUsername, 310 | baseImagePassword, baseImageRegistry string) ([]byte, error) { 311 | var credentials []docker.RegistryCredentials 312 | // add only docker registry to the config 313 | dockerConfig := docker.NewConfig() 314 | if password != "" { 315 | pushToRegistryCreds := docker.RegistryCredentials{ 316 | Registry: registry, 317 | Username: username, 318 | Password: password, 319 | } 320 | // push registry auth 321 | credentials = append(credentials, pushToRegistryCreds) 322 | } 323 | 324 | if baseImageRegistry != "" { 325 | pullFromRegistryCreds := docker.RegistryCredentials{ 326 | Registry: baseImageRegistry, 327 | Username: baseImageUsername, 328 | Password: baseImagePassword, 329 | } 330 | // base image registry auth 331 | credentials = append(credentials, pullFromRegistryCreds) 332 | } 333 | // Creates docker config for both the registries used for authentication 334 | return dockerConfig.CreateDockerConfigJson(credentials) 335 | } 336 | 337 | // helper function to create the docker login command. 338 | func commandLogin(login Login) *exec.Cmd { 339 | if login.Email != "" { 340 | return commandLoginEmail(login) 341 | } 342 | return exec.Command( 343 | dockerExe, "login", 344 | "-u", login.Username, 345 | "-p", login.Password, 346 | login.Registry, 347 | ) 348 | } 349 | 350 | func commandLoginAccessToken(login Login, accessToken string) *exec.Cmd { 351 | cmd := exec.Command(dockerExe, 352 | "login", 353 | "-u", 354 | "oauth2accesstoken", 355 | "--password-stdin", 356 | login.Registry) 357 | cmd.Stdin = strings.NewReader(accessToken) 358 | return cmd 359 | } 360 | 361 | // helper to check if args match "docker pull " 362 | func isCommandPull(args []string) bool { 363 | return len(args) > 2 && args[1] == "pull" 364 | } 365 | 366 | func commandPull(repo string) *exec.Cmd { 367 | return exec.Command(dockerExe, "pull", repo) 368 | } 369 | 370 | func commandLoginEmail(login Login) *exec.Cmd { 371 | return exec.Command( 372 | dockerExe, "login", 373 | "-u", login.Username, 374 | "-p", login.Password, 375 | "-e", login.Email, 376 | login.Registry, 377 | ) 378 | } 379 | 380 | // helper function to create the docker info command. 381 | func commandVersion() *exec.Cmd { 382 | return exec.Command(dockerExe, "version") 383 | } 384 | 385 | // helper function to create the docker info command. 386 | func commandInfo() *exec.Cmd { 387 | return exec.Command(dockerExe, "info") 388 | } 389 | 390 | // helper function to create the docker build command. 391 | func commandBuild(build Build) *exec.Cmd { 392 | args := []string{ 393 | "build", 394 | "--rm=true", 395 | "-f", build.Dockerfile, 396 | "-t", build.TempTag, 397 | } 398 | 399 | args = append(args, build.Context) 400 | if build.Squash { 401 | args = append(args, "--squash") 402 | } 403 | if build.Compress { 404 | args = append(args, "--compress") 405 | } 406 | if build.Pull { 407 | args = append(args, "--pull=true") 408 | } 409 | if build.NoCache { 410 | args = append(args, "--no-cache") 411 | } 412 | for _, arg := range build.CacheFrom { 413 | args = append(args, "--cache-from", arg) 414 | } 415 | for _, arg := range build.ArgsEnv { 416 | addProxyValue(&build, arg) 417 | } 418 | if build.IsMultipleBuildArgs { 419 | for _, arg := range build.ArgsNew { 420 | args = append(args, "--build-arg", arg) 421 | } 422 | } else { 423 | for _, arg := range build.Args { 424 | args = append(args, "--build-arg", arg) 425 | } 426 | } 427 | for _, host := range build.AddHost { 428 | args = append(args, "--add-host", host) 429 | } 430 | if build.Secret != "" { 431 | args = append(args, "--secret", build.Secret) 432 | } 433 | for _, secret := range build.SecretEnvs { 434 | if arg, err := getSecretStringCmdArg(secret); err == nil { 435 | args = append(args, "--secret", arg) 436 | } 437 | } 438 | for _, secret := range build.SecretFiles { 439 | if arg, err := getSecretFileCmdArg(secret); err == nil { 440 | args = append(args, "--secret", arg) 441 | } 442 | } 443 | if build.Target != "" { 444 | args = append(args, "--target", build.Target) 445 | } 446 | if build.Quiet { 447 | args = append(args, "--quiet") 448 | } 449 | if build.Platform != "" { 450 | args = append(args, "--platform", build.Platform) 451 | } 452 | if build.SSHKeyPath != "" { 453 | args = append(args, "--ssh", build.SSHKeyPath) 454 | } 455 | 456 | if build.AutoLabel { 457 | labelSchema := []string{ 458 | fmt.Sprintf("created=%s", time.Now().Format(time.RFC3339)), 459 | fmt.Sprintf("revision=%s", build.Name), 460 | fmt.Sprintf("source=%s", build.Remote), 461 | fmt.Sprintf("url=%s", build.Link), 462 | } 463 | labelPrefix := "org.opencontainers.image" 464 | 465 | if len(build.LabelSchema) > 0 { 466 | labelSchema = append(labelSchema, build.LabelSchema...) 467 | } 468 | 469 | for _, label := range labelSchema { 470 | args = append(args, "--label", fmt.Sprintf("%s.%s", labelPrefix, label)) 471 | } 472 | } 473 | 474 | if len(build.Labels) > 0 { 475 | for _, label := range build.Labels { 476 | args = append(args, "--label", label) 477 | } 478 | } 479 | 480 | // we need to enable buildkit, for secret support and ssh agent support 481 | if build.Secret != "" || len(build.SecretEnvs) > 0 || len(build.SecretFiles) > 0 || build.SSHAgentKey != "" { 482 | os.Setenv("DOCKER_BUILDKIT", "1") 483 | } 484 | return exec.Command(dockerExe, args...) 485 | } 486 | 487 | func getSecretStringCmdArg(kvp string) (string, error) { 488 | return getSecretCmdArg(kvp, false) 489 | } 490 | 491 | func getSecretFileCmdArg(kvp string) (string, error) { 492 | return getSecretCmdArg(kvp, true) 493 | } 494 | 495 | func getSecretCmdArg(kvp string, file bool) (string, error) { 496 | delimIndex := strings.IndexByte(kvp, '=') 497 | if delimIndex == -1 { 498 | return "", fmt.Errorf("%s is not a valid secret", kvp) 499 | } 500 | 501 | key := kvp[:delimIndex] 502 | value := kvp[delimIndex+1:] 503 | 504 | if key == "" || value == "" { 505 | return "", fmt.Errorf("%s is not a valid secret", kvp) 506 | } 507 | 508 | if file { 509 | return fmt.Sprintf("id=%s,src=%s", key, value), nil 510 | } 511 | 512 | return fmt.Sprintf("id=%s,env=%s", key, value), nil 513 | } 514 | 515 | // helper function to add proxy values from the environment 516 | func addProxyBuildArgs(build *Build) { 517 | addProxyValue(build, "http_proxy") 518 | addProxyValue(build, "https_proxy") 519 | addProxyValue(build, "no_proxy") 520 | } 521 | 522 | // helper function to add the upper and lower case version of a proxy value. 523 | func addProxyValue(build *Build, key string) { 524 | value := getProxyValue(key) 525 | 526 | if len(value) > 0 && !hasProxyBuildArg(build, key) { 527 | build.Args = append(build.Args, fmt.Sprintf("%s=%s", key, value)) 528 | build.Args = append(build.Args, fmt.Sprintf("%s=%s", strings.ToUpper(key), value)) 529 | } 530 | if len(value) > 0 && !hasProxyBuildArgNew(build, key) { 531 | build.ArgsNew = append(build.ArgsNew, fmt.Sprintf("%s=%s", key, value)) 532 | build.ArgsNew = append(build.ArgsNew, fmt.Sprintf("%s=%s", strings.ToUpper(key), value)) 533 | } 534 | } 535 | 536 | // helper function to get a proxy value from the environment. 537 | // 538 | // assumes that the upper and lower case versions of are the same. 539 | func getProxyValue(key string) string { 540 | value := os.Getenv(key) 541 | 542 | if len(value) > 0 { 543 | return value 544 | } 545 | 546 | return os.Getenv(strings.ToUpper(key)) 547 | } 548 | 549 | // helper function that looks to see if a proxy value was set in the build args. 550 | func hasProxyBuildArg(build *Build, key string) bool { 551 | keyUpper := strings.ToUpper(key) 552 | 553 | for _, s := range build.Args { 554 | if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) { 555 | return true 556 | } 557 | } 558 | 559 | return false 560 | } 561 | func hasProxyBuildArgNew(build *Build, key string) bool { 562 | keyUpper := strings.ToUpper(key) 563 | 564 | for _, s := range build.ArgsNew { 565 | if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) { 566 | return true 567 | } 568 | } 569 | 570 | return false 571 | } 572 | 573 | // helper function to create the docker tag command. 574 | func commandTag(build Build, tag string) *exec.Cmd { 575 | var ( 576 | source = build.TempTag 577 | target = fmt.Sprintf("%s:%s", build.Repo, tag) 578 | ) 579 | return exec.Command( 580 | dockerExe, "tag", source, target, 581 | ) 582 | } 583 | 584 | // helper function to create the docker push command. 585 | func commandPush(build Build, tag string) *exec.Cmd { 586 | target := fmt.Sprintf("%s:%s", build.Repo, tag) 587 | return exec.Command(dockerExe, "push", target) 588 | } 589 | 590 | // helper function to create the docker daemon command. 591 | func commandDaemon(daemon Daemon) *exec.Cmd { 592 | args := []string{ 593 | "--data-root", daemon.StoragePath, 594 | "--host=unix:///var/run/docker.sock", 595 | } 596 | 597 | if _, err := os.Stat("/etc/docker/default.json"); err == nil { 598 | args = append(args, "--seccomp-profile=/etc/docker/default.json") 599 | } 600 | 601 | if daemon.StorageDriver != "" { 602 | args = append(args, "-s", daemon.StorageDriver) 603 | } 604 | if daemon.Insecure && daemon.Registry != "" { 605 | args = append(args, "--insecure-registry", daemon.Registry) 606 | } 607 | if daemon.IPv6 { 608 | args = append(args, "--ipv6") 609 | } 610 | if len(daemon.Mirror) != 0 { 611 | args = append(args, "--registry-mirror", daemon.Mirror) 612 | } 613 | if len(daemon.Bip) != 0 { 614 | args = append(args, "--bip", daemon.Bip) 615 | } 616 | for _, dns := range daemon.DNS { 617 | args = append(args, "--dns", dns) 618 | } 619 | for _, dnsSearch := range daemon.DNSSearch { 620 | args = append(args, "--dns-search", dnsSearch) 621 | } 622 | if len(daemon.MTU) != 0 { 623 | args = append(args, "--mtu", daemon.MTU) 624 | } 625 | if daemon.Experimental { 626 | args = append(args, "--experimental") 627 | } 628 | return exec.Command(dockerdExe, args...) 629 | } 630 | 631 | // helper to check if args match "docker prune" 632 | func isCommandPrune(args []string) bool { 633 | return len(args) > 3 && args[2] == "prune" 634 | } 635 | 636 | func commandPrune() *exec.Cmd { 637 | return exec.Command(dockerExe, "system", "prune", "-f") 638 | } 639 | 640 | // helper to check if args match "docker rmi" 641 | func isCommandRmi(args []string) bool { 642 | return len(args) > 2 && args[1] == "rmi" 643 | } 644 | 645 | func commandRmi(tag string) *exec.Cmd { 646 | return exec.Command(dockerExe, "rmi", tag) 647 | } 648 | 649 | func writeSSHPrivateKey(key string) (path string, err error) { 650 | home, err := os.UserHomeDir() 651 | if err != nil { 652 | return "", fmt.Errorf("unable to determine home directory: %s", err) 653 | } 654 | if err := os.MkdirAll(filepath.Join(home, ".ssh"), 0700); err != nil { 655 | return "", fmt.Errorf("unable to create .ssh directory: %s", err) 656 | } 657 | pathToKey := filepath.Join(home, ".ssh", "id_rsa") 658 | if err := os.WriteFile(pathToKey, []byte(key), 0400); err != nil { 659 | return "", fmt.Errorf("unable to write ssh key %s: %s", pathToKey, err) 660 | } 661 | path = fmt.Sprintf("default=%s", pathToKey) 662 | 663 | return path, nil 664 | } 665 | 666 | // trace writes each command to stdout with the command wrapped in an xml 667 | // tag so that it can be extracted and displayed in the logs. 668 | func trace(cmd *exec.Cmd) { 669 | fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " ")) 670 | } 671 | 672 | func GetDroneDockerExecCmd() string { 673 | if runtime.GOOS == "windows" { 674 | return "C:/bin/drone-docker.exe" 675 | } 676 | 677 | return "drone-docker" 678 | } 679 | 680 | func getDigest(buildName string) (string, error) { 681 | cmd := exec.Command("docker", "inspect", "--format='{{index .RepoDigests 0}}'", buildName) 682 | output, err := cmd.Output() 683 | if err != nil { 684 | return "", err 685 | } 686 | 687 | // Parse the output to extract the repo digest. 688 | digest := strings.Trim(string(output), "'\n") 689 | parts := strings.Split(digest, "@") 690 | if len(parts) > 1 { 691 | return parts[1], nil 692 | } 693 | return "", errors.New("unable to fetch digest") 694 | } 695 | -------------------------------------------------------------------------------- /docker/acr/Dockerfile.linux.amd64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-amd64 2 | 3 | ADD release/linux/amd64/drone-acr /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-acr"] 5 | -------------------------------------------------------------------------------- /docker/acr/Dockerfile.linux.arm64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-arm64 2 | 3 | ADD release/linux/arm64/drone-acr /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-acr"] 5 | -------------------------------------------------------------------------------- /docker/acr/Dockerfile.windows.amd64.1809: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-1809-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone ACR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-acr.exe C:/bin/drone-acr.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-acr.exe" ] 11 | -------------------------------------------------------------------------------- /docker/acr/Dockerfile.windows.amd64.ltsc2022: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-ltsc2022-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone ACR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-acr.exe C:/bin/drone-acr.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-acr.exe" ] 11 | -------------------------------------------------------------------------------- /docker/acr/manifest.tmpl: -------------------------------------------------------------------------------- 1 | image: plugins/acr:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} 2 | {{#if build.tags}} 3 | tags: 4 | {{#each build.tags}} 5 | - {{this}} 6 | {{/each}} 7 | {{/if}} 8 | manifests: 9 | - 10 | image: plugins/acr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 11 | platform: 12 | architecture: amd64 13 | os: linux 14 | - 15 | image: plugins/acr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 16 | platform: 17 | architecture: arm64 18 | os: linux 19 | variant: v8 20 | - 21 | image: plugins/acr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 22 | platform: 23 | architecture: amd64 24 | os: windows 25 | version: 1809 26 | - 27 | image: plugins/acr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-ltsc2022-amd64 28 | platform: 29 | architecture: amd64 30 | os: windows 31 | version: ltsc2022 32 | -------------------------------------------------------------------------------- /docker/docker/Dockerfile.linux.amd64: -------------------------------------------------------------------------------- 1 | FROM docker:20.10.14-dind 2 | 3 | ENV DOCKER_HOST=unix:///var/run/docker.sock 4 | 5 | ADD release/linux/amd64/drone-docker /bin/ 6 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-docker"] 7 | -------------------------------------------------------------------------------- /docker/docker/Dockerfile.linux.arm64: -------------------------------------------------------------------------------- 1 | FROM arm64v8/docker:20.10.14-dind 2 | 3 | ENV DOCKER_HOST=unix:///var/run/docker.sock 4 | 5 | ADD release/linux/arm64/drone-docker /bin/ 6 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-docker"] 7 | -------------------------------------------------------------------------------- /docker/docker/Dockerfile.windows.amd64.1809: -------------------------------------------------------------------------------- 1 | # escape=` 2 | # using 1809-KB5021237-amd64 as base image, 1809-KB5022286-amd64 does not work 3 | FROM mcr.microsoft.com/windows/servercore:1809-KB5021237 as download 4 | 5 | SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] 6 | 7 | ENV DOCKER_VERSION 19.03.1 8 | 9 | RUN Invoke-WebRequest 'http://constexpr.org/innoextract/files/innoextract-1.6-windows.zip' -OutFile 'innoextract.zip' -UseBasicParsing ; ` 10 | Expand-Archive innoextract.zip -DestinationPath C:\ ; ` 11 | Remove-Item -Path innoextract.zip 12 | 13 | RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; ` 14 | Invoke-WebRequest $('https://github.com/docker/toolbox/releases/download/v{0}/DockerToolbox-{0}.exe' -f $env:DOCKER_VERSION) -OutFile 'dockertoolbox.exe' -UseBasicParsing 15 | RUN /innoextract.exe dockertoolbox.exe 16 | 17 | FROM mcr.microsoft.com/windows/nanoserver:1809-KB5021237-amd64 18 | 19 | USER ContainerAdministrator 20 | 21 | LABEL maintainer="Drone.IO Community " ` 22 | org.label-schema.name="Drone Docker" ` 23 | org.label-schema.vendor="Drone.IO Community" ` 24 | org.label-schema.schema-version="1.0" 25 | 26 | RUN mkdir C:\bin 27 | COPY --from=download /windows/system32/netapi32.dll /windows/system32/netapi32.dll 28 | COPY --from=download /app/docker.exe C:/bin/docker.exe 29 | ADD release/windows/amd64/drone-docker.exe C:/bin/drone-docker.exe 30 | ENTRYPOINT [ "C:\\bin\\drone-docker.exe" ] 31 | -------------------------------------------------------------------------------- /docker/docker/Dockerfile.windows.amd64.ltsc2022: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM mcr.microsoft.com/windows/servercore:ltsc2022 as download 3 | 4 | SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] 5 | 6 | ENV DOCKER_VERSION 19.03.1 7 | 8 | RUN Invoke-WebRequest 'http://constexpr.org/innoextract/files/innoextract-1.7-windows.zip' -OutFile 'innoextract.zip' -UseBasicParsing ; ` 9 | Expand-Archive innoextract.zip -DestinationPath C:\ ; ` 10 | Remove-Item -Path innoextract.zip 11 | 12 | RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; ` 13 | Invoke-WebRequest $('https://github.com/docker/toolbox/releases/download/v{0}/DockerToolbox-{0}.exe' -f $env:DOCKER_VERSION) -OutFile 'dockertoolbox.exe' -UseBasicParsing 14 | RUN /innoextract.exe dockertoolbox.exe 15 | 16 | FROM mcr.microsoft.com/windows/nanoserver:ltsc2022 17 | USER ContainerAdministrator 18 | 19 | LABEL maintainer="Drone.IO Community " ` 20 | org.label-schema.name="Drone Docker" ` 21 | org.label-schema.vendor="Drone.IO Community" ` 22 | org.label-schema.schema-version="1.0" 23 | 24 | RUN mkdir C:\bin 25 | COPY --from=download /windows/system32/netapi32.dll /windows/system32/netapi32.dll 26 | COPY --from=download /app/docker.exe C:/bin/docker.exe 27 | ADD release/windows/amd64/drone-docker.exe C:/bin/drone-docker.exe 28 | ENTRYPOINT [ "C:\\bin\\drone-docker.exe" ] 29 | -------------------------------------------------------------------------------- /docker/docker/manifest.tmpl: -------------------------------------------------------------------------------- 1 | image: plugins/docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} 2 | {{#if build.tags}} 3 | tags: 4 | {{#each build.tags}} 5 | - {{this}} 6 | {{/each}} 7 | {{/if}} 8 | manifests: 9 | - 10 | image: plugins/docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 11 | platform: 12 | architecture: amd64 13 | os: linux 14 | - 15 | image: plugins/docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 16 | platform: 17 | architecture: arm64 18 | os: linux 19 | variant: v8 20 | - 21 | image: plugins/docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 22 | platform: 23 | architecture: amd64 24 | os: windows 25 | version: 1809 26 | - 27 | image: plugins/docker:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-ltsc2022-amd64 28 | platform: 29 | architecture: amd64 30 | os: windows 31 | version: ltsc2022 32 | -------------------------------------------------------------------------------- /docker/ecr/Dockerfile.linux.amd64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-amd64 2 | 3 | ADD release/linux/amd64/drone-ecr /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-ecr"] 5 | -------------------------------------------------------------------------------- /docker/ecr/Dockerfile.linux.arm64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-arm64 2 | 3 | ADD release/linux/arm64/drone-ecr /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-ecr"] 5 | -------------------------------------------------------------------------------- /docker/ecr/Dockerfile.windows.amd64.1809: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-1809-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone ECR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-ecr.exe C:/bin/drone-ecr.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-ecr.exe" ] 11 | -------------------------------------------------------------------------------- /docker/ecr/Dockerfile.windows.amd64.ltsc2022: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-ltsc2022-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone ECR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-ecr.exe C:/bin/drone-ecr.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-ecr.exe" ] 11 | -------------------------------------------------------------------------------- /docker/ecr/manifest.tmpl: -------------------------------------------------------------------------------- 1 | image: plugins/ecr:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} 2 | {{#if build.tags}} 3 | tags: 4 | {{#each build.tags}} 5 | - {{this}} 6 | {{/each}} 7 | {{/if}} 8 | manifests: 9 | - 10 | image: plugins/ecr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 11 | platform: 12 | architecture: amd64 13 | os: linux 14 | - 15 | image: plugins/ecr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 16 | platform: 17 | architecture: arm64 18 | os: linux 19 | variant: v8 20 | - 21 | image: plugins/ecr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 22 | platform: 23 | architecture: amd64 24 | os: windows 25 | version: 1809 26 | - 27 | image: plugins/ecr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-ltsc2022-amd64 28 | platform: 29 | architecture: amd64 30 | os: windows 31 | version: ltsc2022 32 | -------------------------------------------------------------------------------- /docker/gar/Dockerfile.linux.amd64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-amd64 2 | 3 | ADD release/linux/amd64/drone-gar /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-gar"] 5 | -------------------------------------------------------------------------------- /docker/gar/Dockerfile.linux.arm64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-arm64 2 | 3 | ADD release/linux/arm64/drone-gar /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-gar"] 5 | -------------------------------------------------------------------------------- /docker/gar/Dockerfile.windows.amd64.1809: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-1809-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone GAR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-gar.exe C:/bin/drone-gar.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-gar.exe" ] 11 | -------------------------------------------------------------------------------- /docker/gar/Dockerfile.windows.amd64.ltsc2022: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-ltsc2022-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone GAR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-gar.exe C:/bin/drone-gar.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-gar.exe" ] 11 | -------------------------------------------------------------------------------- /docker/gar/manifest.tmpl: -------------------------------------------------------------------------------- 1 | image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} 2 | {{#if build.tags}} 3 | tags: 4 | {{#each build.tags}} 5 | - {{this}} 6 | {{/each}} 7 | {{/if}} 8 | manifests: 9 | - 10 | image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 11 | platform: 12 | architecture: amd64 13 | os: linux 14 | - 15 | image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 16 | platform: 17 | architecture: arm64 18 | os: linux 19 | variant: v8 20 | - 21 | image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 22 | platform: 23 | architecture: amd64 24 | os: windows 25 | version: 1809 26 | - 27 | image: plugins/gar:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-ltsc2022-amd64 28 | platform: 29 | architecture: amd64 30 | os: windows 31 | version: ltsc2022 32 | -------------------------------------------------------------------------------- /docker/gcr/Dockerfile.linux.amd64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-amd64 2 | 3 | ADD release/linux/amd64/drone-gcr /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-gcr"] 5 | -------------------------------------------------------------------------------- /docker/gcr/Dockerfile.linux.arm64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-arm64 2 | 3 | ADD release/linux/arm64/drone-gcr /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-gcr"] 5 | -------------------------------------------------------------------------------- /docker/gcr/Dockerfile.windows.amd64.1809: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-1809-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone GCR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-gcr.exe C:/bin/drone-gcr.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-gcr.exe" ] 11 | -------------------------------------------------------------------------------- /docker/gcr/Dockerfile.windows.amd64.ltsc2022: -------------------------------------------------------------------------------- 1 | # escape=` 2 | FROM plugins/docker:windows-ltsc2022-amd64 3 | 4 | LABEL maintainer="Drone.IO Community " ` 5 | org.label-schema.name="Drone GCR" ` 6 | org.label-schema.vendor="Drone.IO Community" ` 7 | org.label-schema.schema-version="1.0" 8 | 9 | ADD release/windows/amd64/drone-gcr.exe C:/bin/drone-gcr.exe 10 | ENTRYPOINT [ "C:\\bin\\drone-gcr.exe" ] 11 | -------------------------------------------------------------------------------- /docker/gcr/manifest.tmpl: -------------------------------------------------------------------------------- 1 | image: plugins/gcr:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} 2 | {{#if build.tags}} 3 | tags: 4 | {{#each build.tags}} 5 | - {{this}} 6 | {{/each}} 7 | {{/if}} 8 | manifests: 9 | - 10 | image: plugins/gcr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 11 | platform: 12 | architecture: amd64 13 | os: linux 14 | - 15 | image: plugins/gcr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 16 | platform: 17 | architecture: arm64 18 | os: linux 19 | variant: v8 20 | - 21 | image: plugins/gcr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-1809-amd64 22 | platform: 23 | architecture: amd64 24 | os: windows 25 | version: 1809 26 | - 27 | image: plugins/gcr:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}windows-ltsc2022-amd64 28 | platform: 29 | architecture: amd64 30 | os: windows 31 | version: ltsc2022 32 | -------------------------------------------------------------------------------- /docker/heroku/Dockerfile.linux.amd64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-amd64 2 | 3 | ADD release/linux/amd64/drone-heroku /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-heroku"] 5 | -------------------------------------------------------------------------------- /docker/heroku/Dockerfile.linux.arm64: -------------------------------------------------------------------------------- 1 | FROM plugins/docker:linux-arm64 2 | 3 | ADD release/linux/arm64/drone-heroku /bin/ 4 | ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh", "/bin/drone-heroku"] 5 | -------------------------------------------------------------------------------- /docker/heroku/manifest.tmpl: -------------------------------------------------------------------------------- 1 | image: plugins/heroku:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}} 2 | {{#if build.tags}} 3 | tags: 4 | {{#each build.tags}} 5 | - {{this}} 6 | {{/each}} 7 | {{/if}} 8 | manifests: 9 | - 10 | image: plugins/heroku:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64 11 | platform: 12 | architecture: amd64 13 | os: linux 14 | - 15 | image: plugins/heroku:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64 16 | platform: 17 | architecture: arm64 18 | os: linux 19 | variant: v8 20 | -------------------------------------------------------------------------------- /docker_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "os/exec" 5 | "reflect" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/dchest/uniuri" 10 | ) 11 | 12 | func TestCommandBuild(t *testing.T) { 13 | tempTag := strings.ToLower(uniuri.New()) 14 | tcs := []struct { 15 | name string 16 | build Build 17 | want *exec.Cmd 18 | }{ 19 | { 20 | name: "secret from env var", 21 | build: Build{ 22 | Name: "plugins/drone-docker:latest", 23 | TempTag: tempTag, 24 | Dockerfile: "Dockerfile", 25 | Context: ".", 26 | SecretEnvs: []string{ 27 | "foo_secret=FOO_SECRET_ENV_VAR", 28 | }, 29 | }, 30 | want: exec.Command( 31 | dockerExe, 32 | "build", 33 | "--rm=true", 34 | "-f", 35 | "Dockerfile", 36 | "-t", 37 | tempTag, 38 | ".", 39 | "--secret id=foo_secret,env=FOO_SECRET_ENV_VAR", 40 | ), 41 | }, 42 | { 43 | name: "secret from file", 44 | build: Build{ 45 | Name: "plugins/drone-docker:latest", 46 | TempTag: tempTag, 47 | Dockerfile: "Dockerfile", 48 | Context: ".", 49 | SecretFiles: []string{ 50 | "foo_secret=/path/to/foo_secret", 51 | }, 52 | }, 53 | want: exec.Command( 54 | dockerExe, 55 | "build", 56 | "--rm=true", 57 | "-f", 58 | "Dockerfile", 59 | "-t", 60 | tempTag, 61 | ".", 62 | "--secret id=foo_secret,src=/path/to/foo_secret", 63 | ), 64 | }, 65 | { 66 | name: "multiple mixed secrets", 67 | build: Build{ 68 | Name: "plugins/drone-docker:latest", 69 | TempTag: tempTag, 70 | Dockerfile: "Dockerfile", 71 | Context: ".", 72 | SecretEnvs: []string{ 73 | "foo_secret=FOO_SECRET_ENV_VAR", 74 | "bar_secret=BAR_SECRET_ENV_VAR", 75 | }, 76 | SecretFiles: []string{ 77 | "foo_secret=/path/to/foo_secret", 78 | "bar_secret=/path/to/bar_secret", 79 | }, 80 | }, 81 | want: exec.Command( 82 | dockerExe, 83 | "build", 84 | "--rm=true", 85 | "-f", 86 | "Dockerfile", 87 | "-t", 88 | tempTag, 89 | ".", 90 | "--secret id=foo_secret,env=FOO_SECRET_ENV_VAR", 91 | "--secret id=bar_secret,env=BAR_SECRET_ENV_VAR", 92 | "--secret id=foo_secret,src=/path/to/foo_secret", 93 | "--secret id=bar_secret,src=/path/to/bar_secret", 94 | ), 95 | }, 96 | { 97 | name: "invalid mixed secrets", 98 | build: Build{ 99 | Name: "plugins/drone-docker:latest", 100 | TempTag: tempTag, 101 | Dockerfile: "Dockerfile", 102 | Context: ".", 103 | SecretEnvs: []string{ 104 | "foo_secret=", 105 | "=FOO_SECRET_ENV_VAR", 106 | "", 107 | }, 108 | SecretFiles: []string{ 109 | "foo_secret=", 110 | "=/path/to/bar_secret", 111 | "", 112 | }, 113 | }, 114 | want: exec.Command( 115 | dockerExe, 116 | "build", 117 | "--rm=true", 118 | "-f", 119 | "Dockerfile", 120 | "-t", 121 | tempTag, 122 | ".", 123 | ), 124 | }, 125 | { 126 | name: "platform argument", 127 | build: Build{ 128 | Name: "plugins/drone-docker:latest", 129 | TempTag: tempTag, 130 | Dockerfile: "Dockerfile", 131 | Context: ".", 132 | Platform: "test/platform", 133 | }, 134 | want: exec.Command( 135 | dockerExe, 136 | "build", 137 | "--rm=true", 138 | "-f", 139 | "Dockerfile", 140 | "-t", 141 | tempTag, 142 | ".", 143 | "--platform", 144 | "test/platform", 145 | ), 146 | }, 147 | { 148 | name: "ssh agent", 149 | build: Build{ 150 | Name: "plugins/drone-docker:latest", 151 | TempTag: tempTag, 152 | Dockerfile: "Dockerfile", 153 | Context: ".", 154 | SSHKeyPath: "id_rsa=/root/.ssh/id_rsa", 155 | }, 156 | want: exec.Command( 157 | dockerExe, 158 | "build", 159 | "--rm=true", 160 | "-f", 161 | "Dockerfile", 162 | "-t", 163 | tempTag, 164 | ".", 165 | "--ssh id_rsa=/root/.ssh/id_rsa", 166 | ), 167 | }, 168 | } 169 | 170 | for _, tc := range tcs { 171 | tc := tc 172 | 173 | t.Run(tc.name, func(t *testing.T) { 174 | cmd := commandBuild(tc.build) 175 | 176 | if !reflect.DeepEqual(cmd.String(), tc.want.String()) { 177 | t.Errorf("Got cmd %v, want %v", cmd, tc.want) 178 | } 179 | }) 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /docs/card.data.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": "sha256:3b0709c9afb41629c79c93355feed114d08a8c1bedd975eb53af08f4b867fd91", 3 | "RepoTags": [ 4 | "798a0dae10d63d281eff4c06eaa12001ffd23740:latest", 5 | "tphoney/test:latest" 6 | ], 7 | "ParsedRepoTags": [ 8 | { 9 | "Tag": "" 10 | }, 11 | { 12 | "Tag": "" 13 | }, 14 | { 15 | "Tag": "798a0dae10d63d281eff4c06eaa12001ffd23740:latest" 16 | }, 17 | { 18 | "Tag": "tphoney/test:latest" 19 | } 20 | ], 21 | "RepoDigests": [ 22 | "tphoney/test@sha256:93f8b95aaae7d194208b72e94a3a90544b00c8f2ad45aeb89d81a0c6ccbc5e19" 23 | ], 24 | "Parent": "sha256:493aa330a5929027dd8ecded9fa8c473a1508d17c0fd7d6a94a7f197f8d22c60", 25 | "Comment": "", 26 | "Created": "2022-02-16T11:13:40.8956582Z", 27 | "Container": "a57c0ca4dd2e081df8758e00549f7abe83803f1a1a7aaaf1cd8e685a5eb5a097", 28 | "DockerVersion": "20.10.9", 29 | "Author": "", 30 | "Architecture": "amd64", 31 | "Os": "linux", 32 | "Size": 14045949, 33 | "VirtualSize": 14045949, 34 | "Metadata": { 35 | "LastTagTime": "2022-02-16T11:13:40.9433973Z" 36 | }, 37 | "SizeString": "13.40MB", 38 | "VirtualSizeString": "13.40MB", 39 | "Time": "2022-02-16T11:13:40Z", 40 | "URL": "http://hub.docker.com/repositories/tphoney/test/" 41 | } -------------------------------------------------------------------------------- /docs/card.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "AdaptiveCard", 3 | "body": [ 4 | { 5 | "type": "ColumnSet", 6 | "columns": [ 7 | { 8 | "type": "Column", 9 | "items": [ 10 | { 11 | "type": "Image", 12 | "url": "https://d36jcksde1wxzq.cloudfront.net/be7833db9bddb4494d2a7c3dd659199a.png", 13 | "size": "small" 14 | } 15 | ], 16 | "width": "auto" 17 | }, 18 | { 19 | "type": "Column", 20 | "items": [ 21 | { 22 | "type": "TextBlock", 23 | "text": "Plugin: Drone Docker", 24 | "wrap": true, 25 | "size": "Small", 26 | "weight": "Bolder", 27 | "isSubtle": false, 28 | "spacing": "Small" 29 | }, 30 | { 31 | "type": "TextBlock", 32 | "text": "DIGEST: ${RepoDigests[0]}", 33 | "wrap": true, 34 | "size": "Small", 35 | "weight": "Lighter", 36 | "isSubtle": true, 37 | "spacing": "Small" 38 | } 39 | ], 40 | "width": "stretch" 41 | } 42 | ], 43 | "style": "default" 44 | }, 45 | { 46 | "type": "ColumnSet", 47 | "columns": [ 48 | { 49 | "type": "Column", 50 | "items": [ 51 | { 52 | "type": "TextBlock", 53 | "weight": "Lighter", 54 | "text": "TAGS", 55 | "wrap": true, 56 | "size": "Small", 57 | "isSubtle": true, 58 | "spacing": "Medium" 59 | }, 60 | { 61 | "type": "FactSet", 62 | "facts": [ 63 | { 64 | "title": "${Tag}", 65 | "value": "" 66 | } 67 | ], 68 | "spacing": "Small", 69 | "$data": "${ParsedRepoTags}", 70 | "wrap": true, 71 | "size": "Small", 72 | "weight": "Bolder" 73 | } 74 | ], 75 | "separator": true, 76 | "width": "auto" 77 | }, 78 | { 79 | "type": "Column", 80 | "items": [ 81 | { 82 | "type": "TextBlock", 83 | "weight": "Lighter", 84 | "text": "SIZE", 85 | "wrap": true, 86 | "size": "Small", 87 | "isSubtle": true 88 | }, 89 | { 90 | "type": "TextBlock", 91 | "spacing": "Small", 92 | "text": "${SizeString}", 93 | "wrap": true, 94 | "weight": "Bolder" 95 | } 96 | ], 97 | "width": "auto", 98 | "separator": true, 99 | "spacing": "Medium" 100 | }, 101 | { 102 | "type": "Column", 103 | "items": [ 104 | { 105 | "type": "TextBlock", 106 | "weight": "Lighter", 107 | "text": "LAST PUSHED", 108 | "wrap": true, 109 | "size": "Small", 110 | "isSubtle": true 111 | }, 112 | { 113 | "type": "TextBlock", 114 | "spacing": "Small", 115 | "text": "{{DATE(${Time})}} - {{TIME(${Time})}}", 116 | "wrap": true, 117 | "weight": "Bolder" 118 | } 119 | ], 120 | "width": "auto", 121 | "separator": true, 122 | "spacing": "Medium" 123 | } 124 | ], 125 | "style": "default", 126 | "separator": true 127 | } 128 | ], 129 | "actions": [ 130 | { 131 | "type": "Action.OpenUrl", 132 | "title": "Go to image", 133 | "url": "${url}" 134 | } 135 | ], 136 | "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", 137 | "version": "1.5" 138 | } -------------------------------------------------------------------------------- /docs/index.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drone-plugins/drone-docker/8c5277ae89fb269de94adc231a1236a19cc39a6f/docs/index.json -------------------------------------------------------------------------------- /git-hooks/.gitleaksignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drone-plugins/drone-docker/8c5277ae89fb269de94adc231a1236a19cc39a6f/git-hooks/.gitleaksignore -------------------------------------------------------------------------------- /git-hooks/README.md: -------------------------------------------------------------------------------- 1 | This document explains on how to install certain git hooks globally for all repositories in your machine. 2 | 3 | Step 1: git clone https://github.com/drone-plugins/drone-docker.git 4 | Step 2: cd git-hooks 5 | Step 3: Run install.sh 6 | 7 | "install.sh" script will create .git_template in the user directory and will put the git hook and its dependent scripts in it. Along with the .git_template folder, it will add 2 sections "init" and "hooks boolean" in the .gitconfig file in the same user's root directory. 8 | After running "install.sh" if you create/clone a new git repository then all the hooks will get install automatically for the git repository. In case of existing git repository copy the contents of ~/.git_template/hooks into the .git/hooks directory of existing git repository. -------------------------------------------------------------------------------- /git-hooks/hooks/git-leaks-pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Helper script to be used as a pre-commit hook. 4 | 5 | echo "This hook checks for any secrets getting pushed as part of commit. If you feel that scan is false positive. \ 6 | Then add the exclusion in .gitleaksignore file. For more info visit: https://github.com/zricethezav/gitleaks" 7 | 8 | GIT_LEAKS_PRE_COMMIT=s$(git config --bool hook.pre-commit.gitleak) 9 | 10 | echo "INFO: Scanning Commits information for any GIT LEAKS" 11 | gitleaks protect --staged -v --exit-code=100 12 | STATUS=$? 13 | if [ $STATUS = 100 ]; then 14 | echo "WARNING: GIT LEAKS has detected sensitive information in your changes. Please remove them or add them (IF NON-SENSITIVE) in .gitleaksignore file." 15 | else 16 | exit 0 17 | fi -------------------------------------------------------------------------------- /git-hooks/hooks/git-leaks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Helper script to be used as a pre-commit hook. 4 | 5 | echo "This hook checks for any secrets getting pushed as part of commit. If you feel that scan is false positive. \ 6 | Then add the exclusion in .gitleaksignore file. For more info visit: https://github.com/zricethezav/gitleaks" 7 | 8 | GIT_LEAKS=$(git config --bool hook.pre-push.gitleaks) 9 | 10 | echo "INFO: Scanning Commits information for any GIT LEAKS" 11 | gitleaks detect -s ./ --log-level=debug --log-opts=-1 -v 12 | STATUS=$? 13 | if [ $STATUS != 0 ]; then 14 | echo "WARNING: GIT LEAKS has detected sensitive information in your changes. Please remove them or add them (IF NON-SENSITIVE) in .gitleaksignore file." 15 | exit $STATUS 16 | else 17 | exit 0 18 | fi -------------------------------------------------------------------------------- /git-hooks/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GL_SCRIPT_PATH="$HOME/.git_template/hooks/git-leaks-pre-commit.sh" 4 | 5 | pushd `dirname $0` > /dev/null && cd ../.. && BASEDIR=$(pwd -L) && popd > /dev/null 6 | BASENAME=`basename $0` 7 | 8 | if git rev-parse --verify HEAD >/dev/null 2>&1 9 | then 10 | against=HEAD 11 | else 12 | #Initial commit : diff against an empty tree object 13 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 14 | fi 15 | 16 | GIT_LEAKS_PRE_COMMIT=hook.pre-commit.gitleaks 17 | if [ "`git config $GIT_LEAKS_PRE_COMMIT`" == "false" ] 18 | then 19 | echo -e '\033[0;31m' checking git leaks is disabled - to enable: '\033[0;37m'git config --unset $GIT_LEAKS_PRE_COMMIT '\033[0m' 20 | echo -e '\033[0;34m' checking git leaks ... to enable: '\033[0;37m'git config --add $GIT_LEAKS_PRE_COMMIT true '\033[0m' 21 | else 22 | echo -e '\033[0;34m' checking for git leaks... 23 | [ -f "${GL_SCRIPT_PATH}" ] && . ${GL_SCRIPT_PATH} || echo "ERROR: Hook Script Not Found..." && exit 404 24 | fi -------------------------------------------------------------------------------- /git-hooks/hooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GL_SCRIPT_PATH="$HOME/.git_template/hooks/git-leaks.sh" 4 | 5 | pushd `dirname $0` > /dev/null && cd ../.. && BASEDIR=$(pwd -L) && popd > /dev/null 6 | BASENAME=`basename $0` 7 | 8 | if git rev-parse --verify HEAD >/dev/null 2>&1 9 | then 10 | against=HEAD 11 | else 12 | #Initial commit : diff against an empty tree object 13 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 14 | fi 15 | 16 | GIT_LEAKS=hook.pre-push.gitleaks 17 | if [ "`git config $GIT_LEAKS`" == "false" ] 18 | then 19 | echo -e '\033[0;31m' checking git leaks is disabled - to enable: '\033[0;37m'git config --unset $GIT_LEAKS '\033[0m' 20 | echo -e '\033[0;34m' checking git leaks ... to enable: '\033[0;37m'git config --add $GIT_LEAKS true '\033[0m' 21 | else 22 | echo -e '\033[0;34m' checking for git leaks... 23 | [ -f "${GL_SCRIPT_PATH}" ] && . ${GL_SCRIPT_PATH} || echo "ERROR: Hook Script Not Found..." && exit 404 24 | fi -------------------------------------------------------------------------------- /git-hooks/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #Function to check if package is installed or not 4 | #args: $1: Name of the Package 5 | function check_package_installed() { 6 | LOCAL_PACKAGE_NAME=$1 7 | echo "Checking if $LOCAL_PACKAGE_NAME is installed or not..." 8 | brew list $LOCAL_PACKAGE_NAME 9 | if [ "$?" -eq 1 ];then 10 | echo "Installing $LOCAL_PACKAGE_NAME package..." 11 | brew install $LOCAL_PACKAGE_NAME 12 | fi 13 | } 14 | 15 | function create_git_template() { 16 | cd $BASEDIR 17 | mkdir -p ~/.git_template/hooks 18 | git config --global init.templatedir ${GIT_TEMPLATE} 19 | git config --global --add $GIT_LEAKS true 20 | git config --global --add $GIT_LEAKS_PRE_COMMIT true 21 | find hooks/ -type f -exec cp "{}" ~/.git_template/hooks \; 22 | #cp -f hooks/* ~/.git_template/hooks 23 | cat ~/.gitconfig 24 | } 25 | 26 | GIT_TEMPLATE="~/.git_template" 27 | GIT_LEAKS=hook.pre-push.gitleaks 28 | GIT_LEAKS_PRE_COMMIT=hook.pre-commit.gitleaks 29 | 30 | pushd `dirname $0` && BASEDIR=$(pwd -L) && popd 31 | 32 | echo This script will install hooks that run scripts that could be updated without notice. 33 | 34 | while true; do 35 | read -p "Do you wish to install these hooks?" yn 36 | case $yn in 37 | [Yy]* ) check_package_installed "gitleaks"; 38 | break;; 39 | [Nn]* ) exit;; 40 | * ) echo "Please answer yes or no.";; 41 | esac 42 | done 43 | 44 | create_git_template -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/drone-plugins/drone-docker 2 | 3 | require ( 4 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 5 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 6 | github.com/aws/aws-sdk-go v1.26.7 7 | github.com/coreos/go-semver v0.3.0 8 | github.com/dchest/uniuri v1.2.0 9 | github.com/drone-plugins/drone-plugin-lib v0.4.1 10 | github.com/drone/drone-go v1.7.1 11 | github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 12 | github.com/joho/godotenv v1.3.0 13 | github.com/pkg/errors v0.9.1 14 | github.com/sirupsen/logrus v1.9.0 15 | github.com/stretchr/testify v1.10.0 16 | github.com/urfave/cli v1.22.2 17 | golang.org/x/oauth2 v0.13.0 18 | google.golang.org/api v0.146.0 19 | ) 20 | 21 | require ( 22 | cloud.google.com/go/compute v1.23.1 // indirect 23 | cloud.google.com/go/compute/metadata v0.2.3 // indirect 24 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect 25 | github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect 26 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 27 | github.com/davecgh/go-spew v1.1.1 // indirect 28 | github.com/golang-jwt/jwt/v5 v5.2.1 // indirect 29 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 30 | github.com/golang/protobuf v1.5.3 // indirect 31 | github.com/google/s2a-go v0.1.7 // indirect 32 | github.com/google/uuid v1.6.0 // indirect 33 | github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect 34 | github.com/googleapis/gax-go/v2 v2.12.0 // indirect 35 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect 36 | github.com/kylelemons/godebug v1.1.0 // indirect 37 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect 38 | github.com/pmezard/go-difflib v1.0.0 // indirect 39 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 40 | go.opencensus.io v0.24.0 // indirect 41 | golang.org/x/crypto v0.36.0 // indirect 42 | golang.org/x/net v0.37.0 // indirect 43 | golang.org/x/sys v0.31.0 // indirect 44 | golang.org/x/text v0.23.0 // indirect 45 | google.golang.org/appengine v1.6.8 // indirect 46 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect 47 | google.golang.org/grpc v1.59.0 // indirect 48 | google.golang.org/protobuf v1.31.0 // indirect 49 | gopkg.in/yaml.v2 v2.2.8 // indirect 50 | gopkg.in/yaml.v3 v3.0.1 // indirect 51 | ) 52 | 53 | go 1.23.0 54 | 55 | toolchain go1.23.7 56 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= 3 | cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= 4 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 5 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 6 | github.com/99designs/httpsignatures-go v0.0.0-20170731043157-88528bf4ca7e/go.mod h1:Xa6lInWHNQnuWoF0YPSsx+INFA9qk7/7pTjwb3PInkY= 7 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo= 8 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4= 9 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= 10 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= 11 | github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= 12 | github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= 13 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= 14 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= 15 | github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= 16 | github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= 17 | github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= 18 | github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 19 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 20 | github.com/aws/aws-sdk-go v1.26.7 h1:ObjEnmzvSdYy8KVd3me7v/UMyCn81inLy2SyoIPoBkg= 21 | github.com/aws/aws-sdk-go v1.26.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 22 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 23 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 24 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 25 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 26 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 27 | github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 28 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 29 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 30 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 31 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 32 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 33 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 34 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 | github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g= 36 | github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY= 37 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 38 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 39 | github.com/drone-plugins/drone-plugin-lib v0.4.1 h1:47rZlmcMpr1hSp+6Gl+1Z4t+efi/gMQU3lxukC1Yg64= 40 | github.com/drone-plugins/drone-plugin-lib v0.4.1/go.mod h1:KwCu92jFjHV3xv2hu5Qg/8zBNvGwbhoJDQw/EwnTvoM= 41 | github.com/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw= 42 | github.com/drone/drone-go v1.7.1/go.mod h1:fxCf9jAnXDZV1yDr0ckTuWd1intvcQwfJmTRpTZ1mXg= 43 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 44 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 45 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 46 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 47 | github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= 48 | github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 49 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 50 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 51 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 52 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 53 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 54 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 55 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 56 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 57 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 58 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 59 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 60 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 61 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 62 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 63 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 64 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 65 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 66 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 67 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 68 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 69 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 70 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 71 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 72 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 73 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 74 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 75 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 76 | github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= 77 | github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= 78 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 79 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 80 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 81 | github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= 82 | github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= 83 | github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= 84 | github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= 85 | github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 h1:X3Xxno5Ji8idrNiUoFc7QyXpqhSYlDRYQmc7mlpMBzU= 86 | github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs= 87 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= 88 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 89 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 90 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 91 | github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= 92 | github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= 93 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 94 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 95 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 96 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 97 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 98 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 99 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= 100 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 101 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 102 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 103 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 104 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 105 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 106 | github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= 107 | github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= 108 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 109 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 110 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 111 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 112 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 113 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 114 | github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= 115 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 116 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 117 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 118 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 119 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 120 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 121 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 122 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 123 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 124 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 125 | github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= 126 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 127 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 128 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 129 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 130 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 131 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 132 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 133 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= 134 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 135 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 136 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 137 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 138 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 139 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 140 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 141 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 142 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 143 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 144 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 145 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 146 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 147 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 148 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 149 | golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= 150 | golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 151 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 152 | golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= 153 | golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= 154 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 155 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 156 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 157 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 158 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 159 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 160 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 161 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 162 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 163 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 164 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 165 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 166 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 167 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 168 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 169 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 170 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 171 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 172 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 173 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 174 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 175 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 176 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 177 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 178 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 179 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 180 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 181 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 182 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 183 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 184 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 185 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 186 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 187 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 188 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 189 | google.golang.org/api v0.146.0 h1:9aBYT4vQXt9dhCuLNfwfd3zpwu8atg0yPkjBymwSrOM= 190 | google.golang.org/api v0.146.0/go.mod h1:OARJqIfoYjXJj4C1AiBSXYZt03qsoz8FQYU6fBEfrHM= 191 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 192 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 193 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 194 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 195 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 196 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 197 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 198 | google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8= 199 | google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE= 200 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI= 201 | google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= 202 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= 203 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= 204 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 205 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 206 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 207 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 208 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 209 | google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= 210 | google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 211 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 212 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 213 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 214 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 215 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 216 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 217 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 218 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 219 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 220 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 221 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 222 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 223 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 224 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 225 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 226 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 227 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 228 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 229 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 230 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 231 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 232 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 233 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 234 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 235 | -------------------------------------------------------------------------------- /internal/docker/config.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | ) 9 | 10 | const ( 11 | v2HubRegistryURL string = "https://registry.hub.docker.com/v2/" 12 | v1RegistryURL string = "https://index.docker.io/v1/" // Default registry 13 | v2RegistryURL string = "https://index.docker.io/v2/" // v2 registry is not supported 14 | ) 15 | 16 | type ( 17 | Auth struct { 18 | Auth string `json:"auth"` 19 | } 20 | 21 | Config struct { 22 | Auths map[string]Auth `json:"auths"` 23 | CredHelpers map[string]string `json:"credHelpers,omitempty"` 24 | } 25 | ) 26 | 27 | type RegistryCredentials struct { 28 | Registry string 29 | Username string 30 | Password string 31 | } 32 | 33 | func NewConfig() *Config { 34 | return &Config{ 35 | Auths: make(map[string]Auth), 36 | CredHelpers: make(map[string]string), 37 | } 38 | } 39 | 40 | func (c *Config) SetAuth(registry, username, password string) { 41 | authBytes := []byte(username + ":" + password) 42 | encodedString := base64.StdEncoding.EncodeToString(authBytes) 43 | c.Auths[registry] = Auth{Auth: encodedString} 44 | } 45 | 46 | func (c *Config) SetCredHelper(registry, helper string) { 47 | c.CredHelpers[registry] = helper 48 | } 49 | 50 | func (c *Config) CreateDockerConfigJson(credentials []RegistryCredentials) ([]byte, error) { 51 | for _, cred := range credentials { 52 | if cred.Registry != "" { 53 | 54 | if cred.Username == "" { 55 | return nil, fmt.Errorf("Username must be specified for registry: %s", cred.Registry) 56 | } 57 | if cred.Password == "" { 58 | return nil, fmt.Errorf("Password must be specified for registry: %s", cred.Registry) 59 | } 60 | c.SetAuth(cred.Registry, cred.Username, cred.Password) 61 | } 62 | } 63 | 64 | jsonBytes, err := json.Marshal(c) 65 | if err != nil { 66 | return nil, errors.New("failed to serialize docker config json") 67 | } 68 | 69 | return jsonBytes, nil 70 | } 71 | -------------------------------------------------------------------------------- /internal/docker/config_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | "path/filepath" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | const ( 14 | RegistryV1 string = "https://index.docker.io/v1/" 15 | RegistryV2 string = "https://index.docker.io/v2/" 16 | RegistryECRPublic string = "public.ecr.aws" 17 | ) 18 | 19 | func TestConfig(t *testing.T) { 20 | c := NewConfig() 21 | assert.NotNil(t, c.Auths) 22 | assert.NotNil(t, c.CredHelpers) 23 | 24 | c.SetAuth(RegistryV1, "test", "password") 25 | expectedAuth := Auth{Auth: "dGVzdDpwYXNzd29yZA=="} 26 | assert.Equal(t, expectedAuth, c.Auths[RegistryV1]) 27 | 28 | c.SetCredHelper(RegistryECRPublic, "ecr-login") 29 | assert.Equal(t, "ecr-login", c.CredHelpers[RegistryECRPublic]) 30 | 31 | tempDir, err := ioutil.TempDir("", "docker-config-test") 32 | assert.NoError(t, err) 33 | defer os.RemoveAll(tempDir) 34 | 35 | credentials := []RegistryCredentials{ 36 | { 37 | Registry: "https://index.docker.io/v1/", 38 | Username: "user1", 39 | Password: "pass1", 40 | }, 41 | { 42 | Registry: "gcr.io", 43 | Username: "user2", 44 | Password: "pass2", 45 | }, 46 | } 47 | 48 | jsonBytes, err := c.CreateDockerConfigJson(credentials) 49 | assert.NoError(t, err) 50 | 51 | configPath := filepath.Join(tempDir, "config.json") 52 | err = ioutil.WriteFile(configPath, jsonBytes, 0644) 53 | assert.NoError(t, err) 54 | 55 | data, err := ioutil.ReadFile(configPath) 56 | assert.NoError(t, err) 57 | 58 | var configFromFile Config 59 | err = json.Unmarshal(data, &configFromFile) 60 | assert.NoError(t, err) 61 | 62 | assert.Equal(t, c.Auths, configFromFile.Auths) 63 | assert.Equal(t, c.CredHelpers, configFromFile.CredHelpers) 64 | } 65 | -------------------------------------------------------------------------------- /internal/gcp/tokenutil.go: -------------------------------------------------------------------------------- 1 | package gcp 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "golang.org/x/oauth2" 8 | "google.golang.org/api/iamcredentials/v1" 9 | "google.golang.org/api/option" 10 | "google.golang.org/api/sts/v1" 11 | ) 12 | 13 | type staticTokenSource struct { 14 | token *oauth2.Token 15 | } 16 | 17 | func (s *staticTokenSource) Token() (*oauth2.Token, error) { 18 | return s.token, nil 19 | } 20 | 21 | func GetFederalToken(idToken, projectNumber, poolId, providerId string) (string, error) { 22 | ctx := context.Background() 23 | stsService, err := sts.NewService(ctx, option.WithoutAuthentication()) 24 | if err != nil { 25 | return "", err 26 | } 27 | audience := fmt.Sprintf("//iam.googleapis.com/projects/%s/locations/global/workloadIdentityPools/%s/providers/%s", projectNumber, poolId, providerId) 28 | tokenRequest := &sts.GoogleIdentityStsV1ExchangeTokenRequest{ 29 | GrantType: "urn:ietf:params:oauth:grant-type:token-exchange", 30 | SubjectToken: idToken, 31 | Audience: audience, 32 | Scope: "https://www.googleapis.com/auth/cloud-platform", 33 | RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", 34 | SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token", 35 | } 36 | tokenResponse, err := stsService.V1.Token(tokenRequest).Do() 37 | if err != nil { 38 | return "", err 39 | } 40 | 41 | return tokenResponse.AccessToken, nil 42 | } 43 | 44 | func GetGoogleCloudAccessToken(federatedToken string, serviceAccountEmail string) (string, error) { 45 | ctx := context.Background() 46 | tokenSource := &staticTokenSource{ 47 | token: &oauth2.Token{AccessToken: federatedToken}, 48 | } 49 | service, err := iamcredentials.NewService(ctx, option.WithTokenSource(tokenSource)) 50 | if err != nil { 51 | return "", err 52 | } 53 | 54 | name := "projects/-/serviceAccounts/" + serviceAccountEmail 55 | rb := &iamcredentials.GenerateAccessTokenRequest{ 56 | Scope: []string{"https://www.googleapis.com/auth/cloud-platform"}, 57 | } 58 | 59 | resp, err := service.Projects.ServiceAccounts.GenerateAccessToken(name, rb).Do() 60 | if err != nil { 61 | return "", err 62 | } 63 | 64 | return resp.AccessToken, nil 65 | } 66 | -------------------------------------------------------------------------------- /scripts/windows/latest.ps1: -------------------------------------------------------------------------------- 1 | # this script is used by the continuous integration server to 2 | # build and publish the docker image for a commit to master. 3 | $ErrorActionPreference = "Stop" 4 | 5 | $env:GOOS="windows" 6 | $env:GOARCH="amd64" 7 | $env:CGO_ENABLED="0" 8 | 9 | if (-not (Test-Path env:VERSION)) { 10 | $env:VERSION="1809" 11 | } 12 | 13 | if (-not (Test-Path env:REGISTRY)) { 14 | $env:REGISTRY="docker" 15 | } 16 | 17 | echo $env:GOOS 18 | echo $env:GOARCH 19 | echo $env:VERSION 20 | echo $env:REGISTRY 21 | 22 | # build the binary 23 | Write-Host "+ go build -o release/windows/amd64/drone-${env:REGISTRY}.exe ./cmd/drone-${env:REGISTRY}"; 24 | go build -o release/windows/amd64/drone-${env:REGISTRY}.exe ./cmd/drone-${env:REGISTRY} 25 | 26 | # build and publish the docker image 27 | docker login -u ${env:USERNAME} -p ${env:PASSWORD} 28 | Write-Host "+ docker build -f docker/${env:REGISTRY}/Dockerfile.windows.amd64.${env:VERSION} -t plugins/${env:REGISTRY}:windows-${env:VERSION}-amd64 ."; 29 | docker build -f docker/${env:REGISTRY}/Dockerfile.windows.amd64.${env:VERSION} -t plugins/${env:REGISTRY}:windows-${env:VERSION}-amd64 . 30 | Write-Host "+ docker push plugins/${env:REGISTRY}:windows-${env:VERSION}-amd64" 31 | docker push plugins/${env:REGISTRY}:windows-${env:VERSION}-amd64 32 | 33 | # remove images from local cache 34 | Write-Host "+ docker rmi plugins/${env:REGISTRY}:windows-${env:VERSION}-amd64" 35 | docker rmi plugins/${env:REGISTRY}:windows-${env:VERSION}-amd64 36 | -------------------------------------------------------------------------------- /scripts/windows/tag.ps1: -------------------------------------------------------------------------------- 1 | # this script is used by the continuous integration server to 2 | # build and publish the docker image for a tagged revsision. 3 | $ErrorActionPreference = "Stop" 4 | 5 | $env:GOOS="windows" 6 | $env:GOARCH="amd64" 7 | $env:CGO_ENABLED="0" 8 | 9 | if (-not (Test-Path env:VERSION)) { 10 | $env:VERSION="1809" 11 | } 12 | 13 | if (-not (Test-Path env:DRONE_SEMVER_SHORT)) { 14 | echo "missing semver" 15 | exit 1 16 | } 17 | 18 | if (-not (Test-Path env:REGISTRY)) { 19 | $env:REGISTRY="docker" 20 | } 21 | 22 | # define the image tags 23 | $env:IMAGE_PATCH="plugins/${env:REGISTRY}:${env:DRONE_SEMVER_SHORT}-windows-${env:VERSION}-amd64" 24 | $env:IMAGE_MAJOR="plugins/${env:REGISTRY}:${env:DRONE_SEMVER_MAJOR}-windows-${env:VERSION}-amd64" 25 | $env:IMAGE_MINOR="plugins/${env:REGISTRY}:${env:DRONE_SEMVER_MAJOR}.${env:DRONE_SEMVER_MINOR}-windows-${env:VERSION}-amd64" 26 | 27 | echo "build environment:" 28 | echo $env:GOOS 29 | echo $env:GOARCH 30 | echo $env:VERSION 31 | 32 | # build the binary 33 | Write-Host "+ go build -o release/windows/amd64/drone-${env:REGISTRY}.exe ./cmd/drone-${env:REGISTRY}" 34 | go build -o release/windows/amd64/drone-${env:REGISTRY}.exe ./cmd/drone-${env:REGISTRY} 35 | 36 | # authenticate with the docker registry 37 | docker login -u ${env:USERNAME} -p ${env:PASSWORD} 38 | 39 | echo "building images:" 40 | echo ${env:IMAGE_PATCH} 41 | echo ${env:IMAGE_MINOR} 42 | echo ${env:IMAGE_MAJOR} 43 | 44 | # build and tag the docker images 45 | Write-Host "+ docker build -f docker/${env:REGISTRY}/Dockerfile.windows.amd64.${env:VERSION} -t ${env:IMAGE_PATCH} ." 46 | docker build -f docker/${env:REGISTRY}/Dockerfile.windows.amd64.${env:VERSION} -t ${env:IMAGE_PATCH} . 47 | Write-Host "+ docker tag ${env:IMAGE_PATCH} ${env:IMAGE_MAJOR}" 48 | docker tag ${env:IMAGE_PATCH} ${env:IMAGE_MAJOR} 49 | Write-Host "+ docker tag ${env:IMAGE_PATCH} ${env:IMAGE_MINOR}" 50 | docker tag ${env:IMAGE_PATCH} ${env:IMAGE_MINOR} 51 | 52 | # publish the docker images 53 | Write-Host "+ docker push ${env:IMAGE_MAJOR}" 54 | docker push ${env:IMAGE_MAJOR} 55 | Write-Host "+ docker push ${env:IMAGE_MINOR}" 56 | docker push ${env:IMAGE_MINOR} 57 | Write-Host "+ docker push ${env:IMAGE_PATCH}" 58 | docker push ${env:IMAGE_PATCH} 59 | 60 | # remove images after from local cache 61 | Write-Host "+ docker rmi ${env:IMAGE_MAJOR}" 62 | docker rmi ${env:IMAGE_MAJOR} 63 | Write-Host "+ docker rmi ${env:IMAGE_MINOR}" 64 | docker rmi ${env:IMAGE_MINOR} 65 | Write-Host "+ docker rmi ${env:IMAGE_PATCH}" 66 | docker rmi ${env:IMAGE_PATCH} 67 | -------------------------------------------------------------------------------- /tags.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/coreos/go-semver/semver" 8 | ) 9 | 10 | // DefaultTagSuffix returns a set of default suggested tags 11 | // based on the commit ref with an attached suffix. 12 | func DefaultTagSuffix(ref, suffix string) ([]string, error) { 13 | tags, err := DefaultTags(ref) 14 | if err != nil { 15 | return nil, err 16 | } 17 | if len(suffix) == 0 { 18 | return tags, nil 19 | } 20 | for i, tag := range tags { 21 | if tag == "latest" { 22 | tags[i] = suffix 23 | } else { 24 | tags[i] = fmt.Sprintf("%s-%s", tag, suffix) 25 | } 26 | } 27 | return tags, nil 28 | } 29 | 30 | func splitOff(input string, delim string) string { 31 | parts := strings.SplitN(input, delim, 2) 32 | 33 | if len(parts) == 2 { 34 | return parts[0] 35 | } 36 | 37 | return input 38 | } 39 | 40 | // DefaultTags returns a set of default suggested tags based on 41 | // the commit ref. 42 | func DefaultTags(ref string) ([]string, error) { 43 | if !strings.HasPrefix(ref, "refs/tags/") { 44 | return []string{"latest"}, nil 45 | } 46 | v := stripTagPrefix(ref) 47 | version, err := semver.NewVersion(v) 48 | if err != nil { 49 | return []string{"latest"}, err 50 | } 51 | if version.PreRelease != "" || version.Metadata != "" { 52 | return []string{ 53 | version.String(), 54 | }, nil 55 | } 56 | 57 | v = stripTagPrefix(ref) 58 | v = splitOff(splitOff(v, "+"), "-") 59 | dotParts := strings.SplitN(v, ".", 3) 60 | 61 | if version.Major == 0 { 62 | return []string{ 63 | fmt.Sprintf("%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor), 64 | fmt.Sprintf("%0*d.%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor, len(dotParts[2]), version.Patch), 65 | }, nil 66 | } 67 | return []string{ 68 | fmt.Sprintf("%0*d", len(dotParts[0]), version.Major), 69 | fmt.Sprintf("%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor), 70 | fmt.Sprintf("%0*d.%0*d.%0*d", len(dotParts[0]), version.Major, len(dotParts[1]), version.Minor, len(dotParts[2]), version.Patch), 71 | }, nil 72 | } 73 | 74 | // UseDefaultTag for keep only default branch for latest tag 75 | func UseDefaultTag(ref, defaultBranch string) bool { 76 | if strings.HasPrefix(ref, "refs/tags/") { 77 | return true 78 | } 79 | if stripHeadPrefix(ref) == defaultBranch { 80 | return true 81 | } 82 | return false 83 | } 84 | 85 | func stripHeadPrefix(ref string) string { 86 | return strings.TrimPrefix(ref, "refs/heads/") 87 | } 88 | 89 | func stripTagPrefix(ref string) string { 90 | ref = strings.TrimPrefix(ref, "refs/tags/") 91 | ref = strings.TrimPrefix(ref, "v") 92 | return ref 93 | } 94 | -------------------------------------------------------------------------------- /tags_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func Test_stripTagPrefix(t *testing.T) { 9 | var tests = []struct { 10 | Before string 11 | After string 12 | }{ 13 | {"refs/tags/1.0.0", "1.0.0"}, 14 | {"refs/tags/v1.0.0", "1.0.0"}, 15 | {"v1.0.0", "1.0.0"}, 16 | } 17 | 18 | for _, test := range tests { 19 | got, want := stripTagPrefix(test.Before), test.After 20 | if got != want { 21 | t.Errorf("Got tag %s, want %s", got, want) 22 | } 23 | } 24 | } 25 | 26 | func TestDefaultTags(t *testing.T) { 27 | var tests = []struct { 28 | Before string 29 | After []string 30 | }{ 31 | {"", []string{"latest"}}, 32 | {"refs/heads/master", []string{"latest"}}, 33 | {"refs/tags/0.9.0", []string{"0.9", "0.9.0"}}, 34 | {"refs/tags/1.0.0", []string{"1", "1.0", "1.0.0"}}, 35 | {"refs/tags/v1.0.0", []string{"1", "1.0", "1.0.0"}}, 36 | {"refs/tags/v1.0.0-alpha.1", []string{"1.0.0-alpha.1"}}, 37 | } 38 | 39 | for _, test := range tests { 40 | tags, err := DefaultTags(test.Before) 41 | if err != nil { 42 | t.Error(err) 43 | continue 44 | } 45 | got, want := tags, test.After 46 | if !reflect.DeepEqual(got, want) { 47 | t.Errorf("Got tag %v, want %v", got, want) 48 | } 49 | } 50 | } 51 | 52 | func TestDefaultTagsError(t *testing.T) { 53 | var tests = []string{ 54 | "refs/tags/x1.0.0", 55 | "refs/tags/20190203", 56 | } 57 | 58 | for _, test := range tests { 59 | _, err := DefaultTags(test) 60 | if err == nil { 61 | t.Errorf("Expect tag error for %s", test) 62 | } 63 | } 64 | } 65 | 66 | func TestDefaultTagSuffix(t *testing.T) { 67 | var tests = []struct { 68 | Before string 69 | Suffix string 70 | After []string 71 | }{ 72 | // without suffix 73 | { 74 | After: []string{"latest"}, 75 | }, 76 | { 77 | Before: "refs/tags/v1.0.0", 78 | After: []string{ 79 | "1", 80 | "1.0", 81 | "1.0.0", 82 | }, 83 | }, 84 | // with suffix 85 | { 86 | Suffix: "linux-amd64", 87 | After: []string{"linux-amd64"}, 88 | }, 89 | { 90 | Before: "refs/tags/v1.0.0", 91 | Suffix: "linux-amd64", 92 | After: []string{ 93 | "1-linux-amd64", 94 | "1.0-linux-amd64", 95 | "1.0.0-linux-amd64", 96 | }, 97 | }, 98 | { 99 | Suffix: "nanoserver", 100 | After: []string{"nanoserver"}, 101 | }, 102 | { 103 | Before: "refs/tags/v1.9.2", 104 | Suffix: "nanoserver", 105 | After: []string{ 106 | "1-nanoserver", 107 | "1.9-nanoserver", 108 | "1.9.2-nanoserver", 109 | }, 110 | }, 111 | { 112 | Before: "refs/tags/v18.06.0", 113 | Suffix: "nanoserver", 114 | After: []string{ 115 | "18-nanoserver", 116 | "18.06-nanoserver", 117 | "18.06.0-nanoserver", 118 | }, 119 | }, 120 | } 121 | 122 | for _, test := range tests { 123 | tag, err := DefaultTagSuffix(test.Before, test.Suffix) 124 | if err != nil { 125 | t.Error(err) 126 | continue 127 | } 128 | got, want := tag, test.After 129 | if !reflect.DeepEqual(got, want) { 130 | t.Errorf("Got tag %v, want %v", got, want) 131 | } 132 | } 133 | } 134 | 135 | func Test_stripHeadPrefix(t *testing.T) { 136 | type args struct { 137 | ref string 138 | } 139 | tests := []struct { 140 | args args 141 | want string 142 | }{ 143 | { 144 | args: args{ 145 | ref: "refs/heads/master", 146 | }, 147 | want: "master", 148 | }, 149 | } 150 | for _, tt := range tests { 151 | if got := stripHeadPrefix(tt.args.ref); got != tt.want { 152 | t.Errorf("stripHeadPrefix() = %v, want %v", got, tt.want) 153 | } 154 | } 155 | } 156 | 157 | func TestUseDefaultTag(t *testing.T) { 158 | type args struct { 159 | ref string 160 | defaultBranch string 161 | } 162 | tests := []struct { 163 | name string 164 | args args 165 | want bool 166 | }{ 167 | { 168 | name: "latest tag for default branch", 169 | args: args{ 170 | ref: "refs/heads/master", 171 | defaultBranch: "master", 172 | }, 173 | want: true, 174 | }, 175 | { 176 | name: "build from tags", 177 | args: args{ 178 | ref: "refs/tags/v1.0.0", 179 | defaultBranch: "master", 180 | }, 181 | want: true, 182 | }, 183 | { 184 | name: "skip build for not default branch", 185 | args: args{ 186 | ref: "refs/heads/develop", 187 | defaultBranch: "master", 188 | }, 189 | want: false, 190 | }, 191 | } 192 | for _, tt := range tests { 193 | if got := UseDefaultTag(tt.args.ref, tt.args.defaultBranch); got != tt.want { 194 | t.Errorf("%q. UseDefaultTag() = %v, want %v", tt.name, got, tt.want) 195 | } 196 | } 197 | } 198 | --------------------------------------------------------------------------------