diff --git a/.drone.yml b/.drone.yml
deleted file mode 100644
index 4a688bf36..000000000
--- a/.drone.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-kind: pipeline
-name: default
-
-workspace:
-  base: /go
-  path: src/github.com/drone/go-scm
-
-
-steps:
-- name: test
-  image: golang:1.11
-  commands:
-  - go get github.com/google/go-cmp/cmp
-  - go get github.com/h2non/gock
-  - go test -cover github.com/drone/go-scm/scm/...
diff --git a/.drone.yml.bak b/.drone.yml.bak
new file mode 100644
index 000000000..f723ac8f8
--- /dev/null
+++ b/.drone.yml.bak
@@ -0,0 +1,56 @@
+---
+kind: pipeline
+type: vm
+name: default
+
+pool:
+  use: ubuntu
+
+steps:
+- name: vet
+  image: golang:1.22
+  commands:
+  - go vet ./...
+  volumes:
+  - name: gopath
+    path: /go
+  depends_on:
+  - clone
+
+- name: test
+  image: golang:1.22
+  commands:
+  - go test -cover ./...
+  volumes:
+  - name: gopath
+    path: /go
+  depends_on:
+  - vet
+
+- name: check go.mod is up to date
+  image: golang:1.22
+  commands:
+  - cp go.mod go.mod.bak
+  - go mod tidy
+  - diff go.mod go.mod.bak || (echo "go.mod is not up to date" && exit 1)
+  volumes:
+  - name: gopath
+    path: /go
+  depends_on:
+  - vet
+
+- name: golangci-lint
+  image: golangci/golangci-lint:v1.48-alpine
+  commands:
+  - golangci-lint run --timeout 500s --new-from-rev=HEAD~
+  volumes:
+  - name: gopath
+    path: /go
+  depends_on:
+  - clone
+
+volumes:
+- name: gopath
+  temp: {}
+
+...
diff --git a/.github_changelog_generator b/.github_changelog_generator
new file mode 100644
index 000000000..3051fa60c
--- /dev/null
+++ b/.github_changelog_generator
@@ -0,0 +1 @@
+since-tag=v1.15.1
diff --git a/.gitignore b/.gitignore
index cbc620eda..2cada1ba5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
 *.out
 _*.md
 .vscode
+.idea
 cmd
\ No newline at end of file
diff --git a/.golangci.json b/.golangci.json
new file mode 100644
index 000000000..dc836dacc
--- /dev/null
+++ b/.golangci.json
@@ -0,0 +1,51 @@
+{
+    "linters": {
+        "disable-all": true,
+        "enable": [
+           "govet",
+           "revive",
+           "goimports",
+           "misspell",
+           "ineffassign",
+           "gofmt"
+        ]
+    },
+    "linters-settings": {
+        "govet": {
+            "check-shadowing": false
+        },
+        "gofmt": {
+            "simplify": false
+        }
+    },
+    "run": {
+        "skip-dirs": [
+            "vendor",
+            "tests",
+            "pkg/client",
+            "pkg/generated"
+        ],
+        "tests": false,
+        "timeout": "10m"
+    },
+    "issues": {
+        "exclude-rules": [
+            {
+                "linters": "govet",
+                "text": "^(nilness|structtag)"
+            },
+            {
+                "linters": "revive",
+                "text": "should have comment"
+            },
+            {
+                "linters": "revive",
+                "text": "should be of the form"
+            },
+            {
+                "linters": "typecheck",
+                "text": "imported but not used as apierrors"
+            }
+        ]
+    }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2311d619d..d63a81137 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,596 @@
 # Changelog
+
+## [Unreleased](https://github.com/drone/go-scm/tree/HEAD)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.34.3...HEAD)
+
+**Closed issues:**
+
+- Cron Jobs don't run with Gitea-scm [\#292](https://github.com/drone/go-scm/issues/292)
+
+**Merged pull requests:**
+
+- feat: change as per new contract of webhook in harness code [\#294](https://github.com/drone/go-scm/pull/294) ([abhinav-harness](https://github.com/abhinav-harness))
+- Stash pr commits pagination [\#293](https://github.com/drone/go-scm/pull/293) ([raghavharness](https://github.com/raghavharness))
+- Added support for branch names containing '&' and '\#' for GetFile Operations. [\#291](https://github.com/drone/go-scm/pull/291) ([senjucanon2](https://github.com/senjucanon2))
+
+## [v1.34.3](https://github.com/drone/go-scm/tree/v1.34.3) (2023-12-20)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.34.2...v1.34.3)
+
+**Merged pull requests:**
+
+- feat: add pr link as coming from new webhook [\#290](https://github.com/drone/go-scm/pull/290) ([abhinav-harness](https://github.com/abhinav-harness))
+
+## [v1.34.2](https://github.com/drone/go-scm/tree/v1.34.2) (2023-12-20)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.34.1...v1.34.2)
+
+**Merged pull requests:**
+
+- feat: support more events in webhook parse in go-scm for gitness [\#289](https://github.com/drone/go-scm/pull/289) ([abhinav-harness](https://github.com/abhinav-harness))
+- fix: ref should be branch name for harness code [\#288](https://github.com/drone/go-scm/pull/288) ([abhinav-harness](https://github.com/abhinav-harness))
+
+## [v1.34.1](https://github.com/drone/go-scm/tree/v1.34.1) (2023-12-08)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.34.0...v1.34.1)
+
+**Fixed bugs:**
+
+- fix: use opts for harness list commits [\#286](https://github.com/drone/go-scm/pull/286) ([abhinav-harness](https://github.com/abhinav-harness))
+
+**Merged pull requests:**
+
+- \(maint\)  v1.34.1 release prep [\#287](https://github.com/drone/go-scm/pull/287) ([tphoney](https://github.com/tphoney))
+
+## [v1.34.0](https://github.com/drone/go-scm/tree/v1.34.0) (2023-12-07)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.33.0...v1.34.0)
+
+**Implemented enhancements:**
+
+- feat: add support for branch update for gitness [\#283](https://github.com/drone/go-scm/pull/283) ([abhinav-harness](https://github.com/abhinav-harness))
+
+**Merged pull requests:**
+
+- \(maint\) v1.34.0 prep [\#284](https://github.com/drone/go-scm/pull/284) ([tphoney](https://github.com/tphoney))
+
+## [v1.33.0](https://github.com/drone/go-scm/tree/v1.33.0) (2023-10-27)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.32.3...v1.33.0)
+
+**Implemented enhancements:**
+
+- feat: Add pr\_comment webhook for harness [\#280](https://github.com/drone/go-scm/pull/280) ([abhinav-harness](https://github.com/abhinav-harness))
+
+**Merged pull requests:**
+
+- \(maint\) prep 1.33.0 [\#281](https://github.com/drone/go-scm/pull/281) ([tphoney](https://github.com/tphoney))
+
+## [v1.32.3](https://github.com/drone/go-scm/tree/v1.32.3) (2023-10-11)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.32.2...v1.32.3)
+
+**Fixed bugs:**
+
+- fix: ref should have pullreq instead of pull for gitness [\#279](https://github.com/drone/go-scm/pull/279) ([abhinav-harness](https://github.com/abhinav-harness))
+- fix: ref should have pullreq instead of pull for gitness [\#278](https://github.com/drone/go-scm/pull/278) ([abhinav-harness](https://github.com/abhinav-harness))
+
+## [v1.32.2](https://github.com/drone/go-scm/tree/v1.32.2) (2023-10-03)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.32.1...v1.32.2)
+
+**Implemented enhancements:**
+
+- feat: Harness list commits api update as per new spec [\#277](https://github.com/drone/go-scm/pull/277) ([abhinav-harness](https://github.com/abhinav-harness))
+
+## [v1.32.1](https://github.com/drone/go-scm/tree/v1.32.1) (2023-09-27)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.32.0...v1.32.1)
+
+**Fixed bugs:**
+
+- fix: Gitness get content missing query param [\#275](https://github.com/drone/go-scm/pull/275) ([abhinav-harness](https://github.com/abhinav-harness))
+
+**Merged pull requests:**
+
+- \(maint\) prep for 1.32.1 [\#276](https://github.com/drone/go-scm/pull/276) ([tphoney](https://github.com/tphoney))
+- \(maint\) clean integration testing for stash [\#273](https://github.com/drone/go-scm/pull/273) ([tphoney](https://github.com/tphoney))
+
+## [v1.32.0](https://github.com/drone/go-scm/tree/v1.32.0) (2023-09-12)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.31.2...v1.32.0)
+
+**Implemented enhancements:**
+
+- \[feat\]: \[CDS-75848\]: Add new action type for github provider [\#270](https://github.com/drone/go-scm/pull/270) ([rathodmeetsatish](https://github.com/rathodmeetsatish))
+
+**Merged pull requests:**
+
+- \(maint\) release prep for 1.32.0 [\#272](https://github.com/drone/go-scm/pull/272) ([tphoney](https://github.com/tphoney))
+
+## [v1.31.2](https://github.com/drone/go-scm/tree/v1.31.2) (2023-08-31)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.31.1...v1.31.2)
+
+**Fixed bugs:**
+
+- fix: \[CODE-727\]: change branch in source and target for harness provider [\#264](https://github.com/drone/go-scm/pull/264) ([abhinav-harness](https://github.com/abhinav-harness))
+
+## [v1.31.1](https://github.com/drone/go-scm/tree/v1.31.1) (2023-08-29)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.31.0...v1.31.1)
+
+**Fixed bugs:**
+
+- Fix diff api response conversion for harness compareChange [\#269](https://github.com/drone/go-scm/pull/269) ([shubham149](https://github.com/shubham149))
+- Fix api name for fetching diff in harness driver [\#268](https://github.com/drone/go-scm/pull/268) ([shubham149](https://github.com/shubham149))
+- Fix compare change api result for harness [\#267](https://github.com/drone/go-scm/pull/267) ([shubham149](https://github.com/shubham149))
+
+## [v1.31.0](https://github.com/drone/go-scm/tree/v1.31.0) (2023-08-15)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.30.0...v1.31.0)
+
+**Implemented enhancements:**
+
+- \[IAC-941\]: PR comment creation for BitBucket [\#265](https://github.com/drone/go-scm/pull/265) ([scottyw-harness](https://github.com/scottyw-harness))
+- Implemented FindMembership method in organization service for gitea driver [\#263](https://github.com/drone/go-scm/pull/263) ([cod3rboy](https://github.com/cod3rboy))
+
+**Closed issues:**
+
+- \(missing feature\) add support to check organization membership in gitea driver [\#262](https://github.com/drone/go-scm/issues/262)
+
+**Merged pull requests:**
+
+- \(maint\) v1.31.0 release prep [\#266](https://github.com/drone/go-scm/pull/266) ([tphoney](https://github.com/tphoney))
+
+## [v1.30.0](https://github.com/drone/go-scm/tree/v1.30.0) (2023-07-19)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.29.1...v1.30.0)
+
+**Implemented enhancements:**
+
+- \[feat\]: \[CDS-73572\]: Support List Repo Live Search for all git providers [\#261](https://github.com/drone/go-scm/pull/261) ([adivishy1](https://github.com/adivishy1))
+- \[feat\]: \[CDS-73030\]: Support for text based branch filtration [\#260](https://github.com/drone/go-scm/pull/260) ([adivishy1](https://github.com/adivishy1))
+- feat: \[CDS-69341\]: add find user email api for github in go-scm [\#256](https://github.com/drone/go-scm/pull/256) ([shalini-agr](https://github.com/shalini-agr))
+
+**Fixed bugs:**
+
+- fix: \[CDS-67745\]: fix find user email api for bitbucket in go-scm [\#255](https://github.com/drone/go-scm/pull/255) ([shalini-agr](https://github.com/shalini-agr))
+- fix: \[CI-6978\] fixed gitlab webhook parse [\#253](https://github.com/drone/go-scm/pull/253) ([devkimittal](https://github.com/devkimittal))
+- Add required header for bitbucket server in commit API use-case to handle csrf failures [\#252](https://github.com/drone/go-scm/pull/252) ([mohitg0795](https://github.com/mohitg0795))
+
+**Merged pull requests:**
+
+- \(maint\) stash/bitbucket on prem v5 add push webhook test [\#257](https://github.com/drone/go-scm/pull/257) ([tphoney](https://github.com/tphoney))
+
+## [v1.29.1](https://github.com/drone/go-scm/tree/v1.29.1) (2023-02-16)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.29.0...v1.29.1)
+
+**Fixed bugs:**
+
+- \(fix\) - azure content list queryparam incorrect [\#249](https://github.com/drone/go-scm/pull/249) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+## [v1.29.0](https://github.com/drone/go-scm/tree/v1.29.0) (2023-02-15)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.28.1...v1.29.0)
+
+**Implemented enhancements:**
+
+- \(feat\) harness, add finduser [\#250](https://github.com/drone/go-scm/pull/250) ([tphoney](https://github.com/tphoney))
+- \(feat\) harness, fix create branch, PR calls [\#247](https://github.com/drone/go-scm/pull/247) ([tphoney](https://github.com/tphoney))
+- \(feat\) harness, add user and compare branches [\#246](https://github.com/drone/go-scm/pull/246) ([tphoney](https://github.com/tphoney))
+- \(feat\) harness, add list commits / branches [\#245](https://github.com/drone/go-scm/pull/245) ([tphoney](https://github.com/tphoney))
+- \(feat\) harness, add webhook parsing [\#244](https://github.com/drone/go-scm/pull/244) ([tphoney](https://github.com/tphoney))
+- fetch branch for bitbucket onprem [\#242](https://github.com/drone/go-scm/pull/242) ([devkimittal](https://github.com/devkimittal))
+- \(feat\) harness, add repo list [\#241](https://github.com/drone/go-scm/pull/241) ([tphoney](https://github.com/tphoney))
+- Harness move [\#237](https://github.com/drone/go-scm/pull/237) ([tphoney](https://github.com/tphoney))
+
+**Fixed bugs:**
+
+- \(fix\) harness, webhook fixes [\#248](https://github.com/drone/go-scm/pull/248) ([tphoney](https://github.com/tphoney))
+- fix: \[PIE-7927\]: Fix header value typo issue for BB OnPrem CSRF header [\#236](https://github.com/drone/go-scm/pull/236) ([mohitg0795](https://github.com/mohitg0795))
+
+**Merged pull requests:**
+
+- \(maint\) prep for 1.29.0 [\#251](https://github.com/drone/go-scm/pull/251) ([tphoney](https://github.com/tphoney))
+
+## [v1.28.1](https://github.com/drone/go-scm/tree/v1.28.1) (2023-01-27)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.28.0...v1.28.1)
+
+**Fixed bugs:**
+
+- feat: \[PIE-7927\]: added header to avoid/bypass csrf check [\#234](https://github.com/drone/go-scm/pull/234) ([mohitg0795](https://github.com/mohitg0795))
+
+**Closed issues:**
+
+- Gogs commit fails to deserialize commitDetails in some cases [\#231](https://github.com/drone/go-scm/issues/231)
+
+**Merged pull requests:**
+
+- \(maint\) prep 1.28.1 release [\#235](https://github.com/drone/go-scm/pull/235) ([tphoney](https://github.com/tphoney))
+
+## [v1.28.0](https://github.com/drone/go-scm/tree/v1.28.0) (2022-11-22)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.27.0...v1.28.0)
+
+**Implemented enhancements:**
+
+- Add Actor UUID to push and branch create events for Bitbucket [\#230](https://github.com/drone/go-scm/pull/230) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness))
+- Add support for Github release webhook [\#229](https://github.com/drone/go-scm/pull/229) ([vcalasansh](https://github.com/vcalasansh))
+- Add Actor UUID to Sender for all webhooks responses for Bitbucket [\#227](https://github.com/drone/go-scm/pull/227) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness))
+- added date info for commits in push hook [\#223](https://github.com/drone/go-scm/pull/223) ([raghavharness](https://github.com/raghavharness))
+- Added support for branch in list commits bb onprem API [\#215](https://github.com/drone/go-scm/pull/215) ([mohitg0795](https://github.com/mohitg0795))
+- \[PL-26239\]: added api to list installation for github app [\#213](https://github.com/drone/go-scm/pull/213) ([bhavya181](https://github.com/bhavya181))
+
+**Fixed bugs:**
+
+- fixbug: gitee convert repository [\#226](https://github.com/drone/go-scm/pull/226) ([kit101](https://github.com/kit101))
+- Bitbucket sha fix for merged pr [\#225](https://github.com/drone/go-scm/pull/225) ([raghavharness](https://github.com/raghavharness))
+- decoding projectName for azure repo [\#224](https://github.com/drone/go-scm/pull/224) ([raghavharness](https://github.com/raghavharness))
+- added omitempty annotation for secret [\#221](https://github.com/drone/go-scm/pull/221) ([raghavharness](https://github.com/raghavharness))
+- \[PL-26239\]: fix for list response [\#218](https://github.com/drone/go-scm/pull/218) ([bhavya181](https://github.com/bhavya181))
+
+**Closed issues:**
+
+- gitlab: force\_remove\_source\_branch type is inconsistent [\#228](https://github.com/drone/go-scm/issues/228)
+- gitee: When the name and path are inconsistent, got 404 error [\#217](https://github.com/drone/go-scm/issues/217)
+- file naming conventions [\#208](https://github.com/drone/go-scm/issues/208)
+- Support for Azure Devops git repos? [\#53](https://github.com/drone/go-scm/issues/53)
+
+**Merged pull requests:**
+
+- \(maint\) release prep for 1.28.0 [\#232](https://github.com/drone/go-scm/pull/232) ([tphoney](https://github.com/tphoney))
+- \(maint\) fixing naming and add more go best practice [\#211](https://github.com/drone/go-scm/pull/211) ([tphoney](https://github.com/tphoney))
+
+## [v1.27.0](https://github.com/drone/go-scm/tree/v1.27.0) (2022-07-19)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.26.0...v1.27.0)
+
+**Merged pull requests:**
+
+- Update scm version 1.27.0 [\#206](https://github.com/drone/go-scm/pull/206) ([raghavharness](https://github.com/raghavharness))
+- Using resource version 2.0 for Azure [\#205](https://github.com/drone/go-scm/pull/205) ([raghavharness](https://github.com/raghavharness))
+
+## [v1.26.0](https://github.com/drone/go-scm/tree/v1.26.0) (2022-07-01)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.25.0...v1.26.0)
+
+**Implemented enhancements:**
+
+- Support parsing PR comment events for Bitbucket Cloud [\#202](https://github.com/drone/go-scm/pull/202) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness))
+- added issue comment hook support for Azure [\#200](https://github.com/drone/go-scm/pull/200) ([raghavharness](https://github.com/raghavharness))
+
+**Fixed bugs:**
+
+- \[CI-4623\] - Azure webhook parseAPI changes [\#198](https://github.com/drone/go-scm/pull/198) ([raghavharness](https://github.com/raghavharness))
+
+**Merged pull requests:**
+
+- Fixed formatting in README.md [\#199](https://github.com/drone/go-scm/pull/199) ([hemanthmantri](https://github.com/hemanthmantri))
+
+## [v1.25.0](https://github.com/drone/go-scm/tree/v1.25.0) (2022-06-16)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.24.0...v1.25.0)
+
+**Implemented enhancements:**
+
+- Support parsing Gitlab Note Hook event [\#194](https://github.com/drone/go-scm/pull/194) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness))
+
+**Fixed bugs:**
+
+- \[PL-25889\]: fix list branches Azure API [\#195](https://github.com/drone/go-scm/pull/195) ([bhavya181](https://github.com/bhavya181))
+- Return project specific hooks only in ListHooks API for Azure. [\#192](https://github.com/drone/go-scm/pull/192) ([raghavharness](https://github.com/raghavharness))
+
+**Merged pull requests:**
+
+- Update scm version 1.25.0 [\#197](https://github.com/drone/go-scm/pull/197) ([rutvijmehta-harness](https://github.com/rutvijmehta-harness))
+
+## [v1.24.0](https://github.com/drone/go-scm/tree/v1.24.0) (2022-06-07)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.23.0...v1.24.0)
+
+**Implemented enhancements:**
+
+- Added PR find and listCommit API support for Azure [\#188](https://github.com/drone/go-scm/pull/188) ([raghavharness](https://github.com/raghavharness))
+
+**Fixed bugs:**
+
+- remove redundant slash from list commits api [\#190](https://github.com/drone/go-scm/pull/190) ([aman-harness](https://github.com/aman-harness))
+- Using target commit instead of source in base info for azure [\#189](https://github.com/drone/go-scm/pull/189) ([raghavharness](https://github.com/raghavharness))
+
+**Closed issues:**
+
+- gitee client pagination bug [\#187](https://github.com/drone/go-scm/issues/187)
+
+**Merged pull requests:**
+
+- release\_prep\_v1.24.0 [\#191](https://github.com/drone/go-scm/pull/191) ([tphoney](https://github.com/tphoney))
+
+## [v1.23.0](https://github.com/drone/go-scm/tree/v1.23.0) (2022-05-23)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.22.0...v1.23.0)
+
+**Implemented enhancements:**
+
+- Add the support to fetch commit of a particular file [\#182](https://github.com/drone/go-scm/pull/182) ([DeepakPatankar](https://github.com/DeepakPatankar))
+
+**Fixed bugs:**
+
+- Remove the null value de-reference issue when the bitbucket server url is nil [\#183](https://github.com/drone/go-scm/pull/183) ([DeepakPatankar](https://github.com/DeepakPatankar))
+- \[PL-24913\]: Handle the error raised while creating a multipart input [\#181](https://github.com/drone/go-scm/pull/181) ([DeepakPatankar](https://github.com/DeepakPatankar))
+
+**Merged pull requests:**
+
+- Upgrade the scm version [\#185](https://github.com/drone/go-scm/pull/185) ([DeepakPatankar](https://github.com/DeepakPatankar))
+
+## [v1.22.0](https://github.com/drone/go-scm/tree/v1.22.0) (2022-05-10)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.21.1...v1.22.0)
+
+**Implemented enhancements:**
+
+- \[feat\]: \[PL-24913\]: Add the support for create and update file in bitbucket server [\#177](https://github.com/drone/go-scm/pull/177) ([DeepakPatankar](https://github.com/DeepakPatankar))
+- \[feat\]: \[PL-24915\]: Add the support for create branches in bitbucket server [\#174](https://github.com/drone/go-scm/pull/174) ([DeepakPatankar](https://github.com/DeepakPatankar))
+- \[PL-24911\]: Make project name as optional param in Azure Repo APIs [\#173](https://github.com/drone/go-scm/pull/173) ([DeepakPatankar](https://github.com/DeepakPatankar))
+
+**Fixed bugs:**
+
+- \[feat\]: \[PL-25025\]: Updated Project validation for Azure API [\#179](https://github.com/drone/go-scm/pull/179) ([mankrit-singh](https://github.com/mankrit-singh))
+- \[fix\]: \[PL-24880\]: Trim the ref when fetching default branch in get Repo API [\#172](https://github.com/drone/go-scm/pull/172) ([DeepakPatankar](https://github.com/DeepakPatankar))
+- fixbug: gitee populatePageValues [\#167](https://github.com/drone/go-scm/pull/167) ([kit101](https://github.com/kit101))
+
+**Closed issues:**
+
+- gitea find commit   [\#125](https://github.com/drone/go-scm/issues/125)
+
+**Merged pull requests:**
+
+- \[feat\]: \[PL-25025\]: Changelog Updated/New Version [\#180](https://github.com/drone/go-scm/pull/180) ([mankrit-singh](https://github.com/mankrit-singh))
+
+## [v1.21.1](https://github.com/drone/go-scm/tree/v1.21.1) (2022-04-22)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.21.0...v1.21.1)
+
+**Fixed bugs:**
+
+- remove double invocation of convertRepository [\#170](https://github.com/drone/go-scm/pull/170) ([d1wilko](https://github.com/d1wilko))
+
+**Merged pull requests:**
+
+- \(maint\) release prep for 1.21.1 [\#171](https://github.com/drone/go-scm/pull/171) ([d1wilko](https://github.com/d1wilko))
+
+## [v1.21.0](https://github.com/drone/go-scm/tree/v1.21.0) (2022-04-22)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.20.0...v1.21.0)
+
+**Implemented enhancements:**
+
+- Add support for repository find in azure [\#164](https://github.com/drone/go-scm/pull/164) ([goelsatyam2](https://github.com/goelsatyam2))
+- \(feat\) add azure webhook parsing, creation deletion & list [\#163](https://github.com/drone/go-scm/pull/163) ([tphoney](https://github.com/tphoney))
+- \(DRON-242\) azure add compare commits,get commit,list repos [\#162](https://github.com/drone/go-scm/pull/162) ([tphoney](https://github.com/tphoney))
+
+**Fixed bugs:**
+
+- \(fix\) handle nil repos in github responses [\#168](https://github.com/drone/go-scm/pull/168) ([tphoney](https://github.com/tphoney))
+
+**Closed issues:**
+
+- When attempting to clone my git repo from GitLab drone hangs on git fetch. [\#161](https://github.com/drone/go-scm/issues/161)
+- Fix dump response [\#119](https://github.com/drone/go-scm/issues/119)
+
+**Merged pull requests:**
+
+- \(maint\) release prep for 1.21.0 [\#169](https://github.com/drone/go-scm/pull/169) ([d1wilko](https://github.com/d1wilko))
+
+## [v1.20.0](https://github.com/drone/go-scm/tree/v1.20.0) (2022-03-08)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.19.1...v1.20.0)
+
+**Implemented enhancements:**
+
+- \(DRON-242\) initial implementation of azure devops support [\#158](https://github.com/drone/go-scm/pull/158) ([tphoney](https://github.com/tphoney))
+
+**Fixed bugs:**
+
+- fixed raw response dumping in client [\#159](https://github.com/drone/go-scm/pull/159) ([marko-gacesa](https://github.com/marko-gacesa))
+
+**Merged pull requests:**
+
+- \(maint\) release prep for 1.20.0 [\#160](https://github.com/drone/go-scm/pull/160) ([d1wilko](https://github.com/d1wilko))
+
+## [v1.19.1](https://github.com/drone/go-scm/tree/v1.19.1) (2022-02-23)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.19.0...v1.19.1)
+
+**Fixed bugs:**
+
+- Bitbucket list files fix [\#154](https://github.com/drone/go-scm/pull/154) ([mohitg0795](https://github.com/mohitg0795))
+- GitHub list commits fix [\#152](https://github.com/drone/go-scm/pull/152) ([mohitg0795](https://github.com/mohitg0795))
+- Bitbucket compare changes fix for rename and removed file ops [\#151](https://github.com/drone/go-scm/pull/151) ([mohitg0795](https://github.com/mohitg0795))
+
+**Merged pull requests:**
+
+- prep for v1.19.1 [\#155](https://github.com/drone/go-scm/pull/155) ([tphoney](https://github.com/tphoney))
+
+## [v1.19.0](https://github.com/drone/go-scm/tree/v1.19.0) (2022-02-09)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.18.0...v1.19.0)
+
+**Implemented enhancements:**
+
+- \(feat\) add path support for list commits on github and gitlab [\#149](https://github.com/drone/go-scm/pull/149) ([tphoney](https://github.com/tphoney))
+- Extending bitbucket listCommits API to fetch commits for a given file [\#148](https://github.com/drone/go-scm/pull/148) ([mohitg0795](https://github.com/mohitg0795))
+- Update GitHub signature header to use sha256 [\#123](https://github.com/drone/go-scm/pull/123) ([nlecoy](https://github.com/nlecoy))
+
+**Merged pull requests:**
+
+- v1.19.0 release prep [\#150](https://github.com/drone/go-scm/pull/150) ([tphoney](https://github.com/tphoney))
+
+## [v1.18.0](https://github.com/drone/go-scm/tree/v1.18.0) (2022-01-18)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.17.0...v1.18.0)
+
+**Implemented enhancements:**
+
+- Added support for parsing prevFilePath field from github compare commits API response [\#143](https://github.com/drone/go-scm/pull/143) ([mohitg0795](https://github.com/mohitg0795))
+
+**Fixed bugs:**
+
+- Implement parsing/handling for missing pull request webhook events for BitBucket Server \(Stash\) driver [\#130](https://github.com/drone/go-scm/pull/130) ([raphendyr](https://github.com/raphendyr))
+
+**Closed issues:**
+
+- Bitbucket Stash driver doesn't handle event `pr:from_ref_updated` \(new commits / force push\) [\#116](https://github.com/drone/go-scm/issues/116)
+
+**Merged pull requests:**
+
+- release prep v1.18.0 [\#147](https://github.com/drone/go-scm/pull/147) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+## [v1.17.0](https://github.com/drone/go-scm/tree/v1.17.0) (2022-01-07)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.16.3...v1.17.0)
+
+**Implemented enhancements:**
+
+- \(feat\) map archive flag to repo response [\#141](https://github.com/drone/go-scm/pull/141) ([eoinmcafee00](https://github.com/eoinmcafee00))
+- Add the support for delete of the bitbucket file [\#139](https://github.com/drone/go-scm/pull/139) ([DeepakPatankar](https://github.com/DeepakPatankar))
+
+**Fixed bugs:**
+
+- Fix the syntax error of the example code [\#135](https://github.com/drone/go-scm/pull/135) ([LinuxSuRen](https://github.com/LinuxSuRen))
+
+**Closed issues:**
+
+- The deprecation of Bitbucket API endpoint /2.0/teams breaks user registration [\#136](https://github.com/drone/go-scm/issues/136)
+
+**Merged pull requests:**
+
+- release prep for v1.17.0 [\#142](https://github.com/drone/go-scm/pull/142) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+## [v1.16.3](https://github.com/drone/go-scm/tree/v1.16.3) (2021-12-30)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.16.2...v1.16.3)
+
+**Fixed bugs:**
+
+- fix the deprecation of Bitbucket API endpoint /2.0/teams breaks user registration \(136\) [\#137](https://github.com/drone/go-scm/pull/137) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+**Closed issues:**
+
+- Any plans to support manage wehook [\#134](https://github.com/drone/go-scm/issues/134)
+
+**Merged pull requests:**
+
+- V1.16.3 [\#138](https://github.com/drone/go-scm/pull/138) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+## [v1.16.2](https://github.com/drone/go-scm/tree/v1.16.2) (2021-11-30)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.16.1...v1.16.2)
+
+**Merged pull requests:**
+
+- release prep v1.16.2 [\#132](https://github.com/drone/go-scm/pull/132) ([marko-gacesa](https://github.com/marko-gacesa))
+- fixbug: gitee webhook parse [\#131](https://github.com/drone/go-scm/pull/131) ([kit101](https://github.com/kit101))
+
+## [v1.16.1](https://github.com/drone/go-scm/tree/v1.16.1) (2021-11-19)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.16.0...v1.16.1)
+
+**Fixed bugs:**
+
+- swap repo and target in bitbucket CompareChanges [\#127](https://github.com/drone/go-scm/pull/127) ([jimsheldon](https://github.com/jimsheldon))
+
+**Merged pull requests:**
+
+- release prep v1.16.1 [\#129](https://github.com/drone/go-scm/pull/129) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+## [v1.16.0](https://github.com/drone/go-scm/tree/v1.16.0) (2021-11-19)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.15.2...v1.16.0)
+
+**Implemented enhancements:**
+
+- release prep for 1.16.0 [\#128](https://github.com/drone/go-scm/pull/128) ([eoinmcafee00](https://github.com/eoinmcafee00))
+- Feat: implemented gitee provider [\#124](https://github.com/drone/go-scm/pull/124) ([kit101](https://github.com/kit101))
+- add release & milestone functionality [\#121](https://github.com/drone/go-scm/pull/121) ([eoinmcafee00](https://github.com/eoinmcafee00))
+
+**Fixed bugs:**
+
+- Fix Gitea example code on README.md [\#126](https://github.com/drone/go-scm/pull/126) ([lunny](https://github.com/lunny))
+
+## [v1.15.2](https://github.com/drone/go-scm/tree/v1.15.2) (2021-07-20)
+
+[Full Changelog](https://github.com/drone/go-scm/compare/v1.15.1...v1.15.2)
+
+**Fixed bugs:**
+
+- Fixing Gitea commit API in case `ref/heads/` prefix is added to ref [\#108](https://github.com/drone/go-scm/pull/108) ([Vici37](https://github.com/Vici37))
+- use access json header / extend error message parsing for stash [\#89](https://github.com/drone/go-scm/pull/89) ([bakito](https://github.com/bakito))
+
+**Closed issues:**
+
+- Drone and Bitbucket broken for write permission detection for drone build restart permission. [\#87](https://github.com/drone/go-scm/issues/87)
+
+**Merged pull requests:**
+
+- \(maint\) prep for v.1.15.2 release [\#118](https://github.com/drone/go-scm/pull/118) ([tphoney](https://github.com/tphoney))
+- Add a vet step to drone config [\#83](https://github.com/drone/go-scm/pull/83) ([tboerger](https://github.com/tboerger))
+
 All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## 1.15.1
+### Added
+- (DRON-88) github fix pr ListChanges deleted/renamed status, from [@tphoney](https://github.com/tphoney). See [#113](https://github.com/drone/go-scm/pull/113).
+- (DRON-84) github fix pr write permission issue with bitbucket server, from [@eoinmcafee0](https://github.com/eoinmcafee00). See [#114](https://github.com/drone/go-scm/pull/114).
+
+## 1.15.0
+### Added
+- add delete file for github and gitlab, from [@tphoney](https://github.com/tphoney). See [#110](https://github.com/drone/go-scm/pull/110).
+
+## 1.14.1
+### Fixed
+- fix gitlab repo encoding in commits from pr request, from [@aradisavljevic](https://github.com/aradisavljevic). See [#109](https://github.com/drone/go-scm/pull/109).
+
+## 1.14.0
+### Added
+- Added ListCommits in pull request api, from [@aradisavljevic](https://github.com/aradisavljevic). See [#106](https://github.com/drone/go-scm/pull/106).
+
+## 1.13.1
+### Fixed
+- gitlab, content.find return last_commit_id not commit_id, from [@tphoney](https://github.com/tphoney). See [#104](https://github.com/drone/go-scm/pull/104).
+
+## 1.13.0
+### Added
+- Create branch functionality, from [@tphoney](https://github.com/tphoney). See [#103](https://github.com/drone/go-scm/pull/103).
+
+## 1.12.0
+### Added
+- return sha/blob_id for content.list, from [@tphoney](https://github.com/tphoney). See [#102](https://github.com/drone/go-scm/pull/102).
+
+## 1.11.0
+### Added
+- normalise sha in content, add bitbucket create/update, from [@tphoney](https://github.com/tphoney). See [#101](https://github.com/drone/go-scm/pull/101).
+
+## 1.10.0
+### Added
+- return hash/object_id for files changed in github, from [@tphoney](https://github.com/tphoney). See [#99](https://github.com/drone/go-scm/pull/99).
+
+## 1.9.0
+### Added
+- Added issue_comment parsing for github webhook, from [@aman-harness](https://github.com/aman-harness). See [#91](https://github.com/drone/go-scm/pull/91).
+- Added Pr in issue, from [@aman-harness](https://github.com/aman-harness). See [#93](https://github.com/drone/go-scm/pull/93).
+- gitlab contents. Find returns hash/blob, from [@tphoney](https://github.com/tphoney). See [#97](https://github.com/drone/go-scm/pull/97).
+- add ListCommits for gitea and stash, from [@tphoney](https://github.com/tphoney). See [#98](https://github.com/drone/go-scm/pull/98).
+
+### Changed
+- retry with event subset for legacy stash versions, from [@bakito](https://github.com/bakito). See [#90](https://github.com/drone/go-scm/pull/90).
+
 ## 1.8.0
 ### Added
 - Support for GitLab visibility attribute, from [@bradrydzewski](https://github.com/bradrydzewski). See [79951ad](https://github.com/drone/go-scm/commit/79951ad7a0d0b1989ea84d99be31fcb9320ae348).
@@ -76,3 +663,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Fix missing avatar in Gitea commit from [@jgeek1011](https://github.com/geek1011).
 - Implement GET commit endpoint for Gogs from [@ogarcia](https://github.com/ogarcia).
+
+
+\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
diff --git a/HISTORY.md b/HISTORY.md
new file mode 100644
index 000000000..2bcfdc061
--- /dev/null
+++ b/HISTORY.md
@@ -0,0 +1,125 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## 1.15.1
+### Added
+- (DRON-88) github fix pr ListChanges deleted/renamed status, from [@tphoney](https://github.com/tphoney). See [#113](https://github.com/drone/go-scm/pull/113).
+- (DRON-84) github fix pr write permission issue with bitbucket server, from [@eoinmcafee0](https://github.com/eoinmcafee00). See [#114](https://github.com/drone/go-scm/pull/114).
+
+## 1.15.0
+### Added
+- add delete file for github and gitlab, from [@tphoney](https://github.com/tphoney). See [#110](https://github.com/drone/go-scm/pull/110).
+
+## 1.14.1
+### Fixed
+- fix gitlab repo encoding in commits from pr request, from [@aradisavljevic](https://github.com/aradisavljevic). See [#109](https://github.com/drone/go-scm/pull/109).
+
+## 1.14.0
+### Added
+- Added ListCommits in pull request api, from [@aradisavljevic](https://github.com/aradisavljevic). See [#106](https://github.com/drone/go-scm/pull/106).
+
+## 1.13.1
+### Fixed
+- gitlab, content.find return last_commit_id not commit_id, from [@tphoney](https://github.com/tphoney). See [#104](https://github.com/drone/go-scm/pull/104).
+
+## 1.13.0
+### Added
+- Create branch functionality, from [@tphoney](https://github.com/tphoney). See [#103](https://github.com/drone/go-scm/pull/103).
+
+## 1.12.0
+### Added
+- return sha/blob_id for content.list, from [@tphoney](https://github.com/tphoney). See [#102](https://github.com/drone/go-scm/pull/102).
+
+## 1.11.0
+### Added
+- normalise sha in content, add bitbucket create/update, from [@tphoney](https://github.com/tphoney). See [#101](https://github.com/drone/go-scm/pull/101).
+
+## 1.10.0
+### Added
+- return hash/object_id for files changed in github, from [@tphoney](https://github.com/tphoney). See [#99](https://github.com/drone/go-scm/pull/99).
+
+## 1.9.0
+### Added
+- Added issue_comment parsing for github webhook, from [@aman-harness](https://github.com/aman-harness). See [#91](https://github.com/drone/go-scm/pull/91).
+- Added Pr in issue, from [@aman-harness](https://github.com/aman-harness). See [#93](https://github.com/drone/go-scm/pull/93).
+- gitlab contents. Find returns hash/blob, from [@tphoney](https://github.com/tphoney). See [#97](https://github.com/drone/go-scm/pull/97).
+- add ListCommits for gitea and stash, from [@tphoney](https://github.com/tphoney). See [#98](https://github.com/drone/go-scm/pull/98).
+
+### Changed
+- retry with event subset for legacy stash versions, from [@bakito](https://github.com/bakito). See [#90](https://github.com/drone/go-scm/pull/90).
+
+## 1.8.0
+### Added
+- Support for GitLab visibility attribute, from [@bradrydzewski](https://github.com/bradrydzewski). See [79951ad](https://github.com/drone/go-scm/commit/79951ad7a0d0b1989ea84d99be31fcb9320ae348).
+- Support for GitHub visibility attribute, from [@bradrydzewski](https://github.com/bradrydzewski). See [5141b8e](https://github.com/drone/go-scm/commit/5141b8e1db921fe2101c12594c5159b9ffffebc3).
+
+### Changed
+- Support for parsing unknown pull request events, from [@bradrydzewski](https://github.com/bradrydzewski). See [ffa46d9](https://github.com/drone/go-scm/commit/ffa46d955454baa609975eebbe9fdfc4b0a9f7e9).
+
+## 1.7.2
+### Added
+- Support for finding and listing repository tags in GitHub driver, from [@chhsia0](https://github.com/chhsia0). See [#79](https://github.com/drone/go-scm/pull/79).
+- Support for finding and listing repository tags in Gitea driver, from [@bradyrdzewski](https://github.com/bradyrdzewski). See [427b8a8](https://github.com/drone/go-scm/commit/427b8a85897c892148801824760bc66d3a3cdcdb).
+- Support for git object hashes in GitHub, from from [@bradyrdzewski](https://github.com/bradyrdzewski). See [5230330](https://github.com/drone/go-scm/commit/523033025a7ee875fcfb156f4c660b37e269b1a8).
+- Support for before and after commit sha in Stash driver, from [@jlehtimaki](https://github.com/jlehtimaki). See [#82](https://github.com/drone/go-scm/pull/82).
+- Support for before and after commit sha in GitLab and Bitbucket driver, from [@shubhag](https://github.com/shubhag). See [#85](https://github.com/drone/go-scm/pull/85).
+
+## 1.7.1
+### Added
+- Support for skip verification in Bitbucket webhook creation, from [@chhsia0](https://github.com/chhsia0). See [#63](https://github.com/drone/go-scm/pull/63).
+- Support for Gitea pagination, from [@CirnoT](https://github.com/CirnoT). See [#66](https://github.com/drone/go-scm/pull/66).
+- Support for labels in pull request resources, from [@takirala](https://github.com/takirala). See [#67](https://github.com/drone/go-scm/pull/67).
+- Support for updating webhooks, from [@chhsia0](https://github.com/chhsia0). See [#71](https://github.com/drone/go-scm/pull/71).
+
+### Fixed
+- Populate diff links in pull request resources, from [@shubhag](https://github.com/shubhag). See [#75](https://github.com/drone/go-scm/pull/75).
+- Filter Bitbucket repository search by project, from [@bradrydzewski](https://github.com/bradrydzewski).
+
+## 1.7.0
+### Added
+- Improve status display text in new bitbucket pull request screen, from [@bradrydzewski](https://github.com/bradrydzewski). See [#27](https://github.com/drone/go-scm/issues/27).
+- Implement timestamp value for GitHub push webhooks, from [@bradrydzewski](https://github.com/bradrydzewski).
+- Implement deep link to branch.
+- Implement git compare function to compare two separate commits, from [@chhsia0](https://github.com/chhsia0).
+- Implement support for creating and updating GitLab and GitHub repository contents, from [@zhuxiaoyang](https://github.com/zhuxiaoyang).
+- Capture Repository link for Gitea, Gogs and Gitlab, from [@chhsia0](https://github.com/chhsia0).
+
+### Fixed
+- Fix issue with GitHub enterprise deep link including API prefix, from [@bradrydzewski](https://github.com/bradrydzewski).
+- Fix issue with GitHub deploy hooks for commits having an invalid reference, from [@bradrydzewski](https://github.com/bradrydzewski).
+- Support for Skipping SSL verification for GitLab webhooks. See [#40](https://github.com/drone/go-scm/pull/40).
+- Support for Skipping SSL verification for GitHub webhooks. See [#44](https://github.com/drone/go-scm/pull/40).
+- Fix issue with handling slashes in Bitbucket branch names. See [#7](https://github.com/drone/go-scm/pull/47).
+- Fix incorrect Gitea tag link. See [#52](https://github.com/drone/go-scm/pull/52).
+- Encode ref when making Gitea API calls. See [#61](https://github.com/drone/go-scm/pull/61).
+
+## [1.6.0]
+### Added
+- Support Head and Base sha for GitHub pull request, from [@bradrydzewski](https://github.com/bradrydzewski).
+- Support Before sha for Bitbucket, from [@jkdev81](https://github.com/jkdev81).
+- Support for creating GitHub deployment hooks, from [@bradrydzewski](https://github.com/bradrydzewski).
+- Endpoint to get organization membership for GitHub, from [@bradrydzewski](https://github.com/bradrydzewski).
+- Functions to generate deep links to git resources, from [@bradrydzewski](https://github.com/bradrydzewski).
+
+### Fixed
+- Fix issue getting a GitLab commit by ref, from [@bradrydzewski](https://github.com/bradrydzewski).
+
+## [1.5.0]
+### Added
+
+- Fix missing sha for Gitea tag hooks, from [@techknowlogick](https://github.com/techknowlogick). See [#22](https://github.com/drone/go-scm/pull/22).
+- Support for Gitea webhook signature verification, from [@techknowlogick](https://github.com/techknowlogick).
+
+## [1.4.0]
+### Added
+
+- Fix issues base64 decoding GitLab content, from [@bradrydzewski](https://github.com/bradrydzewski).
+
+## [1.3.0]
+### Added
+
+- Fix missing avatar in Gitea commit from [@jgeek1011](https://github.com/geek1011).
+- Implement GET commit endpoint for Gogs from [@ogarcia](https://github.com/ogarcia).
diff --git a/README.md b/README.md
index 66349fb6c..a95de0461 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,10 @@
+# go-scm
+
 [![Go Doc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/drone/go-scm/scm)
 
-Package scm provides a unified interface to multiple source code management systems including GitHub, GitHub Enterprise, Bitbucket, Bitbucket Server, Gitea and Gogs.
+Package scm provides a unified interface to multiple source code management systems including GitHub, GitHub Enterprise, Bitbucket, Bitbucket Server, Gitee, Gitea and Gogs.
 
-# Getting Started
+## Getting Started
 
 Create a GitHub client:
 
@@ -10,12 +12,12 @@ Create a GitHub client:
 package main
 
 import (
-	"github.com/drone/go-scm/scm"
-	"github.com/drone/go-scm/scm/driver/github"
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/github"
 )
 
 func main() {
-	client := github.NewDefault()
+client := github.NewDefault()
 }
 ```
 
@@ -23,8 +25,8 @@ Create a GitHub Enterprise client:
 
 ```Go
 import (
-	"github.com/drone/go-scm/scm"
-	"github.com/drone/go-scm/scm/driver/github"
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/github"
 )
 
 func main() {
@@ -36,8 +38,8 @@ Create a Bitbucket client:
 
 ```Go
 import (
-	"github.com/drone/go-scm/scm"
-	"github.com/drone/go-scm/scm/driver/bitbucket"
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/bitbucket"
 )
 
 func main() {
@@ -49,8 +51,8 @@ Create a Bitbucket Server (Stash) client:
 
 ```Go
 import (
-	"github.com/drone/go-scm/scm"
-	"github.com/drone/go-scm/scm/driver/stash"
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/stash"
 )
 
 func main() {
@@ -62,50 +64,130 @@ Create a Gitea client:
 
 ```Go
 import (
-	"github.com/drone/go-scm/scm"
-	"github.com/drone/go-scm/scm/driver/github"
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/gitea"
 )
 
 func main() {
-    client, err := gitea.New("https://gitea.company.com")
+  client, err := gitea.New("https://gitea.company.com")
 }
 ```
 
-# Authentication
+Create a Gitee client:
 
-The scm client does not directly handle authentication. Instead, when creating a new client, provide a `http.Client` that can handle authentication for you. For convenience, this library includes oauth1 and oauth2 implementations that can be used to authenticate requests.
+```Go
+import (
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/gitee"
+)
+
+func main() {
+  client, err := gitee.New("https://gitee.com/api/v5")
+}
+```
+
+## Authentication
+
+The scm client does not directly handle authentication. Instead, when creating a new client, provide an `http.Client` that can handle authentication for you. For convenience, this library includes oauth1 and oauth2 implementations that can be used to authenticate requests.
 
 ```Go
 package main
 
 import (
-	"github.com/drone/go-scm/scm"
-	"github.com/drone/go-scm/scm/driver/github"
-	"github.com/drone/go-scm/scm/driver/transport/oauth2"
+  "github.com/drone/go-scm/scm"
+  "github.com/drone/go-scm/scm/driver/github"
+  "github.com/drone/go-scm/scm/transport"
+  "github.com/drone/go-scm/scm/transport/oauth2"
 )
 
 func main() {
-	client := github.NewDefault()
-
-	// provide a custom http.Client with a transport
-	// that injects the oauth2 token.
-	client.Client := &http.Client{
-		Transport: &Transport{
-			Source: StaticTokenSource(
-				&scm.Token{
-					Token: "ecf4c1f9869f59758e679ab54b4",
-				},
-			),
-		},
-	}
-
-	// provide a custom http.Client with a transport
-	// that injects the private GitLab token through
-	// the PRIVATE_TOKEN header variable.
-	client.Client := &http.Client{
-		Transport: &PrivateToken{
-			Token: "ecf4c1f9869f59758e679ab54b4",
-		},
-	}
+  client := github.NewDefault()
+
+  // provide a custom http.Client with a transport
+  // that injects the oauth2 token.
+  client.Client = &http.Client{
+    Transport: &oauth2.Transport{
+      Source: oauth2.StaticTokenSource(
+        &scm.Token{
+          Token: "ecf4c1f9869f59758e679ab54b4",
+        },
+      ),
+    },
+  }
+
+  // provide a custom http.Client with a transport
+  // that injects the private GitLab token through
+  // the PRIVATE_TOKEN header variable.
+  client.Client = &http.Client{
+    Transport: &transport.PrivateToken{
+      Token: "ecf4c1f9869f59758e679ab54b4",
+    },
+  }
 }
 ```
+
+## Usage
+
+The scm client exposes dozens of endpoints for working with repositories, issues, comments, files and more. Please see the [godocs](https://pkg.go.dev/github.com/drone/go-scm/scm#pkg-examples) to learn more.
+
+Example code to get an issue:
+
+```Go
+issue, _, err := client.Issues.Find(ctx, "octocat/Hello-World", 1)
+```
+
+Example code to get a list of issues:
+
+```Go
+opts := scm.IssueListOptions{
+  Page:   1,
+  Size:   30,
+  Open:   true,
+  Closed: false,
+}
+
+issues, _, err := client.Issues.List(ctx, "octocat/Hello-World", opts)
+```
+
+Example code to create an issue comment:
+
+```Go
+in := &scm.CommentInput{
+  Body: "Found a bug",
+}
+
+comment, _, err := client.Issues.CreateComment(ctx, "octocat/Hello-World", 1, in)
+```
+
+## Useful links
+
+Here are some useful links to providers API documentation:
+
+- [Azure DevOps](https://docs.microsoft.com/en-us/rest/api/azure/devops/git/?view=azure-devops-rest-6.0)
+- [Bitbucket cloud API](https://developer.atlassian.com/cloud/bitbucket/rest/intro/)
+- [Bitbucket server/Stash API](https://docs.atlassian.com/bitbucket-server/rest/5.16.0/bitbucket-rest.html)
+- [Gitea API](https://gitea.com/api/swagger/#/)
+- [Gitee API](https://gitee.com/api/swagger/#/)
+- [Github API](https://docs.github.com/en/rest/reference)
+- [Gitlab API](https://docs.gitlab.com/ee/api/api_resources.html)
+- [Gogs API](https://github.com/gogs/docs-api)
+
+## Release procedure
+
+Run the changelog generator.
+
+```BASH
+docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator -u drone -p go-scm -t <secret github token>
+```
+
+You can generate a token by logging into your GitHub account and going to Settings -> Personal access tokens.
+
+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.
+
+Run the changelog generator again with the future version according to semver.
+
+```BASH
+docker run -it --rm -v "$(pwd)":/usr/local/src/your-app githubchangeloggenerator/github-changelog-generator -u drone -p go-scm -t <secret token> --future-release v1.15.2
+```
+
+Create your pull request for the release. Get it merged then tag the release.
diff --git a/go.mod b/go.mod
index cd3f85a78..1a7e7c5ea 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/drone/go-scm
 require (
 	github.com/google/go-cmp v0.2.0
 	github.com/h2non/gock v1.0.9
+	github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
 )
 
 go 1.13
diff --git a/go.sum b/go.sum
index 5f9a39cba..6d69376b6 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,6 @@
 github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
 github.com/h2non/gock v1.0.9 h1:17gCehSo8ZOgEsFKpQgqHiR7VLyjxdAG3lkhVvO9QZU=
-github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
\ No newline at end of file
+github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
diff --git a/scm/client.go b/scm/client.go
index e6cbc5ae9..0d05b3b9a 100644
--- a/scm/client.go
+++ b/scm/client.go
@@ -10,6 +10,7 @@ import (
 	"io"
 	"net/http"
 	"net/url"
+	"os"
 	"strconv"
 	"strings"
 	"sync"
@@ -67,6 +68,26 @@ type (
 		Reset     int64
 	}
 
+	// BranchListOptions specifies optional branch search term and pagination
+	// parameters.
+	BranchListOptions struct {
+		SearchTerm      string
+		PageListOptions ListOptions
+	}
+
+	// RepoListOptions specifies optional repo search term and pagination
+	// parameters.
+	RepoListOptions struct {
+		ListOptions
+		RepoSearchTerm
+	}
+
+	// RepoSearchTerm specifies searchable parameters.
+	RepoSearchTerm struct {
+		RepoName string
+		User     string
+	}
+
 	// ListOptions specifies optional pagination
 	// parameters.
 	ListOptions struct {
@@ -93,8 +114,10 @@ type (
 		Git           GitService
 		Organizations OrganizationService
 		Issues        IssueService
+		Milestones    MilestoneService
 		PullRequests  PullRequestService
 		Repositories  RepositoryService
+		Releases      ReleaseService
 		Reviews       ReviewService
 		Users         UserService
 		Webhooks      WebhookService
@@ -165,7 +188,9 @@ func (c *Client) Do(ctx context.Context, in *Request) (*Response, error) {
 
 	// dumps the response for debugging purposes.
 	if c.DumpResponse != nil {
-		c.DumpResponse(res, true)
+		if raw, errDump := c.DumpResponse(res, true); errDump == nil {
+			_, _ = os.Stdout.Write(raw)
+		}
 	}
 	return newResponse(res), nil
 }
diff --git a/scm/const.go b/scm/const.go
index ac81da919..ad46ac906 100644
--- a/scm/const.go
+++ b/scm/const.go
@@ -40,8 +40,16 @@ const (
 	// pull requests
 	ActionSync
 	ActionMerge
+	ActionReviewReady
 	// issue comment
 	ActionEdit
+	// release
+	ActionPublish
+	ActionUnpublish
+	ActionPrerelease
+	ActionRelease
+	ActionSubmitted
+	ActionDismissed
 )
 
 // String returns the string representation of Action.
@@ -67,6 +75,22 @@ func (a Action) String() (s string) {
 		return "synchronized"
 	case ActionMerge:
 		return "merged"
+	case ActionPublish:
+		return "published"
+	case ActionUnpublish:
+		return "unpublished"
+	case ActionPrerelease:
+		return "prereleased"
+	case ActionRelease:
+		return "released"
+	case ActionReviewReady:
+		return "review_ready"
+	case ActionSubmitted:
+		return "submitted"
+	case ActionEdit:
+		return "edited"
+	case ActionDismissed:
+		return "dismissed"
 	default:
 		return
 	}
@@ -104,6 +128,22 @@ func (a *Action) UnmarshalJSON(data []byte) error {
 		*a = ActionSync
 	case "merged":
 		*a = ActionMerge
+	case "edited":
+		*a = ActionEdit
+	case "published":
+		*a = ActionPublish
+	case "unpublished":
+		*a = ActionUnpublish
+	case "prereleased":
+		*a = ActionPrerelease
+	case "released":
+		*a = ActionRelease
+	case "review_ready":
+		*a = ActionReviewReady
+	case "submitted":
+		*a = ActionSubmitted
+	case "dismissed":
+		*a = ActionDismissed
 	}
 	return nil
 }
@@ -121,6 +161,9 @@ const (
 	DriverBitbucket
 	DriverStash
 	DriverCoding
+	DriverGitee
+	DriverAzure
+	DriverHarness
 )
 
 // String returns the string representation of Driver.
@@ -140,6 +183,12 @@ func (d Driver) String() (s string) {
 		return "stash"
 	case DriverCoding:
 		return "coding"
+	case DriverGitee:
+		return "gitee"
+	case DriverAzure:
+		return "azure"
+	case DriverHarness:
+		return "harness"
 	default:
 		return "unknown"
 	}
@@ -195,6 +244,11 @@ func (k ContentKind) String() string {
 	}
 }
 
+// MarshalJSON returns the JSON-encoded Action.
+func (k ContentKind) MarshalJSON() ([]byte, error) {
+	return json.Marshal(k.String())
+}
+
 // UnmarshalJSON unmarshales the JSON-encoded ContentKind.
 func (k *ContentKind) UnmarshalJSON(data []byte) error {
 	var s string
@@ -240,3 +294,67 @@ func (v Visibility) String() (s string) {
 		return "unknown"
 	}
 }
+
+// Status defines an enum for execution status
+type ExecutionStatus int
+
+const (
+	StatusUnknown ExecutionStatus = iota
+	StatusPending
+	StatusRunning
+	StatusSuccess
+	StatusFailed
+	StatusCanceled
+)
+
+// String returns the string representation of ExecutionStatus.
+func (k ExecutionStatus) String() string {
+	switch k {
+	case StatusSuccess:
+		return "success"
+	case StatusPending:
+		return "pending"
+	case StatusRunning:
+		return "running"
+	case StatusFailed:
+		return "failed"
+	case StatusCanceled:
+		return "canceled"
+	case StatusUnknown:
+		return "Unknown"
+	default:
+		return "unsupported"
+	}
+}
+
+// MarshalJSON returns the JSON-encoded Action.
+func (k ExecutionStatus) MarshalJSON() ([]byte, error) {
+	return json.Marshal(k.String())
+}
+
+// UnmarshalJSON unmarshales the JSON-encoded ExecutionStatus.
+func (k *ExecutionStatus) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	switch s {
+	case StatusSuccess.String():
+		*k = StatusSuccess
+	case StatusPending.String():
+		*k = StatusPending
+	case StatusPending.String():
+		*k = StatusPending
+	case StatusRunning.String():
+		*k = StatusRunning
+	case StatusFailed.String():
+		*k = StatusFailed
+	case StatusCanceled.String():
+		*k = StatusCanceled
+	default:
+		*k = StatusUnknown
+	}
+	return nil
+}
+
+const SearchTimeFormat = "2006-01-02T15:04:05Z"
diff --git a/scm/content.go b/scm/content.go
index e28dc7b56..2d5cb6fde 100644
--- a/scm/content.go
+++ b/scm/content.go
@@ -9,12 +9,10 @@ import "context"
 type (
 	// Content stores the contents of a repository file.
 	Content struct {
-		Path string
-		Data []byte
-
-		// the has of the blob. this is the equivalent
-		// to running the git hash-object command.
-		Hash string
+		Path   string
+		Data   []byte
+		Sha    string
+		BlobID string
 	}
 
 	// ContentParams provide parameters for creating and
@@ -25,13 +23,16 @@ type (
 		Message   string
 		Data      []byte
 		Sha       string
+		BlobID    string
 		Signature Signature
 	}
 
 	// ContentInfo stores the kind of any content in a repository.
 	ContentInfo struct {
-		Path string
-		Kind ContentKind
+		Path   string
+		Sha    string
+		BlobID string
+		Kind   ContentKind
 	}
 
 	// ContentService provides access to repositroy content.
@@ -39,14 +40,14 @@ type (
 		// Find returns the repository file content by path.
 		Find(ctx context.Context, repo, path, ref string) (*Content, *Response, error)
 
-		// Create creates a new repositroy file.
+		// Create creates a new repository file.
 		Create(ctx context.Context, repo, path string, params *ContentParams) (*Response, error)
 
 		// Update updates a repository file.
 		Update(ctx context.Context, repo, path string, params *ContentParams) (*Response, error)
 
-		// Delete deletes a reository file.
-		Delete(ctx context.Context, repo, path, ref string) (*Response, error)
+		// Delete deletes a repository file.
+		Delete(ctx context.Context, repo, path string, params *ContentParams) (*Response, error)
 
 		// List returns a list of contents in a repository directory by path. It is
 		// up to the driver to list the directory recursively or non-recursively,
diff --git a/scm/driver/azure/azure.go b/scm/driver/azure/azure.go
new file mode 100644
index 000000000..e762a6db1
--- /dev/null
+++ b/scm/driver/azure/azure.go
@@ -0,0 +1,126 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package azure implements a azure client.
+package azure
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/url"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+// New returns a new azure API client.
+func New(uri, owner, project string) (*scm.Client, error) {
+	base, err := url.Parse(uri)
+	if err != nil {
+		return nil, err
+	}
+	if !strings.HasSuffix(base.Path, "/") {
+		base.Path = base.Path + "/"
+	}
+	if owner == "" {
+		return nil, fmt.Errorf("azure owner is required")
+	}
+	client := &wrapper{
+		new(scm.Client),
+		owner,
+		project,
+	}
+	client.BaseURL = base
+	// initialize services
+	client.Driver = scm.DriverAzure
+	client.Linker = &linker{base.String()}
+	client.Contents = &contentService{client}
+	client.Git = &gitService{client}
+	client.Issues = &issueService{client}
+	client.Organizations = &organizationService{client}
+	client.PullRequests = &pullService{&issueService{client}}
+	client.Repositories = &RepositoryService{client}
+	client.Reviews = &reviewService{client}
+	client.Users = &userService{client}
+	client.Webhooks = &webhookService{client}
+	return client.Client, nil
+}
+
+// NewDefault returns a new azure API client.
+func NewDefault(owner, project string) *scm.Client {
+	client, _ := New("https://dev.azure.com", owner, project)
+	return client
+}
+
+// wrapper wraps the Client to provide high level helper functions for making http requests and unmarshaling the response.
+type wrapper struct {
+	*scm.Client
+	owner   string
+	project string
+}
+
+// do wraps the Client.Do function by creating the Request and unmarshalling the response.
+func (c *wrapper) do(ctx context.Context, method, path string, in, out interface{}) (*scm.Response, error) {
+	req := &scm.Request{
+		Method: method,
+		Path:   path,
+	}
+	// if we are posting or putting data, we need to write it to the body of the request.
+	if in != nil {
+		buf := new(bytes.Buffer)
+		_ = json.NewEncoder(buf).Encode(in)
+		req.Header = map[string][]string{
+			"Content-Type": {"application/json"},
+		}
+		req.Body = buf
+	}
+	// execute the http request
+	res, err := c.Client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	defer res.Body.Close()
+
+	// error response.
+	if res.Status > 300 {
+		err := new(Error)
+		_ = json.NewDecoder(res.Body).Decode(err)
+		return res, err
+	}
+	// the following is used for debugging purposes.
+	// bytes, err := io.ReadAll(res.Body)
+	// if err != nil {
+	// 	log.Fatal(err)
+	// }
+	// fmt.Println(string(bytes))
+
+	if out == nil {
+		return res, nil
+	}
+	// if a json response is expected, parse and return the json response.
+	decodeErr := json.NewDecoder(res.Body).Decode(out)
+	// following line is used for debugging purposes.
+	//_ = json.NewEncoder(os.Stdout).Encode(out)
+	return res, decodeErr
+}
+
+// Error represents am Azure error.
+type Error struct {
+	Message string `json:"message"`
+}
+
+func (e *Error) Error() string {
+	return e.Message
+}
+
+func ProjectRequiredError() error {
+	return errors.New("This API endpoint requires a project to be specified")
+}
+
+func SanitizeBranchName(name string) string {
+	return "refs/heads/" + name
+}
diff --git a/scm/driver/azure/azure_test.go b/scm/driver/azure/azure_test.go
new file mode 100644
index 000000000..c3cc56732
--- /dev/null
+++ b/scm/driver/azure/azure_test.go
@@ -0,0 +1,71 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"testing"
+)
+
+func TestClient_Base(t *testing.T) {
+	client, err := New("https://dev.azure.com", "org", "proj")
+	if err != nil {
+		t.Error(err)
+	}
+	got, want := client.BaseURL.String(), "https://dev.azure.com/"
+	if got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+}
+
+func TestClient_Default(t *testing.T) {
+	client := NewDefault("org", "proj")
+	if got, want := client.BaseURL.String(), "https://dev.azure.com/"; got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+}
+
+func TestClient_azure_special(t *testing.T) {
+	client, _ := New("https://dev.azure.com", "org", "")
+	if got, want := client.BaseURL.String(), "https://dev.azure.com/"; got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+	client2, _ := New("https://dev.azure.com", "", "proj")
+	if client2 != nil {
+		t.Errorf("Want nil client, got %v", client2)
+	}
+}
+
+func TestSanitizeBranchName(t *testing.T) {
+	type args struct {
+		name string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			"master",
+			args{
+				"master",
+			},
+			"refs/heads/master",
+		},
+		{
+			"feature/main-patch",
+			args{
+				"feature/main-patch",
+			},
+			"refs/heads/feature/main-patch",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := SanitizeBranchName(tt.args.name); got != tt.want {
+				t.Errorf("SanitizeBranchName() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/scm/driver/azure/content.go b/scm/driver/azure/content.go
new file mode 100644
index 000000000..a31258d8a
--- /dev/null
+++ b/scm/driver/azure/content.go
@@ -0,0 +1,218 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type contentService struct {
+	client *wrapper
+}
+
+func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/items/get?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	urlEncodedRef := url.QueryEscape(ref)
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/items?path=%s&includeContent=true&$format=json", s.client.owner, s.client.project, repo, path)
+	endpoint += generateURIFromRef(urlEncodedRef)
+	endpoint += "&api-version=6.0"
+	out := new(content)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
+	data := []byte(out.Content)
+	return &scm.Content{
+		Path:   out.Path,
+		Data:   data,
+		Sha:    out.CommitID,
+		BlobID: out.ObjectID,
+	}, res, err
+}
+
+func (s *contentService) Create(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	if s.client.project == "" {
+		return nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pushes?api-version=6.0", s.client.owner, s.client.project, repo)
+	ref := refUpdate{
+		Name:        SanitizeBranchName(params.Branch),
+		OldObjectID: params.Ref,
+	}
+	cha := change{
+		ChangeType: "add",
+	}
+	cha.Item.Path = path
+	cha.NewContent.Content = base64.StdEncoding.EncodeToString(params.Data)
+	cha.NewContent.ContentType = "base64encoded"
+
+	com := commit{
+		Comment: params.Message,
+		Changes: []change{cha},
+	}
+	in := &contentCreateUpdate{
+		RefUpdates: []refUpdate{ref},
+		Commits:    []commit{com},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) Update(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	if s.client.project == "" {
+		return nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pushes?api-version=6.0", s.client.owner, s.client.project, repo)
+	ref := refUpdate{
+		Name:        SanitizeBranchName(params.Branch),
+		OldObjectID: params.Sha,
+	}
+	cha := change{
+		ChangeType: "edit",
+	}
+	cha.Item.Path = path
+	cha.NewContent.Content = base64.StdEncoding.EncodeToString(params.Data)
+	cha.NewContent.ContentType = "base64encoded"
+
+	com := commit{
+		Comment: params.Message,
+		Changes: []change{cha},
+	}
+	in := &contentCreateUpdate{
+		RefUpdates: []refUpdate{ref},
+		Commits:    []commit{com},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	if s.client.project == "" {
+		return nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pushes?api-version=6.0", s.client.owner, s.client.project, repo)
+	ref := refUpdate{
+		Name:        SanitizeBranchName(params.Branch),
+		OldObjectID: params.Sha,
+	}
+	change1 := change{
+		ChangeType: "delete",
+	}
+	change1.Item.Path = path
+	com := commit{
+		Comment: params.Message,
+		Changes: []change{change1},
+	}
+	in := &contentCreateUpdate{
+		RefUpdates: []refUpdate{ref},
+		Commits:    []commit{com},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) List(ctx context.Context, repo, path, ref string, _ scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/items/list?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/items?scopePath=%s&recursionLevel=Full&$format=json", s.client.owner, s.client.project, repo, path)
+	endpoint += generateURIFromRef(ref)
+	out := new(contentList)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertContentInfoList(out.Value), res, err
+}
+
+type content struct {
+	ObjectID      string `json:"objectId"`
+	GitObjectType string `json:"gitObjectType"`
+	CommitID      string `json:"commitId"`
+	Path          string `json:"path"`
+	Content       string `json:"content"`
+	URL           string `json:"url"`
+	Links         struct {
+		Self struct {
+			Href string `json:"href"`
+		} `json:"self"`
+		Repository struct {
+			Href string `json:"href"`
+		} `json:"repository"`
+		Blob struct {
+			Href string `json:"href"`
+		} `json:"blob"`
+	} `json:"_links"`
+}
+
+type contentList struct {
+	Count int        `json:"count"`
+	Value []*content `json:"value"`
+}
+type refUpdate struct {
+	Name        string `json:"name"`
+	OldObjectID string `json:"oldObjectId,omitempty"`
+}
+type change struct {
+	ChangeType string `json:"changeType"`
+	Item       struct {
+		Path string `json:"path"`
+	} `json:"item"`
+	NewContent struct {
+		Content     string `json:"content,omitempty"`
+		ContentType string `json:"contentType,omitempty"`
+	} `json:"newContent,omitempty"`
+}
+type commit struct {
+	Comment string   `json:"comment"`
+	Changes []change `json:"changes"`
+}
+type contentCreateUpdate struct {
+	RefUpdates []refUpdate `json:"refUpdates"`
+	Commits    []commit    `json:"commits"`
+}
+
+func convertContentInfoList(from []*content) []*scm.ContentInfo {
+	to := []*scm.ContentInfo{}
+	for _, v := range from {
+		to = append(to, convertContentInfo(v))
+	}
+	return to
+}
+func convertContentInfo(from *content) *scm.ContentInfo {
+	to := &scm.ContentInfo{Path: from.Path}
+	switch from.GitObjectType {
+	case "blob":
+		to.Kind = scm.ContentKindFile
+	case "tree":
+		to.Kind = scm.ContentKindDirectory
+	default:
+		to.Kind = scm.ContentKindUnsupported
+	}
+	to.Path = from.Path
+	to.BlobID = from.ObjectID
+	to.Sha = from.CommitID
+	return to
+}
+
+func generateURIFromRef(ref string) (uri string) {
+	if ref != "" {
+		if len(ref) == 40 {
+			return fmt.Sprintf("&versionDescriptor.versionType=commit&versionDescriptor.version=%s", ref)
+		} else if strings.HasPrefix(ref, "refs/tags/") {
+			return fmt.Sprintf("&versionDescriptor.versionType=tag&versionDescriptor.version=%s", scm.TrimRef(ref))
+		} else {
+			return fmt.Sprintf("&versionDescriptor.versionType=branch&versionDescriptor.version=%s", ref)
+		}
+	}
+	return ""
+}
diff --git a/scm/driver/azure/content_test.go b/scm/driver/azure/content_test.go
new file mode 100644
index 000000000..ad857ccd9
--- /dev/null
+++ b/scm/driver/azure/content_test.go
@@ -0,0 +1,203 @@
+package azure
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestContentFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/items").
+		MatchParam("path", "README").
+		MatchParam("versionDescriptor.version", "b1&b2").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Contents.Find(
+		context.Background(),
+		"REPOID",
+		"README",
+		"b1&b2",
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Content)
+	raw, _ := ioutil.ReadFile("testdata/content.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestContentCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Post("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(201).
+		Type("application/json").
+		File("testdata/content_create.json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Data:    []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
+	}
+
+	client := NewDefault("ORG", "PROJ")
+	res, err := client.Contents.Create(
+		context.Background(),
+		"REPOID",
+		"README",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentUpdate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Post("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_update.json")
+
+	params := &scm.ContentParams{
+		Message: "a new commit message",
+		Data:    []byte("bXkgdXBkYXRlZCBmaWxlIGNvbnRlbnRz"),
+		Sha:     "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
+	}
+
+	client := NewDefault("ORG", "PROJ")
+	res, err := client.Contents.Update(
+		context.Background(),
+		"REPOID",
+		"README",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Post("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_delete.json")
+
+	params := &scm.ContentParams{
+		Message: "a new commit message",
+		BlobID:  "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
+	}
+
+	client := NewDefault("ORG", "PROJ")
+	res, err := client.Contents.Delete(
+		context.Background(),
+		"REPOID",
+		"README",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_list.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Contents.List(
+		context.Background(),
+		"REPOID",
+		"",
+		"",
+		scm.ListOptions{},
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.ContentInfo{}
+	raw, _ := ioutil.ReadFile("testdata/content_list.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func Test_generateURIFromRef(t *testing.T) {
+	type args struct {
+		ref string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		wantUri string
+	}{
+		{
+			name:    "branch",
+			args:    args{ref: "branch"},
+			wantUri: "&versionDescriptor.versionType=branch&versionDescriptor.version=branch",
+		},
+		{
+			name:    "commit",
+			args:    args{ref: "6bbcbc818c804d35b88a12bbd2ed297e41c4d10d"},
+			wantUri: "&versionDescriptor.versionType=commit&versionDescriptor.version=6bbcbc818c804d35b88a12bbd2ed297e41c4d10d",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if gotUri := generateURIFromRef(tt.args.ref); gotUri != tt.wantUri {
+				t.Errorf("generateURIFromRef() = %v, want %v", gotUri, tt.wantUri)
+			}
+		})
+	}
+}
diff --git a/scm/driver/azure/git.go b/scm/driver/azure/git.go
new file mode 100644
index 000000000..dae86f58b
--- /dev/null
+++ b/scm/driver/azure/git.go
@@ -0,0 +1,317 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type gitService struct {
+	client *wrapper
+}
+
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/refs/update-refs?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/refs?api-version=6.0", s.client.owner, s.client.project, repo)
+
+	in := make(crudBranch, 1)
+	in[0].Name = scm.ExpandRef(params.Name, "refs/heads")
+	in[0].NewObjectID = params.Sha
+	in[0].OldObjectID = "0000000000000000000000000000000000000000"
+	return s.client.do(ctx, "POST", endpoint, in, nil)
+}
+
+func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/commits/get?view=azure-devops-rest-6.0#get-by-id
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/commits/%s?api-version=6.0", s.client.owner, s.client.project, repo, ref)
+	out := new(gitCommit)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
+	return convertCommit(out), res, err
+}
+
+func (s *gitService) FindTag(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *gitService) ListBranches(ctx context.Context, repo string, _ scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/refs/list?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/refs?includeMyBranches=true&api-version=6.0", s.client.owner, s.client.project, repo)
+	out := new(branchList)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertBranchList(out.Value), res, err
+}
+
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/refs/list?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/refs?api-version=6.0&filterContains=%s", s.client.owner, s.client.project, repo, opts.SearchTerm)
+	out := new(branchList)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertBranchList(out.Value), res, err
+}
+
+func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/commits/get-commits?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/commits?", s.client.owner, s.client.project, repo)
+	if opts.Ref != "" {
+		endpoint += fmt.Sprintf("searchCriteria.itemVersion.version=%s&", opts.Ref)
+	}
+	if opts.Path != "" {
+		endpoint += fmt.Sprintf("searchCriteria.itemPath=%s&", opts.Path)
+	}
+	endpoint += "api-version=6.0"
+
+	out := new(commitList)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertCommitList(out.Value), res, err
+}
+
+func (s *gitService) ListTags(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/refs?", s.client.owner, s.client.project, repo)
+	// add tags
+	endpoint += fmt.Sprintf("filter=tags/")
+	// add target
+	endpoint += fmt.Sprintf("&api-version=7.1-preview.1")
+	out := new(tags)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+
+	return convertTags(out.Value), res, err
+}
+
+func (s *gitService) ListChanges(ctx context.Context, repo, ref string, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *gitService) CompareChanges(ctx context.Context, repo, source, target string, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/diffs/get?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/diffs/commits?", s.client.owner, s.client.project, repo)
+	// add base
+	endpoint += fmt.Sprintf("baseVersion=%s&baseVersionType=commit&", source)
+	// add target
+	endpoint += fmt.Sprintf("targetVersion=%s&targetVersionType=commit&api-version=6.0", target)
+	out := new(compare)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+
+	changes := out.Changes
+	return convertChangeList(changes), res, err
+}
+
+type crudBranch []struct {
+	Name        string `json:"name"`
+	OldObjectID string `json:"oldObjectId"`
+	NewObjectID string `json:"newObjectId"`
+}
+
+type branchList struct {
+	Value []*branch `json:"value"`
+	Count int       `json:"count"`
+}
+
+type branch struct {
+	Name     string `json:"name"`
+	ObjectID string `json:"objectId"`
+	Creator  struct {
+		DisplayName string `json:"displayName"`
+		URL         string `json:"url"`
+		Links       struct {
+			Avatar struct {
+				Href string `json:"href"`
+			} `json:"avatar"`
+		} `json:"_links"`
+		ID         string `json:"id"`
+		UniqueName string `json:"uniqueName"`
+		ImageURL   string `json:"imageUrl"`
+		Descriptor string `json:"descriptor"`
+	} `json:"creator"`
+	URL string `json:"url"`
+}
+
+type commitList struct {
+	Value []*gitCommit `json:"value"`
+	Count int          `json:"count"`
+}
+type gitCommit struct {
+	CommitID string `json:"commitId"`
+	Author   struct {
+		Name  string    `json:"name"`
+		Email string    `json:"email"`
+		Date  time.Time `json:"date"`
+	} `json:"author"`
+	Committer struct {
+		Name  string    `json:"name"`
+		Email string    `json:"email"`
+		Date  time.Time `json:"date"`
+	} `json:"committer"`
+	Comment          string `json:"comment"`
+	CommentTruncated bool   `json:"commentTruncated"`
+	ChangeCounts     struct {
+		Add    int `json:"Add"`
+		Edit   int `json:"Edit"`
+		Delete int `json:"Delete"`
+	} `json:"changeCounts"`
+	URL       string `json:"url"`
+	RemoteURL string `json:"remoteUrl"`
+}
+
+type file struct {
+	ChangeType string `json:"changeType"`
+	Item       struct {
+		CommitID         string `json:"commitId"`
+		GitObjectType    string `json:"gitObjectType"`
+		IsFolder         bool   `json:"isFolder"`
+		ObjectID         string `json:"objectId"`
+		OriginalObjectID string `json:"originalObjectId"`
+		Path             string `json:"path"`
+		URL              string `json:"url"`
+	} `json:"item"`
+}
+
+type compare struct {
+	AheadCount         int64  `json:"aheadCount"`
+	AllChangesIncluded bool   `json:"allChangesIncluded"`
+	BaseCommit         string `json:"baseCommit"`
+	BehindCount        int64  `json:"behindCount"`
+	ChangeCounts       struct {
+		Add  int64 `json:"Add"`
+		Edit int64 `json:"Edit"`
+	} `json:"changeCounts"`
+	Changes      []*file `json:"changes"`
+	CommonCommit string  `json:"commonCommit"`
+	TargetCommit string  `json:"targetCommit"`
+}
+
+type tags struct {
+	Value []*tag `json:"value"`
+	Count int    `json:"count"`
+}
+type tag struct {
+	Name     string `json:"name"`
+	ObjectID string `json:"objectId"`
+	Creator  struct {
+		DisplayName string `json:"displayName"`
+		URL         string `json:"url"`
+		Links       struct {
+			Avatar struct {
+				Href string `json:"href"`
+			} `json:"avatar"`
+		} `json:"_links"`
+		ID         string `json:"id"`
+		UniqueName string `json:"uniqueName"`
+		ImageURL   string `json:"imageUrl"`
+		Descriptor string `json:"descriptor"`
+	} `json:"creator"`
+	URL string `json:"url"`
+}
+
+func convertBranchList(from []*branch) []*scm.Reference {
+	to := []*scm.Reference{}
+	for _, v := range from {
+		to = append(to, convertBranch(v))
+	}
+	return to
+}
+
+func convertBranch(from *branch) *scm.Reference {
+	return &scm.Reference{
+		Name: scm.TrimRef(from.Name),
+		Path: from.Name,
+		Sha:  from.ObjectID,
+	}
+}
+
+func convertCommitList(from []*gitCommit) []*scm.Commit {
+	to := []*scm.Commit{}
+	for _, v := range from {
+		to = append(to, convertCommit(v))
+	}
+	return to
+}
+
+func convertCommit(from *gitCommit) *scm.Commit {
+	return &scm.Commit{
+		Message: from.Comment,
+		Sha:     from.CommitID,
+		Link:    from.URL,
+		Author: scm.Signature{
+			Login: from.Author.Name,
+			Name:  from.Author.Name,
+			Email: from.Author.Email,
+			Date:  from.Author.Date,
+		},
+		Committer: scm.Signature{
+			Login: from.Committer.Name,
+			Name:  from.Committer.Name,
+			Email: from.Committer.Email,
+			Date:  from.Committer.Date,
+		},
+	}
+}
+
+func convertChangeList(from []*file) []*scm.Change {
+	to := []*scm.Change{}
+	for _, v := range from {
+		to = append(to, convertChange(v))
+	}
+	return to
+}
+
+func convertChange(from *file) *scm.Change {
+	returnVal := &scm.Change{
+		Path: from.Item.Path,
+	}
+	switch from.ChangeType {
+	case "add":
+		returnVal.Added = true
+	case "delete":
+		returnVal.Deleted = true
+	case "rename":
+		returnVal.Renamed = true
+	}
+
+	return returnVal
+}
+
+func convertTags(from []*tag) []*scm.Reference {
+	var to []*scm.Reference
+	for _, v := range from {
+		to = append(to, &scm.Reference{
+			Name: scm.TrimRef(v.Name),
+			Path: scm.ExpandRef(v.Name, "refs/tags/"),
+			Sha:  v.ObjectID,
+		})
+	}
+	return to
+}
diff --git a/scm/driver/azure/git_test.go b/scm/driver/azure/git_test.go
new file mode 100644
index 000000000..4f79b78eb
--- /dev/null
+++ b/scm/driver/azure/git_test.go
@@ -0,0 +1,236 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestGitFindCommit(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commit.json")
+
+	client := NewDefault("ORG", "PROJ")
+
+	got, _, err := client.Git.FindCommit(context.Background(), "REPOID", "14897f4465d2d63508242b5cbf68aa2865f693e7")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Commit)
+	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
+
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestGitCreateBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Post("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(201).
+		Type("application/json").
+		File("testdata/branch_create.json")
+
+	params := &scm.ReferenceInput{
+		Name: "test_branch",
+		Sha:  "312797ba52425353dec56871a255e2a36fc96344",
+	}
+
+	client := NewDefault("ORG", "PROJ")
+	res, err := client.Git.CreateBranch(context.Background(), "REPOID", params)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestGitListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Git.ListCommits(context.Background(), "REPOID", scm.CommitListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestGitListBranches(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branches.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Git.ListBranches(context.Background(), "REPOID", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestGitListBranchesV2(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branches_filter.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Git.ListBranchesV2(context.Background(), "REPOID", scm.BranchListOptions{SearchTerm: "main"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches_filter.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestGitCompareChanges(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/compare.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Git.CompareChanges(context.Background(), "REPOID", "9788e5ddf8b387cb79228628f34d8dc18582d606", "66df312dad61e84dd896d1e8d14ee3dce53b62f0", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Change{}
+	raw, _ := ioutil.ReadFile("testdata/compare.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+}
+
+func TestGitListTags(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(200).
+		Type("application/json").
+		File("testdata/tags.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Git.ListTags(context.Background(), "REPOID", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/tags.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestConvertTags(t *testing.T) {
+	from := []*tag{
+		{
+			Name:     "refs/tags/v1.0.0",
+			ObjectID: "23d9c1d0d6c41f1c8e08ab98a6a79c2d5ada649d",
+		},
+		{
+			Name:     "refs/tags/v1.1.0",
+			ObjectID: "8a9db19b5e19613c18e9059d7b9fa5d6d6a3785f",
+		},
+	}
+
+	got := convertTags(from)
+
+	want := []*scm.Reference{
+		{
+			Name: "v1.0.0",
+			Path: "refs/tags/v1.0.0",
+			Sha:  "23d9c1d0d6c41f1c8e08ab98a6a79c2d5ada649d",
+		},
+		{
+			Name: "v1.1.0",
+			Path: "refs/tags/v1.1.0",
+			Sha:  "8a9db19b5e19613c18e9059d7b9fa5d6d6a3785f",
+		},
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/azure/integration/content_test.go b/scm/driver/azure/integration/content_test.go
new file mode 100644
index 000000000..b2b200052
--- /dev/null
+++ b/scm/driver/azure/integration/content_test.go
@@ -0,0 +1,132 @@
+package integration
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/azure"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestContentsFind(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Integration test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	content, response, err := client.Contents.Find(context.Background(), repoID, "README.md", "")
+	if err != nil {
+		t.Errorf("We got an error %v", err)
+	}
+	if content.Sha == "" {
+		t.Errorf("we did not get a sha back %v", content.Sha)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("we did not get a 200 back %v", response.Status)
+	}
+}
+
+func TestCreateUpdateDeleteFileAzure(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	// get latest commit first
+	currentCommit, commitErr := GetCurrentCommitOfBranch(client, "main")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	// create a new file
+	createParams := scm.ContentParams{
+		Message: "go-scm create crud file",
+		Data:    []byte("hello"),
+		Branch:  "refs/heads/main",
+		Ref:     currentCommit,
+	}
+	createResponse, createErr := client.Contents.Create(context.Background(), repoID, "CRUD", &createParams)
+	if createErr != nil {
+		t.Errorf("Contents.Create we got an error %v", createErr)
+	}
+	if createResponse.Status != http.StatusCreated {
+		t.Errorf("Contents.Create we did not get a 201 back %v", createResponse.Status)
+	}
+	// get latest commit first
+	currentCommit, commitErr = GetCurrentCommitOfBranch(client, "main")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	// update the file
+	updateParams := scm.ContentParams{
+		Message: "go-scm update crud file",
+		Data:    []byte("updated test data"),
+		Branch:  "refs/heads/main",
+		Sha:     currentCommit,
+	}
+	updateResponse, updateErr := client.Contents.Update(context.Background(), repoID, "CRUD", &updateParams)
+	if updateErr != nil {
+		t.Errorf("Contents.Update we got an error %v", updateErr)
+	}
+	if updateResponse.Status != http.StatusCreated {
+		t.Errorf("Contents.Update we did not get a 201 back %v", updateResponse.Status)
+	}
+	// get latest commit first
+	currentCommit, commitErr = GetCurrentCommitOfBranch(client, "main")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	// delete the file
+	deleteParams := scm.ContentParams{
+		Message: "go-scm delete crud file",
+		Branch:  "refs/heads/main",
+		Sha:     currentCommit,
+	}
+	deleteResponse, deleteErr := client.Contents.Delete(context.Background(), repoID, "CRUD", &deleteParams)
+	if deleteErr != nil {
+		t.Errorf("Contents.Delete we got an error %v", deleteErr)
+	}
+	if deleteResponse.Status != http.StatusCreated {
+		t.Errorf("Contents.Delete we did not get a 201 back %v", deleteResponse.Status)
+	}
+}
+
+func TestListFiles(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	contentInfo, listResponse, listerr := client.Contents.List(context.Background(),
+		repoID, "", "", scm.ListOptions{})
+	if listerr != nil {
+		t.Errorf("Contents.List we got an error %v", listerr)
+	}
+	if listResponse.Status != http.StatusOK {
+		t.Errorf("Contents.Delete we did not get a 200 back %v", listResponse.Status)
+	}
+	if 2 >= len(contentInfo) {
+		t.Errorf("Contents.List there should be at least 2 files %d", len(contentInfo))
+	}
+
+}
diff --git a/scm/driver/azure/integration/git_test.go b/scm/driver/azure/integration/git_test.go
new file mode 100644
index 000000000..aeb9f41b8
--- /dev/null
+++ b/scm/driver/azure/integration/git_test.go
@@ -0,0 +1,154 @@
+package integration
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/azure"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestListBranches(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	references, response, listerr := client.Git.ListBranches(context.Background(), repoID, scm.ListOptions{})
+	if listerr != nil {
+		t.Errorf("ListBranches got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("ListBranches did not get a 200 back %v", response.Status)
+	}
+	if len(references) < 1 {
+		t.Errorf("ListBranches  should have at least 1 branch %d", len(references))
+	}
+	if references[0].Sha == "" {
+		t.Errorf("ListBranches first entry did not get a sha back %v", references[0].Sha)
+	}
+}
+
+func TestCreateBranch(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	currentCommit, commitErr := GetCurrentCommitOfBranch(client, "main")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	input := &scm.ReferenceInput{
+		Name: "test_branch",
+		Sha:  currentCommit,
+	}
+	response, listerr := client.Git.CreateBranch(context.Background(), repoID, input)
+	if listerr != nil {
+		t.Errorf("CreateBranch got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("CreateBranch did not get a 200 back %v", response.Status)
+	}
+
+}
+
+func TestFindCommit(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	currentCommit, commitErr := GetCurrentCommitOfBranch(client, "main")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	commit, response, listerr := client.Git.FindCommit(context.Background(), repoID, currentCommit)
+	if listerr != nil {
+		t.Errorf("FindCommit got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("FindCommit did not get a 200 back %v", response.Status)
+	}
+	if commit.Author.Name == "" {
+		t.Errorf("There is no author %v", commit.Author)
+	}
+}
+
+func TestListCommits(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	commits, response, listerr := client.Git.ListCommits(context.Background(), repoID, scm.CommitListOptions{})
+	if listerr != nil {
+		t.Errorf("ListCommits  got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("ListCommitsdid not get a 200 back %v", response.Status)
+	}
+	if len(commits) < 1 {
+		t.Errorf("Contents.List there should be at least 1 commit %d", len(commits))
+	}
+	if commits[0].Sha == "" {
+		t.Errorf("Contents.List first entry did not get a sha back %v", commits[0].Sha)
+	}
+}
+
+func TestCompareChanges(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	// get all the commits
+	commits, _, err := client.Git.ListCommits(context.Background(), repoID, scm.CommitListOptions{})
+	if err != nil {
+		t.Errorf("we got an error %v", err)
+	}
+	// compare the last two commits
+	changes, response, listerr := client.Git.CompareChanges(context.Background(), repoID, commits[10].Sha, commits[0].Sha, scm.ListOptions{})
+	if listerr != nil {
+		t.Errorf("CompareChanges got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("CompareChanges did not get a 200 back %v", response.Status)
+	}
+	if len(changes) == 0 {
+		t.Errorf("There is at least one change %d", len(changes))
+	}
+}
diff --git a/scm/driver/azure/integration/integration.go b/scm/driver/azure/integration/integration.go
new file mode 100644
index 000000000..682b35830
--- /dev/null
+++ b/scm/driver/azure/integration/integration.go
@@ -0,0 +1,25 @@
+package integration
+
+import (
+	"context"
+	"os"
+
+	"github.com/drone/go-scm/scm"
+)
+
+var (
+	client *scm.Client
+	token  = os.Getenv("AZURE_TOKEN")
+
+	organization = "tphoney"
+	project      = "test_project"
+	repoID       = "fde2d21f-13b9-4864-a995-83329045289a"
+)
+
+func GetCurrentCommitOfBranch(client *scm.Client, branch string) (string, error) {
+	commit, _, err := client.Contents.List(context.Background(), repoID, "", "main", scm.ListOptions{})
+	if err != nil {
+		return "", err
+	}
+	return commit[0].Sha, nil
+}
diff --git a/scm/driver/azure/integration/pr_test.go b/scm/driver/azure/integration/pr_test.go
new file mode 100644
index 000000000..b82307340
--- /dev/null
+++ b/scm/driver/azure/integration/pr_test.go
@@ -0,0 +1,93 @@
+package integration
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/azure"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestCreatePR(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	input := &scm.PullRequestInput{
+		Title:  "test_pr",
+		Body:   "test_pr_body",
+		Source: "pr_branch",
+		Target: "main",
+	}
+	outputPR, response, listerr := client.PullRequests.Create(context.Background(), repoID, input)
+	if listerr != nil {
+		t.Errorf("PullRequests.Create got an error %v", listerr)
+	}
+	if response.Status != http.StatusCreated {
+		t.Errorf("PullRequests.Create did not get a 201 back %v", response.Status)
+	}
+	if outputPR.Title != "test_pr" {
+		t.Errorf("PullRequests.Create does not have the correct title %v", outputPR.Title)
+	}
+}
+
+func TestPullRequestFind(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	outputPR, response, err := client.PullRequests.Find(context.Background(), repoID, 1)
+	if err != nil {
+		t.Errorf("PullRequests.Find got an error %v", err)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("PullRequests.Find did not get a 200 back %v", response.Status)
+	}
+	if outputPR.Title != "test_pr" {
+		t.Errorf("PullRequests.Find does not have the correct title %v", outputPR.Title)
+	}
+}
+
+func TestPullRequestCommits(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	commits, response, err := client.PullRequests.ListCommits(context.Background(), repoID, 1, scm.ListOptions{})
+	if err != nil {
+		t.Errorf("PullRequests.ListCommits got an error %v", err)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("PullRequests.ListCommits did not get a 200 back %v", response.Status)
+	}
+	if len(commits) < 1 {
+		t.Errorf("PullRequests.ListCommits there should be at least 1 commit %d", len(commits))
+	}
+	if commits[0].Sha == "" {
+		t.Errorf("PullRequests.ListCommits first entry did not get a sha back %v", commits[0].Sha)
+	}
+}
diff --git a/scm/driver/azure/integration/repo_test.go b/scm/driver/azure/integration/repo_test.go
new file mode 100644
index 000000000..27f0bf543
--- /dev/null
+++ b/scm/driver/azure/integration/repo_test.go
@@ -0,0 +1,109 @@
+package integration
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/azure"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestListRepos(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	references, response, listerr := client.Repositories.List(context.Background(), scm.ListOptions{})
+	if listerr != nil {
+		t.Errorf("List got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("List did not get a 200 back %v", response.Status)
+	}
+	if len(references) < 1 {
+		t.Errorf("List should have at least 1 repo %d", len(references))
+	}
+}
+
+func TestListHooks(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	hooks, response, listerr := client.Repositories.ListHooks(context.Background(), repoID, scm.ListOptions{})
+	if listerr != nil {
+		t.Errorf("List got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("List did not get a 200 back %v", response.Status)
+	}
+	if len(hooks) < 1 {
+		t.Errorf("List should have at least 1 hook %d", len(hooks))
+	}
+}
+
+func TestCreateDeleteHooks(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client = azure.NewDefault(organization, project)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("Authorization", fmt.Sprintf("Basic %s", token))
+			},
+		},
+	}
+	originalHooks, _, _ := client.Repositories.ListHooks(context.Background(), repoID, scm.ListOptions{})
+	// create a new hook
+	inputHook := &scm.HookInput{
+		Name:         "web",
+		NativeEvents: []string{"git.push"},
+		Target:       "http://www.example.com/webhook",
+	}
+	outHook, createResponse, createErr := client.Repositories.CreateHook(context.Background(), repoID, inputHook)
+	if createErr != nil {
+		t.Errorf("Create got an error %v", createErr)
+	}
+	if createResponse.Status != http.StatusOK {
+		t.Errorf("Create did not get a 200 back %v", createResponse.Status)
+	}
+	if len(outHook.Events) != 1 {
+		t.Errorf("New hook has one event %d", len(outHook.Events))
+	}
+	// get the hooks again, and make sure the new hook is there
+	afterCreate, _, _ := client.Repositories.ListHooks(context.Background(), repoID, scm.ListOptions{})
+	if len(afterCreate) != len(originalHooks)+1 {
+		t.Errorf("After create, the number of hooks is not correct %d. It should be %d", len(afterCreate), len(originalHooks)+1)
+	}
+	// delete the hook we created
+	deleteResponse, deleteErr := client.Repositories.DeleteHook(context.Background(), repoID, outHook.ID)
+	if deleteErr != nil {
+		t.Errorf("Delete got an error %v", deleteErr)
+	}
+	if deleteResponse.Status != http.StatusNoContent {
+		t.Errorf("Delete did not get a 204 back, got %v", deleteResponse.Status)
+	}
+	// get the hooks again, and make sure the new hook is gone
+	afterDelete, _, _ := client.Repositories.ListHooks(context.Background(), repoID, scm.ListOptions{})
+	if len(afterDelete) != len(originalHooks) {
+		t.Errorf("After Delete, the number of hooks is not correct %d. It should be %d", len(afterDelete), len(originalHooks))
+	}
+}
diff --git a/scm/driver/azure/issue.go b/scm/driver/azure/issue.go
new file mode 100644
index 000000000..6179d8312
--- /dev/null
+++ b/scm/driver/azure/issue.go
@@ -0,0 +1,55 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type issueService struct {
+	client *wrapper
+}
+
+func (s *issueService) Find(ctx context.Context, repo string, number int) (*scm.Issue, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) FindComment(ctx context.Context, repo string, index, id int) (*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) List(ctx context.Context, repo string, opts scm.IssueListOptions) ([]*scm.Issue, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Create(ctx context.Context, repo string, input *scm.IssueInput) (*scm.Issue, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) DeleteComment(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Close(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Lock(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Unlock(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/azure/linker.go b/scm/driver/azure/linker.go
new file mode 100644
index 000000000..fc08ef2cc
--- /dev/null
+++ b/scm/driver/azure/linker.go
@@ -0,0 +1,25 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type linker struct {
+	base string
+}
+
+// Resource returns a link to the resource.
+func (l *linker) Resource(ctx context.Context, repo string, ref scm.Reference) (string, error) {
+	return "", scm.ErrNotSupported
+}
+
+// Diff returns a link to the diff.
+func (l *linker) Diff(ctx context.Context, repo string, source, target scm.Reference) (string, error) {
+	return "", scm.ErrNotSupported
+}
diff --git a/scm/driver/azure/org.go b/scm/driver/azure/org.go
new file mode 100644
index 000000000..9ad977b9b
--- /dev/null
+++ b/scm/driver/azure/org.go
@@ -0,0 +1,27 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type organizationService struct {
+	client *wrapper
+}
+
+func (s *organizationService) Find(ctx context.Context, name string) (*scm.Organization, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *organizationService) FindMembership(ctx context.Context, name, username string) (*scm.Membership, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *organizationService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Organization, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/azure/pr.go b/scm/driver/azure/pr.go
new file mode 100644
index 000000000..d2e36c48f
--- /dev/null
+++ b/scm/driver/azure/pr.go
@@ -0,0 +1,189 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type pullService struct {
+	*issueService
+}
+
+func (s *pullService) Find(ctx context.Context, repo string, number int) (*scm.PullRequest, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-request?view=azure-devops-rest-6.0
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pullrequests/%d?api-version=6.0",
+		s.client.owner, s.client.project, repo, number)
+	out := new(pr)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
+	return convertPullRequest(out), res, err
+}
+
+func (s *pullService) List(ctx context.Context, repo string, opts scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *pullService) ListChanges(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *pullService) ListCommits(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-commits/get-pull-request-commits?view=azure-devops-rest-6.0
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pullRequests/%d/commits?api-version=6.0",
+		s.client.owner, s.client.project, repo, number)
+	out := new(commitList)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
+	return convertCommitList(out.Value), res, err
+}
+
+func (s *pullService) Merge(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *pullService) Close(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/create?view=azure-devops-rest-6.0
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pullrequests?api-version=6.0", s.client.owner, s.client.project, repo)
+	in := &prInput{
+		Title:         input.Title,
+		Description:   input.Body,
+		SourceRefName: scm.ExpandRef(input.Source, "refs/heads"),
+		TargetRefName: scm.ExpandRef(input.Target, "refs/heads"),
+	}
+	out := new(pr)
+	res, err := s.client.do(ctx, "POST", endpoint, in, out)
+	return convertPullRequest(out), res, err
+}
+
+type prInput struct {
+	SourceRefName string `json:"sourceRefName"`
+	TargetRefName string `json:"targetRefName"`
+	Title         string `json:"title"`
+	Description   string `json:"description"`
+	Reviewers     []struct {
+		ID string `json:"id"`
+	} `json:"reviewers"`
+}
+
+type pr struct {
+	Repository struct {
+		ID      string `json:"id"`
+		Name    string `json:"name"`
+		URL     string `json:"url"`
+		Project struct {
+			ID          string `json:"id"`
+			Name        string `json:"name"`
+			Description string `json:"description"`
+			URL         string `json:"url"`
+			State       string `json:"state"`
+			Revision    int    `json:"revision"`
+		} `json:"project"`
+		RemoteURL string `json:"remoteUrl"`
+	} `json:"repository"`
+	PullRequestID int    `json:"pullRequestId"`
+	CodeReviewID  int    `json:"codeReviewId"`
+	Status        string `json:"status"`
+	CreatedBy     struct {
+		ID          string `json:"id"`
+		DisplayName string `json:"displayName"`
+		UniqueName  string `json:"uniqueName"`
+		URL         string `json:"url"`
+		ImageURL    string `json:"imageUrl"`
+	} `json:"createdBy"`
+	CreationDate          time.Time   `json:"creationDate"`
+	ClosedDate            null.String `json:"closedDate"`
+	Title                 string      `json:"title"`
+	Description           string      `json:"description"`
+	SourceRefName         string      `json:"sourceRefName"`
+	TargetRefName         string      `json:"targetRefName"`
+	MergeStatus           string      `json:"mergeStatus"`
+	IsDraft               bool        `json:"isDraft"`
+	MergeID               string      `json:"mergeId"`
+	LastMergeSourceCommit struct {
+		CommitID string `json:"commitId"`
+		URL      string `json:"url"`
+	} `json:"lastMergeSourceCommit"`
+	LastMergeTargetCommit struct {
+		CommitID string `json:"commitId"`
+		URL      string `json:"url"`
+	} `json:"lastMergeTargetCommit"`
+	Reviewers []struct {
+		ReviewerURL string `json:"reviewerUrl"`
+		Vote        int    `json:"vote"`
+		ID          string `json:"id"`
+		DisplayName string `json:"displayName"`
+		UniqueName  string `json:"uniqueName"`
+		URL         string `json:"url"`
+		ImageURL    string `json:"imageUrl"`
+	} `json:"reviewers"`
+	URL   string `json:"url"`
+	Links struct {
+		Self struct {
+			Href string `json:"href"`
+		} `json:"self"`
+		Repository struct {
+			Href string `json:"href"`
+		} `json:"repository"`
+		WorkItems struct {
+			Href string `json:"href"`
+		} `json:"workItems"`
+		SourceBranch struct {
+			Href string `json:"href"`
+		} `json:"sourceBranch"`
+		TargetBranch struct {
+			Href string `json:"href"`
+		} `json:"targetBranch"`
+		SourceCommit struct {
+			Href string `json:"href"`
+		} `json:"sourceCommit"`
+		TargetCommit struct {
+			Href string `json:"href"`
+		} `json:"targetCommit"`
+		CreatedBy struct {
+			Href string `json:"href"`
+		} `json:"createdBy"`
+		Iterations struct {
+			Href string `json:"href"`
+		} `json:"iterations"`
+	} `json:"_links"`
+	SupportsIterations bool   `json:"supportsIterations"`
+	ArtifactID         string `json:"artifactId"`
+}
+
+func convertPullRequest(from *pr) *scm.PullRequest {
+	return &scm.PullRequest{
+		Number: from.PullRequestID,
+		Title:  from.Title,
+		Body:   from.Description,
+		Sha:    from.LastMergeSourceCommit.CommitID,
+		Source: scm.TrimRef(from.SourceRefName),
+		Target: scm.TrimRef(from.TargetRefName),
+		Link:   from.URL,
+		Draft:  from.IsDraft,
+		Closed: from.ClosedDate.Valid,
+		Merged: from.Status == "completed",
+		Ref:    fmt.Sprintf("refs/pull/%d/merge", from.PullRequestID),
+		Head: scm.Reference{
+			Sha: from.LastMergeSourceCommit.CommitID,
+		},
+		Base: scm.Reference{
+
+			Sha: from.LastMergeTargetCommit.CommitID,
+		},
+		Author: scm.User{
+			Login:  from.CreatedBy.UniqueName,
+			Avatar: from.CreatedBy.ImageURL,
+		},
+		Created: from.CreationDate,
+	}
+}
diff --git a/scm/driver/azure/pr_test.go b/scm/driver/azure/pr_test.go
new file mode 100644
index 000000000..42b60dfe7
--- /dev/null
+++ b/scm/driver/azure/pr_test.go
@@ -0,0 +1,102 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestPullCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Post("/ORG/PROJ/_apis/git/repositories/REPOID/").
+		Reply(201).
+		Type("application/json").
+		File("testdata/pr.json")
+
+	input := scm.PullRequestInput{
+		Title:  "test_pr",
+		Body:   "test_pr_body",
+		Source: "pr_branch",
+		Target: "main",
+	}
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.PullRequests.Create(context.Background(), "REPOID", &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.PullRequest)
+	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/pullrequests/1").
+		Reply(200).
+		Type("application/json").
+		File("testdata/pr.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.PullRequests.Find(context.Background(), "REPOID", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.PullRequest)
+	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/REPOID/pullRequests/1/commits").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.PullRequests.ListCommits(context.Background(), "REPOID", 1, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/azure/repo.go b/scm/driver/azure/repo.go
new file mode 100644
index 000000000..64eead88b
--- /dev/null
+++ b/scm/driver/azure/repo.go
@@ -0,0 +1,319 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+// RepositoryService implements the repository service for
+// the GitHub driver.
+type RepositoryService struct {
+	client *wrapper
+}
+
+// Find returns the repository by name.
+func (s *RepositoryService) Find(ctx context.Context, repo string) (*scm.Repository, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/repositories/get?view=azure-devops-rest-4.1
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/%s/_apis/git/repositories/%s?api-version=6.0", s.client.owner, s.client.project, repo)
+
+	out := new(repository)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertRepository(out, s.client.owner), res, err
+}
+
+// FindHook returns a repository hook.
+func (s *RepositoryService) FindHook(ctx context.Context, repo string, id string) (*scm.Hook, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// FindPerms returns the repository permissions.
+func (s *RepositoryService) FindPerms(ctx context.Context, repo string) (*scm.Perm, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// List returns the user repository list.
+func (s *RepositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/git/repositories/list?view=azure-devops-rest-6.0
+	var endpoint string
+	if s.client.project == "" {
+		endpoint = fmt.Sprintf("%s/_apis/git/repositories?api-version=6.0", s.client.owner)
+	} else {
+		endpoint = fmt.Sprintf("%s/%s/_apis/git/repositories?api-version=6.0", s.client.owner, s.client.project)
+	}
+
+	out := new(repositories)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertRepositoryList(out, s.client.owner), res, err
+}
+
+// ListV2 returns the user repository list.
+func (s *RepositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// Azure does not support search filters, hence calling List api without search filtering
+	return s.List(ctx, opts.ListOptions)
+}
+
+// ListNamespace is of no use in azure as our client already has project information
+func (s *RepositoryService) ListNamespace(ctx context.Context, _ string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// Azure client already has org/proj information
+	return s.List(ctx, opts)
+}
+
+// ListHooks returns a list or repository hooks.
+func (s *RepositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/hooks/subscriptions/list?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	projectID, projErr := s.getProjectIDFromProjectName(ctx, s.client.project)
+	if projErr != nil {
+		return nil, nil, fmt.Errorf("ListHooks was unable to look up the project's projectID, %s", projErr)
+	}
+	endpoint := fmt.Sprintf("%s/_apis/hooks/subscriptions?api-version=6.0", s.client.owner)
+	out := new(subscriptions)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertHookList(out.Value, projectID, repo), res, err
+}
+
+// ListStatus returns a list of commit statuses.
+func (s *RepositoryService) ListStatus(ctx context.Context, repo, ref string, opts scm.ListOptions) ([]*scm.Status, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// CreateHook creates a new repository webhook.
+func (s *RepositoryService) CreateHook(ctx context.Context, repo string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/hooks/subscriptions/create?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/_apis/hooks/subscriptions?api-version=6.0", s.client.owner)
+	in := new(subscription)
+	in.Status = "enabled"
+	in.PublisherID = "tfs"
+	in.ResourceVersion = "1.0"
+	in.ConsumerID = "webHooks"
+	in.ConsumerActionID = "httpRequest"
+	// we do not support scm hookevents, only native events
+	if input.NativeEvents == nil {
+		return nil, nil, fmt.Errorf("CreateHook, You must pass at least one native event")
+	}
+	if len(input.NativeEvents) > 1 {
+		return nil, nil, fmt.Errorf("CreateHook, Azure only allows the creation of a single hook at a time %v", input.NativeEvents)
+	}
+	in.EventType = input.NativeEvents[0]
+	// publisher
+	projectID, projErr := s.getProjectIDFromProjectName(ctx, s.client.project)
+	if projErr != nil {
+		return nil, nil, fmt.Errorf("CreateHook was unable to look up the project's projectID, %s", projErr)
+	}
+	in.PublisherInputs.ProjectID = projectID
+	in.PublisherInputs.Repository = repo
+	// consumer
+	in.ConsumerInputs.URL = input.Target
+	if input.SkipVerify {
+		in.ConsumerInputs.AcceptUntrustedCerts = "enabled"
+	}
+	// with version 1.0, azure provides incomplete data for issue-comment
+	if in.EventType == "ms.vss-code.git-pullrequest-comment-event" {
+		in.ResourceVersion = "2.0"
+	}
+	out := new(subscription)
+	res, err := s.client.do(ctx, "POST", endpoint, in, out)
+	return convertHook(out), res, err
+}
+
+// CreateStatus creates a new commit status.
+func (s *RepositoryService) CreateStatus(ctx context.Context, repo, ref string, input *scm.StatusInput) (*scm.Status, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// CreateDeployStatus creates a new deployment status.
+func (s *RepositoryService) CreateDeployStatus(ctx context.Context, repo string, input *scm.DeployStatus) (*scm.DeployStatus, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// UpdateHook updates a repository webhook.
+func (s *RepositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// DeleteHook deletes a repository webhook.
+func (s *RepositoryService) DeleteHook(ctx context.Context, repo, id string) (*scm.Response, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/hooks/subscriptions/delete?view=azure-devops-rest-6.0
+	if s.client.project == "" {
+		return nil, ProjectRequiredError()
+	}
+	endpoint := fmt.Sprintf("%s/_apis/hooks/subscriptions/%s?api-version=6.0", s.client.owner, id)
+	return s.client.do(ctx, "DELETE", endpoint, nil, nil)
+}
+
+// helper function to return the projectID from the project name
+func (s *RepositoryService) getProjectIDFromProjectName(ctx context.Context, projectName string) (string, error) {
+	// https://docs.microsoft.com/en-us/rest/api/azure/devops/core/projects/list?view=azure-devops-rest-6.0
+	projectName, err := url.PathUnescape(projectName)
+	if err != nil {
+		return "", fmt.Errorf("unable to unscape project: %s", projectName)
+	}
+
+	endpoint := fmt.Sprintf("%s/_apis/projects?api-version=6.0", s.client.owner)
+	type projects struct {
+		Count int64 `json:"count"`
+		Value []struct {
+			Description string `json:"description"`
+			ID          string `json:"id"`
+			Name        string `json:"name"`
+			State       string `json:"state"`
+			URL         string `json:"url"`
+		} `json:"value"`
+	}
+
+	out := new(projects)
+	response, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	if err != nil {
+		fmt.Println(response)
+		return "", fmt.Errorf("failed to list projects: %s", err)
+	}
+	for _, v := range out.Value {
+		if v.Name == projectName {
+			return v.ID, nil
+		}
+	}
+	return "", fmt.Errorf("failed to find project id for %s", projectName)
+}
+
+type repositories struct {
+	Count int64         `json:"count"`
+	Value []*repository `json:"value"`
+}
+
+type repository struct {
+	DefaultBranch string `json:"defaultBranch"`
+	ID            string `json:"id"`
+	Name          string `json:"name"`
+	Project       struct {
+		ID         string `json:"id"`
+		Name       string `json:"name"`
+		State      string `json:"state"`
+		URL        string `json:"url"`
+		Visibility string `json:"visibility"`
+	} `json:"project"`
+	RemoteURL string `json:"remoteUrl"`
+	URL       string `json:"url"`
+}
+
+type subscriptions struct {
+	Count int64           `json:"count"`
+	Value []*subscription `json:"value"`
+}
+
+type subscription struct {
+	ActionDescription string `json:"actionDescription"`
+	ConsumerActionID  string `json:"consumerActionId"`
+	ConsumerID        string `json:"consumerId"`
+	ConsumerInputs    struct {
+		AccountName          string `json:"accountName,omitempty"`
+		AcceptUntrustedCerts string `json:"acceptUntrustedCerts,omitempty"`
+		AddToTop             string `json:"addToTop,omitempty"`
+		APIToken             string `json:"apiToken,omitempty"`
+		BoardID              string `json:"boardId,omitempty"`
+		BuildName            string `json:"buildName,omitempty"`
+		BuildParameterized   string `json:"buildParameterized,omitempty"`
+		FeedID               string `json:"feedId,omitempty"`
+		ListID               string `json:"listId,omitempty"`
+		PackageSourceID      string `json:"packageSourceId,omitempty"`
+		Password             string `json:"password,omitempty"`
+		ServerBaseURL        string `json:"serverBaseUrl,omitempty"`
+		URL                  string `json:"url,omitempty"`
+		UserToken            string `json:"userToken,omitempty"`
+		Username             string `json:"username,omitempty"`
+	} `json:"consumerInputs"`
+	CreatedBy struct {
+		ID string `json:"id"`
+	} `json:"createdBy"`
+	CreatedDate      string `json:"createdDate"`
+	EventDescription string `json:"eventDescription"`
+	EventType        string `json:"eventType"`
+	ID               string `json:"id"`
+	ModifiedBy       struct {
+		ID string `json:"id"`
+	} `json:"modifiedBy"`
+	ModifiedDate     string `json:"modifiedDate"`
+	ProbationRetries int64  `json:"probationRetries"`
+	PublisherID      string `json:"publisherId"`
+	PublisherInputs  struct {
+		AreaPath          string `json:"areaPath,omitempty"`
+		Branch            string `json:"branch,omitempty"`
+		BuildStatus       string `json:"buildStatus,omitempty"`
+		ChangedFields     string `json:"changedFields,omitempty"`
+		CommentPattern    string `json:"commentPattern,omitempty"`
+		DefinitionName    string `json:"definitionName,omitempty"`
+		HostID            string `json:"hostId,omitempty"`
+		Path              string `json:"path,omitempty"`
+		ProjectID         string `json:"projectId,omitempty"`
+		Repository        string `json:"repository,omitempty"`
+		TfsSubscriptionID string `json:"tfsSubscriptionId,omitempty"`
+		WorkItemType      string `json:"workItemType,omitempty"`
+	} `json:"publisherInputs"`
+	ResourceVersion string `json:"resourceVersion"`
+	Status          string `json:"status"`
+	URL             string `json:"url"`
+}
+
+// helper function to convert from the azure devops repository list to
+// the common repository structure.
+func convertRepositoryList(from *repositories, owner string) []*scm.Repository {
+	to := []*scm.Repository{}
+	for _, v := range from.Value {
+		to = append(to, convertRepository(v, owner))
+	}
+	return to
+}
+
+// helper function to convert from the azure devops repository structure
+// to the common repository structure.
+func convertRepository(from *repository, owner string) *scm.Repository {
+	namespace := []string{owner, from.Project.Name}
+	return &scm.Repository{
+		ID:         from.ID,
+		Name:       from.Name,
+		Namespace:  strings.Join(namespace, "/"),
+		Link:       from.URL,
+		Branch:     scm.TrimRef(from.DefaultBranch),
+		Clone:      from.RemoteURL,
+		Private:    scm.ConvertPrivate(from.Project.Visibility),
+		Visibility: scm.ConvertVisibility(from.Project.Visibility),
+	}
+}
+
+func convertHookList(from []*subscription, projectFilter string, repositoryFilter string) []*scm.Hook {
+	to := []*scm.Hook{}
+	for _, v := range from {
+		if repositoryFilter != "" && projectFilter == v.PublisherInputs.ProjectID && repositoryFilter == v.PublisherInputs.Repository {
+			to = append(to, convertHook(v))
+		}
+	}
+	return to
+}
+
+func convertHook(from *subscription) *scm.Hook {
+	returnVal := &scm.Hook{
+		ID: from.ID,
+
+		Active:     from.Status == "enabled",
+		Target:     from.ConsumerInputs.URL,
+		Events:     []string{from.EventType},
+		SkipVerify: from.ConsumerInputs.AcceptUntrustedCerts == "true",
+	}
+
+	return returnVal
+}
diff --git a/scm/driver/azure/repo_test.go b/scm/driver/azure/repo_test.go
new file mode 100644
index 000000000..f2bd509db
--- /dev/null
+++ b/scm/driver/azure/repo_test.go
@@ -0,0 +1,172 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestRepositoryList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories").
+		Reply(200).
+		Type("application/json").
+		File("testdata/repos.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Repositories.List(context.Background(), scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
+	jsonErr := json.Unmarshal(raw, &want)
+	if jsonErr != nil {
+		t.Error(jsonErr)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryHookCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/_apis/projects").
+		Reply(201).
+		Type("application/json").
+		File("testdata/projects.json")
+
+	gock.New("https:/dev.azure.com/").
+		Post("/ORG/_apis/hooks/subscriptions").
+		Reply(201).
+		Type("application/json").
+		File("testdata/hook.json")
+
+	in := &scm.HookInput{
+		Name:         "web",
+		NativeEvents: []string{"git.push"},
+		Target:       "http://www.example.com/webhook",
+	}
+
+	client := NewDefault("ORG", "test_project")
+	got, _, err := client.Repositories.CreateHook(context.Background(), "test_project", in)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Hook)
+	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestHooksList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/_apis/projects").
+		Reply(201).
+		Type("application/json").
+		File("testdata/projects.json")
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/_apis/hooks/subscriptions").
+		Reply(200).
+		Type("application/json").
+		File("testdata/hooks.json")
+
+	client := NewDefault("ORG", "test_project")
+	repoID := "fde2d21f-13b9-4864-a995-83329045289a"
+
+	got, _, err := client.Repositories.ListHooks(context.Background(), repoID, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Hook{}
+	raw, _ := ioutil.ReadFile("testdata/hooks.json.golden")
+	jsonErr := json.Unmarshal(raw, &want)
+	if jsonErr != nil {
+		t.Error(jsonErr)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryHookDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Delete("/ORG/_apis/hooks/subscriptions").
+		Reply(204).
+		Type("application/json")
+
+	client := NewDefault("ORG", "PROJ")
+	res, err := client.Repositories.DeleteHook(context.Background(), "", "test-project")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if got, want := res.Status, 204; got != want {
+		t.Errorf("Want response status %d, got %d", want, got)
+	}
+
+}
+
+func TestRepositoryFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https:/dev.azure.com/").
+		Get("/ORG/PROJ/_apis/git/repositories/test_project").
+		Reply(200).
+		Type("application/json").
+		File("testdata/repo.json")
+
+	client := NewDefault("ORG", "PROJ")
+	got, _, err := client.Repositories.Find(context.Background(), "test_project")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Repository)
+	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
+	jsonErr := json.Unmarshal(raw, &want)
+	if jsonErr != nil {
+		t.Error(jsonErr)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+}
diff --git a/scm/driver/azure/review.go b/scm/driver/azure/review.go
new file mode 100644
index 000000000..f93a1f2b8
--- /dev/null
+++ b/scm/driver/azure/review.go
@@ -0,0 +1,31 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type reviewService struct {
+	client *wrapper
+}
+
+func (s *reviewService) Find(ctx context.Context, repo string, number, id int) (*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) List(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) Create(ctx context.Context, repo string, number int, input *scm.ReviewInput) (*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) Delete(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/azure/testdata/branch_create.json b/scm/driver/azure/testdata/branch_create.json
new file mode 100644
index 000000000..6a6700ae4
--- /dev/null
+++ b/scm/driver/azure/testdata/branch_create.json
@@ -0,0 +1,14 @@
+{
+    "value": [
+      {
+        "repositoryId": "d3d1760b-311c-4175-a726-20dfc6a7f885",
+        "name": "refs/heads/vsts-api-sample/answer-woman-flame",
+        "oldObjectId": "0000000000000000000000000000000000000000",
+        "newObjectId": "ffe9cba521f00d7f60e322845072238635edb451",
+        "isLocked": false,
+        "updateStatus": "succeeded",
+        "success": true
+      }
+    ],
+    "count": 1
+  }
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/branches.json b/scm/driver/azure/testdata/branches.json
new file mode 100644
index 000000000..8f804ee43
--- /dev/null
+++ b/scm/driver/azure/testdata/branches.json
@@ -0,0 +1,77 @@
+{
+    "value": [
+        {
+            "name": "refs/heads/main",
+            "objectId": "e0aee6aa543294d62520fb906689da6710af149c",
+            "creator": {
+                "displayName": "tp",
+                "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "_links": {
+                    "avatar": {
+                        "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+                    }
+                },
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs?filter=heads%2Fmain"
+        },
+        {
+            "name": "refs/heads/pr_branch",
+            "objectId": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356",
+            "creator": {
+                "displayName": "tp",
+                "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "_links": {
+                    "avatar": {
+                        "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+                    }
+                },
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs?filter=heads%2Fpr_branch"
+        },
+        {
+            "name": "refs/heads/test_branch",
+            "objectId": "d2036abdecd80290e971e263fe668ca87608f8d1",
+            "creator": {
+                "displayName": "tp",
+                "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "_links": {
+                    "avatar": {
+                        "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+                    }
+                },
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs?filter=heads%2Ftest_branch"
+        },
+        {
+            "name": "refs/pull/1/merge",
+            "objectId": "e29c0a25614589e1310574b42f799101903b7e30",
+            "creator": {
+                "displayName": "Microsoft.VisualStudio.Services.TFS",
+                "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/00000002-0000-8888-8000-000000000000",
+                "_links": {
+                    "avatar": {
+                        "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/s2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA"
+                    }
+                },
+                "id": "00000002-0000-8888-8000-000000000000",
+                "uniqueName": "00000002-0000-8888-8000-000000000000@2c895908-04e0-4952-89fd-54b0046d6288",
+                "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=00000002-0000-8888-8000-000000000000",
+                "descriptor": "s2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA"
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs?filter=pull%2F1%2Fmerge"
+        }
+    ],
+    "count": 4
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/branches.json.golden b/scm/driver/azure/testdata/branches.json.golden
new file mode 100644
index 000000000..6bfe635cb
--- /dev/null
+++ b/scm/driver/azure/testdata/branches.json.golden
@@ -0,0 +1,22 @@
+[
+  {
+    "Name": "main",
+    "Path": "refs/heads/main",
+    "Sha": "e0aee6aa543294d62520fb906689da6710af149c"
+  },
+  {
+    "Name": "pr_branch",
+    "Path": "refs/heads/pr_branch",
+    "Sha": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356"
+  },
+  {
+    "Name": "test_branch",
+    "Path": "refs/heads/test_branch",
+    "Sha": "d2036abdecd80290e971e263fe668ca87608f8d1"
+  },
+  {
+    "Name": "refs/pull/1/merge",
+    "Path": "refs/pull/1/merge",
+    "Sha": "e29c0a25614589e1310574b42f799101903b7e30"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/branches_filter.json b/scm/driver/azure/testdata/branches_filter.json
new file mode 100644
index 000000000..b9043edb5
--- /dev/null
+++ b/scm/driver/azure/testdata/branches_filter.json
@@ -0,0 +1,41 @@
+{
+  "value": [
+    {
+      "name": "refs/heads/main",
+      "objectId": "e0aee6aa543294d62520fb906689da6710af149c",
+      "creator": {
+        "displayName": "tp",
+        "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "_links": {
+          "avatar": {
+            "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+          }
+        },
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+      },
+      "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs?filter=heads%2Fmain"
+    },
+    {
+      "name": "refs/heads/main-patch",
+      "objectId": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356",
+      "creator": {
+        "displayName": "tp",
+        "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "_links": {
+          "avatar": {
+            "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+          }
+        },
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+      },
+      "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs?filter=heads%2Fpr_branch"
+    }
+  ],
+  "count": 2
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/branches_filter.json.golden b/scm/driver/azure/testdata/branches_filter.json.golden
new file mode 100644
index 000000000..df3589ce6
--- /dev/null
+++ b/scm/driver/azure/testdata/branches_filter.json.golden
@@ -0,0 +1,12 @@
+[
+  {
+    "Name": "main",
+    "Path": "refs/heads/main",
+    "Sha": "e0aee6aa543294d62520fb906689da6710af149c"
+  },
+  {
+    "Name": "main-patch",
+    "Path": "refs/heads/main-patch",
+    "Sha": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/commit.json b/scm/driver/azure/testdata/commit.json
new file mode 100644
index 000000000..ea37dc05b
--- /dev/null
+++ b/scm/driver/azure/testdata/commit.json
@@ -0,0 +1,53 @@
+{
+    "treeId": "efbaf98cd9984e7480f600f8c4b592432a428518",
+    "commitId": "14897f4465d2d63508242b5cbf68aa2865f693e7",
+    "author": {
+        "name": "tp",
+        "email": "tp@harness.io",
+        "date": "2022-03-15T17:08:22Z",
+        "imageUrl": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "committer": {
+        "name": "tp",
+        "email": "tp@harness.io",
+        "date": "2022-03-15T17:08:22Z",
+        "imageUrl": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "comment": "update CRUD",
+    "parents": [
+        "0e969a16c531c2a6961e5dcf9f82f4456c7bbe68"
+    ],
+    "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/14897f4465d2d63508242b5cbf68aa2865f693e7",
+    "remoteUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2/commit/14897f4465d2d63508242b5cbf68aa2865f693e7",
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/14897f4465d2d63508242b5cbf68aa2865f693e7"
+        },
+        "repository": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a"
+        },
+        "web": {
+            "href": "https://dev.azure.com/tphoney/test_project/_git/test_repo2/commit/14897f4465d2d63508242b5cbf68aa2865f693e7"
+        },
+        "changes": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/14897f4465d2d63508242b5cbf68aa2865f693e7/changes"
+        }
+    },
+    "push": {
+        "pushedBy": {
+            "displayName": "tp",
+            "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+            "_links": {
+                "avatar": {
+                    "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+                }
+            },
+            "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+            "uniqueName": "tp@harness.io",
+            "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+            "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+        },
+        "pushId": 296,
+        "date": "2022-03-15T17:08:22.7905002Z"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/commit.json.golden b/scm/driver/azure/testdata/commit.json.golden
new file mode 100644
index 000000000..1659822a9
--- /dev/null
+++ b/scm/driver/azure/testdata/commit.json.golden
@@ -0,0 +1,19 @@
+{
+  "Sha": "14897f4465d2d63508242b5cbf68aa2865f693e7",
+  "Message": "update CRUD",
+  "Author": {
+    "Name": "tp",
+    "Email": "tp@harness.io",
+    "Date": "2022-03-15T17:08:22Z",
+    "Login": "tp",
+    "Avatar": ""
+  },
+  "Committer": {
+    "Name": "tp",
+    "Email": "tp@harness.io",
+    "Date": "2022-03-15T17:08:22Z",
+    "Login": "tp",
+    "Avatar": ""
+  },
+  "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/14897f4465d2d63508242b5cbf68aa2865f693e7"
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/commits.json b/scm/driver/azure/testdata/commits.json
new file mode 100644
index 000000000..4f4748d75
--- /dev/null
+++ b/scm/driver/azure/testdata/commits.json
@@ -0,0 +1,71 @@
+{
+    "value": [
+        {
+            "commitId": "e0aee6aa543294d62520fb906689da6710af149c",
+            "author": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-04T12:19:58Z"
+            },
+            "committer": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-04T12:19:58Z"
+            },
+            "comment": "go-scm delete crud file",
+            "commentTruncated": false,
+            "changeCounts": {
+                "Add": 0,
+                "Edit": 0,
+                "Delete": 1
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/e0aee6aa543294d62520fb906689da6710af149c",
+            "remoteUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2/commit/e0aee6aa543294d62520fb906689da6710af149c"
+        },
+        {
+            "commitId": "1fe456794debece7c4125b9e283b601c974977a9",
+            "author": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-04T12:19:57Z"
+            },
+            "committer": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-04T12:19:57Z"
+            },
+            "comment": "go-scm update crud file",
+            "commentTruncated": false,
+            "changeCounts": {
+                "Add": 0,
+                "Edit": 1,
+                "Delete": 0
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/1fe456794debece7c4125b9e283b601c974977a9",
+            "remoteUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2/commit/1fe456794debece7c4125b9e283b601c974977a9"
+        },
+        {
+            "commitId": "dc49e8e6e22bb3456366a09365ce9e72912f26b5",
+            "author": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-04T12:19:56Z"
+            },
+            "committer": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-04T12:19:56Z"
+            },
+            "comment": "go-scm create crud file",
+            "commentTruncated": false,
+            "changeCounts": {
+                "Add": 1,
+                "Edit": 0,
+                "Delete": 0
+            },
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/dc49e8e6e22bb3456366a09365ce9e72912f26b5",
+            "remoteUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2/commit/dc49e8e6e22bb3456366a09365ce9e72912f26b5"
+        }
+    ],
+    "count": 3
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/commits.json.golden b/scm/driver/azure/testdata/commits.json.golden
new file mode 100644
index 000000000..a42699cf2
--- /dev/null
+++ b/scm/driver/azure/testdata/commits.json.golden
@@ -0,0 +1,59 @@
+[
+  {
+    "Sha": "e0aee6aa543294d62520fb906689da6710af149c",
+    "Message": "go-scm delete crud file",
+    "Author": {
+      "Name": "tp",
+      "Email": "tp@harness.io",
+      "Date": "2022-03-04T12:19:58Z",
+      "Login": "tp",
+      "Avatar": ""
+    },
+    "Committer": {
+      "Name": "tp",
+      "Email": "tp@harness.io",
+      "Date": "2022-03-04T12:19:58Z",
+      "Login": "tp",
+      "Avatar": ""
+    },
+    "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/e0aee6aa543294d62520fb906689da6710af149c"
+  },
+  {
+    "Sha": "1fe456794debece7c4125b9e283b601c974977a9",
+    "Message": "go-scm update crud file",
+    "Author": {
+      "Name": "tp",
+      "Email": "tp@harness.io",
+      "Date": "2022-03-04T12:19:57Z",
+      "Login": "tp",
+      "Avatar": ""
+    },
+    "Committer": {
+      "Name": "tp",
+      "Email": "tp@harness.io",
+      "Date": "2022-03-04T12:19:57Z",
+      "Login": "tp",
+      "Avatar": ""
+    },
+    "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/1fe456794debece7c4125b9e283b601c974977a9"
+  },
+  {
+    "Sha": "dc49e8e6e22bb3456366a09365ce9e72912f26b5",
+    "Message": "go-scm create crud file",
+    "Author": {
+      "Name": "tp",
+      "Email": "tp@harness.io",
+      "Date": "2022-03-04T12:19:56Z",
+      "Login": "tp",
+      "Avatar": ""
+    },
+    "Committer": {
+      "Name": "tp",
+      "Email": "tp@harness.io",
+      "Date": "2022-03-04T12:19:56Z",
+      "Login": "tp",
+      "Avatar": ""
+    },
+    "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/dc49e8e6e22bb3456366a09365ce9e72912f26b5"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/compare.json b/scm/driver/azure/testdata/compare.json
new file mode 100644
index 000000000..7e23610f0
--- /dev/null
+++ b/scm/driver/azure/testdata/compare.json
@@ -0,0 +1,24 @@
+{
+    "allChangesIncluded": true,
+    "changeCounts": {
+        "Edit": 1
+    },
+    "changes": [
+        {
+            "item": {
+                "objectId": "a493dad221fb22ff4c35ff89f112a61f8c39e924",
+                "originalObjectId": "c42585a91aea68fcdd2f06508f7073983689aa5f",
+                "gitObjectType": "blob",
+                "commitId": "66df312dad61e84dd896d1e8d14ee3dce53b62f0",
+                "path": "/testfile",
+                "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/items/testfile?versionType=Commit&version=66df312dad61e84dd896d1e8d14ee3dce53b62f0"
+            },
+            "changeType": "edit"
+        }
+    ],
+    "commonCommit": "9788e5ddf8b387cb79228628f34d8dc18582d606",
+    "baseCommit": "9788e5ddf8b387cb79228628f34d8dc18582d606",
+    "targetCommit": "66df312dad61e84dd896d1e8d14ee3dce53b62f0",
+    "aheadCount": 10,
+    "behindCount": 0
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/compare.json.golden b/scm/driver/azure/testdata/compare.json.golden
new file mode 100644
index 000000000..adbdcb42a
--- /dev/null
+++ b/scm/driver/azure/testdata/compare.json.golden
@@ -0,0 +1,9 @@
+[
+    {
+        "Path": "/testfile",
+        "Added": false,
+        "Renamed": false,
+        "Deleted": false,
+        "BlobID": ""
+    }
+]
diff --git a/scm/driver/azure/testdata/content.json b/scm/driver/azure/testdata/content.json
new file mode 100644
index 000000000..34dc1a328
--- /dev/null
+++ b/scm/driver/azure/testdata/content.json
@@ -0,0 +1,19 @@
+{
+    "objectId": "0ca446aab9d09eac8625b53e3df8da661976c458",
+    "gitObjectType": "blob",
+    "commitId": "2c0c712b26c3328ed66d5771213360812be9d035",
+    "path": "/README.md",
+    "content": "Hello World!\n",
+    "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/items?path=%2FREADME.md&versionType=Branch&versionOptions=None",
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/items?path=%2FREADME.md&versionType=Branch&versionOptions=None"
+        },
+        "repository": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a"
+        },
+        "blob": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/blobs/0ca446aab9d09eac8625b53e3df8da661976c458"
+        }
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/content.json.golden b/scm/driver/azure/testdata/content.json.golden
new file mode 100644
index 000000000..d5e39f579
--- /dev/null
+++ b/scm/driver/azure/testdata/content.json.golden
@@ -0,0 +1,6 @@
+{
+    "Path": "/README.md",
+    "Data": "SGVsbG8gV29ybGQhCg==",
+    "Sha": "2c0c712b26c3328ed66d5771213360812be9d035",
+    "BlobID": "0ca446aab9d09eac8625b53e3df8da661976c458"
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/content_create.json b/scm/driver/azure/testdata/content_create.json
new file mode 100644
index 000000000..e1bf88fc5
--- /dev/null
+++ b/scm/driver/azure/testdata/content_create.json
@@ -0,0 +1,83 @@
+{
+    "commits": [
+        {
+            "treeId": "175455a66a5fee6f99aeed0c75b4716835d22c64",
+            "commitId": "aa97ef963bff4dd90dde7456d503dd6ba8a28703",
+            "author": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-01T14:40:55Z"
+            },
+            "committer": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-01T14:40:55Z"
+            },
+            "comment": "test message create tickles",
+            "parents": [
+                "2c0c712b26c3328ed66d5771213360812be9d035"
+            ],
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/aa97ef963bff4dd90dde7456d503dd6ba8a28703"
+        }
+    ],
+    "refUpdates": [
+        {
+            "repositoryId": "fde2d21f-13b9-4864-a995-83329045289a",
+            "name": "refs/heads/main",
+            "oldObjectId": "2c0c712b26c3328ed66d5771213360812be9d035",
+            "newObjectId": "aa97ef963bff4dd90dde7456d503dd6ba8a28703"
+        }
+    ],
+    "repository": {
+        "id": "fde2d21f-13b9-4864-a995-83329045289a",
+        "name": "test_repo2",
+        "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a",
+        "project": {
+            "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "name": "test_project",
+            "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "state": "wellFormed",
+            "revision": 11,
+            "visibility": "private",
+            "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+        },
+        "size": 713,
+        "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "sshUrl": "git@ssh.dev.azure.com:v3/tphoney/test_project/test_repo2",
+        "webUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "isDisabled": false
+    },
+    "pushedBy": {
+        "displayName": "tp",
+        "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "_links": {
+            "avatar": {
+                "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            }
+        },
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "pushId": 11,
+    "date": "2022-03-01T14:40:55.3379799Z",
+    "url": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/11",
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/11"
+        },
+        "repository": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a"
+        },
+        "commits": {
+            "href": "https://dev.azure.com/tphoney/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/11/commits"
+        },
+        "pusher": {
+            "href": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109"
+        },
+        "refs": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs/heads/main"
+        }
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/content_delete.json b/scm/driver/azure/testdata/content_delete.json
new file mode 100644
index 000000000..68eff44ed
--- /dev/null
+++ b/scm/driver/azure/testdata/content_delete.json
@@ -0,0 +1,83 @@
+{
+    "commits": [
+        {
+            "treeId": "9804b758e84cac41a6acc4d011f57310a1f63102",
+            "commitId": "e25d5d5f8dba6a25d5d66c020b101278d818a8b8",
+            "author": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-01T14:54:53Z"
+            },
+            "committer": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-01T14:54:53Z"
+            },
+            "comment": "test delete message",
+            "parents": [
+                "86260582b1ace66941ea2d1230ac083b68eb95cc"
+            ],
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/e25d5d5f8dba6a25d5d66c020b101278d818a8b8"
+        }
+    ],
+    "refUpdates": [
+        {
+            "repositoryId": "fde2d21f-13b9-4864-a995-83329045289a",
+            "name": "refs/heads/main",
+            "oldObjectId": "86260582b1ace66941ea2d1230ac083b68eb95cc",
+            "newObjectId": "e25d5d5f8dba6a25d5d66c020b101278d818a8b8"
+        }
+    ],
+    "repository": {
+        "id": "fde2d21f-13b9-4864-a995-83329045289a",
+        "name": "test_repo2",
+        "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a",
+        "project": {
+            "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "name": "test_project",
+            "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "state": "wellFormed",
+            "revision": 11,
+            "visibility": "private",
+            "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+        },
+        "size": 713,
+        "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "sshUrl": "git@ssh.dev.azure.com:v3/tphoney/test_project/test_repo2",
+        "webUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "isDisabled": false
+    },
+    "pushedBy": {
+        "displayName": "tp",
+        "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "_links": {
+            "avatar": {
+                "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            }
+        },
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "pushId": 13,
+    "date": "2022-03-01T14:54:54.0241357Z",
+    "url": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/13",
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/13"
+        },
+        "repository": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a"
+        },
+        "commits": {
+            "href": "https://dev.azure.com/tphoney/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/13/commits"
+        },
+        "pusher": {
+            "href": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109"
+        },
+        "refs": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs/heads/main"
+        }
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/content_list.json b/scm/driver/azure/testdata/content_list.json
new file mode 100644
index 000000000..00e9899bf
--- /dev/null
+++ b/scm/driver/azure/testdata/content_list.json
@@ -0,0 +1,20 @@
+{
+    "count": 2,
+    "value": [
+        {
+            "objectId": "9804b758e84cac41a6acc4d011f57310a1f63102",
+            "gitObjectType": "tree",
+            "commitId": "e25d5d5f8dba6a25d5d66c020b101278d818a8b8",
+            "path": "/",
+            "isFolder": true,
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/items?path=%2F&versionType=Branch&versionOptions=None"
+        },
+        {
+            "objectId": "0ca446aab9d09eac8625b53e3df8da661976c458",
+            "gitObjectType": "blob",
+            "commitId": "e25d5d5f8dba6a25d5d66c020b101278d818a8b8",
+            "path": "/README.md",
+            "url": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/items//README.md?versionType=Branch&versionOptions=None"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/content_list.json.golden b/scm/driver/azure/testdata/content_list.json.golden
new file mode 100644
index 000000000..b5644b732
--- /dev/null
+++ b/scm/driver/azure/testdata/content_list.json.golden
@@ -0,0 +1,14 @@
+[
+  {
+    "path": "/",
+    "kind": "directory",
+    "Sha": "e25d5d5f8dba6a25d5d66c020b101278d818a8b8",
+    "BlobID": "9804b758e84cac41a6acc4d011f57310a1f63102"
+  },
+  {
+    "path": "/README.md",
+    "kind": "file",
+    "Sha": "e25d5d5f8dba6a25d5d66c020b101278d818a8b8",
+    "BlobID": "0ca446aab9d09eac8625b53e3df8da661976c458"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/content_update.json b/scm/driver/azure/testdata/content_update.json
new file mode 100644
index 000000000..c9ebc450e
--- /dev/null
+++ b/scm/driver/azure/testdata/content_update.json
@@ -0,0 +1,83 @@
+{
+    "commits": [
+        {
+            "treeId": "8c48e1f820617b4d33efb453feef450f4252a8b0",
+            "commitId": "86260582b1ace66941ea2d1230ac083b68eb95cc",
+            "author": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-01T14:50:23Z"
+            },
+            "committer": {
+                "name": "tp",
+                "email": "tp@harness.io",
+                "date": "2022-03-01T14:50:23Z"
+            },
+            "comment": "test message update tickles",
+            "parents": [
+                "aa97ef963bff4dd90dde7456d503dd6ba8a28703"
+            ],
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/86260582b1ace66941ea2d1230ac083b68eb95cc"
+        }
+    ],
+    "refUpdates": [
+        {
+            "repositoryId": "fde2d21f-13b9-4864-a995-83329045289a",
+            "name": "refs/heads/main",
+            "oldObjectId": "aa97ef963bff4dd90dde7456d503dd6ba8a28703",
+            "newObjectId": "86260582b1ace66941ea2d1230ac083b68eb95cc"
+        }
+    ],
+    "repository": {
+        "id": "fde2d21f-13b9-4864-a995-83329045289a",
+        "name": "test_repo2",
+        "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a",
+        "project": {
+            "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "name": "test_project",
+            "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "state": "wellFormed",
+            "revision": 11,
+            "visibility": "private",
+            "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+        },
+        "size": 713,
+        "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "sshUrl": "git@ssh.dev.azure.com:v3/tphoney/test_project/test_repo2",
+        "webUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "isDisabled": false
+    },
+    "pushedBy": {
+        "displayName": "tp",
+        "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "_links": {
+            "avatar": {
+                "href": "https://dev.azure.com/tphoney/_apis/GraphProfile/MemberAvatars/msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            }
+        },
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "pushId": 12,
+    "date": "2022-03-01T14:50:23.2995Z",
+    "url": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/12",
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/test_project/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/12"
+        },
+        "repository": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a"
+        },
+        "commits": {
+            "href": "https://dev.azure.com/tphoney/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pushes/12/commits"
+        },
+        "pusher": {
+            "href": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109"
+        },
+        "refs": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs/heads/main"
+        }
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/hook.json b/scm/driver/azure/testdata/hook.json
new file mode 100644
index 000000000..ff8a2daea
--- /dev/null
+++ b/scm/driver/azure/testdata/hook.json
@@ -0,0 +1,58 @@
+{
+    "id": "d455cb11-20a0-4b15-b546-7e9fb9973cc6",
+    "url": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc6",
+    "status": "enabled",
+    "publisherId": "tfs",
+    "eventType": "git.pullrequest.created",
+    "subscriber": null,
+    "resourceVersion": "1.0",
+    "eventDescription": "Repository test_repo2",
+    "consumerId": "webHooks",
+    "consumerActionId": "httpRequest",
+    "actionDescription": "To host www.bla.com",
+    "probationRetries": 1,
+    "createdBy": {
+        "displayName": "tp",
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "createdDate": "2022-03-25T13:28:12.39Z",
+    "modifiedBy": {
+        "displayName": "tp",
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "uniqueName": "tp@harness.io",
+        "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+    },
+    "modifiedDate": "2022-03-29T10:39:13.813Z",
+    "lastProbationRetryDate": "2022-03-28T10:44:51.093Z",
+    "publisherInputs": {
+        "branch": "",
+        "projectId": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+        "pullrequestCreatedBy": "",
+        "pullrequestReviewersContains": "",
+        "repository": "fde2d21f-13b9-4864-a995-83329045289a",
+        "tfsSubscriptionId": "4ce8d6c4-f655-418d-8eb6-9462dd01ff39"
+    },
+    "consumerInputs": {
+        "acceptUntrustedCerts": "true",
+        "url": "http://www.bla.com"
+    },
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc6"
+        },
+        "consumer": {
+            "href": "https://dev.azure.com/tphoney/_apis/hooks/consumers/webHooks"
+        },
+        "actions": {
+            "href": "https://dev.azure.com/tphoney/_apis/hooks/consumers/webHooks/actions"
+        },
+        "notifications": {
+            "href": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc6/notifications"
+        },
+        "publisher": {
+            "href": "https://dev.azure.com/tphoney/_apis/hooks/publishers/tfs"
+        }
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/hook.json.golden b/scm/driver/azure/testdata/hook.json.golden
new file mode 100644
index 000000000..927ae8496
--- /dev/null
+++ b/scm/driver/azure/testdata/hook.json.golden
@@ -0,0 +1,10 @@
+{
+    "ID": "d455cb11-20a0-4b15-b546-7e9fb9973cc6",
+    "Name": "",
+    "Target": "http://www.bla.com",
+    "Events": [
+        "git.pullrequest.created"  
+    ],
+    "Active": true,
+    "SkipVerify": true
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/hooks.json b/scm/driver/azure/testdata/hooks.json
new file mode 100644
index 000000000..76efd534c
--- /dev/null
+++ b/scm/driver/azure/testdata/hooks.json
@@ -0,0 +1,121 @@
+{
+    "count": 2,
+    "value": [
+        {
+            "id": "d455cb11-20a0-4b15-b546-7e9fb9973cc6",
+            "url": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc6",
+            "status": "enabled",
+            "publisherId": "tfs",
+            "eventType": "git.pullrequest.created",
+            "subscriber": null,
+            "resourceVersion": "1.0",
+            "eventDescription": "Repository test_repo2",
+            "consumerId": "webHooks",
+            "consumerActionId": "httpRequest",
+            "actionDescription": "To host www.bla.com",
+            "probationRetries": 1,
+            "createdBy": {
+                "displayName": "tp",
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "createdDate": "2022-03-25T13:28:12.39Z",
+            "modifiedBy": {
+                "displayName": "tp",
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "modifiedDate": "2022-03-29T10:39:13.813Z",
+            "lastProbationRetryDate": "2022-03-28T10:44:51.093Z",
+            "publisherInputs": {
+                "branch": "",
+                "projectId": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+                "pullrequestCreatedBy": "",
+                "pullrequestReviewersContains": "",
+                "repository": "fde2d21f-13b9-4864-a995-83329045289a",
+                "tfsSubscriptionId": "4ce8d6c4-f655-418d-8eb6-9462dd01ff39"
+            },
+            "consumerInputs": {
+                "acceptUntrustedCerts": "true",
+                "url": "http://www.bla.com"
+            },
+            "_links": {
+                "self": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc6"
+                },
+                "consumer": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/consumers/webHooks"
+                },
+                "actions": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/consumers/webHooks/actions"
+                },
+                "notifications": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc6/notifications"
+                },
+                "publisher": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/publishers/tfs"
+                }
+            }
+        },
+        {
+            "id": "d455cb11-20a0-4b15-b546-7e9fb9973cc7",
+            "url": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc7",
+            "status": "enabled",
+            "publisherId": "tfs",
+            "eventType": "git.pullrequest.merged",
+            "subscriber": null,
+            "resourceVersion": "1.0",
+            "eventDescription": "Repository test_repo2",
+            "consumerId": "webHooks",
+            "consumerActionId": "httpRequest",
+            "actionDescription": "To host www.bla.com",
+            "probationRetries": 1,
+            "createdBy": {
+                "displayName": "tp",
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "createdDate": "2022-03-25T13:28:12.39Z",
+            "modifiedBy": {
+                "displayName": "tp",
+                "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+                "uniqueName": "tp@harness.io",
+                "descriptor": "msa.M2ZmNGEyMGYtMzA2ZS03NzdlLThhMDEtNTdmMzVlNzFmMTA5"
+            },
+            "modifiedDate": "2022-03-29T10:39:13.813Z",
+            "lastProbationRetryDate": "2022-03-28T10:44:51.093Z",
+            "publisherInputs": {
+                "branch": "",
+                "projectId": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+                "pullrequestCreatedBy": "",
+                "pullrequestReviewersContains": "",
+                "repository": "fde2d21f-13b9-4864-a995-83329045289a",
+                "tfsSubscriptionId": "4ce8d6c4-f655-418d-8eb6-9462dd01ff39"
+            },
+            "consumerInputs": {
+                "acceptUntrustedCerts": "true",
+                "url": "http://www.bla.com"
+            },
+            "_links": {
+                "self": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc7"
+                },
+                "consumer": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/consumers/webHooks"
+                },
+                "actions": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/consumers/webHooks/actions"
+                },
+                "notifications": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/subscriptions/d455cb11-20a0-4b15-b546-7e9fb9973cc7/notifications"
+                },
+                "publisher": {
+                    "href": "https://dev.azure.com/tphoney/_apis/hooks/publishers/tfs"
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/hooks.json.golden b/scm/driver/azure/testdata/hooks.json.golden
new file mode 100644
index 000000000..78d873eb2
--- /dev/null
+++ b/scm/driver/azure/testdata/hooks.json.golden
@@ -0,0 +1,22 @@
+[
+    {
+        "ID": "d455cb11-20a0-4b15-b546-7e9fb9973cc6",
+        "Name": "",
+        "Target": "http://www.bla.com",
+        "Events": [
+            "git.pullrequest.created"  
+        ],
+        "Active": true,
+        "SkipVerify": true
+    },
+    {
+        "ID": "d455cb11-20a0-4b15-b546-7e9fb9973cc7",
+        "Name": "",
+        "Target": "http://www.bla.com",
+        "Events": [
+            "git.pullrequest.merged"  
+        ],
+        "Active": true,
+        "SkipVerify": true
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/pr.json b/scm/driver/azure/testdata/pr.json
new file mode 100644
index 000000000..01c08b31e
--- /dev/null
+++ b/scm/driver/azure/testdata/pr.json
@@ -0,0 +1,76 @@
+{
+    "repository": {
+        "id": "fde2d21f-13b9-4864-a995-83329045289a",
+        "name": "test_repo2",
+        "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a",
+        "project": {
+            "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "name": "test_project",
+            "description": "",
+            "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "state": "wellFormed",
+            "revision": 11
+        },
+        "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_repo2"
+    },
+    "pullRequestId": 19,
+    "codeReviewId": 19,
+    "status": "completed",
+    "createdBy": {
+        "id": "3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "displayName": "tp",
+        "uniqueName": "tp@harness.io",
+        "url": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109",
+        "imageUrl": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109"
+    },
+    "creationDate": "2022-03-04T13:34:54.3177724Z",
+    "closedDate": "2022-06-03T06:33:42.2405472Z",
+    "title": "test_pr",
+    "description": "test_pr_body",
+    "sourceRefName": "refs/heads/pr_branch",
+    "targetRefName": "refs/heads/main",
+    "mergeStatus": "queued",
+    "isDraft": false,
+    "mergeId": "36c88bf7-3d14-437f-82aa-e38cce733261",
+    "lastMergeSourceCommit": {
+        "commitId": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356",
+        "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/01768d964c03e97260af0bd8cd9e5cd1f9ac6356"
+    },
+    "lastMergeTargetCommit": {
+        "commitId": "b748ab7eb49b8627214f22f631f878c4af9893b5",
+        "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/b748ab7eb49b8627214f22f631f878c4af9893b5"
+    },
+    "reviewers": [],
+    "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pullRequests/19",
+    "_links": {
+        "self": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pullRequests/19"
+        },
+        "repository": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a"
+        },
+        "workItems": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pullRequests/19/workitems"
+        },
+        "sourceBranch": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs/heads/pr_branch"
+        },
+        "targetBranch": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/refs/heads/main"
+        },
+        "sourceCommit": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/01768d964c03e97260af0bd8cd9e5cd1f9ac6356"
+        },
+        "targetCommit": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/commits/b748ab7eb49b8627214f22f631f878c4af9893b5"
+        },
+        "createdBy": {
+            "href": "https://spsproduks1.vssps.visualstudio.com/A93f74f38-2b8d-42d4-a5cb-74646f46666e/_apis/Identities/3ff4a20f-306e-677e-8a01-57f35e71f109"
+        },
+        "iterations": {
+            "href": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pullRequests/19/iterations"
+        }
+    },
+    "supportsIterations": true,
+    "artifactId": "vstfs:///Git/PullRequestId/d350c9c0-7749-4ff8-a78f-f9c1f0e56729%2ffde2d21f-13b9-4864-a995-83329045289a%2f19"
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/pr.json.golden b/scm/driver/azure/testdata/pr.json.golden
new file mode 100644
index 000000000..23330a15e
--- /dev/null
+++ b/scm/driver/azure/testdata/pr.json.golden
@@ -0,0 +1,37 @@
+{
+  "Number": 19,
+  "Title": "test_pr",
+  "Body": "test_pr_body",
+  "Sha": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356",
+  "Ref": "",
+  "Source": "pr_branch",
+  "Target": "main",
+  "Ref": "refs/pull/19/merge",
+  "Fork": "",
+  "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a/pullRequests/19",
+  "Diff": "",
+  "Draft": false,
+  "Closed": true,
+  "Merged": true,
+  "Base": {
+    "Name": "",
+    "Path": "",
+    "Sha": "b748ab7eb49b8627214f22f631f878c4af9893b5"
+  },
+  "Head": {
+    "Name": "",
+    "Path": "",
+    "Sha": "01768d964c03e97260af0bd8cd9e5cd1f9ac6356"
+  },
+  "Author": {
+    "Login": "tp@harness.io",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://dev.azure.com/tphoney/_api/_common/identityImage?id=3ff4a20f-306e-677e-8a01-57f35e71f109",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Created": "2022-03-04T13:34:54.3177724Z",
+  "Updated": "0001-01-01T00:00:00Z",
+  "Labels": null
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/projects.json b/scm/driver/azure/testdata/projects.json
new file mode 100644
index 000000000..933b642be
--- /dev/null
+++ b/scm/driver/azure/testdata/projects.json
@@ -0,0 +1,14 @@
+{
+    "count": 1,
+    "value": [
+        {
+            "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "name": "test_project",
+            "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+            "state": "wellFormed",
+            "revision": 11,
+            "visibility": "private",
+            "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/repo.json b/scm/driver/azure/testdata/repo.json
new file mode 100644
index 000000000..c21fec46a
--- /dev/null
+++ b/scm/driver/azure/testdata/repo.json
@@ -0,0 +1,20 @@
+{
+  "id": "91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+  "name": "test_project",
+  "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+  "project": {
+    "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+    "name": "test_project",
+    "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+    "state": "wellFormed",
+    "revision": 11,
+    "visibility": "private",
+    "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+  },
+  "defaultBranch": "refs/heads/main",
+  "size": 1232,
+  "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_project",
+  "sshUrl": "git@ssh.dev.azure.com:v3/tphoney/test_project/test_project",
+  "webUrl": "https://dev.azure.com/tphoney/test_project/_git/test_project",
+  "isDisabled": false
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/repo.json.golden b/scm/driver/azure/testdata/repo.json.golden
new file mode 100644
index 000000000..c65a2d23d
--- /dev/null
+++ b/scm/driver/azure/testdata/repo.json.golden
@@ -0,0 +1,10 @@
+{
+    "ID": "91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+    "Name": "test_project",
+    "Namespace": "ORG/test_project",
+    "Branch": "main",
+    "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+    "Clone": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_project",
+    "Private": true,
+    "Visibility": 3
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/repos.json b/scm/driver/azure/testdata/repos.json
new file mode 100644
index 000000000..e5d7c6a23
--- /dev/null
+++ b/scm/driver/azure/testdata/repos.json
@@ -0,0 +1,45 @@
+{
+    "value": [
+        {
+            "id": "91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+            "name": "test_project",
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+            "project": {
+                "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+                "name": "test_project",
+                "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+                "state": "wellFormed",
+                "revision": 11,
+                "visibility": "private",
+                "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+            },
+            "defaultBranch": "refs/heads/main",
+            "size": 1232,
+            "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_project",
+            "sshUrl": "git@ssh.dev.azure.com:v3/tphoney/test_project/test_project",
+            "webUrl": "https://dev.azure.com/tphoney/test_project/_git/test_project",
+            "isDisabled": false
+        },
+        {
+            "id": "fde2d21f-13b9-4864-a995-83329045289a",
+            "name": "test_repo2",
+            "url": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a",
+            "project": {
+                "id": "d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+                "name": "test_project",
+                "url": "https://dev.azure.com/tphoney/_apis/projects/d350c9c0-7749-4ff8-a78f-f9c1f0e56729",
+                "state": "wellFormed",
+                "revision": 11,
+                "visibility": "public",
+                "lastUpdateTime": "2022-02-24T15:31:27.89Z"
+            },
+            "defaultBranch": "refs/heads/main",
+            "size": 37687,
+            "remoteUrl": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_repo2",
+            "sshUrl": "git@ssh.dev.azure.com:v3/tphoney/test_project/test_repo2",
+            "webUrl": "https://dev.azure.com/tphoney/test_project/_git/test_repo2",
+            "isDisabled": false
+        }
+    ],
+    "count": 2
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/repos.json.golden b/scm/driver/azure/testdata/repos.json.golden
new file mode 100644
index 000000000..137b73660
--- /dev/null
+++ b/scm/driver/azure/testdata/repos.json.golden
@@ -0,0 +1,22 @@
+[
+    {
+        "ID": "91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+        "Name": "test_project",
+        "Namespace": "ORG/test_project",
+        "Branch": "main",
+        "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/91f0d4cb-4c36-49a5-b28d-2d72da089c4d",
+        "Clone": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_project",
+        "Private": true,
+        "Visibility": 3
+    },
+    {
+        "ID": "fde2d21f-13b9-4864-a995-83329045289a",
+        "Name": "test_repo2",
+        "Namespace": "ORG/test_project",
+        "Branch": "main",
+        "Link": "https://dev.azure.com/tphoney/d350c9c0-7749-4ff8-a78f-f9c1f0e56729/_apis/git/repositories/fde2d21f-13b9-4864-a995-83329045289a",
+        "Clone": "https://tphoney@dev.azure.com/tphoney/test_project/_git/test_repo2",
+        "Private": false,
+        "Visibility": 1
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/tags.json b/scm/driver/azure/testdata/tags.json
new file mode 100644
index 000000000..090690921
--- /dev/null
+++ b/scm/driver/azure/testdata/tags.json
@@ -0,0 +1,41 @@
+{
+  "value": [
+    {
+      "name": "refs/tags/v1.0.0",
+      "objectId": "23d9c1d0d6c41f1c8e08ab98a6a79c2d5ada649d",
+      "creator": {
+        "displayName": "Test User",
+        "url": "https://dev.azure.com/fabrikam/_apis/Identities/d6245f20-2af8-44f4-9451-8107cb2767db",
+        "_links": {
+          "avatar": {
+            "href": "https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YTYyNDVmMjAtMmFmOC00NGY0LTk0NTEtODEwN2NiMjc2N2Ri"
+          }
+        },
+        "id": "d6245f20-2af8-44f4-9451-8107cb2767db",
+        "uniqueName": "test@example.com",
+        "imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=d6245f20-2af8-44f4-9451-8107cb2767db",
+        "descriptor": "aad.YTYyNDVmMjAtMmFmOC00NGY0LTk0NTEtODEwN2NiMjc2N2Ri"
+      },
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/refs/tags/v1.0.0"
+    },
+    {
+      "name": "refs/tags/v1.1.0",
+      "objectId": "8a9db19b5e19613c18e9059d7b9fa5d6d6a3785f",
+      "creator": {
+        "displayName": "Test User",
+        "url": "https://dev.azure.com/fabrikam/_apis/Identities/d6245f20-2af8-44f4-9451-8107cb2767db",
+        "_links": {
+          "avatar": {
+            "href": "https://dev.azure.com/fabrikam/_apis/GraphProfile/MemberAvatars/aad.YTYyNDVmMjAtMmFmOC00NGY0LTk0NTEtODEwN2NiMjc2N2Ri"
+          }
+        },
+        "id": "d6245f20-2af8-44f4-9451-8107cb2767db",
+        "uniqueName": "test@example.com",
+        "imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=d6245f20-2af8-44f4-9451-8107cb2767db",
+        "descriptor": "aad.YTYyNDVmMjAtMmFmOC00NGY0LTk0NTEtODEwN2NiMjc2N2Ri"
+      },
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/refs/tags/v1.1.0"
+    }
+  ],
+  "count": 2
+}
diff --git a/scm/driver/azure/testdata/tags.json.golden b/scm/driver/azure/testdata/tags.json.golden
new file mode 100644
index 000000000..28ef8bbf3
--- /dev/null
+++ b/scm/driver/azure/testdata/tags.json.golden
@@ -0,0 +1,12 @@
+[
+  {
+    "Name": "v1.0.0",
+    "Path": "refs/tags/v1.0.0",
+    "Sha": "23d9c1d0d6c41f1c8e08ab98a6a79c2d5ada649d"
+  },
+  {
+    "Name": "v1.1.0",
+    "Path": "refs/tags/v1.1.0",
+    "Sha": "8a9db19b5e19613c18e9059d7b9fa5d6d6a3785f"
+  }
+]
diff --git a/scm/driver/azure/testdata/webhooks/issue_comment.json b/scm/driver/azure/testdata/webhooks/issue_comment.json
new file mode 100644
index 000000000..33cd10cad
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/issue_comment.json
@@ -0,0 +1,132 @@
+{
+    "subscriptionId": "00000000-0000-0000-0000-000000000000",
+    "notificationId": 2,
+    "id": "af07be1b-f3ad-44c8-a7f1-c4835f2df06b",
+    "eventType": "ms.vss-code.git-pullrequest-comment-event",
+    "publisherId": "tfs",
+    "message": {
+      "text": "Jamal Hartnett has edited a pull request comment",
+      "html": "Jamal Hartnett has <a href=\"https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5\">edited</a> a pull request comment",
+      "markdown": "Jamal Hartnett has [edited](https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5) a pull request comment"
+    },
+    "detailedMessage": {
+      "text": "Jamal Hartnett has edited a pull request comment\r\nThis is my comment.\r\n",
+      "html": "Jamal Hartnett has <a href=\"https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5\">edited</a> a pull request comment<p>This is my comment.</p>",
+      "markdown": "Jamal Hartnett has [edited](https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5) a pull request comment\r\nThis is my comment.\r\n"
+    },
+    "resource": {
+      "comment": {
+        "id": 2,
+        "parentCommentId": 1,
+        "author": {
+          "displayName": "Jamal Hartnett",
+          "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "uniqueName": "fabrikamfiber4@hotmail.com",
+          "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "content": "This is my comment.",
+        "publishedDate": "2014-06-17T16:55:46.589889Z",
+        "lastUpdatedDate": "2014-06-17T16:55:46.589889Z",
+        "lastContentUpdatedDate": "2014-06-17T16:58:33.123889Z",
+        "commentType": "text",
+        "_links": {
+          "self": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/threads/5/comments/2"
+          },
+          "repository": {
+            "href": "http://joscol2/DefaultCollection/ebed510c-62eb-474b-965f-fd151ebb82e4/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079"
+          },
+          "threads": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/threads/5"
+          }
+        }
+      },
+      "pullRequest": {
+        "repository": {
+          "id": "4bc14d40-c903-45e2-872e-0462c7748079",
+          "name": "Fabrikam",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
+          "project": {
+            "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+            "name": "Fabrikam",
+            "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+            "state": "wellFormed",
+            "visibility": "unchanged",
+            "lastUpdateTime": "0001-01-01T00:00:00"
+          },
+          "sshUrl": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+          "webUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+          "remoteUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam"
+        },
+        "pullRequestId": 1,
+        "status": "active",
+        "createdBy": {
+          "displayName": "Jamal Hartnett",
+          "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "uniqueName": "fabrikamfiber4@hotmail.com",
+          "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "creationDate": "2014-06-17T16:55:46.589889Z",
+        "title": "my first pull request",
+        "description": " - test2\r\n",
+        "sourceRefName": "refs/heads/mytopic",
+        "targetRefName": "refs/heads/master",
+        "mergeStatus": "succeeded",
+        "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
+        "lastMergeSourceCommit": {
+          "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+        },
+        "lastMergeTargetCommit": {
+          "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
+        },
+        "lastMergeCommit": {
+          "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
+        },
+        "reviewers": [
+          {
+            "reviewerUrl": null,
+            "vote": 0,
+            "displayName": "[Mobile]\\Mobile Team",
+            "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
+            "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "isContainer": true
+          }
+        ],
+        "commits": [
+          {
+            "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+            "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+          }
+        ],
+        "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+        "_links": {
+          "web": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1#view=discussion"
+          },
+          "statuses": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/statuses"
+          }
+        }
+      }
+    },
+    "resourceVersion": "2.0",
+    "resourceContainers": {
+      "collection": {
+        "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+      },
+      "account": {
+        "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+      },
+      "project": {
+        "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+      }
+    },
+    "createdDate": "2022-06-21T13:03:20.480894Z"
+  }
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/issue_comment.json.golden b/scm/driver/azure/testdata/webhooks/issue_comment.json.golden
new file mode 100644
index 000000000..70b4da243
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/issue_comment.json.golden
@@ -0,0 +1,88 @@
+{
+  "Action": "created",
+  "Repo": {
+    "ID": "4bc14d40-c903-45e2-872e-0462c7748079",
+    "Namespace": "Fabrikam",
+    "Name": "Fabrikam",
+    "Perm": null,
+    "Branch": "mytopic",
+    "Archived": false,
+    "Private": false,
+    "Visibility": 0,
+    "Clone": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "CloneSSH": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Issue": {
+    "Number": 1,
+    "Title": "my first pull request",
+    "Body": " - test2\r\n",
+    "Link": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+    "Author": {
+        "Login": "Jamal Hartnett",
+        "Name": "Jamal Hartnett",
+        "Email": "fabrikamfiber4@hotmail.com",
+        "Avatar": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 1,
+        "Title": "my first pull request",
+        "Body": " - test2\r\n",
+        "Sha": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+        "Ref": "refs/heads/mytopic",
+        "Source": "mytopic",
+        "Target": "master",
+        "Fork": "",
+        "Link": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+        "Diff": "",
+        "Closed": false,
+        "Merged": false,
+        "Base": {
+            "Name": "",
+            "Path": "",
+            "Sha": ""
+        },
+        "Head": {
+            "Name": "",
+            "Path": "",
+            "Sha": ""
+        },
+        "Author": {
+            "Login": "Jamal Hartnett",
+            "Name": "Jamal Hartnett",
+            "Email": "fabrikamfiber4@hotmail.com",
+            "Avatar": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2014-06-17T16:55:46.589889Z",
+        "Updated": "0001-01-01T00:00:00Z",
+        "Labels": null
+    },
+    "Created": "2014-06-17T16:55:46.589889Z"
+  },
+  "Comment": {
+        "ID": 2,
+        "Body": "This is my comment.",
+        "Author": {
+            "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "Name": "Jamal Hartnett",
+            "Email": "fabrikamfiber4@hotmail.com",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2014-06-17T16:55:46.589889Z",
+        "Updated": "2014-06-17T16:55:46.589889Z"
+    },
+  "Sender": {
+    "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Name": "Jamal Hartnett",
+    "Email": "fabrikamfiber4@hotmail.com",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/issue_comment_delete.json b/scm/driver/azure/testdata/webhooks/issue_comment_delete.json
new file mode 100644
index 000000000..5b154f7b3
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/issue_comment_delete.json
@@ -0,0 +1,133 @@
+{
+    "subscriptionId": "00000000-0000-0000-0000-000000000000",
+    "notificationId": 2,
+    "id": "af07be1b-f3ad-44c8-a7f1-c4835f2df06b",
+    "eventType": "ms.vss-code.git-pullrequest-comment-event",
+    "publisherId": "tfs",
+    "message": {
+      "text": "Jamal Hartnett has edited a pull request comment",
+      "html": "Jamal Hartnett has <a href=\"https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5\">edited</a> a pull request comment",
+      "markdown": "Jamal Hartnett has [edited](https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5) a pull request comment"
+    },
+    "detailedMessage": {
+      "text": "Jamal Hartnett has edited a pull request comment\r\nThis is my comment.\r\n",
+      "html": "Jamal Hartnett has <a href=\"https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5\">edited</a> a pull request comment<p>This is my comment.</p>",
+      "markdown": "Jamal Hartnett has [edited](https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5) a pull request comment\r\nThis is my comment.\r\n"
+    },
+    "resource": {
+      "comment": {
+        "id": 2,
+        "parentCommentId": 1,
+        "author": {
+          "displayName": "Jamal Hartnett",
+          "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "uniqueName": "fabrikamfiber4@hotmail.com",
+          "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "content": "This is my comment.",
+        "publishedDate": "2014-06-17T16:55:46.589889Z",
+        "lastUpdatedDate": "2014-06-17T16:58:33.123889Z",
+        "lastContentUpdatedDate": "2014-06-17T16:58:33.123889Z",
+        "commentType": "text",
+        "isDeleted": true,
+        "_links": {
+          "self": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/threads/5/comments/2"
+          },
+          "repository": {
+            "href": "http://joscol2/DefaultCollection/ebed510c-62eb-474b-965f-fd151ebb82e4/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079"
+          },
+          "threads": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/threads/5"
+          }
+        }
+      },
+      "pullRequest": {
+        "repository": {
+          "id": "4bc14d40-c903-45e2-872e-0462c7748079",
+          "name": "Fabrikam",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
+          "project": {
+            "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+            "name": "Fabrikam",
+            "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+            "state": "wellFormed",
+            "visibility": "unchanged",
+            "lastUpdateTime": "0001-01-01T00:00:00"
+          },
+          "sshUrl": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+          "webUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+          "remoteUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam"
+        },
+        "pullRequestId": 1,
+        "status": "active",
+        "createdBy": {
+          "displayName": "Jamal Hartnett",
+          "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "uniqueName": "fabrikamfiber4@hotmail.com",
+          "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "creationDate": "2014-06-17T16:55:46.589889Z",
+        "title": "my first pull request",
+        "description": " - test2\r\n",
+        "sourceRefName": "refs/heads/mytopic",
+        "targetRefName": "refs/heads/master",
+        "mergeStatus": "succeeded",
+        "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
+        "lastMergeSourceCommit": {
+          "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+        },
+        "lastMergeTargetCommit": {
+          "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
+        },
+        "lastMergeCommit": {
+          "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
+        },
+        "reviewers": [
+          {
+            "reviewerUrl": null,
+            "vote": 0,
+            "displayName": "[Mobile]\\Mobile Team",
+            "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
+            "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "isContainer": true
+          }
+        ],
+        "commits": [
+          {
+            "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+            "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+          }
+        ],
+        "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+        "_links": {
+          "web": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1#view=discussion"
+          },
+          "statuses": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/statuses"
+          }
+        }
+      }
+    },
+    "resourceVersion": "2.0",
+    "resourceContainers": {
+      "collection": {
+        "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+      },
+      "account": {
+        "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+      },
+      "project": {
+        "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+      }
+    },
+    "createdDate": "2022-06-21T13:03:20.480894Z"
+  }
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/issue_comment_delete.json.golden b/scm/driver/azure/testdata/webhooks/issue_comment_delete.json.golden
new file mode 100644
index 000000000..cd1e33741
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/issue_comment_delete.json.golden
@@ -0,0 +1,88 @@
+{
+  "Action": "deleted",
+  "Repo": {
+    "ID": "4bc14d40-c903-45e2-872e-0462c7748079",
+    "Namespace": "Fabrikam",
+    "Name": "Fabrikam",
+    "Perm": null,
+    "Branch": "mytopic",
+    "Archived": false,
+    "Private": false,
+    "Visibility": 0,
+    "Clone": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "CloneSSH": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Issue": {
+    "Number": 1,
+    "Title": "my first pull request",
+    "Body": " - test2\r\n",
+    "Link": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+    "Author": {
+        "Login": "Jamal Hartnett",
+        "Name": "Jamal Hartnett",
+        "Email": "fabrikamfiber4@hotmail.com",
+        "Avatar": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 1,
+        "Title": "my first pull request",
+        "Body": " - test2\r\n",
+        "Sha": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+        "Ref": "refs/heads/mytopic",
+        "Source": "mytopic",
+        "Target": "master",
+        "Fork": "",
+        "Link": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+        "Diff": "",
+        "Closed": false,
+        "Merged": false,
+        "Base": {
+            "Name": "",
+            "Path": "",
+            "Sha": ""
+        },
+        "Head": {
+            "Name": "",
+            "Path": "",
+            "Sha": ""
+        },
+        "Author": {
+            "Login": "Jamal Hartnett",
+            "Name": "Jamal Hartnett",
+            "Email": "fabrikamfiber4@hotmail.com",
+            "Avatar": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2014-06-17T16:55:46.589889Z",
+        "Updated": "0001-01-01T00:00:00Z",
+        "Labels": null
+    },
+    "Created": "2014-06-17T16:55:46.589889Z"
+  },
+  "Comment": {
+        "ID": 2,
+        "Body": "This is my comment.",
+        "Author": {
+            "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "Name": "Jamal Hartnett",
+            "Email": "fabrikamfiber4@hotmail.com",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2014-06-17T16:55:46.589889Z",
+        "Updated": "2014-06-17T16:58:33.123889Z"
+    },
+  "Sender": {
+    "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Name": "Jamal Hartnett",
+    "Email": "fabrikamfiber4@hotmail.com",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/issue_comment_edit.json b/scm/driver/azure/testdata/webhooks/issue_comment_edit.json
new file mode 100644
index 000000000..971daae0d
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/issue_comment_edit.json
@@ -0,0 +1,132 @@
+{
+    "subscriptionId": "00000000-0000-0000-0000-000000000000",
+    "notificationId": 2,
+    "id": "af07be1b-f3ad-44c8-a7f1-c4835f2df06b",
+    "eventType": "ms.vss-code.git-pullrequest-comment-event",
+    "publisherId": "tfs",
+    "message": {
+      "text": "Jamal Hartnett has edited a pull request comment",
+      "html": "Jamal Hartnett has <a href=\"https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5\">edited</a> a pull request comment",
+      "markdown": "Jamal Hartnett has [edited](https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5) a pull request comment"
+    },
+    "detailedMessage": {
+      "text": "Jamal Hartnett has edited a pull request comment\r\nThis is my comment.\r\n",
+      "html": "Jamal Hartnett has <a href=\"https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5\">edited</a> a pull request comment<p>This is my comment.</p>",
+      "markdown": "Jamal Hartnett has [edited](https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1?discussionId=5) a pull request comment\r\nThis is my comment.\r\n"
+    },
+    "resource": {
+      "comment": {
+        "id": 2,
+        "parentCommentId": 1,
+        "author": {
+          "displayName": "Jamal Hartnett",
+          "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "uniqueName": "fabrikamfiber4@hotmail.com",
+          "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "content": "This is my comment.",
+        "publishedDate": "2014-06-17T16:55:46.589889Z",
+        "lastUpdatedDate": "2014-06-17T16:58:33.123889Z",
+        "lastContentUpdatedDate": "2014-06-17T16:58:33.123889Z",
+        "commentType": "text",
+        "_links": {
+          "self": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/threads/5/comments/2"
+          },
+          "repository": {
+            "href": "http://joscol2/DefaultCollection/ebed510c-62eb-474b-965f-fd151ebb82e4/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079"
+          },
+          "threads": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/threads/5"
+          }
+        }
+      },
+      "pullRequest": {
+        "repository": {
+          "id": "4bc14d40-c903-45e2-872e-0462c7748079",
+          "name": "Fabrikam",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
+          "project": {
+            "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+            "name": "Fabrikam",
+            "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+            "state": "wellFormed",
+            "visibility": "unchanged",
+            "lastUpdateTime": "0001-01-01T00:00:00"
+          },
+          "sshUrl": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+          "webUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+          "remoteUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam"
+        },
+        "pullRequestId": 1,
+        "status": "active",
+        "createdBy": {
+          "displayName": "Jamal Hartnett",
+          "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+          "uniqueName": "fabrikamfiber4@hotmail.com",
+          "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "creationDate": "2014-06-17T16:55:46.589889Z",
+        "title": "my first pull request",
+        "description": " - test2\r\n",
+        "sourceRefName": "refs/heads/mytopic",
+        "targetRefName": "refs/heads/master",
+        "mergeStatus": "succeeded",
+        "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
+        "lastMergeSourceCommit": {
+          "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+        },
+        "lastMergeTargetCommit": {
+          "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
+        },
+        "lastMergeCommit": {
+          "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
+          "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
+        },
+        "reviewers": [
+          {
+            "reviewerUrl": null,
+            "vote": 0,
+            "displayName": "[Mobile]\\Mobile Team",
+            "url": "https://fabrikam.vssps.visualstudio.com/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
+            "imageUrl": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
+            "isContainer": true
+          }
+        ],
+        "commits": [
+          {
+            "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+            "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+          }
+        ],
+        "url": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+        "_links": {
+          "web": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_git/Fabrikam/pullrequest/1#view=discussion"
+          },
+          "statuses": {
+            "href": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1/statuses"
+          }
+        }
+      }
+    },
+    "resourceVersion": "2.0",
+    "resourceContainers": {
+      "collection": {
+        "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+      },
+      "account": {
+        "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+      },
+      "project": {
+        "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+      }
+    },
+    "createdDate": "2022-06-21T13:03:20.480894Z"
+  }
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/issue_comment_edit.json.golden b/scm/driver/azure/testdata/webhooks/issue_comment_edit.json.golden
new file mode 100644
index 000000000..4ae2669fb
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/issue_comment_edit.json.golden
@@ -0,0 +1,88 @@
+{
+  "Action": "edited",
+  "Repo": {
+    "ID": "4bc14d40-c903-45e2-872e-0462c7748079",
+    "Namespace": "Fabrikam",
+    "Name": "Fabrikam",
+    "Perm": null,
+    "Branch": "mytopic",
+    "Archived": false,
+    "Private": false,
+    "Visibility": 0,
+    "Clone": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "CloneSSH": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Issue": {
+    "Number": 1,
+    "Title": "my first pull request",
+    "Body": " - test2\r\n",
+    "Link": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+    "Author": {
+        "Login": "Jamal Hartnett",
+        "Name": "Jamal Hartnett",
+        "Email": "fabrikamfiber4@hotmail.com",
+        "Avatar": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 1,
+        "Title": "my first pull request",
+        "Body": " - test2\r\n",
+        "Sha": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+        "Ref": "refs/heads/mytopic",
+        "Source": "mytopic",
+        "Target": "master",
+        "Fork": "",
+        "Link": "https://fabrikam.visualstudio.com/DefaultCollection/_apis/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+        "Diff": "",
+        "Closed": false,
+        "Merged": false,
+        "Base": {
+            "Name": "",
+            "Path": "",
+            "Sha": ""
+        },
+        "Head": {
+            "Name": "",
+            "Path": "",
+            "Sha": ""
+        },
+        "Author": {
+            "Login": "Jamal Hartnett",
+            "Name": "Jamal Hartnett",
+            "Email": "fabrikamfiber4@hotmail.com",
+            "Avatar": "https://fabrikam.visualstudio.com/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2014-06-17T16:55:46.589889Z",
+        "Updated": "0001-01-01T00:00:00Z",
+        "Labels": null
+    },
+    "Created": "2014-06-17T16:55:46.589889Z"
+  },
+  "Comment": {
+        "ID": 2,
+        "Body": "This is my comment.",
+        "Author": {
+            "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "Name": "Jamal Hartnett",
+            "Email": "fabrikamfiber4@hotmail.com",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2014-06-17T16:55:46.589889Z",
+        "Updated": "2014-06-17T16:58:33.123889Z"
+    },
+  "Sender": {
+    "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Name": "Jamal Hartnett",
+    "Email": "fabrikamfiber4@hotmail.com",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/pr_created.json b/scm/driver/azure/testdata/webhooks/pr_created.json
new file mode 100644
index 000000000..9b544c962
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/pr_created.json
@@ -0,0 +1,86 @@
+{
+  "id": "2ab4e3d3-b7a6-425e-92b1-5a9982c1269e",
+  "eventType": "git.pullrequest.created",
+  "publisherId": "tfs",
+  "scope": "all",
+  "message": {
+    "text": "Jamal Hartnett created a new pull request",
+    "html": "Jamal Hartnett created a new pull request",
+    "markdown": "Jamal Hartnett created a new pull request"
+  },
+  "detailedMessage": {
+    "text": "Jamal Hartnett created a new pull request\r\n\r\n- Merge status: Succeeded\r\n- Merge commit: eef717(https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n",
+    "html": "Jamal Hartnett created a new pull request\r\n<ul>\r\n<li>Merge status: Succeeded</li>\r\n<li>Merge commit: <a href=\"https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72\">eef717</a></li>\r\n</ul>",
+    "markdown": "Jamal Hartnett created a new pull request\r\n\r\n+ Merge status: Succeeded\r\n+ Merge commit: [eef717](https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n"
+  },
+  "resource": {
+    "repository": {
+      "id": "4bc14d40-c903-45e2-872e-0462c7748079",
+      "name": "Fabrikam",
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
+      "project": {
+        "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+        "name": "Fabrikam",
+        "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+        "state": "wellFormed"
+      },
+      "sshUrl": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+      "webUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+      "remoteUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam"
+    },
+    "pullRequestId": 1,
+    "status": "active",
+    "createdBy": {
+      "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+      "displayName": "Jamal Hartnett",
+      "uniqueName": "fabrikamfiber4@hotmail.com",
+      "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+      "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+    },
+    "creationDate": "2014-06-17T16:55:46.589889Z",
+    "title": "my first pull request",
+    "description": " - test2\r\n",
+    "sourceRefName": "refs/heads/mytopic",
+    "targetRefName": "refs/heads/master",
+    "mergeStatus": "succeeded",
+    "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
+    "lastMergeSourceCommit": {
+      "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+    },
+    "lastMergeTargetCommit": {
+      "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
+    },
+    "lastMergeCommit": {
+      "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
+    },
+    "reviewers": [
+      {
+        "reviewerUrl": null,
+        "vote": 0,
+        "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
+        "displayName": "[Mobile]\\Mobile Team",
+        "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
+        "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
+        "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
+        "isContainer": true
+      }
+    ],
+    "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1"
+  },
+  "resourceVersion": "1.0",
+  "resourceContainers": {
+    "collection": {
+      "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+    },
+    "account": {
+      "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+    },
+    "project": {
+      "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+    }
+  },
+  "createdDate": "2016-09-19T13:03:27.2879096Z"
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/pr_created.json.golden b/scm/driver/azure/testdata/webhooks/pr_created.json.golden
new file mode 100644
index 000000000..e081e85fa
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/pr_created.json.golden
@@ -0,0 +1,61 @@
+{
+  "Action": "created",
+  "Repo": {
+    "ID": "4bc14d40-c903-45e2-872e-0462c7748079",
+    "Namespace": "Fabrikam",
+    "Name": "Fabrikam",
+    "Perm": null,
+    "Branch": "",
+    "Archived": false,
+    "Private": false,
+    "Visibility": 0,
+    "Clone": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "CloneSSH": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 1,
+    "Title": "my first pull request",
+    "Body": " - test2\r\n",
+    "Sha": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+    "Ref": "refs/heads/mytopic",
+    "Source": "mytopic",
+    "Target": "master",
+    "Fork": "",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+    "Diff": "",
+    "Closed": false,
+    "Merged": false,
+    "Base": {
+      "Name": "",
+      "Path": "",
+      "Sha": ""
+    },
+    "Head": {
+      "Name": "",
+      "Path": "",
+      "Sha": ""
+    },
+    "Author": {
+      "Login": "Jamal Hartnett",
+      "Name": "Jamal Hartnett",
+      "Email": "fabrikamfiber4@hotmail.com",
+      "Avatar": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+      "Created": "0001-01-01T00:00:00Z",
+      "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Created": "2014-06-17T16:55:46.589889Z",
+    "Updated": "0001-01-01T00:00:00Z",
+    "Labels": null
+  },
+  "Sender": {
+    "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Name": "Jamal Hartnett",
+    "Email": "fabrikamfiber4@hotmail.com",
+    "Avatar": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/pr_merged.json b/scm/driver/azure/testdata/webhooks/pr_merged.json
new file mode 100644
index 000000000..15537ac3e
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/pr_merged.json
@@ -0,0 +1,87 @@
+{
+    "id": "6872ee8c-b333-4eff-bfb9-0d5274943566",
+    "eventType": "git.pullrequest.merged",
+    "publisherId": "tfs",
+    "scope": "all",
+    "message": {
+      "text": "Jamal Hartnett has created a pull request merge commit",
+      "html": "Jamal Hartnett has created a pull request merge commit",
+      "markdown": "Jamal Hartnett has created a pull request merge commit"
+    },
+    "detailedMessage": {
+      "text": "Jamal Hartnett has created a pull request merge commit\r\n\r\n- Merge status: Succeeded\r\n- Merge commit: eef717(https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n",
+      "html": "Jamal Hartnett has created a pull request merge commit\r\n<ul>\r\n<li>Merge status: Succeeded</li>\r\n<li>Merge commit: <a href=\"https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72\">eef717</a></li>\r\n</ul>",
+      "markdown": "Jamal Hartnett has created a pull request merge commit\r\n\r\n+ Merge status: Succeeded\r\n+ Merge commit: [eef717](https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n"
+    },
+    "resource": {
+      "repository": {
+        "id": "4bc14d40-c903-45e2-872e-0462c7748079",
+        "name": "Fabrikam",
+        "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
+        "project": {
+          "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+          "name": "Fabrikam",
+          "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+          "state": "wellFormed"
+        },
+        "sshUrl": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+        "webUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+        "remoteUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam"
+      },
+      "pullRequestId": 1,
+      "status": "completed",
+      "createdBy": {
+        "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+        "displayName": "Jamal Hartnett",
+        "uniqueName": "fabrikamfiber4@hotmail.com",
+        "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+        "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+      },
+      "creationDate": "2014-06-17T16:55:46.589889Z",
+      "closedDate": "2014-06-30T18:59:12.3660573Z",
+      "title": "my first pull request",
+      "description": " - test2\r\n",
+      "sourceRefName": "refs/heads/mytopic",
+      "targetRefName": "refs/heads/master",
+      "mergeStatus": "succeeded",
+      "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
+      "lastMergeSourceCommit": {
+        "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+        "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+      },
+      "lastMergeTargetCommit": {
+        "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
+        "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
+      },
+      "lastMergeCommit": {
+        "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
+        "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
+      },
+      "reviewers": [
+        {
+          "reviewerUrl": null,
+          "vote": 0,
+          "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
+          "displayName": "[Mobile]\\Mobile Team",
+          "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
+          "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
+          "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
+          "isContainer": true
+        }
+      ],
+      "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1"
+    },
+    "resourceVersion": "1.0",
+    "resourceContainers": {
+      "collection": {
+        "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+      },
+      "account": {
+        "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+      },
+      "project": {
+        "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+      }
+    },
+    "createdDate": "2016-09-19T13:03:27.3156388Z"
+  }
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/pr_merged.json.golden b/scm/driver/azure/testdata/webhooks/pr_merged.json.golden
new file mode 100644
index 000000000..844d14542
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/pr_merged.json.golden
@@ -0,0 +1,61 @@
+{
+  "Action": "merged",
+  "Repo": {
+    "ID": "4bc14d40-c903-45e2-872e-0462c7748079",
+    "Namespace": "Fabrikam",
+    "Name": "Fabrikam",
+    "Perm": null,
+    "Branch": "",
+    "Archived": false,
+    "Private": false,
+    "Visibility": 0,
+    "Clone": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "CloneSSH": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 1,
+    "Title": "my first pull request",
+    "Body": " - test2\r\n",
+    "Sha": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+    "Ref": "refs/heads/mytopic",
+    "Source": "mytopic",
+    "Target": "master",
+    "Fork": "",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+    "Diff": "",
+    "Closed": false,
+    "Merged": true,
+    "Base": {
+      "Name": "",
+      "Path": "",
+      "Sha": ""
+    },
+    "Head": {
+      "Name": "",
+      "Path": "",
+      "Sha": ""
+    },
+    "Author": {
+      "Login": "Jamal Hartnett",
+      "Name": "Jamal Hartnett",
+      "Email": "fabrikamfiber4@hotmail.com",
+      "Avatar": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+      "Created": "0001-01-01T00:00:00Z",
+      "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Created": "2014-06-17T16:55:46.589889Z",
+    "Updated": "0001-01-01T00:00:00Z",
+    "Labels": null
+  },
+  "Sender": {
+    "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Name": "Jamal Hartnett",
+    "Email": "fabrikamfiber4@hotmail.com",
+    "Avatar": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/pr_updated.json b/scm/driver/azure/testdata/webhooks/pr_updated.json
new file mode 100644
index 000000000..ac0f7f1ac
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/pr_updated.json
@@ -0,0 +1,93 @@
+{
+    "id": "af07be1b-f3ad-44c8-a7f1-c4835f2df06b",
+    "eventType": "git.pullrequest.updated",
+    "publisherId": "tfs",
+    "scope": "all",
+    "message": {
+        "text": "Jamal Hartnett marked the pull request as completed",
+        "html": "Jamal Hartnett marked the pull request as completed",
+        "markdown": "Jamal Hartnett marked the pull request as completed"
+    },
+    "detailedMessage": {
+        "text": "Jamal Hartnett marked the pull request as completed\r\n\r\n- Merge status: Succeeded\r\n- Merge commit: eef717(https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n",
+        "html": "Jamal Hartnett marked the pull request as completed\r\n<ul>\r\n<li>Merge status: Succeeded</li>\r\n<li>Merge commit: <a href=\"https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72\">eef717</a></li>\r\n</ul>",
+        "markdown": "Jamal Hartnett marked the pull request as completed\r\n\r\n+ Merge status: Succeeded\r\n+ Merge commit: [eef717](https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72)\r\n"
+    },
+    "resource": {
+        "repository": {
+            "id": "4bc14d40-c903-45e2-872e-0462c7748079",
+            "name": "Fabrikam",
+            "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079",
+            "project": {
+                "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+                "name": "Fabrikam",
+                "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+                "state": "wellFormed"
+            },
+            "sshUrl": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+            "webUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+            "remoteUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam"
+        },
+        "pullRequestId": 1,
+        "status": "completed",
+        "createdBy": {
+            "id": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "displayName": "Jamal Hartnett",
+            "uniqueName": "fabrikamfiber4@hotmail.com",
+            "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/54d125f7-69f7-4191-904f-c5b96b6261c8",
+            "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8"
+        },
+        "creationDate": "2014-06-17T16:55:46.589889Z",
+        "closedDate": "2014-06-30T18:59:12.3660573Z",
+        "title": "my first pull request",
+        "description": " - test2\r\n",
+        "sourceRefName": "refs/heads/mytopic",
+        "targetRefName": "refs/heads/master",
+        "mergeStatus": "succeeded",
+        "mergeId": "a10bb228-6ba6-4362-abd7-49ea21333dbd",
+        "lastMergeSourceCommit": {
+            "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+            "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+        },
+        "lastMergeTargetCommit": {
+            "commitId": "a511f535b1ea495ee0c903badb68fbc83772c882",
+            "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/a511f535b1ea495ee0c903badb68fbc83772c882"
+        },
+        "lastMergeCommit": {
+            "commitId": "eef717f69257a6333f221566c1c987dc94cc0d72",
+            "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/eef717f69257a6333f221566c1c987dc94cc0d72"
+        },
+        "reviewers": [
+            {
+                "reviewerUrl": null,
+                "vote": 0,
+                "id": "2ea2d095-48f9-4cd6-9966-62f6f574096c",
+                "displayName": "[Mobile]\\Mobile Team",
+                "uniqueName": "vstfs:///Classification/TeamProject/f0811a3b-8c8a-4e43-a3bf-9a049b4835bd\\Mobile Team",
+                "url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/2ea2d095-48f9-4cd6-9966-62f6f574096c",
+                "imageUrl": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=2ea2d095-48f9-4cd6-9966-62f6f574096c",
+                "isContainer": true
+            }
+        ],
+        "commits": [
+            {
+                "commitId": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+                "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/commits/53d54ac915144006c2c9e90d2c7d3880920db49c"
+            }
+        ],
+        "url": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1"
+    },
+    "resourceVersion": "1.0",
+    "resourceContainers": {
+        "collection": {
+            "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+        },
+        "account": {
+            "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+        },
+        "project": {
+            "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+        }
+    },
+    "createdDate": "2016-09-19T13:03:27.2813828Z"
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/pr_updated.json.golden b/scm/driver/azure/testdata/webhooks/pr_updated.json.golden
new file mode 100644
index 000000000..ae36b5d5b
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/pr_updated.json.golden
@@ -0,0 +1,61 @@
+{
+  "Action": "updated",
+  "Repo": {
+    "ID": "4bc14d40-c903-45e2-872e-0462c7748079",
+    "Namespace": "Fabrikam",
+    "Name": "Fabrikam",
+    "Perm": null,
+    "Branch": "",
+    "Archived": false,
+    "Private": false,
+    "Visibility": 0,
+    "Clone": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "CloneSSH": "git@ssh.dev.azure.com:v3/fabrikam/DefaultCollection/Fabrikam",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 1,
+    "Title": "my first pull request",
+    "Body": " - test2\r\n",
+    "Sha": "53d54ac915144006c2c9e90d2c7d3880920db49c",
+    "Ref": "refs/heads/mytopic",
+    "Source": "mytopic",
+    "Target": "master",
+    "Fork": "",
+    "Link": "https://dev.azure.com/fabrikam/DefaultCollection/_apis/repos/git/repositories/4bc14d40-c903-45e2-872e-0462c7748079/pullRequests/1",
+    "Diff": "",
+    "Closed": true,
+    "Merged": false,
+    "Base": {
+      "Name": "",
+      "Path": "",
+      "Sha": ""
+    },
+    "Head": {
+      "Name": "",
+      "Path": "",
+      "Sha": ""
+    },
+    "Author": {
+      "Login": "Jamal Hartnett",
+      "Name": "Jamal Hartnett",
+      "Email": "fabrikamfiber4@hotmail.com",
+      "Avatar": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+      "Created": "0001-01-01T00:00:00Z",
+      "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Created": "2014-06-17T16:55:46.589889Z",
+    "Updated": "0001-01-01T00:00:00Z",
+    "Labels": null
+  },
+  "Sender": {
+    "Login": "54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Name": "Jamal Hartnett",
+    "Email": "fabrikamfiber4@hotmail.com",
+    "Avatar": "https://dev.azure.com/fabrikam/DefaultCollection/_api/_common/identityImage?id=54d125f7-69f7-4191-904f-c5b96b6261c8",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/push.json b/scm/driver/azure/testdata/webhooks/push.json
new file mode 100644
index 000000000..a5fc4d254
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/push.json
@@ -0,0 +1,76 @@
+{
+    "id": "03c164c2-8912-4d5e-8009-3707d5f83734",
+    "eventType": "git.push",
+    "publisherId": "tfs",
+    "scope": "all",
+    "message": {
+        "text": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.",
+        "html": "Jamal Hartnett pushed updates to branch master of repository Fabrikam-Fiber-Git.",
+        "markdown": "Jamal Hartnett pushed updates to branch `master` of repository `Fabrikam-Fiber-Git`."
+    },
+    "detailedMessage": {
+        "text": "Jamal Hartnett pushed 1 commit to branch master of repository Fabrikam-Fiber-Git.\n - Fixed bug in web.config file 33b55f7c",
+        "html": "Jamal Hartnett pushed 1 commit to branch <a href=\"https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/#version=GBmaster\">master</a> of repository <a href=\"https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/\">Fabrikam-Fiber-Git</a>.\n<ul>\n<li>Fixed bug in web.config file <a href=\"https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74\">33b55f7c</a>\n</ul>",
+        "markdown": "Jamal Hartnett pushed 1 commit to branch [master](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/#version=GBmaster) of repository [Fabrikam-Fiber-Git](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/).\n* Fixed bug in web.config file [33b55f7c](https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74)"
+    },
+    "resource": {
+        "commits": [
+            {
+                "commitId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74",
+                "author": {
+                    "name": "Jamal Hartnett",
+                    "email": "fabrikamfiber4@hotmail.com",
+                    "date": "2015-02-25T19:01:00Z"
+                },
+                "committer": {
+                    "name": "Jamal Hartnett",
+                    "email": "fabrikamfiber4@hotmail.com",
+                    "date": "2015-02-25T19:01:00Z"
+                },
+                "comment": "Fixed bug in web.config file",
+                "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74"
+            }
+        ],
+        "refUpdates": [
+            {
+                "name": "refs/heads/master",
+                "oldObjectId": "aad331d8d3b131fa9ae03cf5e53965b51942618a",
+                "newObjectId": "33b55f7cb7e7e245323987634f960cf4a6e6bc74"
+            }
+        ],
+        "repository": {
+            "id": "278d5cd2-584d-4b63-824a-2ba458937249",
+            "name": "Fabrikam-Fiber-Git",
+            "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/repos/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249",
+            "project": {
+                "id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+                "name": "Fabrikam-Fiber-Git",
+                "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
+                "state": "wellFormed"
+            },
+            "defaultBranch": "refs/heads/master",
+            "remoteUrl": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git"
+        },
+        "pushedBy": {
+            "id": "00067FFED5C7AF52@Live.com",
+            "displayName": "Jamal Hartnett",
+            "uniqueName": "Windows Live ID\\fabrikamfiber4@hotmail.com"
+        },
+        "pushId": 14,
+        "date": "2014-05-02T19:17:13.3309587Z",
+        "url": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_apis/repos/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/pushes/14"
+    },
+    "resourceVersion": "1.0",
+    "resourceContainers": {
+        "collection": {
+            "id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
+        },
+        "account": {
+            "id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
+        },
+        "project": {
+            "id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
+        }
+    },
+    "createdDate": "2016-09-19T13:03:27.0379153Z"
+}
\ No newline at end of file
diff --git a/scm/driver/azure/testdata/webhooks/push.json.golden b/scm/driver/azure/testdata/webhooks/push.json.golden
new file mode 100644
index 000000000..59fae065b
--- /dev/null
+++ b/scm/driver/azure/testdata/webhooks/push.json.golden
@@ -0,0 +1,62 @@
+{
+  "Ref": "refs/heads/master",
+  "Before": "aad331d8d3b131fa9ae03cf5e53965b51942618a",
+  "After": "33b55f7cb7e7e245323987634f960cf4a6e6bc74",
+  "Repo": {
+    "ID": "278d5cd2-584d-4b63-824a-2ba458937249",
+    "Namespace": "Fabrikam-Fiber-Git",
+    "Name": "Fabrikam-Fiber-Git",
+    "Perm": null,
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git",
+    "CloneSSH": "",
+    "Link": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Commits": [
+    {
+      "Sha": "33b55f7cb7e7e245323987634f960cf4a6e6bc74",
+      "Message": "Fixed bug in web.config file",
+      "Author": {
+        "Name": "Jamal Hartnett",
+        "Email": "fabrikamfiber4@hotmail.com",
+        "Date": "2015-02-25T19:01:00-00:00",
+        "Login": "Jamal Hartnett",
+        "Avatar": ""
+      },
+      "Committer": {
+        "Name": "Jamal Hartnett",
+        "Email": "fabrikamfiber4@hotmail.com",
+        "Date": "2015-02-25T19:01:00-00:00",
+        "Login": "Jamal Hartnett",
+        "Avatar": ""
+      },
+      "Link": "https://dev.azure.com/fabrikam-fiber-inc/DefaultCollection/_git/Fabrikam-Fiber-Git/commit/33b55f7cb7e7e245323987634f960cf4a6e6bc74"
+    }
+  ],
+  "Sender": {
+    "Login": "00067FFED5C7AF52@Live.com",
+    "Name": "Jamal Hartnett",
+    "Email": "Windows Live ID\\fabrikamfiber4@hotmail.com",
+    "Avatar": ""
+  },
+  "Commit": {
+    "Sha": "33b55f7cb7e7e245323987634f960cf4a6e6bc74",
+    "Message": "",
+    "Author": {
+      "Login": "00067FFED5C7AF52@Live.com",
+      "Name": "Jamal Hartnett",
+      "Email": "Windows Live ID\\fabrikamfiber4@hotmail.com",
+      "Avatar": ""
+    },
+    "Committer": {
+      "Login": "00067FFED5C7AF52@Live.com",
+      "Name": "Jamal Hartnett",
+      "Email": "Windows Live ID\\fabrikamfiber4@hotmail.com",
+      "Avatar": ""
+    },
+    "Link": ""
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/azure/user.go b/scm/driver/azure/user.go
new file mode 100644
index 000000000..711edd848
--- /dev/null
+++ b/scm/driver/azure/user.go
@@ -0,0 +1,31 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type userService struct {
+	client *wrapper
+}
+
+func (s *userService) Find(ctx context.Context) (*scm.User, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *userService) FindLogin(ctx context.Context, login string) (*scm.User, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, error) {
+	return "", nil, scm.ErrNotSupported
+}
+
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/azure/util.go b/scm/driver/azure/util.go
new file mode 100644
index 000000000..d9f74acdd
--- /dev/null
+++ b/scm/driver/azure/util.go
@@ -0,0 +1,23 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"net/url"
+	"strconv"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func encodeListOptions(opts scm.ListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	return params.Encode()
+}
diff --git a/scm/driver/azure/webhook.go b/scm/driver/azure/webhook.go
new file mode 100644
index 000000000..9e17da18f
--- /dev/null
+++ b/scm/driver/azure/webhook.go
@@ -0,0 +1,790 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type webhookService struct {
+	client *wrapper
+}
+
+func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhook, error) {
+	data, err := ioutil.ReadAll(
+		io.LimitReader(req.Body, 10000000),
+	)
+	if err != nil {
+		return nil, err
+	}
+	// we need to read the json data then look at the eventType
+	var unstructuredJSON map[string]interface{}
+	jsonErr := json.Unmarshal([]byte(data), &unstructuredJSON)
+	if jsonErr != nil {
+		return nil, fmt.Errorf("Error parsing JSON from webhook: %s", jsonErr)
+	}
+	eventType := unstructuredJSON["eventType"].(string)
+
+	switch eventType {
+	case "git.push":
+		// https://docs.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.push
+		src := new(pushHook)
+		err := json.Unmarshal(data, src)
+		if err != nil {
+			return nil, err
+		}
+		dst := convertPushHook(src)
+		return dst, nil
+	case "git.pullrequest.created":
+		// https://docs.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.pullrequest.created
+		src := new(createPullRequestHook)
+		err := json.Unmarshal(data, src)
+		if err != nil {
+			return nil, err
+		}
+		dst := convertCreatePullRequestHook(src)
+		dst.Action = scm.ActionCreate
+		return dst, nil
+	case "git.pullrequest.updated":
+		// https://docs.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.pullrequest.updated
+		src := new(updatePullRequestHook)
+		err := json.Unmarshal(data, src)
+		if err != nil {
+			return nil, err
+		}
+		dst := convertUpdatePullRequestHook(src)
+		dst.Action = scm.ActionUpdate
+		return dst, nil
+	case "git.pullrequest.merged":
+		// https://docs.microsoft.com/en-us/azure/devops/service-hooks/events?view=azure-devops#git.pullrequest.merged
+		src := new(mergePullRequestHook)
+		err := json.Unmarshal(data, src)
+		if err != nil {
+			return nil, err
+		}
+		dst := convertMergePullRequestHook(src)
+		dst.Action = scm.ActionMerge
+		return dst, nil
+	case "ms.vss-code.git-pullrequest-comment-event":
+		src := new(issueCommentPullRequestHook)
+		err := json.Unmarshal(data, src)
+		if err != nil {
+			return nil, err
+		}
+		dst := convertIssueCommentHook(src)
+		dst.Action = getIssueCommentAction(src)
+		return dst, nil
+	default:
+		return nil, scm.ErrUnknownEvent
+	}
+}
+
+func getIssueCommentAction(src *issueCommentPullRequestHook) scm.Action {
+	if src.Resource.Comment.IsDeleted {
+		return scm.ActionDelete
+	} else if src.Resource.Comment.PublishedDate.Equal(src.Resource.Comment.LastUpdatedDate) {
+		return scm.ActionCreate
+	} else {
+		return scm.ActionEdit
+	}
+}
+
+func convertPushHook(src *pushHook) *scm.PushHook {
+	var commits []scm.Commit
+	for _, c := range src.Resource.Commits {
+		commits = append(commits,
+			scm.Commit{
+				Sha:     c.CommitID,
+				Message: c.Comment,
+				Link:    c.URL,
+				Author: scm.Signature{
+					Login: c.Author.Name,
+					Email: c.Author.Email,
+					Name:  c.Author.Name,
+					Date:  c.Author.Date,
+				},
+				Committer: scm.Signature{
+					Login: c.Committer.Name,
+					Email: c.Committer.Email,
+					Name:  c.Committer.Name,
+					Date:  c.Committer.Date,
+				},
+			})
+	}
+	dst := &scm.PushHook{
+		Commit: scm.Commit{
+			Sha: src.Resource.RefUpdates[0].NewObjectID,
+			Message: "",
+			Author: scm.Signature{
+				Login:  src.Resource.PushedBy.ID,
+				Name:   src.Resource.PushedBy.DisplayName,
+				Email:  src.Resource.PushedBy.UniqueName,
+				Avatar: src.Resource.PushedBy.ImageURL,
+			},
+			Committer: scm.Signature{
+				Login:  src.Resource.PushedBy.ID,
+				Name:   src.Resource.PushedBy.DisplayName,
+				Email:  src.Resource.PushedBy.UniqueName,
+				Avatar: src.Resource.PushedBy.ImageURL,
+			},
+			Link: "",
+		},
+		Ref:    src.Resource.RefUpdates[0].Name,
+		Before: src.Resource.RefUpdates[0].OldObjectID,
+		After:  src.Resource.RefUpdates[0].NewObjectID,
+		Sender: scm.User{
+			Login:  src.Resource.PushedBy.ID,
+			Name:   src.Resource.PushedBy.DisplayName,
+			Email:  src.Resource.PushedBy.UniqueName,
+			Avatar: src.Resource.PushedBy.ImageURL,
+		},
+		Repo: scm.Repository{
+			ID:        src.Resource.Repository.ID,
+			Branch:    scm.TrimRef(src.Resource.Repository.DefaultBranch),
+			Name:      src.Resource.Repository.Name,
+			Namespace: src.Resource.Repository.Project.Name,
+			Clone:     src.Resource.Repository.RemoteURL,
+			Link:      src.Resource.Repository.RemoteURL,
+		},
+		Commits: commits,
+	}
+	return dst
+}
+
+func convertCreatePullRequestHook(src *createPullRequestHook) (returnVal *scm.PullRequestHook) {
+	returnVal = &scm.PullRequestHook{
+		PullRequest: scm.PullRequest{
+			Number: src.Resource.PullRequestID,
+			Title:  src.Resource.Title,
+			Body:   src.Resource.Description,
+			Sha:    src.Resource.LastMergeSourceCommit.CommitID,
+			Ref:    src.Resource.SourceRefName,
+			Source: scm.TrimRef(src.Resource.SourceRefName),
+			Target: scm.TrimRef(src.Resource.TargetRefName),
+			Link:   src.Resource.URL,
+			Closed: false,
+			Merged: false,
+			Author: scm.User{
+				Login:  src.Resource.CreatedBy.DisplayName,
+				Name:   src.Resource.CreatedBy.DisplayName,
+				Email:  src.Resource.CreatedBy.UniqueName,
+				Avatar: src.Resource.CreatedBy.ImageURL,
+			},
+			Created: src.Resource.CreationDate,
+		},
+		Repo: scm.Repository{
+			ID:        src.Resource.Repository.ID,
+			Name:      src.Resource.Repository.Name,
+			Namespace: src.Resource.Repository.Project.Name,
+			Link:      src.Resource.Repository.WebURL,
+			Clone:     src.Resource.Repository.WebURL,
+			CloneSSH:  src.Resource.Repository.SSHURL,
+		},
+		Sender: scm.User{
+			Login:  src.Resource.CreatedBy.ID,
+			Name:   src.Resource.CreatedBy.DisplayName,
+			Email:  src.Resource.CreatedBy.UniqueName,
+			Avatar: src.Resource.CreatedBy.ImageURL,
+		},
+	}
+	return returnVal
+}
+
+func convertUpdatePullRequestHook(src *updatePullRequestHook) (returnVal *scm.PullRequestHook) {
+	returnVal = &scm.PullRequestHook{
+		PullRequest: scm.PullRequest{
+			Number: src.Resource.PullRequestID,
+			Title:  src.Resource.Title,
+			Body:   src.Resource.Description,
+			Sha:    src.Resource.LastMergeSourceCommit.CommitID,
+			Ref:    src.Resource.SourceRefName,
+			Source: scm.TrimRef(src.Resource.SourceRefName),
+			Target: scm.TrimRef(src.Resource.TargetRefName),
+			Link:   src.Resource.URL,
+			Closed: src.Resource.ClosedDate.Valid,
+			Merged: false,
+			Author: scm.User{
+				Login:  src.Resource.CreatedBy.DisplayName,
+				Name:   src.Resource.CreatedBy.DisplayName,
+				Email:  src.Resource.CreatedBy.UniqueName,
+				Avatar: src.Resource.CreatedBy.ImageURL,
+			},
+			Created: src.Resource.CreationDate,
+		},
+		Repo: scm.Repository{
+			ID:        src.Resource.Repository.ID,
+			Name:      src.Resource.Repository.Name,
+			Namespace: src.Resource.Repository.Project.Name,
+			Link:      src.Resource.Repository.WebURL,
+			Clone:     src.Resource.Repository.WebURL,
+			CloneSSH:  src.Resource.Repository.SSHURL,
+		},
+		Sender: scm.User{
+			Login:  src.Resource.CreatedBy.ID,
+			Name:   src.Resource.CreatedBy.DisplayName,
+			Email:  src.Resource.CreatedBy.UniqueName,
+			Avatar: src.Resource.CreatedBy.ImageURL,
+		},
+	}
+	return returnVal
+}
+
+func convertMergePullRequestHook(src *mergePullRequestHook) (returnVal *scm.PullRequestHook) {
+	returnVal = &scm.PullRequestHook{
+		PullRequest: scm.PullRequest{
+			Number: src.Resource.PullRequestID,
+			Title:  src.Resource.Title,
+			Body:   src.Resource.Description,
+			Sha:    src.Resource.LastMergeSourceCommit.CommitID,
+			Ref:    src.Resource.SourceRefName,
+			Source: scm.TrimRef(src.Resource.SourceRefName),
+			Target: scm.TrimRef(src.Resource.TargetRefName),
+			Link:   src.Resource.URL,
+			Closed: false,
+			Merged: true,
+			Author: scm.User{
+				Login:  src.Resource.CreatedBy.DisplayName,
+				Name:   src.Resource.CreatedBy.DisplayName,
+				Email:  src.Resource.CreatedBy.UniqueName,
+				Avatar: src.Resource.CreatedBy.ImageURL,
+			},
+			Created: src.Resource.CreationDate,
+		},
+		Repo: scm.Repository{
+			ID:        src.Resource.Repository.ID,
+			Name:      src.Resource.Repository.Name,
+			Namespace: src.Resource.Repository.Project.Name,
+			Link:      src.Resource.Repository.WebURL,
+			Clone:     src.Resource.Repository.WebURL,
+			CloneSSH:  src.Resource.Repository.SSHURL,
+		},
+		Sender: scm.User{
+			Login:  src.Resource.CreatedBy.ID,
+			Name:   src.Resource.CreatedBy.DisplayName,
+			Email:  src.Resource.CreatedBy.UniqueName,
+			Avatar: src.Resource.CreatedBy.ImageURL,
+		},
+	}
+	return returnVal
+}
+
+func convertIssueCommentHook(src *issueCommentPullRequestHook) *scm.IssueCommentHook {
+	dst := &scm.IssueCommentHook{
+		Repo: scm.Repository{
+			ID:         src.Resource.PullRequest.Repository.ID,
+			Namespace:  src.Resource.PullRequest.Repository.Project.Name,
+			Name:       src.Resource.PullRequest.Repository.Name,
+			Branch:     scm.TrimRef(src.Resource.PullRequest.SourceRefName),
+			Private:    false,
+			Clone:      src.Resource.PullRequest.Repository.WebURL,
+			CloneSSH:   src.Resource.PullRequest.Repository.SSHURL,
+			Link:       src.Resource.PullRequest.Repository.WebURL,
+		},
+		Issue: scm.Issue{
+			Number:      src.Resource.PullRequest.PullRequestID,
+			Title:       src.Resource.PullRequest.Title,
+			Body:        src.Resource.PullRequest.Description,
+			Link:        src.Resource.PullRequest.URL,
+			Author:      scm.User{
+				Login:  src.Resource.PullRequest.CreatedBy.DisplayName,
+				Name:   src.Resource.PullRequest.CreatedBy.DisplayName,
+				Email:  src.Resource.PullRequest.CreatedBy.UniqueName,
+				Avatar: src.Resource.PullRequest.CreatedBy.ImageURL,
+			},
+			PullRequest: scm.PullRequest{
+				Number: src.Resource.PullRequest.PullRequestID,
+				Title:  src.Resource.PullRequest.Title,
+				Body:   src.Resource.PullRequest.Description,
+				Sha:    src.Resource.PullRequest.LastMergeSourceCommit.CommitID,
+				Ref:    src.Resource.PullRequest.SourceRefName,
+				Source: scm.TrimRef(src.Resource.PullRequest.SourceRefName),
+				Target: scm.TrimRef(src.Resource.PullRequest.TargetRefName),
+				Link:   src.Resource.PullRequest.URL,
+				Closed: false,
+				Merged: false,
+				Author: scm.User{
+					Login:  src.Resource.PullRequest.CreatedBy.DisplayName,
+					Name:   src.Resource.PullRequest.CreatedBy.DisplayName,
+					Email:  src.Resource.PullRequest.CreatedBy.UniqueName,
+					Avatar: src.Resource.PullRequest.CreatedBy.ImageURL,
+				},
+				Created: src.Resource.PullRequest.CreationDate,
+			},
+			Created: src.Resource.PullRequest.CreationDate,
+		},
+		Comment: scm.Comment{
+			ID:      src.Resource.Comment.ID,
+			Body:    src.Resource.Comment.Content,
+			Author:  scm.User{
+				Email:   src.Resource.Comment.Author.UniqueName,
+				Login:   src.Resource.Comment.Author.ID,
+				Name:    src.Resource.Comment.Author.DisplayName,
+			},
+			Created: src.Resource.Comment.PublishedDate,
+			Updated: src.Resource.Comment.LastUpdatedDate,
+		},
+		Sender: scm.User{
+			Email:   src.Resource.Comment.Author.UniqueName,
+			Login:   src.Resource.Comment.Author.ID,
+			Name:    src.Resource.Comment.Author.DisplayName,
+		},
+	}
+	return dst
+}
+
+type pushHook struct {
+	CreatedDate     string `json:"createdDate"`
+	DetailedMessage struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"detailedMessage"`
+	EventType string `json:"eventType"`
+	ID        string `json:"id"`
+	Message   struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"message"`
+	PublisherID string `json:"publisherId"`
+	Resource    struct {
+		Commits []struct {
+			Author struct {
+				Date  time.Time `json:"date"`
+				Email string    `json:"email"`
+				Name  string    `json:"name"`
+			} `json:"author"`
+			Comment   string `json:"comment"`
+			CommitID  string `json:"commitId"`
+			Committer struct {
+				Date  time.Time `json:"date"`
+				Email string    `json:"email"`
+				Name  string    `json:"name"`
+			} `json:"committer"`
+			URL string `json:"url"`
+		} `json:"commits"`
+		Date     string `json:"date"`
+		PushID   int64  `json:"pushId"`
+		PushedBy struct {
+			DisplayName string `json:"displayName"`
+			ID          string `json:"id"`
+			UniqueName  string `json:"uniqueName"`
+			ImageURL    string `json:"imageUrl"`
+		} `json:"pushedBy"`
+		RefUpdates []struct {
+			Name        string `json:"name"`
+			NewObjectID string `json:"newObjectId"`
+			OldObjectID string `json:"oldObjectId"`
+		} `json:"refUpdates"`
+		Repository struct {
+			DefaultBranch string `json:"defaultBranch"`
+			ID            string `json:"id"`
+			Name          string `json:"name"`
+			Project       struct {
+				ID    string `json:"id"`
+				Name  string `json:"name"`
+				State string `json:"state"`
+				URL   string `json:"url"`
+			} `json:"project"`
+			RemoteURL string `json:"remoteUrl"`
+			URL       string `json:"url"`
+		} `json:"repository"`
+		URL string `json:"url"`
+	} `json:"resource"`
+	ResourceContainers struct {
+		Account struct {
+			ID string `json:"id"`
+		} `json:"account"`
+		Collection struct {
+			ID string `json:"id"`
+		} `json:"collection"`
+		Project struct {
+			ID string `json:"id"`
+		} `json:"project"`
+	} `json:"resourceContainers"`
+	ResourceVersion string `json:"resourceVersion"`
+	Scope           string `json:"scope"`
+}
+
+type createPullRequestHook struct {
+	ID          string `json:"id"`
+	EventType   string `json:"eventType"`
+	PublisherID string `json:"publisherId"`
+	Scope       string `json:"scope"`
+	Message     struct {
+		Text     string `json:"text"`
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+	} `json:"message"`
+	DetailedMessage struct {
+		Text     string `json:"text"`
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+	} `json:"detailedMessage"`
+	Resource struct {
+		Repository struct {
+			ID      string `json:"id"`
+			Name    string `json:"name"`
+			URL     string `json:"url"`
+			WebURL  string `json:"webUrl"`
+			SSHURL  string `json:"sshUrl"`
+			Project struct {
+				ID    string `json:"id"`
+				Name  string `json:"name"`
+				URL   string `json:"url"`
+				State string `json:"state"`
+			} `json:"project"`
+			DefaultBranch string `json:"defaultBranch"`
+			RemoteURL     string `json:"remoteUrl"`
+		} `json:"repository"`
+		PullRequestID int    `json:"pullRequestId"`
+		Status        string `json:"status"`
+		CreatedBy     struct {
+			ID          string `json:"id"`
+			DisplayName string `json:"displayName"`
+			UniqueName  string `json:"uniqueName"`
+			URL         string `json:"url"`
+			ImageURL    string `json:"imageUrl"`
+		} `json:"createdBy"`
+		CreationDate          time.Time `json:"creationDate"`
+		Title                 string    `json:"title"`
+		Description           string    `json:"description"`
+		SourceRefName         string    `json:"sourceRefName"`
+		TargetRefName         string    `json:"targetRefName"`
+		MergeStatus           string    `json:"mergeStatus"`
+		MergeID               string    `json:"mergeId"`
+		LastMergeSourceCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeSourceCommit"`
+		LastMergeTargetCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeTargetCommit"`
+		LastMergeCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeCommit"`
+		Reviewers []struct {
+			ReviewerURL interface{} `json:"reviewerUrl"`
+			Vote        int         `json:"vote"`
+			ID          string      `json:"id"`
+			DisplayName string      `json:"displayName"`
+			UniqueName  string      `json:"uniqueName"`
+			URL         string      `json:"url"`
+			ImageURL    string      `json:"imageUrl"`
+			IsContainer bool        `json:"isContainer"`
+		} `json:"reviewers"`
+		URL string `json:"url"`
+	} `json:"resource"`
+	ResourceVersion    string `json:"resourceVersion"`
+	ResourceContainers struct {
+		Collection struct {
+			ID string `json:"id"`
+		} `json:"collection"`
+		Account struct {
+			ID string `json:"id"`
+		} `json:"account"`
+		Project struct {
+			ID string `json:"id"`
+		} `json:"project"`
+	} `json:"resourceContainers"`
+	CreatedDate time.Time `json:"createdDate"`
+}
+
+type updatePullRequestHook struct {
+	CreatedDate     string `json:"createdDate"`
+	DetailedMessage struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"detailedMessage"`
+	EventType string `json:"eventType"`
+	ID        string `json:"id"`
+	Message   struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"message"`
+	PublisherID string `json:"publisherId"`
+	Resource    struct {
+		ClosedDate null.String `json:"closedDate"`
+		Commits    []struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"commits"`
+		CreatedBy struct {
+			DisplayName string `json:"displayName"`
+			ID          string `json:"id"`
+			ImageURL    string `json:"imageUrl"`
+			UniqueName  string `json:"uniqueName"`
+			URL         string `json:"url"`
+		} `json:"createdBy"`
+		CreationDate    time.Time `json:"creationDate"`
+		Description     string    `json:"description"`
+		LastMergeCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeCommit"`
+		LastMergeSourceCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeSourceCommit"`
+		LastMergeTargetCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeTargetCommit"`
+		MergeID       string `json:"mergeId"`
+		MergeStatus   string `json:"mergeStatus"`
+		PullRequestID int    `json:"pullRequestId"`
+		Repository    struct {
+			DefaultBranch string `json:"defaultBranch"`
+			ID            string `json:"id"`
+			Name          string `json:"name"`
+			Project       struct {
+				ID    string `json:"id"`
+				Name  string `json:"name"`
+				State string `json:"state"`
+				URL   string `json:"url"`
+			} `json:"project"`
+			RemoteURL string `json:"remoteUrl"`
+			URL       string `json:"url"`
+			WebURL    string `json:"webUrl"`
+			SSHURL    string `json:"sshUrl"`
+		} `json:"repository"`
+		Reviewers []struct {
+			DisplayName string      `json:"displayName"`
+			ID          string      `json:"id"`
+			ImageURL    string      `json:"imageUrl"`
+			IsContainer bool        `json:"isContainer"`
+			ReviewerURL interface{} `json:"reviewerUrl"`
+			UniqueName  string      `json:"uniqueName"`
+			URL         string      `json:"url"`
+			Vote        int64       `json:"vote"`
+		} `json:"reviewers"`
+		SourceRefName string `json:"sourceRefName"`
+		Status        string `json:"status"`
+		TargetRefName string `json:"targetRefName"`
+		Title         string `json:"title"`
+		URL           string `json:"url"`
+	} `json:"resource"`
+	ResourceContainers struct {
+		Account struct {
+			ID string `json:"id"`
+		} `json:"account"`
+		Collection struct {
+			ID string `json:"id"`
+		} `json:"collection"`
+		Project struct {
+			ID string `json:"id"`
+		} `json:"project"`
+	} `json:"resourceContainers"`
+	ResourceVersion string `json:"resourceVersion"`
+	Scope           string `json:"scope"`
+}
+
+type mergePullRequestHook struct {
+	CreatedDate     string `json:"createdDate"`
+	DetailedMessage struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"detailedMessage"`
+	EventType string `json:"eventType"`
+	ID        string `json:"id"`
+	Message   struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"message"`
+	PublisherID string `json:"publisherId"`
+	Resource    struct {
+		ClosedDate string `json:"closedDate"`
+		CreatedBy  struct {
+			DisplayName string `json:"displayName"`
+			ID          string `json:"id"`
+			ImageURL    string `json:"imageUrl"`
+			UniqueName  string `json:"uniqueName"`
+			URL         string `json:"url"`
+		} `json:"createdBy"`
+		CreationDate    time.Time `json:"creationDate"`
+		Description     string    `json:"description"`
+		LastMergeCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeCommit"`
+		LastMergeSourceCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeSourceCommit"`
+		LastMergeTargetCommit struct {
+			CommitID string `json:"commitId"`
+			URL      string `json:"url"`
+		} `json:"lastMergeTargetCommit"`
+		MergeID       string `json:"mergeId"`
+		MergeStatus   string `json:"mergeStatus"`
+		PullRequestID int    `json:"pullRequestId"`
+		Repository    struct {
+			DefaultBranch string `json:"defaultBranch"`
+			ID            string `json:"id"`
+			Name          string `json:"name"`
+			Project       struct {
+				ID    string `json:"id"`
+				Name  string `json:"name"`
+				State string `json:"state"`
+				URL   string `json:"url"`
+			} `json:"project"`
+			RemoteURL string `json:"remoteUrl"`
+			URL       string `json:"url"`
+			WebURL    string `json:"webUrl"`
+			SSHURL    string `json:"sshUrl"`
+		} `json:"repository"`
+		Reviewers []struct {
+			DisplayName string      `json:"displayName"`
+			ID          string      `json:"id"`
+			ImageURL    string      `json:"imageUrl"`
+			IsContainer bool        `json:"isContainer"`
+			ReviewerURL interface{} `json:"reviewerUrl"`
+			UniqueName  string      `json:"uniqueName"`
+			URL         string      `json:"url"`
+			Vote        int64       `json:"vote"`
+		} `json:"reviewers"`
+		SourceRefName string `json:"sourceRefName"`
+		Status        string `json:"status"`
+		TargetRefName string `json:"targetRefName"`
+		Title         string `json:"title"`
+		URL           string `json:"url"`
+	} `json:"resource"`
+	ResourceContainers struct {
+		Account struct {
+			ID string `json:"id"`
+		} `json:"account"`
+		Collection struct {
+			ID string `json:"id"`
+		} `json:"collection"`
+		Project struct {
+			ID string `json:"id"`
+		} `json:"project"`
+	} `json:"resourceContainers"`
+	ResourceVersion string `json:"resourceVersion"`
+	Scope           string `json:"scope"`
+}
+
+type issueCommentPullRequestHook struct {
+	CreatedDate     string `json:"createdDate"`
+	DetailedMessage struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"detailedMessage"`
+	EventType string `json:"eventType"`
+	ID        string `json:"id"`
+	Message   struct {
+		HTML     string `json:"html"`
+		Markdown string `json:"markdown"`
+		Text     string `json:"text"`
+	} `json:"message"`
+	PublisherID string `json:"publisherId"`
+	Resource    struct {
+		PullRequest struct {
+			CreatedBy  struct {
+				DisplayName string `json:"displayName"`
+				ID          string `json:"id"`
+				ImageURL    string `json:"imageUrl"`
+				UniqueName  string `json:"uniqueName"`
+				URL         string `json:"url"`
+			} `json:"createdBy"`
+			CreationDate    time.Time `json:"creationDate"`
+			Description     string    `json:"description"`
+			LastMergeCommit struct {
+				CommitID string `json:"commitId"`
+				Author struct {
+					Date  time.Time `json:"date"`
+					Email string    `json:"email"`
+					Name  string    `json:"name"`
+				} `json:"author"`
+				URL      string `json:"url"`
+			} `json:"lastMergeCommit"`
+			LastMergeSourceCommit struct {
+				CommitID string `json:"commitId"`
+				URL      string `json:"url"`
+			} `json:"lastMergeSourceCommit"`
+			LastMergeTargetCommit struct {
+				CommitID string `json:"commitId"`
+				URL      string `json:"url"`
+			} `json:"lastMergeTargetCommit"`
+			MergeID       string `json:"mergeId"`
+			MergeStatus   string `json:"mergeStatus"`
+			PullRequestID int    `json:"pullRequestId"`
+			Repository    struct {
+				ID            string `json:"id"`
+				Name          string `json:"name"`
+				Project       struct {
+					ID    string `json:"id"`
+					Name  string `json:"name"`
+					State string `json:"state"`
+					URL   string `json:"url"`
+				} `json:"project"`
+				RemoteURL string `json:"remoteUrl"`
+				URL       string `json:"url"`
+				WebURL    string `json:"webUrl"`
+				SSHURL    string `json:"sshUrl"`
+			} `json:"repository"`
+			Reviewers []struct {
+				DisplayName string      `json:"displayName"`
+				ID          string      `json:"id"`
+				ImageURL    string      `json:"imageUrl"`
+				IsContainer bool        `json:"isContainer"`
+				ReviewerURL interface{} `json:"reviewerUrl"`
+				UniqueName  string      `json:"uniqueName"`
+				URL         string      `json:"url"`
+				Vote        int64       `json:"vote"`
+			} `json:"reviewers"`
+			SourceRefName string `json:"sourceRefName"`
+			Status        string `json:"status"`
+			TargetRefName string `json:"targetRefName"`
+			Title         string `json:"title"`
+			URL           string `json:"url"`
+		} `json:"pullRequest"`
+		Comment struct {
+			ID 					   int       `json:"id"`
+			ParentCommentId 	   int       `json:"parentCommentId"`
+			Content 			   string    `json:"content"`
+			PublishedDate          time.Time `json:"publishedDate"`
+			LastUpdatedDate        time.Time `json:"lastUpdatedDate"`
+			LastContentUpdatedDate time.Time `json:"lastContentUpdatedDate"`
+			CommentType 		   string    `json:"commentType"`
+			IsDeleted     		   bool      `json:"isDeleted"`
+			Author 				   struct {
+				DisplayName string `json:"displayName"`
+				ID          string `json:"id"`
+				ImageURL    string `json:"imageUrl"`
+				UniqueName  string `json:"uniqueName"`
+				URL         string `json:"url"`
+			} `json:"author"`
+		} `json:"comment"`
+	} `json:"resource"`
+	ResourceContainers struct {
+		Account struct {
+			ID string `json:"id"`
+		} `json:"account"`
+		Collection struct {
+			ID string `json:"id"`
+		} `json:"collection"`
+		Project struct {
+			ID string `json:"id"`
+		} `json:"project"`
+	} `json:"resourceContainers"`
+	ResourceVersion string `json:"resourceVersion"`
+	Scope           string `json:"scope"`
+}
\ No newline at end of file
diff --git a/scm/driver/azure/webhook_test.go b/scm/driver/azure/webhook_test.go
new file mode 100644
index 000000000..d4024c2f8
--- /dev/null
+++ b/scm/driver/azure/webhook_test.go
@@ -0,0 +1,125 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package azure
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestWebhooks(t *testing.T) {
+	tests := []struct {
+		before string
+		after  string
+		obj    interface{}
+	}{
+		// push hook
+		{
+			before: "testdata/webhooks/push.json",
+			after:  "testdata/webhooks/push.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		// pull request events
+		// pull request created
+		{
+			before: "testdata/webhooks/pr_created.json",
+			after:  "testdata/webhooks/pr_created.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request updated
+		{
+			before: "testdata/webhooks/pr_updated.json",
+			after:  "testdata/webhooks/pr_updated.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request merged
+		{
+			before: "testdata/webhooks/pr_merged.json",
+			after:  "testdata/webhooks/pr_merged.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// issue comment create
+		{
+			before: "testdata/webhooks/issue_comment.json",
+			after:  "testdata/webhooks/issue_comment.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		// issue comment edit
+		{
+			before: "testdata/webhooks/issue_comment_edit.json",
+			after:  "testdata/webhooks/issue_comment_edit.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		// issue comment delete
+		{
+			before: "testdata/webhooks/issue_comment_delete.json",
+			after:  "testdata/webhooks/issue_comment_delete.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+	}
+
+	for _, test := range tests {
+		before, err := ioutil.ReadFile(test.before)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		after, err := ioutil.ReadFile(test.after)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		buf := bytes.NewBuffer(before)
+		r, _ := http.NewRequest("GET", "/?secret=71295b197fa25f4356d2fb9965df3f2379d903d7", buf)
+
+		s := new(webhookService)
+		o, err := s.Parse(r, secretFunc)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		err = json.Unmarshal(after, &test.obj)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		if diff := cmp.Diff(test.obj, o); diff != "" {
+			t.Errorf("Error unmarshaling %s", test.before)
+			t.Log(diff)
+
+			// debug only. remove once implemented
+			//	_ = json.NewEncoder(os.Stdout).Encode(o)
+		}
+
+		switch event := o.(type) {
+		case *scm.PushHook:
+			if !strings.HasPrefix(event.Ref, "refs/") {
+				t.Errorf("Push hook reference must start with refs/")
+			}
+		case *scm.BranchHook:
+			if strings.HasPrefix(event.Ref.Name, "refs/") {
+				t.Errorf("Branch hook reference must not start with refs/")
+			}
+		case *scm.TagHook:
+			if strings.HasPrefix(event.Ref.Name, "refs/") {
+				t.Errorf("Branch hook reference must not start with refs/")
+			}
+		}
+	}
+}
+
+func secretFunc(scm.Webhook) (string, error) {
+	return "71295b197fa25f4356d2fb9965df3f2379d903d7", nil
+}
diff --git a/scm/driver/bitbucket/bitbucket.go b/scm/driver/bitbucket/bitbucket.go
index 8aaa9ae74..f05ab670e 100644
--- a/scm/driver/bitbucket/bitbucket.go
+++ b/scm/driver/bitbucket/bitbucket.go
@@ -10,6 +10,7 @@ import (
 	"context"
 	"encoding/json"
 	"io"
+	"mime/multipart"
 	"net/url"
 	"strings"
 
@@ -33,9 +34,11 @@ func New(uri string) (*scm.Client, error) {
 	client.Contents = &contentService{client}
 	client.Git = &gitService{client}
 	client.Issues = &issueService{client}
+	client.Milestones = &milestoneService{client}
 	client.Organizations = &organizationService{client}
-	client.PullRequests = &pullService{&issueService{client}}
+	client.PullRequests = &pullService{client}
 	client.Repositories = &repositoryService{client}
+	client.Releases = &releaseService{client}
 	client.Reviews = &reviewService{client}
 	client.Users = &userService{client}
 	client.Webhooks = &webhookService{client}
@@ -65,12 +68,74 @@ func (c *wrapper) do(ctx context.Context, method, path string, in, out interface
 	// if we are posting or putting data, we need to
 	// write it to the body of the request.
 	if in != nil {
-		buf := new(bytes.Buffer)
-		json.NewEncoder(buf).Encode(in)
-		req.Header = map[string][]string{
-			"Content-Type": {"application/json"},
+		// create or update content
+		switch content := in.(type) {
+		case *contentCreateUpdate:
+			// add the content to the multipart
+			myReader := strings.NewReader(string(content.Content))
+			var b bytes.Buffer
+			w := multipart.NewWriter(&b)
+			var fw io.Writer
+			fw, _ = w.CreateFormFile(content.Files, "")
+			_, _ = io.Copy(fw, myReader)
+			// add the other fields
+			if content.Message != "" {
+				_ = w.WriteField("message", content.Message)
+			}
+			if content.Branch != "" {
+				_ = w.WriteField("branch", content.Branch)
+			}
+			if content.Sha != "" {
+				_ = w.WriteField("parents", content.Sha)
+			}
+			if content.Author != "" {
+				_ = w.WriteField("author", content.Author)
+			}
+			w.Close()
+			// write the multipart response to the body
+			req.Body = &b
+			// write the content type that contains the length of the multipart
+			req.Header = map[string][]string{
+				"Content-Type": {w.FormDataContentType()},
+			}
+		case *contentDelete:
+			body := &bytes.Buffer{}
+			writer := multipart.NewWriter(body)
+			fw, err := writer.CreateFormField("files")
+			_, err = io.Copy(fw, strings.NewReader(content.File))
+			if err != nil {
+				return nil, err
+			}
+			fw, err = writer.CreateFormField("message")
+			_, err = io.Copy(fw, strings.NewReader(content.Message))
+			if err != nil {
+				return nil, err
+			}
+			fw, err = writer.CreateFormField("author")
+			_, err = io.Copy(fw, strings.NewReader(content.Author))
+			if err != nil {
+				return nil, err
+			}
+			if content.Branch != "" {
+				fw, err = writer.CreateFormField("branch")
+				_, err = io.Copy(fw, strings.NewReader(content.Branch))
+				if err != nil {
+					return nil, err
+				}
+			}
+			writer.Close()
+			req.Body = bytes.NewReader(body.Bytes())
+			req.Header = map[string][]string{
+				"Content-Type": {writer.FormDataContentType()},
+			}
+		default:
+			buf := new(bytes.Buffer)
+			json.NewEncoder(buf).Encode(in)
+			req.Header = map[string][]string{
+				"Content-Type": {"application/json"},
+			}
+			req.Body = buf
 		}
-		req.Body = buf
 	}
 
 	// execute the http request
diff --git a/scm/driver/bitbucket/content.go b/scm/driver/bitbucket/content.go
index 079b67439..3bb2960cf 100644
--- a/scm/driver/bitbucket/content.go
+++ b/scm/driver/bitbucket/content.go
@@ -8,6 +8,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"net/url"
 
 	"github.com/drone/go-scm/scm"
 )
@@ -17,29 +18,77 @@ type contentService struct {
 }
 
 func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
-	endpoint := fmt.Sprintf("/2.0/repositories/%s/src/%s/%s", repo, ref, path)
+	urlEncodedRef := url.QueryEscape(ref)
+	endpoint := fmt.Sprintf("/2.0/repositories/%s/src/%s/%s", repo, urlEncodedRef, path)
 	out := new(bytes.Buffer)
 	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
-	return &scm.Content{
+	content := &scm.Content{
 		Path: path,
 		Data: out.Bytes(),
-	}, res, err
+	}
+	if err != nil {
+		return content, res, err
+	}
+	metaEndpoint := fmt.Sprintf("/2.0/repositories/%s/src/%s/%s?format=meta", repo, urlEncodedRef, path)
+	metaOut := new(metaContent)
+	metaRes, metaErr := s.client.do(ctx, "GET", metaEndpoint, nil, metaOut)
+	if metaErr == nil {
+		content.Sha = metaOut.Commit.Hash
+		return content, metaRes, metaErr
+	} else {
+		// do not risk that returning an error if getting the meta fails.
+		return content, res, err
+	}
 }
 
 func (s *contentService) Create(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+	endpoint := fmt.Sprintf("/2.0/repositories/%s/src", repo)
+	in := &contentCreateUpdate{
+		Files:   path,
+		Message: params.Message,
+		Branch:  params.Branch,
+		Content: params.Data,
+		Author:  fmt.Sprintf("%s <%s>", params.Signature.Name, params.Signature.Email),
+	}
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
 }
 
 func (s *contentService) Update(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+	// https://jira.atlassian.com/browse/BCLOUD-20424?error=login_required&error_description=Login+required&state=196d85f7-a181-4b63-babe-0b567858d8f5 ugh :(
+	endpoint := fmt.Sprintf("/2.0/repositories/%s/src", repo)
+	in := &contentCreateUpdate{
+		Files:   path,
+		Message: params.Message,
+		Branch:  params.Branch,
+		Content: params.Data,
+		Sha:     params.Sha,
+		Author:  fmt.Sprintf("%s <%s>", params.Signature.Name, params.Signature.Email),
+	}
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
 }
 
-func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	author := fmt.Sprintf("%s <%s>", params.Signature.Name, params.Signature.Email)
+	endpoint := fmt.Sprintf("/2.0/repositories/%s/src", repo)
+	in := &contentDelete{
+		File:    path,
+		Branch:  params.Branch,
+		Message: params.Message,
+		Sha:     params.Sha,
+		Author:  author,
+	}
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
 }
 
 func (s *contentService) List(ctx context.Context, repo, path, ref string, opts scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
 	endpoint := fmt.Sprintf("/2.0/repositories/%s/src/%s/%s?%s", repo, ref, path, encodeListOptions(opts))
+	if opts.URL != "" {
+		endpoint = opts.URL
+	}
+
 	out := new(contents)
 	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
 	copyPagination(out.pagination, res)
@@ -52,11 +101,38 @@ type contents struct {
 }
 
 type content struct {
-	Path       string   `json:"path"`
-	Type       string   `json:"type"`
+	Path   string `json:"path"`
+	Type   string `json:"type"`
+	Commit struct {
+		Hash string `json:"hash"`
+	} `json:"commit"`
 	Attributes []string `json:"attributes"`
 }
 
+type metaContent struct {
+	Path   string `json:"path"`
+	Commit struct {
+		Hash string `json:"hash"`
+	} `json:"commit"`
+}
+
+type contentCreateUpdate struct {
+	Files   string `json:"files"`
+	Branch  string `json:"branch"`
+	Message string `json:"message"`
+	Content []byte `json:"content"`
+	Sha     string `json:"sha"`
+	Author  string `json:"author"`
+}
+
+type contentDelete struct {
+	File    string `json:"file"`
+	Branch  string `json:"branch"`
+	Message string `json:"message"`
+	Sha     string `json:"sha"`
+	Author  string `json:"author"`
+}
+
 func convertContentInfoList(from *contents) []*scm.ContentInfo {
 	to := []*scm.ContentInfo{}
 	for _, v := range from.Values {
@@ -66,7 +142,10 @@ func convertContentInfoList(from *contents) []*scm.ContentInfo {
 }
 
 func convertContentInfo(from *content) *scm.ContentInfo {
-	to := &scm.ContentInfo{Path: from.Path}
+	to := &scm.ContentInfo{
+		Path: from.Path,
+		Sha:  from.Commit.Hash,
+	}
 	switch from.Type {
 	case "commit_file":
 		to.Kind = func() scm.ContentKind {
diff --git a/scm/driver/bitbucket/content_test.go b/scm/driver/bitbucket/content_test.go
index d6a87da01..7c16f4e7e 100644
--- a/scm/driver/bitbucket/content_test.go
+++ b/scm/driver/bitbucket/content_test.go
@@ -25,6 +25,13 @@ func TestContentFind(t *testing.T) {
 		Type("text/plain").
 		File("testdata/content.txt")
 
+	gock.New("https://api.bitbucket.org").
+		MatchParam("format", "meta").
+		Get("/2.0/repositories/atlassian/atlaskit/src/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc/README").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content.json")
+
 	client, _ := New("https://api.bitbucket.org")
 	got, _, err := client.Contents.Find(context.Background(), "atlassian/atlaskit", "README", "425863f9dbe56d70c8dcdbf2e4e0805e85591fcc")
 	if err != nil {
@@ -41,27 +48,171 @@ func TestContentFind(t *testing.T) {
 	}
 }
 
+func TestContentFindNoMeta(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories/atlassian/atlaskit/src/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc/README").
+		Reply(200).
+		Type("text/plain").
+		File("testdata/content.txt")
+
+	gock.New("https://api.bitbucket.org").
+		MatchParam("format", "meta").
+		Get("/2.0/repositories/atlassian/atlaskit/src/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc/README").
+		Reply(404).
+		Type("application/json").
+		File("testdata/content_fail.json")
+
+	client, _ := New("https://api.bitbucket.org")
+	got, _, err := client.Contents.Find(context.Background(), "atlassian/atlaskit", "README", "425863f9dbe56d70c8dcdbf2e4e0805e85591fcc")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Content)
+	raw, _ := ioutil.ReadFile("testdata/content.json.fail")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
 func TestContentCreate(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Create(context.Background(), "atlassian/atlaskit", "README", nil)
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Post("/2.0/repositories/atlassian/atlaskit/src").
+		Reply(201).
+		Type("application/json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Data:    []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
 	}
+
+	client := NewDefault()
+	res, err := client.Contents.Create(
+		context.Background(),
+		"atlassian/atlaskit",
+		"test/hello",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+
 }
 
 func TestContentUpdate(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Update(context.Background(), "atlassian/atlaskit", "README", nil)
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Post("/2.0/repositories/atlassian/atlaskit/src").
+		Reply(201).
+		Type("application/json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Data:    []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Update(
+		context.Background(),
+		"atlassian/atlaskit",
+		"test/hello",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentUpdateBadCommitID(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Post("/2.0/repositories/atlassian/atlaskit/src").
+		Reply(400).
+		Type("application/json").
+		File("testdata/content_update.json.fail")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Data:    []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
+		Sha:     "bad commit",
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	_, err := client.Contents.Update(
+		context.Background(),
+		"atlassian/atlaskit",
+		"test/hello",
+		params,
+	)
+	if err.Error() != "parents: Commit not found: 1a7eba6c-d4fe-47b7-b767-859abc660efc" {
+		t.Errorf("Expecting 'parents: Commit not found: 1a7eba6c-d4fe-47b7-b767-859abc660efc'")
 	}
 }
 
 func TestContentDelete(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Delete(context.Background(), "atlassian/atlaskit", "README", "master")
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Post("/2.0/repositories/atlassian/atlaskit/src").
+		Reply(201).
+		Type("application/json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Update(
+		context.Background(),
+		"atlassian/atlaskit",
+		"test/hello",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
 	}
 }
 
@@ -89,3 +240,29 @@ func TestContentList(t *testing.T) {
 		t.Log(diff)
 	}
 }
+
+func TestContentListWithUrlInput(t *testing.T) {
+	defer gock.Off()
+
+	mockNextPageUri := "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/master/packages/activity?pageLen=3&page=RPfL"
+
+	gock.New(mockNextPageUri).
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_list.json")
+
+	client, _ := New("https://api.bitbucket.org")
+	got, _, err := client.Contents.List(context.Background(), "atlassian/atlaskit", "packages/activity", "master", scm.ListOptions{URL: mockNextPageUri})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.ContentInfo{}
+	raw, _ := ioutil.ReadFile("testdata/content_list.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/bitbucket/git.go b/scm/driver/bitbucket/git.go
index 79b6cae29..df16afbcb 100644
--- a/scm/driver/bitbucket/git.go
+++ b/scm/driver/bitbucket/git.go
@@ -7,6 +7,7 @@ package bitbucket
 import (
 	"context"
 	"fmt"
+	"strings"
 	"time"
 
 	"github.com/drone/go-scm/scm"
@@ -16,6 +17,17 @@ type gitService struct {
 	client *wrapper
 }
 
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	path := fmt.Sprintf("2.0/repositories/%s/refs/branches", repo)
+	in := &createBranch{
+		Name: params.Name,
+		Target: target{
+			Hash: params.Sha,
+		},
+	}
+	return s.client.do(ctx, "POST", path, in, nil)
+}
+
 func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
 	path := fmt.Sprintf("2.0/repositories/%s/refs/branches/%s", repo, name)
 	out := new(branch)
@@ -24,7 +36,21 @@ func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Re
 }
 
 func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
-	path := fmt.Sprintf("2.0/repositories/%s/commit/%s", repo, ref)
+	// github and gitlab permit fetching a commit by sha
+	// or branch. This code emulates the github and gitlab
+	// behavior for bitbucket by fetching the commit sha
+	// for the branch and using in the subsequent API call.
+	if scm.IsHash(ref) == false {
+		if branch, _, err := s.FindBranch(ctx, repo, scm.TrimRef(ref)); err == nil {
+			ref = branch.Sha // replace ref with sha
+		}
+	}
+	var path string
+	if strings.Contains(ref, "/") {
+		path = fmt.Sprintf("2.0/repositories/%s/?at=%s", repo, ref)
+	} else {
+		path = fmt.Sprintf("2.0/repositories/%s/commit/%s", repo, ref)
+	}
 	out := new(commit)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
 	return convertCommit(out), res, err
@@ -44,6 +70,13 @@ func (s *gitService) ListBranches(ctx context.Context, repo string, opts scm.Lis
 	copyPagination(out.pagination, res)
 	return convertBranchList(out), res, err
 }
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	path := fmt.Sprintf("2.0/repositories/%s/refs/branches?%s", repo, encodeBranchListOptions(opts))
+	out := new(branches)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	copyPagination(out.pagination, res)
+	return convertBranchList(out), res, err
+}
 
 func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
 	path := fmt.Sprintf("2.0/repositories/%s/commits/%s?%s", repo, opts.Ref, encodeCommitListOptions(opts))
@@ -54,10 +87,19 @@ func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.Comm
 }
 
 func (s *gitService) ListTags(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// make page params only with 'pagelen' as there is a bug with 'page' param
+	opts.Page = 0
 	path := fmt.Sprintf("2.0/repositories/%s/refs/tags?%s", repo, encodeListOptions(opts))
+
 	out := new(branches)
 	res, err := s.client.do(ctx, "GET", path, nil, &out)
+
 	copyPagination(out.pagination, res)
+
+	if res != nil {
+		res.Page.Next = 0
+	}
+
 	return convertTagList(out), res, err
 }
 
@@ -70,7 +112,7 @@ func (s *gitService) ListChanges(ctx context.Context, repo, ref string, opts scm
 }
 
 func (s *gitService) CompareChanges(ctx context.Context, repo, source, target string, opts scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
-	path := fmt.Sprintf("2.0/repositories/%s/diffstat/%s..%s?%s", repo, source, target, encodeListOptions(opts))
+	path := fmt.Sprintf("2.0/repositories/%s/diffstat/%s..%s?%s", repo, target, source, encodeListOptions(opts))
 	out := new(diffstats)
 	res, err := s.client.do(ctx, "GET", path, nil, &out)
 	copyPagination(out.pagination, res)
@@ -85,6 +127,15 @@ type branch struct {
 	} `json:"target"`
 }
 
+type createBranch struct {
+	Name   string `json:"name"`
+	Target target `json:"target"`
+}
+
+type target struct {
+	Hash string `json:"hash"`
+}
+
 type commits struct {
 	pagination
 	Values []*commit `json:"values"`
@@ -190,12 +241,20 @@ func convertDiffstats(from *diffstats) []*scm.Change {
 }
 
 func convertDiffstat(from *diffstat) *scm.Change {
-	return &scm.Change{
+	response := &scm.Change{
 		Path:    from.New.Path,
 		Added:   from.Status == "added",
 		Renamed: from.Status == "renamed",
 		Deleted: from.Status == "removed",
 	}
+
+	if response.Renamed {
+		response.PrevFilePath = from.Old.Path
+	} else if response.Deleted {
+		response.Path = from.Old.Path
+	}
+
+	return response
 }
 
 func convertCommitList(from *commits) []*scm.Commit {
diff --git a/scm/driver/bitbucket/git_test.go b/scm/driver/bitbucket/git_test.go
index 7a15e8a83..912434bf1 100644
--- a/scm/driver/bitbucket/git_test.go
+++ b/scm/driver/bitbucket/git_test.go
@@ -41,6 +41,88 @@ func TestGitFindCommit(t *testing.T) {
 	}
 }
 
+func TestGitFindCommitForTagSlash(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories/atlassian/stash-example-plugin").
+		MatchParam("at", "ab/cd").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commit.json")
+
+	client, _ := New("https://api.bitbucket.org")
+	got, _, err := client.Git.FindCommit(context.Background(), "atlassian/stash-example-plugin", "ab/cd")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Commit)
+	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestGitFindCommitForBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories/atlassian/stash-example-plugin/commit/a6e5e7d797edf751cbd839d6bd4aef86c941eec9").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commit.json")
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories/atlassian/stash-example-plugin/refs/branches/master").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branch.json")
+
+	client, _ := New("https://api.bitbucket.org")
+	got, _, err := client.Git.FindCommit(context.Background(), "atlassian/stash-example-plugin", "master")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Commit)
+	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestGitCreateBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Post("/2.0/repositories/atlassian/stash-example-plugin/refs/branches").
+		Reply(201).
+		Type("application/json").
+		File("testdata/branch_create.json")
+
+	params := &scm.ReferenceInput{
+		Name: "yooo",
+		Sha:  "2e684d13a43afd86cb48ea36d9f40f43e791fae9",
+	}
+	client, _ := New("https://api.bitbucket.org")
+	res, err := client.Git.CreateBranch(context.Background(), "atlassian/stash-example-plugin", params)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
 func TestGitFindBranch(t *testing.T) {
 	defer gock.Off()
 
@@ -98,12 +180,13 @@ func TestGitListCommits(t *testing.T) {
 		Get("/2.0/repositories/atlassian/stash-example-plugin/commits/master").
 		MatchParam("page", "1").
 		MatchParam("pagelen", "30").
+		MatchParam("path", "LICENSE").
 		Reply(200).
 		Type("application/json").
 		File("testdata/commits.json")
 
 	client, _ := New("https://api.bitbucket.org")
-	got, res, err := client.Git.ListCommits(context.Background(), "atlassian/stash-example-plugin", scm.CommitListOptions{Ref: "master", Page: 1, Size: 30})
+	got, res, err := client.Git.ListCommits(context.Background(), "atlassian/stash-example-plugin", scm.CommitListOptions{Ref: "master", Page: 1, Size: 30, Path: "LICENSE"})
 	if err != nil {
 		t.Error(err)
 	}
@@ -149,19 +232,51 @@ func TestGitListBranches(t *testing.T) {
 	t.Run("Page", testPage(res))
 }
 
+func TestGitListBranchesV2(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories/atlassian/stash-example-plugin/refs").
+		MatchParam("q", "name~\\\"mast\\\"").
+		MatchParam("page", "1").
+		MatchParam("pagelen", "30").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branches_filter.json")
+
+	client, _ := New("https://api.bitbucket.org")
+	got, res, err := client.Git.ListBranchesV2(context.Background(), "atlassian/stash-example-plugin", scm.BranchListOptions{
+		SearchTerm:      "mast",
+		PageListOptions: scm.ListOptions{Page: 1, Size: 30},
+	})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches_filter.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Page", testPage(res))
+}
+
 func TestGitListTags(t *testing.T) {
 	defer gock.Off()
 
 	gock.New("https://api.bitbucket.org").
 		Get("/2.0/repositories/atlassian/atlaskit/refs/tags").
-		MatchParam("page", "1").
 		MatchParam("pagelen", "30").
 		Reply(200).
 		Type("application/json").
 		File("testdata/tags.json")
 
 	client, _ := New("https://api.bitbucket.org")
-	got, res, err := client.Git.ListTags(context.Background(), "atlassian/atlaskit", scm.ListOptions{Page: 1, Size: 30})
+	got, _, err := client.Git.ListTags(context.Background(), "atlassian/atlaskit", scm.ListOptions{Page: 1, Size: 30})
 	if err != nil {
 		t.Error(err)
 	}
@@ -175,7 +290,6 @@ func TestGitListTags(t *testing.T) {
 		t.Log(diff)
 	}
 
-	t.Run("Page", testPage(res))
 }
 
 func TestGitListChanges(t *testing.T) {
@@ -208,7 +322,7 @@ func TestGitCompareChanges(t *testing.T) {
 	defer gock.Off()
 
 	gock.New("https://api.bitbucket.org").
-		Get("/2.0/repositories/atlassian/atlaskit/diffstat/dec26e0fe887167743c2b7e36531dedfeb6cd478..425863f9dbe56d70c8dcdbf2e4e0805e85591fcc").
+		Get("/2.0/repositories/atlassian/atlaskit/diffstat/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc..dec26e0fe887167743c2b7e36531dedfeb6cd478").
 		MatchParam("page", "1").
 		MatchParam("pagelen", "30").
 		Reply(200).
diff --git a/scm/driver/bitbucket/milestone.go b/scm/driver/bitbucket/milestone.go
new file mode 100644
index 000000000..7c2624dc6
--- /dev/null
+++ b/scm/driver/bitbucket/milestone.go
@@ -0,0 +1,31 @@
+package bitbucket
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/bitbucket/org.go b/scm/driver/bitbucket/org.go
index e14ba594c..7efd0e2a3 100644
--- a/scm/driver/bitbucket/org.go
+++ b/scm/driver/bitbucket/org.go
@@ -16,7 +16,7 @@ type organizationService struct {
 }
 
 func (s *organizationService) Find(ctx context.Context, name string) (*scm.Organization, *scm.Response, error) {
-	path := fmt.Sprintf("2.0/teams/%s", name)
+	path := fmt.Sprintf("2.0/workspaces/%s", name)
 	out := new(organization)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
 	return convertOrganization(out), res, err
@@ -27,7 +27,7 @@ func (s *organizationService) FindMembership(ctx context.Context, name, username
 }
 
 func (s *organizationService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Organization, *scm.Response, error) {
-	path := fmt.Sprintf("2.0/teams?%s", encodeListRoleOptions(opts))
+	path := fmt.Sprintf("2.0/workspaces?%s", encodeListRoleOptions(opts))
 	out := new(organizationList)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
 	copyPagination(out.pagination, res)
@@ -48,7 +48,7 @@ type organizationList struct {
 }
 
 type organization struct {
-	Login string `json:"username"`
+	Login string `json:"slug"`
 }
 
 func convertOrganization(from *organization) *scm.Organization {
diff --git a/scm/driver/bitbucket/org_test.go b/scm/driver/bitbucket/org_test.go
index 0868f7b34..62f19c7df 100644
--- a/scm/driver/bitbucket/org_test.go
+++ b/scm/driver/bitbucket/org_test.go
@@ -20,7 +20,7 @@ func TestOrganizationFind(t *testing.T) {
 	defer gock.Off()
 
 	gock.New("https://api.bitbucket.org").
-		Get("/2.0/teams/atlassian").
+		Get("/2.0/workspaces/atlassian").
 		Reply(200).
 		Type("application/json").
 		File("testdata/team.json")
@@ -45,7 +45,7 @@ func TestOrganizationList(t *testing.T) {
 	defer gock.Off()
 
 	gock.New("https://api.bitbucket.org").
-		Get("/2.0/teams").
+		Get("/2.0/workspaces").
 		MatchParam("pagelen", "30").
 		MatchParam("page", "1").
 		Reply(200).
diff --git a/scm/driver/bitbucket/pr.go b/scm/driver/bitbucket/pr.go
index 854fa0803..c0282f714 100644
--- a/scm/driver/bitbucket/pr.go
+++ b/scm/driver/bitbucket/pr.go
@@ -13,7 +13,7 @@ import (
 )
 
 type pullService struct {
-	*issueService
+	client *wrapper
 }
 
 func (s *pullService) Find(ctx context.Context, repo string, number int) (*scm.PullRequest, *scm.Response, error) {
@@ -39,6 +39,14 @@ func (s *pullService) ListChanges(ctx context.Context, repo string, number int,
 	return convertDiffstats(out), res, err
 }
 
+func (s *pullService) ListCommits(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/commits?%s", repo, number, encodeListOptions(opts))
+	out := new(commits)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	copyPagination(out.pagination, res)
+	return convertCommitList(out), res, err
+}
+
 func (s *pullService) Merge(ctx context.Context, repo string, number int) (*scm.Response, error) {
 	path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/merge", repo, number)
 	res, err := s.client.do(ctx, "POST", path, nil, nil)
@@ -61,6 +69,27 @@ func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRe
 	return convertPullRequest(out), res, err
 }
 
+func (s *pullService) FindComment(ctx context.Context, repo string, index, id int) (*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *pullService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *pullService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("2.0/repositories/%s/pullrequests/%d/comments", repo, number)
+	in := &prCommentInput{}
+	in.Content.Raw = input.Body
+	out := new(prComment)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertPullRequestComment(out), res, err
+}
+
+func (s *pullService) DeleteComment(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
 type reference struct {
 	Commit struct {
 		Hash  string `json:"hash"`
@@ -100,6 +129,10 @@ type pr struct {
 		HTML   string `json:"html"`
 		Type   string `json:"type"`
 	} `json:"summary"`
+	MergeCommit struct {
+		Type string `json:"type"`
+		Hash string `json:"hash"`
+	} `json:"merge_commit"`
 	Source    reference `json:"source"`
 	State     string    `json:"state"`
 	Author    user      `json:"author"`
@@ -141,6 +174,7 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Title:  from.Title,
 		Body:   from.Description,
 		Sha:    from.Source.Commit.Hash,
+		Merge:  from.MergeCommit.Hash,
 		Source: from.Source.Branch.Name,
 		Target: from.Destination.Branch.Name,
 		Fork:   from.Source.Repository.FullName,
@@ -159,7 +193,7 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 			Sha:  from.Destination.Commit.Hash,
 		},
 		Author: scm.User{
-			Login:  from.Author.Nickname,
+			ID:     from.Author.AccountID,
 			Name:   from.Author.DisplayName,
 			Avatar: from.Author.Links.Avatar.Href,
 		},
@@ -167,3 +201,18 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Updated: from.UpdatedOn,
 	}
 }
+
+func convertPullRequestComment(from *prComment) *scm.Comment {
+	return &scm.Comment{
+		ID:   from.ID,
+		Body: from.Content.Raw,
+		Author: scm.User{
+			ID:     from.User.UUID,
+			Login:  from.User.Nickname,
+			Name:   from.User.DisplayName,
+			Avatar: from.User.Links.Avatar.Href,
+		},
+		Created: from.CreatedOn,
+		Updated: from.UpdatedOn,
+	}
+}
diff --git a/scm/driver/bitbucket/pr_test.go b/scm/driver/bitbucket/pr_test.go
index 90d8648d6..038ec1952 100644
--- a/scm/driver/bitbucket/pr_test.go
+++ b/scm/driver/bitbucket/pr_test.go
@@ -149,3 +149,79 @@ func TestPullCreate(t *testing.T) {
 		t.Log(diff)
 	}
 }
+func TestPullListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories/atlassian/atlaskit/pullrequests/1/commits").
+		MatchParam("pagelen", "30").
+		MatchParam("page", "1").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
+	client, _ := New("https://api.bitbucket.org")
+	got, _, err := client.PullRequests.ListCommits(context.Background(), "atlassian/atlaskit", 1, scm.ListOptions{Size: 30, Page: 1})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullRequestCommentFind(t *testing.T) {
+	_, _, err := NewDefault().PullRequests.FindComment(context.Background(), "", 0, 0)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestPullRequestListComments(t *testing.T) {
+	_, _, err := NewDefault().PullRequests.ListComments(context.Background(), "", 0, scm.ListOptions{})
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestPullRequestCreateComment(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Post("/2.0/repositories/atlassian/atlaskit/pullrequests/12").
+		Reply(201).
+		Type("application/json").
+		File("testdata/prcomment.json")
+
+	input := &scm.CommentInput{
+		Body: "Lovely comment",
+	}
+
+	client, _ := New("https://api.bitbucket.org")
+	got, _, err := client.PullRequests.CreateComment(context.Background(), "atlassian/atlaskit", 12, input)
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Comment)
+	raw, _ := ioutil.ReadFile("testdata/prcomment.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullRequestCommentDelete(t *testing.T) {
+	_, err := NewDefault().PullRequests.DeleteComment(context.Background(), "", 0, 0)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
diff --git a/scm/driver/bitbucket/release.go b/scm/driver/bitbucket/release.go
new file mode 100644
index 000000000..c38d0b40a
--- /dev/null
+++ b/scm/driver/bitbucket/release.go
@@ -0,0 +1,43 @@
+package bitbucket
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/bitbucket/repo.go b/scm/driver/bitbucket/repo.go
index cccae00d5..cc942c49a 100644
--- a/scm/driver/bitbucket/repo.go
+++ b/scm/driver/bitbucket/repo.go
@@ -105,6 +105,29 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
 	return convertRepositoryList(out), res, err
 }
 
+// ListV2 returns the user repository list based on the searchTerm passed.
+func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("2.0/repositories?%s", encodeRepoListOptions(opts))
+	if opts.ListOptions.URL != "" {
+		path = opts.ListOptions.URL
+	}
+	out := new(repositories)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	copyPagination(out.pagination, res)
+	return convertRepositoryList(out), res, err
+}
+
+func (s *repositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("2.0/repositories/%s?%s", namespace, encodeListRoleOptions(opts))
+	if opts.URL != "" {
+		path = opts.URL
+	}
+	out := new(repositories)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	copyPagination(out.pagination, res)
+	return convertRepositoryList(out), res, err
+}
+
 // ListHooks returns a list or repository hooks.
 func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
 	path := fmt.Sprintf("2.0/repositories/%s/hooks?%s", repo, encodeListOptions(opts))
diff --git a/scm/driver/bitbucket/repo_test.go b/scm/driver/bitbucket/repo_test.go
index 8b8efd79d..6c1f02b1b 100644
--- a/scm/driver/bitbucket/repo_test.go
+++ b/scm/driver/bitbucket/repo_test.go
@@ -136,6 +136,37 @@ func TestRepositoryList(t *testing.T) {
 	}
 }
 
+func TestRepositoryListV2(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/repositories").
+		MatchParam("q", "name~\\\"plugin1\\\"").
+		MatchParam("role", "member").
+		Reply(200).
+		Type("application/json").
+		File("testdata/repos_filter.json")
+
+	got := []*scm.Repository{}
+	opts := scm.RepoListOptions{RepoSearchTerm: scm.RepoSearchTerm{RepoName: "plugin1"}}
+	client, _ := New("https://api.bitbucket.org")
+
+	repos, _, err := client.Repositories.ListV2(context.Background(), opts)
+	if err != nil {
+		t.Error(err)
+	}
+	got = append(got, repos...)
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos_filter.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
 func TestStatusList(t *testing.T) {
 	defer gock.Off()
 
diff --git a/scm/driver/bitbucket/testdata/branch_create.json b/scm/driver/bitbucket/testdata/branch_create.json
new file mode 100644
index 000000000..ec47672a5
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/branch_create.json
@@ -0,0 +1,103 @@
+{
+    "name": "yooo",
+    "links": {
+        "commits": {
+            "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commits/yooo"
+        },
+        "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/refs/branches/yooo"
+        },
+        "html": {
+            "href": "https://bitbucket.org/tphoney/scm-test/branch/yooo"
+        }
+    },
+    "default_merge_strategy": "merge_commit",
+    "merge_strategies": [
+        "merge_commit",
+        "squash",
+        "fast_forward"
+    ],
+    "type": "branch",
+    "target": {
+        "hash": "2e684d13a43afd86cb48ea36d9f40f43e791fae9",
+        "repository": {
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test"
+                },
+                "html": {
+                    "href": "https://bitbucket.org/tphoney/scm-test"
+                },
+                "avatar": {
+                    "href": "https://bytebucket.org/ravatar/%7Bb918ca22-3472-4053-b234-c61deee57314%7D?ts=default"
+                }
+            },
+            "type": "repository",
+            "name": "scm-test",
+            "full_name": "tphoney/scm-test",
+            "uuid": "{b918ca22-3472-4053-b234-c61deee57314}"
+        },
+        "links": {
+            "self": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/2e684d13a43afd86cb48ea36d9f40f43e791fae9"
+            },
+            "comments": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/2e684d13a43afd86cb48ea36d9f40f43e791fae9/comments"
+            },
+            "patch": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/patch/2e684d13a43afd86cb48ea36d9f40f43e791fae9"
+            },
+            "html": {
+                "href": "https://bitbucket.org/tphoney/scm-test/commits/2e684d13a43afd86cb48ea36d9f40f43e791fae9"
+            },
+            "diff": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/diff/2e684d13a43afd86cb48ea36d9f40f43e791fae9"
+            },
+            "approve": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/2e684d13a43afd86cb48ea36d9f40f43e791fae9/approve"
+            },
+            "statuses": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/2e684d13a43afd86cb48ea36d9f40f43e791fae9/statuses"
+            }
+        },
+        "author": {
+            "raw": "TP Honey <thomas.honey@harness.io>",
+            "type": "author",
+            "user": {
+                "display_name": "TP Honey",
+                "uuid": "{1d020398-429a-4fb3-9339-7e2096b1ecaf}",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/users/%7B1d020398-429a-4fb3-9339-7e2096b1ecaf%7D"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/%7B1d020398-429a-4fb3-9339-7e2096b1ecaf%7D/"
+                    },
+                    "avatar": {
+                        "href": "https://secure.gravatar.com/avatar/cb9ef853b678d8a34ce0734e57a24f2f?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FTH-3.png"
+                    }
+                },
+                "nickname": "thomas.honey",
+                "type": "user",
+                "account_id": "60259ce77db80e006a2e0935"
+            }
+        },
+        "parents": [
+            {
+                "hash": "ba88d4dd3c98b442a77588ff1e623a16f2656a63",
+                "type": "commit",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/ba88d4dd3c98b442a77588ff1e623a16f2656a63"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/tphoney/scm-test/commits/ba88d4dd3c98b442a77588ff1e623a16f2656a63"
+                    }
+                }
+            }
+        ],
+        "date": "2021-04-01T10:38:26+00:00",
+        "message": "asdasd created online with Bitbucket",
+        "type": "commit"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/branches_filter.json b/scm/driver/bitbucket/testdata/branches_filter.json
new file mode 100644
index 000000000..122655ec4
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/branches_filter.json
@@ -0,0 +1,201 @@
+{
+  "pagelen": 30,
+  "values": [
+    {
+      "type": "branch",
+      "name": "master",
+      "links": {
+        "commits": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commits\/master"
+        },
+        "self": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/refs\/branches\/master"
+        },
+        "html": {
+          "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/branch\/master"
+        }
+      },
+      "target": {
+        "hash": "a6e5e7d797edf751cbd839d6bd4aef86c941eec9",
+        "repository": {
+          "links": {
+            "self": {
+              "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin"
+            },
+            "html": {
+              "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin"
+            },
+            "avatar": {
+              "href": "https:\/\/bytebucket.org\/ravatar\/%7B7dd600e6-0d9c-4801-b967-cb4cc17359ff%7D?ts=default"
+            }
+          },
+          "type": "repository",
+          "name": "stash-example-plugin",
+          "full_name": "atlassian\/stash-example-plugin",
+          "uuid": "{7dd600e6-0d9c-4801-b967-cb4cc17359ff}"
+        },
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "comments": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9\/comments"
+          },
+          "patch": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/patch\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "html": {
+            "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/commits\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "diff": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/diff\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "approve": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9\/approve"
+          },
+          "statuses": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9\/statuses"
+          }
+        },
+        "author": {
+          "raw": "Adam Ahmed <aahmed@atlassian.com>",
+          "type": "author",
+          "user": {
+            "username": "aahmed",
+            "display_name": "Adam Ahmed",
+            "account_id": "557057:74dc5efb-ffe7-49af-b427-6abc299bb3b9",
+            "links": {
+              "self": {
+                "href": "https:\/\/api.bitbucket.org\/2.0\/users\/aahmed"
+              },
+              "html": {
+                "href": "https:\/\/bitbucket.org\/aahmed\/"
+              },
+              "avatar": {
+                "href": "https:\/\/bitbucket.org\/account\/aahmed\/avatar\/32\/"
+              }
+            },
+            "type": "user",
+            "uuid": "{3d5de233-98d4-4138-b4af-8678fbb009ad}"
+          }
+        },
+        "parents": [
+          {
+            "hash": "5be6855032e171280a1acb860d7265c29f40487c",
+            "type": "commit",
+            "links": {
+              "self": {
+                "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/5be6855032e171280a1acb860d7265c29f40487c"
+              },
+              "html": {
+                "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/commits\/5be6855032e171280a1acb860d7265c29f40487c"
+              }
+            }
+          }
+        ],
+        "date": "2015-08-27T03:25:04+00:00",
+        "message": "Add Apache 2.0 License\n",
+        "type": "commit"
+      }
+    },
+    {
+      "type": "branch",
+      "name": "master-patch",
+      "links": {
+        "commits": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commits\/master"
+        },
+        "self": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/refs\/branches\/master"
+        },
+        "html": {
+          "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/branch\/master"
+        }
+      },
+      "target": {
+        "hash": "a6e5e7d797edf751cbd839d6bd4aef86c941eec8",
+        "repository": {
+          "links": {
+            "self": {
+              "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin"
+            },
+            "html": {
+              "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin"
+            },
+            "avatar": {
+              "href": "https:\/\/bytebucket.org\/ravatar\/%7B7dd600e6-0d9c-4801-b967-cb4cc17359ff%7D?ts=default"
+            }
+          },
+          "type": "repository",
+          "name": "stash-example-plugin",
+          "full_name": "atlassian\/stash-example-plugin",
+          "uuid": "{7dd600e6-0d9c-4801-b967-cb4cc17359ff}"
+        },
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "comments": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9\/comments"
+          },
+          "patch": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/patch\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "html": {
+            "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/commits\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "diff": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/diff\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+          },
+          "approve": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9\/approve"
+          },
+          "statuses": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/a6e5e7d797edf751cbd839d6bd4aef86c941eec9\/statuses"
+          }
+        },
+        "author": {
+          "raw": "Adam Ahmed <aahmed@atlassian.com>",
+          "type": "author",
+          "user": {
+            "username": "aahmed",
+            "display_name": "Adam Ahmed",
+            "account_id": "557057:74dc5efb-ffe7-49af-b427-6abc299bb3b9",
+            "links": {
+              "self": {
+                "href": "https:\/\/api.bitbucket.org\/2.0\/users\/aahmed"
+              },
+              "html": {
+                "href": "https:\/\/bitbucket.org\/aahmed\/"
+              },
+              "avatar": {
+                "href": "https:\/\/bitbucket.org\/account\/aahmed\/avatar\/32\/"
+              }
+            },
+            "type": "user",
+            "uuid": "{3d5de233-98d4-4138-b4af-8678fbb009ad}"
+          }
+        },
+        "parents": [
+          {
+            "hash": "5be6855032e171280a1acb860d7265c29f40487c",
+            "type": "commit",
+            "links": {
+              "self": {
+                "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin\/commit\/5be6855032e171280a1acb860d7265c29f40487c"
+              },
+              "html": {
+                "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/commits\/5be6855032e171280a1acb860d7265c29f40487c"
+              }
+            }
+          }
+        ],
+        "date": "2015-08-27T03:25:04+00:00",
+        "message": "Add Apache 2.0 License\n",
+        "type": "commit"
+      }
+    }
+  ],
+  "page": 1,
+  "next": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin\/refs\/branches?pagelen=30&page=2"
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/branches_filter.json.golden b/scm/driver/bitbucket/testdata/branches_filter.json.golden
new file mode 100644
index 000000000..79754a78f
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/branches_filter.json.golden
@@ -0,0 +1,12 @@
+[
+    {
+        "Name": "master",
+        "Path": "refs/heads/master",
+        "Sha": "a6e5e7d797edf751cbd839d6bd4aef86c941eec9"
+    },
+    {
+        "Name": "master-patch",
+        "Path": "refs/heads/master-patch",
+        "Sha": "a6e5e7d797edf751cbd839d6bd4aef86c941eec8"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/content.json b/scm/driver/bitbucket/testdata/content.json
new file mode 100644
index 000000000..8a4d8820e
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/content.json
@@ -0,0 +1,31 @@
+{
+    "mimetype": null,
+    "links": {
+        "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/src/0846a192175701903ddd9264ece31922d8437c1a/README.md"
+        },
+        "meta": {
+            "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/src/0846a192175701903ddd9264ece31922d8437c1a/README.md?format=meta"
+        },
+        "history": {
+            "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/filehistory/0846a192175701903ddd9264ece31922d8437c1a/README.md"
+        }
+    },
+    "escaped_path": "README.md",
+    "path": "README.md",
+    "commit": {
+        "type": "commit",
+        "hash": "0846a192175701903ddd9264ece31922d8437c1a",
+        "links": {
+            "self": {
+                "href": "https://api.bitbucket.org/2.0/repositories/tphoney/scm-test/commit/0846a192175701903ddd9264ece31922d8437c1a"
+            },
+            "html": {
+                "href": "https://bitbucket.org/tphoney/scm-test/commits/0846a192175701903ddd9264ece31922d8437c1a"
+            }
+        }
+    },
+    "attributes": [],
+    "type": "commit_file",
+    "size": 39
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/content.json.fail b/scm/driver/bitbucket/testdata/content.json.fail
new file mode 100644
index 000000000..5c12f59e2
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/content.json.fail
@@ -0,0 +1,4 @@
+{
+    "Path": "README",
+    "Data": "SEVMTE8gV09STEQK"
+}
diff --git a/scm/driver/bitbucket/testdata/content.json.golden b/scm/driver/bitbucket/testdata/content.json.golden
index b31f9dcdc..f7e307b61 100644
--- a/scm/driver/bitbucket/testdata/content.json.golden
+++ b/scm/driver/bitbucket/testdata/content.json.golden
@@ -1,4 +1,5 @@
 {
     "Path": "README",
-    "Data": "SEVMTE8gV09STEQK"
-}
\ No newline at end of file
+    "Data": "SEVMTE8gV09STEQK",
+    "Sha": "0846a192175701903ddd9264ece31922d8437c1a"
+}
diff --git a/scm/driver/bitbucket/testdata/content_fail.json b/scm/driver/bitbucket/testdata/content_fail.json
new file mode 100644
index 000000000..d8bf6879d
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/content_fail.json
@@ -0,0 +1,6 @@
+{
+    "type": "error",
+    "error": {
+        "message": "No such file or directory: README.md.asdas"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/content_list.json b/scm/driver/bitbucket/testdata/content_list.json
index 7d3e5b2ed..9ea2f3523 100644
--- a/scm/driver/bitbucket/testdata/content_list.json
+++ b/scm/driver/bitbucket/testdata/content_list.json
@@ -1 +1,277 @@
-{"pagelen":10,"values":[{"path":"packages/activity/build","type":"commit_directory","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/build/"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/build/?format=meta"}},"commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}}},{"path":"packages/activity/dist","type":"commit_directory","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/dist/"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/dist/?format=meta"}},"commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}}},{"path":"packages/activity/docs","type":"commit_directory","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/docs/"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/docs/?format=meta"}},"commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}}},{"path":"packages/activity/src","type":"commit_directory","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/src/"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/src/?format=meta"}},"commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}}},{"path":"packages/activity/test","type":"commit_directory","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/test/"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/test/?format=meta"}},"commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}}},{"mimetype":null,"links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/.npmignore"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/.npmignore?format=meta"},"history":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/.npmignore"}},"path":"packages/activity/.npmignore","commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}},"attributes":[],"type":"commit_file","size":10},{"mimetype":null,"links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/LICENSE"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/LICENSE?format=meta"},"history":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/LICENSE"}},"path":"packages/activity/LICENSE","commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}},"attributes":[],"type":"commit_file","size":558},{"mimetype":"text/vnd.trolltech.linguist","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/index.ts"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/index.ts?format=meta"},"history":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/index.ts"}},"path":"packages/activity/index.ts","commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}},"attributes":[],"type":"commit_file","size":494},{"mimetype":"application/json","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json?format=meta"},"history":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json"}},"path":"packages/activity/package.json","commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}},"attributes":[],"type":"commit_file","size":1914},{"mimetype":"text/vnd.trolltech.linguist","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json.d.ts"},"meta":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json.d.ts?format=meta"},"history":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json.d.ts"}},"path":"packages/activity/package.json.d.ts","commit":{"type":"commit","hash":"710db794f15bd187db9e7a7b952aac48ebac08bb","links":{"self":{"href":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"},"html":{"href":"https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"}}},"attributes":[],"type":"commit_file","size":61}],"page":1,"next":"https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/master/packages/activity?page=8xhd"}
\ No newline at end of file
+{
+    "pagelen": 10,
+    "values": [
+        {
+            "path": "packages/activity/build",
+            "type": "commit_directory",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/build/"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/build/?format=meta"
+                }
+            },
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            }
+        },
+        {
+            "path": "packages/activity/dist",
+            "type": "commit_directory",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/dist/"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/dist/?format=meta"
+                }
+            },
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            }
+        },
+        {
+            "path": "packages/activity/docs",
+            "type": "commit_directory",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/docs/"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/docs/?format=meta"
+                }
+            },
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            }
+        },
+        {
+            "path": "packages/activity/src",
+            "type": "commit_directory",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/src/"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/src/?format=meta"
+                }
+            },
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            }
+        },
+        {
+            "path": "packages/activity/test",
+            "type": "commit_directory",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/test/"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/test/?format=meta"
+                }
+            },
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            }
+        },
+        {
+            "mimetype": null,
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/.npmignore"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/.npmignore?format=meta"
+                },
+                "history": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/.npmignore"
+                }
+            },
+            "path": "packages/activity/.npmignore",
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            },
+            "attributes": [],
+            "type": "commit_file",
+            "size": 10
+        },
+        {
+            "mimetype": null,
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/LICENSE"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/LICENSE?format=meta"
+                },
+                "history": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/LICENSE"
+                }
+            },
+            "path": "packages/activity/LICENSE",
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            },
+            "attributes": [],
+            "type": "commit_file",
+            "size": 558
+        },
+        {
+            "mimetype": "text/vnd.trolltech.linguist",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/index.ts"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/index.ts?format=meta"
+                },
+                "history": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/index.ts"
+                }
+            },
+            "path": "packages/activity/index.ts",
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            },
+            "attributes": [],
+            "type": "commit_file",
+            "size": 494
+        },
+        {
+            "mimetype": "application/json",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json?format=meta"
+                },
+                "history": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json"
+                }
+            },
+            "path": "packages/activity/package.json",
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            },
+            "attributes": [],
+            "type": "commit_file",
+            "size": 1914
+        },
+        {
+            "mimetype": "text/vnd.trolltech.linguist",
+            "links": {
+                "self": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json.d.ts"
+                },
+                "meta": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json.d.ts?format=meta"
+                },
+                "history": {
+                    "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/filehistory/710db794f15bd187db9e7a7b952aac48ebac08bb/packages/activity/package.json.d.ts"
+                }
+            },
+            "path": "packages/activity/package.json.d.ts",
+            "commit": {
+                "type": "commit",
+                "hash": "710db794f15bd187db9e7a7b952aac48ebac08bb",
+                "links": {
+                    "self": {
+                        "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/commit/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    },
+                    "html": {
+                        "href": "https://bitbucket.org/atlassian/atlaskit/commits/710db794f15bd187db9e7a7b952aac48ebac08bb"
+                    }
+                }
+            },
+            "attributes": [],
+            "type": "commit_file",
+            "size": 61
+        }
+    ],
+    "page": 1,
+    "next": "https://api.bitbucket.org/2.0/repositories/atlassian/atlaskit/src/master/packages/activity?page=8xhd"
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/content_list.json.golden b/scm/driver/bitbucket/testdata/content_list.json.golden
index c0dc728b1..6ec39c44e 100644
--- a/scm/driver/bitbucket/testdata/content_list.json.golden
+++ b/scm/driver/bitbucket/testdata/content_list.json.golden
@@ -1,42 +1,52 @@
 [
   {
     "path": "packages/activity/build",
-    "kind": "directory"
+    "kind": "directory",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/dist",
-    "kind": "directory"
+    "kind": "directory",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/docs",
-    "kind": "directory"
+    "kind": "directory",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/src",
-    "kind": "directory"
+    "kind": "directory",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/test",
-    "kind": "directory"
+    "kind": "directory",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/.npmignore",
-    "kind": "file"
+    "kind": "file",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/LICENSE",
-    "kind": "file"
+    "kind": "file",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/index.ts",
-    "kind": "file"
+    "kind": "file",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/package.json",
-    "kind": "file"
+    "kind": "file",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   },
   {
     "path": "packages/activity/package.json.d.ts",
-    "kind": "file"
+    "kind": "file",
+    "Sha": "710db794f15bd187db9e7a7b952aac48ebac08bb"
   }
 ]
diff --git a/scm/driver/bitbucket/testdata/content_update.json.fail b/scm/driver/bitbucket/testdata/content_update.json.fail
new file mode 100644
index 000000000..085a52569
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/content_update.json.fail
@@ -0,0 +1,9 @@
+{
+    "type": "error",
+    "error": {
+        "fields": {
+            "parents": []
+        },
+        "message": "parents: Commit not found: 1a7eba6c-d4fe-47b7-b767-859abc660efc"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/diffstat.json b/scm/driver/bitbucket/testdata/diffstat.json
index 6d6030ad5..97a36f5bf 100644
--- a/scm/driver/bitbucket/testdata/diffstat.json
+++ b/scm/driver/bitbucket/testdata/diffstat.json
@@ -24,6 +24,54 @@
         }
       },
       "type": "diffstat"
+    },
+    {
+      "status": "renamed",
+      "old": {
+        "path": "old-folder/CONTRIBUTING.md",
+        "type": "commit_file",
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/atlaskit\/src\/dec26e0fe887167743c2b7e36531dedfeb6cd478\/CONTRIBUTING.md"
+          }
+        }
+      },
+      "lines_removed": 0,
+      "lines_added": 0,
+      "new": {
+        "path": "new-folder/CONTRIBUTING.md",
+        "type": "commit_file",
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/atlaskit\/src\/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc\/CONTRIBUTING.md"
+          }
+        }
+      },
+      "type": "diffstat"
+    },
+    {
+      "status": "removed",
+      "old": {
+        "path": "CONTRIBUTING.md",
+        "type": "commit_file",
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/atlaskit\/src\/dec26e0fe887167743c2b7e36531dedfeb6cd478\/CONTRIBUTING.md"
+          }
+        }
+      },
+      "lines_removed": 15,
+      "lines_added": 0,
+      "new": {
+        "path": "",
+        "type": "commit_file",
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/atlaskit\/src\/425863f9dbe56d70c8dcdbf2e4e0805e85591fcc\/CONTRIBUTING.md"
+          }
+        }
+      },
+      "type": "diffstat"
     }
   ],
   "page": 1,
diff --git a/scm/driver/bitbucket/testdata/diffstat.json.golden b/scm/driver/bitbucket/testdata/diffstat.json.golden
index 168e87d94..03fecb3ff 100644
--- a/scm/driver/bitbucket/testdata/diffstat.json.golden
+++ b/scm/driver/bitbucket/testdata/diffstat.json.golden
@@ -4,5 +4,18 @@
         "Added": false,
         "Renamed": false,
         "Deleted": false
+    },
+    {
+        "Path": "new-folder/CONTRIBUTING.md",
+        "Added": false,
+        "Renamed": true,
+        "Deleted": false,
+        "PrevFilePath": "old-folder/CONTRIBUTING.md"
+    },
+    {
+        "Path": "CONTRIBUTING.md",
+        "Added": false,
+        "Renamed": false,
+        "Deleted": true
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/pr.json.golden b/scm/driver/bitbucket/testdata/pr.json.golden
index 8132df37f..1ed8a643f 100644
--- a/scm/driver/bitbucket/testdata/pr.json.golden
+++ b/scm/driver/bitbucket/testdata/pr.json.golden
@@ -22,7 +22,7 @@
       "Name": "Lachlan-Vass/ios-date-picker-component-duplicate-marc-1579222909688"
     },
     "Author": {
-      "Login": "Lachlan",
+      "ID": "5c7c7b1a0b79db7c3e33eca2",
       "Name": "Lachlan Vass",
       "Avatar": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/5c7c7b1a0b79db7c3e33eca2/6b6b8178-0da0-4a37-b0dd-f8b5e3628eaa/128"
     },
diff --git a/scm/driver/bitbucket/testdata/prcomment.json b/scm/driver/bitbucket/testdata/prcomment.json
new file mode 100644
index 000000000..953b2d158
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/prcomment.json
@@ -0,0 +1,52 @@
+{
+    "id": 419169807,
+    "created_on": "2023-08-14T11:38:53.460132+00:00",
+    "updated_on": "2023-08-14T11:38:53.460205+00:00",
+    "content": {
+        "type": "rendered",
+        "raw": "Lovely comment",
+        "markup": "markdown",
+        "html": "<p>Lovely comment<\/p>"
+    },
+    "user": {
+        "display_name": "Brian Jacobson",
+        "links": {
+            "self": {
+                "href": "https:\/\/bitbucket.org\/!api\/2.0\/users\/%7B6dff94-1b8b-4f62-b37f-3069e13dfdf33e%7D"
+            },
+            "avatar": {
+                "href": "http:\/\/localhost:3000\/avatars\/1"
+            },
+            "html": {
+                "href": "https:\/\/bitbucket.org\/%7B6dff94-1b8b-4f62-b37f-3069e13dfdf33e%7D\/"
+            }
+        },
+        "type": "user",
+        "uuid": "{6b408a94-1b8b-4f62-b37f-3069e13bc33e}",
+        "account_id": "60259ce8164527007100d945",
+        "nickname": "brian.jacobson"
+    },
+    "deleted": false,
+    "type": "pullrequest_comment",
+    "links": {
+        "self": {
+            "href": "https:\/\/bitbucket.org\/!api\/2.0\/repositories\/brianharness\/test\/pullrequests\/3\/comments\/419169807"
+        },
+        "html": {
+            "href": "https:\/\/bitbucket.org\/brianharness\/test\/pull-requests\/3\/_\/diff#comment-419169807"
+        }
+    },
+    "pullrequest": {
+        "type": "pullrequest",
+        "id": 3,
+        "title": "README.md edited online with Bitbucket",
+        "links": {
+            "self": {
+                "href": "https:\/\/bitbucket.org\/!api\/2.0\/repositories\/brianharness\/test\/pullrequests\/3"
+            },
+            "html": {
+                "href": "https:\/\/bitbucket.org\/brianharness\/test\/pull-requests\/3"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/prcomment.json.golden b/scm/driver/bitbucket/testdata/prcomment.json.golden
new file mode 100644
index 000000000..e6dea457a
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/prcomment.json.golden
@@ -0,0 +1,12 @@
+{
+    "ID": 419169807,
+    "Body": "Lovely comment",
+    "Author": {
+        "ID": "{6b408a94-1b8b-4f62-b37f-3069e13bc33e}",
+        "Login": "brian.jacobson",
+        "Name": "Brian Jacobson",
+        "Avatar": "http://localhost:3000/avatars/1"
+    },
+    "Created": "2023-08-14T11:38:53.460132+00:00",
+    "Updated": "2023-08-14T11:38:53.460205+00:00"
+}
diff --git a/scm/driver/bitbucket/testdata/prs.json.golden b/scm/driver/bitbucket/testdata/prs.json.golden
index 29cafe903..90a246041 100644
--- a/scm/driver/bitbucket/testdata/prs.json.golden
+++ b/scm/driver/bitbucket/testdata/prs.json.golden
@@ -23,7 +23,7 @@
       "Name": "Lachlan-Vass/ios-date-picker-component-duplicate-marc-1579222909688"
     },
     "Author": {
-      "Login": "Lachlan",
+      "ID": "5c7c7b1a0b79db7c3e33eca2",
       "Name": "Lachlan Vass",
       "Avatar": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/5c7c7b1a0b79db7c3e33eca2/6b6b8178-0da0-4a37-b0dd-f8b5e3628eaa/128"
     },
diff --git a/scm/driver/bitbucket/testdata/repos_filter.json b/scm/driver/bitbucket/testdata/repos_filter.json
new file mode 100644
index 000000000..df610e1f4
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/repos_filter.json
@@ -0,0 +1,110 @@
+{
+  "pagelen": 1,
+  "values": [
+    {
+      "scm": "git",
+      "website": "",
+      "has_wiki": false,
+      "uuid": "{7dd600e6-0d9c-4801-b967-cb4cc17359ff}",
+      "links": {
+        "watchers": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/watchers"
+        },
+        "branches": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/refs\/branches"
+        },
+        "tags": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/refs\/tags"
+        },
+        "commits": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/commits"
+        },
+        "clone": [
+          {
+            "href": "https:\/\/brydzewski@bitbucket.org\/atlassian\/stash-example-plugin1.git",
+            "name": "https"
+          },
+          {
+            "href": "git@bitbucket.org:atlassian\/stash-example-plugin1.git",
+            "name": "ssh"
+          }
+        ],
+        "self": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1"
+        },
+        "source": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/src"
+        },
+        "html": {
+          "href": "https:\/\/bitbucket.org\/atlassian\/stash-example-plugin1"
+        },
+        "avatar": {
+          "href": "https:\/\/bytebucket.org\/ravatar\/%7B7dd600e6-0d9c-4801-b967-cb4cc17359ff%7D?ts=default"
+        },
+        "hooks": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/hooks"
+        },
+        "forks": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/forks"
+        },
+        "downloads": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/downloads"
+        },
+        "pullrequests": {
+          "href": "https:\/\/api.bitbucket.org\/2.0\/repositories\/atlassian\/stash-example-plugin1\/pullrequests"
+        }
+      },
+      "fork_policy": "allow_forks",
+      "name": "stash-example-plugin1",
+      "project": {
+        "key": "PROJ",
+        "type": "project",
+        "uuid": "{8b56daff-dbc7-4cae-a7a3-1228c526906b}",
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/teams\/atlassian\/projects\/PROJ"
+          },
+          "html": {
+            "href": "https:\/\/bitbucket.org\/account\/user\/atlassian\/projects\/PROJ"
+          },
+          "avatar": {
+            "href": "https:\/\/bitbucket.org\/account\/user\/atlassian\/projects\/PROJ\/avatar\/32"
+          }
+        },
+        "name": "Project: Atlassian"
+      },
+      "language": "",
+      "created_on": "2013-04-15T03:05:05.595458+00:00",
+      "mainbranch": {
+        "type": "branch",
+        "name": "master"
+      },
+      "full_name": "atlassian\/stash-example-plugin1",
+      "has_issues": false,
+      "owner": {
+        "username": "atlassian",
+        "display_name": "Atlassian",
+        "type": "team",
+        "uuid": "{02b941e3-cfaa-40f9-9a58-cec53e20bdc3}",
+        "links": {
+          "self": {
+            "href": "https:\/\/api.bitbucket.org\/2.0\/teams\/atlassian"
+          },
+          "html": {
+            "href": "https:\/\/bitbucket.org\/atlassian\/"
+          },
+          "avatar": {
+            "href": "https:\/\/bitbucket.org\/account\/atlassian\/avatar\/32\/"
+          }
+        }
+      },
+      "updated_on": "2018-04-01T16:36:35.970175+00:00",
+      "size": 1116345,
+      "type": "repository",
+      "slug": "stash-example-plugin1",
+      "is_private": true,
+      "description": "Examples on how to decorate various pages around Stash."
+    }
+  ],
+  "next": "https:\/\/api.bitbucket.org\/2.0\/repositories?pagelen=1&after=PLACEHOLDER&role=member"
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/repos_filter.json.golden b/scm/driver/bitbucket/testdata/repos_filter.json.golden
new file mode 100644
index 000000000..cf1ace3ba
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/repos_filter.json.golden
@@ -0,0 +1,15 @@
+[
+    {
+        "ID": "{7dd600e6-0d9c-4801-b967-cb4cc17359ff}",
+        "Namespace": "atlassian",
+        "Name": "stash-example-plugin1",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "https://bitbucket.org/atlassian/stash-example-plugin1.git",
+        "CloneSSH": "git@bitbucket.org:atlassian/stash-example-plugin1.git",
+        "Link": "https://bitbucket.org/atlassian/stash-example-plugin1",
+        "Created": "2013-04-15T03:05:05.595458Z",
+        "Updated": "2018-04-01T16:36:35.970175Z"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/tags.json.golden b/scm/driver/bitbucket/testdata/tags.json.golden
index 8cb6251e0..0e0e95ed0 100644
--- a/scm/driver/bitbucket/testdata/tags.json.golden
+++ b/scm/driver/bitbucket/testdata/tags.json.golden
@@ -1,7 +1,7 @@
 [
     {
-        "Name": "@atlaskit/activity@1.0.3",
-        "Path": "refs/tags/@atlaskit/activity@1.0.3",
+        "Name": "@atlaskit\/activity@1.0.3",
+        "Path": "refs/tags/@atlaskit\/activity@1.0.3",
         "Sha": "ceb01356c3f062579bdfeb15bc53fe151b9e00f0"
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/team.json b/scm/driver/bitbucket/testdata/team.json
index 929b2595f..97a446fad 100644
--- a/scm/driver/bitbucket/testdata/team.json
+++ b/scm/driver/bitbucket/testdata/team.json
@@ -1,17 +1,37 @@
 {
-  "username": "atlassian",
-  "type": "team",
-  "display_name": "Atlassian",
   "uuid": "{02b941e3-cfaa-40f9-9a58-cec53e20bdc3}",
   "links": {
+    "owners": {
+      "href": "https://api.bitbucket.org/2.0/workspaces/atlassian/members?q=permission%3D%22owner%22"
+    },
+    "hooks": {
+      "href": "https://api.bitbucket.org/2.0/workspaces/atlassian/hooks"
+    },
     "self": {
-      "href": "https:\/\/api.bitbucket.org\/2.0\/teams\/atlassian"
+      "href": "https://api.bitbucket.org/2.0/workspaces/atlassian"
+    },
+    "repositories": {
+      "href": "https://api.bitbucket.org/2.0/repositories/atlassian"
     },
     "html": {
-      "href": "https:\/\/bitbucket.org\/atlassian\/"
+      "href": "https://bitbucket.org/atlassian/"
     },
     "avatar": {
-      "href": "https:\/\/bitbucket.org\/account\/atlassian\/avatar\/32\/"
+      "href": "https://bitbucket.org/workspaces/atlassian/avatar/?ts=1612327398"
+    },
+    "members": {
+      "href": "https://api.bitbucket.org/2.0/workspaces/atlassian/members"
+    },
+    "projects": {
+      "href": "https://api.bitbucket.org/2.0/workspaces/atlassian/projects"
+    },
+    "snippets": {
+      "href": "https://api.bitbucket.org/2.0/snippets/atlassian"
     }
-  }
+  },
+  "created_on": "2018-11-29T02:26:39.297476+00:00",
+  "type": "workspace",
+  "slug": "atlassian",
+  "is_private": true,
+  "name": "Atlassian"
 }
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/teams.json b/scm/driver/bitbucket/testdata/teams.json
index b767565d1..3b47bbf52 100644
--- a/scm/driver/bitbucket/testdata/teams.json
+++ b/scm/driver/bitbucket/testdata/teams.json
@@ -2,9 +2,10 @@
   "pagelen": 30,
   "values": [
     {
-      "username": "atlassian",
+      "slug": "atlassian",
       "website": null,
-      "display_name": "Atlassian",
+      "is_private": false,
+      "name": "Atlassian",
       "uuid": "{d5bb8c49-f033-4498-910e-7e4b546b04ee}",
       "links": {
         "hooks": {
@@ -39,8 +40,7 @@
         }
       },
       "created_on": "2012-11-08T19:05:39.080491+00:00",
-      "location": null,
-      "type": "team"
+      "type": "workspace"
     }
   ],
   "page": 1,
diff --git a/scm/driver/bitbucket/testdata/userEmail.json b/scm/driver/bitbucket/testdata/userEmail.json
new file mode 100644
index 000000000..80e1c227a
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/userEmail.json
@@ -0,0 +1,8 @@
+{
+  "Values": [
+    {
+      "email": "test@harness.io",
+      "is_primary": true
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_created.json b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_created.json
new file mode 100644
index 000000000..02c506c67
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_created.json
@@ -0,0 +1,175 @@
+{
+    "repository": {
+      "type": "repository",
+      "full_name": "automationtestharness/testrepo",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo"
+        },
+        "html": {
+          "href": "https://bitbucket.org/automationtestharness/testrepo"
+        },
+        "avatar": {
+          "href": "https://bytebucket.org/ravatar/%7B2b922c6d-3824-4e91-a7be-90c8b8392f84%7D?ts=default"
+        }
+      },
+      "name": "testRepo",
+      "scm": "git",
+      "website": null,
+      "owner": {
+        "display_name": "Automationtestharness",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/workspaces/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D"
+          },
+          "avatar": {
+            "href": "https://bitbucket.org/account/automationtestharness/avatar/"
+          },
+          "html": {
+            "href": "https://bitbucket.org/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D/"
+          }
+        },
+        "type": "team",
+        "uuid": "{612f7b18-b167-4bff-8ee9-d43570117ff1}",
+        "username": "automationtestharness"
+      },
+      "workspace": {
+        "type": "workspace",
+        "uuid": "{612f7b18-b167-4bff-8ee9-d43570117ff1}",
+        "name": "Automationtestharness",
+        "slug": "automationtestharness",
+        "links": {
+          "avatar": {
+            "href": "https://bitbucket.org/workspaces/automationtestharness/avatar/?ts=1740994558"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/"
+          },
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/workspaces/automationtestharness"
+          }
+        }
+      },
+      "is_private": true,
+      "project": {
+        "type": "project",
+        "key": "TEST",
+        "uuid": "{af6d7f47-6ce6-48b6-9beb-d5ad6b674ba0}",
+        "name": "test",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/workspaces/automationtestharness/projects/TEST"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/workspace/projects/TEST"
+          },
+          "avatar": {
+            "href": "https://bitbucket.org/automationtestharness/workspace/projects/TEST/avatar/32?ts=1738907820"
+          }
+        }
+      },
+      "uuid": "{2b922c6d-3824-4e91-a7be-90c8b8392f84}",
+      "parent": null
+    },
+    "actor": {
+      "display_name": "Automationtestharness",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/workspaces/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D"
+        },
+        "avatar": {
+          "href": "https://bitbucket.org/account/automationtestharness/avatar/"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D/"
+        }
+      },
+      "type": "team",
+      "uuid": "{612f7b18-b167-4bff-8ee9-d43570117ff1}",
+      "username": "automationtestharness"
+    },
+    "commit_status": {
+      "key": "3343801",
+      "type": "build",
+      "state": "INPROGRESS",
+      "name": "Pipeline #4 for main",
+      "refname": "main",
+      "commit": {
+        "type": "commit",
+        "hash": "bdf6fac0519ffd1562461eb82e018ff62efffbc9",
+        "date": "2025-03-24T08:48:15+00:00",
+        "author": {
+          "type": "author",
+          "raw": "Shivam Negi <shivamnegi94@gmail.com>",
+          "user": {
+            "display_name": "Shivam Negi",
+            "links": {
+              "self": {
+                "href": "https://api.bitbucket.org/2.0/users/%7Bce7d5dfc-83c9-4878-beaf-7b91610e104a%7D"
+              },
+              "avatar": {
+                "href": "https://secure.gravatar.com/avatar/39b64ab2df0f61fcce00db05f94c600b?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FSN-2.png"
+              },
+              "html": {
+                "href": "https://bitbucket.org/%7Bce7d5dfc-83c9-4878-beaf-7b91610e104a%7D/"
+              }
+            },
+            "type": "user",
+            "uuid": "{ce7d5dfc-83c9-4878-beaf-7b91610e104a}",
+            "account_id": "601ba1bdb7bda90068ed5d1b",
+            "nickname": "Shivam Negi"
+          }
+        },
+        "message": "Merged in Shivam-Negi/testyaml-edited-online-with-bitbucket-1742806076994 (pull request #13)\n\ntest.yaml edited online with Bitbucket\n",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/testrepo/commits/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+          },
+          "diff": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/diff/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+          },
+          "approve": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/approve"
+          },
+          "comments": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/comments"
+          },
+          "statuses": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/statuses"
+          }
+        }
+      },
+      "url": "https://bitbucket.org/automationtestharness/testrepo/addon/pipelines/home#!/results/4",
+      "repository": {
+        "type": "repository",
+        "full_name": "automationtestharness/testrepo",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/testrepo"
+          },
+          "avatar": {
+            "href": "https://bytebucket.org/ravatar/%7B2b922c6d-3824-4e91-a7be-90c8b8392f84%7D?ts=default"
+          }
+        },
+        "name": "testRepo",
+        "uuid": "{2b922c6d-3824-4e91-a7be-90c8b8392f84}"
+      },
+      "description": "",
+      "created_on": "2025-03-24T08:48:56.572438+00:00",
+      "updated_on": "2025-03-24T08:48:56.572450+00:00",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/statuses/build/3343801"
+        },
+        "commit": {
+          "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+        }
+      }
+    }
+  }
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_created.json.golden b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_created.json.golden
new file mode 100644
index 000000000..b0e6c2a38
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_created.json.golden
@@ -0,0 +1,39 @@
+{
+  "Repo": {
+    "ID": "{2b922c6d-3824-4e91-a7be-90c8b8392f84}",
+    "Namespace": "automationtestharness",
+    "Name": "testrepo",
+    "Clone": "https://bitbucket.org/automationtestharness/testrepo",
+    "CloneSSH": "",
+    "Branch": "main",
+    "Private": true
+  },
+  "Commit": {
+    "Sha": "bdf6fac0519ffd1562461eb82e018ff62efffbc9",
+    "Message": "Merged in Shivam-Negi/testyaml-edited-online-with-bitbucket-1742806076994 (pull request #13)\n\ntest.yaml edited online with Bitbucket\n",
+    "Author": {
+      "Name": "Shivam Negi",
+      "Email": "shivamnegi94@gmail.com",
+      "Avatar": "https://secure.gravatar.com/avatar/39b64ab2df0f61fcce00db05f94c600b?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FSN-2.png"
+    },
+    "Committer": {
+      "Name": "Shivam Negi",
+      "Email": "shivamnegi94@gmail.com",
+      "Avatar": "https://secure.gravatar.com/avatar/39b64ab2df0f61fcce00db05f94c600b?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FSN-2.png"
+    },
+    "Link": "https://bitbucket.org/automationtestharness/testrepo/commits/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+  },
+  "Execution": {
+    "Number": 4,
+    "Status": "running",
+    "Created": "2025-03-24T08:48:56.572438+00:00",
+    "URL": "https://bitbucket.org/automationtestharness/testrepo/addon/pipelines/home#!/results/4"
+  },
+  "Sender": {
+    "Login": "automationtestharness",
+    "Name": "Automationtestharness",
+    "Email": "",
+    "Avatar": "https://bitbucket.org/account/automationtestharness/avatar/",
+    "ID": "{612f7b18-b167-4bff-8ee9-d43570117ff1}"
+  }
+}
diff --git a/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_update.json b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_update.json
new file mode 100644
index 000000000..10655dd05
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_update.json
@@ -0,0 +1,175 @@
+{
+    "repository": {
+      "type": "repository",
+      "full_name": "automationtestharness/testrepo",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo"
+        },
+        "html": {
+          "href": "https://bitbucket.org/automationtestharness/testrepo"
+        },
+        "avatar": {
+          "href": "https://bytebucket.org/ravatar/%7B2b922c6d-3824-4e91-a7be-90c8b8392f84%7D?ts=default"
+        }
+      },
+      "name": "testRepo",
+      "scm": "git",
+      "website": null,
+      "owner": {
+        "display_name": "Automationtestharness",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/workspaces/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D"
+          },
+          "avatar": {
+            "href": "https://bitbucket.org/account/automationtestharness/avatar/"
+          },
+          "html": {
+            "href": "https://bitbucket.org/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D/"
+          }
+        },
+        "type": "team",
+        "uuid": "{612f7b18-b167-4bff-8ee9-d43570117ff1}",
+        "username": "automationtestharness"
+      },
+      "workspace": {
+        "type": "workspace",
+        "uuid": "{612f7b18-b167-4bff-8ee9-d43570117ff1}",
+        "name": "Automationtestharness",
+        "slug": "automationtestharness",
+        "links": {
+          "avatar": {
+            "href": "https://bitbucket.org/workspaces/automationtestharness/avatar/?ts=1740994558"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/"
+          },
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/workspaces/automationtestharness"
+          }
+        }
+      },
+      "is_private": true,
+      "project": {
+        "type": "project",
+        "key": "TEST",
+        "uuid": "{af6d7f47-6ce6-48b6-9beb-d5ad6b674ba0}",
+        "name": "test",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/workspaces/automationtestharness/projects/TEST"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/workspace/projects/TEST"
+          },
+          "avatar": {
+            "href": "https://bitbucket.org/automationtestharness/workspace/projects/TEST/avatar/32?ts=1738907820"
+          }
+        }
+      },
+      "uuid": "{2b922c6d-3824-4e91-a7be-90c8b8392f84}",
+      "parent": null
+    },
+    "actor": {
+      "display_name": "Automationtestharness",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/workspaces/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D"
+        },
+        "avatar": {
+          "href": "https://bitbucket.org/account/automationtestharness/avatar/"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B612f7b18-b167-4bff-8ee9-d43570117ff1%7D/"
+        }
+      },
+      "type": "team",
+      "uuid": "{612f7b18-b167-4bff-8ee9-d43570117ff1}",
+      "username": "automationtestharness"
+    },
+    "commit_status": {
+      "key": "3343801",
+      "type": "build",
+      "state": "SUCCESSFUL",
+      "name": "Pipeline #4 for main",
+      "refname": "main",
+      "commit": {
+        "type": "commit",
+        "hash": "bdf6fac0519ffd1562461eb82e018ff62efffbc9",
+        "date": "2025-03-24T08:48:15+00:00",
+        "author": {
+          "type": "author",
+          "raw": "Shivam Negi <shivamnegi94@gmail.com>",
+          "user": {
+            "display_name": "Shivam Negi",
+            "links": {
+              "self": {
+                "href": "https://api.bitbucket.org/2.0/users/%7Bce7d5dfc-83c9-4878-beaf-7b91610e104a%7D"
+              },
+              "avatar": {
+                "href": "https://secure.gravatar.com/avatar/39b64ab2df0f61fcce00db05f94c600b?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FSN-2.png"
+              },
+              "html": {
+                "href": "https://bitbucket.org/%7Bce7d5dfc-83c9-4878-beaf-7b91610e104a%7D/"
+              }
+            },
+            "type": "user",
+            "uuid": "{ce7d5dfc-83c9-4878-beaf-7b91610e104a}",
+            "account_id": "601ba1bdb7bda90068ed5d1b",
+            "nickname": "Shivam Negi"
+          }
+        },
+        "message": "Merged in Shivam-Negi/testyaml-edited-online-with-bitbucket-1742806076994 (pull request #13)\n\ntest.yaml edited online with Bitbucket\n",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/testrepo/commits/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+          },
+          "diff": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/diff/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+          },
+          "approve": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/approve"
+          },
+          "comments": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/comments"
+          },
+          "statuses": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/statuses"
+          }
+        }
+      },
+      "url": "https://bitbucket.org/automationtestharness/testrepo/addon/pipelines/home#!/results/4",
+      "repository": {
+        "type": "repository",
+        "full_name": "automationtestharness/testrepo",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo"
+          },
+          "html": {
+            "href": "https://bitbucket.org/automationtestharness/testrepo"
+          },
+          "avatar": {
+            "href": "https://bytebucket.org/ravatar/%7B2b922c6d-3824-4e91-a7be-90c8b8392f84%7D?ts=default"
+          }
+        },
+        "name": "testRepo",
+        "uuid": "{2b922c6d-3824-4e91-a7be-90c8b8392f84}"
+      },
+      "description": "",
+      "created_on": "2025-03-24T08:48:56.572438+00:00",
+      "updated_on": "2025-03-24T08:49:42.838984+00:00",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9/statuses/build/3343801"
+        },
+        "commit": {
+          "href": "https://api.bitbucket.org/2.0/repositories/automationtestharness/testrepo/commit/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+        }
+      }
+    }
+  }
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_update.json.golden b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_update.json.golden
new file mode 100644
index 000000000..1b0b7623f
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pipeline_hook_update.json.golden
@@ -0,0 +1,39 @@
+{
+  "Repo": {
+    "ID": "{2b922c6d-3824-4e91-a7be-90c8b8392f84}",
+    "Namespace": "automationtestharness",
+    "Name": "testrepo",
+    "Clone": "https://bitbucket.org/automationtestharness/testrepo",
+    "CloneSSH": "",
+    "Branch": "main",
+    "Private": true
+  },
+  "Commit": {
+    "Sha": "bdf6fac0519ffd1562461eb82e018ff62efffbc9",
+    "Message": "Merged in Shivam-Negi/testyaml-edited-online-with-bitbucket-1742806076994 (pull request #13)\n\ntest.yaml edited online with Bitbucket\n",
+    "Author": {
+      "Name": "Shivam Negi",
+      "Email": "shivamnegi94@gmail.com",
+      "Avatar": "https://secure.gravatar.com/avatar/39b64ab2df0f61fcce00db05f94c600b?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FSN-2.png"
+    },
+    "Committer": {
+      "Name": "Shivam Negi",
+      "Email": "shivamnegi94@gmail.com",
+      "Avatar": "https://secure.gravatar.com/avatar/39b64ab2df0f61fcce00db05f94c600b?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FSN-2.png"
+    },
+    "Link": "https://bitbucket.org/automationtestharness/testrepo/commits/bdf6fac0519ffd1562461eb82e018ff62efffbc9"
+  },
+  "Execution": {
+    "Number": 4,
+    "Status": "success",
+    "Created": "2025-03-24T08:48:56.572438+00:00",
+    "URL": "https://bitbucket.org/automationtestharness/testrepo/addon/pipelines/home#!/results/4"
+  },
+  "Sender": {
+    "Login": "automationtestharness",
+    "Name": "Automationtestharness",
+    "Email": "",
+    "Avatar": "https://bitbucket.org/account/automationtestharness/avatar/",
+    "ID": "{612f7b18-b167-4bff-8ee9-d43570117ff1}"
+  }
+}
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_comment_created.json b/scm/driver/bitbucket/testdata/webhooks/pr_comment_created.json
new file mode 100644
index 000000000..b13056f91
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_comment_created.json
@@ -0,0 +1,331 @@
+{
+  "comment": {
+    "links": {
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/comments/311512047"
+      },
+      "html": {
+        "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1/_/diff#comment-311512047"
+      }
+    },
+    "deleted": false,
+    "pullrequest": {
+      "type": "pullrequest",
+      "id": 1,
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1"
+        },
+        "html": {
+          "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1"
+        }
+      },
+      "title": "Update pom.xml"
+    },
+    "content": {
+      "raw": "test comment",
+      "markup": "markdown",
+      "html": "<p>test comment</p>",
+      "type": "rendered"
+    },
+    "created_on": "2022-06-23T22:10:09.939925+00:00",
+    "user": {
+      "display_name": "Rutvij Mehta",
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+        },
+        "avatar": {
+          "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+        }
+      },
+      "type": "user",
+      "nickname": "Rutvij Mehta",
+      "account_id": "624de6f6fd5e450070486936"
+    },
+    "updated_on": "2022-06-23T22:10:09.939978+00:00",
+    "type": "pullrequest_comment",
+    "id": 311512047
+  },
+  "pullrequest": {
+    "rendered": {
+      "description": {
+        "raw": "Test",
+        "markup": "markdown",
+        "html": "<p>Test</p>",
+        "type": "rendered"
+      },
+      "title": {
+        "raw": "Update pom.xml",
+        "markup": "markdown",
+        "html": "<p>Update pom.xml</p>",
+        "type": "rendered"
+      }
+    },
+    "type": "pullrequest",
+    "description": "Test",
+    "links": {
+      "decline": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/decline"
+      },
+      "diffstat": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/diffstat/rutvijmehta-harness/spring-cloud-alibaba:b9437f32dddd%0Dcfd2d864e389?from_pullrequest_id=1&topic=true"
+      },
+      "commits": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/commits"
+      },
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1"
+      },
+      "comments": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/comments"
+      },
+      "merge": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/merge"
+      },
+      "html": {
+        "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1"
+      },
+      "activity": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/activity"
+      },
+      "request-changes": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/request-changes"
+      },
+      "diff": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/diff/rutvijmehta-harness/spring-cloud-alibaba:b9437f32dddd%0Dcfd2d864e389?from_pullrequest_id=1&topic=true"
+      },
+      "approve": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/approve"
+      },
+      "statuses": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/statuses"
+      }
+    },
+    "title": "Update pom.xml",
+    "close_source_branch": false,
+    "reviewers": [],
+    "id": 1,
+    "destination": {
+      "commit": {
+        "hash": "cfd2d864e389",
+        "type": "commit",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/commit/cfd2d864e389"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/commits/cfd2d864e389"
+          }
+        }
+      },
+      "repository": {
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "avatar": {
+            "href": "https://bytebucket.org/ravatar/%7B4402cbae-7790-453a-b29e-5fcab61a84df%7D?ts=default"
+          }
+        },
+        "type": "repository",
+        "name": "spring-cloud-alibaba",
+        "full_name": "rutvijmehta-harness/spring-cloud-alibaba",
+        "uuid": "{4402cbae-7790-453a-b29e-5fcab61a84df}"
+      },
+      "branch": {
+        "name": "2021.x"
+      }
+    },
+    "created_on": "2022-06-23T19:27:25.443049+00:00",
+    "summary": {
+      "raw": "Test",
+      "markup": "markdown",
+      "html": "<p>Test</p>",
+      "type": "rendered"
+    },
+    "source": {
+      "commit": {
+        "hash": "b9437f32dddd",
+        "type": "commit",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/commit/b9437f32dddd"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/commits/b9437f32dddd"
+          }
+        }
+      },
+      "repository": {
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "avatar": {
+            "href": "https://bytebucket.org/ravatar/%7B4402cbae-7790-453a-b29e-5fcab61a84df%7D?ts=default"
+          }
+        },
+        "type": "repository",
+        "name": "spring-cloud-alibaba",
+        "full_name": "rutvijmehta-harness/spring-cloud-alibaba",
+        "uuid": "{4402cbae-7790-453a-b29e-5fcab61a84df}"
+      },
+      "branch": {
+        "name": "tiwhitepaper-rutvij"
+      }
+    },
+    "comment_count": 1,
+    "state": "OPEN",
+    "task_count": 0,
+    "participants": [
+      {
+        "participated_on": "2022-06-23T22:10:09.939978+00:00",
+        "state": null,
+        "role": "PARTICIPANT",
+        "user": {
+          "display_name": "Rutvij Mehta",
+          "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+          "links": {
+            "self": {
+              "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+            },
+            "html": {
+              "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+            },
+            "avatar": {
+              "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+            }
+          },
+          "type": "user",
+          "nickname": "Rutvij Mehta",
+          "account_id": "624de6f6fd5e450070486936"
+        },
+        "type": "participant",
+        "approved": false
+      }
+    ],
+    "reason": "",
+    "updated_on": "2022-06-23T22:10:09.939978+00:00",
+    "author": {
+      "display_name": "Rutvij Mehta",
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+        },
+        "avatar": {
+          "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+        }
+      },
+      "type": "user",
+      "nickname": "Rutvij Mehta",
+      "account_id": "624de6f6fd5e450070486936"
+    },
+    "merge_commit": null,
+    "closed_by": null
+  },
+  "repository": {
+    "scm": "git",
+    "website": null,
+    "uuid": "{4402cbae-7790-453a-b29e-5fcab61a84df}",
+    "links": {
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba"
+      },
+      "html": {
+        "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba"
+      },
+      "avatar": {
+        "href": "https://bytebucket.org/ravatar/%7B4402cbae-7790-453a-b29e-5fcab61a84df%7D?ts=default"
+      }
+    },
+    "project": {
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/workspaces/rutvijmehta-harness/projects/TEST"
+        },
+        "html": {
+          "href": "https://bitbucket.org/rutvijmehta-harness/workspace/projects/TEST"
+        },
+        "avatar": {
+          "href": "https://bitbucket.org/account/user/rutvijmehta-harness/projects/TEST/avatar/32?ts=1655023274"
+        }
+      },
+      "type": "project",
+      "name": "Test",
+      "key": "TEST",
+      "uuid": "{aa857cae-daad-4fbd-93ef-503cc3d6c3d6}"
+    },
+    "full_name": "rutvijmehta-harness/spring-cloud-alibaba",
+    "owner": {
+      "display_name": "Rutvij Mehta",
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+        },
+        "avatar": {
+          "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+        }
+      },
+      "type": "user",
+      "nickname": "Rutvij Mehta",
+      "account_id": "624de6f6fd5e450070486936"
+    },
+    "workspace": {
+      "slug": "rutvijmehta-harness",
+      "type": "workspace",
+      "name": "Rutvij Mehta",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/workspaces/rutvijmehta-harness"
+        },
+        "html": {
+          "href": "https://bitbucket.org/rutvijmehta-harness/"
+        },
+        "avatar": {
+          "href": "https://bitbucket.org/workspaces/rutvijmehta-harness/avatar/?ts=1655023237"
+        }
+      },
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}"
+    },
+    "type": "repository",
+    "is_private": false,
+    "name": "spring-cloud-alibaba"
+  },
+  "actor": {
+    "display_name": "Rutvij Mehta",
+    "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+    "links": {
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+      },
+      "html": {
+        "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+      },
+      "avatar": {
+        "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+      }
+    },
+    "type": "user",
+    "nickname": "Rutvij Mehta",
+    "account_id": "624de6f6fd5e450070486936"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_comment_created.json.golden b/scm/driver/bitbucket/testdata/webhooks/pr_comment_created.json.golden
new file mode 100644
index 000000000..f4b06fb9d
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_comment_created.json.golden
@@ -0,0 +1,96 @@
+{
+	"Action": "created",
+	"Repo": {
+		"ID": "{4402cbae-7790-453a-b29e-5fcab61a84df}",
+		"Namespace": "rutvijmehta-harness",
+		"Name": "spring-cloud-alibaba",
+		"Perm": null,
+		"Branch": "",
+		"Archived": false,
+		"Private": false,
+		"Visibility": 0,
+		"Clone": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba.git",
+		"CloneSSH": "git@bitbucket.org:rutvijmehta-harness/spring-cloud-alibaba.git",
+		"Link": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba",
+		"Created": "0001-01-01T00:00:00Z",
+		"Updated": "0001-01-01T00:00:00Z"
+	},
+	"Issue": {
+		"Number": 1,
+		"Title": "Update pom.xml",
+		"Body": "Test",
+		"Link": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1",
+		"Labels": null,
+		"Closed": false,
+		"Locked": false,
+		"Author": {
+			"Login": "",
+			"Name": "Rutvij Mehta",
+			"Email": "",
+			"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+			"Created": "0001-01-01T00:00:00Z",
+			"Updated": "0001-01-01T00:00:00Z"
+		},
+		"PullRequest": {
+			"Number": 1,
+			"Title": "Update pom.xml",
+			"Body": "Test",
+			"Sha": "b9437f32dddd",
+			"Ref": "refs/pull-requests/1/from",
+			"Source": "tiwhitepaper-rutvij",
+			"Target": "2021.x",
+			"Fork": "rutvijmehta-harness/spring-cloud-alibaba",
+			"Link": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1",
+			"Diff": "",
+			"Closed": false,
+			"Merged": false,
+			"Base": {
+				"Name": "",
+				"Path": "",
+				"Sha": ""
+			},
+			"Head": {
+				"Name": "",
+				"Path": "",
+				"Sha": ""
+			},
+			"Author": {
+				"Login": "",
+				"Name": "Rutvij Mehta",
+				"Email": "",
+				"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+				"Created": "0001-01-01T00:00:00Z",
+				"Updated": "0001-01-01T00:00:00Z"
+			},
+			"Created": "2022-06-23T19:27:25.443049Z",
+			"Updated": "2022-06-23T22:10:09.939978Z",
+			"Labels": null
+		},
+		"Created": "2022-06-23T19:27:25.443049Z",
+		"Updated": "2022-06-23T22:10:09.939978Z"
+	},
+	"Comment": {
+		"ID": 311512047,
+		"Body": "test comment",
+		"Author": {
+		    "ID": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+			"Login": "",
+			"Name": "Rutvij Mehta",
+			"Email": "",
+			"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+			"Created": "0001-01-01T00:00:00Z",
+			"Updated": "0001-01-01T00:00:00Z"
+		},
+		"Created": "2022-06-23T22:10:09.939925Z",
+		"Updated": "2022-06-23T22:10:09.939978Z"
+	},
+	"Sender": {
+	    "ID": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+		"Login": "",
+		"Name": "Rutvij Mehta",
+		"Email": "",
+		"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+		"Created": "0001-01-01T00:00:00Z",
+		"Updated": "0001-01-01T00:00:00Z"
+	}
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_comment_deleted.json b/scm/driver/bitbucket/testdata/webhooks/pr_comment_deleted.json
new file mode 100644
index 000000000..23b3f0e3b
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_comment_deleted.json
@@ -0,0 +1,305 @@
+{
+  "comment": {
+    "links": {
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/comments/311512047"
+      },
+      "html": {
+        "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1/_/diff#comment-311512047"
+      }
+    },
+    "deleted": true,
+    "pullrequest": {
+      "type": "pullrequest",
+      "id": 1,
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1"
+        },
+        "html": {
+          "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1"
+        }
+      },
+      "title": "Update pom.xml"
+    },
+    "content": {
+      "raw": "",
+      "markup": "markdown",
+      "html": "",
+      "type": "rendered"
+    },
+    "created_on": "2022-06-23T22:10:09.939925+00:00",
+    "user": {
+      "display_name": "Rutvij Mehta",
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+        },
+        "avatar": {
+          "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+        }
+      },
+      "type": "user",
+      "nickname": "Rutvij Mehta",
+      "account_id": "624de6f6fd5e450070486936"
+    },
+    "updated_on": "2022-06-24T01:38:47.831843+00:00",
+    "type": "pullrequest_comment",
+    "id": 311512047
+  },
+  "pullrequest": {
+    "rendered": {
+      "description": {
+        "raw": "Test",
+        "markup": "markdown",
+        "html": "<p>Test</p>",
+        "type": "rendered"
+      },
+      "title": {
+        "raw": "Update pom.xml",
+        "markup": "markdown",
+        "html": "<p>Update pom.xml</p>",
+        "type": "rendered"
+      }
+    },
+    "type": "pullrequest",
+    "description": "Test",
+    "links": {
+      "decline": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/decline"
+      },
+      "diffstat": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/diffstat/rutvijmehta-harness/spring-cloud-alibaba:b9437f32dddd%0Dcfd2d864e389?from_pullrequest_id=1&topic=true"
+      },
+      "commits": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/commits"
+      },
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1"
+      },
+      "comments": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/comments"
+      },
+      "merge": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/merge"
+      },
+      "html": {
+        "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1"
+      },
+      "activity": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/activity"
+      },
+      "request-changes": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/request-changes"
+      },
+      "diff": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/diff/rutvijmehta-harness/spring-cloud-alibaba:b9437f32dddd%0Dcfd2d864e389?from_pullrequest_id=1&topic=true"
+      },
+      "approve": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/approve"
+      },
+      "statuses": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/pullrequests/1/statuses"
+      }
+    },
+    "title": "Update pom.xml",
+    "close_source_branch": false,
+    "reviewers": [],
+    "id": 1,
+    "destination": {
+      "commit": {
+        "hash": "cfd2d864e389",
+        "type": "commit",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/commit/cfd2d864e389"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/commits/cfd2d864e389"
+          }
+        }
+      },
+      "repository": {
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "avatar": {
+            "href": "https://bytebucket.org/ravatar/%7B4402cbae-7790-453a-b29e-5fcab61a84df%7D?ts=default"
+          }
+        },
+        "type": "repository",
+        "name": "spring-cloud-alibaba",
+        "full_name": "rutvijmehta-harness/spring-cloud-alibaba",
+        "uuid": "{4402cbae-7790-453a-b29e-5fcab61a84df}"
+      },
+      "branch": {
+        "name": "2021.x"
+      }
+    },
+    "created_on": "2022-06-23T19:27:25.443049+00:00",
+    "summary": {
+      "raw": "Test",
+      "markup": "markdown",
+      "html": "<p>Test</p>",
+      "type": "rendered"
+    },
+    "source": {
+      "commit": {
+        "hash": "b9437f32dddd",
+        "type": "commit",
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba/commit/b9437f32dddd"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/commits/b9437f32dddd"
+          }
+        }
+      },
+      "repository": {
+        "links": {
+          "self": {
+            "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "html": {
+            "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba"
+          },
+          "avatar": {
+            "href": "https://bytebucket.org/ravatar/%7B4402cbae-7790-453a-b29e-5fcab61a84df%7D?ts=default"
+          }
+        },
+        "type": "repository",
+        "name": "spring-cloud-alibaba",
+        "full_name": "rutvijmehta-harness/spring-cloud-alibaba",
+        "uuid": "{4402cbae-7790-453a-b29e-5fcab61a84df}"
+      },
+      "branch": {
+        "name": "tiwhitepaper-rutvij"
+      }
+    },
+    "comment_count": 0,
+    "state": "OPEN",
+    "task_count": 0,
+    "participants": [],
+    "reason": "",
+    "updated_on": "2022-06-24T01:40:18.667126+00:00",
+    "author": {
+      "display_name": "Rutvij Mehta",
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+        },
+        "avatar": {
+          "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+        }
+      },
+      "type": "user",
+      "nickname": "Rutvij Mehta",
+      "account_id": "624de6f6fd5e450070486936"
+    },
+    "merge_commit": null,
+    "closed_by": null
+  },
+  "repository": {
+    "scm": "git",
+    "website": null,
+    "uuid": "{4402cbae-7790-453a-b29e-5fcab61a84df}",
+    "links": {
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/repositories/rutvijmehta-harness/spring-cloud-alibaba"
+      },
+      "html": {
+        "href": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba"
+      },
+      "avatar": {
+        "href": "https://bytebucket.org/ravatar/%7B4402cbae-7790-453a-b29e-5fcab61a84df%7D?ts=default"
+      }
+    },
+    "project": {
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/workspaces/rutvijmehta-harness/projects/TEST"
+        },
+        "html": {
+          "href": "https://bitbucket.org/rutvijmehta-harness/workspace/projects/TEST"
+        },
+        "avatar": {
+          "href": "https://bitbucket.org/account/user/rutvijmehta-harness/projects/TEST/avatar/32?ts=1655023274"
+        }
+      },
+      "type": "project",
+      "name": "Test",
+      "key": "TEST",
+      "uuid": "{aa857cae-daad-4fbd-93ef-503cc3d6c3d6}"
+    },
+    "full_name": "rutvijmehta-harness/spring-cloud-alibaba",
+    "owner": {
+      "display_name": "Rutvij Mehta",
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+        },
+        "html": {
+          "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+        },
+        "avatar": {
+          "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+        }
+      },
+      "type": "user",
+      "nickname": "Rutvij Mehta",
+      "account_id": "624de6f6fd5e450070486936"
+    },
+    "workspace": {
+      "slug": "rutvijmehta-harness",
+      "type": "workspace",
+      "name": "Rutvij Mehta",
+      "links": {
+        "self": {
+          "href": "https://api.bitbucket.org/2.0/workspaces/rutvijmehta-harness"
+        },
+        "html": {
+          "href": "https://bitbucket.org/rutvijmehta-harness/"
+        },
+        "avatar": {
+          "href": "https://bitbucket.org/workspaces/rutvijmehta-harness/avatar/?ts=1655023237"
+        }
+      },
+      "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}"
+    },
+    "type": "repository",
+    "is_private": false,
+    "name": "spring-cloud-alibaba"
+  },
+  "actor": {
+    "display_name": "Rutvij Mehta",
+    "uuid": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+    "links": {
+      "self": {
+        "href": "https://api.bitbucket.org/2.0/users/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D"
+      },
+      "html": {
+        "href": "https://bitbucket.org/%7B145a94da-035e-42d6-bf85-296cea8005ba%7D/"
+      },
+      "avatar": {
+        "href": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png"
+      }
+    },
+    "type": "user",
+    "nickname": "Rutvij Mehta",
+    "account_id": "624de6f6fd5e450070486936"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_comment_deleted.json.golden b/scm/driver/bitbucket/testdata/webhooks/pr_comment_deleted.json.golden
new file mode 100644
index 000000000..7190b1ff7
--- /dev/null
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_comment_deleted.json.golden
@@ -0,0 +1,96 @@
+{
+	"Action": "deleted",
+	"Repo": {
+		"ID": "{4402cbae-7790-453a-b29e-5fcab61a84df}",
+		"Namespace": "rutvijmehta-harness",
+		"Name": "spring-cloud-alibaba",
+		"Perm": null,
+		"Branch": "",
+		"Archived": false,
+		"Private": false,
+		"Visibility": 0,
+		"Clone": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba.git",
+		"CloneSSH": "git@bitbucket.org:rutvijmehta-harness/spring-cloud-alibaba.git",
+		"Link": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba",
+		"Created": "0001-01-01T00:00:00Z",
+		"Updated": "0001-01-01T00:00:00Z"
+	},
+	"Issue": {
+		"Number": 1,
+		"Title": "Update pom.xml",
+		"Body": "Test",
+		"Link": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1",
+		"Labels": null,
+		"Closed": false,
+		"Locked": false,
+		"Author": {
+			"Login": "",
+			"Name": "Rutvij Mehta",
+			"Email": "",
+			"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+			"Created": "0001-01-01T00:00:00Z",
+			"Updated": "0001-01-01T00:00:00Z"
+		},
+		"PullRequest": {
+			"Number": 1,
+			"Title": "Update pom.xml",
+			"Body": "Test",
+			"Sha": "b9437f32dddd",
+			"Ref": "refs/pull-requests/1/from",
+			"Source": "tiwhitepaper-rutvij",
+			"Target": "2021.x",
+			"Fork": "rutvijmehta-harness/spring-cloud-alibaba",
+			"Link": "https://bitbucket.org/rutvijmehta-harness/spring-cloud-alibaba/pull-requests/1",
+			"Diff": "",
+			"Closed": false,
+			"Merged": false,
+			"Base": {
+				"Name": "",
+				"Path": "",
+				"Sha": ""
+			},
+			"Head": {
+				"Name": "",
+				"Path": "",
+				"Sha": ""
+			},
+			"Author": {
+				"Login": "",
+				"Name": "Rutvij Mehta",
+				"Email": "",
+				"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+				"Created": "0001-01-01T00:00:00Z",
+				"Updated": "0001-01-01T00:00:00Z"
+			},
+			"Created": "2022-06-23T19:27:25.443049Z",
+			"Updated": "2022-06-24T01:40:18.667126Z",
+			"Labels": null
+		},
+		"Created": "2022-06-23T19:27:25.443049Z",
+		"Updated": "2022-06-24T01:40:18.667126Z"
+	},
+	"Comment": {
+		"ID": 311512047,
+		"Body": "",
+		"Author": {
+		    "ID": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+			"Login": "",
+			"Name": "Rutvij Mehta",
+			"Email": "",
+			"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+			"Created": "0001-01-01T00:00:00Z",
+			"Updated": "0001-01-01T00:00:00Z"
+		},
+		"Created": "2022-06-23T22:10:09.939925Z",
+		"Updated": "2022-06-24T01:38:47.831843Z"
+	},
+	"Sender": {
+	    "ID": "{145a94da-035e-42d6-bf85-296cea8005ba}",
+		"Login": "",
+		"Name": "Rutvij Mehta",
+		"Email": "",
+		"Avatar": "https://secure.gravatar.com/avatar/1bf4d1258f18330b314d2737db430cb0?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRM-5.png",
+		"Created": "0001-01-01T00:00:00Z",
+		"Updated": "0001-01-01T00:00:00Z"
+	}
+}
\ No newline at end of file
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_created.json.golden b/scm/driver/bitbucket/testdata/webhooks/pr_created.json.golden
index 00825c69a..f1901d6d9 100644
--- a/scm/driver/bitbucket/testdata/webhooks/pr_created.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_created.json.golden
@@ -35,6 +35,7 @@
         "Updated": "2018-07-02T21:51:39.532546Z"
     },
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_declined.json.golden b/scm/driver/bitbucket/testdata/webhooks/pr_declined.json.golden
index 395a0bc80..081cf4aff 100644
--- a/scm/driver/bitbucket/testdata/webhooks/pr_declined.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_declined.json.golden
@@ -35,6 +35,7 @@
         "Updated": "2018-07-03T01:44:00.030575Z"
     },
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_fulfilled.json.golden b/scm/driver/bitbucket/testdata/webhooks/pr_fulfilled.json.golden
index 1cc60a2f0..642fe2a9f 100644
--- a/scm/driver/bitbucket/testdata/webhooks/pr_fulfilled.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_fulfilled.json.golden
@@ -18,6 +18,7 @@
         "Title": "Awesome new feature",
         "Body": "made some changes",
         "Sha": "0704fc5beccc",
+        "Merge": "4f8f6de9d0ff",
         "Ref": "refs/pull-requests/1/from",
         "Source": "develop",
         "Target": "master",
@@ -35,6 +36,7 @@
         "Updated": "2018-07-03T01:28:05.903251Z"
     },
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/pr_updated.json.golden b/scm/driver/bitbucket/testdata/webhooks/pr_updated.json.golden
index b5a933fbb..3c4a45ea4 100644
--- a/scm/driver/bitbucket/testdata/webhooks/pr_updated.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/pr_updated.json.golden
@@ -35,6 +35,7 @@
         "Updated": "2018-07-02T21:54:34.210775Z"
     },
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/push.json.golden b/scm/driver/bitbucket/testdata/webhooks/push.json.golden
index 5c76ff577..7d6e576b5 100644
--- a/scm/driver/bitbucket/testdata/webhooks/push.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/push.json.golden
@@ -56,6 +56,7 @@
         }
     ],
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/push_branch_create.json.golden b/scm/driver/bitbucket/testdata/webhooks/push_branch_create.json.golden
index 0797aae42..d8986b55a 100644
--- a/scm/driver/bitbucket/testdata/webhooks/push_branch_create.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/push_branch_create.json.golden
@@ -74,6 +74,7 @@
         }
     ],
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/push_branch_delete.json.golden b/scm/driver/bitbucket/testdata/webhooks/push_branch_delete.json.golden
index 34a12d028..9c5ed6dce 100644
--- a/scm/driver/bitbucket/testdata/webhooks/push_branch_delete.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/push_branch_delete.json.golden
@@ -18,6 +18,7 @@
     },
     "Action": "deleted",
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/push_tag_create.json.golden b/scm/driver/bitbucket/testdata/webhooks/push_tag_create.json.golden
index 322223a56..688fff348 100644
--- a/scm/driver/bitbucket/testdata/webhooks/push_tag_create.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/push_tag_create.json.golden
@@ -34,6 +34,7 @@
         "Link": "https://bitbucket.org/brydzewski/foo/commits/141977fedf5cf35aa290ac87d4b5177ac4cd9de1"
     },
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/testdata/webhooks/push_tag_delete.json.golden b/scm/driver/bitbucket/testdata/webhooks/push_tag_delete.json.golden
index 5fe074bcb..479ffec02 100644
--- a/scm/driver/bitbucket/testdata/webhooks/push_tag_delete.json.golden
+++ b/scm/driver/bitbucket/testdata/webhooks/push_tag_delete.json.golden
@@ -18,6 +18,7 @@
     },
     "Action": "deleted",
     "Sender": {
+        "ID": "{87bb15eb-47c1-49b3-9f16-ca824a2979a4}",
         "Login": "brydzewski",
         "Name": "Brad Rydzewski",
         "Email": "",
diff --git a/scm/driver/bitbucket/user.go b/scm/driver/bitbucket/user.go
index d5ad1e4b8..eba34f022 100644
--- a/scm/driver/bitbucket/user.go
+++ b/scm/driver/bitbucket/user.go
@@ -29,7 +29,22 @@ func (s *userService) FindLogin(ctx context.Context, login string) (*scm.User, *
 }
 
 func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, error) {
-	return "", nil, scm.ErrNotSupported
+    out := new(emails)
+    res, err := s.client.do(ctx, "GET", "2.0/user/emails", nil, &out)
+    return convertEmailList(out), res, err
+}
+
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func convertEmailList(from *emails) string {
+	for _, v := range from.Values {
+		if v.IsPrimary == true {
+		return v.Email
+		}
+	}
+	return ""
 }
 
 type user struct {
@@ -49,6 +64,15 @@ type user struct {
 	UUID string `json:"uuid"`
 }
 
+type email struct {
+    Email string `json:"email"`
+    IsPrimary bool `json:"is_primary"`
+}
+
+type emails struct {
+    Values []*email `json:"values"`
+}
+
 func convertUser(from *user) *scm.User {
 	return &scm.User{
 		Avatar: fmt.Sprintf("https://bitbucket.org/account/%s/avatar/32/", from.Username),
diff --git a/scm/driver/bitbucket/user_test.go b/scm/driver/bitbucket/user_test.go
index aa3b4e947..4e6b3c9f4 100644
--- a/scm/driver/bitbucket/user_test.go
+++ b/scm/driver/bitbucket/user_test.go
@@ -67,9 +67,24 @@ func TestUserLoginFind(t *testing.T) {
 }
 
 func TestUserFindEmail(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.bitbucket.org").
+		Get("/2.0/user/emails").
+		Reply(200).
+		Type("application/json").
+		File("testdata/userEmail.json")
+
 	client, _ := New("https://api.bitbucket.org")
-	_, _, err := client.Users.FindEmail(context.Background())
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	got, _, err := client.Users.FindEmail(context.Background())
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := "test@harness.io"
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
 	}
 }
diff --git a/scm/driver/bitbucket/util.go b/scm/driver/bitbucket/util.go
index 38314c051..e43ea71cf 100644
--- a/scm/driver/bitbucket/util.go
+++ b/scm/driver/bitbucket/util.go
@@ -8,6 +8,7 @@ import (
 	"net/url"
 	"regexp"
 	"strconv"
+	"strings"
 
 	"github.com/drone/go-scm/scm"
 )
@@ -24,6 +25,26 @@ func extractEmail(gitauthor string) (author string) {
 	return
 }
 
+func encodeBranchListOptions(opts scm.BranchListOptions) string {
+	params := url.Values{}
+	if opts.SearchTerm != "" {
+		var sb strings.Builder
+		sb.WriteString("name~\"")
+		sb.WriteString(opts.SearchTerm)
+		sb.WriteString("\"")
+		params.Set("q", sb.String())
+	}
+	if opts.PageListOptions != (scm.ListOptions{}) {
+		if opts.PageListOptions.Page != 0 {
+			params.Set("page", strconv.Itoa(opts.PageListOptions.Page))
+		}
+		if opts.PageListOptions.Size != 0 {
+			params.Set("pagelen", strconv.Itoa(opts.PageListOptions.Size))
+		}
+	}
+	return params.Encode()
+}
+
 func encodeListOptions(opts scm.ListOptions) string {
 	params := url.Values{}
 	if opts.Page != 0 {
@@ -47,6 +68,27 @@ func encodeListRoleOptions(opts scm.ListOptions) string {
 	return params.Encode()
 }
 
+func encodeRepoListOptions(opts scm.RepoListOptions) string {
+	params := url.Values{}
+	if opts.RepoSearchTerm.RepoName != "" {
+		var sb strings.Builder
+		sb.WriteString("name~\"")
+		sb.WriteString(opts.RepoSearchTerm.RepoName)
+		sb.WriteString("\"")
+		params.Set("q", sb.String())
+	}
+	if opts.ListOptions != (scm.ListOptions{}) {
+		if opts.ListOptions.Page != 0 {
+			params.Set("page", strconv.Itoa(opts.ListOptions.Page))
+		}
+		if opts.ListOptions.Size != 0 {
+			params.Set("pagelen", strconv.Itoa(opts.ListOptions.Size))
+		}
+	}
+	params.Set("role", "member")
+	return params.Encode()
+}
+
 func encodeCommitListOptions(opts scm.CommitListOptions) string {
 	params := url.Values{}
 	if opts.Page != 0 {
@@ -55,6 +97,9 @@ func encodeCommitListOptions(opts scm.CommitListOptions) string {
 	if opts.Size != 0 {
 		params.Set("pagelen", strconv.Itoa(opts.Size))
 	}
+	if opts.Path != "" {
+		params.Set("path", opts.Path)
+	}
 	return params.Encode()
 }
 
diff --git a/scm/driver/bitbucket/webhook.go b/scm/driver/bitbucket/webhook.go
index da62ba876..3b1836220 100644
--- a/scm/driver/bitbucket/webhook.go
+++ b/scm/driver/bitbucket/webhook.go
@@ -11,6 +11,8 @@ import (
 	"io"
 	"io/ioutil"
 	"net/http"
+	"regexp"
+	"strconv"
 	"time"
 
 	"github.com/drone/go-scm/scm"
@@ -53,6 +55,25 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 		if hook != nil {
 			hook.(*scm.PullRequestHook).Action = scm.ActionClose
 		}
+	case "pullrequest:comment_created":
+		hook, err = s.parsePullRequestCommentHook(data)
+		if hook != nil {
+			hook.(*scm.IssueCommentHook).Action = scm.ActionCreate
+		}
+	case "pullrequest:comment_updated":
+		// Bitbucket PR Comment Update is unreliable and does not send events
+		// most of the time https://github.com/iterative/cml/issues/817
+		hook, err = s.parsePullRequestCommentHook(data)
+		if hook != nil {
+			hook.(*scm.IssueCommentHook).Action = scm.ActionEdit
+		}
+	case "pullrequest:comment_deleted":
+		hook, err = s.parsePullRequestCommentHook(data)
+		if hook != nil {
+			hook.(*scm.IssueCommentHook).Action = scm.ActionDelete
+		}
+	case "repo:commit_status_updated", "repo:commit_status_created":
+		hook, err = s.parsePipelineHook(data)
 	}
 	if err != nil {
 		return nil, err
@@ -78,6 +99,12 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 	return hook, nil
 }
 
+func (s *webhookService) parsePullRequestCommentHook(data []byte) (scm.Webhook, error) {
+	dst := new(prCommentHook)
+	err := json.Unmarshal(data, dst)
+	return convertPrCommentHook(dst), err
+}
+
 func (s *webhookService) parsePushHook(data []byte) (scm.Webhook, error) {
 	dst := new(pushHook)
 	err := json.Unmarshal(data, dst)
@@ -115,6 +142,15 @@ func (s *webhookService) parsePullRequestHook(data []byte) (*scm.PullRequestHook
 	}
 }
 
+func (s *webhookService) parsePipelineHook(data []byte) (*scm.PipelineHook, error) {
+	dst := new(pipelineHook)
+	err := json.Unmarshal(data, dst)
+	if err != nil {
+		return nil, err
+	}
+	return convertBitbucketHook(dst), err
+}
+
 //
 // native data structures
 //
@@ -381,6 +417,278 @@ type (
 		} `json:"links"`
 		UUID string `json:"uuid"`
 	}
+
+	prCommentInput struct {
+		Content struct {
+			Raw string `json:"raw"`
+		} `json:"content"`
+	}
+
+	prComment struct {
+		Links struct {
+			Self link `json:"self"`
+			HTML link `json:"html"`
+		} `json:"links"`
+		Deleted     bool `json:"deleted"`
+		PullRequest struct {
+			Type  string `json:"type"`
+			ID    int    `json:"id"`
+			Links struct {
+				Self link `json:"self"`
+				HTML link `json:"html"`
+			} `json:"links"`
+			Title string `json:"title"`
+		}
+		Content struct {
+			Raw    string `json:"raw"`
+			Markup string `json:"markup"`
+			Html   string `json:"html"`
+			Type   string `json:"type"`
+		}
+		CreatedOn time.Time         `json:"created_on"`
+		User      prCommentHookUser `json:"user"`
+		UpdatedOn time.Time         `json:"updated_on"`
+		Type      string            `json:"type"`
+		ID        int               `json:"id"`
+	}
+
+	prCommentHookRepo struct {
+		Scm     string `json:"scm"`
+		Website string `json:"website"`
+		UUID    string `json:"uuid"`
+		Links   struct {
+			Self   link `json:"self"`
+			HTML   link `json:"html"`
+			Avatar link `json:"avatar"`
+		} `json:"links"`
+		Project struct {
+			Links struct {
+				Self   link `json:"self"`
+				HTML   link `json:"html"`
+				Avatar link `json:"avatar"`
+			} `json:"links"`
+			Type string `json:"type"`
+			Name string `json:"name"`
+			Key  string `json:"key"`
+			UUID string `json:"uuid"`
+		} `json:"project"`
+		FullName  string            `json:"full_name"`
+		Owner     prCommentHookUser `json:"owner"`
+		Workspace struct {
+			Slug  string `json:"slugg"`
+			Type  string `json:"type"`
+			Name  string `json:"name"`
+			Links struct {
+				Self   link `json:"self"`
+				HTML   link `json:"html"`
+				Avatar link `json:"avatar"`
+			} `json:"links"`
+			UUID string `json:"uuid"`
+		} `json:"workspace"`
+		Type      string `json:"type"`
+		IsPrivate bool   `json:"is_private"`
+		Name      string `json:"name"`
+	}
+
+	prCommentHookUser struct {
+		Username    string `json:"username"`
+		DisplayName string `json:"display_name"`
+		UUID        string `json:"uuid"`
+		Links       struct {
+			Self   link `json:"self"`
+			HTML   link `json:"html"`
+			Avatar link `json:"avatar"`
+		} `json:"links"`
+		Type      string `json:"type"`
+		Nickname  string `json:"nickname"`
+		AccountID string `json:"account_id"`
+	}
+
+	prCommentHookPullRequest struct {
+		Rendered struct {
+			Description struct {
+				Raw    string `json:"raw"`
+				Markup string `json:"markup"`
+				Html   string `json:"html"`
+				Type   string `json:"type"`
+			} `json:"description"`
+			Title struct {
+				Raw    string `json:"raw"`
+				Markup string `json:"markup"`
+				Html   string `json:"html"`
+				Type   string `json:"type"`
+			} `json:"title"`
+		} `json:"rendered"`
+		Type        string `json:"type"`
+		Description string `json:"description"`
+		Links       struct {
+			Decline        link `json:"decline"`
+			Diffstat       link `json:"diffstat"`
+			Commits        link `json:"commits"`
+			Self           link `json:"self"`
+			Comments       link `json:"comments"`
+			Merge          link `json:"merge"`
+			Html           link `json:"html"`
+			Activity       link `json:"activity"`
+			RequestChanges link `json:"request-changes"`
+			Diff           link `json:"diff"`
+			Approve        link `json:"approve"`
+			Statuses       link `json:"statuses"`
+		} `json:"links"`
+		Title             string        `json:"title"`
+		CloseSourceBranch bool          `json:"close_source_branch"`
+		Reviewers         []interface{} `json:"reviewers"`
+		ID                int           `json:"id"`
+		Destination       struct {
+			Commit struct {
+				Hash  string `json:"hash"`
+				Type  string `json:"type"`
+				Links struct {
+					Self link `json:"self"`
+					HTML link `json:"html"`
+				} `json:"links"`
+			}
+			Repository struct {
+				Links struct {
+					Self   link `json:"self"`
+					HTML   link `json:"html"`
+					Avatar link `json:"avatar"`
+				} `json:"links"`
+				Type     string `json:"type"`
+				Name     string `json:"name"`
+				FullName string `json:"full_name"`
+				UUID     string `json:"uuid"`
+			} `json:"repository"`
+			Branch struct {
+				Name string `json:"name"`
+			} `json:"branch"`
+		} `json:"destination"`
+		CreatedOn time.Time `json:"created_on"`
+		Summary   struct {
+			Raw    string `json:"raw"`
+			Markup string `json:"markup"`
+			Html   string `json:"html"`
+			Type   string `json:"type"`
+		} `json:"summary"`
+		Source struct {
+			Commit struct {
+				Hash  string `json:"hash"`
+				Type  string `json:"type"`
+				Links struct {
+					Self link `json:"self"`
+					HTML link `json:"html"`
+				} `json:"links"`
+			}
+			Repository struct {
+				Links struct {
+					Self   link `json:"self"`
+					HTML   link `json:"html"`
+					Avatar link `json:"avatar"`
+				} `json:"links"`
+				Type     string `json:"type"`
+				Name     string `json:"name"`
+				FullName string `json:"full_name"`
+				UUID     string `json:"uuid"`
+			} `json:"repository"`
+			Branch struct {
+				Name string `json:"name"`
+			} `json:"branch"`
+		} `json:"source"`
+		CommentCount int               `json:"comment_count"`
+		State        string            `json:"state"`
+		TaskCount    int               `json:"task_count"`
+		Participants []interface{}     `json:"participants"`
+		Reason       string            `json:"reason"`
+		UpdatedOn    time.Time         `json:"updated_on"`
+		Author       prCommentHookUser `json:"author"`
+		MergeCommit  interface{}       `json:"merge_commit"`
+		ClosedBy     interface{}       `json:"closed_by"`
+	}
+
+	prCommentHook struct {
+		Comment     prComment                `json:"comment"`
+		PullRequest prCommentHookPullRequest `json:"pullRequest"`
+		Repository  prCommentHookRepo        `json:"repository"`
+		Actor       prCommentHookUser        `json:"actor"`
+	}
+
+	pipelineHook struct {
+		Repository   webhookRepository `json:"repository"`
+		Actor        actor             `json:"actor"`
+		CommitStatus struct {
+			Key     string `json:"key"`
+			Type    string `json:"type"`
+			State   string `json:"state"`
+			Name    string `json:"name"`
+			RefName string `json:"refname"`
+			Commit  struct {
+				Type    string    `json:"type"`
+				Hash    string    `json:"hash"`
+				Date    time.Time `json:"date"`
+				Author  author    `json:"author"`
+				Message string    `json:"message"`
+				Links   struct {
+					Self   href `json:"self"`
+					HTML   href `json:"html"`
+					Avatar href `json:"avatar"`
+				} `json:"links"`
+			} `json:"commit"`
+			URL         string    `json:"url"`
+			Repository  info      `json:"repository"`
+			Description string    `json:"description"`
+			CreatedOn   time.Time `json:"created_on"`
+			UpdatedOn   time.Time `json:"updated_on"`
+			Links       struct {
+				Self   href `json:"self"`
+				Commit href `json:"commit"`
+			} `json:"links"`
+		} `json:"commit_status"`
+	}
+
+	href struct {
+		Href string `json:"href"`
+	}
+
+	actor struct {
+		DisplayName string `json:"display_name"`
+		Links       struct {
+			Self   href `json:"self"`
+			HTML   href `json:"html"`
+			Avatar href `json:"avatar"`
+		} `json:"links"`
+		Type     string `json:"type"`
+		UUID     string `json:"uuid"`
+		Username string `json:"username"`
+	}
+
+	author struct {
+		Type string `json:"type"`
+		Raw  string `json:"raw"`
+		User struct {
+			DisplayName string `json:"display_name"`
+			Links       struct {
+				Self   href `json:"self"`
+				HTML   href `json:"html"`
+				Avatar href `json:"avatar"`
+			} `json:"links"`
+			Type      string `json:"type"`
+			UUID      string `json:"uuid"`
+			AccountID string `json:"account_id"`
+			Nickname  string `json:"nickname"`
+		} `json:"user"`
+	}
+
+	info struct {
+		Type     string `json:"type"`
+		FullName string `json:"full_name"`
+		Links    struct {
+			Self   href `json:"self"`
+			HTML   href `json:"html"`
+			Avatar href `json:"avatar"`
+		} `json:"links"`
+		Name string `json:"name"`
+		UUID string `json:"uuid"`
+	}
 )
 
 //
@@ -446,6 +754,7 @@ func convertPushHook(src *pushHook) *scm.PushHook {
 			Link:      src.Repository.Links.HTML.Href,
 		},
 		Sender: scm.User{
+			ID:     src.Actor.UUID,
 			Login:  src.Actor.Username,
 			Name:   src.Actor.DisplayName,
 			Avatar: src.Actor.Links.Avatar.Href,
@@ -478,6 +787,7 @@ func convertBranchCreateHook(src *pushHook) *scm.BranchHook {
 			Link:      src.Repository.Links.HTML.Href,
 		},
 		Sender: scm.User{
+			ID:     src.Actor.UUID,
 			Login:  src.Actor.Username,
 			Name:   src.Actor.DisplayName,
 			Avatar: src.Actor.Links.Avatar.Href,
@@ -505,6 +815,7 @@ func convertBranchDeleteHook(src *pushHook) *scm.BranchHook {
 			Link:      src.Repository.Links.HTML.Href,
 		},
 		Sender: scm.User{
+			ID:     src.Actor.UUID,
 			Login:  src.Actor.Username,
 			Name:   src.Actor.DisplayName,
 			Avatar: src.Actor.Links.Avatar.Href,
@@ -532,6 +843,7 @@ func convertTagCreateHook(src *pushHook) *scm.TagHook {
 			Link:      src.Repository.Links.HTML.Href,
 		},
 		Sender: scm.User{
+			ID:     src.Actor.UUID,
 			Login:  src.Actor.Username,
 			Name:   src.Actor.DisplayName,
 			Avatar: src.Actor.Links.Avatar.Href,
@@ -559,6 +871,7 @@ func convertTagDeleteHook(src *pushHook) *scm.TagHook {
 			Link:      src.Repository.Links.HTML.Href,
 		},
 		Sender: scm.User{
+			ID:     src.Actor.UUID,
 			Login:  src.Actor.Username,
 			Name:   src.Actor.DisplayName,
 			Avatar: src.Actor.Links.Avatar.Href,
@@ -579,6 +892,7 @@ func convertPullRequestHook(src *webhook) *scm.PullRequestHook {
 			Title:  src.PullRequest.Title,
 			Body:   src.PullRequest.Description,
 			Sha:    src.PullRequest.Source.Commit.Hash,
+			Merge:  src.PullRequest.MergeCommit.Hash,
 			Ref:    fmt.Sprintf("refs/pull-requests/%d/from", src.PullRequest.ID),
 			Source: src.PullRequest.Source.Branch.Name,
 			Target: src.PullRequest.Destination.Branch.Name,
@@ -604,9 +918,136 @@ func convertPullRequestHook(src *webhook) *scm.PullRequestHook {
 			Link:      src.Repository.Links.HTML.Href,
 		},
 		Sender: scm.User{
+			ID:     src.Actor.UUID,
 			Login:  src.Actor.Username,
 			Name:   src.Actor.DisplayName,
 			Avatar: src.Actor.Links.Avatar.Href,
 		},
 	}
 }
+
+func convertPrCommentHook(src *prCommentHook) *scm.IssueCommentHook {
+	namespace, _ := scm.Split(src.Repository.FullName)
+	dst := scm.IssueCommentHook{
+		Repo: scm.Repository{
+			ID:        src.Repository.UUID,
+			Namespace: namespace,
+			Name:      src.Repository.Name,
+			Clone:     fmt.Sprintf("https://bitbucket.org/%s.git", src.Repository.FullName),
+			CloneSSH:  fmt.Sprintf("git@bitbucket.org:%s.git", src.Repository.FullName),
+			Link:      src.Repository.Links.HTML.Href,
+			Private:   src.Repository.IsPrivate,
+		},
+		Issue: scm.Issue{
+			Number: src.PullRequest.ID,
+			Title:  src.PullRequest.Title,
+			Body:   src.PullRequest.Description,
+			Link:   src.PullRequest.Links.Html.Href,
+			Author: scm.User{
+				Login:  src.PullRequest.Author.Username,
+				Name:   src.PullRequest.Author.DisplayName,
+				Avatar: src.PullRequest.Author.Links.Avatar.Href,
+			},
+			PullRequest: scm.PullRequest{
+				Number: src.PullRequest.ID,
+				Title:  src.PullRequest.Title,
+				Body:   src.PullRequest.Description,
+				Sha:    src.PullRequest.Source.Commit.Hash,
+				// Bitbucket does not support PR Refs: https://jira.atlassian.com/browse/BCLOUD-5814
+				Ref:    fmt.Sprintf("refs/pull-requests/%d/from", src.PullRequest.ID),
+				Source: src.PullRequest.Source.Branch.Name,
+				Target: src.PullRequest.Destination.Branch.Name,
+				Fork:   src.PullRequest.Source.Repository.FullName,
+				Link:   src.PullRequest.Links.Html.Href,
+				Closed: src.PullRequest.State != "OPEN",
+				Merged: src.PullRequest.State == "MERGED",
+				Author: scm.User{
+					Login:  src.PullRequest.Author.Username,
+					Name:   src.PullRequest.Author.DisplayName,
+					Avatar: src.PullRequest.Author.Links.Avatar.Href,
+				},
+				Created: src.PullRequest.CreatedOn,
+				Updated: src.PullRequest.UpdatedOn,
+			},
+			Created: src.PullRequest.CreatedOn,
+			Updated: src.PullRequest.UpdatedOn,
+		},
+		Comment: scm.Comment{
+			ID:   src.Comment.ID,
+			Body: src.Comment.Content.Raw,
+			Author: scm.User{
+				ID:     src.Comment.User.UUID,
+				Login:  src.Comment.User.Username,
+				Name:   src.Comment.User.DisplayName,
+				Avatar: src.Comment.User.Links.Avatar.Href,
+			},
+			Created: src.Comment.CreatedOn,
+			Updated: src.Comment.UpdatedOn,
+		},
+		Sender: scm.User{
+			ID:     src.Actor.UUID,
+			Login:  src.Actor.Username,
+			Name:   src.Actor.DisplayName,
+			Avatar: src.Actor.Links.Avatar.Href,
+		},
+	}
+	return &dst
+}
+
+func convertBitbucketHook(src *pipelineHook) *scm.PipelineHook {
+	if src.CommitStatus.Type == "" || src.CommitStatus.Type != "build" {
+		return nil
+	}
+	namespace, name := scm.Split(src.Repository.FullName)
+
+	return &scm.PipelineHook{
+		Repo: scm.Repository{
+			ID:        src.Repository.UUID,
+			Namespace: namespace,
+			Name:      name,
+			Clone:     src.Repository.Links.HTML.Href, // Assuming HTML link as Clone URL
+			Branch:    src.CommitStatus.RefName,
+			Private:   src.Repository.IsPrivate,
+		},
+		Commit: scm.Commit{
+			Sha:     src.CommitStatus.Commit.Hash,
+			Message: src.CommitStatus.Commit.Message,
+			Author: scm.Signature{
+				Name:   src.CommitStatus.Commit.Author.User.DisplayName,
+				Email:  extractEmail(src.CommitStatus.Commit.Author.Raw),
+				Avatar: src.CommitStatus.Commit.Author.User.Links.Avatar.Href,
+			},
+			Committer: scm.Signature{
+				Name:   src.CommitStatus.Commit.Author.User.DisplayName,
+				Email:  extractEmail(src.CommitStatus.Commit.Author.Raw),
+				Avatar: src.CommitStatus.Commit.Author.User.Links.Avatar.Href,
+			},
+			Link: src.CommitStatus.Commit.Links.HTML.Href,
+		},
+		Execution: scm.Execution{
+			Number:  extractExecutionId(src.CommitStatus.URL),
+			Status:  scm.ConvertExecutionStatus(src.CommitStatus.State),
+			Created: src.CommitStatus.CreatedOn,
+			URL:     src.CommitStatus.URL,
+		},
+		Sender: scm.User{
+			Login:  src.Actor.Username,
+			Name:   src.Actor.DisplayName,
+			Avatar: src.Actor.Links.Avatar.Href,
+			ID:     src.Actor.UUID,
+		},
+		PullRequest: scm.PullRequest{},
+	}
+}
+
+func extractExecutionId(url string) int {
+	re := regexp.MustCompile(`/results/(\d+)`)
+	match := re.FindStringSubmatch(url)
+	if len(match) > 1 {
+		id, err := strconv.Atoi(match[1])
+		if err == nil {
+			return id
+		}
+	}
+	return -1
+}
diff --git a/scm/driver/bitbucket/webhook_test.go b/scm/driver/bitbucket/webhook_test.go
index 9e633ed23..71f2b7e89 100644
--- a/scm/driver/bitbucket/webhook_test.go
+++ b/scm/driver/bitbucket/webhook_test.go
@@ -132,6 +132,34 @@ func TestWebhooks(t *testing.T) {
 		// 			after:  "samples/pr_unlabeled.json.golden",
 		// 			obj:    new(scm.PullRequestHook),
 		// 		},
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "pullrequest:comment_created",
+			before: "testdata/webhooks/pr_comment_created.json",
+			after:  "testdata/webhooks/pr_comment_created.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "pullrequest:comment_deleted",
+			before: "testdata/webhooks/pr_comment_deleted.json",
+			after:  "testdata/webhooks/pr_comment_deleted.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "repo:commit_status_created",
+			before: "testdata/webhooks/pipeline_hook_created.json",
+			after:  "testdata/webhooks/pipeline_hook_created.json.golden",
+			obj:    new(scm.PipelineHook),
+		},
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "repo:commit_status_updated",
+			before: "testdata/webhooks/pipeline_hook_update.json",
+			after:  "testdata/webhooks/pipeline_hook_update.json.golden",
+			obj:    new(scm.PipelineHook),
+		},
 	}
 
 	for _, test := range tests {
diff --git a/scm/driver/gitea/content.go b/scm/driver/gitea/content.go
index 07945a48a..d0c73ad5c 100644
--- a/scm/driver/gitea/content.go
+++ b/scm/driver/gitea/content.go
@@ -34,7 +34,7 @@ func (s *contentService) Update(ctx context.Context, repo, path string, params *
 	return nil, scm.ErrNotSupported
 }
 
-func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
 	return nil, scm.ErrNotSupported
 }
 
@@ -48,6 +48,7 @@ func (s *contentService) List(ctx context.Context, repo, path, ref string, _ scm
 type content struct {
 	Path string `json:"path"`
 	Type string `json:"type"`
+	Sha  string `json:"sha"`
 }
 
 func convertContentInfoList(from []*content) []*scm.ContentInfo {
@@ -59,7 +60,7 @@ func convertContentInfoList(from []*content) []*scm.ContentInfo {
 }
 
 func convertContentInfo(from *content) *scm.ContentInfo {
-	to := &scm.ContentInfo{Path: from.Path}
+	to := &scm.ContentInfo{Path: from.Path, BlobID: from.Sha}
 	switch from.Type {
 	case "file":
 		to.Kind = scm.ContentKindFile
diff --git a/scm/driver/gitea/content_test.go b/scm/driver/gitea/content_test.go
index 4e73acfc5..5a276da4c 100644
--- a/scm/driver/gitea/content_test.go
+++ b/scm/driver/gitea/content_test.go
@@ -61,7 +61,7 @@ func TestContentUpdate(t *testing.T) {
 
 func TestContentDelete(t *testing.T) {
 	client, _ := New("https://try.gitea.io")
-	_, err := client.Contents.Delete(context.Background(), "go-gitea/gitea", "README.md", "master")
+	_, err := client.Contents.Delete(context.Background(), "go-gitea/gitea", "README.md", &scm.ContentParams{})
 	if err != scm.ErrNotSupported {
 		t.Errorf("Expect Not Supported error")
 	}
diff --git a/scm/driver/gitea/git.go b/scm/driver/gitea/git.go
index d20580561..aaa58134f 100644
--- a/scm/driver/gitea/git.go
+++ b/scm/driver/gitea/git.go
@@ -17,6 +17,10 @@ type gitService struct {
 	client *wrapper
 }
 
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
 func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
 	path := fmt.Sprintf("api/v1/repos/%s/branches/%s", repo, name)
 	out := new(branch)
@@ -25,6 +29,7 @@ func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Re
 }
 
 func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
+	ref = scm.TrimRef(ref)
 	path := fmt.Sprintf("api/v1/repos/%s/git/commits/%s", repo, url.PathEscape(ref))
 	out := new(commitInfo)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
@@ -54,8 +59,17 @@ func (s *gitService) ListBranches(ctx context.Context, repo string, opts scm.Lis
 	return convertBranchList(out), res, err
 }
 
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// Gitea doesnt provide support listing based on searchTerm
+	// Hence calling the ListBranches
+	return s.ListBranches(ctx, repo, opts.PageListOptions)
+}
+
 func (s *gitService) ListCommits(ctx context.Context, repo string, _ scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
-	return nil, nil, scm.ErrNotSupported
+	path := fmt.Sprintf("api/v1/repos/%s/commits", repo)
+	out := []*commitInfo{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertCommitList(out), res, err
 }
 
 func (s *gitService) ListTags(ctx context.Context, repo string, _ scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
@@ -142,13 +156,13 @@ func convertBranch(src *branch) *scm.Reference {
 	}
 }
 
-// func convertCommitList(src []*commit) []*scm.Commit {
-// 	dst := []*scm.Commit{}
-// 	for _, v := range src {
-// 		dst = append(dst, convertCommitInfo(v))
-// 	}
-// 	return dst
-// }
+func convertCommitList(src []*commitInfo) []*scm.Commit {
+	dst := []*scm.Commit{}
+	for _, v := range src {
+		dst = append(dst, convertCommitInfo(v))
+	}
+	return dst
+}
 
 func convertCommitInfo(src *commitInfo) *scm.Commit {
 	return &scm.Commit{
diff --git a/scm/driver/gitea/git_test.go b/scm/driver/gitea/git_test.go
index 65f139bea..de91410ce 100644
--- a/scm/driver/gitea/git_test.go
+++ b/scm/driver/gitea/git_test.go
@@ -24,7 +24,7 @@ func TestGitFindCommit(t *testing.T) {
 		Get("/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630").
 		Reply(200).
 		Type("application/json").
-		File("testdata/commits.json")
+		File("testdata/commit.json")
 
 	client, _ := New("https://try.gitea.io")
 	got, _, err := client.Git.FindCommit(
@@ -37,7 +37,7 @@ func TestGitFindCommit(t *testing.T) {
 	}
 
 	want := new(scm.Commit)
-	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
 	json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
@@ -47,10 +47,25 @@ func TestGitFindCommit(t *testing.T) {
 }
 
 func TestGitListCommits(t *testing.T) {
+	gock.New("https://try.gitea.io").
+		Get("/api/v1/repos/go-gitea/gitea/commits").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
 	client, _ := New("https://try.gitea.io")
-	_, _, err := client.Git.ListCommits(context.Background(), "go-gitea/gitea", scm.CommitListOptions{})
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	got, _, err := client.Git.ListCommits(context.Background(), "go-gitea/gitea", scm.CommitListOptions{})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
 	}
 }
 
diff --git a/scm/driver/gitea/gitea.go b/scm/driver/gitea/gitea.go
index e52900189..3e8031cbd 100644
--- a/scm/driver/gitea/gitea.go
+++ b/scm/driver/gitea/gitea.go
@@ -35,9 +35,11 @@ func New(uri string) (*scm.Client, error) {
 	client.Contents = &contentService{client}
 	client.Git = &gitService{client}
 	client.Issues = &issueService{client}
+	client.Milestones = & milestoneService{client}
 	client.Organizations = &organizationService{client}
 	client.PullRequests = &pullService{client}
 	client.Repositories = &repositoryService{client}
+	client.Releases = &releaseService{client}
 	client.Reviews = &reviewService{client}
 	client.Users = &userService{client}
 	client.Webhooks = &webhookService{client}
diff --git a/scm/driver/gitea/gitea_test.go b/scm/driver/gitea/gitea_test.go
index 79f0573da..658aef0c5 100644
--- a/scm/driver/gitea/gitea_test.go
+++ b/scm/driver/gitea/gitea_test.go
@@ -5,7 +5,10 @@
 // package gitea implements a Gogs client.
 package gitea
 
-import "testing"
+import (
+	"github.com/drone/go-scm/scm"
+	"testing"
+)
 
 func TestClient(t *testing.T) {
 	client, err := New("https://try.gitea.io")
@@ -33,3 +36,20 @@ func TestClient_Error(t *testing.T) {
 		t.Errorf("Expect error when invalid URL")
 	}
 }
+
+func testPage(res *scm.Response) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := res.Page.Next, 2; got != want {
+			t.Errorf("Want next page %d, got %d", want, got)
+		}
+		if got, want := res.Page.Prev, 1; got != want {
+			t.Errorf("Want prev page %d, got %d", want, got)
+		}
+		if got, want := res.Page.First, 1; got != want {
+			t.Errorf("Want first page %d, got %d", want, got)
+		}
+		if got, want := res.Page.Last, 5; got != want {
+			t.Errorf("Want last page %d, got %d", want, got)
+		}
+	}
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/milestone.go b/scm/driver/gitea/milestone.go
new file mode 100644
index 000000000..73c69bccd
--- /dev/null
+++ b/scm/driver/gitea/milestone.go
@@ -0,0 +1,130 @@
+package gitea
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/milestones/%d", namespace, name, id)
+	out := new(milestone)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertMilestone(out), res, err
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/milestones%s", namespace, name, encodeMilestoneListOptions(opts))
+	out := []*milestone{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertMilestoneList(out), res, err
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/milestones", namespace, name)
+	in := &milestoneInput{
+		Title:       input.Title,
+		Description: input.Description,
+		State:       stateOpen,
+		Deadline:    null.TimeFrom(input.DueDate),
+	}
+	if input.State == "closed" {
+		in.State = stateClosed
+	}
+	out := new(milestone)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertMilestone(out), res, err
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/milestones/%d", namespace, name, id)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/milestones/%d", namespace, name, id)
+	in := milestoneInput{}
+	if input.Title != "" {
+		in.Title = input.Title
+	}
+	switch input.State {
+	case "open":
+		in.State = stateOpen
+	case "close", "closed":
+		in.State = stateClosed
+	}
+	if input.Description != "" {
+		in.Description = input.Description
+	}
+	if !input.DueDate.IsZero() {
+		in.Deadline = null.TimeFrom(input.DueDate)
+	}
+	out := new(milestone)
+	res, err := s.client.do(ctx, "PATCH", path, in, out)
+	return convertMilestone(out), res, err
+}
+
+// stateType issue state type
+type stateType string
+
+const (
+	// stateOpen pr/issue is open
+	stateOpen stateType = "open"
+	// stateClosed pr/issue is closed
+	stateClosed stateType = "closed"
+	// stateAll is all
+	stateAll stateType = "all"
+)
+
+type milestone struct {
+	ID           int64     `json:"id"`
+	Title        string    `json:"title"`
+	Description  string    `json:"description"`
+	State        stateType `json:"state"`
+	OpenIssues   int       `json:"open_issues"`
+	ClosedIssues int       `json:"closed_issues"`
+	Created      null.Time `json:"created_at"`
+	Updated      null.Time `json:"updated_at"`
+	Closed       null.Time `json:"closed_at"`
+	Deadline     null.Time `json:"due_on"`
+}
+
+type milestoneInput struct {
+	Title       string    `json:"title"`
+	Description string    `json:"description"`
+	State       stateType `json:"state"`
+	Deadline    null.Time `json:"due_on"`
+}
+
+func convertMilestoneList(src []*milestone) []*scm.Milestone {
+	var dst []*scm.Milestone
+	for _, v := range src {
+		dst = append(dst, convertMilestone(v))
+	}
+	return dst
+}
+
+func convertMilestone(src *milestone) *scm.Milestone {
+	if src == nil || src.Deadline.IsZero() {
+		return nil
+	}
+	return &scm.Milestone{
+		Number:      int(src.ID),
+		ID:          int(src.ID),
+		Title:       src.Title,
+		Description: src.Description,
+		State:       string(src.State),
+		DueDate:     src.Deadline.ValueOrZero(),
+	}
+}
diff --git a/scm/driver/gitea/milestone_test.go b/scm/driver/gitea/milestone_test.go
new file mode 100644
index 000000000..93d9f185e
--- /dev/null
+++ b/scm/driver/gitea/milestone_test.go
@@ -0,0 +1,185 @@
+package gitea
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestMilestoneFind(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Get("/api/v1/repos/jcitizen/my-repo/milestones/1").
+		Reply(200).
+		Type("application/json").
+		File("testdata/milestone.json")
+
+	client, _ := New("https://try.gitea.io")
+	got, _, err := client.Milestones.Find(context.Background(), "jcitizen/my-repo", 1)
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Milestone)
+	raw, _ := ioutil.ReadFile("testdata/milestone.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestMilestoneList(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Get("/api/v1/repos/jcitizen/my-repo/milestones").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockPageHeaders).
+		File("testdata/milestones.json")
+
+	client, _ := New("https://try.gitea.io")
+	got, res, err := client.Milestones.List(context.Background(), "jcitizen/my-repo", scm.MilestoneListOptions{})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Milestone{}
+	raw, _ := ioutil.ReadFile("testdata/milestones.json.golden")
+	err = json.Unmarshal(raw, &want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Page", testPage(res))
+}
+
+func TestMilestoneCreate(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Post("/api/v1/repos/jcitizen/my-repo/milestones").
+		File("testdata/milestone_create.json").
+		Reply(200).
+		Type("application/json").
+		File("testdata/milestone.json")
+
+	client, _ := New("https://try.gitea.io")
+	dueDate, _ := time.Parse(scm.SearchTimeFormat, "2012-10-09T23:39:01Z")
+	input := &scm.MilestoneInput{
+		Title:       "v1.0",
+		Description: "Tracking milestone for version 1.0",
+		State:       "open",
+		DueDate:     dueDate,
+	}
+	got, _, err := client.Milestones.Create(context.Background(), "jcitizen/my-repo", input)
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Milestone)
+	raw, _ := ioutil.ReadFile("testdata/milestone.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestMilestoneUpdate(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Patch("/api/v1/repos/jcitizen/my-repo/milestones").
+		File("testdata/milestone_create.json").
+		Reply(200).
+		Type("application/json").
+		File("testdata/milestone.json")
+
+	client, _ := New("https://try.gitea.io")
+	dueDate, _ := time.Parse(scm.SearchTimeFormat, "2012-10-09T23:39:01Z")
+	input := &scm.MilestoneInput{
+		Title:       "v1.0",
+		Description: "Tracking milestone for version 1.0",
+		State:       "open",
+		DueDate:     dueDate,
+	}
+	got, _, err := client.Milestones.Update(context.Background(), "jcitizen/my-repo", 1, input)
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Milestone)
+	raw, _ := ioutil.ReadFile("testdata/milestone.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestMilestoneDelete(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Delete("/api/v1/repos/jcitizen/my-repo/milestones/1").
+		Reply(200).
+		Type("application/json")
+
+	client, _ := New("https://try.gitea.io")
+	_, err := client.Milestones.Delete(context.Background(), "jcitizen/my-repo", 1)
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+var mockPageHeaders = map[string]string{
+	"Link": `<https://try.gitea.io/v1/resource?page=2>; rel="next",` +
+		`<https://try.gitea.io/v1/resource?page=1>; rel="prev",` +
+		`<https://try.gitea.io/v1/resource?page=1>; rel="first",` +
+		`<https://try.gitea.io/v1/resource?page=5>; rel="last"`,
+}
+
+func mockServerVersion() {
+	gock.New("https://try.gitea.io").
+		Get("/api/v1/version").
+		Reply(200).
+		Type("application/json").
+		File("testdata/version.json")
+}
diff --git a/scm/driver/gitea/org.go b/scm/driver/gitea/org.go
index c7c7efc03..6ca8ec7ea 100644
--- a/scm/driver/gitea/org.go
+++ b/scm/driver/gitea/org.go
@@ -23,7 +23,13 @@ func (s *organizationService) Find(ctx context.Context, name string) (*scm.Organ
 }
 
 func (s *organizationService) FindMembership(ctx context.Context, name, username string) (*scm.Membership, *scm.Response, error) {
-	return nil, nil, scm.ErrNotSupported
+	membership := new(membership)
+	membership.Active = s.checkMembership(ctx, name, username)
+	out := new(permissions)
+	path := fmt.Sprintf("api/v1/users/%s/orgs/%s/permissions", username, name)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	membership.Permissions = out
+	return convertMembership(membership), res, err
 }
 
 func (s *organizationService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Organization, *scm.Response, error) {
@@ -33,6 +39,18 @@ func (s *organizationService) List(ctx context.Context, opts scm.ListOptions) ([
 	return convertOrgList(out), res, err
 }
 
+type permissions struct {
+	IsOwner             bool `json:"is_owner"`
+	IsAdmin             bool `json:"is_admin"`
+	CanWrite            bool `json:"can_write"`
+	CanRead             bool `json:"can_read"`
+	CanCreateRepository bool `json:"can_create_repository"`
+}
+type membership struct {
+	Permissions *permissions
+	Active      bool
+}
+
 //
 // native data structures
 //
@@ -60,3 +78,27 @@ func convertOrg(from *org) *scm.Organization {
 		Avatar: from.Avatar,
 	}
 }
+
+func (s *organizationService) checkMembership(ctx context.Context, name, username string) bool {
+	path := fmt.Sprintf("api/v1/orgs/%s/members/%s", name, username)
+	res, err := s.client.do(ctx, "GET", path, nil, nil)
+	if err != nil {
+		return false
+	}
+	return res.Status == 204
+}
+
+func convertMembership(from *membership) *scm.Membership {
+	to := new(scm.Membership)
+	to.Active = from.Active
+	isAdmin := from.Permissions.IsAdmin
+	isMember := from.Permissions.CanRead || from.Permissions.CanWrite || from.Permissions.CanCreateRepository
+	if isAdmin {
+		to.Role = scm.RoleAdmin
+	} else if isMember {
+		to.Role = scm.RoleMember
+	} else {
+		to.Role = scm.RoleUndefined
+	}
+	return to
+}
diff --git a/scm/driver/gitea/org_test.go b/scm/driver/gitea/org_test.go
index 90a0a193c..ceaba401b 100644
--- a/scm/driver/gitea/org_test.go
+++ b/scm/driver/gitea/org_test.go
@@ -42,10 +42,31 @@ func TestOrgFind(t *testing.T) {
 }
 
 func TestOrganizationFindMembership(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://try.gitea.io").
+		Get("/api/v1/orgs/gogits/members/jcitizen").
+		Reply(204)
+
+	gock.New("https://try.gitea.io").
+		Get("/api/v1/users/jcitizen/orgs/gogits/permissions").
+		Reply(200).
+		Type("application/json").
+		File("testdata/permissions.json")
+
 	client, _ := New("https://try.gitea.io")
-	_, _, err := client.Organizations.FindMembership(context.Background(), "gogits", "jcitizen")
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	got, _, err := client.Organizations.FindMembership(context.Background(), "gogits", "jcitizen")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.Membership)
+	raw, _ := ioutil.ReadFile("testdata/membership.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
 	}
 }
 
diff --git a/scm/driver/gitea/pr.go b/scm/driver/gitea/pr.go
index ab7fc4142..754f46240 100644
--- a/scm/driver/gitea/pr.go
+++ b/scm/driver/gitea/pr.go
@@ -38,6 +38,10 @@ func (s *pullService) ListComments(context.Context, string, int, scm.ListOptions
 	return nil, nil, scm.ErrNotSupported
 }
 
+func (s *pullService) ListCommits(context.Context, string, int, scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
 func (s *pullService) ListChanges(context.Context, string, int, scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
 	return nil, nil, scm.ErrNotSupported
 }
diff --git a/scm/driver/gitea/pr_test.go b/scm/driver/gitea/pr_test.go
index 6bff3bb3c..fc2d88e97 100644
--- a/scm/driver/gitea/pr_test.go
+++ b/scm/driver/gitea/pr_test.go
@@ -171,3 +171,11 @@ func TestPullRequestCommentDelete(t *testing.T) {
 		t.Errorf("Expect Not Supported error")
 	}
 }
+
+func TestPullListCommits(t *testing.T) {
+	client, _ := New("https://try.gitea.io")
+	_, _, err := client.PullRequests.ListCommits(context.Background(), "go-gitea/gitea", 1, scm.ListOptions{})
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
diff --git a/scm/driver/gitea/release.go b/scm/driver/gitea/release.go
new file mode 100644
index 000000000..f64eab834
--- /dev/null
+++ b/scm/driver/gitea/release.go
@@ -0,0 +1,157 @@
+package gitea
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases/%d", namespace, name, id)
+	out := new(release)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases/tags/%s", namespace, name, tag)
+	out := new(release)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases?%s", namespace, name, encodeReleaseListOptions(releaseListOptionsToGiteaListOptions(opts)))
+	out := []*release{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertReleaseList(out), res, err
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases", namespace, name)
+	in := &ReleaseInput{
+		TagName:      input.Tag,
+		Target:       input.Commitish,
+		Title:        input.Title,
+		Note:         input.Description,
+		IsDraft:      input.Draft,
+		IsPrerelease: input.Prerelease,
+	}
+	out := new(release)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases/%d", namespace, name, id)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases/tags/%s", namespace, name, tag)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("api/v1/repos/%s/%s/releases/%d", namespace, name, id)
+	in := &ReleaseInput{
+		TagName:      input.Tag,
+		Target:       input.Commitish,
+		Title:        input.Title,
+		Note:         input.Description,
+		IsDraft:      input.Draft,
+		IsPrerelease: input.Prerelease,
+	}
+	out := new(release)
+	res, err := s.client.do(ctx, "PATCH", path, in, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	rel, _, err := s.FindByTag(ctx, repo, tag)
+	if err != nil {
+		return nil, nil, err
+	}
+	return s.Update(ctx, repo, rel.ID, input)
+}
+
+type ReleaseInput struct {
+	TagName      string `json:"tag_name"`
+	Target       string `json:"target_commitish"`
+	Title        string `json:"name"`
+	Note         string `json:"body"`
+	IsDraft      bool   `json:"draft"`
+	IsPrerelease bool   `json:"prerelease"`
+}
+
+// release represents a repository release
+type release struct {
+	ID           int64         `json:"id"`
+	TagName      string        `json:"tag_name"`
+	Target       string        `json:"target_commitish"`
+	Title        string        `json:"name"`
+	Note         string        `json:"body"`
+	URL          string        `json:"url"`
+	HTMLURL      string        `json:"html_url"`
+	TarURL       string        `json:"tarball_url"`
+	ZipURL       string        `json:"zipball_url"`
+	IsDraft      bool          `json:"draft"`
+	IsPrerelease bool          `json:"prerelease"`
+	CreatedAt    null.Time     `json:"created_at"`
+	PublishedAt  null.Time     `json:"published_at"`
+	Publisher    *user         `json:"author"`
+	Attachments  []*Attachment `json:"assets"`
+}
+
+type Attachment struct {
+	ID            int64     `json:"id"`
+	Name          string    `json:"name"`
+	Size          int64     `json:"size"`
+	DownloadCount int64     `json:"download_count"`
+	Created       null.Time `json:"created_at"`
+	UUID          string    `json:"uuid"`
+	DownloadURL   string    `json:"browser_download_url"`
+}
+
+func convertRelease(src *release) *scm.Release {
+	return &scm.Release{
+		ID:          int(src.ID),
+		Title:       src.Title,
+		Description: src.Note,
+		Link:        convertAPIURLToHTMLURL(src.URL, src.TagName),
+		Tag:         src.TagName,
+		Commitish:   src.Target,
+		Draft:       src.IsDraft,
+		Prerelease:  src.IsPrerelease,
+		Created:     src.CreatedAt.ValueOrZero(),
+		Published:   src.PublishedAt.ValueOrZero(),
+	}
+}
+
+func convertReleaseList(src []*release) []*scm.Release {
+	var dst []*scm.Release
+	for _, v := range src {
+		dst = append(dst, convertRelease(v))
+	}
+	return dst
+}
+
+func releaseListOptionsToGiteaListOptions(in scm.ReleaseListOptions) ListOptions {
+	return ListOptions{
+		Page:     in.Page,
+		PageSize: in.Size,
+	}
+}
diff --git a/scm/driver/gitea/release_test.go b/scm/driver/gitea/release_test.go
new file mode 100644
index 000000000..4cf491890
--- /dev/null
+++ b/scm/driver/gitea/release_test.go
@@ -0,0 +1,352 @@
+package gitea
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestConvertAPIURLToHTMLURL(t *testing.T) {
+
+	got := convertAPIURLToHTMLURL("https://try.gitea.com/api/v1/repos/octocat/Hello-World/123", "v1.0.0")
+	want := "https://try.gitea.com/octocat/Hello-World/releases/tag/v1.0.0"
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		t.Log("got:")
+		t.Log(string(got))
+	}
+
+}
+
+func TestConvertAPIURLToHTMLURLEmptyLinkWhenURLParseFails(t *testing.T) {
+
+	broken := []string{"http s://try.gitea.com/api/v1/repos/octocat/Hello-World/123", "https://try.gitea.com/api/v1/repos/octocat/Hello-World"}
+	for _, url := range broken {
+
+		got := convertAPIURLToHTMLURL(url, "v1.0.0")
+		want := ""
+
+		if diff := cmp.Diff(got, want); diff != "" {
+			t.Errorf("Unexpected Results")
+			t.Log(diff)
+
+			t.Log("got:")
+			t.Log(string(got))
+		}
+	}
+
+}
+
+func TestReleaseFind(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Get("/repos/octocat/hello-world/releases/1").
+		Reply(200).
+		Type("application/json").
+		File("testdata/release.json")
+
+	client, err := New("https://try.gitea.io")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	got, _, err := client.Releases.Find(context.Background(), "octocat/hello-world", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+}
+
+func TestReleaseFindByTag(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Get("/repos/octocat/hello-world/releases/tags/v1.0.0").
+		Reply(200).
+		Type("application/json").
+		File("testdata/release.json")
+
+	client, err := New("https://try.gitea.io")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	got, _, err := client.Releases.FindByTag(context.Background(), "octocat/hello-world", "v1.0.0")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+}
+
+func TestReleaseList(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Get("/repos/octocat/hello-world/releases").
+		MatchParam("page", "1").
+		MatchParam("limit", "30").
+		Reply(200).
+		Type("application/json").
+		File("testdata/releases.json")
+
+	client, err := New("https://try.gitea.io")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	got, _, err := client.Releases.List(context.Background(), "octocat/hello-world", scm.ReleaseListOptions{Page: 1, Size: 30, Open: true, Closed: true})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Release{}
+	raw, _ := ioutil.ReadFile("testdata/releases.json.golden")
+	err = json.Unmarshal(raw, &want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+}
+
+func TestReleaseCreate(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Post("/repos/octocat/hello-world/releases").
+		File("testdata/release_create.json").
+		Reply(200).
+		Type("application/json").
+		File("testdata/release.json")
+
+	client, err := New("https://try.gitea.io")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	input := &scm.ReleaseInput{
+		Title:       "v1.0.0",
+		Description: "Description of the release",
+		Tag:         "v1.0.0",
+		Commitish:   "master",
+		Draft:       false,
+		Prerelease:  false,
+	}
+
+	got, _, err := client.Releases.Create(context.Background(), "octocat/hello-world", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+}
+
+func TestReleaseUpdate(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Patch("/repos/octocat/hello-world/releases/1").
+		File("testdata/release_update.json").
+		Reply(200).
+		Type("application/json").
+		File("testdata/release.json")
+
+	client, err := New("https://try.gitea.io")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	input := &scm.ReleaseInput{
+		Title:       "v1.0.0",
+		Description: "Description of the release",
+		Tag:         "v1.0.0",
+		Commitish:   "master",
+		Draft:       false,
+		Prerelease:  false,
+	}
+	got, _, err := client.Releases.Update(context.Background(), "octocat/hello-world", 1, input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+}
+
+func TestReleaseUpdateByTag(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Get("/repos/octocat/hello-world/releases/tags/v1.0.0").
+		Reply(200).
+		Type("application/json").
+		File("testdata/release.json")
+
+	gock.New("https://try.gitea.io").
+		Patch("/repos/octocat/hello-world/releases/1").
+		File("testdata/release_update.json").
+		Reply(200).
+		Type("application/json").
+		File("testdata/release.json")
+
+	client, err := New("https://try.gitea.io")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	input := &scm.ReleaseInput{
+		Title:       "v1.0.0",
+		Description: "Description of the release",
+		Tag:         "v1.0.0",
+		Commitish:   "master",
+		Draft:       false,
+		Prerelease:  false,
+	}
+	got, _, err := client.Releases.UpdateByTag(context.Background(), "octocat/hello-world", "v1.0.0", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+}
+
+func TestReleaseDelete(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Delete("/repos/octocat/hello-world/releases/1").
+		Reply(200).
+		Type("application/json")
+
+	client, err := New("https://try.gitea.io")
+	_, err = client.Releases.Delete(context.Background(), "octocat/hello-world", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+}
+
+func TestReleaseDeleteByTag(t *testing.T) {
+	defer gock.Off()
+
+	mockServerVersion()
+
+	gock.New("https://try.gitea.io").
+		Delete("/repos/octocat/hello-world/releases/tags/v1.0.0").
+		Reply(200).
+		Type("application/json")
+
+	client, err := New("https://try.gitea.io")
+	_, err = client.Releases.DeleteByTag(context.Background(), "octocat/hello-world", "v1.0.0")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+}
diff --git a/scm/driver/gitea/repo.go b/scm/driver/gitea/repo.go
index 154c7f322..81d92a5f5 100644
--- a/scm/driver/gitea/repo.go
+++ b/scm/driver/gitea/repo.go
@@ -46,6 +46,18 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
 	return convertRepositoryList(out), res, err
 }
 
+func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// gitea does not support search filters, hence calling List api without search filtering
+	return s.List(ctx, opts.ListOptions)
+}
+
+func (s *repositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("api/v1/orgs/%s/repos?%s", namespace, encodeListOptions(opts))
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+
 func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
 	path := fmt.Sprintf("api/v1/repos/%s/hooks?%s", repo, encodeListOptions(opts))
 	out := []*hook{}
@@ -148,6 +160,7 @@ type (
 		CreatedAt     time.Time `json:"created_at"`
 		UpdatedAt     time.Time `json:"updated_at"`
 		Permissions   perm      `json:"permissions"`
+		Archived      bool      `json:"archived"`
 	}
 
 	// gitea permissions details.
@@ -215,6 +228,7 @@ func convertRepository(src *repository) *scm.Repository {
 		Clone:     src.CloneURL,
 		CloneSSH:  src.SSHURL,
 		Link:      src.HTMLURL,
+		Archived:  src.Archived,
 	}
 }
 
diff --git a/scm/driver/gitea/testdata/commit.json b/scm/driver/gitea/testdata/commit.json
new file mode 100644
index 000000000..d43d473de
--- /dev/null
+++ b/scm/driver/gitea/testdata/commit.json
@@ -0,0 +1,39 @@
+{
+    "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+    "sha": "c43399cad8766ee521b873a32c1652407c5a4630",
+    "html_url": "https://try.gitea.io/gitea/gitea/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+    "commit": {
+        "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+        "author": {
+            "name": "Lewis Cowles",
+            "email": "lewiscowles@me.com",
+            "date": "2018-09-09T03:36:08Z"
+        },
+        "committer": {
+            "name": "Lunny Xiao",
+            "email": "xiaolunwen@gmail.com",
+            "date": "2018-09-09T03:36:08Z"
+        },
+        "message": "Fixes repo branch endpoint summary (#4893)",
+        "tree": {
+            "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/trees/c43399cad8766ee521b873a32c1652407c5a4630",
+            "sha": "c43399cad8766ee521b873a32c1652407c5a4630"
+        }
+    },
+    "author": null,
+    "committer": {
+        "id": 3,
+        "login": "lunny",
+        "full_name": "Lunny Xiao",
+        "email": "xiaolunwen@gmail.com",
+        "avatar_url": "https://secure.gravatar.com/avatar/271fc56bcea89c6f69ab0024b59b3f81?d=identicon",
+        "language": "zh-CN",
+        "username": "lunny"
+    },
+    "parents": [
+        {
+            "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/d293a2b9d6722dffde7998c953c3087e47a38a83",
+            "sha": "d293a2b9d6722dffde7998c953c3087e47a38a83"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/commit.json.golden b/scm/driver/gitea/testdata/commit.json.golden
new file mode 100644
index 000000000..da61dbddd
--- /dev/null
+++ b/scm/driver/gitea/testdata/commit.json.golden
@@ -0,0 +1,11 @@
+{
+    "committer": {
+        "name": "Lunny Xiao",
+        "login": "lunny",
+        "email": "xiaolunwen@gmail.com",
+        "avatar": "https://secure.gravatar.com/avatar/271fc56bcea89c6f69ab0024b59b3f81?d=identicon"
+    },
+    "link": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+    "sha": "c43399cad8766ee521b873a32c1652407c5a4630",
+    "message": "Fixes repo branch endpoint summary (#4893)"
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/commits.json b/scm/driver/gitea/testdata/commits.json
index 1478fa4be..936a6029d 100644
--- a/scm/driver/gitea/testdata/commits.json
+++ b/scm/driver/gitea/testdata/commits.json
@@ -1 +1,41 @@
-{"url":"https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630","sha":"c43399cad8766ee521b873a32c1652407c5a4630","html_url":"https://try.gitea.io/gitea/gitea/commits/c43399cad8766ee521b873a32c1652407c5a4630","commit":{"url":"https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630","author":{"name":"Lewis Cowles","email":"lewiscowles@me.com","date":"2018-09-09T03:36:08Z"},"committer":{"name":"Lunny Xiao","email":"xiaolunwen@gmail.com","date":"2018-09-09T03:36:08Z"},"message":"Fixes repo branch endpoint summary (#4893)","tree":{"url":"https://try.gitea.io/api/v1/repos/gitea/gitea/trees/c43399cad8766ee521b873a32c1652407c5a4630","sha":"c43399cad8766ee521b873a32c1652407c5a4630"}},"author":null,"committer":{"id":3,"login":"lunny","full_name":"Lunny Xiao","email":"xiaolunwen@gmail.com","avatar_url":"https://secure.gravatar.com/avatar/271fc56bcea89c6f69ab0024b59b3f81?d=identicon","language":"zh-CN","username":"lunny"},"parents":[{"url":"https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/d293a2b9d6722dffde7998c953c3087e47a38a83","sha":"d293a2b9d6722dffde7998c953c3087e47a38a83"}]}
\ No newline at end of file
+[
+    {
+        "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+        "sha": "c43399cad8766ee521b873a32c1652407c5a4630",
+        "html_url": "https://try.gitea.io/gitea/gitea/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+        "commit": {
+            "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+            "author": {
+                "name": "Lewis Cowles",
+                "email": "lewiscowles@me.com",
+                "date": "2018-09-09T03:36:08Z"
+            },
+            "committer": {
+                "name": "Lunny Xiao",
+                "email": "xiaolunwen@gmail.com",
+                "date": "2018-09-09T03:36:08Z"
+            },
+            "message": "Fixes repo branch endpoint summary (#4893)",
+            "tree": {
+                "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/trees/c43399cad8766ee521b873a32c1652407c5a4630",
+                "sha": "c43399cad8766ee521b873a32c1652407c5a4630"
+            }
+        },
+        "author": null,
+        "committer": {
+            "id": 3,
+            "login": "lunny",
+            "full_name": "Lunny Xiao",
+            "email": "xiaolunwen@gmail.com",
+            "avatar_url": "https://secure.gravatar.com/avatar/271fc56bcea89c6f69ab0024b59b3f81?d=identicon",
+            "language": "zh-CN",
+            "username": "lunny"
+        },
+        "parents": [
+            {
+                "url": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/d293a2b9d6722dffde7998c953c3087e47a38a83",
+                "sha": "d293a2b9d6722dffde7998c953c3087e47a38a83"
+            }
+        ]
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/commits.json.golden b/scm/driver/gitea/testdata/commits.json.golden
index da61dbddd..bb9d78ed4 100644
--- a/scm/driver/gitea/testdata/commits.json.golden
+++ b/scm/driver/gitea/testdata/commits.json.golden
@@ -1,11 +1,13 @@
-{
-    "committer": {
-        "name": "Lunny Xiao",
-        "login": "lunny",
-        "email": "xiaolunwen@gmail.com",
-        "avatar": "https://secure.gravatar.com/avatar/271fc56bcea89c6f69ab0024b59b3f81?d=identicon"
-    },
-    "link": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
-    "sha": "c43399cad8766ee521b873a32c1652407c5a4630",
-    "message": "Fixes repo branch endpoint summary (#4893)"
-}
\ No newline at end of file
+[
+    {
+        "committer": {
+            "name": "Lunny Xiao",
+            "login": "lunny",
+            "email": "xiaolunwen@gmail.com",
+            "avatar": "https://secure.gravatar.com/avatar/271fc56bcea89c6f69ab0024b59b3f81?d=identicon"
+        },
+        "link": "https://try.gitea.io/api/v1/repos/gitea/gitea/git/commits/c43399cad8766ee521b873a32c1652407c5a4630",
+        "sha": "c43399cad8766ee521b873a32c1652407c5a4630",
+        "message": "Fixes repo branch endpoint summary (#4893)"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/content_list.json b/scm/driver/gitea/testdata/content_list.json
index 72efd49f6..b7b2871a4 100644
--- a/scm/driver/gitea/testdata/content_list.json
+++ b/scm/driver/gitea/testdata/content_list.json
@@ -1 +1,82 @@
-[{"name":"advanced.en-us.md","path":"docs/content/doc/advanced.en-us.md","sha":"de2bfeed6f2d3ea1d2f91e457e9450efc582bb31","type":"file","size":192,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.en-us.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.en-us.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/de2bfeed6f2d3ea1d2f91e457e9450efc582bb31","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/advanced.en-us.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.en-us.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/de2bfeed6f2d3ea1d2f91e457e9450efc582bb31","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.en-us.md"}},{"name":"advanced.fr-fr.md","path":"docs/content/doc/advanced.fr-fr.md","sha":"04734a8f81f5641de2b6d738bc9b777c2cc541c6","type":"file","size":190,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.fr-fr.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.fr-fr.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/04734a8f81f5641de2b6d738bc9b777c2cc541c6","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/advanced.fr-fr.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.fr-fr.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/04734a8f81f5641de2b6d738bc9b777c2cc541c6","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.fr-fr.md"}},{"name":"advanced.zh-cn.md","path":"docs/content/doc/advanced.zh-cn.md","sha":"1f7ebf81e723a0ba82a89eec18a2706ac1b0dace","type":"file","size":188,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.zh-cn.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.zh-cn.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/1f7ebf81e723a0ba82a89eec18a2706ac1b0dace","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/advanced.zh-cn.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.zh-cn.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/1f7ebf81e723a0ba82a89eec18a2706ac1b0dace","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.zh-cn.md"}},{"name":"advanced","path":"docs/content/doc/advanced","sha":"8c43a4fb023735dfd026014bae92ad91414b88bd","type":"dir","size":0,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/8c43a4fb023735dfd026014bae92ad91414b88bd","download_url":null,"submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/8c43a4fb023735dfd026014bae92ad91414b88bd","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced"}},{"name":"features.en-us.md","path":"docs/content/doc/features.en-us.md","sha":"9cc90b46a0d17864e5ce1749dbd1609a0921f8f7","type":"file","size":192,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features.en-us.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features.en-us.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/9cc90b46a0d17864e5ce1749dbd1609a0921f8f7","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/features.en-us.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features.en-us.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/9cc90b46a0d17864e5ce1749dbd1609a0921f8f7","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features.en-us.md"}},{"name":"features.zh-cn.md","path":"docs/content/doc/features.zh-cn.md","sha":"68d2f71b11f6bb5b1ca31adad00bc0f6ed3e94d8","type":"file","size":188,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features.zh-cn.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features.zh-cn.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/68d2f71b11f6bb5b1ca31adad00bc0f6ed3e94d8","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/features.zh-cn.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features.zh-cn.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/68d2f71b11f6bb5b1ca31adad00bc0f6ed3e94d8","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features.zh-cn.md"}},{"name":"features.zh-tw.md","path":"docs/content/doc/features.zh-tw.md","sha":"889e0df88213a937914ba43bb52956d0e54b1c98","type":"file","size":188,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features.zh-tw.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features.zh-tw.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/889e0df88213a937914ba43bb52956d0e54b1c98","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/features.zh-tw.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features.zh-tw.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/889e0df88213a937914ba43bb52956d0e54b1c98","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features.zh-tw.md"}},{"name":"features","path":"docs/content/doc/features","sha":"e212486606cf756b3b626f0a5bb1dd386b83b698","type":"dir","size":0,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/e212486606cf756b3b626f0a5bb1dd386b83b698","download_url":null,"submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/features?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/e212486606cf756b3b626f0a5bb1dd386b83b698","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/features"}},{"name":"help.en-us.md","path":"docs/content/doc/help.en-us.md","sha":"5ad1dd7f1edb3b78a3e3fba2113ecd5b10625dc4","type":"file","size":176,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/help.en-us.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/help.en-us.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/5ad1dd7f1edb3b78a3e3fba2113ecd5b10625dc4","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/help.en-us.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/help.en-us.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/5ad1dd7f1edb3b78a3e3fba2113ecd5b10625dc4","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/help.en-us.md"}},{"name":"help.zh-cn.md","path":"docs/content/doc/help.zh-cn.md","sha":"6af7aa1719b99e35874b76911aff29b9ad4bbc68","type":"file","size":180,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/help.zh-cn.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/help.zh-cn.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/6af7aa1719b99e35874b76911aff29b9ad4bbc68","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/help.zh-cn.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/help.zh-cn.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/6af7aa1719b99e35874b76911aff29b9ad4bbc68","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/help.zh-cn.md"}},{"name":"help","path":"docs/content/doc/help","sha":"91adc879a877f6c44aab1f27453bfa2679e4c3a0","type":"dir","size":0,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/help?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/help","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/91adc879a877f6c44aab1f27453bfa2679e4c3a0","download_url":null,"submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/help?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/91adc879a877f6c44aab1f27453bfa2679e4c3a0","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/help"}},{"name":"installation.en-us.md","path":"docs/content/doc/installation.en-us.md","sha":"4257521d973b0d28a7040f9030f701bbd1bf7bef","type":"file","size":208,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.en-us.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.en-us.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/4257521d973b0d28a7040f9030f701bbd1bf7bef","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/installation.en-us.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.en-us.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/4257521d973b0d28a7040f9030f701bbd1bf7bef","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.en-us.md"}},{"name":"installation.fr-fr.md","path":"docs/content/doc/installation.fr-fr.md","sha":"55b48bda3eca4e459c12e44d54de9c4573d9b0b7","type":"file","size":208,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.fr-fr.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.fr-fr.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/55b48bda3eca4e459c12e44d54de9c4573d9b0b7","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/installation.fr-fr.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.fr-fr.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/55b48bda3eca4e459c12e44d54de9c4573d9b0b7","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.fr-fr.md"}},{"name":"installation.zh-cn.md","path":"docs/content/doc/installation.zh-cn.md","sha":"8f57e0f00c273619c1ce66f04f7536f62d830e88","type":"file","size":196,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.zh-cn.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.zh-cn.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/8f57e0f00c273619c1ce66f04f7536f62d830e88","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/installation.zh-cn.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.zh-cn.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/8f57e0f00c273619c1ce66f04f7536f62d830e88","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.zh-cn.md"}},{"name":"installation.zh-tw.md","path":"docs/content/doc/installation.zh-tw.md","sha":"f955e994ac09834f546212a6ec494978a4d10039","type":"file","size":196,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.zh-tw.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.zh-tw.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/f955e994ac09834f546212a6ec494978a4d10039","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/installation.zh-tw.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation.zh-tw.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/f955e994ac09834f546212a6ec494978a4d10039","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation.zh-tw.md"}},{"name":"installation","path":"docs/content/doc/installation","sha":"2a3ff66218ae980c07444b8c97b710b3d7b5b846","type":"dir","size":0,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/2a3ff66218ae980c07444b8c97b710b3d7b5b846","download_url":null,"submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/installation?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/2a3ff66218ae980c07444b8c97b710b3d7b5b846","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/installation"}},{"name":"upgrade.en-us.md","path":"docs/content/doc/upgrade.en-us.md","sha":"9623ff9f1eaf74e9ebc03f5869bc0b9d2b3372d3","type":"file","size":188,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.en-us.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.en-us.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/9623ff9f1eaf74e9ebc03f5869bc0b9d2b3372d3","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/upgrade.en-us.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.en-us.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/9623ff9f1eaf74e9ebc03f5869bc0b9d2b3372d3","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.en-us.md"}},{"name":"upgrade.fr-fr.md","path":"docs/content/doc/upgrade.fr-fr.md","sha":"ca08daf9c096bf027ba95a13b44c5fe4d0a47afe","type":"file","size":198,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.fr-fr.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.fr-fr.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/ca08daf9c096bf027ba95a13b44c5fe4d0a47afe","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/upgrade.fr-fr.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.fr-fr.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/ca08daf9c096bf027ba95a13b44c5fe4d0a47afe","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.fr-fr.md"}},{"name":"upgrade.zh-cn.md","path":"docs/content/doc/upgrade.zh-cn.md","sha":"d12e150b9cbce2c26acb64c724914cec8667bb5d","type":"file","size":186,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.zh-cn.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.zh-cn.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/d12e150b9cbce2c26acb64c724914cec8667bb5d","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/upgrade.zh-cn.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.zh-cn.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/d12e150b9cbce2c26acb64c724914cec8667bb5d","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.zh-cn.md"}},{"name":"upgrade.zh-tw.md","path":"docs/content/doc/upgrade.zh-tw.md","sha":"b57fccbdd578e9c981171838a9dd1e152f69bce9","type":"file","size":186,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.zh-tw.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.zh-tw.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/b57fccbdd578e9c981171838a9dd1e152f69bce9","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/upgrade.zh-tw.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade.zh-tw.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/b57fccbdd578e9c981171838a9dd1e152f69bce9","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade.zh-tw.md"}},{"name":"upgrade","path":"docs/content/doc/upgrade","sha":"ff064862ee6ca9aa7bfda908c5354c83fbac25a4","type":"dir","size":0,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/ff064862ee6ca9aa7bfda908c5354c83fbac25a4","download_url":null,"submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/upgrade?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/ff064862ee6ca9aa7bfda908c5354c83fbac25a4","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/upgrade"}},{"name":"usage.en-us.md","path":"docs/content/doc/usage.en-us.md","sha":"6be9769d64603f93e05e099a8b2dcbceae1e98df","type":"file","size":180,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/usage.en-us.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/usage.en-us.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/6be9769d64603f93e05e099a8b2dcbceae1e98df","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/usage.en-us.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/usage.en-us.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/6be9769d64603f93e05e099a8b2dcbceae1e98df","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/usage.en-us.md"}},{"name":"usage.zh-cn.md","path":"docs/content/doc/usage.zh-cn.md","sha":"c533df662481ea381cba02f0cb7996a3c1b377fd","type":"file","size":194,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/usage.zh-cn.md?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/usage.zh-cn.md","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/c533df662481ea381cba02f0cb7996a3c1b377fd","download_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/usage.zh-cn.md","submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/usage.zh-cn.md?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/c533df662481ea381cba02f0cb7996a3c1b377fd","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/usage.zh-cn.md"}},{"name":"usage","path":"docs/content/doc/usage","sha":"ac7d00a3401c3299068e09da540badfaf29d5e58","type":"dir","size":0,"encoding":null,"content":null,"target":null,"url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/usage?ref=master","html_url":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/usage","git_url":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/ac7d00a3401c3299068e09da540badfaf29d5e58","download_url":null,"submodule_git_url":null,"_links":{"self":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/usage?ref=master","git":"https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/ac7d00a3401c3299068e09da540badfaf29d5e58","html":"https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/usage"}}]
\ No newline at end of file
+[
+    {
+        "name": "advanced.en-us.md",
+        "path": "docs/content/doc/advanced.en-us.md",
+        "sha": "de2bfeed6f2d3ea1d2f91e457e9450efc582bb31",
+        "type": "file",
+        "size": 192,
+        "encoding": null,
+        "content": null,
+        "target": null,
+        "url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.en-us.md?ref=master",
+        "html_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.en-us.md",
+        "git_url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/de2bfeed6f2d3ea1d2f91e457e9450efc582bb31",
+        "download_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/advanced.en-us.md",
+        "submodule_git_url": null,
+        "_links": {
+            "self": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.en-us.md?ref=master",
+            "git": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/de2bfeed6f2d3ea1d2f91e457e9450efc582bb31",
+            "html": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.en-us.md"
+        }
+    },
+    {
+        "name": "advanced.fr-fr.md",
+        "path": "docs/content/doc/advanced.fr-fr.md",
+        "sha": "04734a8f81f5641de2b6d738bc9b777c2cc541c6",
+        "type": "file",
+        "size": 190,
+        "encoding": null,
+        "content": null,
+        "target": null,
+        "url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.fr-fr.md?ref=master",
+        "html_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.fr-fr.md",
+        "git_url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/04734a8f81f5641de2b6d738bc9b777c2cc541c6",
+        "download_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/advanced.fr-fr.md",
+        "submodule_git_url": null,
+        "_links": {
+            "self": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.fr-fr.md?ref=master",
+            "git": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/04734a8f81f5641de2b6d738bc9b777c2cc541c6",
+            "html": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.fr-fr.md"
+        }
+    },
+    {
+        "name": "advanced.zh-cn.md",
+        "path": "docs/content/doc/advanced.zh-cn.md",
+        "sha": "1f7ebf81e723a0ba82a89eec18a2706ac1b0dace",
+        "type": "file",
+        "size": 188,
+        "encoding": null,
+        "content": null,
+        "target": null,
+        "url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.zh-cn.md?ref=master",
+        "html_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.zh-cn.md",
+        "git_url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/1f7ebf81e723a0ba82a89eec18a2706ac1b0dace",
+        "download_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/raw/branch/master/docs/content/doc/advanced.zh-cn.md",
+        "submodule_git_url": null,
+        "_links": {
+            "self": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced.zh-cn.md?ref=master",
+            "git": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/1f7ebf81e723a0ba82a89eec18a2706ac1b0dace",
+            "html": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced.zh-cn.md"
+        }
+    },
+    {
+        "name": "advanced",
+        "path": "docs/content/doc/advanced",
+        "sha": "8c43a4fb023735dfd026014bae92ad91414b88bd",
+        "type": "dir",
+        "size": 0,
+        "encoding": null,
+        "content": null,
+        "target": null,
+        "url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced?ref=master",
+        "html_url": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced",
+        "git_url": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/8c43a4fb023735dfd026014bae92ad91414b88bd",
+        "download_url": null,
+        "submodule_git_url": null,
+        "_links": {
+            "self": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/contents/docs/content/doc/advanced?ref=master",
+            "git": "https://try.gitea.io/api/v1/repos/aaaaaz/go-gitea.gitea/git/blobs/8c43a4fb023735dfd026014bae92ad91414b88bd",
+            "html": "https://try.gitea.io/aaaaaz/go-gitea.gitea/src/branch/master/docs/content/doc/advanced"
+        }
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/content_list.json.golden b/scm/driver/gitea/testdata/content_list.json.golden
index cc23f85ff..7b6547bab 100644
--- a/scm/driver/gitea/testdata/content_list.json.golden
+++ b/scm/driver/gitea/testdata/content_list.json.golden
@@ -1,98 +1,22 @@
 [
   {
     "path": "docs/content/doc/advanced.en-us.md",
-    "kind": "file"
+    "kind": "file",
+    "blobid": "de2bfeed6f2d3ea1d2f91e457e9450efc582bb31"
   },
   {
     "path": "docs/content/doc/advanced.fr-fr.md",
-    "kind": "file"
+    "kind": "file",
+    "blobid": "04734a8f81f5641de2b6d738bc9b777c2cc541c6"
   },
   {
     "path": "docs/content/doc/advanced.zh-cn.md",
-    "kind": "file"
+    "kind": "file",
+    "blobid": "1f7ebf81e723a0ba82a89eec18a2706ac1b0dace"
   },
   {
     "path": "docs/content/doc/advanced",
-    "kind": "directory"
-  },
-  {
-    "path": "docs/content/doc/features.en-us.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/features.zh-cn.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/features.zh-tw.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/features",
-    "kind": "directory"
-  },
-  {
-    "path": "docs/content/doc/help.en-us.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/help.zh-cn.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/help",
-    "kind": "directory"
-  },
-  {
-    "path": "docs/content/doc/installation.en-us.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/installation.fr-fr.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/installation.zh-cn.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/installation.zh-tw.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/installation",
-    "kind": "directory"
-  },
-  {
-    "path": "docs/content/doc/upgrade.en-us.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/upgrade.fr-fr.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/upgrade.zh-cn.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/upgrade.zh-tw.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/upgrade",
-    "kind": "directory"
-  },
-  {
-    "path": "docs/content/doc/usage.en-us.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/usage.zh-cn.md",
-    "kind": "file"
-  },
-  {
-    "path": "docs/content/doc/usage",
-    "kind": "directory"
+    "kind": "directory",
+    "blobid": "8c43a4fb023735dfd026014bae92ad91414b88bd"
   }
 ]
diff --git a/scm/driver/gitea/testdata/membership.json.golden b/scm/driver/gitea/testdata/membership.json.golden
new file mode 100644
index 000000000..96ec5685e
--- /dev/null
+++ b/scm/driver/gitea/testdata/membership.json.golden
@@ -0,0 +1,4 @@
+{
+  "Active": true,
+  "Role": 2
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/milestone.json b/scm/driver/gitea/testdata/milestone.json
new file mode 100644
index 000000000..9d69fb31c
--- /dev/null
+++ b/scm/driver/gitea/testdata/milestone.json
@@ -0,0 +1,12 @@
+{
+  "closed_at": "2020-09-11T19:32:38.046Z",
+  "closed_issues": 0,
+  "created_at": "2020-09-11T19:32:38.046Z",
+  "description": "string",
+  "due_on": "2020-09-11T19:32:38.046Z",
+  "id": 1,
+  "open_issues": 0,
+  "state": "open",
+  "title": "string",
+  "updated_at": "2020-09-11T19:32:38.046Z"
+}
diff --git a/scm/driver/gitea/testdata/milestone.json.golden b/scm/driver/gitea/testdata/milestone.json.golden
new file mode 100644
index 000000000..be885236a
--- /dev/null
+++ b/scm/driver/gitea/testdata/milestone.json.golden
@@ -0,0 +1,8 @@
+{
+  "Description": "string",
+  "DueDate": "2020-09-11T19:32:38.046Z",
+  "ID": 1,
+  "Number": 1,
+  "State": "open",
+  "Title": "string"
+}
diff --git a/scm/driver/gitea/testdata/milestone_create.json b/scm/driver/gitea/testdata/milestone_create.json
new file mode 100644
index 000000000..ca4130241
--- /dev/null
+++ b/scm/driver/gitea/testdata/milestone_create.json
@@ -0,0 +1,6 @@
+{
+  "title": "v1.0",
+  "description": "Tracking milestone for version 1.0",
+  "state": "open",
+  "due_on": "2012-10-09T23:39:01Z"
+}
diff --git a/scm/driver/gitea/testdata/milestones.json b/scm/driver/gitea/testdata/milestones.json
new file mode 100644
index 000000000..90cb3ed92
--- /dev/null
+++ b/scm/driver/gitea/testdata/milestones.json
@@ -0,0 +1,14 @@
+[
+  {
+    "closed_at": "2020-09-11T19:32:38.046Z",
+    "closed_issues": 0,
+    "created_at": "2020-09-11T19:32:38.046Z",
+    "description": "string",
+    "due_on": "2020-09-11T19:32:38.046Z",
+    "id": 1,
+    "open_issues": 0,
+    "state": "open",
+    "title": "string",
+    "updated_at": "2020-09-11T19:32:38.046Z"
+  }
+]
diff --git a/scm/driver/gitea/testdata/milestones.json.golden b/scm/driver/gitea/testdata/milestones.json.golden
new file mode 100644
index 000000000..23ce5fd75
--- /dev/null
+++ b/scm/driver/gitea/testdata/milestones.json.golden
@@ -0,0 +1,10 @@
+[
+  {
+    "Description": "string",
+    "DueDate": "2020-09-11T19:32:38.046Z",
+    "ID": 1,
+    "Number": 1,
+    "State": "open",
+    "Title": "string"
+  }
+]
diff --git a/scm/driver/gitea/testdata/permissions.json b/scm/driver/gitea/testdata/permissions.json
new file mode 100644
index 000000000..77e2cff85
--- /dev/null
+++ b/scm/driver/gitea/testdata/permissions.json
@@ -0,0 +1,7 @@
+{
+  "is_owner": true,
+  "is_admin": true,
+  "can_write": true,
+  "can_read": true,
+  "can_create_repository": true
+}
diff --git a/scm/driver/gitea/testdata/release.json b/scm/driver/gitea/testdata/release.json
new file mode 100644
index 000000000..02eeed341
--- /dev/null
+++ b/scm/driver/gitea/testdata/release.json
@@ -0,0 +1,10 @@
+{
+  "id": 1,
+  "name": "v1.0.0",
+  "body": "Description of the release",
+  "url": "https://try.gitea.com/api/v1/repos/octocat/Hello-World/123",
+  "tag_name": "v1.0.0",
+  "target_commitish": "master",
+  "draft": false,
+  "prerelease": false
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/release.json.golden b/scm/driver/gitea/testdata/release.json.golden
new file mode 100644
index 000000000..5ebc0ddf8
--- /dev/null
+++ b/scm/driver/gitea/testdata/release.json.golden
@@ -0,0 +1,10 @@
+{
+  "ID": 1,
+  "Title": "v1.0.0",
+  "Description": "Description of the release",
+  "Link": "https://try.gitea.com/octocat/Hello-World/releases/tag/v1.0.0",
+  "Tag": "v1.0.0",
+  "Commitish": "master",
+  "Draft": false,
+  "Prerelease": false
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/release_create.json b/scm/driver/gitea/testdata/release_create.json
new file mode 100644
index 000000000..37e53d251
--- /dev/null
+++ b/scm/driver/gitea/testdata/release_create.json
@@ -0,0 +1,8 @@
+{
+  "tag_name": "v1.0.0",
+  "target_commitish": "master",
+  "name": "v1.0.0",
+  "body": "Description of the release",
+  "draft": false,
+  "prerelease": false
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/release_update.json b/scm/driver/gitea/testdata/release_update.json
new file mode 100644
index 000000000..37e53d251
--- /dev/null
+++ b/scm/driver/gitea/testdata/release_update.json
@@ -0,0 +1,8 @@
+{
+  "tag_name": "v1.0.0",
+  "target_commitish": "master",
+  "name": "v1.0.0",
+  "body": "Description of the release",
+  "draft": false,
+  "prerelease": false
+}
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/releases.json b/scm/driver/gitea/testdata/releases.json
new file mode 100644
index 000000000..252ace560
--- /dev/null
+++ b/scm/driver/gitea/testdata/releases.json
@@ -0,0 +1,12 @@
+[
+  {
+    "id": 1,
+    "name": "v1.0.0",
+    "body": "Description of the release",
+    "url": "https://try.gitea.com/api/v1/repos/octocat/Hello-World/123",
+    "tag_name": "v1.0.0",
+    "target_commitish": "master",
+    "draft": false,
+    "prerelease": false
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/releases.json.golden b/scm/driver/gitea/testdata/releases.json.golden
new file mode 100644
index 000000000..51a6b244a
--- /dev/null
+++ b/scm/driver/gitea/testdata/releases.json.golden
@@ -0,0 +1,12 @@
+[
+  {
+    "ID": 1,
+    "Title": "v1.0.0",
+    "Description": "Description of the release",
+    "Link": "https://try.gitea.com/octocat/Hello-World/releases/tag/v1.0.0",
+    "Tag": "v1.0.0",
+    "Commitish": "master",
+    "Draft": false,
+    "Prerelease": false
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/repo.json b/scm/driver/gitea/testdata/repo.json
index 536d5f238..2f1756fb5 100644
--- a/scm/driver/gitea/testdata/repo.json
+++ b/scm/driver/gitea/testdata/repo.json
@@ -32,5 +32,6 @@
     "admin": true,
     "push": true,
     "pull": true
-  }
+  },
+  "archived": false
 }
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/repo.json.golden b/scm/driver/gitea/testdata/repo.json.golden
index 15fc567c1..62aee5e3c 100644
--- a/scm/driver/gitea/testdata/repo.json.golden
+++ b/scm/driver/gitea/testdata/repo.json.golden
@@ -13,5 +13,6 @@
     "CloneSSH": "git@try.gitea.io:go-gitea/gitea.git",
     "Link": "https://try.gitea.io/go-gitea/gitea",
     "Created": "0001-01-01T00:00:00Z",
-    "Updated": "0001-01-01T00:00:00Z"
+    "Updated": "0001-01-01T00:00:00Z",
+    "Archived": false
 }
\ No newline at end of file
diff --git a/scm/driver/gitea/testdata/repos.json b/scm/driver/gitea/testdata/repos.json
index 89e446201..8576aaeab 100644
--- a/scm/driver/gitea/testdata/repos.json
+++ b/scm/driver/gitea/testdata/repos.json
@@ -33,6 +33,7 @@
       "admin": true,
       "push": true,
       "pull": true
-    }
+    },
+    "archived": false
   }
 ]
diff --git a/scm/driver/gitea/testdata/repos.json.golden b/scm/driver/gitea/testdata/repos.json.golden
index 620565c96..35314d2ac 100644
--- a/scm/driver/gitea/testdata/repos.json.golden
+++ b/scm/driver/gitea/testdata/repos.json.golden
@@ -14,6 +14,7 @@
         "CloneSSH": "git@try.gitea.io:go-gitea/gitea.git",
         "Link": "https://try.gitea.io/go-gitea/gitea",
         "Created": "0001-01-01T00:00:00Z",
-        "Updated": "0001-01-01T00:00:00Z"
+        "Updated": "0001-01-01T00:00:00Z",
+        "Archived": false
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/gitea/user.go b/scm/driver/gitea/user.go
index 09b9e9399..38ab64acc 100644
--- a/scm/driver/gitea/user.go
+++ b/scm/driver/gitea/user.go
@@ -33,6 +33,10 @@ func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, err
 	return user.Email, res, err
 }
 
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
 //
 // native data structures
 //
diff --git a/scm/driver/gitea/util.go b/scm/driver/gitea/util.go
index 1179ddaeb..29915e910 100644
--- a/scm/driver/gitea/util.go
+++ b/scm/driver/gitea/util.go
@@ -5,8 +5,10 @@
 package gitea
 
 import (
+	"fmt"
 	"net/url"
 	"strconv"
+	"strings"
 
 	"github.com/drone/go-scm/scm"
 )
@@ -53,3 +55,49 @@ func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
 	}
 	return params.Encode()
 }
+
+// convertAPIURLToHTMLURL converts an release API endpoint into a html endpoint
+func convertAPIURLToHTMLURL(apiURL string, tagName string) string {
+	// "url": "https://try.gitea.com/api/v1/repos/octocat/Hello-World/123",
+	// "html_url": "https://try.gitea.com/octocat/Hello-World/releases/tag/v1.0.0",
+	// the url field is the API url, not the html url, so until go-sdk v0.13.3, build it ourselves
+	link, err := url.Parse(apiURL)
+	if err != nil {
+		return ""
+	}
+
+	pathParts := strings.Split(link.Path, "/")
+	if len(pathParts) != 7 {
+		return ""
+	}
+	link.Path = fmt.Sprintf("/%s/%s/releases/tag/%s", pathParts[4], pathParts[5], tagName)
+	return link.String()
+}
+
+func encodeMilestoneListOptions(opts scm.MilestoneListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
+
+type ListOptions struct {
+	Page     int
+	PageSize int
+}
+
+func encodeReleaseListOptions(o ListOptions) string {
+	query := make(url.Values)
+	query.Add("page", fmt.Sprintf("%d", o.Page))
+	query.Add("limit", fmt.Sprintf("%d", o.PageSize))
+	return query.Encode()
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/content.go b/scm/driver/gitee/content.go
new file mode 100644
index 000000000..7ac8d6445
--- /dev/null
+++ b/scm/driver/gitee/content.go
@@ -0,0 +1,142 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/base64"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type contentService struct {
+	client *wrapper
+}
+
+func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
+	endpoint := fmt.Sprintf("repos/%s/contents/%s?ref=%s", repo, path, ref)
+	out := new(content)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
+	raw, _ := base64.StdEncoding.DecodeString(out.Content)
+	return &scm.Content{
+		Path: out.Path,
+		Data: raw,
+		Sha:  out.Sha,
+	}, res, err
+}
+
+func (s *contentService) Create(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	endpoint := fmt.Sprintf("repos/%s/contents/%s", repo, path)
+	in := &contentCreateUpdate{
+		Message: params.Message,
+		Branch:  params.Branch,
+		Content: params.Data,
+		Committer: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+		Author: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) Update(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	endpoint := fmt.Sprintf("repos/%s/contents/%s", repo, path)
+	in := &contentCreateUpdate{
+		Message: params.Message,
+		Branch:  params.Branch,
+		Content: params.Data,
+		Sha:     params.Sha,
+		Committer: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+		Author: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+	res, err := s.client.do(ctx, "PUT", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	endpoint := fmt.Sprintf("repos/%s/contents/%s", repo, path)
+	in := &contentCreateUpdate{
+		Message: params.Message,
+		Branch:  params.Branch,
+		Sha:     params.Sha,
+		Committer: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+		Author: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+	res, err := s.client.do(ctx, "DELETE", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) List(ctx context.Context, repo, path, ref string, _ scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
+	endpoint := fmt.Sprintf("repos/%s/contents/%s?ref=%s", repo, path, ref)
+	out := []*content{}
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertContentInfoList(out), res, err
+}
+
+type content struct {
+	Name    string `json:"name"`
+	Path    string `json:"path"`
+	Sha     string `json:"sha"`
+	Content string `json:"content"`
+	Type    string `json:"type"`
+}
+
+type contentCreateUpdate struct {
+	Branch    string       `json:"branch"`
+	Message   string       `json:"message"`
+	Content   []byte       `json:"content"`
+	Sha       string       `json:"sha"`
+	Author    commitAuthor `json:"author"`
+	Committer commitAuthor `json:"committer"`
+}
+
+type commitAuthor struct {
+	Name  string `json:"name"`
+	Date  string `json:"date"`
+	Email string `json:"email"`
+}
+
+func convertContentInfoList(from []*content) []*scm.ContentInfo {
+	to := []*scm.ContentInfo{}
+	for _, v := range from {
+		to = append(to, convertContentInfo(v))
+	}
+	return to
+}
+func convertContentInfo(from *content) *scm.ContentInfo {
+	to := &scm.ContentInfo{Path: from.Path}
+	switch from.Type {
+	case "file":
+		to.Kind = scm.ContentKindFile
+	case "dir":
+		to.Kind = scm.ContentKindDirectory
+	case "symlink":
+		to.Kind = scm.ContentKindSymlink
+	case "submodule":
+		to.Kind = scm.ContentKindGitlink
+	default:
+		to.Kind = scm.ContentKindUnsupported
+	}
+	return to
+}
diff --git a/scm/driver/gitee/content_test.go b/scm/driver/gitee/content_test.go
new file mode 100644
index 000000000..0ded74af6
--- /dev/null
+++ b/scm/driver/gitee/content_test.go
@@ -0,0 +1,200 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestContentFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/contents/README.md").
+		MatchParam("ref", "d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content.json")
+
+	client := NewDefault()
+	got, res, err := client.Contents.Find(
+		context.Background(),
+		"kit101/drone-yml-test",
+		"README.md",
+		"d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83",
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Content)
+	raw, _ := ioutil.ReadFile("testdata/content.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestContentCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Post("/repos/kit101/drone-yml-test/contents/apitest/CreateByDroneGiteeProvider.md").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_create.json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Data:    []byte("bY3JlYXRlIGJ5IGRyb25lLXNjbSBnaXRlZSBwcm92aWRlci4gMjAyMS0wOC0xNyAyMzowNToxNi4="),
+		Signature: scm.Signature{
+			Name:  "kit101",
+			Email: "kit101@gitee.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Create(
+		context.Background(),
+		"kit101/drone-yml-test",
+		"apitest/CreateByDroneGiteeProvider.md",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentUpdate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Put("/repos/kit101/drone-yml-test/contents/apitest/UpdateByDroneGiteeProvider.md").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_update.json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message by update",
+		Data:    []byte("bdXBkYXRlIGJ5IGRyb25lLXNjbSBnaXRlZSBwcm92aWRlci4gMjAyMS0wOC0xNyAyMzozMDozNy4="),
+		Sha:     "9de0cf94e1e3c1cbe0a25c3865de4cc9ede7ad3e",
+		Signature: scm.Signature{
+			Name:  "kit101",
+			Email: "kit101@gitee.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Update(
+		context.Background(),
+		"kit101/drone-yml-test",
+		"apitest/UpdateByDroneGiteeProvider.md",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Delete("/repos/kit101/drone-yml-test/contents/apitest/DeleteByDroneGiteeProvider.md").
+		Reply(204).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_delete.json")
+
+	contentParams := scm.ContentParams{
+		Branch:  "master",
+		Sha:     "ae0653e4ab697cd77fc559cd798bdb30e8bb4d7e",
+		Message: "delete commit message",
+		Signature: scm.Signature{
+			Name:  "kit101",
+			Email: "kit101@gitee.com",
+		},
+	}
+	client := NewDefault()
+	res, err := client.Contents.Delete(
+		context.Background(),
+		"kit101/drone-yml-test",
+		"apitest/DeleteByDroneGiteeProvider.md",
+		&contentParams,
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if got, want := res.Status, 204; got != want {
+		t.Errorf("Want response status %d, got %d", want, got)
+	}
+	t.Run("Request", testRequest(res))
+}
+
+func TestContentList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/contents/apitest").
+		MatchParam("ref", "master").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_list.json")
+
+	client := NewDefault()
+	got, res, err := client.Contents.List(
+		context.Background(),
+		"kit101/drone-yml-test",
+		"apitest",
+		"master",
+		scm.ListOptions{},
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.ContentInfo{}
+	raw, _ := ioutil.ReadFile("testdata/content_list.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
diff --git a/scm/driver/gitee/git.go b/scm/driver/gitee/git.go
new file mode 100644
index 000000000..b3377a43c
--- /dev/null
+++ b/scm/driver/gitee/git.go
@@ -0,0 +1,308 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type gitService struct {
+	client *wrapper
+}
+
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/branches", repo)
+	in := &branchCreate{
+		Refs:       params.Sha,
+		BranchName: params.Name,
+	}
+	res, err := s.client.do(ctx, "POST", path, in, nil)
+	return res, err
+}
+
+func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/branches/%s", repo, name)
+	out := new(branch)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertBranch(out), res, err
+}
+
+func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/commits/%s", repo, ref)
+	out := new(commit)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertCommit(out), res, err
+}
+
+func (s *gitService) FindTag(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
+	tags, res, err := s.ListTags(ctx, repo, scm.ListOptions{})
+	if err != nil {
+		return nil, nil, err
+	}
+	for _, tag := range tags {
+		if tag.Name == name {
+			return tag, res, err
+		}
+	}
+	return nil, res, err
+}
+
+func (s *gitService) ListBranches(ctx context.Context, repo string, _ scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/branches", repo)
+	out := []*branch{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertBranchList(out), res, err
+}
+
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// Gitee doesnt provide support listing based on searchTerm
+	// Hence calling the ListBranches
+	return s.ListBranches(ctx, repo, opts.PageListOptions)
+}
+
+func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/commits?%s", repo, encodeCommitListOptions(opts))
+	out := []*commit{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertCommitList(out), res, err
+}
+
+func (s *gitService) ListTags(ctx context.Context, repo string, _ scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/tags", repo)
+	out := []*releasesTags{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertTagList(out), res, err
+}
+
+func (s *gitService) ListChanges(ctx context.Context, repo, ref string, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/commits/%s", repo, ref)
+	out := new(commit)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertChangeList(out.Files), res, err
+}
+
+func (s *gitService) CompareChanges(ctx context.Context, repo, source, target string, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/compare/%s...%s", repo, source, target)
+	out := new(compare)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertChangeList(out.Files), res, err
+}
+
+type branchCreate struct {
+	Refs       string `json:"refs"`
+	BranchName string `json:"branch_name"`
+}
+
+type branch struct {
+	//Links         string `json:"_links"`
+	Name          string `json:"name"`
+	Commit        tree   `json:"commit"`
+	Protected     bool   `json:"protected"`
+	ProtectionURL string `json:"protection_url"`
+}
+
+type commit struct {
+	URL         string `json:"url"`
+	Sha         string `json:"sha"`
+	HtmlURL     string `json:"html_url"`
+	CommentsURL string `json:"comments_url"`
+	Commit      struct {
+		Author    committer `json:"author"`
+		Committer committer `json:"committer"`
+		Message   string    `json:"message"`
+		Tree      tree      `json:"tree"`
+	} `json:"commit"`
+	Author    author `json:"author"`
+	Committer author `json:"committer"`
+	Parents   []tree `json:"parents"`
+	Stats     struct {
+		ID        string `json:"id"`
+		Additions int64  `json:"additions"`
+		Deletions int64  `json:"deletions"`
+		Total     int64  `json:"total"`
+	} `json:"stats"`
+	Files []*file `json:"files"`
+}
+
+type committer struct {
+	Name  string    `json:"name"`
+	Date  time.Time `json:"date"`
+	Email string    `json:"email"`
+}
+
+type tree struct {
+	Sha string `json:"sha"`
+	URL string `json:"url"`
+}
+
+type file struct {
+	SHA        string `json:"sha"`
+	Filename   string `json:"filename"`
+	Status     string `json:"status"`
+	Additions  int64  `json:"additions"`
+	Deletions  int64  `json:"deletions"`
+	Changes    int64  `json:"changes"`
+	BlobURL    string `json:"blob_url"`
+	RawURL     string `json:"raw_url"`
+	ContentURL string `json:"content_url"`
+	Patch      string `json:"patch"`
+}
+
+type compare struct {
+	BaseCommit struct {
+		URL         string        `json:"url"`
+		Sha         string        `json:"sha"`
+		HTMLURL     string        `json:"html_url"`
+		CommentsURL string        `json:"comments_url"`
+		Commit      compareCommit `json:"commit"`
+		Author      compareAuthor `json:"author"`
+		Committer   committer     `json:"committer"`
+		Parents     []tree        `json:"parents"`
+	} `json:"base_commit"`
+	MergeBaseCommit struct {
+		URL         string        `json:"url"`
+		Sha         string        `json:"sha"`
+		HTMLURL     string        `json:"html_url"`
+		CommentsURL string        `json:"comments_url"`
+		Commit      compareCommit `json:"commit"`
+		Author      compareAuthor `json:"author"`
+		Committer   committer     `json:"committer"`
+		Parents     []tree        `json:"parents"`
+	} `json:"merge_base_commit"`
+	Commits []struct {
+		URL         string        `json:"url"`
+		Sha         string        `json:"sha"`
+		HTMLURL     string        `json:"html_url"`
+		CommentsURL string        `json:"comments_url"`
+		Commit      compareCommit `json:"commit"`
+		Author      compareAuthor `json:"author"`
+		Committer   committer     `json:"committer"`
+		Parents     []tree        `json:"parents"`
+	} `json:"commits"`
+	Files []*file `json:"files"`
+}
+type compareAuthor struct {
+	Name  string    `json:"name"`
+	Date  time.Time `json:"date"`
+	Email string    `json:"email"`
+}
+type compareCommit struct {
+	Author    compareAuthor `json:"author"`
+	Committer committer     `json:"committer"`
+	Message   string        `json:"message"`
+	Tree      tree          `json:"tree"`
+}
+
+type releasesTags struct {
+	Name    string `json:"name"`
+	Message string `json:"message"`
+	Commit  struct {
+		Sha  string    `json:"sha"`
+		Date time.Time `json:"date"`
+	} `json:"commit"`
+}
+
+type author struct {
+	ID                int    `json:"id"`
+	Login             string `json:"login"`
+	Name              string `json:"name"`
+	AvatarURL         string `json:"avatar_url"`
+	URL               string `json:"url"`
+	HTMLURL           string `json:"html_url"`
+	FollowersURL      string `json:"followers_url"`
+	FollowingURL      string `json:"following_url"`
+	GistsURL          string `json:"gists_url"`
+	StarredURL        string `json:"starred_url"`
+	SubscriptionsURL  string `json:"subscriptions_url"`
+	OrganizationsURL  string `json:"organizations_url"`
+	ReposURL          string `json:"repos_url"`
+	EventsURL         string `json:"events_url"`
+	ReceivedEventsURL string `json:"received_events_url"`
+	Type              string `json:"type"`
+}
+
+func convertCommitList(from []*commit) []*scm.Commit {
+	to := []*scm.Commit{}
+	for _, v := range from {
+		to = append(to, convertCommit(v))
+	}
+	return to
+}
+
+func convertCommit(from *commit) *scm.Commit {
+	return &scm.Commit{
+		Message: from.Commit.Message,
+		Sha:     from.Sha,
+		Link:    from.HtmlURL,
+		Author: scm.Signature{
+			Name:   from.Commit.Author.Name,
+			Email:  from.Commit.Author.Email,
+			Date:   from.Commit.Author.Date,
+			Login:  from.Author.Login,
+			Avatar: from.Author.AvatarURL,
+		},
+		Committer: scm.Signature{
+			Name:   from.Commit.Committer.Name,
+			Email:  from.Commit.Committer.Email,
+			Date:   from.Commit.Committer.Date,
+			Login:  from.Committer.Login,
+			Avatar: from.Committer.AvatarURL,
+		},
+	}
+}
+
+func convertBranchList(from []*branch) []*scm.Reference {
+	to := []*scm.Reference{}
+	for _, v := range from {
+		to = append(to, convertBranch(v))
+	}
+	return to
+}
+
+func convertBranch(from *branch) *scm.Reference {
+	return &scm.Reference{
+		Name: scm.TrimRef(from.Name),
+		Path: scm.ExpandRef(from.Name, "refs/heads/"),
+		Sha:  from.Commit.Sha,
+	}
+}
+
+func convertTagList(from []*releasesTags) []*scm.Reference {
+	to := []*scm.Reference{}
+	for _, v := range from {
+		to = append(to, convertTag(v))
+	}
+	return to
+}
+
+func convertTag(from *releasesTags) *scm.Reference {
+	return &scm.Reference{
+		Name: scm.TrimRef(from.Name),
+		Path: scm.ExpandRef(from.Name, "refs/tags/"),
+		Sha:  from.Commit.Sha,
+	}
+}
+
+func convertChangeList(from []*file) []*scm.Change {
+	to := []*scm.Change{}
+	for _, v := range from {
+		to = append(to, convertChange(v))
+	}
+	return to
+}
+
+func convertChange(from *file) *scm.Change {
+	return &scm.Change{
+		Path:    from.Filename,
+		Added:   from.Status == "added",
+		Deleted: from.Status == "removed",
+		Renamed: from.Status == "modified" && from.Additions == 0 && from.Deletions == 0 && from.Changes == 0,
+		BlobID:  from.SHA,
+	}
+}
diff --git a/scm/driver/gitee/git_test.go b/scm/driver/gitee/git_test.go
new file mode 100644
index 000000000..bdceb7634
--- /dev/null
+++ b/scm/driver/gitee/git_test.go
@@ -0,0 +1,279 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestGitCreateBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Post("/repos/kit101/drone-yml-test/branches").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	input := scm.ReferenceInput{
+		Name: "create-by-api",
+		Sha:  "b72a4c4a2d838d96a545a42d41d7776ae5566f4a",
+	}
+	res, err := client.Git.CreateBranch(context.Background(), "kit101/drone-yml-test", &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if got, want := res.Status, 201; got != want {
+		t.Errorf("Want response status %d, got %d", want, got)
+	}
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitFindBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/branches/master").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/branch.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.FindBranch(context.Background(), "kit101/drone-yml-test", "master")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Reference)
+	raw, _ := ioutil.ReadFile("testdata/branch.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitFindCommit(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/commit.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.FindCommit(context.Background(), "kit101/drone-yml-test", "e3c0ff4d5cef439ea11b30866fb1ed79b420801d")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Commit)
+	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitFindTag(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/tags").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/tags.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.FindTag(context.Background(), "kit101/drone-yml-test", "1.0")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Reference)
+	raw, _ := ioutil.ReadFile("testdata/tag.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitListBranches(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/branches").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/branches.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.ListBranches(context.Background(), "kit101/drone-yml-test", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/commits").
+		MatchParam("sha", "master").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/commits.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.ListCommits(context.Background(), "kit101/drone-yml-test",
+		scm.CommitListOptions{Ref: "master", Page: 1, Size: 3})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestGitListTags(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/tags").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/tags.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.ListTags(context.Background(), "kit101/drone-yml-test", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/tags.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitListChanges(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com").
+		Get("/repos/kit101/drone-yml-test/commits/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/changes.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.ListChanges(context.Background(), "kit101/drone-yml-test", "7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Change{}
+	raw, _ := ioutil.ReadFile("testdata/changes.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestGitCompareChanges(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/compare/e3c0ff4d5cef439ea11b30866fb1ed79b420801d...2700445cd84c08546f4d003f8aa54d2099a006b7").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/compare.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.CompareChanges(context.Background(), "kit101/drone-yml-test", "e3c0ff4d5cef439ea11b30866fb1ed79b420801d", "2700445cd84c08546f4d003f8aa54d2099a006b7", scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Change{}
+	raw, _ := ioutil.ReadFile("testdata/compare.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
diff --git a/scm/driver/gitee/gitee.go b/scm/driver/gitee/gitee.go
new file mode 100644
index 000000000..9bf40fd29
--- /dev/null
+++ b/scm/driver/gitee/gitee.go
@@ -0,0 +1,167 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gitee implements a Gitee client.
+package gitee
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"net/url"
+	"strconv"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+// New returns a new Gitee API client.
+func New(uri string) (*scm.Client, error) {
+	base, err := url.Parse(uri)
+	if err != nil {
+		return nil, err
+	}
+	if !strings.HasSuffix(base.Path, "/") {
+		base.Path = base.Path + "/"
+	}
+	client := &wrapper{new(scm.Client)}
+	client.BaseURL = base
+	// initialize services
+	client.Driver = scm.DriverGitee
+	client.Linker = &linker{websiteAddress(base)}
+	client.Contents = &contentService{client}
+	client.Git = &gitService{client}
+	client.Issues = &issueService{client}
+	client.Organizations = &organizationService{client}
+	client.PullRequests = &pullService{client}
+	client.Repositories = &RepositoryService{client}
+	client.Reviews = &reviewService{client}
+	client.Users = &userService{client}
+	client.Webhooks = &webhookService{client}
+	return client.Client, nil
+}
+
+// NewDefault returns a new Gitee API client using the
+// default gitee.com/api/v5 address.
+func NewDefault() *scm.Client {
+	client, _ := New("https://gitee.com/api/v5")
+	return client
+}
+
+// wrapper wraps the Client to provide high level helper functions
+// for making http requests and unmarshaling the response.
+type wrapper struct {
+	*scm.Client
+}
+
+// do wraps the Client.Do function by creating the Request and
+// unmarshalling the response.
+func (c *wrapper) do(ctx context.Context, method, path string, in, out interface{}) (*scm.Response, error) {
+	req := &scm.Request{
+		Method: method,
+		Path:   path,
+	}
+	// if we are posting or putting data, we need to
+	// write it to the body of the request.
+	if in != nil {
+		buf := new(bytes.Buffer)
+		json.NewEncoder(buf).Encode(in)
+		req.Header = map[string][]string{
+			"Content-Type": {"application/json"},
+		}
+		req.Body = buf
+	}
+	// execute the http request
+	res, err := c.Client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	defer res.Body.Close()
+	// parse the gitee request id.
+	res.ID = res.Header.Get("X-Request-Id")
+
+	// gitee pageValues
+	populatePageValues(req, res)
+
+	// if an error is encountered, unmarshal and return the
+	// error response.
+	if res.Status > 300 {
+		err := new(Error)
+		json.NewDecoder(res.Body).Decode(err)
+		return res, err
+	}
+
+	if out == nil {
+		return res, nil
+	}
+
+	// if a json response is expected, parse and return
+	// the json response.
+	return res, json.NewDecoder(res.Body).Decode(out)
+}
+
+// Error represents a Gitee error.
+type Error struct {
+	Message string `json:"message"`
+}
+
+func (e *Error) Error() string {
+	return e.Message
+}
+
+// helper function converts the Gitee API url to
+// the website url.
+func websiteAddress(u *url.URL) string {
+	host, proto := u.Host, u.Scheme
+	switch host {
+	case "gitee.com/api/v5":
+		return "https://gitee.com/"
+	}
+	return proto + "://" + host + "/"
+}
+
+// populatePageValues parses the HTTP Link response headers
+// and populates the various pagination link values in the
+// Response.
+// response header: total_page, total_count
+func populatePageValues(req *scm.Request, resp *scm.Response) {
+	// get last
+	last, totalError := strconv.Atoi(resp.Header.Get("total_page"))
+	if totalError != nil {
+		return
+	}
+	// get curren page
+	reqURL, err := url.Parse(req.Path)
+	if err != nil {
+		return
+	}
+	currentPageStr := reqURL.Query().Get("page")
+	var current int
+	if currentPageStr == "" {
+		current = 1
+	} else {
+		currentPage, currentError := strconv.Atoi(currentPageStr)
+		if currentError != nil {
+			return
+		}
+		current = currentPage
+	}
+
+	// first, prev
+	if current <= 1 {
+		resp.Page.First = 0
+		resp.Page.Prev = 0
+	} else {
+		resp.Page.First = 1
+		resp.Page.Prev = current - 1
+	}
+	// last, next
+	if current >= last {
+		resp.Page.Last = 0
+		resp.Page.Next = 0
+	} else {
+		resp.Page.Last = last
+		resp.Page.Next = current + 1
+	}
+}
diff --git a/scm/driver/gitee/gitee_test.go b/scm/driver/gitee/gitee_test.go
new file mode 100644
index 000000000..7547de351
--- /dev/null
+++ b/scm/driver/gitee/gitee_test.go
@@ -0,0 +1,97 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"net/url"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+var mockHeaders = map[string]string{
+	"X-Request-Id": "7cb049dbf1fafae67b4f0aa81ca7e870",
+}
+
+var mockPageHeaders = map[string]string{
+	"total_page": `3`,
+}
+
+func TestClient(t *testing.T) {
+	client, err := New("https://gitee.com/api/v5")
+	if err != nil {
+		t.Error(err)
+	}
+	if got, want := client.BaseURL.String(), "https://gitee.com/api/v5/"; got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+}
+
+func TestClient_Base(t *testing.T) {
+	client, err := New("https://gitee.com/api/v5")
+	if err != nil {
+		t.Error(err)
+	}
+	got, want := client.BaseURL.String(), "https://gitee.com/api/v5/"
+	if got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+}
+
+func TestClient_Default(t *testing.T) {
+	client := NewDefault()
+	if got, want := client.BaseURL.String(), "https://gitee.com/api/v5/"; got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+}
+
+func TestClient_Error(t *testing.T) {
+	_, err := New("http://a b.com/")
+	if err == nil {
+		t.Errorf("Expect error when invalid URL")
+	}
+}
+
+func testPage(res *scm.Response) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := res.Page.Prev, 0; got != want {
+			t.Errorf("Want prev page %d, got %d", want, got)
+		}
+		if got, want := res.Page.Next, 2; got != want {
+			t.Errorf("Want next page %d, got %d", want, got)
+		}
+		if got, want := res.Page.Last, 3; got != want {
+			t.Errorf("Want last page %d, got %d", want, got)
+		}
+		if got, want := res.Page.First, 0; got != want {
+			t.Errorf("Want first page %d, got %d", want, got)
+		}
+	}
+}
+
+func testRequest(res *scm.Response) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := res.ID, "7cb049dbf1fafae67b4f0aa81ca7e870"; got != want {
+			t.Errorf("Want X-Request-Id %q, got %q", want, got)
+		}
+	}
+}
+
+func TestWebsiteAddress(t *testing.T) {
+	tests := []struct {
+		api string
+		web string
+	}{
+		{"https://gitee.com/api/v5/", "https://gitee.com/"},
+	}
+
+	for _, test := range tests {
+		parsed, _ := url.Parse(test.api)
+		got, want := websiteAddress(parsed), test.web
+		if got != want {
+			t.Errorf("Want website address %q, got %q", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/content_test.go b/scm/driver/gitee/integration/content_test.go
new file mode 100644
index 000000000..8e0404906
--- /dev/null
+++ b/scm/driver/gitee/integration/content_test.go
@@ -0,0 +1,54 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func testContents(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testContentFind(client))
+		t.Run("Find/Branch", testContentFindBranch(client))
+	}
+}
+
+func testContentFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Contents.Find(context.Background(), "kit101/drone-yml-test", "main.py", "5e7876efb3468ff679410b82a72f7c002382d41e")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if got, want := result.Path, "main.py"; got != want {
+			t.Errorf("Got file path %q, want %q", got, want)
+		}
+		if got, want := string(result.Data), "if __name__ == '__main__':\r\n    print('Hello world.')"; got != want {
+			t.Errorf("Got file data %q, want %q", got, want)
+		}
+	}
+}
+
+func testContentFindBranch(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Contents.Find(context.Background(), "kit101/drone-yml-test", "main.py", "feat-4")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if got, want := result.Path, "main.py"; got != want {
+			t.Errorf("Got file path %q, want %q", got, want)
+		}
+		if got, want := string(result.Data), "if __name__ == '__main__':\r\n    print('Hello world.')"; got != want {
+			t.Errorf("Got file data %q, want %q", got, want)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/git_test.go b/scm/driver/gitee/integration/git_test.go
new file mode 100644
index 000000000..d7267fc60
--- /dev/null
+++ b/scm/driver/gitee/integration/git_test.go
@@ -0,0 +1,214 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+//
+// git sub-tests
+//
+
+func testGit(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Branches", testBranches(client))
+		t.Run("Commits", testCommits(client))
+		t.Run("Tags", testTags(client))
+	}
+}
+
+//
+// branch sub-tests
+//
+
+func testBranches(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testBranchFind(client))
+		t.Run("List", testBranchList(client))
+	}
+}
+
+func testBranchFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Git.FindBranch(context.Background(), "kit101/drone-yml-test", "feat-4")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		t.Run("Branch", testBranch(result))
+	}
+}
+
+func testBranchList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.ListOptions{}
+		result, _, err := client.Git.ListBranches(context.Background(), "kit101/drone-yml-test", opts)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if len(result) == 0 {
+			t.Errorf("Want a non-empty branch list")
+		}
+		for _, branch := range result {
+			if branch.Name == "feat-4" {
+				t.Run("Branch", testBranch(branch))
+			}
+		}
+	}
+}
+
+//
+// branch sub-tests
+//
+
+func testTags(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testTagFind(client))
+		t.Run("List", testTagList(client))
+	}
+}
+
+func testTagFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		tag, _, err := client.Git.FindTag(context.Background(), "kit101/drone-yml-test", "1.1")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		t.Run("Tag", testTag(tag))
+	}
+}
+
+func testTagList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.ListOptions{}
+		result, _, err := client.Git.ListTags(context.Background(), "kit101/drone-yml-test", opts)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if len(result) == 0 {
+			t.Errorf("Want a non-empty tag list")
+		}
+		for _, tag := range result {
+			if tag.Name == "1.1" {
+				t.Run("Tag", testTag(tag))
+			}
+		}
+	}
+}
+
+//
+// commit sub-tests
+//
+
+func testCommits(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testCommitFind(client))
+		t.Run("List", testCommitList(client))
+	}
+}
+
+func testCommitFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Git.FindCommit(context.Background(), "kit101/drone-yml-test", "e3c0ff4d5cef439ea11b30866fb1ed79b420801d")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		t.Run("Commit", testCommit(result))
+	}
+}
+
+func testCommitList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.CommitListOptions{
+			Ref: "feat-4",
+		}
+		result, _, err := client.Git.ListCommits(context.Background(), "kit101/drone-yml-test", opts)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if len(result) == 0 {
+			t.Errorf("Want a non-empty commit list")
+		}
+		for _, commit := range result {
+			if commit.Sha == "e3c0ff4d5cef439ea11b30866fb1ed79b420801d" {
+				t.Run("Commit", testCommit(commit))
+			}
+		}
+	}
+}
+
+//
+// struct sub-tests
+//
+
+func testBranch(branch *scm.Reference) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := branch.Name, "feat-4"; got != want {
+			t.Errorf("Want branch Name %q, got %q", want, got)
+		}
+		if got, want := branch.Sha, "2eac1cac02c325058cf959725c45b0612d3e8177"; got != want {
+			t.Errorf("Want branch Avatar %q, got %q", want, got)
+		}
+	}
+}
+
+func testTag(tag *scm.Reference) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := tag.Name, "1.1"; got != want {
+			t.Errorf("Want tag Name %q, got %q", want, got)
+		}
+		if got, want := tag.Sha, "5e7876efb3468ff679410b82a72f7c002382d41e"; got != want {
+			t.Errorf("Want tag Avatar %q, got %q", want, got)
+		}
+	}
+}
+
+func testCommit(commit *scm.Commit) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := commit.Message, "add/update/delete/rename\n"; got != want {
+			t.Errorf("Want commit Message %q, got %q", want, got)
+		}
+		if got, want := commit.Sha, "e3c0ff4d5cef439ea11b30866fb1ed79b420801d"; got != want {
+			t.Errorf("Want commit Sha %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Name, "kit101"; got != want {
+			t.Errorf("Want commit author Name %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Email, "qkssk1711@163.com"; got != want {
+			t.Errorf("Want commit author Email %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Date.Unix(), int64(1629733553); got != want {
+			t.Errorf("Want commit author Date %d, got %d", want, got)
+		}
+		if got, want := commit.Committer.Name, "kit101"; got != want {
+			t.Errorf("Want commit author Name %q, got %q", want, got)
+		}
+		if got, want := commit.Committer.Email, "qkssk1711@163.com"; got != want {
+			t.Errorf("Want commit author Email %q, got %q", want, got)
+		}
+		if got, want := commit.Committer.Date.Unix(), int64(1629733553); got != want {
+			t.Errorf("Want commit author Date %d, got %d", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/gitee_test.go b/scm/driver/gitee/integration/gitee_test.go
new file mode 100644
index 000000000..cb9918130
--- /dev/null
+++ b/scm/driver/gitee/integration/gitee_test.go
@@ -0,0 +1,37 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"net/http"
+	"os"
+	"testing"
+
+	"github.com/drone/go-scm/scm/driver/gitee"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestGitee(t *testing.T) {
+	accessToken := os.Getenv("GITEE_TOKEN")
+	if accessToken == "" {
+		t.Skipf("missing GITEE_TOKEN environment variable")
+		return
+	}
+
+	client := gitee.NewDefault()
+	client.Client = &http.Client{
+		Transport: &transport.BearerToken{
+			Token: accessToken,
+		},
+	}
+
+	t.Run("Contents", testContents(client))
+	t.Run("Git", testGit(client))
+	t.Run("Issues", testIssues(client))
+	t.Run("Organizations", testOrgs(client))
+	t.Run("PullRequests", testPullRequests(client))
+	t.Run("Repositories", testRepos(client))
+	t.Run("Users", testUsers(client))
+}
diff --git a/scm/driver/gitee/integration/integration.go b/scm/driver/gitee/integration/integration.go
new file mode 100644
index 000000000..acc0ad401
--- /dev/null
+++ b/scm/driver/gitee/integration/integration.go
@@ -0,0 +1,5 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
diff --git a/scm/driver/gitee/integration/issue_test.go b/scm/driver/gitee/integration/issue_test.go
new file mode 100644
index 000000000..f2d6ca846
--- /dev/null
+++ b/scm/driver/gitee/integration/issue_test.go
@@ -0,0 +1,145 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+//
+// issue sub-tests
+//
+
+func testIssues(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("List", testIssueList(client))
+		t.Run("Find", testIssueFind(client))
+		t.Run("Comments", testIssueComments(client))
+	}
+}
+
+func testIssueList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.IssueListOptions{
+			Open:   true,
+			Closed: true,
+		}
+		result, _, err := client.Issues.List(context.Background(), "kit101/drone-yml-test", opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Got empty issue list")
+		}
+	}
+}
+
+func testIssueFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Issues.Find(context.Background(), "kit101/drone-yml-test", 735267685380)
+		if err != nil {
+			t.Error(err)
+		}
+		t.Run("Issue", testIssue(result))
+	}
+}
+
+//
+// issue comment sub-tests
+//
+
+func testIssueComments(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Run("List", testIssueCommentList(client))
+		t.Run("Find", testIssueCommentFind(client))
+	}
+}
+
+func testIssueCommentList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		opts := scm.ListOptions{}
+		result, _, err := client.Issues.ListComments(context.Background(), "kit101/drone-yml-test", 735267685380, opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Want a non-empty issue comment list")
+		}
+		for _, comment := range result {
+			if comment.ID == 6877445 {
+				t.Run("Comment", testIssueComment(comment))
+			}
+		}
+	}
+}
+
+func testIssueCommentFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		result, _, err := client.Issues.FindComment(context.Background(), "kit101/drone-yml-test", 735267685380, 6877445)
+		if err != nil {
+			t.Error(err)
+		}
+		t.Run("Comment", testIssueComment(result))
+	}
+}
+
+//
+// struct sub-tests
+//
+
+func testIssue(issue *scm.Issue) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := issue.Number, 735267685380; got != want {
+			t.Errorf("Want issue Number %d, got %d", want, got)
+		}
+		if got, want := issue.Title, "test issue 1"; got != want {
+			t.Errorf("Want issue Title %q, got %q", want, got)
+		}
+		if got, want := issue.Body, "test issue 1"; got != want {
+			t.Errorf("Want issue Body %q, got %q", want, got)
+		}
+		if got, want := issue.Link, "https://gitee.com/kit101/drone-yml-test/issues/I4CD5P"; got != want {
+			t.Errorf("Want issue Link %q, got %q", want, got)
+		}
+		if got, want := issue.Author.Login, "kit101"; got != want {
+			t.Errorf("Want issue Author Login %q, got %q", want, got)
+		}
+		if got, want := issue.Author.Avatar, "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"; got != want {
+			t.Errorf("Want issue Author Avatar %q, got %q", want, got)
+		}
+		if got, want := issue.Closed, true; got != want {
+			t.Errorf("Want issue Closed %v, got %v", want, got)
+		}
+		if got, want := issue.Created.Unix(), int64(1632878488); got != want {
+			t.Errorf("Want issue Created %d, got %d", want, got)
+		}
+	}
+}
+
+func testIssueComment(comment *scm.Comment) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := comment.ID, 6877445; got != want {
+			t.Errorf("Want issue comment ID %d, got %d", want, got)
+		}
+		if got, want := comment.Body, "it's ok."; got != want {
+			t.Errorf("Want issue comment Body %q, got %q", want, got)
+		}
+		if got, want := comment.Author.Login, "kit101"; got != want {
+			t.Errorf("Want issue comment Author Login %q, got %q", want, got)
+		}
+		if got, want := comment.Created.Unix(), int64(1632884450); got != want {
+			t.Errorf("Want issue comment Created %d, got %d", want, got)
+		}
+		if got, want := comment.Updated.Unix(), int64(1632884450); got != want {
+			t.Errorf("Want issue comment Updated %d, got %d", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/org_test.go b/scm/driver/gitee/integration/org_test.go
new file mode 100644
index 000000000..e25590a19
--- /dev/null
+++ b/scm/driver/gitee/integration/org_test.go
@@ -0,0 +1,51 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+//
+// organization sub-tests
+//
+
+func testOrgs(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testOrgFind(client))
+	}
+}
+
+func testOrgFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Organizations.Find(context.Background(), "gitee-community")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		t.Run("Organization", testOrg(result))
+	}
+}
+
+//
+// struct sub-tests
+//
+
+func testOrg(organization *scm.Organization) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		if got, want := organization.Name, "gitee-community"; got != want {
+			t.Errorf("Want organization Name %q, got %q", want, got)
+		}
+		if got, want := organization.Avatar, "https://portrait.gitee.com/uploads/avatars/namespace/1715/5146011_gitee-community_1607593452.png?is_link=true"; got != want {
+			t.Errorf("Want organization Avatar %q, got %q", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/pr_test.go b/scm/driver/gitee/integration/pr_test.go
new file mode 100644
index 000000000..99caee142
--- /dev/null
+++ b/scm/driver/gitee/integration/pr_test.go
@@ -0,0 +1,212 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+//
+// pull request sub-tests
+//
+
+func testPullRequests(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("List", testPullRequestList(client))
+		t.Run("Find", testPullRequestFind(client))
+		t.Run("Changes", testPullRequestChanges(client))
+		t.Run("Comments", testPullRequestComments(client))
+	}
+}
+
+func testPullRequestList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.PullRequestListOptions{
+			Open:   true,
+			Closed: true,
+		}
+		result, _, err := client.PullRequests.List(context.Background(), "kit101/drone-yml-test", opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Got empty pull request list")
+		}
+		for _, pr := range result {
+			if pr.Number == 7 {
+				t.Run("PullRequest", testPullRequest(pr))
+			}
+		}
+	}
+}
+
+func testPullRequestFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.PullRequests.Find(context.Background(), "kit101/drone-yml-test", 7)
+		if err != nil {
+			t.Error(err)
+		}
+		t.Run("PullRequest", testPullRequest(result))
+	}
+}
+
+//
+// pull request comment sub-tests
+//
+
+func testPullRequestComments(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("List", testPullRequestCommentFind(client))
+		t.Run("Find", testPullRequestCommentList(client))
+	}
+}
+
+func testPullRequestCommentFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.PullRequests.FindComment(context.Background(), "kit101/drone-yml-test", 7, 6922557)
+		if err != nil {
+			t.Error(err)
+		}
+		t.Run("Comment", testPullRequestComment(result))
+	}
+}
+
+func testPullRequestCommentList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.ListOptions{}
+		result, _, err := client.PullRequests.ListComments(context.Background(), "kit101/drone-yml-test", 7, opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Got empty pull request comment list")
+		}
+		for _, comment := range result {
+			if comment.ID == 6922557 {
+				t.Run("Comment", testPullRequestComment(comment))
+			}
+		}
+	}
+}
+
+//
+// pull request changes sub-tests
+//
+
+func testPullRequestChanges(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.ListOptions{}
+		result, _, err := client.PullRequests.ListChanges(context.Background(), "kit101/drone-yml-test", 7, opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Got empty pull request change list")
+			return
+		}
+		t.Run("File", testChange(result[0]))
+	}
+}
+
+//
+// struct sub-tests
+//
+
+func testPullRequest(pr *scm.PullRequest) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := pr.Number, 7; got != want {
+			t.Errorf("Want pr Number %d, got %d", want, got)
+		}
+		if got, want := pr.Title, "feat add 3"; got != want {
+			t.Errorf("Want pr Title %q, got %q", want, got)
+		}
+		if got, want := pr.Body, ""; got != want {
+			t.Errorf("Want pr Body %q, got %q", want, got)
+		}
+		if got, want := pr.Source, "feat-2"; got != want {
+			t.Errorf("Want pr Source %q, got %q", want, got)
+		}
+		if got, want := pr.Target, "master"; got != want {
+			t.Errorf("Want pr Target %q, got %q", want, got)
+		}
+		if got, want := pr.Ref, "refs/pull/7/head"; got != want {
+			t.Errorf("Want pr Ref %q, got %q", want, got)
+		}
+		if got, want := pr.Sha, "6168d9dae737b47f00c59fafca10c913a6850c3a"; got != want {
+			t.Errorf("Want pr Sha %q, got %q", want, got)
+		}
+		if got, want := pr.Link, "https://gitee.com/kit101/drone-yml-test/pulls/7"; got != want {
+			t.Errorf("Want pr Link %q, got %q", want, got)
+		}
+		if got, want := pr.Diff, "https://gitee.com/kit101/drone-yml-test/pulls/7.diff"; got != want {
+			t.Errorf("Want pr Diff %q, got %q", want, got)
+		}
+		if got, want := pr.Author.Login, "kit101"; got != want {
+			t.Errorf("Want pr Author Login %q, got %q", want, got)
+		}
+		if got, want := pr.Author.Avatar, "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"; got != want {
+			t.Errorf("Want pr Author Avatar %q, got %q", want, got)
+		}
+		if got, want := pr.Closed, false; got != want {
+			t.Errorf("Want pr Closed %v, got %v", want, got)
+		}
+		if got, want := pr.Merged, false; got != want {
+			t.Errorf("Want pr Merged %v, got %v", want, got)
+		}
+		if got, want := pr.Created.Unix(), int64(1632996627); got != want {
+			t.Errorf("Want pr Created %d, got %d", want, got)
+		}
+	}
+}
+
+func testPullRequestComment(comment *scm.Comment) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := comment.ID, 6922557; got != want {
+			t.Errorf("Want pr comment ID %d, got %d", want, got)
+		}
+		if got, want := comment.Body, "test comment 1"; got != want {
+			t.Errorf("Want pr comment Body %q, got %q", want, got)
+		}
+		if got, want := comment.Author.Login, "kit101"; got != want {
+			t.Errorf("Want pr comment Author Login %q, got %q", want, got)
+		}
+		if got, want := comment.Author.Name, "kit101"; got != want {
+			t.Errorf("Want pr comment Author Name %q, got %q", want, got)
+		}
+		if got, want := comment.Created.Unix(), int64(1633570005); got != want {
+			t.Errorf("Want pr comment Created %d, got %d", want, got)
+		}
+		if got, want := comment.Updated.Unix(), int64(1633570005); got != want {
+			t.Errorf("Want pr comment Updated %d, got %d", want, got)
+		}
+	}
+}
+
+func testChange(change *scm.Change) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := change.Path, "change/add3.txt"; got != want {
+			t.Errorf("Want file change Path %q, got %q", want, got)
+		}
+		if got, want := change.Added, true; got != want {
+			t.Errorf("Want file Added %v, got %v", want, got)
+		}
+		if got, want := change.Deleted, false; got != want {
+			t.Errorf("Want file Deleted %v, got %v", want, got)
+		}
+		if got, want := change.Renamed, false; got != want {
+			t.Errorf("Want file Renamed %v, got %v", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/repo_test.go b/scm/driver/gitee/integration/repo_test.go
new file mode 100644
index 000000000..5ff0cef15
--- /dev/null
+++ b/scm/driver/gitee/integration/repo_test.go
@@ -0,0 +1,85 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+//
+// repository sub-tests
+//
+
+func testRepos(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testRepoFind(client))
+	}
+}
+
+func testRepoFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Repositories.Find(context.Background(), "kit101/drone-yml-test")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		t.Run("Repository", testRepo(result))
+		t.Run("Permissions", testPerm(result.Perm))
+	}
+}
+
+//
+// struct sub-tests
+//
+
+func testRepo(repository *scm.Repository) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		if got, want := repository.Name, "drone-yml-test"; got != want {
+			t.Errorf("Want repository Name %q, got %q", want, got)
+		}
+		if got, want := repository.Namespace, "kit101"; got != want {
+			t.Errorf("Want repository Namespace %q, got %q", want, got)
+		}
+		if got, want := repository.Branch, "master"; got != want {
+			t.Errorf("Want repository Branch %q, got %q", want, got)
+		}
+		if got, want := repository.Clone, "https://gitee.com/kit101/drone-yml-test.git"; got != want {
+			t.Errorf("Want repository Clone URL %q, got %q", want, got)
+		}
+		if got, want := repository.CloneSSH, "git@gitee.com:kit101/drone-yml-test.git"; got != want {
+			t.Errorf("Want repository SSH URL %q, got %q", want, got)
+		}
+		if got, want := repository.Link, "https://gitee.com/kit101/drone-yml-test.git"; got != want {
+			t.Errorf("Want repository Link %q, got %q", want, got)
+		}
+		if got, want := repository.Created.Unix(), int64(1616556274); got != want {
+			t.Errorf("Want repository Created %d, got %d", want, got)
+		}
+		if got, want := repository.Private, false; got != want {
+			t.Errorf("Want repository Private %v, got %v", want, got)
+		}
+	}
+}
+
+func testPerm(perms *scm.Perm) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		if got, want := perms.Pull, true; got != want {
+			t.Errorf("Want permission Pull %v, got %v", want, got)
+		}
+		if got, want := perms.Push, false; got != want {
+			t.Errorf("Want permission Push %v, got %v", want, got)
+		}
+		if got, want := perms.Admin, false; got != want {
+			t.Errorf("Want permission Admin %v, got %v", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/integration/user_test.go b/scm/driver/gitee/integration/user_test.go
new file mode 100644
index 000000000..26c731e12
--- /dev/null
+++ b/scm/driver/gitee/integration/user_test.go
@@ -0,0 +1,54 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package integration
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+//
+// user sub-tests
+//
+
+func testUsers(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		t.Run("Find", testUserFind(client))
+	}
+}
+
+func testUserFind(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		result, _, err := client.Users.FindLogin(context.Background(), "kit101")
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		t.Run("User", testUser(result))
+	}
+}
+
+//
+// struct sub-tests
+//
+
+func testUser(user *scm.User) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		if got, want := user.Login, "kit101"; got != want {
+			t.Errorf("Want user Login %q, got %q", want, got)
+		}
+		if got, want := user.Name, "kit101"; got != want {
+			t.Errorf("Want user Name %q, got %q", want, got)
+		}
+		if got, want := user.Avatar, "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"; got != want {
+			t.Errorf("Want user Avatar %q, got %q", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitee/issue.go b/scm/driver/gitee/issue.go
new file mode 100644
index 000000000..4d06350a6
--- /dev/null
+++ b/scm/driver/gitee/issue.go
@@ -0,0 +1,319 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type issueService struct {
+	client *wrapper
+}
+
+func (s *issueService) Find(ctx context.Context, repo string, number int) (*scm.Issue, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/issues/%s", repo, decodeNumber(number))
+	out := new(issue)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertIssue(out), res, err
+}
+
+func (s *issueService) FindComment(ctx context.Context, repo string, number, id int) (*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/issues/comments/%d", repo, id)
+	out := new(issueComment)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertIssueComment(out), res, err
+}
+
+func (s *issueService) List(ctx context.Context, repo string, opts scm.IssueListOptions) ([]*scm.Issue, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/issues?%s", repo, encodeIssueListOptions(opts))
+	out := []*issue{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertIssueList(out), res, err
+}
+
+func (s *issueService) ListComments(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/issues/%s/comments?%s", repo, decodeNumber(number), encodeListOptions(opts))
+	out := []*issueComment{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertIssueCommentList(out), res, err
+}
+
+func (s *issueService) Create(ctx context.Context, repo string, input *scm.IssueInput) (*scm.Issue, *scm.Response, error) {
+	owner, repoName := scm.Split(repo)
+	path := fmt.Sprintf("repos/%s/issues", owner)
+	in := &issueInput{
+		Repo:  repoName,
+		Title: input.Title,
+		Body:  input.Body,
+	}
+	out := new(issue)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertIssue(out), res, err
+}
+
+func (s *issueService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/issues/%s/comments", repo, decodeNumber(number))
+	in := &issueCommentInput{
+		Body: input.Body,
+	}
+	out := new(issueComment)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertIssueComment(out), res, err
+}
+
+func (s *issueService) DeleteComment(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/issues/comments/%d", repo, id)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *issueService) Close(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	owner, repoName := scm.Split(repo)
+	path := fmt.Sprintf("repos/%s/issues/%s", owner, decodeNumber(number))
+	data := map[string]string{
+		"repo":  repoName,
+		"state": "closed",
+	}
+	out := new(issue)
+	res, err := s.client.do(ctx, "PATCH", path, &data, out)
+	return res, err
+}
+
+func (s *issueService) Lock(context.Context, string, int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Unlock(context.Context, string, int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+type issue struct {
+	ID               int              `json:"id"`
+	URL              string           `json:"url"`
+	RepositoryURL    string           `json:"repository_url"`
+	LabelsURL        string           `json:"labels_url"`
+	CommentsURL      string           `json:"comments_url"`
+	HtmlURL          string           `json:"html_url"`
+	ParentURL        interface{}      `json:"parent_url"`
+	Number           string           `json:"number"`
+	ParentID         int              `json:"parent_id"`
+	Depth            int              `json:"depth"`
+	State            string           `json:"state"`
+	Title            string           `json:"title"`
+	Body             string           `json:"body"`
+	User             user             `json:"user"`
+	Labels           []label          `json:"labels"`
+	Assignee         assignee         `json:"assignee"`
+	Collaborators    []interface{}    `json:"collaborators"`
+	Repository       issueRepository  `json:"repository"`
+	Milestone        milestone        `json:"milestone"`
+	CreatedAt        time.Time        `json:"created_at"`
+	UpdatedAt        time.Time        `json:"updated_at"`
+	PlanStartedAt    interface{}      `json:"plan_started_at"`
+	Deadline         interface{}      `json:"deadline"`
+	FinishedAt       interface{}      `json:"finished_at"`
+	ScheduledTime    float64          `json:"scheduled_time"`
+	Comments         int              `json:"comments"`
+	Priority         int              `json:"priority"`
+	IssueType        string           `json:"issue_type"`
+	SecurityHole     bool             `json:"security_hole"`
+	IssueState       string           `json:"issue_state"`
+	IssueTypeDetail  issueTypeDetail  `json:"issue_type_detail"`
+	IssueStateDetail issueStateDetail `json:"issue_state_detail"`
+}
+type issueRepository struct {
+	ID                  int           `json:"id"`
+	FullName            string        `json:"full_name"`
+	HumanName           string        `json:"human_name"`
+	URL                 string        `json:"url"`
+	Namespace           namespace     `json:"namespace"`
+	Path                string        `json:"path"`
+	Name                string        `json:"name"`
+	Owner               user          `json:"owner"`
+	Assigner            assignee      `json:"assigner"`
+	Description         string        `json:"description"`
+	Private             bool          `json:"private"`
+	Public              bool          `json:"public"`
+	Internal            bool          `json:"internal"`
+	Fork                bool          `json:"fork"`
+	HTMLURL             string        `json:"html_url"`
+	SSHURL              string        `json:"ssh_url"`
+	ForksURL            string        `json:"forks_url"`
+	KeysURL             string        `json:"keys_url"`
+	CollaboratorsURL    string        `json:"collaborators_url"`
+	HooksURL            string        `json:"hooks_url"`
+	BranchesURL         string        `json:"branches_url"`
+	TagsURL             string        `json:"tags_url"`
+	BlobsURL            string        `json:"blobs_url"`
+	StargazersURL       string        `json:"stargazers_url"`
+	ContributorsURL     string        `json:"contributors_url"`
+	CommitsURL          string        `json:"commits_url"`
+	CommentsURL         string        `json:"comments_url"`
+	IssueCommentURL     string        `json:"issue_comment_url"`
+	IssuesURL           string        `json:"issues_url"`
+	PullsURL            string        `json:"pulls_url"`
+	MilestonesURL       string        `json:"milestones_url"`
+	NotificationsURL    string        `json:"notifications_url"`
+	LabelsURL           string        `json:"labels_url"`
+	ReleasesURL         string        `json:"releases_url"`
+	Recommend           bool          `json:"recommend"`
+	Gvp                 bool          `json:"gvp"`
+	Homepage            string        `json:"homepage"`
+	Language            interface{}   `json:"language"`
+	ForksCount          int           `json:"forks_count"`
+	StargazersCount     int           `json:"stargazers_count"`
+	WatchersCount       int           `json:"watchers_count"`
+	DefaultBranch       string        `json:"default_branch"`
+	OpenIssuesCount     int           `json:"open_issues_count"`
+	HasIssues           bool          `json:"has_issues"`
+	HasWiki             bool          `json:"has_wiki"`
+	IssueComment        bool          `json:"issue_comment"`
+	CanComment          bool          `json:"can_comment"`
+	PullRequestsEnabled bool          `json:"pull_requests_enabled"`
+	HasPage             bool          `json:"has_page"`
+	License             interface{}   `json:"license"`
+	Outsourced          bool          `json:"outsourced"`
+	ProjectCreator      string        `json:"project_creator"`
+	Members             []string      `json:"members"`
+	PushedAt            time.Time     `json:"pushed_at"`
+	CreatedAt           time.Time     `json:"created_at"`
+	UpdatedAt           time.Time     `json:"updated_at"`
+	Parent              interface{}   `json:"parent"`
+	Paas                interface{}   `json:"paas"`
+	AssigneesNumber     int           `json:"assignees_number"`
+	TestersNumber       int           `json:"testers_number"`
+	Assignee            []assignee    `json:"assignee"`
+	Testers             []tester      `json:"testers"`
+	Status              string        `json:"status"`
+	EmptyRepo           bool          `json:"empty_repo"`
+	Programs            []interface{} `json:"programs"`
+	Enterprise          interface{}   `json:"enterprise"`
+}
+type issueTypeDetail struct {
+	ID        int         `json:"id"`
+	Title     string      `json:"title"`
+	Template  interface{} `json:"template"`
+	Ident     string      `json:"ident"`
+	Color     string      `json:"color"`
+	IsSystem  bool        `json:"is_system"`
+	CreatedAt time.Time   `json:"created_at"`
+	UpdatedAt time.Time   `json:"updated_at"`
+}
+type issueStateDetail struct {
+	ID        int         `json:"id"`
+	Title     string      `json:"title"`
+	Color     string      `json:"color"`
+	Icon      string      `json:"icon"`
+	Command   interface{} `json:"command"`
+	Serial    int         `json:"serial"`
+	CreatedAt time.Time   `json:"created_at"`
+	UpdatedAt time.Time   `json:"updated_at"`
+}
+
+type issueInput struct {
+	Repo  string `json:"repo"`
+	Title string `json:"title"`
+	Body  string `json:"body"`
+}
+
+type issueComment struct {
+	ID      int    `json:"id"`
+	HTMLURL string `json:"html_url"`
+	User    struct {
+		ID        int    `json:"id"`
+		Login     string `json:"login"`
+		AvatarURL string `json:"avatar_url"`
+	} `json:"user"`
+	Body      string    `json:"body"`
+	CreatedAt time.Time `json:"created_at"`
+	UpdatedAt time.Time `json:"updated_at"`
+}
+
+type issueCommentInput struct {
+	Body string `json:"body"`
+}
+
+func convertIssueList(from []*issue) []*scm.Issue {
+	to := []*scm.Issue{}
+	for _, v := range from {
+		to = append(to, convertIssue(v))
+	}
+	return to
+}
+func convertIssue(from *issue) *scm.Issue {
+	return &scm.Issue{
+		Number: encodeNumber(from.Number),
+		Title:  from.Title,
+		Body:   from.Body,
+		Link:   from.HtmlURL,
+		Labels: convertLabels(from),
+		Closed: from.State == "closed",
+		Author: scm.User{
+			Login:  from.User.Login,
+			Avatar: from.User.AvatarURL,
+		},
+		Created: from.CreatedAt,
+		Updated: from.UpdatedAt,
+	}
+}
+
+func convertIssueCommentList(from []*issueComment) []*scm.Comment {
+	to := []*scm.Comment{}
+	for _, v := range from {
+		to = append(to, convertIssueComment(v))
+	}
+	return to
+}
+func convertIssueComment(from *issueComment) *scm.Comment {
+	return &scm.Comment{
+		ID:   from.ID,
+		Body: from.Body,
+		Author: scm.User{
+			Login:  from.User.Login,
+			Avatar: from.User.AvatarURL,
+		},
+		Created: from.CreatedAt,
+		Updated: from.UpdatedAt,
+	}
+}
+
+func convertLabels(from *issue) []string {
+	var labels []string
+	for _, label := range from.Labels {
+		labels = append(labels, label.Name)
+	}
+	return labels
+}
+
+// The issue number of gitee consists of 6 uppercase letters or numbers.
+// The ASCII of uppercase letters or numbers is between 48 and 90, so encoded issue number(max:9090909090) less than the maximum value of int.
+func encodeNumber(giteeIssueNumber string) int {
+	runes := []rune(giteeIssueNumber)
+	encodedNumber := ""
+	for i := 0; i < len(runes); i++ {
+		encodedNumber += strconv.Itoa(int(runes[i]))
+	}
+	scmNumber, err := strconv.Atoi(encodedNumber)
+	if err != nil {
+		return 0
+	}
+	return scmNumber
+}
+func decodeNumber(scmIssueNumber int) string {
+	issueNumberStr := strconv.Itoa(scmIssueNumber)
+	giteeNumber := ""
+	for i := 0; i < len(issueNumberStr)-1; i += 2 {
+		numberStr, err := strconv.Atoi(issueNumberStr[i : i+2])
+		if err != nil {
+			return ""
+		}
+		giteeNumber += string(rune(numberStr))
+	}
+	return giteeNumber
+}
diff --git a/scm/driver/gitee/issue_test.go b/scm/driver/gitee/issue_test.go
new file mode 100644
index 000000000..a59ea6b29
--- /dev/null
+++ b/scm/driver/gitee/issue_test.go
@@ -0,0 +1,280 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestEncodeAndDecodeIssueNumber(t *testing.T) {
+	giteeIssueNumber := "I4CD5P"
+	scmIssueNumber := 735267685380
+	encodedScmIssueNumber := encodeNumber(giteeIssueNumber)
+	if diff := cmp.Diff(encodedScmIssueNumber, scmIssueNumber); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+	encodedGiteeIssueNumber := decodeNumber(scmIssueNumber)
+	if diff := cmp.Diff(encodedGiteeIssueNumber, giteeIssueNumber); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestIssueFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/issues/I4CD5P").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/issue.json")
+
+	client := NewDefault()
+	got, res, err := client.Issues.Find(context.Background(), "kit101/drone-yml-test", 735267685380)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Issue)
+	raw, _ := ioutil.ReadFile("testdata/issue.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestIssueFindComment(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/issues/comments/6877445").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/issue_comment.json")
+
+	client := NewDefault()
+	got, res, err := client.Issues.FindComment(context.Background(), "kit101/drone-yml-test", 735267685380, 6877445)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Comment)
+	raw, _ := ioutil.ReadFile("testdata/issue_comment.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestIssueList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/issues").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		MatchParam("state", "all").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/issues.json")
+
+	client := NewDefault()
+	got, res, err := client.Issues.List(context.Background(), "kit101/drone-yml-test", scm.IssueListOptions{Page: 1, Size: 3, Open: true, Closed: true})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Issue{}
+	raw, _ := ioutil.ReadFile("testdata/issues.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestIssueListComments(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml/issues/I4CD5P/comments").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/issue_comments.json")
+
+	client := NewDefault()
+	got, res, err := client.Issues.ListComments(context.Background(), "kit101/drone-yml", 735267685380, scm.ListOptions{Size: 3, Page: 1})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Comment{}
+	raw, _ := ioutil.ReadFile("testdata/issue_comments.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestIssueCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Post("/repos/kit101/issues").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/issue.json")
+
+	input := scm.IssueInput{
+		Title: "test issue 1",
+		Body:  "test issue 1",
+	}
+
+	client := NewDefault()
+	got, res, err := client.Issues.Create(context.Background(), "kit101/drone-yml-test", &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Issue)
+	raw, _ := ioutil.ReadFile("testdata/issue.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestIssueCreateComment(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com").
+		Post("/repos/kit101/drone-yml-test/issues/I4CD5P/comments").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/issue_comment.json")
+
+	input := &scm.CommentInput{
+		Body: "it's ok.",
+	}
+
+	client := NewDefault()
+	got, res, err := client.Issues.CreateComment(context.Background(), "kit101/drone-yml-test", 735267685380, input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Comment)
+	raw, _ := ioutil.ReadFile("testdata/issue_comment.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestIssueDeleteComment(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Delete("/repos/kit101/drone-yml-test/issues/comments/6879139").
+		Reply(204).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.Issues.DeleteComment(context.Background(), "kit101/drone-yml-test", 735267685380, 6879139)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if got, want := res.Status, 204; got != want {
+		t.Errorf("Want response status %d, got %d", want, got)
+	}
+	t.Run("Request", testRequest(res))
+}
+
+func TestIssueClose(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Patch("/repos/kit101/issues/I4CD5P").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/issue.json")
+
+	client := NewDefault()
+	res, err := client.Issues.Close(context.Background(), "kit101/drone-yml-test", 735267685380)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestIssueLock(t *testing.T) {
+	_, err := NewDefault().Issues.Lock(context.Background(), "kit101/drone-yml-test", 735267685380)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestIssueUnlock(t *testing.T) {
+	_, err := NewDefault().Issues.Unlock(context.Background(), "kit101/drone-yml-test", 735267685380)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
diff --git a/scm/driver/gitee/linker.go b/scm/driver/gitee/linker.go
new file mode 100644
index 000000000..79b0beac5
--- /dev/null
+++ b/scm/driver/gitee/linker.go
@@ -0,0 +1,50 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type linker struct {
+	base string
+}
+
+func (l *linker) Resource(ctx context.Context, repo string, ref scm.Reference) (string, error) {
+	switch {
+	case scm.IsTag(ref.Path):
+		t := scm.TrimRef(ref.Path)
+		return fmt.Sprintf("%s%s/tree/%s", l.base, repo, t), nil
+	case scm.IsPullRequest(ref.Path):
+		d := scm.ExtractPullRequest(ref.Path)
+		return fmt.Sprintf("%s%s/pulls/%d", l.base, repo, d), nil
+	case ref.Sha == "":
+		t := scm.TrimRef(ref.Path)
+		return fmt.Sprintf("%s%s/tree/%s", l.base, repo, t), nil
+	default:
+		return fmt.Sprintf("%s%s/commit/%s", l.base, repo, ref.Sha), nil
+	}
+}
+
+func (l *linker) Diff(ctx context.Context, repo string, source, target scm.Reference) (string, error) {
+	if scm.IsPullRequest(target.Path) {
+		pullRequestID := scm.ExtractPullRequest(target.Path)
+		return fmt.Sprintf("%s%s/pulls/%d/files", l.base, repo, pullRequestID), nil
+	}
+
+	s := source.Sha
+	t := target.Sha
+	if s == "" {
+		s = scm.TrimRef(source.Path)
+	}
+	if t == "" {
+		t = scm.TrimRef(target.Path)
+	}
+
+	return fmt.Sprintf("%s%s/compare/%s...%s", l.base, repo, s, t), nil
+}
diff --git a/scm/driver/gitee/linker_test.go b/scm/driver/gitee/linker_test.go
new file mode 100644
index 000000000..55c9a84f2
--- /dev/null
+++ b/scm/driver/gitee/linker_test.go
@@ -0,0 +1,103 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func TestLink(t *testing.T) {
+	tests := []struct {
+		path string
+		sha  string
+		want string
+	}{
+		{
+			path: "refs/heads/master",
+			sha:  "2eac1cac02c325058cf959725c45b0612d3e8177",
+			want: "https://gitee.com/kit101/drone-yml-test/commit/2eac1cac02c325058cf959725c45b0612d3e8177",
+		},
+		{
+			path: "refs/pull/7/head",
+			sha:  "00b76e8abd51ae6a96318b3450944b32995f9158",
+			want: "https://gitee.com/kit101/drone-yml-test/pulls/7",
+		},
+		{
+			path: "refs/tags/1.1",
+			want: "https://gitee.com/kit101/drone-yml-test/tree/1.1",
+		},
+		{
+			path: "refs/heads/master",
+			want: "https://gitee.com/kit101/drone-yml-test/tree/master",
+		},
+	}
+
+	for _, test := range tests {
+		client := NewDefault()
+		ref := scm.Reference{
+			Path: test.path,
+			Sha:  test.sha,
+		}
+		got, err := client.Linker.Resource(context.Background(), "kit101/drone-yml-test", ref)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		want := test.want
+		if got != want {
+			t.Errorf("Want link %q, got %q", want, got)
+		}
+	}
+}
+
+func TestDiff(t *testing.T) {
+	tests := []struct {
+		source scm.Reference
+		target scm.Reference
+		want   string
+	}{
+		{
+			source: scm.Reference{Sha: "2eac1cac02c325058cf959725c45b0612d3e8177"},
+			target: scm.Reference{Sha: "00b76e8abd51ae6a96318b3450944b32995f9158"},
+			want:   "https://gitee.com/kit101/drone-yml-test/compare/2eac1cac02c325058cf959725c45b0612d3e8177...00b76e8abd51ae6a96318b3450944b32995f9158",
+		},
+		{
+			source: scm.Reference{Path: "refs/heads/master"},
+			target: scm.Reference{Sha: "00b76e8abd51ae6a96318b3450944b32995f9158"},
+			want:   "https://gitee.com/kit101/drone-yml-test/compare/master...00b76e8abd51ae6a96318b3450944b32995f9158",
+		},
+		{
+			source: scm.Reference{Sha: "00b76e8abd51ae6a96318b3450944b32995f9158"},
+			target: scm.Reference{Path: "refs/heads/master"},
+			want:   "https://gitee.com/kit101/drone-yml-test/compare/00b76e8abd51ae6a96318b3450944b32995f9158...master",
+		},
+		{
+			target: scm.Reference{Path: "refs/pull/7/head"},
+			want:   "https://gitee.com/kit101/drone-yml-test/pulls/7/files",
+		},
+	}
+
+	for _, test := range tests {
+		client := NewDefault()
+		got, err := client.Linker.Diff(context.Background(), "kit101/drone-yml-test", test.source, test.target)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		want := test.want
+		if got != want {
+			t.Errorf("Want link %q, got %q", want, got)
+		}
+	}
+}
+
+func TestLinkBase(t *testing.T) {
+	if got, want := NewDefault().Linker.(*linker).base, "https://gitee.com/"; got != want {
+		t.Errorf("Want url %s, got %s", want, got)
+	}
+}
diff --git a/scm/driver/gitee/org.go b/scm/driver/gitee/org.go
new file mode 100644
index 000000000..1a9e66947
--- /dev/null
+++ b/scm/driver/gitee/org.go
@@ -0,0 +1,97 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type organizationService struct {
+	client *wrapper
+}
+
+func (s *organizationService) Find(ctx context.Context, name string) (*scm.Organization, *scm.Response, error) {
+	path := fmt.Sprintf("orgs/%s", name)
+	out := new(organization)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertOrganization(out), res, err
+}
+
+func (s *organizationService) FindMembership(ctx context.Context, name, username string) (*scm.Membership, *scm.Response, error) {
+	path := fmt.Sprintf("orgs/%s/memberships/%s", name, username)
+	out := new(membership)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertMembership(out), res, err
+}
+
+func (s *organizationService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Organization, *scm.Response, error) {
+	path := fmt.Sprintf("user/orgs?%s", encodeListOptions(opts))
+	out := []*organization{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertOrganizationList(out), res, err
+}
+
+type organization struct {
+	ID          int    `json:"id"`
+	Login       string `json:"login"`
+	Name        string `json:"name"`
+	URL         string `json:"url"`
+	AvatarURL   string `json:"avatar_url"`
+	ReposURL    string `json:"repos_url"`
+	EventsURL   string `json:"events_url"`
+	MembersURL  string `json:"members_url"`
+	Description string `json:"description"`
+	FollowCount int    `json:"follow_count"`
+}
+
+type membership struct {
+	URL             string       `json:"url"`
+	Active          bool         `json:"active"`
+	Remark          string       `json:"remark"`
+	Role            string       `json:"role"`
+	OrganizationURL string       `json:"organization_url"`
+	Organization    organization `json:"organization"`
+	User            struct {
+		ID        int    `json:"id"`
+		Login     string `json:"login"`
+		Name      string `json:"name"`
+		AvatarURL string `json:"avatar_url"`
+		URL       string `json:"url"`
+		HtmlURL   string `json:"html_url"`
+		Remark    string `json:"remark"`
+	} `json:"user"`
+}
+
+func convertOrganizationList(from []*organization) []*scm.Organization {
+	to := []*scm.Organization{}
+	for _, v := range from {
+		to = append(to, convertOrganization(v))
+	}
+	return to
+}
+
+func convertOrganization(from *organization) *scm.Organization {
+	return &scm.Organization{
+		Name:   from.Login,
+		Avatar: from.AvatarURL,
+	}
+}
+
+func convertMembership(from *membership) *scm.Membership {
+	to := new(scm.Membership)
+	to.Active = from.Active
+	switch from.Role {
+	case "admin":
+		to.Role = scm.RoleAdmin
+	case "member":
+		to.Role = scm.RoleMember
+	default:
+		to.Role = scm.RoleUndefined
+	}
+	return to
+}
diff --git a/scm/driver/gitee/org_test.go b/scm/driver/gitee/org_test.go
new file mode 100644
index 000000000..fa08e8927
--- /dev/null
+++ b/scm/driver/gitee/org_test.go
@@ -0,0 +1,108 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestOrganizationFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/orgs/kit101-personal").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/org.json")
+
+	client := NewDefault()
+	got, res, err := client.Organizations.Find(context.Background(), "kit101-personal")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Organization)
+	raw, _ := ioutil.ReadFile("testdata/org.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestOrganizationFindMembership(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/orgs/kit101-personal/memberships/kit101").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/membership.json")
+
+	client := NewDefault()
+	got, res, err := client.Organizations.FindMembership(context.Background(), "kit101-personal", "kit101")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Membership)
+	raw, _ := ioutil.ReadFile("testdata/membership.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestOrganizationList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/user/orgs").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/orgs.json")
+
+	client := NewDefault()
+	got, res, err := client.Organizations.List(context.Background(), scm.ListOptions{Size: 3, Page: 1})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Organization{}
+	raw, _ := ioutil.ReadFile("testdata/orgs.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
diff --git a/scm/driver/gitee/pr.go b/scm/driver/gitee/pr.go
new file mode 100644
index 000000000..7a35768f9
--- /dev/null
+++ b/scm/driver/gitee/pr.go
@@ -0,0 +1,413 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type pullService struct {
+	client *wrapper
+}
+
+func (s *pullService) Find(ctx context.Context, repo string, number int) (*scm.PullRequest, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d", repo, number)
+	out := new(pr)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertPullRequest(out), res, err
+}
+
+func (s *pullService) FindComment(ctx context.Context, repo string, _ int, id int) (*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/comments/%d", repo, id)
+	out := new(prComment)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertPullRequestComment(out), res, err
+}
+
+func (s *pullService) List(ctx context.Context, repo string, opts scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls?%s", repo, encodePullRequestListOptions(opts))
+	out := []*pr{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertPullRequestList(out), res, err
+}
+
+func (s *pullService) ListChanges(ctx context.Context, repo string, number int, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d/files", repo, number)
+	out := []*prFile{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertPrChangeList(out), res, err
+}
+
+func (s *pullService) ListComments(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d/comments/?%s", repo, number, encodeListOptions(opts))
+	out := []*prComment{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertPullRequestComments(out), res, err
+}
+
+func (s *pullService) ListCommits(ctx context.Context, repo string, number int, _ scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	path := fmt.Sprintf("/repos/%s/pulls/%d/commits", repo, number)
+	out := []*prCommit{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertPrCommitList(out), res, err
+}
+
+func (s *pullService) Merge(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d/merge", repo, number)
+	res, err := s.client.do(ctx, "PUT", path, nil, nil)
+	return res, err
+}
+
+func (s *pullService) Close(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d", repo, number)
+	data := map[string]string{"state": "closed"}
+	res, err := s.client.do(ctx, "PATCH", path, &data, nil)
+	return res, err
+}
+
+func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls", repo)
+	in := &prInput{
+		Title: input.Title,
+		Body:  input.Body,
+		Head:  input.Source,
+		Base:  input.Target,
+	}
+	out := new(pr)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertPullRequest(out), res, err
+}
+
+func (s *pullService) CreateComment(ctx context.Context, repo string, number int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d/comments", repo, number)
+	in := &prCommentInput{
+		Body: input.Body,
+	}
+	out := new(prComment)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertPullRequestComment(out), res, err
+}
+
+func (s *pullService) DeleteComment(ctx context.Context, repo string, _ int, id int) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/comments/%d", repo, id)
+	res, err := s.client.do(ctx, "DELETE", path, nil, nil)
+	return res, err
+}
+
+type (
+	pr struct {
+		ID                int        `json:"id"`
+		URL               string     `json:"url"`
+		HtmlURL           string     `json:"html_url"`
+		DiffURL           string     `json:"diff_url"`
+		PatchURL          string     `json:"patch_url"`
+		IssueURL          string     `json:"issue_url"`
+		CommitsURL        string     `json:"commits_url"`
+		ReviewCommentsURL string     `json:"review_comments_url"`
+		ReviewCommentURL  string     `json:"review_comment_url"`
+		CommentsURL       string     `json:"comments_url"`
+		Number            int        `json:"number"`
+		State             string     `json:"state"`
+		Title             string     `json:"title"`
+		Body              string     `json:"body"`
+		AssigneesNumber   int        `json:"assignees_number"`
+		TestersNumber     int        `json:"testers_number"`
+		Assignees         []assignee `json:"assignees"`
+		Testers           []tester   `json:"testers"`
+		Milestone         milestone  `json:"milestone"`
+		Labels            []label    `json:"labels"`
+		Locked            bool       `json:"locked"`
+		CreatedAt         time.Time  `json:"created_at"`
+		UpdatedAt         time.Time  `json:"updated_at"`
+		ClosedAt          time.Time  `json:"closed_at"`
+		MergedAt          time.Time  `json:"merged_at"`
+		Mergeable         bool       `json:"mergeable"`
+		CanMergeCheck     bool       `json:"can_merge_check"`
+		Head              headOrBase `json:"head"`
+		Base              headOrBase `json:"base"`
+		User              user       `json:"user"`
+	}
+	assignee struct {
+		ID                int    `json:"id"`
+		Login             string `json:"login"`
+		Name              string `json:"name"`
+		AvatarURL         string `json:"avatar_url"`
+		URL               string `json:"url"`
+		HtmlURL           string `json:"html_url"`
+		Remark            string `json:"remark"`
+		FollowersURL      string `json:"followers_url"`
+		FollowingURL      string `json:"following_url"`
+		GistsURL          string `json:"gists_url"`
+		StarredURL        string `json:"starred_url"`
+		SubscriptionsURL  string `json:"subscriptions_url"`
+		OrganizationsURL  string `json:"organizations_url"`
+		ReposURL          string `json:"repos_url"`
+		EventsURL         string `json:"events_url"`
+		ReceivedEventsURL string `json:"received_events_url"`
+		Type              string `json:"type"`
+	}
+	tester struct {
+		ID                int    `json:"id"`
+		Login             string `json:"login"`
+		Name              string `json:"name"`
+		AvatarURL         string `json:"avatar_url"`
+		URL               string `json:"url"`
+		HtmlURL           string `json:"html_url"`
+		Remark            string `json:"remark"`
+		FollowersURL      string `json:"followers_url"`
+		FollowingURL      string `json:"following_url"`
+		GistsURL          string `json:"gists_url"`
+		StarredURL        string `json:"starred_url"`
+		SubscriptionsURL  string `json:"subscriptions_url"`
+		OrganizationsURL  string `json:"organizations_url"`
+		ReposURL          string `json:"repos_url"`
+		EventsURL         string `json:"events_url"`
+		ReceivedEventsURL string `json:"received_events_url"`
+		Type              string `json:"type"`
+	}
+	milestone struct {
+		URL          string      `json:"url"`
+		HtmlURL      string      `json:"html_url"`
+		ID           int         `json:"id"`
+		Number       int         `json:"number"`
+		RepositoryID interface{} `json:"repository_id"`
+		State        string      `json:"state"`
+		Title        string      `json:"title"`
+		Description  string      `json:"description"`
+		UpdatedAt    time.Time   `json:"updated_at"`
+		CreatedAt    time.Time   `json:"created_at"`
+		OpenIssues   int         `json:"open_issues"`
+		ClosedIssues int         `json:"closed_issues"`
+		DueOn        string      `json:"due_on"`
+	}
+	label struct {
+		ID           int       `json:"id"`
+		Name         string    `json:"name"`
+		Color        string    `json:"color"`
+		RepositoryID int       `json:"repository_id"`
+		URL          string    `json:"url"`
+		CreatedAt    time.Time `json:"created_at"`
+		UpdatedAt    time.Time `json:"updated_at"`
+	}
+	prRepo struct {
+		ID        int    `json:"id"`
+		FullName  string `json:"full_name"`
+		HumanName string `json:"human_name"`
+		URL       string `json:"url"`
+		//Namespace   namespace `json:"namespace"`
+		Path        string   `json:"path"`
+		Name        string   `json:"name"`
+		Owner       user     `json:"owner"`
+		Assigner    assignee `json:"assigner"`
+		Description string   `json:"description"`
+		Private     bool     `json:"private"`
+		Public      bool     `json:"public"`
+		Internal    bool     `json:"internal"`
+		Fork        bool     `json:"fork"`
+		HtmlURL     string   `json:"html_url"`
+		SshURL      string   `json:"ssh_url"`
+	}
+	headOrBase struct {
+		Label string `json:"label"`
+		Ref   string `json:"ref"`
+		Sha   string `json:"sha"`
+		User  user   `json:"user"`
+		Repo  prRepo `json:"repo"`
+	}
+
+	prComment struct {
+		URL            string    `json:"url"`
+		ID             int       `json:"id"`
+		User           user      `json:"user"`
+		CreatedAt      time.Time `json:"created_at"`
+		UpdatedAt      time.Time `json:"updated_at"`
+		Body           string    `json:"body"`
+		HtmlURL        string    `json:"html_url"`
+		PullRequestURL string    `json:"pull_request_url"`
+	}
+
+	prFile struct {
+		Sha       string `json:"sha"`
+		Filename  string `json:"filename"`
+		Status    string `json:"status"`
+		Additions string `json:"additions"`
+		Deletions string `json:"deletions"`
+		BlobURL   string `json:"blob_url"`
+		RawURL    string `json:"raw_url"`
+		Patch     struct {
+			Diff        string `json:"diff"`
+			NewPath     string `json:"new_path"`
+			OldPath     string `json:"old_path"`
+			AMode       string `json:"a_mode"`
+			BMode       string `json:"b_mode"`
+			NewFile     bool   `json:"new_file"`
+			RenamedFile bool   `json:"renamed_file"`
+			DeletedFile bool   `json:"deleted_file"`
+			TooLarge    bool   `json:"too_large"`
+		} `json:"patch"`
+	}
+
+	prCommit struct {
+		URL         string `json:"url"`
+		Sha         string `json:"sha"`
+		HtmlURL     string `json:"html_url"`
+		CommentsURL string `json:"comments_url"`
+		Commit      struct {
+			URL    string `json:"url"`
+			Author struct {
+				Name  string    `json:"name"`
+				Date  time.Time `json:"date"`
+				Email string    `json:"email"`
+			} `json:"author"`
+			Committer struct {
+				Name  string    `json:"name"`
+				Date  time.Time `json:"date"`
+				Email string    `json:"email"`
+			} `json:"committer"`
+			Message      string `json:"message"`
+			CommentCount int    `json:"comment_count"`
+		} `json:"commit"`
+		Author    author `json:"author"`
+		Committer author `json:"committer"`
+		Parents   struct {
+			URL string `json:"url"`
+			Sha string `json:"sha"`
+		} `json:"parents"`
+	}
+)
+
+type prInput struct {
+	Title string `json:"title"`
+	Body  string `json:"body"`
+	Head  string `json:"head"`
+	Base  string `json:"base"`
+}
+type prCommentInput struct {
+	Body string `json:"body"`
+}
+
+func convertPullRequestList(from []*pr) []*scm.PullRequest {
+	to := []*scm.PullRequest{}
+	for _, v := range from {
+		to = append(to, convertPullRequest(v))
+	}
+	return to
+}
+func convertPullRequest(from *pr) *scm.PullRequest {
+	var labels []scm.Label
+	for _, label := range from.Labels {
+		labels = append(labels, scm.Label{
+			Name:  label.Name,
+			Color: label.Color,
+		})
+	}
+	merged := from.State == "merged"
+	closed := from.State == "closed"
+	if merged {
+		closed = true
+	}
+	return &scm.PullRequest{
+		Number: from.Number,
+		Title:  from.Title,
+		Body:   from.Body,
+		Sha:    from.Head.Sha,
+		Ref:    fmt.Sprintf("refs/pull/%d/head", from.Number),
+		Source: from.Head.Ref,
+		Target: from.Base.Ref,
+		Fork:   from.Head.Repo.FullName,
+		Link:   from.HtmlURL,
+		Diff:   from.DiffURL,
+		Closed: closed,
+		Merged: merged,
+		Head: scm.Reference{
+			Name: from.Head.Ref,
+			Path: scm.ExpandRef(from.Head.Ref, "refs/heads"),
+			Sha:  from.Head.Sha,
+		},
+		Base: scm.Reference{
+			Name: from.Base.Ref,
+			Path: scm.ExpandRef(from.Base.Ref, "refs/heads"),
+			Sha:  from.Base.Sha,
+		},
+		Author: scm.User{
+			Login:  from.User.Login,
+			Avatar: from.User.AvatarURL,
+		},
+		Created: from.CreatedAt,
+		Updated: from.UpdatedAt,
+		Labels:  labels,
+	}
+}
+
+func convertPullRequestComments(from []*prComment) []*scm.Comment {
+	to := []*scm.Comment{}
+	for _, v := range from {
+		to = append(to, convertPullRequestComment(v))
+	}
+	return to
+}
+func convertPullRequestComment(from *prComment) *scm.Comment {
+	return &scm.Comment{
+		ID:   from.ID,
+		Body: from.Body,
+		Author: scm.User{
+			Login:  from.User.Login,
+			Name:   from.User.Name,
+			Avatar: from.User.AvatarURL,
+		},
+		Created: from.CreatedAt,
+		Updated: from.UpdatedAt,
+	}
+}
+
+func convertPrChangeList(from []*prFile) []*scm.Change {
+	to := []*scm.Change{}
+	for _, v := range from {
+		to = append(to, convertPrChange(v))
+	}
+	return to
+}
+func convertPrChange(from *prFile) *scm.Change {
+	return &scm.Change{
+		Path:    from.Filename,
+		Added:   from.Status == "added",
+		Deleted: from.Status == "deleted",
+		Renamed: from.Status == "renamed",
+		BlobID:  from.Sha,
+	}
+}
+
+func convertPrCommitList(from []*prCommit) []*scm.Commit {
+	to := []*scm.Commit{}
+	for _, v := range from {
+		to = append(to, convertPrCommit(v))
+	}
+	return to
+}
+func convertPrCommit(from *prCommit) *scm.Commit {
+	return &scm.Commit{
+		Message: from.Commit.Message,
+		Sha:     from.Sha,
+		Link:    from.HtmlURL,
+		Author: scm.Signature{
+			Name:   from.Commit.Author.Name,
+			Email:  from.Commit.Author.Email,
+			Date:   from.Commit.Author.Date,
+			Login:  from.Author.Login,
+			Avatar: from.Author.AvatarURL,
+		},
+		Committer: scm.Signature{
+			Name:   from.Commit.Committer.Name,
+			Email:  from.Commit.Committer.Email,
+			Date:   from.Commit.Committer.Date,
+			Login:  from.Committer.Login,
+			Avatar: from.Committer.AvatarURL,
+		},
+	}
+}
diff --git a/scm/driver/gitee/pr_test.go b/scm/driver/gitee/pr_test.go
new file mode 100644
index 000000000..bc20d30b5
--- /dev/null
+++ b/scm/driver/gitee/pr_test.go
@@ -0,0 +1,320 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestPullFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/pulls/6").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/pr.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.Find(context.Background(), "kit101/drone-yml-test", 6)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.PullRequest)
+	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullFindComment(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/pulls/comments/6922557").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/pr_comment.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.FindComment(context.Background(), "kit101/drone-yml-test", 7, 6922557)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	want := new(scm.Comment)
+	raw, _ := ioutil.ReadFile("testdata/pr_comment.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/pulls").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		MatchParam("state", "all").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/pulls.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.List(context.Background(), "kit101/drone-yml-test", scm.PullRequestListOptions{Page: 1, Size: 3, Open: true, Closed: true})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.PullRequest{}
+	raw, _ := ioutil.ReadFile("testdata/pulls.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestPullListChanges(t *testing.T) {
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/pulls/6/files").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/pr_files.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.ListChanges(context.Background(), "kit101/drone-yml-test", 6, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Change{}
+	raw, _ := ioutil.ReadFile("testdata/pr_files.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullListComments(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/pulls/7/comments").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/pr_comments.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.ListComments(context.Background(), "kit101/drone-yml-test", 7, scm.ListOptions{Page: 1, Size: 3})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Comment{}
+	raw, _ := ioutil.ReadFile("testdata/pr_comments.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestPullListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/pulls/7/commits").
+		Reply(200).
+		Type("application/json").
+		File("testdata/pr_commits.json")
+
+	client := NewDefault()
+	got, _, err := client.PullRequests.ListCommits(context.Background(), "kit101/drone-yml-test", 7, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/pr_commits.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullMerge(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Put("/repos/kit101/drone-yml-test/pulls/6/merge").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.PullRequests.Merge(context.Background(), "kit101/drone-yml-test", 6)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullClose(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Patch("/repos/kit101/drone-yml-test/pulls/6").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.PullRequests.Close(context.Background(), "kit101/drone-yml-test", 6)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Post("/repos/kit101/drone-yml-test/pulls").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/pr.json")
+
+	input := scm.PullRequestInput{
+		Title:  "new-feature",
+		Body:   "Please pull these awesome changes",
+		Source: "crud",
+		Target: "master",
+	}
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.Create(context.Background(), "kit101/drone-yml-test", &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.PullRequest)
+	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullCommentCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Post("/repos/kit101/drone-yml-test/pulls/7/comments").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/pr_comment.json")
+
+	client := NewDefault()
+	input := scm.CommentInput{
+		Body: "test comment 1",
+	}
+	got, res, err := client.PullRequests.CreateComment(context.Background(), "kit101/drone-yml-test", 7, &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	want := new(scm.Comment)
+	raw, _ := ioutil.ReadFile("testdata/pr_comment.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestPullCommentDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Delete("/repos/kit101/drone-yml-test/pulls/comments/6990588").
+		Reply(204).
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.PullRequests.DeleteComment(context.Background(), "kit101/drone-yml-test", 7, 6990588)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if got, want := res.Status, 204; got != want {
+		t.Errorf("Want response status %d, got %d", want, got)
+	}
+	t.Run("Request", testRequest(res))
+}
diff --git a/scm/driver/gitee/repo.go b/scm/driver/gitee/repo.go
new file mode 100644
index 000000000..967ab4f59
--- /dev/null
+++ b/scm/driver/gitee/repo.go
@@ -0,0 +1,241 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type RepositoryService struct {
+	client *wrapper
+}
+
+func (s *RepositoryService) Find(ctx context.Context, repo string) (*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s", repo)
+	out := new(repository)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRepository(out), res, err
+}
+
+func (s *RepositoryService) FindHook(ctx context.Context, repo string, id string) (*scm.Hook, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/hooks/%s", repo, id)
+	out := new(hook)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertHook(out), res, err
+}
+
+func (s *RepositoryService) FindPerms(ctx context.Context, repo string) (*scm.Perm, *scm.Response, error) {
+	repos, res, err := s.Find(ctx, repo)
+	if err == nil {
+		return repos.Perm, res, err
+	}
+	return nil, res, err
+}
+
+func (s *RepositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("user/repos?%s", encodeListOptions(opts))
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+func (s *RepositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// gitee does not support search filters, hence calling List api without search filtering
+	return s.List(ctx, opts.ListOptions)
+}
+
+func (s *RepositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *RepositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/hooks?%s", repo, encodeListOptions(opts))
+	out := []*hook{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertHookList(out), res, err
+}
+
+func (s *RepositoryService) ListStatus(context.Context, string, string, scm.ListOptions) ([]*scm.Status, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *RepositoryService) CreateHook(ctx context.Context, repo string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/hooks", repo)
+	in := new(hook)
+	// 1: signature
+	in.EncryptionType = 1
+	in.Password = input.Secret
+	in.URL = input.Target
+	convertFromHookEvents(input.Events, in)
+
+	out := new(hook)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertHook(out), res, err
+}
+
+func (s *RepositoryService) CreateStatus(context.Context, string, string, *scm.StatusInput) (*scm.Status, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *RepositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/hooks/%s", repo, id)
+	in := new(hook)
+	// 1: signature
+	in.EncryptionType = 1
+	in.Password = input.Secret
+	in.URL = input.Target
+	convertFromHookEvents(input.Events, in)
+
+	out := new(hook)
+	res, err := s.client.do(ctx, "PATCH", path, in, out)
+	return convertHook(out), res, err
+}
+
+func (s *RepositoryService) DeleteHook(ctx context.Context, repo, id string) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/hooks/%s", repo, id)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+type repository struct {
+	ID    int `json:"id"`
+	Owner struct {
+		ID        int    `json:"id"`
+		Login     string `json:"login"`
+		Name      string `json:"name"`
+		AvatarURL string `json:"avatar_url"`
+	} `json:"owner"`
+	Namespace     namespace `json:"namespace"`
+	Name          string    `json:"name"`
+	FullName      string    `json:"full_name"`
+	HumanName     string    `json:"human_name"`
+	Path          string    `json:"path"`
+	Public        bool      `json:"public"`
+	Private       bool      `json:"private"`
+	Internal      bool      `json:"internal"`
+	Fork          bool      `json:"fork"`
+	URL           string    `json:"url"`
+	HtmlURL       string    `json:"html_url"`
+	SshURL        string    `json:"ssh_url"`
+	DefaultBranch string    `json:"default_branch"`
+	CreatedAt     time.Time `json:"created_at"`
+	UpdatedAt     time.Time `json:"updated_at"`
+	Permission    struct {
+		Admin bool `json:"admin"`
+		Push  bool `json:"push"`
+		Pull  bool `json:"pull"`
+	} `json:"permission"`
+}
+
+type hook struct {
+	ID        int       `json:"id"`
+	URL       string    `json:"url"`
+	CreatedAt time.Time `json:"created_at"`
+	// password encryption type, 0: password, 1: signature
+	EncryptionType      int    `json:"encryption_type"`
+	Password            string `json:"password"`
+	ProjectID           int    `json:"project_id"`
+	Result              string `json:"result"`
+	ResultCode          int    `json:"result_code"`
+	PushEvents          bool   `json:"push_events"`
+	TagPushEvents       bool   `json:"tag_push_events"`
+	IssuesEvents        bool   `json:"issues_events"`
+	NoteEvents          bool   `json:"note_events"`
+	MergeRequestsEvents bool   `json:"merge_requests_events"`
+}
+
+type namespace struct {
+	ID      int    `json:"id"`
+	Type    string `json:"type"`
+	Name    string `json:"name"`
+	Path    string `json:"path"`
+	HtmlURL string `json:"html_url"`
+}
+
+func convertRepositoryList(from []*repository) []*scm.Repository {
+	to := []*scm.Repository{}
+	for _, v := range from {
+		to = append(to, convertRepository(v))
+	}
+	return to
+}
+func convertRepository(from *repository) *scm.Repository {
+	return &scm.Repository{
+		ID:        strconv.Itoa(from.ID),
+		Name:      from.Path,
+		Namespace: from.Namespace.Path,
+		Perm: &scm.Perm{
+			Push:  from.Permission.Push,
+			Pull:  from.Permission.Pull,
+			Admin: from.Permission.Admin,
+		},
+		Link:     from.HtmlURL,
+		Branch:   from.DefaultBranch,
+		Private:  from.Private,
+		Clone:    from.HtmlURL,
+		CloneSSH: from.SshURL,
+		Created:  from.CreatedAt,
+		Updated:  from.UpdatedAt,
+	}
+}
+
+func convertHookList(from []*hook) []*scm.Hook {
+	to := []*scm.Hook{}
+	for _, v := range from {
+		to = append(to, convertHook(v))
+	}
+	return to
+}
+func convertHook(from *hook) *scm.Hook {
+	return &scm.Hook{
+		ID:         strconv.Itoa(from.ID),
+		Active:     true,
+		Target:     from.URL,
+		Events:     convertHookEvent(from),
+		SkipVerify: true,
+	}
+}
+
+func convertHookEvent(from *hook) []string {
+	var events []string
+	if from.PushEvents {
+		events = append(events, "push")
+	}
+	if from.TagPushEvents {
+		events = append(events, "tag_push")
+	}
+	if from.IssuesEvents {
+		events = append(events, "issues")
+	}
+	if from.NoteEvents {
+		events = append(events, "note")
+	}
+	if from.MergeRequestsEvents {
+		events = append(events, "merge_requests")
+	}
+	return events
+}
+
+// convertFromHookEvents not support: Branch, Deployment
+func convertFromHookEvents(from scm.HookEvents, to *hook) {
+	if from.Push {
+		to.PushEvents = true
+	}
+	if from.PullRequest {
+		to.MergeRequestsEvents = true
+	}
+	if from.Issue {
+		to.IssuesEvents = true
+	}
+	if from.IssueComment || from.PullRequestComment || from.ReviewComment {
+		to.NoteEvents = true
+	}
+	if from.Tag {
+		to.TagPushEvents = true
+	}
+}
diff --git a/scm/driver/gitee/repo_test.go b/scm/driver/gitee/repo_test.go
new file mode 100644
index 000000000..1e3459ad9
--- /dev/null
+++ b/scm/driver/gitee/repo_test.go
@@ -0,0 +1,359 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestRepositoryFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/repo.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.Find(context.Background(), "kit101/drone-yml-test")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Repository)
+	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestRepositoryHookFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/hooks/787341").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/hook.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.FindHook(context.Background(), "kit101/drone-yml-test", "787341")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Hook)
+	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestRepositoryPerms(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/repo.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.FindPerms(context.Background(), "kit101/drone-yml-test")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Repository)
+	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want.Perm); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestRepositoryList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/user/repos").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/repos.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.List(context.Background(), scm.ListOptions{Page: 1, Size: 3})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestRepositoryListHook(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/kit101/drone-yml-test/hooks").
+		MatchParam("page", "1").
+		MatchParam("per_page", "3").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/hooks.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.ListHooks(context.Background(), "kit101/drone-yml-test", scm.ListOptions{Page: 1, Size: 3})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Hook{}
+	raw, _ := ioutil.ReadFile("testdata/hooks.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestRepositoryListStatus(t *testing.T) {
+	_, _, err := NewDefault().Repositories.ListStatus(context.Background(), "", "", scm.ListOptions{})
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestRepositoryCreateHook(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Post("/repos/kit101/drone-yml-test/hooks").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/hook.json")
+
+	in := &scm.HookInput{
+		Target: "http://test.kit101.com/webhook",
+		Secret: "123asdas123",
+	}
+
+	client := NewDefault()
+	got, res, err := client.Repositories.CreateHook(context.Background(), "kit101/drone-yml-test", in)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Hook)
+	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestRepositoryCreateStatus(t *testing.T) {
+	_, _, err := NewDefault().Repositories.CreateStatus(context.Background(), "", "", &scm.StatusInput{})
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestRepositoryUpdateHook(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Patch("/repos/kit101/drone-yml-test/hooks/787341").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/hook.json")
+
+	in := &scm.HookInput{
+		Target: "http://test.kit101.com/webhook",
+		Secret: "123asdas123",
+	}
+
+	client := NewDefault()
+	got, res, err := client.Repositories.UpdateHook(context.Background(), "kit101/drone-yml-test", "787341", in)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Hook)
+	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+}
+
+func TestRepositoryDeleteHook(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Delete("/repos/kit101/drone-yml-test/hooks/787341").
+		Reply(204).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.Repositories.DeleteHook(context.Background(), "kit101/drone-yml-test", "787341")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if got, want := res.Status, 204; got != want {
+		t.Errorf("Want response status %d, got %d", want, got)
+	}
+	t.Run("Request", testRequest(res))
+}
+
+func TestRepositoryNotFound(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/repos/dev/null").
+		Reply(404).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/error.json")
+
+	client := NewDefault()
+	_, _, err := client.Repositories.Find(context.Background(), "dev/null")
+	if err == nil {
+		t.Errorf("Expect Not Found error")
+		return
+	}
+	if got, want := err.Error(), "404 Project Not Found"; got != want {
+		t.Errorf("Want error %q, got %q", want, got)
+	}
+}
+
+func TestHookEvents(t *testing.T) {
+	tests := []struct {
+		in  scm.HookEvents
+		out *hook
+	}{
+		{
+			in:  scm.HookEvents{Push: true},
+			out: &hook{PushEvents: true},
+		},
+		{
+			in:  scm.HookEvents{Branch: true},
+			out: &hook{},
+		},
+		{
+			in:  scm.HookEvents{IssueComment: true},
+			out: &hook{NoteEvents: true},
+		},
+		{
+			in:  scm.HookEvents{PullRequestComment: true},
+			out: &hook{NoteEvents: true},
+		},
+		{
+			in:  scm.HookEvents{Issue: true},
+			out: &hook{IssuesEvents: true},
+		},
+		{
+			in:  scm.HookEvents{PullRequest: true},
+			out: &hook{MergeRequestsEvents: true},
+		},
+		{
+			in: scm.HookEvents{
+				Branch:             true,
+				Deployment:         true,
+				Issue:              true,
+				IssueComment:       true,
+				PullRequest:        true,
+				PullRequestComment: true,
+				Push:               true,
+				ReviewComment:      true,
+				Tag:                true,
+			},
+			out: &hook{
+				IssuesEvents:        true,
+				MergeRequestsEvents: true,
+				NoteEvents:          true,
+				PushEvents:          true,
+				TagPushEvents:       true,
+			},
+		},
+	}
+
+	for i, test := range tests {
+		fmt.Println(test, i)
+		got := new(hook)
+		convertFromHookEvents(test.in, got)
+		want := test.out
+		if diff := cmp.Diff(got, want); diff != "" {
+			t.Errorf("Unexpected Results at index %d", i)
+			t.Log(diff)
+		}
+	}
+}
diff --git a/scm/driver/gitee/review.go b/scm/driver/gitee/review.go
new file mode 100644
index 000000000..95749db1a
--- /dev/null
+++ b/scm/driver/gitee/review.go
@@ -0,0 +1,31 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type reviewService struct {
+	client *wrapper
+}
+
+func (s *reviewService) Find(context.Context, string, int, int) (*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) List(context.Context, string, int, scm.ListOptions) ([]*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) Create(context.Context, string, int, *scm.ReviewInput) (*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) Delete(context.Context, string, int, int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/gitee/review_test.go b/scm/driver/gitee/review_test.go
new file mode 100644
index 000000000..28b2fd775
--- /dev/null
+++ b/scm/driver/gitee/review_test.go
@@ -0,0 +1,44 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func TestReviewFind(t *testing.T) {
+	service := new(reviewService)
+	_, _, err := service.Find(context.Background(), "kit101/drone-yml-test", 1, 1)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestReviewList(t *testing.T) {
+	service := new(reviewService)
+	_, _, err := service.List(context.Background(), "kit101/drone-yml-test", 1, scm.ListOptions{})
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestReviewCreate(t *testing.T) {
+	service := new(reviewService)
+	_, _, err := service.Create(context.Background(), "kit101/drone-yml-test", 1, nil)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
+
+func TestReviewDelete(t *testing.T) {
+	service := new(reviewService)
+	_, err := service.Delete(context.Background(), "kit101/drone-yml-test", 1, 1)
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
diff --git a/scm/driver/gitee/testdata/branch.json b/scm/driver/gitee/testdata/branch.json
new file mode 100644
index 000000000..ae0ad3dd7
--- /dev/null
+++ b/scm/driver/gitee/testdata/branch.json
@@ -0,0 +1,49 @@
+{
+  "name": "master",
+  "commit": {
+    "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "commit": {
+      "author": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+      "message": "add/update/delete/rename\n",
+      "tree": {
+        "sha": "55d4c682f3e2b276cc40c70e59cebb17dcb1273a",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/55d4c682f3e2b276cc40c70e59cebb17dcb1273a"
+      },
+      "committer": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      }
+    },
+    "author": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "id": 1535738,
+      "login": "kit101"
+    },
+    "parents": [
+      {
+        "sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374"
+      }
+    ],
+    "committer": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "id": 1535738,
+      "login": "kit101"
+    }
+  },
+  "_links": {
+    "html": "https://gitee.com/kit101/drone-yml-test/tree/master",
+    "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches/master"
+  },
+  "protected": false,
+  "protection_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches/master/protection"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/branch.json.golden b/scm/driver/gitee/testdata/branch.json.golden
new file mode 100644
index 000000000..ec8a3ea78
--- /dev/null
+++ b/scm/driver/gitee/testdata/branch.json.golden
@@ -0,0 +1,5 @@
+{
+  "Name": "master",
+  "Path": "refs/heads/master",
+  "Sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/branches.json b/scm/driver/gitee/testdata/branches.json
new file mode 100644
index 000000000..7f49be0af
--- /dev/null
+++ b/scm/driver/gitee/testdata/branches.json
@@ -0,0 +1,11 @@
+[
+  {
+    "name": "master",
+    "commit": {
+      "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d"
+    },
+    "protected": false,
+    "protection_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches/master/protection"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/branches.json.golden b/scm/driver/gitee/testdata/branches.json.golden
new file mode 100644
index 000000000..161b33c27
--- /dev/null
+++ b/scm/driver/gitee/testdata/branches.json.golden
@@ -0,0 +1,7 @@
+[
+  {
+    "Name": "master",
+    "Path": "refs/heads/master",
+    "Sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/changes.json b/scm/driver/gitee/testdata/changes.json
new file mode 100644
index 000000000..73aa16994
--- /dev/null
+++ b/scm/driver/gitee/testdata/changes.json
@@ -0,0 +1,87 @@
+{
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+  "sha": "7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+  "html_url": "https://gitee.com/kit101/drone-yml-test/commit/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+  "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51/comments",
+  "commit": {
+    "author": {
+      "name": "kit101",
+      "date": "2021-08-23T23:41:22+08:00",
+      "email": "qkssk1711@163.com"
+    },
+    "committer": {
+      "name": "kit101",
+      "date": "2021-08-23T23:41:22+08:00",
+      "email": "qkssk1711@163.com"
+    },
+    "message": "rename\n",
+    "tree": {
+      "sha": "3dd0fe5f5bcde3966871c8eb2c91d55554dab80f",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/3dd0fe5f5bcde3966871c8eb2c91d55554dab80f"
+    }
+  },
+  "author": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "committer": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "parents": [
+    {
+      "sha": "f87d3cca9d85a7e52bb9dadac176817812b76fbd",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/f87d3cca9d85a7e52bb9dadac176817812b76fbd"
+    }
+  ],
+  "stats": {
+    "id": "7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+    "additions": 3,
+    "deletions": 3,
+    "total": 6
+  },
+  "files": [
+    {
+      "sha": "b748919728ba7080c9e41e2b888872b09d246d90",
+      "filename": "rename.4.txt",
+      "status": "modified",
+      "additions": 0,
+      "deletions": 0,
+      "changes": 0,
+      "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51/rename.4.txt",
+      "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51/rename.4.txt",
+      "content_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/rename.4.txt?ref=7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+      "patch": ""
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/changes.json.golden b/scm/driver/gitee/testdata/changes.json.golden
new file mode 100644
index 000000000..859972e23
--- /dev/null
+++ b/scm/driver/gitee/testdata/changes.json.golden
@@ -0,0 +1,9 @@
+[
+  {
+    "Path": "rename.4.txt",
+    "Added": false,
+    "Renamed": true,
+    "Deleted": false,
+    "BlobID": "b748919728ba7080c9e41e2b888872b09d246d90"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/commit.json b/scm/driver/gitee/testdata/commit.json
new file mode 100644
index 000000000..c40d2ee30
--- /dev/null
+++ b/scm/driver/gitee/testdata/commit.json
@@ -0,0 +1,123 @@
+{
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+  "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+  "html_url": "https://gitee.com/kit101/drone-yml-test/commit/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+  "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/comments",
+  "commit": {
+    "author": {
+      "name": "kit101",
+      "date": "2021-08-23T23:45:53+08:00",
+      "email": "qkssk1711@163.com"
+    },
+    "committer": {
+      "name": "kit101",
+      "date": "2021-08-23T23:45:53+08:00",
+      "email": "qkssk1711@163.com"
+    },
+    "message": "add/update/delete/rename\n",
+    "tree": {
+      "sha": "55d4c682f3e2b276cc40c70e59cebb17dcb1273a",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/55d4c682f3e2b276cc40c70e59cebb17dcb1273a"
+    }
+  },
+  "author": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "committer": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "parents": [
+    {
+      "sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374"
+    }
+  ],
+  "stats": {
+    "id": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "additions": 4,
+    "deletions": 3,
+    "total": 7
+  },
+  "files": [
+    {
+      "sha": "d28d40b18823a27071d0e9ce89c149adb3f9c4ee",
+      "filename": "change/add.txt",
+      "status": "added",
+      "additions": 1,
+      "deletions": 0,
+      "changes": 1,
+      "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/change/add.txt",
+      "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/change/add.txt",
+      "content_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/change/add.txt?ref=e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+      "patch": "@@ -0,0 +1 @@\n+add\n\\ No newline at end of file\n"
+    },
+    {
+      "sha": "41f6d1ab2598013cc08173771d81f67106bd15fc",
+      "filename": "change/modified.txt",
+      "status": "modified",
+      "additions": 2,
+      "deletions": 1,
+      "changes": 3,
+      "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/change/modified.txt",
+      "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/change/modified.txt",
+      "content_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/change/modified.txt?ref=e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+      "patch": "@@ -1 +1,2 @@\n-will be modified\n\\ No newline at end of file\n+will be modified\n+modified\n\\ No newline at end of file\n"
+    },
+    {
+      "sha": "026c61940bebac2bd51a709af499db11a62b9beb",
+      "filename": "change/remove.txt",
+      "status": "removed",
+      "additions": 0,
+      "deletions": 1,
+      "changes": 1,
+      "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/537575f44a09c57dfc472e26fe067754fd2f9374/change/remove.txt",
+      "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/537575f44a09c57dfc472e26fe067754fd2f9374/change/remove.txt",
+      "content_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/change/remove.txt?ref=537575f44a09c57dfc472e26fe067754fd2f9374",
+      "patch": "@@ -1 +0,0 @@\n-will be removed\n\\ No newline at end of file\n"
+    },
+    {
+      "sha": "b5442782c85d797e70d7bcd14abab49fd8c40b5b",
+      "filename": "change/rename.1.txt",
+      "status": "modified",
+      "additions": 0,
+      "deletions": 0,
+      "changes": 0,
+      "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/change/rename.1.txt",
+      "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/change/rename.1.txt",
+      "content_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/change/rename.1.txt?ref=e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+      "patch": ""
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/commit.json.golden b/scm/driver/gitee/testdata/commit.json.golden
new file mode 100644
index 000000000..4ee12ddcb
--- /dev/null
+++ b/scm/driver/gitee/testdata/commit.json.golden
@@ -0,0 +1,19 @@
+{
+  "Sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+  "Message": "add/update/delete/rename\n",
+  "Author": {
+    "Name": "kit101",
+    "Date": "2021-08-23T23:45:53+08:00",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+  },
+  "Committer": {
+    "Name": "kit101",
+    "Date": "2021-08-23T23:45:53+08:00",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+  },
+  "Link": "https://gitee.com/kit101/drone-yml-test/commit/e3c0ff4d5cef439ea11b30866fb1ed79b420801d"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/commits.json b/scm/driver/gitee/testdata/commits.json
new file mode 100644
index 000000000..a4cb29163
--- /dev/null
+++ b/scm/driver/gitee/testdata/commits.json
@@ -0,0 +1,203 @@
+[
+  {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/commit/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/comments",
+    "commit": {
+      "author": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "committer": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "message": "add/update/delete/rename\n",
+      "tree": {
+        "sha": "55d4c682f3e2b276cc40c70e59cebb17dcb1273a",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/55d4c682f3e2b276cc40c70e59cebb17dcb1273a"
+      }
+    },
+    "author": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "committer": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "parents": [
+      {
+        "sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374"
+      }
+    ]
+  },
+  {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374",
+    "sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/commit/537575f44a09c57dfc472e26fe067754fd2f9374",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374/comments",
+    "commit": {
+      "author": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:04+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "committer": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:04+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "message": "change ready\n",
+      "tree": {
+        "sha": "c5851e358f5c1950ce15772b0cf05941470793bc",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/c5851e358f5c1950ce15772b0cf05941470793bc"
+      }
+    },
+    "author": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "committer": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "parents": [
+      {
+        "sha": "7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51"
+      }
+    ]
+  },
+  {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+    "sha": "7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/commit/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51/comments",
+    "commit": {
+      "author": {
+        "name": "kit101",
+        "date": "2021-08-23T23:41:22+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "committer": {
+        "name": "kit101",
+        "date": "2021-08-23T23:41:22+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "message": "rename\n",
+      "tree": {
+        "sha": "3dd0fe5f5bcde3966871c8eb2c91d55554dab80f",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/3dd0fe5f5bcde3966871c8eb2c91d55554dab80f"
+      }
+    },
+    "author": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "committer": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "parents": [
+      {
+        "sha": "f87d3cca9d85a7e52bb9dadac176817812b76fbd",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/f87d3cca9d85a7e52bb9dadac176817812b76fbd"
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/commits.json.golden b/scm/driver/gitee/testdata/commits.json.golden
new file mode 100644
index 000000000..91c30b1f4
--- /dev/null
+++ b/scm/driver/gitee/testdata/commits.json.golden
@@ -0,0 +1,59 @@
+[
+  {
+    "Sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "Message": "add/update/delete/rename\n",
+    "Author": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-08-23T23:45:53+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Committer": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-08-23T23:45:53+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Link": "https://gitee.com/kit101/drone-yml-test/commit/e3c0ff4d5cef439ea11b30866fb1ed79b420801d"
+  },
+  {
+    "Sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+    "Message": "change ready\n",
+    "Author": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-08-23T23:45:04+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Committer": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-08-23T23:45:04+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Link": "https://gitee.com/kit101/drone-yml-test/commit/537575f44a09c57dfc472e26fe067754fd2f9374"
+  },
+  {
+    "Sha": "7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51",
+    "Message": "rename\n",
+    "Author": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-08-23T23:41:22+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Committer": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-08-23T23:41:22+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Link": "https://gitee.com/kit101/drone-yml-test/commit/7e84b6f94b8d4bfaa051910cc4ce16b73bcffd51"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/compare.json b/scm/driver/gitee/testdata/compare.json
new file mode 100644
index 000000000..24451e9c3
--- /dev/null
+++ b/scm/driver/gitee/testdata/compare.json
@@ -0,0 +1,201 @@
+{
+  "base_commit": {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/commit/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/comments",
+    "commit": {
+      "author": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "committer": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "message": "add/update/delete/rename\n",
+      "tree": {
+        "sha": "55d4c682f3e2b276cc40c70e59cebb17dcb1273a",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/55d4c682f3e2b276cc40c70e59cebb17dcb1273a"
+      }
+    },
+    "author": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "committer": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "parents": [
+      {
+        "sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374"
+      }
+    ]
+  },
+  "merge_base_commit": {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/commit/e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d/comments",
+    "commit": {
+      "author": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "committer": {
+        "name": "kit101",
+        "date": "2021-08-23T23:45:53+08:00",
+        "email": "qkssk1711@163.com"
+      },
+      "message": "add/update/delete/rename\n",
+      "tree": {
+        "sha": "55d4c682f3e2b276cc40c70e59cebb17dcb1273a",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/55d4c682f3e2b276cc40c70e59cebb17dcb1273a"
+      }
+    },
+    "author": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "committer": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "parents": [
+      {
+        "sha": "537575f44a09c57dfc472e26fe067754fd2f9374",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/537575f44a09c57dfc472e26fe067754fd2f9374"
+      }
+    ]
+  },
+  "commits": [
+    {
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/2700445cd84c08546f4d003f8aa54d2099a006b7",
+      "sha": "2700445cd84c08546f4d003f8aa54d2099a006b7",
+      "html_url": "https://gitee.com/kit101/drone-yml-test/commit/2700445cd84c08546f4d003f8aa54d2099a006b7",
+      "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/2700445cd84c08546f4d003f8aa54d2099a006b7/comments",
+      "commit": {
+        "author": {
+          "name": "kit101",
+          "date": "2021-09-28T16:45:33+00:00",
+          "email": "qkssk1711@163.com"
+        },
+        "committer": {
+          "name": "Gitee",
+          "date": "2021-09-28T16:45:33+00:00",
+          "email": "noreply@gitee.com"
+        },
+        "message": "add feat-compare.txt.\n",
+        "tree": {
+          "sha": "3743833f1906b6343c0e373e9d5c5c02bb793f4d",
+          "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/3743833f1906b6343c0e373e9d5c5c02bb793f4d"
+        }
+      },
+      "author": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "committer": null,
+      "parents": [
+        {
+          "sha": "e3c0ff4d5cef439ea11b30866fb1ed79b420801d",
+          "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/e3c0ff4d5cef439ea11b30866fb1ed79b420801d"
+        }
+      ]
+    }
+  ],
+  "files": [
+    {
+      "sha": "b3a3b1dc93cb4323ef918aa742227356e91ddf1e",
+      "filename": "feat-compare.txt",
+      "status": "added",
+      "additions": 1,
+      "deletions": 0,
+      "changes": 1,
+      "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/2700445cd84c08546f4d003f8aa54d2099a006b7/feat-compare.txt",
+      "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/2700445cd84c08546f4d003f8aa54d2099a006b7/feat-compare.txt",
+      "content_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/feat-compare.txt?ref=2700445cd84c08546f4d003f8aa54d2099a006b7",
+      "patch": "@@ -0,0 +1 @@\n+feat-compare\n\\ No newline at end of file\n"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/compare.json.golden b/scm/driver/gitee/testdata/compare.json.golden
new file mode 100644
index 000000000..06be5bbcd
--- /dev/null
+++ b/scm/driver/gitee/testdata/compare.json.golden
@@ -0,0 +1,9 @@
+[
+  {
+    "Path": "feat-compare.txt",
+    "Added": true,
+    "Renamed": false,
+    "Deleted": false,
+    "BlobID": "b3a3b1dc93cb4323ef918aa742227356e91ddf1e"
+  }
+]
diff --git a/scm/driver/gitee/testdata/content.json b/scm/driver/gitee/testdata/content.json
new file mode 100644
index 000000000..1f9136c8a
--- /dev/null
+++ b/scm/driver/gitee/testdata/content.json
@@ -0,0 +1,16 @@
+{
+  "type": "file",
+  "encoding": "base64",
+  "size": 22,
+  "name": "README.md",
+  "path": "README.md",
+  "content": "IyBkcm9uZS15bWwtdGVzdAoKdGVzdA==",
+  "sha": "1563ebda9da15e6a7753431774920c62b168e5bb",
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/README.md",
+  "html_url": "https://gitee.com/kit101/drone-yml-test/blob/d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83/README.md",
+  "download_url": "https://gitee.com/kit101/drone-yml-test/raw/d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83/README.md",
+  "_links": {
+    "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/README.md",
+    "html": "https://gitee.com/kit101/drone-yml-test/blob/d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83/README.md"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/content.json.golden b/scm/driver/gitee/testdata/content.json.golden
new file mode 100644
index 000000000..8f970cd12
--- /dev/null
+++ b/scm/driver/gitee/testdata/content.json.golden
@@ -0,0 +1,5 @@
+{
+  "Path": "README.md",
+  "Data": "IyBkcm9uZS15bWwtdGVzdAoKdGVzdA==",
+  "Sha": "1563ebda9da15e6a7753431774920c62b168e5bb"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/content_create.json b/scm/driver/gitee/testdata/content_create.json
new file mode 100644
index 000000000..fe60e5d3b
--- /dev/null
+++ b/scm/driver/gitee/testdata/content_create.json
@@ -0,0 +1,40 @@
+{
+  "content": {
+    "name": "CreateByDroneGiteeProvider.md",
+    "path": "apitest/CreateByDroneGiteeProvider.md",
+    "size": 56,
+    "sha": "9de0cf94e1e3c1cbe0a25c3865de4cc9ede7ad3e",
+    "type": "file",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/CreateByDroneGiteeProvider.md",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/CreateByDroneGiteeProvider.md",
+    "download_url": "https://gitee.com/kit101/drone-yml-test/raw/master/apitest/CreateByDroneGiteeProvider.md",
+    "_links": {
+      "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/CreateByDroneGiteeProvider.md",
+      "html": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/CreateByDroneGiteeProvider.md"
+    }
+  },
+  "commit": {
+    "sha": "261b772064f5aed55d626799417ec31b1c48080b",
+    "author": {
+      "name": "kit101",
+      "date": "2021-08-17T15:16:26+00:00",
+      "email": "kit101@gitee.com"
+    },
+    "committer": {
+      "name": "Gitee",
+      "date": "2021-08-17T15:16:26+00:00",
+      "email": "noreply@gitee.com"
+    },
+    "message": "my commit message",
+    "tree": {
+      "sha": "3830427494001c27ed6a9711a823a4fc6af836af",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/3830427494001c27ed6a9711a823a4fc6af836af"
+    },
+    "parents": [
+      {
+        "sha": "d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/d295a4c616d46fbcdfa3dfd1473c1337a1ec6f83"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/content_delete.json b/scm/driver/gitee/testdata/content_delete.json
new file mode 100644
index 000000000..3cfd6c802
--- /dev/null
+++ b/scm/driver/gitee/testdata/content_delete.json
@@ -0,0 +1,27 @@
+{
+  "content": null,
+  "commit": {
+    "sha": "bdce92fc3e96f54d7e918c7f2b6e61053366da5e",
+    "author": {
+      "name": "kit101",
+      "date": "2021-08-17T15:49:36+00:00",
+      "email": "kit101@gitee.com"
+    },
+    "committer": {
+      "name": "Gitee",
+      "date": "2021-08-17T15:49:36+00:00",
+      "email": "noreply@gitee.com"
+    },
+    "message": "delete commit message",
+    "tree": {
+      "sha": "4de6efba7063724cb20ab9135be309db0477c186",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/4de6efba7063724cb20ab9135be309db0477c186"
+    },
+    "parents": [
+      {
+        "sha": "56b7c68eea99df8469f819fca08ca71c2d942d8d",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/56b7c68eea99df8469f819fca08ca71c2d942d8d"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/content_list.json b/scm/driver/gitee/testdata/content_list.json
new file mode 100644
index 000000000..64274caaf
--- /dev/null
+++ b/scm/driver/gitee/testdata/content_list.json
@@ -0,0 +1,44 @@
+[
+  {
+    "type": "file",
+    "size": null,
+    "name": "CreateByDroneGiteeProvider.md",
+    "path": "apitest/CreateByDroneGiteeProvider.md",
+    "sha": "9de0cf94e1e3c1cbe0a25c3865de4cc9ede7ad3e",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/CreateByDroneGiteeProvider.md",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/CreateByDroneGiteeProvider.md",
+    "download_url": "https://gitee.com/kit101/drone-yml-test/raw/master/apitest/CreateByDroneGiteeProvider.md",
+    "_links": {
+      "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/CreateByDroneGiteeProvider.md",
+      "html": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/CreateByDroneGiteeProvider.md"
+    }
+  },
+  {
+    "type": "file",
+    "size": null,
+    "name": "UpdateByDroneGiteeProvider.md",
+    "path": "apitest/UpdateByDroneGiteeProvider.md",
+    "sha": "480a4b3865989bfa78d8f249b124716dda1581d7",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/UpdateByDroneGiteeProvider.md",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/UpdateByDroneGiteeProvider.md",
+    "download_url": "https://gitee.com/kit101/drone-yml-test/raw/master/apitest/UpdateByDroneGiteeProvider.md",
+    "_links": {
+      "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/UpdateByDroneGiteeProvider.md",
+      "html": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/UpdateByDroneGiteeProvider.md"
+    }
+  },
+  {
+    "type": "dir",
+    "size": null,
+    "name": "newdir",
+    "path": "apitest/newdir",
+    "sha": "2f5fb87ae0d8b8a5f78b4dbfefc1fd70b356bdc4",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/newdir",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/newdir",
+    "download_url": "https://gitee.com/kit101/drone-yml-test/raw/master/apitest/newdir",
+    "_links": {
+      "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/newdir",
+      "html": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/newdir"
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/content_list.json.golden b/scm/driver/gitee/testdata/content_list.json.golden
new file mode 100644
index 000000000..7fcd66370
--- /dev/null
+++ b/scm/driver/gitee/testdata/content_list.json.golden
@@ -0,0 +1,14 @@
+[
+  {
+    "kind": "file",
+    "path": "apitest/CreateByDroneGiteeProvider.md"
+  },
+  {
+    "kind": "file",
+    "path": "apitest/UpdateByDroneGiteeProvider.md"
+  },
+  {
+    "kind": "directory",
+    "path": "apitest/newdir"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/content_update.json b/scm/driver/gitee/testdata/content_update.json
new file mode 100644
index 000000000..9ddbbd6ed
--- /dev/null
+++ b/scm/driver/gitee/testdata/content_update.json
@@ -0,0 +1,40 @@
+{
+  "content": {
+    "name": "UpdateByDroneGiteeProvider.md",
+    "path": "apitest/UpdateByDroneGiteeProvider.md",
+    "size": 56,
+    "sha": "480a4b3865989bfa78d8f249b124716dda1581d7",
+    "type": "file",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/UpdateByDroneGiteeProvider.md",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/UpdateByDroneGiteeProvider.md",
+    "download_url": "https://gitee.com/kit101/drone-yml-test/raw/master/apitest/UpdateByDroneGiteeProvider.md",
+    "_links": {
+      "self": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contents/apitest/UpdateByDroneGiteeProvider.md",
+      "html": "https://gitee.com/kit101/drone-yml-test/blob/master/apitest/UpdateByDroneGiteeProvider.md"
+    }
+  },
+  "commit": {
+    "sha": "b280133ab20a219027584bd69671230503c916de",
+    "author": {
+      "name": "kit101",
+      "date": "2021-08-17T15:33:48+00:00",
+      "email": "kit101@gitee.com"
+    },
+    "committer": {
+      "name": "Gitee",
+      "date": "2021-08-17T15:33:48+00:00",
+      "email": "noreply@gitee.com"
+    },
+    "message": "my commit message by update",
+    "tree": {
+      "sha": "4de6efba7063724cb20ab9135be309db0477c186",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/git/trees/4de6efba7063724cb20ab9135be309db0477c186"
+    },
+    "parents": [
+      {
+        "sha": "f24c34e8d884b294c9f197460fc0840d9582220b",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/f24c34e8d884b294c9f197460fc0840d9582220b"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/error.json b/scm/driver/gitee/testdata/error.json
new file mode 100644
index 000000000..74392666a
--- /dev/null
+++ b/scm/driver/gitee/testdata/error.json
@@ -0,0 +1,3 @@
+{
+  "message": "404 Project Not Found"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/hook.json b/scm/driver/gitee/testdata/hook.json
new file mode 100644
index 000000000..b5c6184ac
--- /dev/null
+++ b/scm/driver/gitee/testdata/hook.json
@@ -0,0 +1,14 @@
+{
+  "id": 787341,
+  "url": "http://test.kit101.com/webhook",
+  "created_at": "2021-10-07T10:27:26+08:00",
+  "password": "123asdas123",
+  "project_id": 14836026,
+  "result": "\u003Chtml\u003E \u003Chead\u003E\u003Ctitle\u003E403 Forbidden\u003C/title\u003E\u003C/head\u003E \u003Cbody\u003E \u003Ccenter\u003E\u003Ch1\u003E403 Forbidden\u003C/h1\u003E\u003C/center\u003E \u003C/body\u003E \u003C/html\u003E",
+  "result_code": 403,
+  "push_events": true,
+  "tag_push_events": true,
+  "issues_events": true,
+  "note_events": true,
+  "merge_requests_events": true
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/hook.json.golden b/scm/driver/gitee/testdata/hook.json.golden
new file mode 100644
index 000000000..0deb616ec
--- /dev/null
+++ b/scm/driver/gitee/testdata/hook.json.golden
@@ -0,0 +1,14 @@
+{
+  "ID": "787341",
+  "Name": "",
+  "Target": "http://test.kit101.com/webhook",
+  "Events": [
+    "push",
+    "tag_push",
+    "issues",
+    "note",
+    "merge_requests"
+  ],
+  "Active": true,
+  "SkipVerify": true
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/hooks.json b/scm/driver/gitee/testdata/hooks.json
new file mode 100644
index 000000000..cd8fa4164
--- /dev/null
+++ b/scm/driver/gitee/testdata/hooks.json
@@ -0,0 +1,16 @@
+[
+  {
+    "id": 787341,
+    "url": "http://test.kit101.com/webhook",
+    "created_at": "2021-10-07T10:27:26+08:00",
+    "password": "123asdas123",
+    "project_id": 14836026,
+    "result": "\u003Chtml\u003E \u003Chead\u003E\u003Ctitle\u003E403 Forbidden\u003C/title\u003E\u003C/head\u003E \u003Cbody\u003E \u003Ccenter\u003E\u003Ch1\u003E403 Forbidden\u003C/h1\u003E\u003C/center\u003E \u003C/body\u003E \u003C/html\u003E",
+    "result_code": 403,
+    "push_events": true,
+    "tag_push_events": true,
+    "issues_events": true,
+    "note_events": true,
+    "merge_requests_events": true
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/hooks.json.golden b/scm/driver/gitee/testdata/hooks.json.golden
new file mode 100644
index 000000000..ce6a0d17c
--- /dev/null
+++ b/scm/driver/gitee/testdata/hooks.json.golden
@@ -0,0 +1,16 @@
+[
+  {
+    "ID": "787341",
+    "Name": "",
+    "Target": "http://test.kit101.com/webhook",
+    "Events": [
+      "push",
+      "tag_push",
+      "issues",
+      "note",
+      "merge_requests"
+    ],
+    "Active": true,
+    "SkipVerify": true
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/issue.json b/scm/driver/gitee/testdata/issue.json
new file mode 100644
index 000000000..a93370864
--- /dev/null
+++ b/scm/driver/gitee/testdata/issue.json
@@ -0,0 +1,235 @@
+{
+  "id": 7295389,
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CD5P",
+  "repository_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+  "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CD5P/labels",
+  "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CD5P/comments",
+  "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CD5P",
+  "parent_url": null,
+  "number": "I4CD5P",
+  "parent_id": 0,
+  "depth": 0,
+  "state": "open",
+  "title": "test issue 1",
+  "body": "test issue 1",
+  "user": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "labels": [
+    {
+      "id": 102145592,
+      "name": "feature",
+      "color": "B5CC18",
+      "repository_id": 14836026,
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels/feature",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "updated_at": "2021-09-29T09:55:42+08:00"
+    }
+  ],
+  "assignee": null,
+  "collaborators": [
+  ],
+  "repository": {
+    "id": 14836026,
+    "full_name": "kit101/drone-yml-test",
+    "human_name": "kit101/drone-yml-test",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+    "namespace": {
+      "id": 1576684,
+      "type": "personal",
+      "name": "kit101",
+      "path": "kit101",
+      "html_url": "https://gitee.com/kit101"
+    },
+    "path": "drone-yml-test",
+    "name": "drone-yml-test",
+    "owner": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "assigner": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "description": "",
+    "private": false,
+    "public": true,
+    "internal": false,
+    "fork": false,
+    "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "forks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/forks",
+    "keys_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/keys{/key_id}",
+    "collaborators_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/collaborators{/collaborator}",
+    "hooks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/hooks",
+    "branches_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches{/branch}",
+    "tags_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/tags",
+    "blobs_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/blobs{/sha}",
+    "stargazers_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/stargazers",
+    "contributors_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contributors",
+    "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits{/sha}",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/comments{/number}",
+    "issue_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/comments{/number}",
+    "issues_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues{/number}",
+    "pulls_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls{/number}",
+    "milestones_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/milestones{/number}",
+    "notifications_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/notifications{?since,all,participating}",
+    "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels{/name}",
+    "releases_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/releases{/id}",
+    "recommend": false,
+    "gvp": false,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "language": null,
+    "forks_count": 0,
+    "stargazers_count": 0,
+    "watchers_count": 1,
+    "default_branch": "master",
+    "open_issues_count": 1,
+    "has_issues": true,
+    "has_wiki": true,
+    "issue_comment": false,
+    "can_comment": true,
+    "pull_requests_enabled": true,
+    "has_page": false,
+    "license": null,
+    "outsourced": false,
+    "project_creator": "kit101",
+    "members": [
+      "kit101"
+    ],
+    "pushed_at": "2021-09-29T00:46:59+08:00",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "updated_at": "2021-09-29T09:21:28+08:00",
+    "parent": null,
+    "paas": null,
+    "assignees_number": 1,
+    "testers_number": 1,
+    "assignee": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "testers": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "status": "开始",
+    "empty_repo": false,
+    "programs": [
+    ],
+    "enterprise": null
+  },
+  "milestone": null,
+  "created_at": "2021-09-29T09:21:28+08:00",
+  "updated_at": "2021-09-29T09:55:42+08:00",
+  "plan_started_at": null,
+  "deadline": null,
+  "finished_at": null,
+  "scheduled_time": 0.0,
+  "comments": 0,
+  "priority": 0,
+  "issue_type": "任务",
+  "program": null,
+  "security_hole": false,
+  "issue_state": "待办的",
+  "branch": null,
+  "issue_type_detail": {
+    "id": 1,
+    "title": "任务",
+    "template": null,
+    "ident": "task",
+    "color": "#0086D6",
+    "is_system": true,
+    "created_at": "2017-09-01T03:09:12+08:00",
+    "updated_at": "2017-09-01T03:09:12+08:00"
+  },
+  "issue_state_detail": {
+    "id": 1,
+    "title": "待办的",
+    "color": "#8c92a4",
+    "icon": "icon-task-state-21",
+    "command": null,
+    "serial": 1,
+    "created_at": "2017-09-01T03:09:13+08:00",
+    "updated_at": "2017-09-01T03:09:13+08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/issue.json.golden b/scm/driver/gitee/testdata/issue.json.golden
new file mode 100644
index 000000000..6075529d9
--- /dev/null
+++ b/scm/driver/gitee/testdata/issue.json.golden
@@ -0,0 +1,16 @@
+{
+  "Number": 735267685380,
+  "Title": "test issue 1",
+  "Body": "test issue 1",
+  "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CD5P",
+  "Labels": [
+    "feature"
+  ],
+  "Closed": false,
+  "Author": {
+    "Login": "kit101",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+  },
+  "Created": "2021-09-29T09:21:28+08:00",
+  "Updated": "2021-09-29T09:55:42+08:00"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/issue_comment.json b/scm/driver/gitee/testdata/issue_comment.json
new file mode 100644
index 000000000..09e3a289a
--- /dev/null
+++ b/scm/driver/gitee/testdata/issue_comment.json
@@ -0,0 +1,34 @@
+{
+  "id": 6877445,
+  "body": "it's ok.",
+  "user": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "source": null,
+  "target": {
+    "issue": {
+      "id": 7295389,
+      "title": "test issue 1",
+      "number": "I4CD5P"
+    },
+    "pull_request": null
+  },
+  "created_at": "2021-09-29T11:00:50+08:00",
+  "updated_at": "2021-09-29T11:00:50+08:00"
+}
diff --git a/scm/driver/gitee/testdata/issue_comment.json.golden b/scm/driver/gitee/testdata/issue_comment.json.golden
new file mode 100644
index 000000000..283c558a7
--- /dev/null
+++ b/scm/driver/gitee/testdata/issue_comment.json.golden
@@ -0,0 +1,10 @@
+{
+  "ID": 6877445,
+  "Body": "it's ok.",
+  "Author": {
+    "Login": "kit101",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+  },
+  "Created": "2021-09-29T11:00:50+08:00",
+  "Updated": "2021-09-29T11:00:50+08:00"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/issue_comments.json b/scm/driver/gitee/testdata/issue_comments.json
new file mode 100644
index 000000000..c310a4348
--- /dev/null
+++ b/scm/driver/gitee/testdata/issue_comments.json
@@ -0,0 +1,36 @@
+[
+  {
+    "id": 6877445,
+    "body": "it's ok.",
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "source": null,
+    "target": {
+      "issue": {
+        "id": 7295389,
+        "title": "test issue 1",
+        "number": "I4CD5P"
+      },
+      "pull_request": null
+    },
+    "created_at": "2021-09-29T11:00:50+08:00",
+    "updated_at": "2021-09-29T11:00:50+08:00"
+  }
+]
diff --git a/scm/driver/gitee/testdata/issue_comments.json.golden b/scm/driver/gitee/testdata/issue_comments.json.golden
new file mode 100644
index 000000000..ea7200e52
--- /dev/null
+++ b/scm/driver/gitee/testdata/issue_comments.json.golden
@@ -0,0 +1,12 @@
+[
+  {
+    "ID": 6877445,
+    "Body": "it's ok.",
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-09-29T11:00:50+08:00",
+    "Updated": "2021-09-29T11:00:50+08:00"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/issues.json b/scm/driver/gitee/testdata/issues.json
new file mode 100644
index 000000000..ca238cbb4
--- /dev/null
+++ b/scm/driver/gitee/testdata/issues.json
@@ -0,0 +1,468 @@
+[
+  {
+    "id": 7296764,
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CE7W",
+    "repository_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+    "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CE7W/labels",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CE7W/comments",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CE7W",
+    "parent_url": null,
+    "number": "I4CE7W",
+    "parent_id": 0,
+    "depth": 0,
+    "state": "closed",
+    "title": "close issue test",
+    "body": "close issue test",
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "labels": [
+      {
+        "id": 102145594,
+        "name": "invalid",
+        "color": "1B1C1D",
+        "repository_id": 14836026,
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels/invalid",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "updated_at": "2021-09-29T11:05:30+08:00"
+      }
+    ],
+    "assignee": null,
+    "collaborators": [],
+    "repository": {
+      "id": 14836026,
+      "full_name": "kit101/drone-yml-test",
+      "human_name": "kit101/drone-yml-test",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+      "namespace": {
+        "id": 1576684,
+        "type": "personal",
+        "name": "kit101",
+        "path": "kit101",
+        "html_url": "https://gitee.com/kit101"
+      },
+      "path": "drone-yml-test",
+      "name": "drone-yml-test",
+      "owner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "assigner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "description": "",
+      "private": false,
+      "public": true,
+      "internal": false,
+      "fork": false,
+      "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "forks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/forks",
+      "keys_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/keys{/key_id}",
+      "collaborators_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/collaborators{/collaborator}",
+      "hooks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/hooks",
+      "branches_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches{/branch}",
+      "tags_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/tags",
+      "blobs_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/blobs{/sha}",
+      "stargazers_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/stargazers",
+      "contributors_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contributors",
+      "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits{/sha}",
+      "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/comments{/number}",
+      "issue_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/comments{/number}",
+      "issues_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues{/number}",
+      "pulls_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls{/number}",
+      "milestones_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/milestones{/number}",
+      "notifications_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/notifications{?since,all,participating}",
+      "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels{/name}",
+      "releases_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/releases{/id}",
+      "recommend": false,
+      "gvp": false,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "language": null,
+      "forks_count": 0,
+      "stargazers_count": 0,
+      "watchers_count": 1,
+      "default_branch": "master",
+      "open_issues_count": 1,
+      "has_issues": true,
+      "has_wiki": true,
+      "issue_comment": false,
+      "can_comment": true,
+      "pull_requests_enabled": true,
+      "has_page": false,
+      "license": null,
+      "outsourced": false,
+      "project_creator": "kit101",
+      "members": [
+        "kit101"
+      ],
+      "pushed_at": "2021-09-29T00:46:59+08:00",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "updated_at": "2021-09-29T11:05:49+08:00",
+      "parent": null,
+      "paas": null,
+      "assignees_number": 1,
+      "testers_number": 1,
+      "assignee": [
+        {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        }
+      ],
+      "testers": [
+        {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        }
+      ],
+      "status": "开始",
+      "empty_repo": false,
+      "programs": [],
+      "enterprise": null
+    },
+    "milestone": null,
+    "created_at": "2021-09-29T11:05:30+08:00",
+    "updated_at": "2021-09-29T11:05:49+08:00",
+    "plan_started_at": null,
+    "deadline": null,
+    "finished_at": "2021-09-29T11:05:49+08:00",
+    "scheduled_time": 0.0,
+    "comments": 0,
+    "priority": 0,
+    "issue_type": "任务",
+    "program": null,
+    "security_hole": false,
+    "issue_state": "已完成",
+    "branch": null,
+    "issue_type_detail": {
+      "id": 1,
+      "title": "任务",
+      "template": null,
+      "ident": "task",
+      "color": "#0086D6",
+      "is_system": true,
+      "created_at": "2017-09-01T03:09:12+08:00",
+      "updated_at": "2017-09-01T03:09:12+08:00"
+    },
+    "issue_state_detail": {
+      "id": 3,
+      "title": "已完成",
+      "color": "#4baf50",
+      "icon": "icon-task-state-24",
+      "command": null,
+      "serial": 3,
+      "created_at": "2017-09-01T03:09:13+08:00",
+      "updated_at": "2017-09-01T03:09:13+08:00"
+    }
+  },
+  {
+    "id": 7295389,
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CD5P",
+    "repository_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+    "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CD5P/labels",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/I4CD5P/comments",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CD5P",
+    "parent_url": null,
+    "number": "I4CD5P",
+    "parent_id": 0,
+    "depth": 0,
+    "state": "open",
+    "title": "test issue 1",
+    "body": "test issue 1",
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "labels": [
+      {
+        "id": 102145592,
+        "name": "feature",
+        "color": "B5CC18",
+        "repository_id": 14836026,
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels/feature",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "updated_at": "2021-09-29T09:55:42+08:00"
+      }
+    ],
+    "assignee": null,
+    "collaborators": [],
+    "repository": {
+      "id": 14836026,
+      "full_name": "kit101/drone-yml-test",
+      "human_name": "kit101/drone-yml-test",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+      "namespace": {
+        "id": 1576684,
+        "type": "personal",
+        "name": "kit101",
+        "path": "kit101",
+        "html_url": "https://gitee.com/kit101"
+      },
+      "path": "drone-yml-test",
+      "name": "drone-yml-test",
+      "owner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "assigner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "description": "",
+      "private": false,
+      "public": true,
+      "internal": false,
+      "fork": false,
+      "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "forks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/forks",
+      "keys_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/keys{/key_id}",
+      "collaborators_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/collaborators{/collaborator}",
+      "hooks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/hooks",
+      "branches_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches{/branch}",
+      "tags_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/tags",
+      "blobs_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/blobs{/sha}",
+      "stargazers_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/stargazers",
+      "contributors_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contributors",
+      "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits{/sha}",
+      "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/comments{/number}",
+      "issue_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/comments{/number}",
+      "issues_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues{/number}",
+      "pulls_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls{/number}",
+      "milestones_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/milestones{/number}",
+      "notifications_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/notifications{?since,all,participating}",
+      "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels{/name}",
+      "releases_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/releases{/id}",
+      "recommend": false,
+      "gvp": false,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "language": null,
+      "forks_count": 0,
+      "stargazers_count": 0,
+      "watchers_count": 1,
+      "default_branch": "master",
+      "open_issues_count": 1,
+      "has_issues": true,
+      "has_wiki": true,
+      "issue_comment": false,
+      "can_comment": true,
+      "pull_requests_enabled": true,
+      "has_page": false,
+      "license": null,
+      "outsourced": false,
+      "project_creator": "kit101",
+      "members": [
+        "kit101"
+      ],
+      "pushed_at": "2021-09-29T00:46:59+08:00",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "updated_at": "2021-09-29T11:05:49+08:00",
+      "parent": null,
+      "paas": null,
+      "assignees_number": 1,
+      "testers_number": 1,
+      "assignee": [
+        {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        }
+      ],
+      "testers": [
+        {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        }
+      ],
+      "status": "开始",
+      "empty_repo": false,
+      "programs": [],
+      "enterprise": null
+    },
+    "milestone": null,
+    "created_at": "2021-09-29T09:21:28+08:00",
+    "updated_at": "2021-09-29T11:00:50+08:00",
+    "plan_started_at": null,
+    "deadline": null,
+    "finished_at": null,
+    "scheduled_time": 0.0,
+    "comments": 1,
+    "priority": 0,
+    "issue_type": "任务",
+    "program": null,
+    "security_hole": false,
+    "issue_state": "待办的",
+    "branch": null,
+    "issue_type_detail": {
+      "id": 1,
+      "title": "任务",
+      "template": null,
+      "ident": "task",
+      "color": "#0086D6",
+      "is_system": true,
+      "created_at": "2017-09-01T03:09:12+08:00",
+      "updated_at": "2017-09-01T03:09:12+08:00"
+    },
+    "issue_state_detail": {
+      "id": 1,
+      "title": "待办的",
+      "color": "#8c92a4",
+      "icon": "icon-task-state-21",
+      "command": null,
+      "serial": 1,
+      "created_at": "2017-09-01T03:09:13+08:00",
+      "updated_at": "2017-09-01T03:09:13+08:00"
+    }
+  }
+]
diff --git a/scm/driver/gitee/testdata/issues.json.golden b/scm/driver/gitee/testdata/issues.json.golden
new file mode 100644
index 000000000..b9daeccaf
--- /dev/null
+++ b/scm/driver/gitee/testdata/issues.json.golden
@@ -0,0 +1,34 @@
+[
+  {
+    "Number": 735267695587,
+    "Title": "close issue test",
+    "Body": "close issue test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CE7W",
+    "Labels": [
+      "invalid"
+    ],
+    "Closed": true,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-09-29T11:05:30+08:00",
+    "Updated": "2021-09-29T11:05:49+08:00"
+  },
+  {
+    "Number": 735267685380,
+    "Title": "test issue 1",
+    "Body": "test issue 1",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CD5P",
+    "Labels": [
+      "feature"
+    ],
+    "Closed": false,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-09-29T09:21:28+08:00",
+    "Updated": "2021-09-29T11:00:50+08:00"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/membership.json b/scm/driver/gitee/testdata/membership.json
new file mode 100644
index 000000000..e6edce7dd
--- /dev/null
+++ b/scm/driver/gitee/testdata/membership.json
@@ -0,0 +1,28 @@
+{
+  "url": "https://gitee.com/api/v5/orgs/kit101-personal/memberships/kit101",
+  "active": true,
+  "remark": null,
+  "role": "admin",
+  "organization_url": "https://gitee.com/api/v5/orgs/kit101-personal",
+  "organization": {
+    "id": 8538153,
+    "login": "kit101-personal",
+    "name": "kit101-personal",
+    "url": "https://gitee.com/api/v5/orgs/kit101-personal",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/namespace/2846/8538153_kit101-personal_1632964073.png?is_link=true",
+    "repos_url": "https://gitee.com/api/v5/orgs/kit101-personal/repos",
+    "events_url": "https://gitee.com/api/v5/orgs/kit101-personal/events",
+    "members_url": "https://gitee.com/api/v5/orgs/kit101-personal/members{/member}",
+    "description": "",
+    "follow_count": 1
+  },
+  "user": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": ""
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/membership.json.golden b/scm/driver/gitee/testdata/membership.json.golden
new file mode 100644
index 000000000..96ec5685e
--- /dev/null
+++ b/scm/driver/gitee/testdata/membership.json.golden
@@ -0,0 +1,4 @@
+{
+  "Active": true,
+  "Role": 2
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/org.json b/scm/driver/gitee/testdata/org.json
new file mode 100644
index 000000000..30389776c
--- /dev/null
+++ b/scm/driver/gitee/testdata/org.json
@@ -0,0 +1,12 @@
+{
+  "id": 8538153,
+  "login": "kit101-personal",
+  "name": "kit101-personal",
+  "url": "https://gitee.com/api/v5/orgs/kit101-personal",
+  "avatar_url": "https://portrait.gitee.com/uploads/avatars/namespace/2846/8538153_kit101-personal_1632964073.png?is_link=true",
+  "repos_url": "https://gitee.com/api/v5/orgs/kit101-personal/repos",
+  "events_url": "https://gitee.com/api/v5/orgs/kit101-personal/events",
+  "members_url": "https://gitee.com/api/v5/orgs/kit101-personal/members{/member}",
+  "description": "",
+  "follow_count": 1
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/org.json.golden b/scm/driver/gitee/testdata/org.json.golden
new file mode 100644
index 000000000..0259356f0
--- /dev/null
+++ b/scm/driver/gitee/testdata/org.json.golden
@@ -0,0 +1,4 @@
+{
+  "Name": "kit101-personal",
+  "Avatar": "https://portrait.gitee.com/uploads/avatars/namespace/2846/8538153_kit101-personal_1632964073.png?is_link=true"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/orgs.json b/scm/driver/gitee/testdata/orgs.json
new file mode 100644
index 000000000..bd511847a
--- /dev/null
+++ b/scm/driver/gitee/testdata/orgs.json
@@ -0,0 +1,15 @@
+[
+  {
+    "id": 8538153,
+    "login": "kit101-personal",
+    "name": "kit101-personal",
+    "url": "https://gitee.com/api/v5/orgs/kit101-personal",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/namespace/2846/8538153_kit101-personal_1632964073.png?is_link=true",
+    "repos_url": "https://gitee.com/api/v5/orgs/kit101-personal/repos",
+    "events_url": "https://gitee.com/api/v5/orgs/kit101-personal/events",
+    "members_url": "https://gitee.com/api/v5/orgs/kit101-personal/members{/member}",
+    "description": "",
+    "follow_count": 1
+  }
+]
+  
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/orgs.json.golden b/scm/driver/gitee/testdata/orgs.json.golden
new file mode 100644
index 000000000..c0816e83d
--- /dev/null
+++ b/scm/driver/gitee/testdata/orgs.json.golden
@@ -0,0 +1,6 @@
+[
+  {
+    "Name": "kit101-personal",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/namespace/2846/8538153_kit101-personal_1632964073.png?is_link=true"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr.json b/scm/driver/gitee/testdata/pr.json
new file mode 100644
index 000000000..1cadc54a3
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr.json
@@ -0,0 +1,239 @@
+{
+  "id": 4730187,
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6",
+  "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/6",
+  "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/6.diff",
+  "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/6.patch",
+  "issue_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/issues",
+  "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/commits",
+  "review_comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/{/number}",
+  "review_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments",
+  "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/6/comments",
+  "number": 6,
+  "state": "open",
+  "title": "new-feature",
+  "body": "Please pull these awesome changes",
+  "assignees_number": 1,
+  "testers_number": 1,
+  "assignees": [],
+  "testers": [],
+  "milestone": null,
+  "labels": [],
+  "locked": false,
+  "created_at": "2021-09-30T16:38:19+08:00",
+  "updated_at": "2021-09-30T16:38:19+08:00",
+  "closed_at": null,
+  "merged_at": null,
+  "mergeable": true,
+  "can_merge_check": false,
+  "head": {
+    "label": "crud",
+    "ref": "crud",
+    "sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "repo": {
+      "id": 14836026,
+      "full_name": "kit101/drone-yml-test",
+      "human_name": "kit101/drone-yml-test",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+      "namespace": {
+        "id": 1576684,
+        "type": "personal",
+        "name": "kit101",
+        "path": "kit101",
+        "html_url": "https://gitee.com/kit101"
+      },
+      "path": "drone-yml-test",
+      "name": "drone-yml-test",
+      "owner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "assigner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "description": "",
+      "private": false,
+      "public": true,
+      "internal": false,
+      "fork": false,
+      "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+    }
+  },
+  "base": {
+    "label": "master",
+    "ref": "master",
+    "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "repo": {
+      "id": 14836026,
+      "full_name": "kit101/drone-yml-test",
+      "human_name": "kit101/drone-yml-test",
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+      "namespace": {
+        "id": 1576684,
+        "type": "personal",
+        "name": "kit101",
+        "path": "kit101",
+        "html_url": "https://gitee.com/kit101"
+      },
+      "path": "drone-yml-test",
+      "name": "drone-yml-test",
+      "owner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "assigner": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "description": "",
+      "private": false,
+      "public": true,
+      "internal": false,
+      "fork": false,
+      "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+    }
+  },
+  "_links": {
+    "self": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6"
+    },
+    "html": {
+      "href": "https://gitee.com/kit101/drone-yml-test/pulls/6"
+    },
+    "issue": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/issues"
+    },
+    "comments": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/comments"
+    },
+    "review_comments": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/comments"
+    },
+    "review_comment": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/6"
+    },
+    "commits": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/commits"
+    }
+  },
+  "user": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr.json.golden b/scm/driver/gitee/testdata/pr.json.golden
new file mode 100644
index 000000000..f5fc30fe1
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr.json.golden
@@ -0,0 +1,30 @@
+{
+  "Number": 6,
+  "Title": "new-feature",
+  "Body": "Please pull these awesome changes",
+  "Sha": "411db0d9e9072851383751c260dc1601840906e4",
+  "Ref": "refs/pull/6/head",
+  "Source": "crud",
+  "Target": "master",
+  "Fork": "kit101/drone-yml-test",
+  "Link": "https://gitee.com/kit101/drone-yml-test/pulls/6",
+  "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/6.diff",
+  "Closed": false,
+  "Merged": false,
+  "Base": {
+    "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+    "Path": "refs/heads/master",
+    "Name": "master"
+  },
+  "Head": {
+    "Sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "Path": "refs/heads/crud",
+    "Name": "crud"
+  },
+  "Author": {
+    "Login": "kit101",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+  },
+  "Created": "2021-09-30T16:38:19+08:00",
+  "Updated": "2021-09-30T16:38:19+08:00"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_comment.json b/scm/driver/gitee/testdata/pr_comment.json
new file mode 100644
index 000000000..ff5892789
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_comment.json
@@ -0,0 +1,44 @@
+{
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/6922557",
+  "id": 6922557,
+  "path": null,
+  "position": null,
+  "original_position": null,
+  "commit_id": null,
+  "original_commit_id": null,
+  "user": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "created_at": "2021-10-07T09:26:45+08:00",
+  "updated_at": "2021-10-07T09:26:45+08:00",
+  "body": "test comment 1",
+  "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/7#note_6922557",
+  "pull_request_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/7",
+  "_links": {
+    "self": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/6922557"
+    },
+    "html": {
+      "href": "https://gitee.com/kit101/drone-yml-test/pulls/7#note_6922557"
+    },
+    "pull_request": {
+      "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/7"
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_comment.json.golden b/scm/driver/gitee/testdata/pr_comment.json.golden
new file mode 100644
index 000000000..636bc0e05
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_comment.json.golden
@@ -0,0 +1,11 @@
+{
+  "ID": 6922557,
+  "Body": "test comment 1",
+  "Author": {
+    "Login": "kit101",
+    "Name": "kit101",
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+  },
+  "Created": "2021-10-07T09:26:45+08:00",
+  "Updated": "2021-10-07T09:26:45+08:00"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_comments.json b/scm/driver/gitee/testdata/pr_comments.json
new file mode 100644
index 000000000..02652c4f7
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_comments.json
@@ -0,0 +1,46 @@
+[
+  {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/6922557",
+    "id": 6922557,
+    "path": null,
+    "position": null,
+    "original_position": null,
+    "commit_id": null,
+    "original_commit_id": null,
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "created_at": "2021-10-07T09:26:45+08:00",
+    "updated_at": "2021-10-07T09:26:45+08:00",
+    "body": "test comment 1",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/7#note_6922557",
+    "pull_request_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/7",
+    "_links": {
+      "self": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/6922557"
+      },
+      "html": {
+        "href": "https://gitee.com/kit101/drone-yml-test/pulls/7#note_6922557"
+      },
+      "pull_request": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/7"
+      }
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_comments.json.golden b/scm/driver/gitee/testdata/pr_comments.json.golden
new file mode 100644
index 000000000..93743f276
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_comments.json.golden
@@ -0,0 +1,13 @@
+[
+  {
+    "ID": 6922557,
+    "Body": "test comment 1",
+    "Author": {
+      "Login": "kit101",
+      "Name": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-07T09:26:45+08:00",
+    "Updated": "2021-10-07T09:26:45+08:00"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_commits.json b/scm/driver/gitee/testdata/pr_commits.json
new file mode 100644
index 000000000..9c82717fe
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_commits.json
@@ -0,0 +1,65 @@
+[
+  {
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/00b76e8abd51ae6a96318b3450944b32995f9158",
+    "sha": "00b76e8abd51ae6a96318b3450944b32995f9158",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/commits/00b76e8abd51ae6a96318b3450944b32995f9158",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/00b76e8abd51ae6a96318b3450944b32995f9158/comments",
+    "commit": {
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/00b76e8abd51ae6a96318b3450944b32995f9158",
+      "author": {
+        "name": "kit101",
+        "email": "qkssk1711@163.com",
+        "date": "2021-09-30T18:09:57+08:00"
+      },
+      "committer": {
+        "name": "kit101",
+        "email": "qkssk1711@163.com",
+        "date": "2021-09-30T18:09:57+08:00"
+      },
+      "message": "add3\n",
+      "comment_count": 7
+    },
+    "author": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "committer": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "parents": {
+      "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits/00b76e8abd51ae6a96318b3450944b32995f9158",
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_commits.json.golden b/scm/driver/gitee/testdata/pr_commits.json.golden
new file mode 100644
index 000000000..b28ce6888
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_commits.json.golden
@@ -0,0 +1,21 @@
+[
+  {
+    "Message": "add3\n",
+    "Sha": "00b76e8abd51ae6a96318b3450944b32995f9158",
+    "Link": "https://gitee.com/kit101/drone-yml-test/commits/00b76e8abd51ae6a96318b3450944b32995f9158",
+    "Author": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-09-30T18:09:57+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Committer": {
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-09-30T18:09:57+08:00",
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_files.json b/scm/driver/gitee/testdata/pr_files.json
new file mode 100644
index 000000000..975b27f21
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_files.json
@@ -0,0 +1,82 @@
+[
+  {
+    "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+    "filename": "change/add.txt",
+    "status": "deleted",
+    "additions": "0",
+    "deletions": "1",
+    "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/2eac1cac02c325058cf959725c45b0612d3e8177/change/add.txt",
+    "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/2eac1cac02c325058cf959725c45b0612d3e8177/change/add.txt",
+    "patch": {
+      "diff": "@@ -1 +0,0 @@\n-add\n\\ No newline at end of file\n",
+      "new_path": "change/add.txt",
+      "old_path": "change/add.txt",
+      "a_mode": "100644",
+      "b_mode": "0",
+      "new_file": false,
+      "renamed_file": false,
+      "deleted_file": true,
+      "too_large": false
+    }
+  },
+  {
+    "sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "filename": "change/add2.txt",
+    "status": "added",
+    "additions": "4",
+    "deletions": "0",
+    "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/411db0d9e9072851383751c260dc1601840906e4/change/add2.txt",
+    "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/411db0d9e9072851383751c260dc1601840906e4/change/add2.txt",
+    "patch": {
+      "diff": "@@ -0,0 +1,4 @@\n+add 2\n+add 2\n+add 2\n+add 2\n\\ No newline at end of file\n",
+      "new_path": "change/add2.txt",
+      "old_path": "change/add2.txt",
+      "a_mode": "0",
+      "b_mode": "100644",
+      "new_file": true,
+      "renamed_file": false,
+      "deleted_file": false,
+      "too_large": false
+    }
+  },
+  {
+    "sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "filename": "change/modified.txt",
+    "status": null,
+    "additions": "6",
+    "deletions": "1",
+    "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/411db0d9e9072851383751c260dc1601840906e4/change/modified.txt",
+    "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/411db0d9e9072851383751c260dc1601840906e4/change/modified.txt",
+    "patch": {
+      "diff": "@@ -1,2 +1,7 @@\n will be modified\n-modified\n\\ No newline at end of file\n+modified\n+modified 2\n+modified 2\n+modified 2\n+modified 2\n+modified 2\n\\ No newline at end of file\n",
+      "new_path": "change/modified.txt",
+      "old_path": "change/modified.txt",
+      "a_mode": "100644",
+      "b_mode": "100644",
+      "new_file": false,
+      "renamed_file": false,
+      "deleted_file": false,
+      "too_large": false
+    }
+  },
+  {
+    "sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "filename": "change/rename.2.txt",
+    "status": "renamed",
+    "additions": "0",
+    "deletions": "0",
+    "blob_url": "https://gitee.com/kit101/drone-yml-test/blob/411db0d9e9072851383751c260dc1601840906e4/change/rename.2.txt",
+    "raw_url": "https://gitee.com/kit101/drone-yml-test/raw/411db0d9e9072851383751c260dc1601840906e4/change/rename.2.txt",
+    "patch": {
+      "diff": "",
+      "new_path": "change/rename.2.txt",
+      "old_path": "change/rename.1.txt",
+      "a_mode": "100644",
+      "b_mode": "100644",
+      "new_file": false,
+      "renamed_file": true,
+      "deleted_file": false,
+      "too_large": false
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pr_files.json.golden b/scm/driver/gitee/testdata/pr_files.json.golden
new file mode 100644
index 000000000..be4c1d330
--- /dev/null
+++ b/scm/driver/gitee/testdata/pr_files.json.golden
@@ -0,0 +1,30 @@
+[
+  {
+    "BlobID": "2eac1cac02c325058cf959725c45b0612d3e8177",
+    "Path": "change/add.txt",
+    "Added": false,
+    "Renamed": false,
+    "Deleted": true
+  },
+  {
+    "BlobID": "411db0d9e9072851383751c260dc1601840906e4",
+    "Path": "change/add2.txt",
+    "Added": true,
+    "Renamed": false,
+    "Deleted": false
+  },
+  {
+    "BlobID": "411db0d9e9072851383751c260dc1601840906e4",
+    "Path": "change/modified.txt",
+    "Added": false,
+    "Renamed": false,
+    "Deleted": false
+  },
+  {
+    "BlobID": "411db0d9e9072851383751c260dc1601840906e4",
+    "Path": "change/rename.2.txt",
+    "Added": false,
+    "Renamed": true,
+    "Deleted": false
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pulls.json b/scm/driver/gitee/testdata/pulls.json
new file mode 100644
index 000000000..8f5b93450
--- /dev/null
+++ b/scm/driver/gitee/testdata/pulls.json
@@ -0,0 +1,839 @@
+[
+  {
+    "id": 4730187,
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/6",
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/6.diff",
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/6.patch",
+    "issue_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/issues",
+    "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/commits",
+    "review_comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/{/number}",
+    "review_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/6/comments",
+    "number": 6,
+    "state": "open",
+    "title": "new-feature",
+    "body": "Please pull these awesome changes",
+    "assignees_number": 1,
+    "testers_number": 1,
+    "assignees": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "testers": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "milestone": null,
+    "labels": [],
+    "locked": false,
+    "created_at": "2021-09-30T16:38:19+08:00",
+    "updated_at": "2021-09-30T16:38:19+08:00",
+    "closed_at": null,
+    "merged_at": null,
+    "mergeable": true,
+    "can_merge_check": false,
+    "head": {
+      "label": "crud",
+      "ref": "crud",
+      "sha": "411db0d9e9072851383751c260dc1601840906e4",
+      "user": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "repo": {
+        "id": 14836026,
+        "full_name": "kit101/drone-yml-test",
+        "human_name": "kit101/drone-yml-test",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+        "namespace": {
+          "id": 1576684,
+          "type": "personal",
+          "name": "kit101",
+          "path": "kit101",
+          "html_url": "https://gitee.com/kit101"
+        },
+        "path": "drone-yml-test",
+        "name": "drone-yml-test",
+        "owner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "assigner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "description": "",
+        "private": false,
+        "public": true,
+        "internal": false,
+        "fork": false,
+        "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+      }
+    },
+    "base": {
+      "label": "master",
+      "ref": "master",
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "repo": {
+        "id": 14836026,
+        "full_name": "kit101/drone-yml-test",
+        "human_name": "kit101/drone-yml-test",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+        "namespace": {
+          "id": 1576684,
+          "type": "personal",
+          "name": "kit101",
+          "path": "kit101",
+          "html_url": "https://gitee.com/kit101"
+        },
+        "path": "drone-yml-test",
+        "name": "drone-yml-test",
+        "owner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "assigner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "description": "",
+        "private": false,
+        "public": true,
+        "internal": false,
+        "fork": false,
+        "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+      }
+    },
+    "_links": {
+      "self": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6"
+      },
+      "html": {
+        "href": "https://gitee.com/kit101/drone-yml-test/pulls/6"
+      },
+      "issue": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/issues"
+      },
+      "comments": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/comments"
+      },
+      "review_comments": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/comments"
+      },
+      "review_comment": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/6"
+      },
+      "commits": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/6/commits"
+      }
+    },
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    }
+  },
+  {
+    "id": 4730175,
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/5",
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/5.diff",
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/5.patch",
+    "issue_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5/issues",
+    "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5/commits",
+    "review_comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/{/number}",
+    "review_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/5/comments",
+    "number": 5,
+    "state": "closed",
+    "title": "new-feature",
+    "body": "Please pull these awesome changes",
+    "assignees_number": 1,
+    "testers_number": 1,
+    "assignees": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "testers": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "milestone": null,
+    "labels": [],
+    "locked": false,
+    "created_at": "2021-09-30T16:37:34+08:00",
+    "updated_at": "2021-09-30T16:38:14+08:00",
+    "closed_at": "2021-09-30T16:38:14+08:00",
+    "merged_at": null,
+    "mergeable": true,
+    "can_merge_check": false,
+    "head": {
+      "label": "crud",
+      "ref": "crud",
+      "sha": "411db0d9e9072851383751c260dc1601840906e4",
+      "user": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "repo": {
+        "id": 14836026,
+        "full_name": "kit101/drone-yml-test",
+        "human_name": "kit101/drone-yml-test",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+        "namespace": {
+          "id": 1576684,
+          "type": "personal",
+          "name": "kit101",
+          "path": "kit101",
+          "html_url": "https://gitee.com/kit101"
+        },
+        "path": "drone-yml-test",
+        "name": "drone-yml-test",
+        "owner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "assigner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "description": "",
+        "private": false,
+        "public": true,
+        "internal": false,
+        "fork": false,
+        "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+      }
+    },
+    "base": {
+      "label": "master",
+      "ref": "master",
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "repo": {
+        "id": 14836026,
+        "full_name": "kit101/drone-yml-test",
+        "human_name": "kit101/drone-yml-test",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+        "namespace": {
+          "id": 1576684,
+          "type": "personal",
+          "name": "kit101",
+          "path": "kit101",
+          "html_url": "https://gitee.com/kit101"
+        },
+        "path": "drone-yml-test",
+        "name": "drone-yml-test",
+        "owner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "assigner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "description": "",
+        "private": false,
+        "public": true,
+        "internal": false,
+        "fork": false,
+        "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+      }
+    },
+    "_links": {
+      "self": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5"
+      },
+      "html": {
+        "href": "https://gitee.com/kit101/drone-yml-test/pulls/5"
+      },
+      "issue": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5/issues"
+      },
+      "comments": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5/comments"
+      },
+      "review_comments": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5/comments"
+      },
+      "review_comment": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/5"
+      },
+      "commits": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/5/commits"
+      }
+    },
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    }
+  },
+  {
+    "id": 4730041,
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/4",
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/4.diff",
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/4.patch",
+    "issue_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4/issues",
+    "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4/commits",
+    "review_comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/{/number}",
+    "review_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/4/comments",
+    "number": 4,
+    "state": "open",
+    "title": "create crud pr by api",
+    "body": "create crud pr by api",
+    "assignees_number": 1,
+    "testers_number": 1,
+    "assignees": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "testers": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "milestone": null,
+    "labels": [],
+    "locked": false,
+    "created_at": "2021-09-30T16:28:09+08:00",
+    "updated_at": "2021-09-30T16:28:10+08:00",
+    "closed_at": null,
+    "merged_at": null,
+    "mergeable": true,
+    "can_merge_check": false,
+    "head": {
+      "label": "feat-1",
+      "ref": "feat-1",
+      "sha": "2700445cd84c08546f4d003f8aa54d2099a006b7",
+      "user": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "repo": {
+        "id": 14836026,
+        "full_name": "kit101/drone-yml-test",
+        "human_name": "kit101/drone-yml-test",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+        "namespace": {
+          "id": 1576684,
+          "type": "personal",
+          "name": "kit101",
+          "path": "kit101",
+          "html_url": "https://gitee.com/kit101"
+        },
+        "path": "drone-yml-test",
+        "name": "drone-yml-test",
+        "owner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "assigner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "description": "",
+        "private": false,
+        "public": true,
+        "internal": false,
+        "fork": false,
+        "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+      }
+    },
+    "base": {
+      "label": "master",
+      "ref": "master",
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      },
+      "repo": {
+        "id": 14836026,
+        "full_name": "kit101/drone-yml-test",
+        "human_name": "kit101/drone-yml-test",
+        "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+        "namespace": {
+          "id": 1576684,
+          "type": "personal",
+          "name": "kit101",
+          "path": "kit101",
+          "html_url": "https://gitee.com/kit101"
+        },
+        "path": "drone-yml-test",
+        "name": "drone-yml-test",
+        "owner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "assigner": {
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "url": "https://gitee.com/api/v5/users/kit101",
+          "html_url": "https://gitee.com/kit101",
+          "remark": "",
+          "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+          "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+          "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+          "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+          "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+          "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+          "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+          "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+          "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+          "type": "User"
+        },
+        "description": "",
+        "private": false,
+        "public": true,
+        "internal": false,
+        "fork": false,
+        "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git"
+      }
+    },
+    "_links": {
+      "self": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4"
+      },
+      "html": {
+        "href": "https://gitee.com/kit101/drone-yml-test/pulls/4"
+      },
+      "issue": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4/issues"
+      },
+      "comments": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4/comments"
+      },
+      "review_comments": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4/comments"
+      },
+      "review_comment": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/comments/4"
+      },
+      "commits": {
+        "href": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls/4/commits"
+      }
+    },
+    "user": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/pulls.json.golden b/scm/driver/gitee/testdata/pulls.json.golden
new file mode 100644
index 000000000..e0d127ef0
--- /dev/null
+++ b/scm/driver/gitee/testdata/pulls.json.golden
@@ -0,0 +1,92 @@
+[
+  {
+    "Number": 6,
+    "Title": "new-feature",
+    "Body": "Please pull these awesome changes",
+    "Sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "Ref": "refs/pull/6/head",
+    "Source": "crud",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/6",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/6.diff",
+    "Closed": false,
+    "Merged": false,
+    "Created": "2021-09-30T16:38:19+08:00",
+    "Updated": "2021-09-30T16:38:19+08:00",
+    "Head": {
+      "Sha": "411db0d9e9072851383751c260dc1601840906e4",
+      "Path": "refs/heads/crud",
+      "Name": "crud"
+    },
+    "Base": {
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "Path": "refs/heads/master",
+      "Name": "master"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    }
+  },
+  {
+    "Number": 5,
+    "Title": "new-feature",
+    "Body": "Please pull these awesome changes",
+    "Sha": "411db0d9e9072851383751c260dc1601840906e4",
+    "Ref": "refs/pull/5/head",
+    "Source": "crud",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/5",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/5.diff",
+    "Closed": true,
+    "Merged": false,
+    "Created": "2021-09-30T16:37:34+08:00",
+    "Updated": "2021-09-30T16:38:14+08:00",
+    "Head": {
+      "Sha": "411db0d9e9072851383751c260dc1601840906e4",
+      "Path": "refs/heads/crud",
+      "Name": "crud"
+    },
+    "Base": {
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "Path": "refs/heads/master",
+      "Name": "master"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    }
+  },
+  {
+    "Number": 4,
+    "Title": "create crud pr by api",
+    "Body": "create crud pr by api",
+    "Sha": "2700445cd84c08546f4d003f8aa54d2099a006b7",
+    "Ref": "refs/pull/4/head",
+    "Source": "feat-1",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/4",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/4.diff",
+    "Closed": false,
+    "Merged": false,
+    "Created": "2021-09-30T16:28:09+08:00",
+    "Updated": "2021-09-30T16:28:10+08:00",
+    "Head": {
+      "Sha": "2700445cd84c08546f4d003f8aa54d2099a006b7",
+      "Path": "refs/heads/feat-1",
+      "Name": "feat-1"
+    },
+    "Base": {
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "Path": "refs/heads/master",
+      "Name": "master"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/repo.json b/scm/driver/gitee/testdata/repo.json
new file mode 100644
index 000000000..b567618b5
--- /dev/null
+++ b/scm/driver/gitee/testdata/repo.json
@@ -0,0 +1,160 @@
+{
+  "id": 14836026,
+  "full_name": "kit101/drone-yml-test",
+  "human_name": "kit101/drone-yml-test",
+  "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+  "namespace": {
+    "id": 1576684,
+    "type": "personal",
+    "name": "kit101",
+    "path": "kit101",
+    "html_url": "https://gitee.com/kit101"
+  },
+  "path": "drone-yml-test",
+  "name": "drone-yml-test",
+  "owner": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "assigner": {
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "url": "https://gitee.com/api/v5/users/kit101",
+    "html_url": "https://gitee.com/kit101",
+    "remark": "",
+    "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+    "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+    "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+    "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+    "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+    "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+    "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+    "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+    "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+    "type": "User"
+  },
+  "description": "",
+  "private": false,
+  "public": true,
+  "internal": false,
+  "fork": false,
+  "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+  "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+  "forks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/forks",
+  "keys_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/keys{/key_id}",
+  "collaborators_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/collaborators{/collaborator}",
+  "hooks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/hooks",
+  "branches_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches{/branch}",
+  "tags_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/tags",
+  "blobs_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/blobs{/sha}",
+  "stargazers_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/stargazers",
+  "contributors_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contributors",
+  "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits{/sha}",
+  "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/comments{/number}",
+  "issue_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/comments{/number}",
+  "issues_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues{/number}",
+  "pulls_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls{/number}",
+  "milestones_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/milestones{/number}",
+  "notifications_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/notifications{?since,all,participating}",
+  "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels{/name}",
+  "releases_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/releases{/id}",
+  "recommend": false,
+  "gvp": false,
+  "homepage": "https://gitee.com/kit101/drone-yml-test",
+  "language": null,
+  "forks_count": 0,
+  "stargazers_count": 0,
+  "watchers_count": 1,
+  "default_branch": "master",
+  "open_issues_count": 2,
+  "has_issues": true,
+  "has_wiki": true,
+  "issue_comment": false,
+  "can_comment": true,
+  "pull_requests_enabled": true,
+  "has_page": false,
+  "license": null,
+  "outsourced": false,
+  "project_creator": "kit101",
+  "members": [
+    "kit101"
+  ],
+  "pushed_at": "2021-09-30T18:10:23+08:00",
+  "created_at": "2021-03-24T11:24:34+08:00",
+  "updated_at": "2021-09-30T18:10:27+08:00",
+  "parent": null,
+  "paas": null,
+  "stared": false,
+  "watched": true,
+  "permission": {
+    "pull": true,
+    "push": true,
+    "admin": true
+  },
+  "relation": "master",
+  "assignees_number": 1,
+  "testers_number": 1,
+  "assignee": [
+    {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    }
+  ],
+  "testers": [
+    {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    }
+  ],
+  "status": "开始",
+  "empty_repo": false,
+  "programs": [],
+  "enterprise": null
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/repo.json.golden b/scm/driver/gitee/testdata/repo.json.golden
new file mode 100644
index 000000000..fd7593d97
--- /dev/null
+++ b/scm/driver/gitee/testdata/repo.json.golden
@@ -0,0 +1,17 @@
+{
+  "ID": "14836026",
+  "Namespace": "kit101",
+  "Name": "drone-yml-test",
+  "Perm": {
+    "Pull": true,
+    "Push": true,
+    "Admin": true
+  },
+  "Branch": "master",
+  "Private": false,
+  "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+  "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+  "Link": "https://gitee.com/kit101/drone-yml-test.git",
+  "Created": "2021-03-24T11:24:34+08:00",
+  "Updated": "2021-09-30T18:10:27+08:00"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/repos.json b/scm/driver/gitee/testdata/repos.json
new file mode 100644
index 000000000..57b247b62
--- /dev/null
+++ b/scm/driver/gitee/testdata/repos.json
@@ -0,0 +1,162 @@
+[
+  {
+    "id": 14836026,
+    "full_name": "kit101/drone-yml-test",
+    "human_name": "kit101/drone-yml-test",
+    "url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test",
+    "namespace": {
+      "id": 1576684,
+      "type": "personal",
+      "name": "kit101",
+      "path": "kit101",
+      "html_url": "https://gitee.com/kit101"
+    },
+    "path": "drone-yml-test",
+    "name": "drone-yml-test",
+    "owner": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "assigner": {
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "url": "https://gitee.com/api/v5/users/kit101",
+      "html_url": "https://gitee.com/kit101",
+      "remark": "",
+      "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+      "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+      "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+      "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+      "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+      "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+      "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+      "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+      "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+      "type": "User"
+    },
+    "description": "",
+    "private": false,
+    "public": true,
+    "internal": false,
+    "fork": false,
+    "html_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "forks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/forks",
+    "keys_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/keys{/key_id}",
+    "collaborators_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/collaborators{/collaborator}",
+    "hooks_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/hooks",
+    "branches_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/branches{/branch}",
+    "tags_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/tags",
+    "blobs_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/blobs{/sha}",
+    "stargazers_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/stargazers",
+    "contributors_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/contributors",
+    "commits_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/commits{/sha}",
+    "comments_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/comments{/number}",
+    "issue_comment_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues/comments{/number}",
+    "issues_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/issues{/number}",
+    "pulls_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/pulls{/number}",
+    "milestones_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/milestones{/number}",
+    "notifications_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/notifications{?since,all,participating}",
+    "labels_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/labels{/name}",
+    "releases_url": "https://gitee.com/api/v5/repos/kit101/drone-yml-test/releases{/id}",
+    "recommend": false,
+    "gvp": false,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "language": null,
+    "forks_count": 0,
+    "stargazers_count": 0,
+    "watchers_count": 1,
+    "default_branch": "master",
+    "open_issues_count": 2,
+    "has_issues": true,
+    "has_wiki": true,
+    "issue_comment": false,
+    "can_comment": true,
+    "pull_requests_enabled": true,
+    "has_page": false,
+    "license": null,
+    "outsourced": false,
+    "project_creator": "kit101",
+    "members": [
+      "kit101"
+    ],
+    "pushed_at": "2021-09-30T18:10:23+08:00",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "updated_at": "2021-09-30T18:10:27+08:00",
+    "parent": null,
+    "paas": null,
+    "stared": false,
+    "watched": true,
+    "permission": {
+      "pull": true,
+      "push": true,
+      "admin": true
+    },
+    "relation": "master",
+    "assignees_number": 1,
+    "testers_number": 1,
+    "assignee": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "testers": [
+      {
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "url": "https://gitee.com/api/v5/users/kit101",
+        "html_url": "https://gitee.com/kit101",
+        "remark": "",
+        "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+        "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+        "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+        "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+        "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+        "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+        "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+        "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+        "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+        "type": "User"
+      }
+    ],
+    "status": "开始",
+    "empty_repo": false,
+    "programs": [],
+    "enterprise": null
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/repos.json.golden b/scm/driver/gitee/testdata/repos.json.golden
new file mode 100644
index 000000000..3437a1eb1
--- /dev/null
+++ b/scm/driver/gitee/testdata/repos.json.golden
@@ -0,0 +1,19 @@
+[
+  {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Perm": {
+      "Pull": true,
+      "Push": true,
+      "Admin": true
+    },
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test.git",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-09-30T18:10:27+08:00"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/tag.json b/scm/driver/gitee/testdata/tag.json
new file mode 100644
index 000000000..34d894a5b
--- /dev/null
+++ b/scm/driver/gitee/testdata/tag.json
@@ -0,0 +1,8 @@
+{
+  "name": "1.0",
+  "message": "1.0",
+  "commit": {
+    "sha": "5e7876efb3468ff679410b82a72f7c002382d41e",
+    "date": "2021-03-24T11:29:35+08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/tag.json.golden b/scm/driver/gitee/testdata/tag.json.golden
new file mode 100644
index 000000000..1e286b6e4
--- /dev/null
+++ b/scm/driver/gitee/testdata/tag.json.golden
@@ -0,0 +1,5 @@
+{
+  "name": "1.0",
+  "Path": "refs/tags/1.0",
+  "Sha": "5e7876efb3468ff679410b82a72f7c002382d41e"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/tags.json b/scm/driver/gitee/testdata/tags.json
new file mode 100644
index 000000000..dd2ef627c
--- /dev/null
+++ b/scm/driver/gitee/testdata/tags.json
@@ -0,0 +1,34 @@
+[
+  {
+    "name": "release-test",
+    "message": "",
+    "commit": {
+      "sha": "25b13a79d771314d0075302178eb18aef79e80db",
+      "date": "2021-08-17T16:01:03+00:00"
+    }
+  },
+  {
+    "name": "1.1",
+    "message": "1.1",
+    "commit": {
+      "sha": "5e7876efb3468ff679410b82a72f7c002382d41e",
+      "date": "2021-03-24T11:29:35+08:00"
+    }
+  },
+  {
+    "name": "1.0",
+    "message": "1.0",
+    "commit": {
+      "sha": "5e7876efb3468ff679410b82a72f7c002382d41e",
+      "date": "2021-03-24T11:29:35+08:00"
+    }
+  },
+  {
+    "name": "1.2",
+    "message": "",
+    "commit": {
+      "sha": "5e7876efb3468ff679410b82a72f7c002382d41e",
+      "date": "2021-03-24T11:29:35+08:00"
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/tags.json.golden b/scm/driver/gitee/testdata/tags.json.golden
new file mode 100644
index 000000000..1bcaa50c6
--- /dev/null
+++ b/scm/driver/gitee/testdata/tags.json.golden
@@ -0,0 +1,22 @@
+[
+  {
+    "Name": "release-test",
+    "Path": "refs/tags/release-test",
+    "Sha": "25b13a79d771314d0075302178eb18aef79e80db"
+  },
+  {
+    "name": "1.1",
+    "Path": "refs/tags/1.1",
+    "Sha": "5e7876efb3468ff679410b82a72f7c002382d41e"
+  },
+  {
+    "name": "1.0",
+    "Path": "refs/tags/1.0",
+    "Sha": "5e7876efb3468ff679410b82a72f7c002382d41e"
+  },
+  {
+    "name": "1.2",
+    "Path": "refs/tags/1.2",
+    "Sha": "5e7876efb3468ff679410b82a72f7c002382d41e"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/user.json b/scm/driver/gitee/testdata/user.json
new file mode 100644
index 000000000..b46072350
--- /dev/null
+++ b/scm/driver/gitee/testdata/user.json
@@ -0,0 +1,30 @@
+{
+  "id": 1535738,
+  "login": "kit101",
+  "name": "kit101",
+  "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+  "url": "https://gitee.com/api/v5/users/kit101",
+  "html_url": "https://gitee.com/kit101",
+  "followers_url": "https://gitee.com/api/v5/users/kit101/followers",
+  "following_url": "https://gitee.com/api/v5/users/kit101/following_url{/other_user}",
+  "gists_url": "https://gitee.com/api/v5/users/kit101/gists{/gist_id}",
+  "starred_url": "https://gitee.com/api/v5/users/kit101/starred{/owner}{/repo}",
+  "subscriptions_url": "https://gitee.com/api/v5/users/kit101/subscriptions",
+  "organizations_url": "https://gitee.com/api/v5/users/kit101/orgs",
+  "repos_url": "https://gitee.com/api/v5/users/kit101/repos",
+  "events_url": "https://gitee.com/api/v5/users/kit101/events{/privacy}",
+  "received_events_url": "https://gitee.com/api/v5/users/kit101/received_events",
+  "type": "User",
+  "blog": null,
+  "weibo": null,
+  "bio": "",
+  "public_repos": 6,
+  "public_gists": 0,
+  "followers": 2,
+  "following": 3,
+  "stared": 12,
+  "watched": 17,
+  "created_at": "2017-09-15T11:34:30+08:00",
+  "updated_at": "2021-08-06T16:34:48+08:00",
+  "email": "qkssk1711@163.com"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/user.json.golden b/scm/driver/gitee/testdata/user.json.golden
new file mode 100644
index 000000000..d0ddb0d43
--- /dev/null
+++ b/scm/driver/gitee/testdata/user.json.golden
@@ -0,0 +1,8 @@
+{
+  "Login": "kit101",
+  "Name": "kit101",
+  "Email": "qkssk1711@163.com",
+  "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+  "Created": "2017-09-15T11:34:30+08:00",
+  "Updated": "2021-08-06T16:34:48+08:00"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_assign.json b/scm/driver/gitee/testdata/webhooks/issue_hook_assign.json
new file mode 100644
index 000000000..913920c2d
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_assign.json
@@ -0,0 +1,223 @@
+{
+  "action": "assign",
+  "assignee": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "description": "test issue hook\nupdate 1",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "issue_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": "I4CYEO",
+  "issue": {
+    "assignee": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "body": "test issue hook\nupdate 1",
+    "collaborators": [
+    ],
+    "comments": 0,
+    "created_at": "2021-10-08T16:08:32+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "id": 7322928,
+    "labels": [
+      {
+        "color": "767676",
+        "id": 102145593,
+        "name": "duplicate"
+      }
+    ],
+    "milestone": null,
+    "number": "I4CYEO",
+    "state": "progressing",
+    "state_name": "进行中",
+    "title": "test issue hook",
+    "type_name": "任务",
+    "updated_at": "2021-10-08T16:12:33+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "milestone": null,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:01:12+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:12:29+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:01:12+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:12:29+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "kBmUwLrajnSpjC4uH+Qkb7LpuEulsXRYt8Rf2H4eXg4=",
+  "state": "progressing",
+  "target_user": null,
+  "timestamp": "1633680755274",
+  "title": "test issue hook",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+  "user": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_assign.json.golden b/scm/driver/gitee/testdata/webhooks/issue_hook_assign.json.golden
new file mode 100644
index 000000000..a16772286
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_assign.json.golden
@@ -0,0 +1,37 @@
+{
+  "Action": "updated",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T16:12:29+08:00"
+  },
+  "Issue": {
+    "Number": 735267896979,
+    "Title": "test issue hook",
+    "Body": "test issue hook\nupdate 1",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "Labels": [
+      "duplicate"
+    ],
+    "Closed": false,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T16:08:32+08:00",
+    "Updated": "2021-10-08T16:12:33+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_delete.json b/scm/driver/gitee/testdata/webhooks/issue_hook_delete.json
new file mode 100644
index 000000000..3b09b5aeb
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_delete.json
@@ -0,0 +1,235 @@
+{
+  "action": "delete",
+  "assignee": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "description": "test issue hook\nupdate 1",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "issue_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": "I4CYEO",
+  "issue": {
+    "assignee": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "body": "test issue hook\nupdate 1",
+    "collaborators": [
+    ],
+    "comments": 0,
+    "created_at": "2021-10-08T16:08:32+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "id": 7322928,
+    "labels": [
+      {
+        "color": "767676",
+        "id": 102145593,
+        "name": "duplicate"
+      }
+    ],
+    "milestone": {
+      "closed_issues": 0,
+      "created_at": "2021-09-30T11:23:19+08:00",
+      "description": "测试里程碑",
+      "due_on": "2021-10-29",
+      "html_url": "https://gitee.com/kit101/drone-yml-test/milestones/147062",
+      "id": 147062,
+      "number": 147062,
+      "open_issues": 0,
+      "state": "open",
+      "title": "beta1.0",
+      "updated_at": "2021-09-30T11:23:19+08:00"
+    },
+    "number": "I4CYEO",
+    "state": "progressing",
+    "state_name": "进行中",
+    "title": "test issue hook",
+    "type_name": "任务",
+    "updated_at": "2021-10-08T16:27:14+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "milestone": "beta1.0",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:16:28+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:27:14+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:16:28+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:27:14+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "9J18Ih1nI+TKqddSYd1ORCjVjBUQhVGqIZdX3Jqk32Q=",
+  "state": "progressing",
+  "target_user": null,
+  "timestamp": "1633681683513",
+  "title": "test issue hook",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+  "user": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_delete.json.golden b/scm/driver/gitee/testdata/webhooks/issue_hook_delete.json.golden
new file mode 100644
index 000000000..211795e90
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_delete.json.golden
@@ -0,0 +1,37 @@
+{
+  "Action": "closed",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T16:27:14+08:00"
+  },
+  "Issue": {
+    "Number": 735267896979,
+    "Title": "test issue hook",
+    "Body": "test issue hook\nupdate 1",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "Labels": [
+      "duplicate"
+    ],
+    "Closed": false,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T16:08:32+08:00",
+    "Updated": "2021-10-08T16:27:14+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_open.json b/scm/driver/gitee/testdata/webhooks/issue_hook_open.json
new file mode 100644
index 000000000..f436f4738
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_open.json
@@ -0,0 +1,192 @@
+{
+  "action": "open",
+  "assignee": null,
+  "description": "test issue hook",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "issue_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": "I4CYEO",
+  "issue": {
+    "assignee": null,
+    "body": "test issue hook",
+    "collaborators": [
+    ],
+    "comments": 0,
+    "created_at": "2021-10-08T16:08:32+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "id": 7322928,
+    "labels": [
+    ],
+    "milestone": null,
+    "number": "I4CYEO",
+    "state": "open",
+    "state_name": "待办的",
+    "title": "test issue hook",
+    "type_name": "任务",
+    "updated_at": "2021-10-08T16:08:32+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "milestone": null,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 3,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:01:12+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:08:32+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 3,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:01:12+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:08:32+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "8v2uENuUH9QeZVl3olFEA3aPE/5fECC6RkmmsJV62DQ=",
+  "state": "open",
+  "target_user": null,
+  "timestamp": "1633680515785",
+  "title": "test issue hook",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+  "user": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_open.json.golden b/scm/driver/gitee/testdata/webhooks/issue_hook_open.json.golden
new file mode 100644
index 000000000..a87054c90
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_open.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "opened",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T16:08:32+08:00"
+  },
+  "Issue": {
+    "Number": 735267896979,
+    "Title": "test issue hook",
+    "Body": "test issue hook",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "Closed": false,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T16:08:32+08:00",
+    "Updated": "2021-10-08T16:08:32+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_state_change.json b/scm/driver/gitee/testdata/webhooks/issue_hook_state_change.json
new file mode 100644
index 000000000..0b6c9c8a4
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_state_change.json
@@ -0,0 +1,235 @@
+{
+  "action": "state_change",
+  "assignee": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "description": "test issue hook\nupdate 1",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "issue_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": "I4CYEO",
+  "issue": {
+    "assignee": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "body": "test issue hook\nupdate 1",
+    "collaborators": [
+    ],
+    "comments": 0,
+    "created_at": "2021-10-08T16:08:32+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "id": 7322928,
+    "labels": [
+      {
+        "color": "767676",
+        "id": 102145593,
+        "name": "duplicate"
+      }
+    ],
+    "milestone": {
+      "closed_issues": 0,
+      "created_at": "2021-09-30T11:23:19+08:00",
+      "description": "测试里程碑",
+      "due_on": "2021-10-29",
+      "html_url": "https://gitee.com/kit101/drone-yml-test/milestones/147062",
+      "id": 147062,
+      "number": 147062,
+      "open_issues": 0,
+      "state": "open",
+      "title": "beta1.0",
+      "updated_at": "2021-09-30T11:23:19+08:00"
+    },
+    "number": "I4CYEO",
+    "state": "progressing",
+    "state_name": "进行中",
+    "title": "test issue hook",
+    "type_name": "任务",
+    "updated_at": "2021-10-08T16:27:14+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "milestone": "beta1.0",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:16:28+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:27:14+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:16:28+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:27:14+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "HdIghS5Ua3zzLgqI4LxxQz313V08QxrR3+0YITiGf1s=",
+  "state": "progressing",
+  "target_user": null,
+  "timestamp": "1633681635600",
+  "title": "test issue hook",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+  "user": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/issue_hook_state_change.json.golden b/scm/driver/gitee/testdata/webhooks/issue_hook_state_change.json.golden
new file mode 100644
index 000000000..211c790b4
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/issue_hook_state_change.json.golden
@@ -0,0 +1,37 @@
+{
+  "Action": "opened",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T16:27:14+08:00"
+  },
+  "Issue": {
+    "Number": 735267896979,
+    "Title": "test issue hook",
+    "Body": "test issue hook\nupdate 1",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CYEO",
+    "Labels": [
+      "duplicate"
+    ],
+    "Closed": false,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T16:08:32+08:00",
+    "Updated": "2021-10-08T16:27:14+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/note_hook_issue_comment.json b/scm/driver/gitee/testdata/webhooks/note_hook_issue_comment.json
new file mode 100644
index 000000000..3b0f78cea
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/note_hook_issue_comment.json
@@ -0,0 +1,217 @@
+{
+  "action": "comment",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "comment": {
+    "body": "test issue comment hook\r\n\r\n",
+    "created_at": "2021-10-08T15:45:08+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CF12#note_6933558",
+    "id": 6933558,
+    "updated_at": "2021-10-08T15:45:08+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "note_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "issue": {
+    "assignee": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "body": "create by api body",
+    "collaborators": [
+    ],
+    "comments": 2,
+    "created_at": "2021-09-29T13:33:00+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/issues/I4CF12",
+    "id": 7297814,
+    "labels": [
+      {
+        "color": "DB2828",
+        "id": 102145590,
+        "name": "bug"
+      }
+    ],
+    "milestone": null,
+    "number": "I4CF12",
+    "state": "open",
+    "state_name": "待办的",
+    "title": "create by api",
+    "type_name": "任务",
+    "updated_at": "2021-10-08T15:45:08+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "note": "test issue comment hook\r\n\r\n",
+  "noteable_id": 7297814,
+  "noteable_type": "Issue",
+  "password": "",
+  "per_iid": "#I4CF12",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T13:26:59+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T13:26:59+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": null,
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T13:26:59+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T13:26:59+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "short_commit_id": null,
+  "sign": "Zzv7VMOnJ5SKrQhcEsL4XaC1zR1Kl1qMuxS0HbHwpBI=",
+  "timestamp": "1633679110217",
+  "title": "create by api",
+  "url": "https://gitee.com/kit101/drone-yml-test/issues/I4CF12#note_6933558"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/note_hook_issue_comment.json.golden b/scm/driver/gitee/testdata/webhooks/note_hook_issue_comment.json.golden
new file mode 100644
index 000000000..53845e677
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/note_hook_issue_comment.json.golden
@@ -0,0 +1,49 @@
+{
+  "Action": "created",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T13:26:59+08:00"
+  },
+  "Issue": {
+    "Number": 735267704950,
+    "Title": "create by api",
+    "Body": "create by api body",
+    "Link": "https://gitee.com/kit101/drone-yml-test/issues/I4CF12",
+    "Labels": [
+      "bug"
+    ],
+    "Closed": false,
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-09-29T13:33:00+08:00",
+    "Updated": "2021-10-08T15:45:08+08:00"
+  },
+  "Comment": {
+    "ID": 6933558,
+    "Body": "test issue comment hook\r\n\r\n",
+    "Author": {
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "Email": "qkssk1711@163.com",
+      "Login": "kit101",
+      "Name": "kit101"
+    },
+    "Created": "2021-10-08T15:45:08+08:00",
+    "Updated": "2021-10-08T15:45:08+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
diff --git a/scm/driver/gitee/testdata/webhooks/note_hook_pr_comment.json b/scm/driver/gitee/testdata/webhooks/note_hook_pr_comment.json
new file mode 100644
index 000000000..7d871105b
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/note_hook_pr_comment.json
@@ -0,0 +1,394 @@
+{
+  "action": "comment",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "comment": {
+    "body": "comment",
+    "created_at": "2021-10-08T19:13:20+08:00",
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/7#note_6937563",
+    "id": 6937563,
+    "updated_at": "2021-10-08T19:13:20+08:00",
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "note_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "issue": null,
+  "note": "comment",
+  "noteable_id": 4731382,
+  "noteable_type": "PullRequest",
+  "password": "",
+  "per_iid": "!7",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:54:02+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:54:02+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 11,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:54:02+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:54:02+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "",
+    "changed_files": 2,
+    "closed_at": null,
+    "comments": 11,
+    "commits": 2,
+    "created_at": "2021-09-30T18:10:27+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/7.diff",
+    "head": {
+      "label": "kit101:feat-2",
+      "ref": "feat-2",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:54:02+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:54:02+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/7",
+    "id": 4731382,
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "d10df103c351de96ceb9e62e6e4097f0e3420b72",
+    "merge_reference_name": "refs/pull/7/MERGE",
+    "merge_status": "can_be_resolve",
+    "mergeable": false,
+    "merged": false,
+    "merged_at": null,
+    "milestone": null,
+    "need_review": true,
+    "need_test": true,
+    "number": 7,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/7.patch",
+    "state": "open",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat add 3",
+    "updated_at": "2021-10-08T19:13:20+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:54:02+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:54:02+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "short_commit_id": null,
+  "sign": "wi4uRDdmlbPvs3DMQsm88rV0rzx11rLvuo0AOJOH28s=",
+  "timestamp": "1633691608022",
+  "title": "feat add 3",
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/7#note_6937563"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/note_hook_pr_comment.json.golden b/scm/driver/gitee/testdata/webhooks/note_hook_pr_comment.json.golden
new file mode 100644
index 000000000..c91349ba4
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/note_hook_pr_comment.json.golden
@@ -0,0 +1,63 @@
+{
+  "Action": "created",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:54:02+08:00"
+  },
+  "PullRequest": {
+    "Number": 7,
+    "Title": "feat add 3",
+    "Body": "",
+    "Sha": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+    "Ref": "refs/pull/7/head",
+    "Source": "feat-2",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/7",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/7.diff",
+    "Closed": false,
+    "Merged": false,
+    "Head": {
+      "Name": "feat-2",
+      "Path": "refs/heads/feat-2",
+      "Sha": "6168d9dae737b47f00c59fafca10c913a6850c3a"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-09-30T18:10:27+08:00",
+    "Updated": "2021-10-08T19:13:20+08:00"
+  },
+  "Comment": {
+    "ID": 6937563,
+    "Body": "comment",
+    "Author": {
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "Email": "qkssk1711@163.com",
+      "Login": "kit101",
+      "Name": "kit101"
+    },
+    "Created": "2021-10-08T19:13:20+08:00",
+    "Updated": "2021-10-08T19:13:20+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_close.json b/scm/driver/gitee/testdata/webhooks/pr_close.json
new file mode 100644
index 000000000..d5ad5c9a0
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_close.json
@@ -0,0 +1,601 @@
+{
+  "action": "close",
+  "action_desc": "open",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "body": "feat-3 1",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "merge_request_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": 8,
+  "languages": [
+    "Python"
+  ],
+  "merge_commit_sha": "cd09abc1995aede07da6524a0ace587698d74bbc",
+  "merge_status": "can_be_merged",
+  "number": 8,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:28:43+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 1,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:28:43+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "feat-3 1",
+    "changed_files": 1,
+    "closed_at": "2021-10-08T17:28:43+08:00",
+    "comments": 0,
+    "commits": 1,
+    "created_at": "2021-10-08T17:16:06+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/8.diff",
+    "head": {
+      "label": "kit101:feat-3",
+      "ref": "feat-3",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:28:43+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/8",
+    "id": 4744094,
+    "labels": [
+      {
+        "color": "D7C38B",
+        "id": 127956024,
+        "name": "test"
+      }
+    ],
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "cd09abc1995aede07da6524a0ace587698d74bbc",
+    "merge_reference_name": "refs/pull/8/MERGE",
+    "merge_status": "can_be_merged",
+    "mergeable": true,
+    "merged": false,
+    "merged_at": null,
+    "milestone": null,
+    "need_review": false,
+    "need_test": false,
+    "number": 8,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/8.patch",
+    "state": "closed",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat-3 1",
+    "updated_at": "2021-10-08T17:28:43+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:28:43+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "yuLVNqOZKWnXuYcQ8KbOm1UYgvd9KeiCHX5QsEyrrz8=",
+  "source_branch": "feat-3",
+  "source_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:28:43+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:28:43+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "state": "closed",
+  "target_branch": "master",
+  "target_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:28:43+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:28:43+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "target_user": null,
+  "timestamp": "1633685324936",
+  "title": "feat-3 1",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/8"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_close.json.golden b/scm/driver/gitee/testdata/webhooks/pr_close.json.golden
new file mode 100644
index 000000000..2c71509f2
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_close.json.golden
@@ -0,0 +1,57 @@
+{
+  "Action": "closed",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:28:43+08:00"
+  },
+  "PullRequest": {
+    "Number": 8,
+    "Title": "feat-3 1",
+    "Body": "feat-3 1",
+    "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+    "Ref": "refs/pull/8/head",
+    "Source": "feat-3",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/8",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/8.diff",
+    "Closed": true,
+    "Merged": false,
+    "Head": {
+      "Name": "feat-3",
+      "Path": "refs/heads/feat-3",
+      "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T17:16:06+08:00",
+    "Updated": "2021-10-08T17:28:43+08:00",
+    "Labels": [
+      {
+        "Name": "test",
+        "Color": "D7C38B"
+      }
+    ]
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_labeled.json b/scm/driver/gitee/testdata/webhooks/pr_labeled.json
new file mode 100644
index 000000000..bc682cc3f
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_labeled.json
@@ -0,0 +1,601 @@
+{
+  "action": "update",
+  "action_desc": "update_label",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "body": "feat-3 1",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "merge_request_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": 8,
+  "languages": [
+    "Python"
+  ],
+  "merge_commit_sha": "cd09abc1995aede07da6524a0ace587698d74bbc",
+  "merge_status": "can_be_merged",
+  "number": 8,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:16:06+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 1,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:16:06+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "feat-3 1",
+    "changed_files": 1,
+    "closed_at": null,
+    "comments": 0,
+    "commits": 1,
+    "created_at": "2021-10-08T17:16:06+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/8.diff",
+    "head": {
+      "label": "kit101:feat-3",
+      "ref": "feat-3",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:16:06+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/8",
+    "id": 4744094,
+    "labels": [
+      {
+        "color": "00B5AD",
+        "id": 102145595,
+        "name": "question"
+      }
+    ],
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "cd09abc1995aede07da6524a0ace587698d74bbc",
+    "merge_reference_name": "refs/pull/8/MERGE",
+    "merge_status": "can_be_merged",
+    "mergeable": true,
+    "merged": false,
+    "merged_at": null,
+    "milestone": null,
+    "need_review": true,
+    "need_test": true,
+    "number": 8,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/8.patch",
+    "state": "open",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat-3 1",
+    "updated_at": "2021-10-08T17:19:39+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:16:06+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "Fid06lsV72m4QMmmwMAPqTwYLLwFYNgjz32pUeAIaTE=",
+  "source_branch": "feat-3",
+  "source_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "state": "open",
+  "target_branch": "master",
+  "target_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "target_user": null,
+  "timestamp": "1633684782533",
+  "title": "feat-3 1",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/8"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_labeled.json.golden b/scm/driver/gitee/testdata/webhooks/pr_labeled.json.golden
new file mode 100644
index 000000000..1aaa28281
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_labeled.json.golden
@@ -0,0 +1,57 @@
+{
+  "Action": "labeled",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:16:06+08:00"
+  },
+  "PullRequest": {
+    "Number": 8,
+    "Title": "feat-3 1",
+    "Body": "feat-3 1",
+    "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+    "Ref": "refs/pull/8/head",
+    "Source": "feat-3",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/8",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/8.diff",
+    "Closed": false,
+    "Merged": false,
+    "Head": {
+      "Name": "feat-3",
+      "Path": "refs/heads/feat-3",
+      "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T17:16:06+08:00",
+    "Updated": "2021-10-08T17:19:39+08:00",
+    "Labels": [
+      {
+        "Name": "question",
+        "Color": "00B5AD"
+      }
+    ]
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_merge.json b/scm/driver/gitee/testdata/webhooks/pr_merge.json
new file mode 100644
index 000000000..9b94fc405
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_merge.json
@@ -0,0 +1,596 @@
+{
+  "action": "merge",
+  "action_desc": "open",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "body": "feat-3",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "merge_request_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": 9,
+  "languages": [
+    "Python"
+  ],
+  "merge_commit_sha": "6c363013dfccedad4c68c88e8649f4ffacc644ea",
+  "merge_status": "can_be_merged",
+  "number": 9,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:32:30+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:32:30+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 1,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:32:30+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:32:30+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "feat-3",
+    "changed_files": 1,
+    "closed_at": "2021-10-08T17:32:30+08:00",
+    "comments": 0,
+    "commits": 1,
+    "created_at": "2021-10-08T17:29:49+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/9.diff",
+    "head": {
+      "label": "kit101:feat-3",
+      "ref": "feat-3",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:32:30+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:32:30+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/9",
+    "id": 4744278,
+    "labels": [
+    ],
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "6c363013dfccedad4c68c88e8649f4ffacc644ea",
+    "merge_reference_name": "refs/pull/9/MERGE",
+    "merge_status": "can_be_merged",
+    "mergeable": true,
+    "merged": true,
+    "merged_at": "2021-10-08T17:32:30+08:00",
+    "milestone": null,
+    "need_review": false,
+    "need_test": false,
+    "number": 9,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/9.patch",
+    "state": "merged",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat-3",
+    "updated_at": "2021-10-08T17:32:29+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:32:30+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:32:30+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "SPeRZ3+TLzetOj+eEdAuMt8zVVbs6o+k5xrCPf5WGDI=",
+  "source_branch": "feat-3",
+  "source_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:32:30+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:32:30+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:32:30+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:32:30+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "state": "merged",
+  "target_branch": "master",
+  "target_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:32:30+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:32:30+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:32:30+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:32:30+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "target_user": null,
+  "timestamp": "1633685551222",
+  "title": "feat-3",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/9"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_merge.json.golden b/scm/driver/gitee/testdata/webhooks/pr_merge.json.golden
new file mode 100644
index 000000000..54e89f254
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_merge.json.golden
@@ -0,0 +1,51 @@
+{
+  "Action": "merged",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:32:30+08:00"
+  },
+  "PullRequest": {
+    "Number": 9,
+    "Title": "feat-3",
+    "Body": "feat-3",
+    "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+    "Ref": "refs/pull/9/head",
+    "Source": "feat-3",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/9",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/9.diff",
+    "Closed": true,
+    "Merged": true,
+    "Head": {
+      "Name": "feat-3",
+      "Path": "refs/heads/feat-3",
+      "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T17:29:49+08:00",
+    "Updated": "2021-10-08T17:32:29+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_open.json b/scm/driver/gitee/testdata/webhooks/pr_open.json
new file mode 100644
index 000000000..62b9bd0cb
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_open.json
@@ -0,0 +1,596 @@
+{
+  "action": "open",
+  "action_desc": "open",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "body": "feat-3",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "merge_request_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": 9,
+  "languages": [
+    "Python"
+  ],
+  "merge_commit_sha": "3fbdff5796f6d57ed9f5a5dd48e5d0cad7f9c0a1",
+  "merge_status": "can_be_merged",
+  "number": 9,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:29:49+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 1,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:29:49+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "feat-3",
+    "changed_files": 1,
+    "closed_at": null,
+    "comments": 0,
+    "commits": 1,
+    "created_at": "2021-10-08T17:29:49+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/9.diff",
+    "head": {
+      "label": "kit101:feat-3",
+      "ref": "feat-3",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:29:49+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/9",
+    "id": 4744278,
+    "labels": [
+    ],
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "3fbdff5796f6d57ed9f5a5dd48e5d0cad7f9c0a1",
+    "merge_reference_name": "refs/pull/9/MERGE",
+    "merge_status": "can_be_merged",
+    "mergeable": true,
+    "merged": false,
+    "merged_at": null,
+    "milestone": null,
+    "need_review": true,
+    "need_test": true,
+    "number": 9,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/9.patch",
+    "state": "open",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat-3",
+    "updated_at": "2021-10-08T17:29:50+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:29:49+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "o5L+cMcrXR+F8rVIRW+v2J/jTd9N8ZmyM3cXBte12Nc=",
+  "source_branch": "feat-3",
+  "source_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:29:49+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:29:49+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "state": "open",
+  "target_branch": "master",
+  "target_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:29:49+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:29:49+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "target_user": null,
+  "timestamp": "1633685391315",
+  "title": "feat-3",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/9"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_open.json.golden b/scm/driver/gitee/testdata/webhooks/pr_open.json.golden
new file mode 100644
index 000000000..6372fcc91
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_open.json.golden
@@ -0,0 +1,51 @@
+{
+  "Action": "opened",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:29:49+08:00"
+  },
+  "PullRequest": {
+    "Number": 9,
+    "Title": "feat-3",
+    "Body": "feat-3",
+    "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+    "Ref": "refs/pull/9/head",
+    "Source": "feat-3",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/9",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/9.diff",
+    "Closed": false,
+    "Merged": false,
+    "Head": {
+      "Name": "feat-3",
+      "Path": "refs/heads/feat-3",
+      "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T17:29:49+08:00",
+    "Updated": "2021-10-08T17:29:50+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_unlabeled.json b/scm/driver/gitee/testdata/webhooks/pr_unlabeled.json
new file mode 100644
index 000000000..2e317e8bb
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_unlabeled.json
@@ -0,0 +1,596 @@
+{
+  "action": "update",
+  "action_desc": "update_label",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "body": "feat-3 1",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "merge_request_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": 8,
+  "languages": [
+    "Python"
+  ],
+  "merge_commit_sha": "cd09abc1995aede07da6524a0ace587698d74bbc",
+  "merge_status": "can_be_merged",
+  "number": 8,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:16:06+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 1,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:16:06+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "feat-3 1",
+    "changed_files": 1,
+    "closed_at": null,
+    "comments": 0,
+    "commits": 1,
+    "created_at": "2021-10-08T17:16:06+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/8.diff",
+    "head": {
+      "label": "kit101:feat-3",
+      "ref": "feat-3",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:15:39+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:16:06+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/8",
+    "id": 4744094,
+    "labels": [
+    ],
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "cd09abc1995aede07da6524a0ace587698d74bbc",
+    "merge_reference_name": "refs/pull/8/MERGE",
+    "merge_status": "can_be_merged",
+    "mergeable": true,
+    "merged": false,
+    "merged_at": null,
+    "milestone": null,
+    "need_review": true,
+    "need_test": true,
+    "number": 8,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/8.patch",
+    "state": "open",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat-3 1",
+    "updated_at": "2021-10-08T17:23:09+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:15:39+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:16:06+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "X52dWAExMGFaN1+5a/RAuVtPmP5R0GL3AE4Br/MngRg=",
+  "source_branch": "feat-3",
+  "source_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "state": "open",
+  "target_branch": "master",
+  "target_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:15:39+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:16:06+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "target_user": null,
+  "timestamp": "1633684991291",
+  "title": "feat-3 1",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/8"
+}
diff --git a/scm/driver/gitee/testdata/webhooks/pr_unlabeled.json.golden b/scm/driver/gitee/testdata/webhooks/pr_unlabeled.json.golden
new file mode 100644
index 000000000..4b66f143c
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_unlabeled.json.golden
@@ -0,0 +1,51 @@
+{
+  "Action": "unlabeled",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:16:06+08:00"
+  },
+  "PullRequest": {
+    "Number": 8,
+    "Title": "feat-3 1",
+    "Body": "feat-3 1",
+    "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+    "Ref": "refs/pull/8/head",
+    "Source": "feat-3",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/8",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/8.diff",
+    "Closed": false,
+    "Merged": false,
+    "Head": {
+      "Name": "feat-3",
+      "Path": "refs/heads/feat-3",
+      "Sha": "dc8fa2ebe050d63f639c5b834311e96bc2303523"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-10-08T17:16:06+08:00",
+    "Updated": "2021-10-08T17:23:09+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_update.json b/scm/driver/gitee/testdata/webhooks/pr_update.json
new file mode 100644
index 000000000..8ca8146eb
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_update.json
@@ -0,0 +1,596 @@
+{
+  "action": "update",
+  "action_desc": "source_branch_changed",
+  "author": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "body": "",
+  "enterprise": null,
+  "hook_id": 788005,
+  "hook_name": "merge_request_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "iid": 7,
+  "languages": [
+    "Python"
+  ],
+  "merge_commit_sha": "d10df103c351de96ceb9e62e6e4097f0e3420b72",
+  "merge_status": "can_be_resolve",
+  "number": 7,
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:38:53+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:38:53+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "pull_request": {
+    "additions": 11,
+    "assignee": null,
+    "assignees": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "base": {
+      "label": "kit101:master",
+      "ref": "master",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:38:53+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:38:53+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "2eac1cac02c325058cf959725c45b0612d3e8177",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "body": "",
+    "changed_files": 2,
+    "closed_at": null,
+    "comments": 8,
+    "commits": 2,
+    "created_at": "2021-09-30T18:10:27+08:00",
+    "deletions": 0,
+    "diff_url": "https://gitee.com/kit101/drone-yml-test/pulls/7.diff",
+    "head": {
+      "label": "kit101:feat-2",
+      "ref": "feat-2",
+      "repo": {
+        "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "created_at": "2021-03-24T11:24:34+08:00",
+        "default_branch": "master",
+        "description": "",
+        "fork": false,
+        "forks_count": 0,
+        "full_name": "kit101/drone-yml-test",
+        "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+        "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+        "has_issues": true,
+        "has_pages": false,
+        "has_wiki": true,
+        "homepage": "https://gitee.com/kit101/drone-yml-test",
+        "html_url": "https://gitee.com/kit101/drone-yml-test",
+        "id": 14836026,
+        "language": null,
+        "license": null,
+        "name": "drone-yml-test",
+        "name_with_namespace": "kit101/drone-yml-test",
+        "namespace": "kit101",
+        "open_issues_count": 2,
+        "owner": {
+          "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+          "email": "qkssk1711@163.com",
+          "html_url": "https://gitee.com/kit101",
+          "id": 1535738,
+          "login": "kit101",
+          "name": "kit101",
+          "remark": null,
+          "site_admin": false,
+          "type": "User",
+          "url": "https://gitee.com/kit101",
+          "user_name": "kit101",
+          "username": "kit101"
+        },
+        "path": "drone-yml-test",
+        "path_with_namespace": "kit101/drone-yml-test",
+        "private": false,
+        "pushed_at": "2021-10-08T17:38:53+08:00",
+        "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+        "stargazers_count": 0,
+        "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+        "updated_at": "2021-10-08T17:38:53+08:00",
+        "url": "https://gitee.com/kit101/drone-yml-test",
+        "watchers_count": 1
+      },
+      "sha": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+      "user": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    },
+    "html_url": "https://gitee.com/kit101/drone-yml-test/pulls/7",
+    "id": 4731382,
+    "labels": [
+    ],
+    "languages": [
+      "Python"
+    ],
+    "merge_commit_sha": "d10df103c351de96ceb9e62e6e4097f0e3420b72",
+    "merge_reference_name": "refs/pull/7/MERGE",
+    "merge_status": "can_be_resolve",
+    "mergeable": false,
+    "merged": false,
+    "merged_at": null,
+    "milestone": null,
+    "need_review": true,
+    "need_test": true,
+    "number": 7,
+    "patch_url": "https://gitee.com/kit101/drone-yml-test/pulls/7.patch",
+    "state": "open",
+    "tester": null,
+    "testers": [
+      {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      }
+    ],
+    "title": "feat add 3",
+    "updated_at": "2021-10-08T17:36:07+08:00",
+    "updated_by": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "user": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    }
+  },
+  "push_data": null,
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:38:53+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:38:53+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "/gLVmA+/dd5q+siOduWu3z6UGhBJ8I5Fe/Gl8zxLU7A=",
+  "source_branch": "feat-2",
+  "source_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:38:53+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:38:53+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:38:53+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:38:53+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "state": "open",
+  "target_branch": "master",
+  "target_repo": {
+    "project": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:38:53+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:38:53+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    },
+    "repository": {
+      "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "created_at": "2021-03-24T11:24:34+08:00",
+      "default_branch": "master",
+      "description": "",
+      "fork": false,
+      "forks_count": 0,
+      "full_name": "kit101/drone-yml-test",
+      "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+      "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+      "has_issues": true,
+      "has_pages": false,
+      "has_wiki": true,
+      "homepage": "https://gitee.com/kit101/drone-yml-test",
+      "html_url": "https://gitee.com/kit101/drone-yml-test",
+      "id": 14836026,
+      "language": null,
+      "license": null,
+      "name": "drone-yml-test",
+      "name_with_namespace": "kit101/drone-yml-test",
+      "namespace": "kit101",
+      "open_issues_count": 2,
+      "owner": {
+        "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+        "email": "qkssk1711@163.com",
+        "html_url": "https://gitee.com/kit101",
+        "id": 1535738,
+        "login": "kit101",
+        "name": "kit101",
+        "remark": null,
+        "site_admin": false,
+        "type": "User",
+        "url": "https://gitee.com/kit101",
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "path": "drone-yml-test",
+      "path_with_namespace": "kit101/drone-yml-test",
+      "private": false,
+      "pushed_at": "2021-10-08T17:38:53+08:00",
+      "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+      "stargazers_count": 0,
+      "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+      "updated_at": "2021-10-08T17:38:53+08:00",
+      "url": "https://gitee.com/kit101/drone-yml-test",
+      "watchers_count": 1
+    }
+  },
+  "target_user": null,
+  "timestamp": "1633685873030",
+  "title": "feat add 3",
+  "updated_by": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "url": "https://gitee.com/kit101/drone-yml-test/pulls/7"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/pr_update.json.golden b/scm/driver/gitee/testdata/webhooks/pr_update.json.golden
new file mode 100644
index 000000000..5e11ac3d7
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/pr_update.json.golden
@@ -0,0 +1,51 @@
+{
+  "Action": "synchronized",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:38:53+08:00"
+  },
+  "PullRequest": {
+    "Number": 7,
+    "Title": "feat add 3",
+    "Body": "",
+    "Sha": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+    "Ref": "refs/pull/7/head",
+    "Source": "feat-2",
+    "Target": "master",
+    "Fork": "kit101/drone-yml-test",
+    "Link": "https://gitee.com/kit101/drone-yml-test/pulls/7",
+    "Diff": "https://gitee.com/kit101/drone-yml-test/pulls/7.diff",
+    "Closed": false,
+    "Merged": false,
+    "Head": {
+      "Name": "feat-2",
+      "Path": "refs/heads/feat-2",
+      "Sha": "6168d9dae737b47f00c59fafca10c913a6850c3a"
+    },
+    "Base": {
+      "Name": "master",
+      "Path": "refs/heads/master",
+      "Sha": "2eac1cac02c325058cf959725c45b0612d3e8177"
+    },
+    "Author": {
+      "Login": "kit101",
+      "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png"
+    },
+    "Created": "2021-09-30T18:10:27+08:00",
+    "Updated": "2021-10-08T17:36:07+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/push.json b/scm/driver/gitee/testdata/webhooks/push.json
new file mode 100644
index 000000000..24cde84b3
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/push.json
@@ -0,0 +1,230 @@
+{
+  "after": "9921e45e570db8e57e544650baf837932fb2100f",
+  "before": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+  "commits": [
+    {
+      "added": [
+      ],
+      "author": {
+        "email": "qkssk1711@163.com",
+        "id": 1535738,
+        "name": "kit101",
+        "remark": null,
+        "time": "2021-10-08T17:53:12+08:00",
+        "url": "https://gitee.com/kit101",
+        "user": null,
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "committer": {
+        "email": "qkssk1711@163.com",
+        "id": 1535738,
+        "name": "kit101",
+        "remark": null,
+        "time": null,
+        "url": "https://gitee.com/kit101",
+        "user": null,
+        "user_name": "kit101",
+        "username": "kit101"
+      },
+      "distinct": true,
+      "id": "9921e45e570db8e57e544650baf837932fb2100f",
+      "message": "feat-2-1\n",
+      "modified": [
+        "modify.txt"
+      ],
+      "parent_ids": [
+        "6168d9dae737b47f00c59fafca10c913a6850c3a"
+      ],
+      "removed": [
+      ],
+      "timestamp": "2021-10-08T17:53:12+08:00",
+      "tree_id": "fdbf4a1239cdef7f81b6a0e76451735a8a27d2c8",
+      "url": "https://gitee.com/kit101/drone-yml-test/commit/9921e45e570db8e57e544650baf837932fb2100f"
+    }
+  ],
+  "commits_more_than_ten": false,
+  "compare": "https://gitee.com/kit101/drone-yml-test/compare/6168d9dae737b47f00c59fafca10c913a6850c3a...9921e45e570db8e57e544650baf837932fb2100f",
+  "created": false,
+  "deleted": false,
+  "enterprise": null,
+  "head_commit": {
+    "added": [
+    ],
+    "author": {
+      "email": "qkssk1711@163.com",
+      "id": 1535738,
+      "name": "kit101",
+      "remark": null,
+      "time": "2021-10-08T17:53:12+08:00",
+      "url": "https://gitee.com/kit101",
+      "user": null,
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "committer": {
+      "email": "qkssk1711@163.com",
+      "id": 1535738,
+      "name": "kit101",
+      "remark": null,
+      "time": null,
+      "url": "https://gitee.com/kit101",
+      "user": null,
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "distinct": true,
+    "id": "9921e45e570db8e57e544650baf837932fb2100f",
+    "message": "feat-2-1\n",
+    "modified": [
+      "modify.txt"
+    ],
+    "parent_ids": [
+      "6168d9dae737b47f00c59fafca10c913a6850c3a"
+    ],
+    "removed": [
+    ],
+    "timestamp": "2021-10-08T17:53:12+08:00",
+    "tree_id": "fdbf4a1239cdef7f81b6a0e76451735a8a27d2c8",
+    "url": "https://gitee.com/kit101/drone-yml-test/commit/9921e45e570db8e57e544650baf837932fb2100f"
+  },
+  "hook_id": 788005,
+  "hook_name": "push_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:53:18+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:53:18+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "pusher": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "ref": "refs/heads/feat-2-1",
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:53:18+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:53:18+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "OyjGt7PDU9xXl5khawx5APOUmODD+SaulKYnS/Nifew=",
+  "timestamp": "1633686799688",
+  "total_commits_count": 1,
+  "user": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "user_id": 1535738,
+  "user_name": "kit101"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/push.json.golden b/scm/driver/gitee/testdata/webhooks/push.json.golden
new file mode 100644
index 000000000..ce613f8d6
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/push.json.golden
@@ -0,0 +1,60 @@
+{
+  "Ref": "refs/heads/feat-2-1",
+  "Before": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+  "After": "9921e45e570db8e57e544650baf837932fb2100f",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Perm": null,
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:53:18+08:00"
+  },
+  "Commit": {
+    "Sha": "9921e45e570db8e57e544650baf837932fb2100f",
+    "Message": "feat-2-1\n",
+    "Author": {
+      "Login": "kit101",
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-10-08T17:53:12+08:00"
+    },
+    "Committer": {
+      "Login": "kit101",
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-10-08T17:53:12+08:00"
+    },
+    "Link": "https://gitee.com/kit101/drone-yml-test/compare/6168d9dae737b47f00c59fafca10c913a6850c3a...9921e45e570db8e57e544650baf837932fb2100f"
+  },
+  "Commits": [
+    {
+      "Sha": "9921e45e570db8e57e544650baf837932fb2100f",
+      "Message": "feat-2-1\n",
+      "Link": "https://gitee.com/kit101/drone-yml-test/commit/9921e45e570db8e57e544650baf837932fb2100f",
+      "Author": {
+        "Login": "kit101",
+        "Email": "qkssk1711@163.com",
+        "Name": "kit101",
+        "Date": "2021-10-08T17:53:12+08:00"
+      },
+      "Committer": {
+        "Login": "kit101",
+        "Email": "qkssk1711@163.com",
+        "Name": "kit101",
+        "Date": "2021-10-08T17:53:12+08:00"
+      }
+    }
+  ],
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/push_branch_create.json b/scm/driver/gitee/testdata/webhooks/push_branch_create.json
new file mode 100644
index 000000000..f6d56f311
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/push_branch_create.json
@@ -0,0 +1,189 @@
+{
+  "after": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+  "before": "0000000000000000000000000000000000000000",
+  "commits": null,
+  "commits_more_than_ten": true,
+  "compare": "https://gitee.com/kit101/drone-yml-test/compare/0000000000000000000000000000000000000000...6168d9dae737b47f00c59fafca10c913a6850c3a",
+  "created": true,
+  "deleted": false,
+  "enterprise": null,
+  "head_commit": {
+    "added": [
+    ],
+    "author": {
+      "email": "qkssk1711@163.com",
+      "id": 1535738,
+      "name": "kit101",
+      "remark": null,
+      "time": "2021-10-08T17:37:47+08:00",
+      "url": "https://gitee.com/kit101",
+      "user": null,
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "committer": {
+      "email": "qkssk1711@163.com",
+      "id": 1535738,
+      "name": "kit101",
+      "remark": null,
+      "time": null,
+      "url": "https://gitee.com/kit101",
+      "user": null,
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "distinct": true,
+    "id": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+    "message": "feat-2\n",
+    "modified": [
+      "modify.txt"
+    ],
+    "parent_ids": [
+      "00b76e8abd51ae6a96318b3450944b32995f9158"
+    ],
+    "removed": [
+    ],
+    "timestamp": "2021-10-08T17:37:47+08:00",
+    "tree_id": "59792feaa92f1bc2460eb1fbc0122dd5029ac583",
+    "url": "https://gitee.com/kit101/drone-yml-test/commit/6168d9dae737b47f00c59fafca10c913a6850c3a"
+  },
+  "hook_id": 788005,
+  "hook_name": "push_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:39:43+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:39:44+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "pusher": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "ref": "refs/heads/feat-2-1",
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:39:43+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:39:44+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "5QkJzTgTd5PJ555MAindBUxryzHWESPOMeOz+dIzTFc=",
+  "timestamp": "1633686089510",
+  "total_commits_count": 13,
+  "user": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "user_id": 1535738,
+  "user_name": "kit101"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/push_branch_create.json.golden b/scm/driver/gitee/testdata/webhooks/push_branch_create.json.golden
new file mode 100644
index 000000000..3a185c633
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/push_branch_create.json.golden
@@ -0,0 +1,41 @@
+{
+  "Ref": "refs/heads/feat-2-1",
+  "Before": "0000000000000000000000000000000000000000",
+  "After": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Perm": null,
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:39:44+08:00"
+  },
+  "Commit": {
+    "Sha": "6168d9dae737b47f00c59fafca10c913a6850c3a",
+    "Message": "feat-2\n",
+    "Author": {
+      "Login": "kit101",
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-10-08T17:37:47+08:00"
+    },
+    "Committer": {
+      "Login": "kit101",
+      "Name": "kit101",
+      "Email": "qkssk1711@163.com",
+      "Date": "2021-10-08T17:37:47+08:00"
+    },
+    "Link": "https://gitee.com/kit101/drone-yml-test/compare/0000000000000000000000000000000000000000...6168d9dae737b47f00c59fafca10c913a6850c3a"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/push_branch_delete.json b/scm/driver/gitee/testdata/webhooks/push_branch_delete.json
new file mode 100644
index 000000000..ed3d5c1f9
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/push_branch_delete.json
@@ -0,0 +1,151 @@
+{
+  "after": "0000000000000000000000000000000000000000",
+  "before": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+  "commits": [
+  ],
+  "commits_more_than_ten": null,
+  "compare": "https://gitee.com/kit101/drone-yml-test/compare/dc8fa2ebe050d63f639c5b834311e96bc2303523...0000000000000000000000000000000000000000",
+  "created": false,
+  "deleted": true,
+  "enterprise": null,
+  "head_commit": null,
+  "hook_id": 788005,
+  "hook_name": "push_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:32:30+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:32:30+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "pusher": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "ref": "refs/heads/feat-3",
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T17:32:30+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T17:32:30+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "l9f7bD44j3IX+WILn5LlyNy8E37v4xP1xB6eaiMGa5A=",
+  "timestamp": "1633685551787",
+  "total_commits_count": null,
+  "user": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "user_id": 1535738,
+  "user_name": "kit101"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/push_branch_delete.json.golden b/scm/driver/gitee/testdata/webhooks/push_branch_delete.json.golden
new file mode 100644
index 000000000..1ddc616d3
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/push_branch_delete.json.golden
@@ -0,0 +1,28 @@
+{
+  "Ref": "refs/heads/feat-3",
+  "Before": "dc8fa2ebe050d63f639c5b834311e96bc2303523",
+  "After": "0000000000000000000000000000000000000000",
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Perm": null,
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T17:32:30+08:00"
+  },
+  "Commit": {
+    "Sha": "0000000000000000000000000000000000000000",
+    "Link": "https://gitee.com/kit101/drone-yml-test/compare/dc8fa2ebe050d63f639c5b834311e96bc2303523...0000000000000000000000000000000000000000"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/tag_create.json b/scm/driver/gitee/testdata/webhooks/tag_create.json
new file mode 100644
index 000000000..6a3e31511
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/tag_create.json
@@ -0,0 +1,189 @@
+{
+  "after": "aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5",
+  "before": "0000000000000000000000000000000000000000",
+  "commits": null,
+  "commits_more_than_ten": null,
+  "compare": "https://gitee.com/kit101/drone-yml-test/compare/0000000000000000000000000000000000000000...aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5",
+  "created": true,
+  "deleted": false,
+  "enterprise": null,
+  "head_commit": {
+    "added": [
+    ],
+    "author": {
+      "email": "qkssk1711@163.com",
+      "id": 1535738,
+      "name": "kit101",
+      "remark": null,
+      "time": "2021-10-08T05:26:59+00:00",
+      "url": "https://gitee.com/kit101",
+      "user": null,
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "committer": {
+      "email": "noreply@gitee.com",
+      "id": null,
+      "name": "Gitee",
+      "remark": null,
+      "time": null,
+      "url": null,
+      "user": null,
+      "user_name": null,
+      "username": null
+    },
+    "distinct": true,
+    "id": "aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5",
+    "message": "update .drone.yml.\n",
+    "modified": [
+      ".drone.yml"
+    ],
+    "parent_ids": [
+      "c2b3132aaed2dac67923a0bf7c30d8391f99d4a9"
+    ],
+    "removed": [
+    ],
+    "timestamp": "2021-10-08T05:26:59+00:00",
+    "tree_id": "ce6ea40403ba16fa848a190acc5356bdb4ef2025",
+    "url": "https://gitee.com/kit101/drone-yml-test/commit/aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5"
+  },
+  "hook_id": 788005,
+  "hook_name": "tag_push_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:01:12+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:01:12+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "pusher": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "ref": "refs/tags/1.3",
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:01:12+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:01:12+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "EDYOaI5dlyJgaiRE3jAe/fwaqZPNt1kLsRW1HY0p4tA=",
+  "timestamp": "1633680073933",
+  "total_commits_count": null,
+  "user": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "user_id": 1535738,
+  "user_name": "kit101"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/tag_create.json.golden b/scm/driver/gitee/testdata/webhooks/tag_create.json.golden
new file mode 100644
index 000000000..acb55e6b8
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/tag_create.json.golden
@@ -0,0 +1,25 @@
+{
+  "Action": "created",
+  "Ref": {
+    "Name": "1.3",
+    "Sha": "aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5"
+  },
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T16:01:12+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/tag_delete.json b/scm/driver/gitee/testdata/webhooks/tag_delete.json
new file mode 100644
index 000000000..d3eb43ca8
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/tag_delete.json
@@ -0,0 +1,151 @@
+{
+  "after": "0000000000000000000000000000000000000000",
+  "before": "aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5",
+  "commits": [
+  ],
+  "commits_more_than_ten": null,
+  "compare": "https://gitee.com/kit101/drone-yml-test/compare/aa9d11518a64dcd0d591a2a4eb35dfb4d91d49d5...0000000000000000000000000000000000000000",
+  "created": false,
+  "deleted": true,
+  "enterprise": null,
+  "head_commit": null,
+  "hook_id": 788005,
+  "hook_name": "tag_push_hooks",
+  "hook_url": "https://gitee.com/kit101/drone-yml-test/hooks/788005/edit",
+  "password": "",
+  "project": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:16:28+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:16:28+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "push_data": null,
+  "pusher": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "ref": "refs/tags/1.3",
+  "repository": {
+    "clone_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "created_at": "2021-03-24T11:24:34+08:00",
+    "default_branch": "master",
+    "description": "",
+    "fork": false,
+    "forks_count": 0,
+    "full_name": "kit101/drone-yml-test",
+    "git_http_url": "https://gitee.com/kit101/drone-yml-test.git",
+    "git_ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "git_svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "git_url": "git://gitee.com/kit101/drone-yml-test.git",
+    "has_issues": true,
+    "has_pages": false,
+    "has_wiki": true,
+    "homepage": "https://gitee.com/kit101/drone-yml-test",
+    "html_url": "https://gitee.com/kit101/drone-yml-test",
+    "id": 14836026,
+    "language": null,
+    "license": null,
+    "name": "drone-yml-test",
+    "name_with_namespace": "kit101/drone-yml-test",
+    "namespace": "kit101",
+    "open_issues_count": 2,
+    "owner": {
+      "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+      "email": "qkssk1711@163.com",
+      "html_url": "https://gitee.com/kit101",
+      "id": 1535738,
+      "login": "kit101",
+      "name": "kit101",
+      "remark": null,
+      "site_admin": false,
+      "type": "User",
+      "url": "https://gitee.com/kit101",
+      "user_name": "kit101",
+      "username": "kit101"
+    },
+    "path": "drone-yml-test",
+    "path_with_namespace": "kit101/drone-yml-test",
+    "private": false,
+    "pushed_at": "2021-10-08T16:16:28+08:00",
+    "ssh_url": "git@gitee.com:kit101/drone-yml-test.git",
+    "stargazers_count": 0,
+    "svn_url": "svn://gitee.com/kit101/drone-yml-test",
+    "updated_at": "2021-10-08T16:16:28+08:00",
+    "url": "https://gitee.com/kit101/drone-yml-test",
+    "watchers_count": 1
+  },
+  "sender": {
+    "avatar_url": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "email": "qkssk1711@163.com",
+    "html_url": "https://gitee.com/kit101",
+    "id": 1535738,
+    "login": "kit101",
+    "name": "kit101",
+    "remark": null,
+    "site_admin": false,
+    "type": "User",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "sign": "p2XLcddyIKP/Yf7VatUqTj+N1tuHchQ1c7//Lk4KSE8=",
+  "timestamp": "1633680992245",
+  "total_commits_count": null,
+  "user": {
+    "email": "qkssk1711@163.com",
+    "id": 1535738,
+    "name": "kit101",
+    "url": "https://gitee.com/kit101",
+    "user_name": "kit101",
+    "username": "kit101"
+  },
+  "user_id": 1535738,
+  "user_name": "kit101"
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/testdata/webhooks/tag_delete.json.golden b/scm/driver/gitee/testdata/webhooks/tag_delete.json.golden
new file mode 100644
index 000000000..7152c2287
--- /dev/null
+++ b/scm/driver/gitee/testdata/webhooks/tag_delete.json.golden
@@ -0,0 +1,25 @@
+{
+  "Action": "deleted",
+  "Ref": {
+    "Name": "1.3",
+    "Sha": ""
+  },
+  "Repo": {
+    "ID": "14836026",
+    "Namespace": "kit101",
+    "Name": "drone-yml-test",
+    "Branch": "master",
+    "Private": false,
+    "Clone": "https://gitee.com/kit101/drone-yml-test.git",
+    "CloneSSH": "git@gitee.com:kit101/drone-yml-test.git",
+    "Link": "https://gitee.com/kit101/drone-yml-test",
+    "Created": "2021-03-24T11:24:34+08:00",
+    "Updated": "2021-10-08T16:16:28+08:00"
+  },
+  "Sender": {
+    "Avatar": "https://portrait.gitee.com/uploads/avatars/user/511/1535738_qkssk1711_1578953939.png",
+    "Email": "qkssk1711@163.com",
+    "Login": "kit101",
+    "Name": "kit101"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/gitee/user.go b/scm/driver/gitee/user.go
new file mode 100644
index 000000000..3221e15b1
--- /dev/null
+++ b/scm/driver/gitee/user.go
@@ -0,0 +1,85 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type userService struct {
+	client *wrapper
+}
+
+func (s *userService) Find(ctx context.Context) (*scm.User, *scm.Response, error) {
+	out := new(user)
+	res, err := s.client.do(ctx, "GET", "user", nil, out)
+	return convertUser(out), res, err
+}
+
+func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, error) {
+	user, res, err := s.Find(ctx)
+	if err != nil {
+		return "", nil, err
+	}
+	return user.Email, res, err
+}
+
+func (s *userService) FindLogin(ctx context.Context, login string) (*scm.User, *scm.Response, error) {
+	path := fmt.Sprintf("users/%s", login)
+	out := new(user)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertUser(out), res, err
+}
+
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+type user struct {
+	ID                int       `json:"id"`
+	Login             string    `json:"login"`
+	Name              string    `json:"name"`
+	AvatarURL         string    `json:"avatar_url"`
+	URL               string    `json:"url"`
+	HtmlURL           string    `json:"html_url"`
+	FollowersURL      string    `json:"followers_url"`
+	FollowingURL      string    `json:"following_url"`
+	GistsURL          string    `json:"gists_url"`
+	StarredURL        string    `json:"starred_url"`
+	SubscriptionsURL  string    `json:"subscriptions_url"`
+	OrganizationsURL  string    `json:"organizations_url"`
+	ReposURL          string    `json:"repos_url"`
+	EventsURL         string    `json:"events_url"`
+	ReceivedEventsURL string    `json:"received_events_url"`
+	Type              string    `json:"type"`
+	Blog              string    `json:"blog"`
+	Weibo             string    `json:"weibo"`
+	Bio               string    `json:"bio"`
+	PublicRepos       int       `json:"public_repos"`
+	PublicGists       int       `json:"public_gists"`
+	Followers         int       `json:"followers"`
+	Following         int       `json:"following"`
+	Stared            int       `json:"stared"`
+	Watched           int       `json:"watched"`
+	CreatedAt         time.Time `json:"created_at"`
+	UpdatedAt         time.Time `json:"updated_at"`
+	Email             string    `json:"email"`
+	Remark            string    `json:"remark"`
+}
+
+func convertUser(from *user) *scm.User {
+	return &scm.User{
+		Avatar:  from.AvatarURL,
+		Email:   from.Email,
+		Login:   from.Login,
+		Name:    from.Name,
+		Created: from.CreatedAt,
+		Updated: from.UpdatedAt,
+	}
+}
diff --git a/scm/driver/gitee/user_test.go b/scm/driver/gitee/user_test.go
new file mode 100644
index 000000000..dc299ddb3
--- /dev/null
+++ b/scm/driver/gitee/user_test.go
@@ -0,0 +1,106 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestUserFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/user").
+		Reply(200).
+		Type("application/json").
+		SetHeader("X-Request-Id", "7cb049dbf1fafae67b4f0aa81ca7e870").
+		SetHeader("X-Runtime", "0.040217").
+		SetHeader("ETag", "W/\"c50ecf93b72a24474a76423d6d5c338c\"").
+		File("testdata/user.json")
+
+	client := NewDefault()
+	got, res, err := client.Users.Find(context.Background())
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.User)
+	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	// t.Run("Rate", testRate(res))
+}
+
+func TestUserFindEmail(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/user").
+		Reply(200).
+		Type("application/json").
+		SetHeader("X-Request-Id", "7cb049dbf1fafae67b4f0aa81ca7e870").
+		SetHeader("X-Runtime", "0.040217").
+		SetHeader("ETag", "W/\"c50ecf93b72a24474a76423d6d5c338c\"").
+		File("testdata/user.json")
+
+	client := NewDefault()
+	result, res, err := client.Users.FindEmail(context.Background())
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if got, want := result, "qkssk1711@163.com"; got != want {
+		t.Errorf("Want user Email %q, got %q", want, got)
+	}
+	t.Run("Request", testRequest(res))
+	// t.Run("Rate", testRate(res))
+}
+
+func TestUserFindLogin(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitee.com/api/v5").
+		Get("/users/kit101").
+		Reply(200).
+		Type("application/json").
+		SetHeader("X-Request-Id", "7cb049dbf1fafae67b4f0aa81ca7e870").
+		SetHeader("X-Runtime", "0.040217").
+		File("testdata/user.json")
+
+	client := NewDefault()
+	got, res, err := client.Users.FindLogin(context.Background(), "kit101")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.User)
+	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+		json.NewEncoder(os.Stdout).Encode(got)
+	}
+
+	t.Run("Request", testRequest(res))
+	// t.Run("Rate", testRate(res))
+}
diff --git a/scm/driver/gitee/util.go b/scm/driver/gitee/util.go
new file mode 100644
index 000000000..a5e1f973e
--- /dev/null
+++ b/scm/driver/gitee/util.go
@@ -0,0 +1,75 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"net/url"
+	"strconv"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func encodeListOptions(opts scm.ListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	return params.Encode()
+}
+
+func encodeCommitListOptions(opts scm.CommitListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	if opts.Ref != "" {
+		params.Set("sha", opts.Ref)
+	}
+	return params.Encode()
+}
+
+func encodeIssueListOptions(opts scm.IssueListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	// state: open, progressing, closed, rejected, all; default: open
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	} else if opts.Open {
+		params.Set("state", "open")
+	}
+	return params.Encode()
+}
+
+func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	// state: open, closed, merged, all; default: open
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	} else if opts.Open {
+		params.Set("state", "open")
+	}
+	return params.Encode()
+}
diff --git a/scm/driver/gitee/util_test.go b/scm/driver/gitee/util_test.go
new file mode 100644
index 000000000..2846a9bf2
--- /dev/null
+++ b/scm/driver/gitee/util_test.go
@@ -0,0 +1,94 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func Test_encodeListOptions(t *testing.T) {
+	opts := scm.ListOptions{
+		Page: 10,
+		Size: 30,
+	}
+	want := "page=10&per_page=30"
+	got := encodeListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodeCommitListOptions(t *testing.T) {
+	opts := scm.CommitListOptions{
+		Page: 10,
+		Size: 30,
+		Ref:  "master",
+	}
+	want := "page=10&per_page=30&sha=master"
+	got := encodeCommitListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded commit list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodeIssueListOptions(t *testing.T) {
+	opts := scm.IssueListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   true,
+		Closed: true,
+	}
+	want := "page=10&per_page=30&state=all"
+	got := encodeIssueListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded issue list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodeIssueListOptions_Closed(t *testing.T) {
+	opts := scm.IssueListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   false,
+		Closed: true,
+	}
+	want := "page=10&per_page=30&state=closed"
+	got := encodeIssueListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded issue list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodePullRequestListOptions(t *testing.T) {
+	t.Parallel()
+	opts := scm.PullRequestListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   true,
+		Closed: true,
+	}
+	want := "page=10&per_page=30&state=all"
+	got := encodePullRequestListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded pr list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodePullRequestListOptions_Closed(t *testing.T) {
+	t.Parallel()
+	opts := scm.PullRequestListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   false,
+		Closed: true,
+	}
+	want := "page=10&per_page=30&state=closed"
+	got := encodePullRequestListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded pr list options %q, got %q", want, got)
+	}
+}
diff --git a/scm/driver/gitee/webhook.go b/scm/driver/gitee/webhook.go
new file mode 100644
index 000000000..6866baa54
--- /dev/null
+++ b/scm/driver/gitee/webhook.go
@@ -0,0 +1,468 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type webhookService struct {
+	client *wrapper
+}
+
+func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhook, error) {
+	data, err := ioutil.ReadAll(
+		io.LimitReader(req.Body, 10000000),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	var hook scm.Webhook
+	switch req.Header.Get("X-Gitee-Event") {
+	case "Push Hook":
+		hook, err = s.parsePushHook(data)
+	case "Merge Request Hook":
+		hook, err = s.parseMergeRequestHook(data)
+	case "Issue Hook":
+		hook, err = s.parseIssueHook(data)
+	case "Note Hook":
+		hook, err = s.parseNoteHook(data)
+	case "Tag Push Hook":
+		hook, err = s.parseTagPushHook(data)
+	default:
+		return nil, scm.ErrUnknownEvent
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	key, err := fn(hook)
+	if err != nil {
+		return hook, err
+	} else if key == "" {
+		return hook, nil
+	}
+
+	agent := req.Header.Get("User-Agent")
+	if agent != "git-oschina-hook" {
+		return hook, &Error{
+			Message: "hook's user-agent is not git-oschina-hook",
+		}
+	}
+	timestamp := req.Header.Get("X-Gitee-Timestamp")
+	signature := req.Header.Get("X-Gitee-Token")
+
+	if !validateSignature(signature, key, timestamp) {
+		return hook, scm.ErrSignatureInvalid
+	}
+	return hook, nil
+}
+
+func (s *webhookService) parsePushHook(data []byte) (scm.Webhook, error) {
+	dst := new(pushOrTagPushHook)
+	err := json.Unmarshal(data, dst)
+	return convertPushHook(dst), err
+}
+
+func (s *webhookService) parseMergeRequestHook(data []byte) (scm.Webhook, error) {
+	dst := new(mergeRequestHook)
+	err := json.Unmarshal(data, dst)
+	return convertPullRequestHook(dst), err
+}
+
+func (s *webhookService) parseIssueHook(data []byte) (scm.Webhook, error) {
+	dst := new(issueHook)
+	err := json.Unmarshal(data, dst)
+	return convertIssueHook(dst), err
+}
+
+func (s *webhookService) parseTagPushHook(data []byte) (scm.Webhook, error) {
+	dst := new(pushOrTagPushHook)
+	err := json.Unmarshal(data, dst)
+	return convertTagPushHook(dst), err
+}
+
+func (s *webhookService) parseNoteHook(data []byte) (scm.Webhook, error) {
+	dst := new(noteHook)
+	err := json.Unmarshal(data, dst)
+	return convertNoteHook(dst), err
+}
+
+// validateSignature
+// see https://gitee.com/help/articles/4290#article-header3
+func validateSignature(signature, key, timestamp string) bool {
+	stringToSign := timestamp + "\n" + key
+	h := hmac.New(sha256.New, []byte(key))
+	h.Write([]byte(stringToSign))
+	computedSignature := base64.StdEncoding.EncodeToString(h.Sum(nil))
+	return computedSignature == signature
+}
+
+type (
+	pushOrTagPushHook struct {
+		Action             string         `json:"action"`
+		HookName           string         `json:"hook_name"`
+		Password           string         `json:"password"`
+		HookID             int            `json:"hook_id"`
+		HookURL            string         `json:"hook_url"`
+		Timestamp          string         `json:"timestamp"`
+		Sign               string         `json:"sign"`
+		Ref                string         `json:"ref"`
+		Before             string         `json:"before"`
+		After              string         `json:"after"`
+		Created            bool           `json:"created"`
+		Deleted            bool           `json:"deleted"`
+		Compare            string         `json:"compare"`
+		Commits            []hookCommit   `json:"commits"`
+		HeadCommit         hookCommit     `json:"head_commit"`
+		TotalCommitsCount  int            `json:"total_commits_count"`
+		CommitsMoreThanTen bool           `json:"commits_more_than_ten"`
+		Repository         hookRepository `json:"repository"`
+		Sender             user           `json:"sender"`
+		Enterprise         enterprise     `json:"enterprise"`
+	}
+	mergeRequestHook struct {
+		Action string `json:"action"`
+
+		Number      int            `json:"number"`
+		Title       string         `json:"title"`
+		Project     hookRepository `json:"project"`
+		PullRequest pr             `json:"pull_request"`
+
+		Iid            int            `json:"iid"`
+		ActionDesc     string         `json:"action_desc"`
+		Author         user           `json:"author"`
+		Body           string         `json:"body"`
+		Enterprise     enterprise     `json:"enterprise"`
+		Languages      []string       `json:"languages"`
+		MergeCommitSha string         `json:"merge_commit_sha"`
+		MergeStatus    string         `json:"merge_status"`
+		Password       string         `json:"password"`
+		Repository     hookRepository `json:"repository"`
+		Sender         user           `json:"sender"`
+		SourceBranch   string         `json:"source_branch"`
+
+		SourceRepo struct {
+			Project    hookRepository `json:"project"`
+			Repository hookRepository `json:"repository"`
+		} `json:"source_repo"`
+		TargetRepo struct {
+			Project    hookRepository `json:"project"`
+			Repository hookRepository `json:"repository"`
+		} `json:"target_repo"`
+		State        string `json:"state"`
+		TargetBranch string `json:"target_branch"`
+		TargetUser   user   `json:"target_user"`
+		Timestamp    string `json:"timestamp"`
+		UpdatedBy    user   `json:"updated_by"`
+		URL          string `json:"url"`
+	}
+	noteHook struct {
+		Action       string         `json:"action"`
+		HookName     string         `json:"hook_name"`
+		Password     string         `json:"password"`
+		HookID       int            `json:"hook_id"`
+		HookURL      string         `json:"hook_url"`
+		Timestamp    string         `json:"timestamp"`
+		Sign         string         `json:"sign"`
+		Comment      hookComment    `json:"comment"`
+		NoteableType string         `json:"noteable_type"`
+		Issue        issue          `json:"issue"`
+		PullRequest  pr             `json:"pull_request"`
+		Repository   hookRepository `json:"repository"`
+		Sender       user           `json:"sender"`
+		Enterprise   enterprise     `json:"enterprise"`
+	}
+	issueHook struct {
+		Action     string         `json:"action"`
+		HookName   string         `json:"hook_name"`
+		Password   string         `json:"password"`
+		HookID     int            `json:"hook_id"`
+		HookURL    string         `json:"hook_url"`
+		Timestamp  string         `json:"timestamp"`
+		Sign       string         `json:"sign"`
+		Issue      issue          `json:"issue"`
+		Repository hookRepository `json:"repository"`
+		Sender     user           `json:"sender"`
+		Enterprise enterprise     `json:"enterprise"`
+	}
+
+	hookAuthorOrCommitter struct {
+		Time     time.Time `json:"time"`
+		Name     string    `json:"name"`
+		Email    string    `json:"email"`
+		Username string    `json:"username"`
+		UserName string    `json:"user_name"`
+		URL      string    `json:"url"`
+	}
+	hookCommit struct {
+		ID        string                `json:"id"`
+		TreeID    string                `json:"tree_id"`
+		Distinct  bool                  `json:"distinct"`
+		Message   string                `json:"message"`
+		Timestamp time.Time             `json:"timestamp"`
+		URL       string                `json:"url"`
+		Author    hookAuthorOrCommitter `json:"author"`
+		Committer hookAuthorOrCommitter `json:"committer"`
+		Added     interface{}           `json:"added"`
+		Removed   interface{}           `json:"removed"`
+		Modified  []string              `json:"modified"`
+	}
+	hookComment struct {
+		HtmlURL   string    `json:"html_url"`
+		ID        int       `json:"id"`
+		Body      string    `json:"body"`
+		User      user      `json:"user"`
+		CreatedAt time.Time `json:"created_at"`
+		UpdatedAt time.Time `json:"updated_at"`
+	}
+	hookRepository struct {
+		CloneURL          string      `json:"clone_url"`
+		CreatedAt         time.Time   `json:"created_at"`
+		DefaultBranch     string      `json:"default_branch"`
+		Description       string      `json:"description"`
+		Fork              bool        `json:"fork"`
+		ForksCount        int         `json:"forks_count"`
+		FullName          string      `json:"full_name"`
+		GitHttpURL        string      `json:"git_http_url"`
+		GitSshURL         string      `json:"git_ssh_url"`
+		GitSvnURL         string      `json:"git_svn_url"`
+		GitURL            string      `json:"git_url"`
+		HasIssues         bool        `json:"has_issues"`
+		HasPages          bool        `json:"has_pages"`
+		HasWiki           bool        `json:"has_wiki"`
+		Homepage          string      `json:"homepage"`
+		HtmlURL           string      `json:"html_url"`
+		ID                int         `json:"id"`
+		Language          interface{} `json:"language"`
+		License           interface{} `json:"license"`
+		Name              string      `json:"name"`
+		NameWithNamespace string      `json:"name_with_namespace"`
+		Namespace         string      `json:"namespace"`
+		OpenIssuesCount   int         `json:"open_issues_count"`
+		Owner             user        `json:"owner"`
+		Path              string      `json:"path"`
+		PathWithNamespace string      `json:"path_with_namespace"`
+		Private           bool        `json:"private"`
+		PushedAt          time.Time   `json:"pushed_at"`
+		SSHURL            string      `json:"ssh_url"`
+		StargazersCount   int         `json:"stargazers_count"`
+		SvnURL            string      `json:"svn_url"`
+		UpdatedAt         time.Time   `json:"updated_at"`
+		URL               string      `json:"url"`
+		WatchersCount     int         `json:"watchers_count"`
+	}
+	enterprise struct {
+		Name string `json:"name"`
+		URL  string `json:"url"`
+	}
+)
+
+func convertPushHook(src *pushOrTagPushHook) *scm.PushHook {
+	var commits []scm.Commit
+	if &src.Commits != nil {
+		for _, c := range src.Commits {
+			commits = append(commits,
+				scm.Commit{
+					Sha:     c.ID,
+					Message: c.Message,
+					Link:    c.URL,
+					Author: scm.Signature{
+						Login: c.Author.Username,
+						Email: c.Author.Email,
+						Name:  c.Author.Name,
+						Date:  c.Timestamp,
+					},
+					Committer: scm.Signature{
+						Login: c.Committer.Username,
+						Email: c.Committer.Email,
+						Name:  c.Committer.Name,
+						Date:  c.Timestamp,
+					},
+				})
+		}
+	}
+
+	dst := &scm.PushHook{
+		Ref:    src.Ref,
+		Repo:   *convertHookRepository(&src.Repository),
+		Before: src.Before,
+		After:  src.After,
+		Commit: scm.Commit{
+			Sha:  src.After,
+			Link: src.Compare,
+		},
+		Sender:  *convertUser(&src.Sender),
+		Commits: commits,
+	}
+	if &src.HeadCommit != nil {
+		dst.Commit.Message = src.HeadCommit.Message
+		dst.Commit.Author = scm.Signature{
+			Login: src.HeadCommit.Author.Username,
+			Email: src.HeadCommit.Author.Email,
+			Name:  src.HeadCommit.Author.Name,
+			Date:  src.HeadCommit.Timestamp,
+		}
+		dst.Commit.Committer = scm.Signature{
+			Login: src.HeadCommit.Committer.Username,
+			Email: src.HeadCommit.Committer.Email,
+			Name:  src.HeadCommit.Committer.Name,
+			Date:  src.HeadCommit.Timestamp,
+		}
+	}
+	return dst
+}
+
+func convertPullRequestHook(src *mergeRequestHook) *scm.PullRequestHook {
+	dst := &scm.PullRequestHook{
+		Repo:        *convertHookRepository(&src.Repository),
+		PullRequest: *convertPullRequest(&src.PullRequest),
+		Sender:      *convertUser(&src.Sender),
+	}
+	switch src.Action {
+	case "update":
+		if src.ActionDesc == "update_label" {
+			if len(src.PullRequest.Labels) == 0 {
+				dst.Action = scm.ActionUnlabel
+			} else {
+				dst.Action = scm.ActionLabel
+			}
+		} else if src.ActionDesc == "source_branch_changed" {
+			// Gitee does not provide a synchronize action.
+			// But when action_desc is 'source_branch_changed',
+			// what happens is the same as GitHub's synchronize
+			dst.Action = scm.ActionSync
+		}
+	case "open":
+		dst.Action = scm.ActionOpen
+	case "close":
+		dst.Action = scm.ActionClose
+	case "merge":
+		dst.Action = scm.ActionMerge
+	case "test", "tested", "assign", "approved":
+		dst.Action = scm.ActionUnknown
+	default:
+		dst.Action = scm.ActionUnknown
+	}
+	return dst
+}
+
+func convertIssueHook(src *issueHook) *scm.IssueHook {
+	dst := &scm.IssueHook{
+		Repo:   *convertHookRepository(&src.Repository),
+		Issue:  *convertIssue(&src.Issue),
+		Sender: *convertUser(&src.Sender),
+	}
+	switch src.Action {
+	case "open":
+		dst.Action = scm.ActionOpen
+	case "delete":
+		dst.Action = scm.ActionClose
+	case "state_change":
+		switch src.Issue.State {
+		case "open", "progressing":
+			dst.Action = scm.ActionOpen
+		case "close", "rejected":
+			dst.Action = scm.ActionClose
+		}
+	case "assign":
+		dst.Action = scm.ActionUpdate
+	default:
+		dst.Action = scm.ActionUnknown
+	}
+	return dst
+}
+
+func convertTagPushHook(src *pushOrTagPushHook) scm.Webhook {
+	dst := &scm.TagHook{
+		Ref: scm.Reference{
+			Name: scm.TrimRef(src.Ref),
+			Sha:  src.HeadCommit.ID,
+		},
+		Repo:   *convertHookRepository(&src.Repository),
+		Sender: *convertUser(&src.Sender),
+	}
+	if src.Created {
+		dst.Action = scm.ActionCreate
+	} else if src.Deleted {
+		dst.Action = scm.ActionDelete
+		dst.Ref.Sha = ""
+	} else {
+		dst.Action = scm.ActionUnknown
+	}
+	return dst
+}
+
+func convertNoteHook(src *noteHook) scm.Webhook {
+	convertHookComment := func(comment *hookComment) *scm.Comment {
+		return &scm.Comment{
+			ID:      comment.ID,
+			Body:    comment.Body,
+			Author:  *convertUser(&comment.User),
+			Created: comment.CreatedAt,
+			Updated: comment.UpdatedAt,
+		}
+	}
+	convertCommentAction := func(src string) (action scm.Action) {
+		switch src {
+		case "comment":
+			return scm.ActionCreate
+		case "edited":
+			return scm.ActionEdit
+		case "deleted":
+			return scm.ActionDelete
+		default:
+			return scm.ActionUnknown
+		}
+	}
+
+	if src.NoteableType == "Issue" {
+		return &scm.IssueCommentHook{
+			Action:  convertCommentAction(src.Action),
+			Repo:    *convertHookRepository(&src.Repository),
+			Issue:   *convertIssue(&src.Issue),
+			Comment: *convertHookComment(&src.Comment),
+			Sender:  *convertUser(&src.Sender),
+		}
+	}
+
+	if src.NoteableType == "PullRequest" {
+		// not support review comment
+		return &scm.PullRequestCommentHook{
+			Action:      convertCommentAction(src.Action),
+			Repo:        *convertHookRepository(&src.Repository),
+			PullRequest: *convertPullRequest(&src.PullRequest),
+			Comment:     *convertHookComment(&src.Comment),
+			Sender:      *convertUser(&src.Sender),
+		}
+	}
+	return nil
+}
+
+func convertHookRepository(from *hookRepository) *scm.Repository {
+	return &scm.Repository{
+		ID:        fmt.Sprint(from.ID),
+		Namespace: from.Namespace,
+		Name:      from.Name,
+		Branch:    from.DefaultBranch,
+		Private:   from.Private,
+		Clone:     from.CloneURL,
+		CloneSSH:  from.GitSshURL,
+		Link:      from.HtmlURL,
+		Created:   from.CreatedAt,
+		Updated:   from.UpdatedAt,
+	}
+}
diff --git a/scm/driver/gitee/webhook_test.go b/scm/driver/gitee/webhook_test.go
new file mode 100644
index 000000000..9cb013db3
--- /dev/null
+++ b/scm/driver/gitee/webhook_test.go
@@ -0,0 +1,265 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gitee
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestWebhooks(t *testing.T) {
+	tests := []struct {
+		event  string
+		before string
+		after  string
+		obj    interface{}
+	}{
+		//
+		// Push Hook events
+		//
+
+		// Push Hook
+		{
+			event:  "Push Hook",
+			before: "testdata/webhooks/push.json",
+			after:  "testdata/webhooks/push.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		// Push Hook create
+		{
+			event:  "Push Hook",
+			before: "testdata/webhooks/push_branch_create.json",
+			after:  "testdata/webhooks/push_branch_create.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		// Push Hook delete
+		{
+			event:  "Push Hook",
+			before: "testdata/webhooks/push_branch_delete.json",
+			after:  "testdata/webhooks/push_branch_delete.json.golden",
+			obj:    new(scm.PushHook),
+		},
+
+		//
+		// Push Tag Hook events
+		//
+
+		// Push Tag Hook create
+		{
+			event:  "Tag Push Hook",
+			before: "testdata/webhooks/tag_create.json",
+			after:  "testdata/webhooks/tag_create.json.golden",
+			obj:    new(scm.TagHook),
+		},
+		// Push Tag Hook delete
+		{
+			event:  "Tag Push Hook",
+			before: "testdata/webhooks/tag_delete.json",
+			after:  "testdata/webhooks/tag_delete.json.golden",
+			obj:    new(scm.TagHook),
+		},
+
+		//
+		// Merge Request Hook events
+		//
+
+		// Merge Request Hook merge
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pr_merge.json",
+			after:  "testdata/webhooks/pr_merge.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// Merge Request Hook open
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pr_open.json",
+			after:  "testdata/webhooks/pr_open.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// Merge Request Hook close
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pr_close.json",
+			after:  "testdata/webhooks/pr_close.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request update
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pr_update.json",
+			after:  "testdata/webhooks/pr_update.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// Merge Request Hook labeled
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pr_labeled.json",
+			after:  "testdata/webhooks/pr_labeled.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// Merge Request Hook unlabeled
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pr_unlabeled.json",
+			after:  "testdata/webhooks/pr_unlabeled.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+
+		//
+		// Issue Hook events
+		//
+
+		// Issue Hook open
+		{
+			event:  "Issue Hook",
+			before: "testdata/webhooks/issue_hook_open.json",
+			after:  "testdata/webhooks/issue_hook_open.json.golden",
+			obj:    new(scm.IssueHook),
+		},
+		// Issue Hook delete
+		{
+			event:  "Issue Hook",
+			before: "testdata/webhooks/issue_hook_delete.json",
+			after:  "testdata/webhooks/issue_hook_delete.json.golden",
+			obj:    new(scm.IssueHook),
+		},
+		// Issue Hook state_change
+		{
+			event:  "Issue Hook",
+			before: "testdata/webhooks/issue_hook_state_change.json",
+			after:  "testdata/webhooks/issue_hook_state_change.json.golden",
+			obj:    new(scm.IssueHook),
+		},
+		// Issue Hook assign
+		{
+			event:  "Issue Hook",
+			before: "testdata/webhooks/issue_hook_assign.json",
+			after:  "testdata/webhooks/issue_hook_assign.json.golden",
+			obj:    new(scm.IssueHook),
+		},
+
+		//
+		// Note Hook events
+		//
+
+		// Note Hook issue comment
+		{
+			event:  "Note Hook",
+			before: "testdata/webhooks/note_hook_issue_comment.json",
+			after:  "testdata/webhooks/note_hook_issue_comment.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		// Note Hook pull request comment
+		{
+			event:  "Note Hook",
+			before: "testdata/webhooks/note_hook_pr_comment.json",
+			after:  "testdata/webhooks/note_hook_pr_comment.json.golden",
+			obj:    new(scm.PullRequestCommentHook),
+		},
+	}
+
+	for _, test := range tests {
+		before, err := ioutil.ReadFile(test.before)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		after, err := ioutil.ReadFile(test.after)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		buf := bytes.NewBuffer(before)
+		r, _ := http.NewRequest("GET", "/", buf)
+		r.Header.Set("X-Gitee-Event", test.event)
+		r.Header.Set("X-Gitee-Token", "Xvh4YPVe6l31XpDRL9J2yeaEXabsckIoUUschpXiVck=")
+		r.Header.Set("X-Gitee-Timestamp", "1633679083918")
+		r.Header.Set("User-Agent", "git-oschina-hook")
+
+		s := new(webhookService)
+		o, err := s.Parse(r, secretFunc)
+		if err != nil && err != scm.ErrSignatureInvalid {
+			t.Error(err)
+			continue
+		}
+
+		err = json.Unmarshal(after, test.obj)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		if diff := cmp.Diff(test.obj, o); diff != "" {
+			t.Errorf("Error unmarshaling %s", test.before)
+			t.Log(diff)
+		}
+
+		switch event := o.(type) {
+		case *scm.PushHook:
+			if !strings.HasPrefix(event.Ref, "refs/") {
+				t.Errorf("Push hook reference must start with refs/")
+			}
+		case *scm.TagHook:
+			if strings.HasPrefix(event.Ref.Name, "refs/") {
+				t.Errorf("Branch hook reference must not start with refs/")
+			}
+		}
+	}
+}
+
+func TestWebhook_ErrUnknownEvent(t *testing.T) {
+	f, _ := ioutil.ReadFile("testdata/webhooks/push.json")
+	r, _ := http.NewRequest("GET", "/", bytes.NewBuffer(f))
+
+	s := new(webhookService)
+	_, err := s.Parse(r, secretFunc)
+	if err != scm.ErrUnknownEvent {
+		t.Errorf("Expect unknown event error, got %v", err)
+	}
+}
+
+func TestWebhookInvalid(t *testing.T) {
+	f, _ := ioutil.ReadFile("testdata/webhooks/push.json")
+	r, _ := http.NewRequest("GET", "/", bytes.NewBuffer(f))
+	r.Header.Set("X-Gitee-Event", "Push Hook")
+	r.Header.Set("X-Gitee-Token", "Xvh4YPVe6l31XpDRL9J2yeaEXabsckIoUUschpXiVck=")
+	r.Header.Set("X-Gitee-Timestamp", "1633679083917")
+	r.Header.Set("User-Agent", "git-oschina-hook")
+
+	s := new(webhookService)
+	_, err := s.Parse(r, secretFunc)
+	if err != scm.ErrSignatureInvalid {
+		t.Errorf("Expect invalid signature error, got %v", err)
+	}
+}
+
+func TestWebhookValid(t *testing.T) {
+	f, _ := ioutil.ReadFile("testdata/webhooks/push.json")
+	r, _ := http.NewRequest("GET", "/", bytes.NewBuffer(f))
+	r.Header.Set("X-Gitee-Event", "Push Hook")
+	r.Header.Set("X-Gitee-Token", "Xvh4YPVe6l31XpDRL9J2yeaEXabsckIoUUschpXiVck=")
+	r.Header.Set("X-Gitee-Timestamp", "1633679083918")
+	r.Header.Set("User-Agent", "git-oschina-hook")
+
+	s := new(webhookService)
+	_, err := s.Parse(r, secretFunc)
+	if err != nil {
+		t.Errorf("Expect valid signature, got %v", err)
+	}
+}
+
+func secretFunc(scm.Webhook) (string, error) {
+	return "bBg5lrt03VixkX85CNqYIcecC0SIGASE", nil
+}
diff --git a/scm/driver/github/content.go b/scm/driver/github/content.go
index 16ae4b506..19c734e6a 100644
--- a/scm/driver/github/content.go
+++ b/scm/driver/github/content.go
@@ -8,6 +8,7 @@ import (
 	"context"
 	"encoding/base64"
 	"fmt"
+	"net/url"
 
 	"github.com/drone/go-scm/scm"
 )
@@ -17,14 +18,16 @@ type contentService struct {
 }
 
 func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
-	endpoint := fmt.Sprintf("repos/%s/contents/%s?ref=%s", repo, path, ref)
+	urlEncodedRef := url.QueryEscape(ref)
+	endpoint := fmt.Sprintf("repos/%s/contents/%s?ref=%s", repo, path, urlEncodedRef)
 	out := new(content)
 	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
 	raw, _ := base64.StdEncoding.DecodeString(out.Content)
 	return &scm.Content{
-		Hash: out.Sha,
 		Path: out.Path,
 		Data: raw,
+		// NB the sha returned for github rest api is the blob sha, not the commit sha
+		BlobID: out.Sha,
 	}, res, err
 }
 
@@ -34,7 +37,6 @@ func (s *contentService) Create(ctx context.Context, repo, path string, params *
 		Message: params.Message,
 		Branch:  params.Branch,
 		Content: params.Data,
-		Sha:     params.Sha,
 		Committer: commitAuthor{
 			Name:  params.Signature.Name,
 			Email: params.Signature.Email,
@@ -55,7 +57,8 @@ func (s *contentService) Update(ctx context.Context, repo, path string, params *
 		Message: params.Message,
 		Branch:  params.Branch,
 		Content: params.Data,
-		Sha:     params.Sha,
+		// NB the sha passed to github rest api is the blob sha, not the commit sha
+		Sha: params.BlobID,
 		Committer: commitAuthor{
 			Name:  params.Signature.Name,
 			Email: params.Signature.Email,
@@ -69,8 +72,24 @@ func (s *contentService) Update(ctx context.Context, repo, path string, params *
 	return res, err
 }
 
-func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	endpoint := fmt.Sprintf("repos/%s/contents/%s", repo, path)
+	in := &contentCreateUpdate{
+		Message: params.Message,
+		Branch:  params.Branch,
+		// NB the sha passed to github rest api is the blob sha, not the commit sha
+		Sha: params.BlobID,
+		Committer: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+		Author: commitAuthor{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+	res, err := s.client.do(ctx, "DELETE", endpoint, in, nil)
+	return res, err
 }
 
 func (s *contentService) List(ctx context.Context, repo, path, ref string, _ scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
@@ -111,7 +130,9 @@ func convertContentInfoList(from []*content) []*scm.ContentInfo {
 }
 
 func convertContentInfo(from *content) *scm.ContentInfo {
-	to := &scm.ContentInfo{Path: from.Path}
+	to := &scm.ContentInfo{
+		Path:   from.Path,
+		BlobID: from.Sha}
 	switch from.Type {
 	case "file":
 		to.Kind = scm.ContentKindFile
diff --git a/scm/driver/github/content_test.go b/scm/driver/github/content_test.go
index 5187c713f..c1e8d4110 100644
--- a/scm/driver/github/content_test.go
+++ b/scm/driver/github/content_test.go
@@ -40,7 +40,36 @@ func TestContentFind(t *testing.T) {
 
 	want := new(scm.Content)
 	raw, _ := ioutil.ReadFile("testdata/content.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	gock.New("https://api.github.com").
+		Get("/repos/octocat/hello-world/contents/README").
+		MatchParam("ref", "b1&b2").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content.json")
+
+	client = NewDefault()
+	got, res, err = client.Contents.Find(
+		context.Background(),
+		"octocat/hello-world",
+		"README",
+		"b1&b2",
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want = new(scm.Content)
+	raw, _ = ioutil.ReadFile("testdata/content.json.golden")
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -101,7 +130,7 @@ func TestContentUpdate(t *testing.T) {
 	params := &scm.ContentParams{
 		Message: "a new commit message",
 		Data:    []byte("bXkgdXBkYXRlZCBmaWxlIGNvbnRlbnRz"),
-		Sha:     "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
+		BlobID:  "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
 		Signature: scm.Signature{
 			Name:  "Monalisa Octocat",
 			Email: "octocat@github.com",
@@ -109,7 +138,7 @@ func TestContentUpdate(t *testing.T) {
 	}
 
 	client := NewDefault()
-	res, err := client.Contents.Create(
+	res, err := client.Contents.Update(
 		context.Background(),
 		"octocat/hello-world",
 		"test/hello",
@@ -126,11 +155,72 @@ func TestContentUpdate(t *testing.T) {
 	}
 }
 
+func TestContentUpdateBadBlobID(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Put("/repos/octocat/hello-world/contents/test/hello").
+		Reply(401).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_update.json.fail")
+
+	params := &scm.ContentParams{
+		Message: "a new commit message",
+		Data:    []byte("bXkgdXBkYXRlZCBmaWxlIGNvbnRlbnRz"),
+		BlobID:  "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	_, err := client.Contents.Update(
+		context.Background(),
+		"octocat/hello-world",
+		"test/hello",
+		params,
+	)
+	if err.Error() != "newfile does not match" {
+		t.Errorf("Expecting 'newfile does not match'")
+	}
+}
+
 func TestContentDelete(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Delete(context.Background(), "octocat/hello-world", "README", "master")
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Delete("/repos/octocat/hello-world/contents/test/hello").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_delete.json")
+
+	params := &scm.ContentParams{
+		Message: "a new commit message",
+		BlobID:  "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Delete(
+		context.Background(),
+		"octocat/hello-world",
+		"test/hello",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
 	}
 }
 
@@ -160,7 +250,7 @@ func TestContentList(t *testing.T) {
 
 	want := []*scm.ContentInfo{}
 	raw, _ := ioutil.ReadFile("testdata/content_list.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/github/git.go b/scm/driver/github/git.go
index 79cd8b450..11edba432 100644
--- a/scm/driver/github/git.go
+++ b/scm/driver/github/git.go
@@ -16,6 +16,15 @@ type gitService struct {
 	client *wrapper
 }
 
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/git/refs", repo)
+	in := &createBranch{
+		Ref: scm.ExpandRef(params.Name, "refs/heads"),
+		Sha: params.Sha,
+	}
+	return s.client.do(ctx, "POST", path, in, nil)
+}
+
 func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
 	path := fmt.Sprintf("repos/%s/branches/%s", repo, name)
 	out := new(branch)
@@ -44,6 +53,12 @@ func (s *gitService) ListBranches(ctx context.Context, repo string, opts scm.Lis
 	return convertBranchList(out), res, err
 }
 
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// Github doesnt provide support listing based on searchTerm
+	// Hence calling the ListBranches
+	return s.ListBranches(ctx, repo, opts.PageListOptions)
+}
+
 func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
 	path := fmt.Sprintf("repos/%s/commits?%s", repo, encodeCommitListOptions(opts))
 	out := []*commit{}
@@ -52,10 +67,10 @@ func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.Comm
 }
 
 func (s *gitService) ListTags(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
-	path := fmt.Sprintf("repos/%s/tags?%s", repo, encodeListOptions(opts))
-	out := []*branch{}
+	path := fmt.Sprintf("repos/%s/git/refs/tags?%s", repo, encodeListOptions(opts))
+	out := []*ref{}
 	res, err := s.client.do(ctx, "GET", path, nil, &out)
-	return convertTagList(out), res, err
+	return convertRefList(out), res, err
 }
 
 func (s *gitService) ListChanges(ctx context.Context, repo, ref string, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
@@ -72,6 +87,11 @@ func (s *gitService) CompareChanges(ctx context.Context, repo, source, target st
 	return convertChangeList(out.Files), res, err
 }
 
+type createBranch struct {
+	Ref string `json:"ref"`
+	Sha string `json:"sha"`
+}
+
 type branch struct {
 	Name      string `json:"name"`
 	Commit    commit `json:"commit"`
@@ -171,10 +191,10 @@ func convertRef(from *ref) *scm.Reference {
 	}
 }
 
-func convertTagList(from []*branch) []*scm.Reference {
+func convertRefList(from []*ref) []*scm.Reference {
 	to := []*scm.Reference{}
 	for _, v := range from {
-		to = append(to, convertTag(v))
+		to = append(to, convertRef(v))
 	}
 	return to
 }
diff --git a/scm/driver/github/git_test.go b/scm/driver/github/git_test.go
index 6e8a3707b..8d1ff4c5c 100644
--- a/scm/driver/github/git_test.go
+++ b/scm/driver/github/git_test.go
@@ -35,7 +35,7 @@ func TestGitFindCommit(t *testing.T) {
 
 	want := new(scm.Commit)
 	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -65,7 +65,7 @@ func TestGitFindBranch(t *testing.T) {
 
 	want := new(scm.Reference)
 	raw, _ := ioutil.ReadFile("testdata/branch.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -95,7 +95,7 @@ func TestGitFindTag(t *testing.T) {
 
 	want := new(scm.Reference)
 	raw, _ := ioutil.ReadFile("testdata/tag.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -106,6 +106,38 @@ func TestGitFindTag(t *testing.T) {
 	t.Run("Rate", testRate(res))
 }
 
+func TestGitCreateBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Post("/repos/octocat/hello-world/git/refs").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/branch_create.json")
+
+	params := &scm.ReferenceInput{
+		Name: "Hello",
+		Sha:  "312797ba52425353dec56871a255e2a36fc96344",
+	}
+
+	client := NewDefault()
+	res, err := client.Git.CreateBranch(
+		context.Background(),
+		"octocat/hello-world",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
 func TestGitListCommits(t *testing.T) {
 	defer gock.Off()
 
@@ -113,7 +145,7 @@ func TestGitListCommits(t *testing.T) {
 		Get("/repos/octocat/hello-world/commits").
 		MatchParam("page", "1").
 		MatchParam("per_page", "30").
-		MatchParam("ref", "master").
+		MatchParam("sha", "master").
 		Reply(200).
 		Type("application/json").
 		SetHeaders(mockHeaders).
@@ -129,7 +161,7 @@ func TestGitListCommits(t *testing.T) {
 
 	want := []*scm.Commit{}
 	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -163,7 +195,7 @@ func TestGitListBranches(t *testing.T) {
 
 	want := []*scm.Reference{}
 	raw, _ := ioutil.ReadFile("testdata/branches.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -179,7 +211,7 @@ func TestGitListTags(t *testing.T) {
 	defer gock.Off()
 
 	gock.New("https://api.github.com").
-		Get("/repos/octocat/hello-world/tags").
+		Get("/repos/octocat/hello-world/git/refs/tags").
 		MatchParam("page", "1").
 		MatchParam("per_page", "30").
 		Reply(200).
@@ -197,7 +229,7 @@ func TestGitListTags(t *testing.T) {
 
 	want := []*scm.Reference{}
 	raw, _ := ioutil.ReadFile("testdata/tags.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -228,7 +260,7 @@ func TestGitListChanges(t *testing.T) {
 
 	want := []*scm.Change{}
 	raw, _ := ioutil.ReadFile("testdata/changes.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -258,7 +290,7 @@ func TestGitCompareChanges(t *testing.T) {
 
 	want := []*scm.Change{}
 	raw, _ := ioutil.ReadFile("testdata/compare.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/github/github.go b/scm/driver/github/github.go
index 82f315112..8464b3e2f 100644
--- a/scm/driver/github/github.go
+++ b/scm/driver/github/github.go
@@ -38,9 +38,11 @@ func New(uri string) (*scm.Client, error) {
 	client.Contents = &contentService{client}
 	client.Git = &gitService{client}
 	client.Issues = &issueService{client}
+	client.Milestones = &milestoneService{client}
 	client.Organizations = &organizationService{client}
 	client.PullRequests = &pullService{&issueService{client}}
 	client.Repositories = &RepositoryService{client}
+	client.Releases = &releaseService{client}
 	client.Reviews = &reviewService{client}
 	client.Users = &userService{client}
 	client.Webhooks = &webhookService{client}
diff --git a/scm/driver/github/integration/git_test.go b/scm/driver/github/integration/git_test.go
index c6192cc91..2915ad2f8 100644
--- a/scm/driver/github/integration/git_test.go
+++ b/scm/driver/github/integration/git_test.go
@@ -114,7 +114,8 @@ func testCommits(client *scm.Client) func(t *testing.T) {
 	return func(t *testing.T) {
 		t.Parallel()
 		t.Run("Find", testCommitFind(client))
-		t.Run("List", testCommitList(client))
+		t.Run("List-Default-Branch", testCommitListDefaultBranch(client))
+		t.Run("List-Non-Default-Branch", testCommitListNonDefaultBranch(client))
 	}
 }
 
@@ -130,7 +131,7 @@ func testCommitFind(client *scm.Client) func(t *testing.T) {
 	}
 }
 
-func testCommitList(client *scm.Client) func(t *testing.T) {
+func testCommitListDefaultBranch(client *scm.Client) func(t *testing.T) {
 	return func(t *testing.T) {
 		t.Parallel()
 		opts := scm.CommitListOptions{
@@ -152,6 +153,33 @@ func testCommitList(client *scm.Client) func(t *testing.T) {
 	}
 }
 
+func testCommitListNonDefaultBranch(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.CommitListOptions{
+			Ref: "octocat-patch-1",
+		}
+		result, _, err := client.Git.ListCommits(context.Background(), "octocat/Hello-World", opts)
+		if err != nil {
+			t.Error(err)
+			return
+		}
+		if len(result) == 0 {
+			t.Errorf("Want a non-empty commit list")
+		}
+
+		if got, want := result[0].Sha, "b1b3f9723831141a31a1a7252a213e216ea76e56"; got != want {
+			t.Errorf("Want commit Sha %q, got %q", want, got)
+		}
+
+		for _, commit := range result {
+			if commit.Sha == "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d" {
+				t.Run("Commit", testCommit(commit))
+			}
+		}
+	}
+}
+
 //
 // struct sub-tests
 //
diff --git a/scm/driver/github/integration/issue_test.go b/scm/driver/github/integration/issue_test.go
index 3b28d3aee..5ed080102 100644
--- a/scm/driver/github/integration/issue_test.go
+++ b/scm/driver/github/integration/issue_test.go
@@ -112,7 +112,7 @@ func testIssue(issue *scm.Issue) func(t *testing.T) {
 		if got, want := issue.Author.Login, "octocat"; got != want {
 			t.Errorf("Want issue Author Login %q, got %q", want, got)
 		}
-		if got, want := issue.Author.Avatar, "https://avatars3.githubusercontent.com/u/583231?v=4"; got != want {
+		if got, want := issue.Author.Avatar, "https://avatars.githubusercontent.com/u/583231?v=4"; got != want {
 			t.Errorf("Want issue Author Avatar %q, got %q", want, got)
 		}
 		if got, want := issue.Closed, false; got != want {
diff --git a/scm/driver/github/integration/org_test.go b/scm/driver/github/integration/org_test.go
index 8f72d968d..d58114449 100644
--- a/scm/driver/github/integration/org_test.go
+++ b/scm/driver/github/integration/org_test.go
@@ -44,7 +44,7 @@ func testOrg(organization *scm.Organization) func(t *testing.T) {
 		if got, want := organization.Name, "github"; got != want {
 			t.Errorf("Want organization Name %q, got %q", want, got)
 		}
-		if got, want := organization.Avatar, "https://avatars1.githubusercontent.com/u/9919?v=4"; got != want {
+		if got, want := organization.Avatar, "https://avatars.githubusercontent.com/u/9919?v=4"; got != want {
 			t.Errorf("Want organization Avatar %q, got %q", want, got)
 		}
 	}
diff --git a/scm/driver/github/integration/pr_test.go b/scm/driver/github/integration/pr_test.go
index c4ec31cbf..293aa4c33 100644
--- a/scm/driver/github/integration/pr_test.go
+++ b/scm/driver/github/integration/pr_test.go
@@ -22,6 +22,7 @@ func testPullRequests(client *scm.Client) func(t *testing.T) {
 		t.Run("Find", testPullRequestFind(client))
 		t.Run("Changes", testPullRequestChanges(client))
 		t.Run("Comments", testPullRequestComments(client))
+		t.Run("Commits", testPullRequestCommits(client))
 	}
 }
 
@@ -120,6 +121,26 @@ func testPullRequestChanges(client *scm.Client) func(t *testing.T) {
 	}
 }
 
+//
+// pull request commits sub-tests
+//
+
+func testPullRequestCommits(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.ListOptions{}
+		result, _, err := client.PullRequests.ListCommits(context.Background(), "octocat/Hello-World", 140, opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Got empty pull request change list")
+			return
+		}
+		t.Run("Commit", testPullRequestCommit(result[0]))
+	}
+}
+
 //
 // struct sub-tests
 //
@@ -147,13 +168,13 @@ func testPullRequest(pr *scm.PullRequest) func(t *testing.T) {
 		if got, want := pr.Sha, "b3cbd5bbd7e81436d2eee04537ea2b4c0cad4cdf"; got != want {
 			t.Errorf("Want pr Sha %q, got %q", want, got)
 		}
-		if got, want := pr.Link, "https://github.com/octocat/Hello-World/pull/140.diff"; got != want {
+		if got, want := pr.Link, "https://github.com/octocat/Hello-World/pull/140"; got != want {
 			t.Errorf("Want pr Link %q, got %q", want, got)
 		}
 		if got, want := pr.Author.Login, "octocat"; got != want {
 			t.Errorf("Want pr Author Login %q, got %q", want, got)
 		}
-		if got, want := pr.Author.Avatar, "https://avatars3.githubusercontent.com/u/583231?v=4"; got != want {
+		if got, want := pr.Author.Avatar, "https://avatars.githubusercontent.com/u/583231?v=4"; got != want {
 			t.Errorf("Want pr Author Avatar %q, got %q", want, got)
 		}
 		if got, want := pr.Closed, true; got != want {
@@ -210,3 +231,32 @@ func testChange(change *scm.Change) func(t *testing.T) {
 		}
 	}
 }
+
+func testPullRequestCommit(commit *scm.Commit) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := commit.Message, "Create CONTRIBUTING.md"; got != want {
+			t.Errorf("Want commit Message %q, got %q", want, got)
+		}
+		if got, want := commit.Sha, "b3cbd5bbd7e81436d2eee04537ea2b4c0cad4cdf"; got != want {
+			t.Errorf("Want commit Sha %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Name, "The Octocat"; got != want {
+			t.Errorf("Want commit author Name %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Email, "octocat@nowhere.com"; got != want {
+			t.Errorf("Want commit author Email %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Date.Unix(), int64(1402438946); got != want {
+			t.Errorf("Want commit author Date %d, got %d", want, got)
+		}
+		if got, want := commit.Committer.Name, "The Octocat"; got != want {
+			t.Errorf("Want commit author Name %q, got %q", want, got)
+		}
+		if got, want := commit.Committer.Email, "octocat@nowhere.com"; got != want {
+			t.Errorf("Want commit author Email %q, got %q", want, got)
+		}
+		if got, want := commit.Committer.Date.Unix(), int64(1402438946); got != want {
+			t.Errorf("Want commit author Date %d, got %d", want, got)
+		}
+	}
+}
diff --git a/scm/driver/github/integration/user_test.go b/scm/driver/github/integration/user_test.go
index 0a5b77770..a290b22c6 100644
--- a/scm/driver/github/integration/user_test.go
+++ b/scm/driver/github/integration/user_test.go
@@ -47,7 +47,7 @@ func testUser(user *scm.User) func(t *testing.T) {
 		if got, want := user.Name, "The Octocat"; got != want {
 			t.Errorf("Want user Name %q, got %q", want, got)
 		}
-		if got, want := user.Avatar, "https://avatars3.githubusercontent.com/u/583231?v=4"; got != want {
+		if got, want := user.Avatar, "https://avatars.githubusercontent.com/u/583231?v=4"; got != want {
 			t.Errorf("Want user Avatar %q, got %q", want, got)
 		}
 	}
diff --git a/scm/driver/github/issue_test.go b/scm/driver/github/issue_test.go
index d6ff16f2e..af44e9bbd 100644
--- a/scm/driver/github/issue_test.go
+++ b/scm/driver/github/issue_test.go
@@ -35,7 +35,7 @@ func TestIssueFind(t *testing.T) {
 
 	want := new(scm.Issue)
 	raw, _ := ioutil.ReadFile("testdata/issue.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -65,7 +65,7 @@ func TestIssueCommentFind(t *testing.T) {
 
 	want := new(scm.Comment)
 	raw, _ := ioutil.ReadFile("testdata/issue_comment.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -99,7 +99,7 @@ func TestIssueList(t *testing.T) {
 
 	want := []*scm.Issue{}
 	raw, _ := ioutil.ReadFile("testdata/issues.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -133,7 +133,7 @@ func TestIssueListComments(t *testing.T) {
 
 	want := []*scm.Comment{}
 	raw, _ := ioutil.ReadFile("testdata/issue_comments.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -169,7 +169,7 @@ func TestIssueCreate(t *testing.T) {
 
 	want := new(scm.Issue)
 	raw, _ := ioutil.ReadFile("testdata/issue.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -203,7 +203,7 @@ func TestIssueCreateComment(t *testing.T) {
 
 	want := new(scm.Comment)
 	raw, _ := ioutil.ReadFile("testdata/issue_comment.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/github/milestone.go b/scm/driver/github/milestone.go
new file mode 100644
index 000000000..2ff382138
--- /dev/null
+++ b/scm/driver/github/milestone.go
@@ -0,0 +1,111 @@
+package github
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+type milestone struct {
+	ID           int       `json:"id"`
+	Number       int       `json:"number"`
+	Title        string    `json:"title"`
+	Description  string    `json:"description"`
+	State        string    `json:"state"`
+	DueOn        null.Time `json:"due_on"`
+	URL          string    `json:"url"`
+	HTMLURL      string    `json:"html_url"`
+	LabelsURL    string    `json:"labels_url"`
+	Creator      user      `json:"creator"`
+	OpenIssues   int       `json:"open_issues"`
+	ClosedIssues int       `json:"closed_issues"`
+	NodeID       string    `json:"node_id"`
+	CreatedAt    null.Time `json:"created_at"`
+	UpdatedAt    null.Time `json:"updated_at"`
+	ClosedAt     null.Time `json:"closed_at"`
+}
+
+type milestoneInput struct {
+	Title       string    `json:"title"`
+	State       string    `json:"state"`
+	Description string    `json:"description"`
+	DueOn       null.Time `json:"due_on"`
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/milestones/%d", repo, id)
+	out := new(milestone)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertMilestone(out), res, err
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/milestones?%s", repo, encodeMilestoneListOptions(opts))
+	out := []*milestone{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertMilestoneList(out), res, err
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/milestones", repo)
+	in := &milestoneInput{
+		Title:       input.Title,
+		State:       input.State,
+		Description: input.Description,
+		DueOn:       null.TimeFrom(input.DueDate),
+	}
+	out := new(milestone)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertMilestone(out), res, err
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/milestones/%d", repo, id)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/milestones/%d", repo, id)
+	in := &milestoneInput{}
+	if input.Title != "" {
+		in.Title = input.Title
+	}
+	if input.State != "" {
+		in.State = input.State
+	}
+	if input.Description != "" {
+		in.Description = input.Description
+	}
+	if !input.DueDate.IsZero() {
+		in.DueOn = null.TimeFrom(input.DueDate)
+	}
+	out := new(milestone)
+	res, err := s.client.do(ctx, "PATCH", path, in, out)
+	return convertMilestone(out), res, err
+}
+
+func convertMilestoneList(from []*milestone) []*scm.Milestone {
+	var to []*scm.Milestone
+	for _, m := range from {
+		to = append(to, convertMilestone(m))
+	}
+	return to
+}
+
+func convertMilestone(src *milestone) *scm.Milestone {
+	return &scm.Milestone{
+		Number:      src.Number,
+		ID:          src.ID,
+		Title:       src.Title,
+		Description: src.Description,
+		Link:        src.HTMLURL,
+		State:       src.State,
+		DueDate:     src.DueOn.ValueOrZero(),
+	}
+}
diff --git a/scm/driver/github/milestone_test.go b/scm/driver/github/milestone_test.go
new file mode 100644
index 000000000..0d9c72d7a
--- /dev/null
+++ b/scm/driver/github/milestone_test.go
@@ -0,0 +1,186 @@
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestMilestoneFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/repos/octocat/hello-world/milestones/1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestone.json")
+
+	client := NewDefault()
+	got, res, err := client.Milestones.Find(context.Background(), "octocat/hello-world", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Milestone)
+	raw, _ := ioutil.ReadFile("testdata/milestone.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/repos/octocat/hello-world/milestones").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		MatchParam("state", "all").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestones.json")
+	client := NewDefault()
+	got, res, err := client.Milestones.List(context.Background(), "octocat/hello-world", scm.MilestoneListOptions{Page: 1, Size: 30, Open: true, Closed: true})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Milestone{}
+	raw, _ := ioutil.ReadFile("testdata/milestones.json.golden")
+	err = json.Unmarshal(raw, &want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Post("/repos/octocat/hello-world/milestones").
+		File("testdata/milestone_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestone.json")
+
+	client := NewDefault()
+	dueDate, _ := time.Parse(scm.SearchTimeFormat, "2012-10-09T23:39:01Z")
+	input := &scm.MilestoneInput{
+		Title:       "v1.0",
+		Description: "Tracking milestone for version 1.0",
+		State:       "open",
+		DueDate:     dueDate,
+	}
+
+	got, res, err := client.Milestones.Create(context.Background(), "octocat/hello-world", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Milestone)
+	raw, _ := ioutil.ReadFile("testdata/milestone.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneUpdate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Patch("/repos/octocat/hello-world/milestones/1").
+		File("testdata/milestone_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestone.json")
+
+	client := NewDefault()
+	dueDate, _ := time.Parse(scm.SearchTimeFormat, "2012-10-09T23:39:01Z")
+	input := &scm.MilestoneInput{
+		Title:       "v1.0",
+		Description: "Tracking milestone for version 1.0",
+		State:       "open",
+		DueDate:     dueDate,
+	}
+
+	got, res, err := client.Milestones.Update(context.Background(), "octocat/hello-world", 1, input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Milestone)
+	raw, _ := ioutil.ReadFile("testdata/milestone.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Delete("/repos/octocat/hello-world/milestones/1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.Milestones.Delete(context.Background(), "octocat/hello-world", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
diff --git a/scm/driver/github/org_test.go b/scm/driver/github/org_test.go
index b36c00d8e..eaa9b22f4 100644
--- a/scm/driver/github/org_test.go
+++ b/scm/driver/github/org_test.go
@@ -35,7 +35,7 @@ func TestOrganizationFind(t *testing.T) {
 
 	want := new(scm.Organization)
 	raw, _ := ioutil.ReadFile("testdata/org.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -65,7 +65,7 @@ func TestOrganizationFindMembership(t *testing.T) {
 
 	want := new(scm.Membership)
 	raw, _ := ioutil.ReadFile("testdata/membership.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -98,7 +98,7 @@ func TestOrganizationList(t *testing.T) {
 
 	want := []*scm.Organization{}
 	raw, _ := ioutil.ReadFile("testdata/orgs.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/github/pr.go b/scm/driver/github/pr.go
index c3aebf5d2..52e980937 100644
--- a/scm/driver/github/pr.go
+++ b/scm/driver/github/pr.go
@@ -38,6 +38,13 @@ func (s *pullService) ListChanges(ctx context.Context, repo string, number int,
 	return convertChangeList(out), res, err
 }
 
+func (s *pullService) ListCommits(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/pulls/%d/commits?%s", repo, number, encodeListOptions(opts))
+	out := []*commit{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertCommitList(out), res, err
+}
+
 func (s *pullService) Merge(ctx context.Context, repo string, number int) (*scm.Response, error) {
 	path := fmt.Sprintf("repos/%s/pulls/%d/merge", repo, number)
 	res, err := s.client.do(ctx, "PUT", path, nil, nil)
@@ -69,6 +76,7 @@ type pr struct {
 	State   string `json:"state"`
 	Title   string `json:"title"`
 	Body    string `json:"body"`
+	Draft   bool   `json:"draft"`
 	DiffURL string `json:"diff_url"`
 	HTMLURL string `json:"html_url"`
 	User    struct {
@@ -111,12 +119,13 @@ type prInput struct {
 }
 
 type file struct {
-	Sha       string `json:"sha"`
-	Filename  string `json:"filename"`
-	Status    string `json:"status"`
-	Additions int    `json:"additions"`
-	Deletions int    `json:"deletions"`
-	Changes   int    `json:"changes"`
+	BlobID           string `json:"sha"`
+	Filename         string `json:"filename"`
+	Status           string `json:"status"`
+	Additions        int    `json:"additions"`
+	Deletions        int    `json:"deletions"`
+	Changes          int    `json:"changes"`
+	PreviousFilename string `json:"previous_filename"`
 }
 
 func convertPullRequestList(from []*pr) []*scm.PullRequest {
@@ -146,6 +155,7 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Fork:   from.Head.Repo.FullName,
 		Link:   from.HTMLURL,
 		Diff:   from.DiffURL,
+		Draft:  from.Draft,
 		Closed: from.State != "open",
 		Merged: from.MergedAt.String != "",
 		Head: scm.Reference{
@@ -178,9 +188,11 @@ func convertChangeList(from []*file) []*scm.Change {
 
 func convertChange(from *file) *scm.Change {
 	return &scm.Change{
-		Path:    from.Filename,
-		Added:   from.Status == "added",
-		Deleted: from.Status == "deleted",
-		Renamed: from.Status == "moved",
+		Path:         from.Filename,
+		Added:        from.Status == "added",
+		Deleted:      from.Status == "removed",
+		Renamed:      from.Status == "renamed",
+		BlobID:       from.BlobID,
+		PrevFilePath: from.PreviousFilename,
 	}
 }
diff --git a/scm/driver/github/pr_test.go b/scm/driver/github/pr_test.go
index e239fc3b1..d49d5cef3 100644
--- a/scm/driver/github/pr_test.go
+++ b/scm/driver/github/pr_test.go
@@ -35,7 +35,7 @@ func TestPullFind(t *testing.T) {
 
 	want := new(scm.PullRequest)
 	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -69,7 +69,7 @@ func TestPullList(t *testing.T) {
 
 	want := []*scm.PullRequest{}
 	raw, _ := ioutil.ReadFile("testdata/pulls.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -101,7 +101,7 @@ func TestPullListChanges(t *testing.T) {
 
 	want := []*scm.Change{}
 	raw, _ := ioutil.ReadFile("testdata/pr_files.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -179,7 +179,7 @@ func TestPullCreate(t *testing.T) {
 
 	want := new(scm.PullRequest)
 	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -189,3 +189,35 @@ func TestPullCreate(t *testing.T) {
 	t.Run("Request", testRequest(res))
 	t.Run("rate", testRate(res))
 }
+
+func TestPullListCommits(t *testing.T) {
+	gock.New("https://api.github.com").
+		Get("/repos/octocat/hello-world/pulls/1347/commits").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/commits.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.ListCommits(context.Background(), "octocat/hello-world", 1347, scm.ListOptions{Page: 1, Size: 30})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
diff --git a/scm/driver/github/release.go b/scm/driver/github/release.go
new file mode 100644
index 000000000..afc848c79
--- /dev/null
+++ b/scm/driver/github/release.go
@@ -0,0 +1,131 @@
+package github
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+type release struct {
+	ID          int       `json:"id"`
+	Title       string    `json:"name"`
+	Description string    `json:"body"`
+	Link        string    `json:"html_url,omitempty"`
+	Tag         string    `json:"tag_name,omitempty"`
+	Commitish   string    `json:"target_commitish,omitempty"`
+	Draft       bool      `json:"draft"`
+	Prerelease  bool      `json:"prerelease"`
+	Created     null.Time `json:"created_at"`
+	Published   null.Time `json:"published_at"`
+}
+
+type releaseInput struct {
+	Title       string `json:"name"`
+	Description string `json:"body"`
+	Tag         string `json:"tag_name"`
+	Commitish   string `json:"target_commitish"`
+	Draft       bool   `json:"draft"`
+	Prerelease  bool   `json:"prerelease"`
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/releases/%d", repo, id)
+	out := new(release)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/releases/tags/%s", repo, tag)
+	out := new(release)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/releases?%s", repo, encodeReleaseListOptions(opts))
+	out := []*release{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertReleaseList(out), res, err
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/releases", repo)
+	in := &releaseInput{
+		Title:       input.Title,
+		Commitish:   input.Commitish,
+		Description: input.Description,
+		Draft:       input.Draft,
+		Prerelease:  input.Prerelease,
+		Tag:         input.Tag,
+	}
+	out := new(release)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/releases/%d", repo, id)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	rel, _, _ := s.FindByTag(ctx, repo, tag)
+	return s.Delete(ctx, repo, rel.ID)
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("repos/%s/releases/%d", repo, id)
+	in := &releaseInput{}
+	if input.Title != "" {
+		in.Title = input.Title
+	}
+	if input.Description != "" {
+		in.Description = input.Description
+	}
+	if input.Commitish != "" {
+		in.Commitish = input.Commitish
+	}
+	if input.Tag != "" {
+		in.Tag = input.Tag
+	}
+	in.Draft = input.Draft
+	in.Prerelease = input.Prerelease
+	out := new(release)
+	res, err := s.client.do(ctx, "PATCH", path, in, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	rel, _, _ := s.FindByTag(ctx, repo, tag)
+	return s.Update(ctx, repo, rel.ID, input)
+}
+
+func convertReleaseList(from []*release) []*scm.Release {
+	var to []*scm.Release
+	for _, m := range from {
+		to = append(to, convertRelease(m))
+	}
+	return to
+}
+
+func convertRelease(from *release) *scm.Release {
+	return &scm.Release{
+		ID:          from.ID,
+		Title:       from.Title,
+		Description: from.Description,
+		Link:        from.Link,
+		Tag:         from.Tag,
+		Commitish:   from.Commitish,
+		Draft:       from.Draft,
+		Prerelease:  from.Prerelease,
+		Created:     from.Created.ValueOrZero(),
+		Published:   from.Published.ValueOrZero(),
+	}
+}
diff --git a/scm/driver/github/release_test.go b/scm/driver/github/release_test.go
new file mode 100644
index 000000000..20ebd2ffb
--- /dev/null
+++ b/scm/driver/github/release_test.go
@@ -0,0 +1,194 @@
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestReleaseFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/repos/octocat/hello-world/releases/1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/release.json")
+
+	client := NewDefault()
+	got, res, err := client.Releases.Find(context.Background(), "octocat/hello-world", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/repos/octocat/hello-world/releases").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		MatchParam("state", "all").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/releases.json")
+
+	client := NewDefault()
+	got, res, err := client.Releases.List(context.Background(), "octocat/hello-world", scm.ReleaseListOptions{Page: 1, Size: 30, Open: true, Closed: true})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Release{}
+	raw, _ := ioutil.ReadFile("testdata/releases.json.golden")
+	err = json.Unmarshal(raw, &want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Post("/repos/octocat/hello-world/releases").
+		File("testdata/release_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/release.json")
+
+	client := NewDefault()
+	input := &scm.ReleaseInput{
+		Title:       "v1.0",
+		Description: "Tracking release for version 1.0",
+		Tag:         "v1.0",
+	}
+
+	got, res, err := client.Releases.Create(context.Background(), "octocat/hello-world", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseUpdate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Patch("/repos/octocat/hello-world/releases/1").
+		File("testdata/release_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/release.json")
+
+	client := NewDefault()
+	input := &scm.ReleaseInput{
+		Title:       "v1.0",
+		Description: "Tracking release for version 1.0",
+		Tag:         "v1.0",
+	}
+
+	got, res, err := client.Releases.Update(context.Background(), "octocat/hello-world", 1, input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Delete("/repos/octocat/hello-world/releases/1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.Releases.Delete(context.Background(), "octocat/hello-world", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
diff --git a/scm/driver/github/repo.go b/scm/driver/github/repo.go
index 27cb11794..cad7f917f 100644
--- a/scm/driver/github/repo.go
+++ b/scm/driver/github/repo.go
@@ -6,6 +6,7 @@ package github
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"strconv"
 	"time"
@@ -24,6 +25,7 @@ type repository struct {
 	FullName      string    `json:"full_name"`
 	Private       bool      `json:"private"`
 	Fork          bool      `json:"fork"`
+	Archived      bool      `json:"archived"`
 	Visibility    string    `json:"visibility"`
 	HTMLURL       string    `json:"html_url"`
 	SSHURL        string    `json:"ssh_url"`
@@ -38,6 +40,10 @@ type repository struct {
 	} `json:"permissions"`
 }
 
+type searchRepositoryList struct {
+	Repositories []*repository `json:"items"`
+}
+
 type hook struct {
 	ID     int      `json:"id,omitempty"`
 	Name   string   `json:"name"`
@@ -51,6 +57,11 @@ type hook struct {
 	} `json:"config"`
 }
 
+type repositoryList struct {
+	TotalCount   int           `json:"total_count"`
+	Repositories []*repository `json:"repositories"`
+}
+
 // RepositoryService implements the repository service for
 // the GitHub driver.
 type RepositoryService struct {
@@ -62,7 +73,14 @@ func (s *RepositoryService) Find(ctx context.Context, repo string) (*scm.Reposit
 	path := fmt.Sprintf("repos/%s", repo)
 	out := new(repository)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
-	return convertRepository(out), res, err
+	if err != nil {
+		return nil, res, err
+	}
+	convertedRepo := convertRepository(out)
+	if convertedRepo == nil {
+		return nil, res, errors.New("GitHub returned an unexpected null repository")
+	}
+	return convertedRepo, res, err
 }
 
 // FindHook returns a repository hook.
@@ -78,7 +96,14 @@ func (s *RepositoryService) FindPerms(ctx context.Context, repo string) (*scm.Pe
 	path := fmt.Sprintf("repos/%s", repo)
 	out := new(repository)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
-	return convertRepository(out).Perm, res, err
+	if err != nil {
+		return nil, res, err
+	}
+	convertedRepo := convertRepository(out)
+	if convertedRepo == nil {
+		return nil, res, errors.New("GitHub returned an unexpected null repository")
+	}
+	return convertedRepo.Perm, res, err
 }
 
 // List returns the user repository list.
@@ -89,6 +114,30 @@ func (s *RepositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
 	return convertRepositoryList(out), res, err
 }
 
+// ListV2 returns the user repository list based on the searchTerm passed.
+func (s *RepositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("search/repositories?%s", encodeRepoListOptions(opts))
+	out := new(searchRepositoryList)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out.Repositories), res, err
+}
+
+// ListNamespace returns the orgs' repository list.
+func (s *RepositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("orgs/%s/repos?%s", namespace, encodeListOptions(opts))
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+
+// List returns the github app installation repository list.
+func (s *RepositoryService) ListByInstallation(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("installation/repositories?%s", encodeListOptions(opts))
+	out := new(repositoryList)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRepositoryList(out.Repositories), res, err
+}
+
 // ListHooks returns a list or repository hooks.
 func (s *RepositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
 	path := fmt.Sprintf("repos/%s/hooks?%s", repo, encodeListOptions(opts))
@@ -181,19 +230,24 @@ func (s *RepositoryService) DeleteHook(ctx context.Context, repo, id string) (*s
 	return s.client.do(ctx, "DELETE", path, nil, nil)
 }
 
-// helper function to convert from the gogs repository list to
+// helper function to convert from the github repository list to
 // the common repository structure.
 func convertRepositoryList(from []*repository) []*scm.Repository {
 	to := []*scm.Repository{}
 	for _, v := range from {
-		to = append(to, convertRepository(v))
+		if repo := convertRepository(v); repo != nil {
+			to = append(to, repo)
+		}
 	}
 	return to
 }
 
-// helper function to convert from the gogs repository structure
+// helper function to convert from the github repository structure
 // to the common repository structure.
 func convertRepository(from *repository) *scm.Repository {
+	if from == nil {
+		return nil
+	}
 	return &scm.Repository{
 		ID:        strconv.Itoa(from.ID),
 		Name:      from.Name,
@@ -205,8 +259,9 @@ func convertRepository(from *repository) *scm.Repository {
 		},
 		Link:       from.HTMLURL,
 		Branch:     from.DefaultBranch,
+		Archived:   from.Archived,
 		Private:    from.Private,
-		Visibility: convertVisibility(from.Visibility),
+		Visibility: scm.ConvertVisibility(from.Visibility),
 		Clone:      from.CloneURL,
 		CloneSSH:   from.SSHURL,
 		Created:    from.CreatedAt,
@@ -259,19 +314,6 @@ func convertFromHookEvents(from scm.HookEvents) []string {
 	return events
 }
 
-func convertVisibility(from string) scm.Visibility {
-	switch from {
-	case "public":
-		return scm.VisibilityPublic
-	case "private":
-		return scm.VisibilityPrivate
-	case "internal":
-		return scm.VisibilityInternal
-	default:
-		return scm.VisibilityUndefined
-	}
-}
-
 type status struct {
 	CreatedAt   time.Time `json:"created_at"`
 	UpdatedAt   time.Time `json:"updated_at"`
diff --git a/scm/driver/github/repo_test.go b/scm/driver/github/repo_test.go
index 731f960fe..6af3dba6a 100644
--- a/scm/driver/github/repo_test.go
+++ b/scm/driver/github/repo_test.go
@@ -8,6 +8,7 @@ import (
 	"context"
 	"encoding/json"
 	"io/ioutil"
+	"reflect"
 	"testing"
 
 	"github.com/drone/go-scm/scm"
@@ -34,7 +35,7 @@ func TestRepositoryFind(t *testing.T) {
 
 	want := new(scm.Repository)
 	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -64,7 +65,7 @@ func TestRepositoryPerms(t *testing.T) {
 
 	want := new(scm.Repository)
 	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want.Perm); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -118,7 +119,83 @@ func TestRepositoryList(t *testing.T) {
 
 	want := []*scm.Repository{}
 	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestRepositoryListV2(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/search/repositories").
+		MatchParam("q", "testRepoin:name").
+		MatchParam("q", "user:user123").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/repos_filter.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.ListV2(context.Background(), scm.RepoListOptions{
+		ListOptions: scm.ListOptions{Page: 1, Size: 30},
+		RepoSearchTerm: scm.RepoSearchTerm{
+			RepoName: "testRepo",
+			User:     "user123",
+		},
+	})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos_filter.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
+
+func TestGithubAppInstallationList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/installation/repositories").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/github_app_repos.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.(*RepositoryService).ListByInstallation(context.Background(), scm.ListOptions{Page: 1, Size: 30})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -152,7 +229,7 @@ func TestStatusList(t *testing.T) {
 
 	want := []*scm.Status{}
 	raw, _ := ioutil.ReadFile("testdata/statuses.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -190,7 +267,7 @@ func TestStatusCreate(t *testing.T) {
 
 	want := new(scm.Status)
 	raw, _ := ioutil.ReadFile("testdata/status.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -229,7 +306,7 @@ func TestDeployStatusCreate(t *testing.T) {
 
 	want := new(scm.DeployStatus)
 	raw, _ := ioutil.ReadFile("testdata/deployment.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -259,7 +336,7 @@ func TestRepositoryHookFind(t *testing.T) {
 
 	want := new(scm.Hook)
 	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -292,7 +369,7 @@ func TestRepositoryHookList(t *testing.T) {
 
 	want := []*scm.Hook{}
 	raw, _ := ioutil.ReadFile("testdata/hooks.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -330,7 +407,7 @@ func TestRepositoryHookCreate(t *testing.T) {
 
 	want := new(scm.Hook)
 	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -367,7 +444,7 @@ func TestRepositoryHookUpdate(t *testing.T) {
 
 	want := new(scm.Hook)
 	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -528,3 +605,77 @@ func TestHookEvents(t *testing.T) {
 		}
 	}
 }
+
+func Test_convertRepository(t *testing.T) {
+	permissionsStruct := &repository{
+		Name: "bla",
+	}
+	permissionsStruct.Permissions.Admin = true
+	tests := []struct {
+		name string
+		from *repository
+		want *scm.Repository
+	}{
+		{
+			name: "Simple",
+			from: &repository{
+				Name:        "hello-world",
+				ID:          1,
+				Permissions: permissionsStruct.Permissions,
+			},
+			want: &scm.Repository{
+				Name: "hello-world",
+				ID:   "1",
+				Perm: &scm.Perm{
+					Admin: true,
+				},
+			},
+		},
+		{
+			name: "null",
+			from: nil,
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := convertRepository(tt.from); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("convertRepository() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestRepositoryListWithNull(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/user/repos").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/reposWithNull.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.List(context.Background(), scm.ListOptions{Page: 1, Size: 30})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
diff --git a/scm/driver/github/review_test.go b/scm/driver/github/review_test.go
index 3aaf08ba5..4d12b7947 100644
--- a/scm/driver/github/review_test.go
+++ b/scm/driver/github/review_test.go
@@ -34,7 +34,7 @@ func TestReviewFind(t *testing.T) {
 
 	want := new(scm.Review)
 	raw, _ := ioutil.ReadFile("testdata/pr_comment.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -67,7 +67,7 @@ func TestReviewList(t *testing.T) {
 
 	want := []*scm.Review{}
 	raw, _ := ioutil.ReadFile("testdata/pr_comments.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -105,7 +105,7 @@ func TestReviewCreate(t *testing.T) {
 
 	want := new(scm.Review)
 	raw, _ := ioutil.ReadFile("testdata/pr_comment.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/github/testdata/branch_create.json b/scm/driver/github/testdata/branch_create.json
new file mode 100644
index 000000000..90f12b96a
--- /dev/null
+++ b/scm/driver/github/testdata/branch_create.json
@@ -0,0 +1,10 @@
+{
+    "ref": "refs/heads/Hello",
+    "node_id": "MDM6UmVmMzQ0NDI5MDA5OnJlZnMvaGVhZHMvSGVsbG8=",
+    "url": "https://api.github.com/repos/tphoney/scm-test/git/refs/heads/Hello",
+    "object": {
+        "sha": "312797ba52425353dec56871a255e2a36fc96344",
+        "type": "commit",
+        "url": "https://api.github.com/repos/tphoney/scm-test/git/commits/312797ba52425353dec56871a255e2a36fc96344"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/changes.json.golden b/scm/driver/github/testdata/changes.json.golden
index 9b502353c..b5034d7cf 100644
--- a/scm/driver/github/testdata/changes.json.golden
+++ b/scm/driver/github/testdata/changes.json.golden
@@ -3,6 +3,7 @@
         "Path": "file1.txt",
         "Added": true,
         "Renamed": false,
-        "Deleted": false
+        "Deleted": false,
+        "BlobID": "bbcd538c8e72b8c175046e27cc8f907076331401"
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/compare.json.golden b/scm/driver/github/testdata/compare.json.golden
index f502bc0db..fb8197d1f 100644
--- a/scm/driver/github/testdata/compare.json.golden
+++ b/scm/driver/github/testdata/compare.json.golden
@@ -3,6 +3,7 @@
         "Path": "README",
         "Added": false,
         "Renamed": false,
-        "Deleted": false
+        "Deleted": false,
+        "BlobID": "980a0d5f19a64b4b30a87d4206aade58726b60e3"
     }
 ]
diff --git a/scm/driver/github/testdata/content.json.golden b/scm/driver/github/testdata/content.json.golden
index 4c414c519..d58e10b30 100644
--- a/scm/driver/github/testdata/content.json.golden
+++ b/scm/driver/github/testdata/content.json.golden
@@ -1,5 +1,5 @@
 {
     "Path": "README",
     "Data": "SGVsbG8gV29ybGQhCg==",
-    "Hash": "980a0d5f19a64b4b30a87d4206aade58726b60e3"
+    "BlobID": "980a0d5f19a64b4b30a87d4206aade58726b60e3"
 }
\ No newline at end of file
diff --git a/scm/driver/github/testdata/content_delete.json b/scm/driver/github/testdata/content_delete.json
new file mode 100644
index 000000000..eeefc02b8
--- /dev/null
+++ b/scm/driver/github/testdata/content_delete.json
@@ -0,0 +1,37 @@
+{
+  "content": null,
+  "commit": {
+    "sha": "7638417db6d59f3c431d3e1f261cc637155684cd",
+    "node_id": "MDY6Q29tbWl0NzYzODQxN2RiNmQ1OWYzYzQzMWQzZTFmMjYxY2M2MzcxNTU2ODRjZA==",
+    "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd",
+    "html_url": "https://github.com/octocat/Hello-World/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd",
+    "author": {
+      "date": "2014-11-07T22:01:45Z",
+      "name": "Monalisa Octocat",
+      "email": "octocat@github.com"
+    },
+    "committer": {
+      "date": "2014-11-07T22:01:45Z",
+      "name": "Monalisa Octocat",
+      "email": "octocat@github.com"
+    },
+    "message": "my commit message",
+    "tree": {
+      "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb",
+      "sha": "691272480426f78a0138979dd3ce63b77f706feb"
+    },
+    "parents": [
+      {
+        "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5",
+        "html_url": "https://github.com/octocat/Hello-World/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5",
+        "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5"
+      }
+    ],
+    "verification": {
+      "verified": false,
+      "reason": "unsigned",
+      "signature": null,
+      "payload": null
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/content_list.json.golden b/scm/driver/github/testdata/content_list.json.golden
index 1ecb4b434..3843e16eb 100644
--- a/scm/driver/github/testdata/content_list.json.golden
+++ b/scm/driver/github/testdata/content_list.json.golden
@@ -1,106 +1,132 @@
 [
   {
     "path": "scm/driver/github/content.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "6049c0a9f0859fe56344559006f739a3a81475fb"
   },
   {
     "path": "scm/driver/github/content_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "bec0acdbdf9b5be391a749f13aa6ea223f7f6b2e"
   },
   {
     "path": "scm/driver/github/git.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "fb07733ece5525869d630df704a76ab29dde227c"
   },
   {
     "path": "scm/driver/github/git_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "4bc40050b43314f637c10b767c53c3d548fd359a"
   },
   {
     "path": "scm/driver/github/github.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "82f31511232a18a949e8035aa2f346a90754a387"
   },
   {
     "path": "scm/driver/github/github_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "8d82e48e410796f27cc92fcb5bd9ffbed154b6cb"
   },
   {
     "path": "scm/driver/github/integration",
-    "kind": "directory"
+    "kind": "directory",
+    "BlobID": "f29b9ffe7db8788d4db88de3128015927297f3e1"
   },
   {
     "path": "scm/driver/github/issue.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "b360d4a3b70f368d44734aebac3803de1f9e2bc4"
   },
   {
     "path": "scm/driver/github/issue_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "d6ff16f2eedd1a34200071b97ee5fed0b10e8dad"
   },
   {
     "path": "scm/driver/github/linker.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "92d8da78b2a0433c2df15392fd6211091280e037"
   },
   {
     "path": "scm/driver/github/linker_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "b952f3e5a7ae77a0fa789f3ecfc5bfc46aede1ef"
   },
   {
     "path": "scm/driver/github/org.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "9d4f534054ad4719c25e1551fa2d2bca949f7dc9"
   },
   {
     "path": "scm/driver/github/org_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "b36c00d8e9042ad23f244cee678837cf752d30e9"
   },
   {
     "path": "scm/driver/github/pr.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "a008cbe0473c1f4a4fd9b345c85326120ec4fc35"
   },
   {
     "path": "scm/driver/github/pr_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "6ab42e0a467f593ab805a0ed2992c209ecbe4914"
   },
   {
     "path": "scm/driver/github/repo.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "19eb345a307550d356a1d3f50fc56b48a363acf2"
   },
   {
     "path": "scm/driver/github/repo_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "73880b97306f3e6b6ac9b410b3e10b45f0bede8c"
   },
   {
     "path": "scm/driver/github/review.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "efb31fb34c89d564e4c7381a4cd5a2bf097d8c1b"
   },
   {
     "path": "scm/driver/github/review_test.go",
-    "kind": "file"
+    "kind": "file",
+     "BlobID": "3aaf08ba51ffb1626a73ab8719e4d0d7d5b0ce5f"
   },
   {
     "path": "scm/driver/github/testdata",
-    "kind": "directory"
+    "kind": "directory",
+    "BlobID": "2668c2a5e6a9c5cf4d66e72942e12d85527915b4"
   },
   {
     "path": "scm/driver/github/user.go",
-    "kind": "file"
+    "kind": "file",
+     "BlobID": "4b04b38a805b833f6bd37166e4256a66722b879b"
   },
   {
     "path": "scm/driver/github/user_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "86e5b709daeced102302fba8e46e48e9457adbd3"
   },
   {
     "path": "scm/driver/github/util.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "a6b3c55a7a8924e53e94ee35e6839e5a0a78d724"
   },
   {
     "path": "scm/driver/github/util_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "c6a2664339b965396a861fd224e3bb9733757444"
   },
   {
     "path": "scm/driver/github/webhook.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID": "1789ca55651f7d7903fce3e985212b96592f8800"
   },
   {
     "path": "scm/driver/github/webhook_test.go",
-    "kind": "file"
+    "kind": "file",
+    "BlobID":  "136f2e0e55a4ed961af1b67d45d72fa84a68943a"
   }
 ]
diff --git a/scm/driver/github/testdata/content_update.json.fail b/scm/driver/github/testdata/content_update.json.fail
new file mode 100644
index 000000000..1ae315d60
--- /dev/null
+++ b/scm/driver/github/testdata/content_update.json.fail
@@ -0,0 +1,4 @@
+{
+    "message": "newfile does not match",
+    "documentation_url": "https://docs.github.com/rest/reference/repos#create-or-update-file-contents"
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/emails.json b/scm/driver/github/testdata/emails.json
new file mode 100644
index 000000000..b251b370e
--- /dev/null
+++ b/scm/driver/github/testdata/emails.json
@@ -0,0 +1,8 @@
+[
+    {
+        "email": "octocat@github.com",
+        "verified": true,
+        "primary": true,
+        "visibility": "public"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/emails.json.golden b/scm/driver/github/testdata/emails.json.golden
new file mode 100644
index 000000000..dc0613ac5
--- /dev/null
+++ b/scm/driver/github/testdata/emails.json.golden
@@ -0,0 +1,7 @@
+[
+    {
+        "Value": "octocat@github.com",
+        "verified": true,
+        "primary": true
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/github_app_repos.json b/scm/driver/github/testdata/github_app_repos.json
new file mode 100644
index 000000000..2526a4000
--- /dev/null
+++ b/scm/driver/github/testdata/github_app_repos.json
@@ -0,0 +1,115 @@
+{
+    "total_count": 1,
+    "repositories": [
+        {
+            "id": 1296269,
+            "owner": {
+                "login": "octocat",
+                "id": 1,
+                "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+                "gravatar_id": "",
+                "url": "https://api.github.com/users/octocat",
+                "html_url": "https://github.com/octocat",
+                "followers_url": "https://api.github.com/users/octocat/followers",
+                "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+                "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+                "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+                "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+                "organizations_url": "https://api.github.com/users/octocat/orgs",
+                "repos_url": "https://api.github.com/users/octocat/repos",
+                "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+                "received_events_url": "https://api.github.com/users/octocat/received_events",
+                "type": "User",
+                "site_admin": false
+            },
+            "name": "Hello-World",
+            "full_name": "octocat/Hello-World",
+            "description": "This your first repo!",
+            "private": true,
+            "fork": true,
+            "visibility": "public",
+            "url": "https://api.github.com/repos/octocat/Hello-World",
+            "html_url": "https://github.com/octocat/Hello-World",
+            "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
+            "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}",
+            "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
+            "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}",
+            "clone_url": "https://github.com/octocat/Hello-World.git",
+            "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
+            "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}",
+            "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}",
+            "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
+            "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}",
+            "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors",
+            "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments",
+            "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads",
+            "events_url": "http://api.github.com/repos/octocat/Hello-World/events",
+            "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks",
+            "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
+            "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
+            "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
+            "git_url": "git:github.com/octocat/Hello-World.git",
+            "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks",
+            "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
+            "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
+            "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}",
+            "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
+            "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}",
+            "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages",
+            "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges",
+            "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}",
+            "mirror_url": "git:git.example.com/octocat/Hello-World",
+            "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since, all, participating}",
+            "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}",
+            "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}",
+            "ssh_url": "git@github.com:octocat/Hello-World.git",
+            "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers",
+            "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
+            "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers",
+            "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription",
+            "svn_url": "https://svn.github.com/octocat/Hello-World",
+            "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags",
+            "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams",
+            "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
+            "homepage": "https://github.com",
+            "language": null,
+            "forks_count": 9,
+            "stargazers_count": 80,
+            "watchers_count": 80,
+            "size": 108,
+            "default_branch": "master",
+            "open_issues_count": 0,
+            "topics": [
+                "octocat",
+                "atom",
+                "electron",
+                "API"
+            ],
+            "has_issues": true,
+            "has_wiki": true,
+            "has_pages": false,
+            "has_downloads": true,
+            "archived": false,
+            "pushed_at": "2011-01-26T19:06:43Z",
+            "created_at": "2011-01-26T19:01:12Z",
+            "updated_at": "2011-01-26T19:14:43Z",
+            "permissions": {
+                "admin": true,
+                "push": true,
+                "pull": true
+            },
+            "allow_rebase_merge": true,
+            "allow_squash_merge": true,
+            "allow_merge_commit": true,
+            "subscribers_count": 42,
+            "network_count": 0,
+            "license": {
+                "key": "mit",
+                "name": "MIT License",
+                "spdx_id": "MIT",
+                "url": "https://api.github.com/licenses/mit",
+                "html_url": "http://choosealicense.com/licenses/mit/"
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/milestone.json b/scm/driver/github/testdata/milestone.json
new file mode 100644
index 000000000..cabb83865
--- /dev/null
+++ b/scm/driver/github/testdata/milestone.json
@@ -0,0 +1,37 @@
+{
+  "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1",
+  "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0",
+  "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels",
+  "id": 1002604,
+  "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==",
+  "number": 1,
+  "state": "open",
+  "title": "v1.0",
+  "description": "Tracking milestone for version 1.0",
+  "creator": {
+    "login": "octocat",
+    "id": 1,
+    "node_id": "MDQ6VXNlcjE=",
+    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/octocat",
+    "html_url": "https://github.com/octocat",
+    "followers_url": "https://api.github.com/users/octocat/followers",
+    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+    "organizations_url": "https://api.github.com/users/octocat/orgs",
+    "repos_url": "https://api.github.com/users/octocat/repos",
+    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/octocat/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "open_issues": 4,
+  "closed_issues": 8,
+  "created_at": "2011-04-10T20:09:31Z",
+  "updated_at": "2014-03-03T18:58:10Z",
+  "closed_at": "2013-02-12T13:22:01Z",
+  "due_on": "2012-10-09T23:39:01Z"
+}
diff --git a/scm/driver/github/testdata/milestone.json.golden b/scm/driver/github/testdata/milestone.json.golden
new file mode 100644
index 000000000..ccab27361
--- /dev/null
+++ b/scm/driver/github/testdata/milestone.json.golden
@@ -0,0 +1,9 @@
+{
+  "Link": "https://github.com/octocat/Hello-World/milestones/v1.0",
+  "ID": 1002604,
+  "Number": 1,
+  "State": "open",
+  "Title": "v1.0",
+  "Description": "Tracking milestone for version 1.0",
+  "DueDate": "2012-10-09T23:39:01Z"
+}
diff --git a/scm/driver/github/testdata/milestone_create.json b/scm/driver/github/testdata/milestone_create.json
new file mode 100644
index 000000000..c89bac2bc
--- /dev/null
+++ b/scm/driver/github/testdata/milestone_create.json
@@ -0,0 +1,6 @@
+{
+  "state": "open",
+  "title": "v1.0",
+  "description": "Tracking milestone for version 1.0",
+  "due_on": "2012-10-09T23:39:01Z"
+}
diff --git a/scm/driver/github/testdata/milestones.json b/scm/driver/github/testdata/milestones.json
new file mode 100644
index 000000000..6a4e96bb0
--- /dev/null
+++ b/scm/driver/github/testdata/milestones.json
@@ -0,0 +1,39 @@
+[
+  {
+    "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1",
+    "html_url": "https://github.com/octocat/Hello-World/milestones/v1.0",
+    "labels_url": "https://api.github.com/repos/octocat/Hello-World/milestones/1/labels",
+    "id": 1002604,
+    "node_id": "MDk6TWlsZXN0b25lMTAwMjYwNA==",
+    "number": 1,
+    "state": "open",
+    "title": "v1.0",
+    "description": "Tracking milestone for version 1.0",
+    "creator": {
+      "login": "octocat",
+      "id": 1,
+      "node_id": "MDQ6VXNlcjE=",
+      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/octocat",
+      "html_url": "https://github.com/octocat",
+      "followers_url": "https://api.github.com/users/octocat/followers",
+      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+      "organizations_url": "https://api.github.com/users/octocat/orgs",
+      "repos_url": "https://api.github.com/users/octocat/repos",
+      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/octocat/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "open_issues": 4,
+    "closed_issues": 8,
+    "created_at": "2011-04-10T20:09:31Z",
+    "updated_at": "2014-03-03T18:58:10Z",
+    "closed_at": "2013-02-12T13:22:01Z",
+    "due_on": "2012-10-09T23:39:01Z"
+  }
+]
diff --git a/scm/driver/github/testdata/milestones.json.golden b/scm/driver/github/testdata/milestones.json.golden
new file mode 100644
index 000000000..4d3a51459
--- /dev/null
+++ b/scm/driver/github/testdata/milestones.json.golden
@@ -0,0 +1,11 @@
+[
+  {
+    "Link": "https://github.com/octocat/Hello-World/milestones/v1.0",
+    "ID": 1002604,
+    "Number": 1,
+    "State": "open",
+    "Title": "v1.0",
+    "Description": "Tracking milestone for version 1.0",
+    "DueDate": "2012-10-09T23:39:01Z"
+  }
+]
diff --git a/scm/driver/github/testdata/pr.json b/scm/driver/github/testdata/pr.json
index edcc02d7c..3315fafbc 100644
--- a/scm/driver/github/testdata/pr.json
+++ b/scm/driver/github/testdata/pr.json
@@ -11,6 +11,7 @@
     "comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments",
     "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
     "number": 1347,
+    "draft": false,
     "state": "open",
     "title": "new-feature",
     "body": "Please pull these awesome changes",
diff --git a/scm/driver/github/testdata/pr.json.golden b/scm/driver/github/testdata/pr.json.golden
index 2ea51c709..5bf11ba0c 100644
--- a/scm/driver/github/testdata/pr.json.golden
+++ b/scm/driver/github/testdata/pr.json.golden
@@ -9,6 +9,7 @@
     "Fork": "octocat/Hello-World",
     "Link": "https://github.com/octocat/Hello-World/pull/1347",
     "Diff": "https://github.com/octocat/Hello-World/pull/1347.diff",
+    "Draft": false,
     "Closed": false,
     "Merged": true,
     "Base": {
diff --git a/scm/driver/github/testdata/pr_files.json b/scm/driver/github/testdata/pr_files.json
index d3ca75649..18488635a 100644
--- a/scm/driver/github/testdata/pr_files.json
+++ b/scm/driver/github/testdata/pr_files.json
@@ -1,14 +1,50 @@
 [
     {
-        "sha": "bbcd538c8e72b8c175046e27cc8f907076331401",
-        "filename": "file1.txt",
+        "sha": "291b15982c4926705f5639abffe96b2b6c419ce7",
+        "filename": "asda2",
+        "status": "modified",
+        "additions": 1,
+        "deletions": 1,
+        "changes": 2,
+        "blob_url": "https://github.com/tphoney/scm-test/blob/ad442ec8e99589912117c7d1e689f56b32356868/asda2",
+        "raw_url": "https://github.com/tphoney/scm-test/raw/ad442ec8e99589912117c7d1e689f56b32356868/asda2",
+        "contents_url": "https://api.github.com/repos/tphoney/scm-test/contents/asda2?ref=ad442ec8e99589912117c7d1e689f56b32356868",
+        "patch": "@@ -1,2 +1,2 @@n-n+asdasdn asdasdasd"
+    },
+    {
+        "sha": "ce013625030ba8dba906f756967f9e9ca394464a",
+        "filename": "remove_me",
         "status": "added",
-        "additions": 103,
-        "deletions": 21,
-        "changes": 124,
-        "blob_url": "https://github.com/octocat/Hello-World/blob/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt",
-        "raw_url": "https://github.com/octocat/Hello-World/raw/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt",
-        "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/file1.txt?ref=6dcb09b5b57875f334f61aebed695e2e4193db5e",
-        "patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"
+        "additions": 1,
+        "deletions": 0,
+        "changes": 1,
+        "blob_url": "https://github.com/tphoney/scm-test/blob/ad442ec8e99589912117c7d1e689f56b32356868/remove_me",
+        "raw_url": "https://github.com/tphoney/scm-test/raw/ad442ec8e99589912117c7d1e689f56b32356868/remove_me",
+        "contents_url": "https://api.github.com/repos/tphoney/scm-test/contents/remove_me?ref=ad442ec8e99589912117c7d1e689f56b32356868",
+        "patch": "@@ -0,0 +1 @@n+hello"
+    },
+    {
+        "sha": "70c379b63ffa0795fdbfbc128e5a2818397b7ef8",
+        "filename": "tp",
+        "status": "renamed",
+        "additions": 0,
+        "deletions": 0,
+        "changes": 0,
+        "blob_url": "https://github.com/tphoney/scm-test/blob/ad442ec8e99589912117c7d1e689f56b32356868/tp",
+        "raw_url": "https://github.com/tphoney/scm-test/raw/ad442ec8e99589912117c7d1e689f56b32356868/tp",
+        "contents_url": "https://api.github.com/repos/tphoney/scm-test/contents/tp?ref=ad442ec8e99589912117c7d1e689f56b32356868",
+        "previous_filename": "abhinav"
+    },
+    {
+        "sha": "0f9282d7e71e0f8cb748bfe00e52d7ed13dad036",
+        "filename": "upsert",
+        "status": "removed",
+        "additions": 0,
+        "deletions": 1,
+        "changes": 1,
+        "blob_url": "https://github.com/tphoney/scm-test/blob/6a86c7aca4425c8781876c31ffb6e4284fb07d56/upsert",
+        "raw_url": "https://github.com/tphoney/scm-test/raw/6a86c7aca4425c8781876c31ffb6e4284fb07d56/upsert",
+        "contents_url": "https://api.github.com/repos/tphoney/scm-test/contents/upsert?ref=6a86c7aca4425c8781876c31ffb6e4284fb07d56",
+        "patch": "@@ -1 +0,0 @@n-asdasn No newline at end of file"
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/pr_files.json.golden b/scm/driver/github/testdata/pr_files.json.golden
index 9b502353c..4be7ddc87 100644
--- a/scm/driver/github/testdata/pr_files.json.golden
+++ b/scm/driver/github/testdata/pr_files.json.golden
@@ -1,8 +1,34 @@
 [
     {
-        "Path": "file1.txt",
+        "Path": "asda2",
+        "Added": false,
+        "Renamed": false,
+        "Deleted": false,
+        "BlobID": "291b15982c4926705f5639abffe96b2b6c419ce7",
+        "PrevFilePath": ""
+    },
+    {
+        "Path": "remove_me",
         "Added": true,
         "Renamed": false,
-        "Deleted": false
+        "Deleted": false,
+        "BlobID": "ce013625030ba8dba906f756967f9e9ca394464a",
+        "PrevFilePath": ""
+    },
+    {
+        "Path": "tp",
+        "Added": false,
+        "Renamed": true,
+        "Deleted": false,
+        "BlobID": "70c379b63ffa0795fdbfbc128e5a2818397b7ef8",
+        "PrevFilePath": "abhinav"
+    },
+    {
+        "Path": "upsert",
+        "Added": false,
+        "Renamed": false,
+        "Deleted": true,
+        "BlobID": "0f9282d7e71e0f8cb748bfe00e52d7ed13dad036",
+        "PrevFilePath": ""
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/pulls.json b/scm/driver/github/testdata/pulls.json
index 611d7d71d..e1f85a075 100644
--- a/scm/driver/github/testdata/pulls.json
+++ b/scm/driver/github/testdata/pulls.json
@@ -12,6 +12,7 @@
         "comments_url": "https://api.github.com/repos/octocat/Hello-World/issues/1347/comments",
         "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e",
         "number": 1347,
+        "draft": false,
         "state": "open",
         "title": "new-feature",
         "body": "Please pull these awesome changes",
diff --git a/scm/driver/github/testdata/pulls.json.golden b/scm/driver/github/testdata/pulls.json.golden
index 1a6798b98..f2a97473d 100644
--- a/scm/driver/github/testdata/pulls.json.golden
+++ b/scm/driver/github/testdata/pulls.json.golden
@@ -10,6 +10,7 @@
         "Fork": "octocat/Hello-World",
         "Link": "https://github.com/octocat/Hello-World/pull/1347",
         "Diff": "https://github.com/octocat/Hello-World/pull/1347.diff",
+        "Draft": false,
         "Closed": false,
         "Merged": true,
         "Base": {
diff --git a/scm/driver/github/testdata/release.json b/scm/driver/github/testdata/release.json
new file mode 100644
index 000000000..b85892dbf
--- /dev/null
+++ b/scm/driver/github/testdata/release.json
@@ -0,0 +1,74 @@
+{
+  "url": "https://api.github.com/repos/octocat/Hello-World/releases/1",
+  "html_url": "https://github.com/octocat/Hello-World/releases/v1.0.0",
+  "assets_url": "https://api.github.com/repos/octocat/Hello-World/releases/1/assets",
+  "upload_url": "https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}",
+  "tarball_url": "https://api.github.com/repos/octocat/Hello-World/tarball/v1.0.0",
+  "zipball_url": "https://api.github.com/repos/octocat/Hello-World/zipball/v1.0.0",
+  "id": 1,
+  "node_id": "MDc6UmVsZWFzZTE=",
+  "tag_name": "v1.0.0",
+  "target_commitish": "master",
+  "name": "v1.0.0",
+  "body": "Description of the release",
+  "draft": false,
+  "prerelease": false,
+  "created_at": "2013-02-27T19:35:32Z",
+  "published_at": "2013-02-27T19:35:32Z",
+  "author": {
+    "login": "octocat",
+    "id": 1,
+    "node_id": "MDQ6VXNlcjE=",
+    "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/octocat",
+    "html_url": "https://github.com/octocat",
+    "followers_url": "https://api.github.com/users/octocat/followers",
+    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+    "organizations_url": "https://api.github.com/users/octocat/orgs",
+    "repos_url": "https://api.github.com/users/octocat/repos",
+    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/octocat/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "assets": [
+    {
+      "url": "https://api.github.com/repos/octocat/Hello-World/releases/assets/1",
+      "browser_download_url": "https://github.com/octocat/Hello-World/releases/download/v1.0.0/example.zip",
+      "id": 1,
+      "node_id": "MDEyOlJlbGVhc2VBc3NldDE=",
+      "name": "example.zip",
+      "label": "short description",
+      "state": "uploaded",
+      "content_type": "application/zip",
+      "size": 1024,
+      "download_count": 42,
+      "created_at": "2013-02-27T19:35:32Z",
+      "updated_at": "2013-02-27T19:35:32Z",
+      "uploader": {
+        "login": "octocat",
+        "id": 1,
+        "node_id": "MDQ6VXNlcjE=",
+        "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/octocat",
+        "html_url": "https://github.com/octocat",
+        "followers_url": "https://api.github.com/users/octocat/followers",
+        "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+        "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+        "organizations_url": "https://api.github.com/users/octocat/orgs",
+        "repos_url": "https://api.github.com/users/octocat/repos",
+        "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/octocat/received_events",
+        "type": "User",
+        "site_admin": false
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/release.json.golden b/scm/driver/github/testdata/release.json.golden
new file mode 100644
index 000000000..f199eca60
--- /dev/null
+++ b/scm/driver/github/testdata/release.json.golden
@@ -0,0 +1,12 @@
+{
+  "ID": 1,
+  "Title": "v1.0.0",
+  "Description": "Description of the release",
+  "Link": "https://github.com/octocat/Hello-World/releases/v1.0.0",
+  "Tag": "v1.0.0",
+  "Commitish": "master",
+  "Draft": false,
+  "Prerelease": false,
+  "Created": "2013-02-27T19:35:32Z",
+  "Published": "2013-02-27T19:35:32Z"
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/releases.json b/scm/driver/github/testdata/releases.json
new file mode 100644
index 000000000..e544c30b4
--- /dev/null
+++ b/scm/driver/github/testdata/releases.json
@@ -0,0 +1,76 @@
+[
+  {
+    "url": "https://api.github.com/repos/octocat/Hello-World/releases/1",
+    "html_url": "https://github.com/octocat/Hello-World/releases/v1.0.0",
+    "assets_url": "https://api.github.com/repos/octocat/Hello-World/releases/1/assets",
+    "upload_url": "https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name,label}",
+    "tarball_url": "https://api.github.com/repos/octocat/Hello-World/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/octocat/Hello-World/zipball/v1.0.0",
+    "id": 1,
+    "node_id": "MDc6UmVsZWFzZTE=",
+    "tag_name": "v1.0.0",
+    "target_commitish": "master",
+    "name": "v1.0.0",
+    "body": "Description of the release",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2013-02-27T19:35:32Z",
+    "published_at": "2013-02-27T19:35:32Z",
+    "author": {
+      "login": "octocat",
+      "id": 1,
+      "node_id": "MDQ6VXNlcjE=",
+      "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/octocat",
+      "html_url": "https://github.com/octocat",
+      "followers_url": "https://api.github.com/users/octocat/followers",
+      "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+      "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+      "organizations_url": "https://api.github.com/users/octocat/orgs",
+      "repos_url": "https://api.github.com/users/octocat/repos",
+      "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/octocat/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "assets": [
+      {
+        "url": "https://api.github.com/repos/octocat/Hello-World/releases/assets/1",
+        "browser_download_url": "https://github.com/octocat/Hello-World/releases/download/v1.0.0/example.zip",
+        "id": 1,
+        "node_id": "MDEyOlJlbGVhc2VBc3NldDE=",
+        "name": "example.zip",
+        "label": "short description",
+        "state": "uploaded",
+        "content_type": "application/zip",
+        "size": 1024,
+        "download_count": 42,
+        "created_at": "2013-02-27T19:35:32Z",
+        "updated_at": "2013-02-27T19:35:32Z",
+        "uploader": {
+          "login": "octocat",
+          "id": 1,
+          "node_id": "MDQ6VXNlcjE=",
+          "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/octocat",
+          "html_url": "https://github.com/octocat",
+          "followers_url": "https://api.github.com/users/octocat/followers",
+          "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+          "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+          "organizations_url": "https://api.github.com/users/octocat/orgs",
+          "repos_url": "https://api.github.com/users/octocat/repos",
+          "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/octocat/received_events",
+          "type": "User",
+          "site_admin": false
+        }
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/releases.json.golden b/scm/driver/github/testdata/releases.json.golden
new file mode 100644
index 000000000..db9d598a5
--- /dev/null
+++ b/scm/driver/github/testdata/releases.json.golden
@@ -0,0 +1,14 @@
+[
+  {
+    "ID": 1,
+    "Title": "v1.0.0",
+    "Description": "Description of the release",
+    "Link": "https://github.com/octocat/Hello-World/releases/v1.0.0",
+    "Tag": "v1.0.0",
+    "Commitish": "master",
+    "Draft": false,
+    "Prerelease": false,
+    "Created": "2013-02-27T19:35:32Z",
+    "Published": "2013-02-27T19:35:32Z"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/repo.json b/scm/driver/github/testdata/repo.json
index 7a759a19b..b30d1d653 100644
--- a/scm/driver/github/testdata/repo.json
+++ b/scm/driver/github/testdata/repo.json
@@ -86,7 +86,7 @@
     "has_wiki": true,
     "has_pages": false,
     "has_downloads": true,
-    "archived": false,
+    "archived": true,
     "pushed_at": "2011-01-26T19:06:43Z",
     "created_at": "2011-01-26T19:01:12Z",
     "updated_at": "2011-01-26T19:14:43Z",
diff --git a/scm/driver/github/testdata/repo.json.golden b/scm/driver/github/testdata/repo.json.golden
index 5fa8c4db1..472a08636 100644
--- a/scm/driver/github/testdata/repo.json.golden
+++ b/scm/driver/github/testdata/repo.json.golden
@@ -9,6 +9,7 @@
     },
     "Branch": "master",
     "Private": true,
+    "Archived": true,
     "Visibility": 1,
     "Clone": "https://github.com/octocat/Hello-World.git",
     "CloneSSH": "git@github.com:octocat/Hello-World.git",
diff --git a/scm/driver/github/testdata/reposWithNull.json b/scm/driver/github/testdata/reposWithNull.json
new file mode 100644
index 000000000..bcd5c18c6
--- /dev/null
+++ b/scm/driver/github/testdata/reposWithNull.json
@@ -0,0 +1,113 @@
+[
+    {
+        "id": 1296269,
+        "owner": {
+            "login": "octocat",
+            "id": 1,
+            "avatar_url": "https://github.com/images/error/octocat_happy.gif",
+            "gravatar_id": "",
+            "url": "https://api.github.com/users/octocat",
+            "html_url": "https://github.com/octocat",
+            "followers_url": "https://api.github.com/users/octocat/followers",
+            "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+            "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+            "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+            "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+            "organizations_url": "https://api.github.com/users/octocat/orgs",
+            "repos_url": "https://api.github.com/users/octocat/repos",
+            "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+            "received_events_url": "https://api.github.com/users/octocat/received_events",
+            "type": "User",
+            "site_admin": false
+        },
+        "name": "Hello-World",
+        "full_name": "octocat/Hello-World",
+        "description": "This your first repo!",
+        "private": true,
+        "fork": true,
+        "visibility": "public",
+        "url": "https://api.github.com/repos/octocat/Hello-World",
+        "html_url": "https://github.com/octocat/Hello-World",
+        "archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
+        "assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}",
+        "blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
+        "branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}",
+        "clone_url": "https://github.com/octocat/Hello-World.git",
+        "collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
+        "comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}",
+        "commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}",
+        "compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
+        "contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}",
+        "contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors",
+        "deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments",
+        "downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads",
+        "events_url": "http://api.github.com/repos/octocat/Hello-World/events",
+        "forks_url": "http://api.github.com/repos/octocat/Hello-World/forks",
+        "git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
+        "git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
+        "git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
+        "git_url": "git:github.com/octocat/Hello-World.git",
+        "hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks",
+        "issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
+        "issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
+        "issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}",
+        "keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
+        "labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}",
+        "languages_url": "http://api.github.com/repos/octocat/Hello-World/languages",
+        "merges_url": "http://api.github.com/repos/octocat/Hello-World/merges",
+        "milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}",
+        "mirror_url": "git:git.example.com/octocat/Hello-World",
+        "notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since, all, participating}",
+        "pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}",
+        "releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}",
+        "ssh_url": "git@github.com:octocat/Hello-World.git",
+        "stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers",
+        "statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
+        "subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers",
+        "subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription",
+        "svn_url": "https://svn.github.com/octocat/Hello-World",
+        "tags_url": "http://api.github.com/repos/octocat/Hello-World/tags",
+        "teams_url": "http://api.github.com/repos/octocat/Hello-World/teams",
+        "trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
+        "homepage": "https://github.com",
+        "language": null,
+        "forks_count": 9,
+        "stargazers_count": 80,
+        "watchers_count": 80,
+        "size": 108,
+        "default_branch": "master",
+        "open_issues_count": 0,
+        "topics": [
+            "octocat",
+            "atom",
+            "electron",
+            "API"
+        ],
+        "has_issues": true,
+        "has_wiki": true,
+        "has_pages": false,
+        "has_downloads": true,
+        "archived": false,
+        "pushed_at": "2011-01-26T19:06:43Z",
+        "created_at": "2011-01-26T19:01:12Z",
+        "updated_at": "2011-01-26T19:14:43Z",
+        "permissions": {
+            "admin": true,
+            "push": true,
+            "pull": true
+        },
+        "allow_rebase_merge": true,
+        "allow_squash_merge": true,
+        "allow_merge_commit": true,
+        "subscribers_count": 42,
+        "network_count": 0,
+        "license": {
+            "key": "mit",
+            "name": "MIT License",
+            "spdx_id": "MIT",
+            "url": "https://api.github.com/licenses/mit",
+            "html_url": "http://choosealicense.com/licenses/mit/"
+        }
+    },
+    null
+]
diff --git a/scm/driver/github/testdata/repos_filter.json b/scm/driver/github/testdata/repos_filter.json
new file mode 100644
index 000000000..5e724c4a7
--- /dev/null
+++ b/scm/driver/github/testdata/repos_filter.json
@@ -0,0 +1,107 @@
+{
+  "total_count": 5,
+  "incomplete_results": false,
+  "items": [
+    {
+      "id": 508719340,
+      "node_id": "R_kgDOHlJw7A",
+      "name": "testRepo2",
+      "full_name": "user123/testRepo2",
+      "private": false,
+      "owner": {
+        "login": "user123",
+        "id": 103414561,
+        "node_id": "U_kgDOBin7IQ",
+        "avatar_url": "https://avatars.githubusercontent.com/u/103414561?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/user123",
+        "html_url": "https://github.com/user123",
+        "followers_url": "https://api.github.com/users/user123/followers",
+        "following_url": "https://api.github.com/users/user123/following{/other_user}",
+        "gists_url": "https://api.github.com/users/user123/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/user123/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/user123/subscriptions",
+        "organizations_url": "https://api.github.com/users/user123/orgs",
+        "repos_url": "https://api.github.com/users/user123/repos",
+        "events_url": "https://api.github.com/users/user123/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/user123/received_events",
+        "type": "User",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/user123/testRepo2",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/user123/testRepo2",
+      "forks_url": "https://api.github.com/repos/user123/testRepo2/forks",
+      "keys_url": "https://api.github.com/repos/user123/testRepo2/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/user123/testRepo2/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/user123/testRepo2/teams",
+      "hooks_url": "https://api.github.com/repos/user123/testRepo2/hooks",
+      "issue_events_url": "https://api.github.com/repos/user123/testRepo2/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/user123/testRepo2/events",
+      "assignees_url": "https://api.github.com/repos/user123/testRepo2/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/user123/testRepo2/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/user123/testRepo2/tags",
+      "blobs_url": "https://api.github.com/repos/user123/testRepo2/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/user123/testRepo2/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/user123/testRepo2/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/user123/testRepo2/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/user123/testRepo2/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/user123/testRepo2/languages",
+      "stargazers_url": "https://api.github.com/repos/user123/testRepo2/stargazers",
+      "contributors_url": "https://api.github.com/repos/user123/testRepo2/contributors",
+      "subscribers_url": "https://api.github.com/repos/user123/testRepo2/subscribers",
+      "subscription_url": "https://api.github.com/repos/user123/testRepo2/subscription",
+      "commits_url": "https://api.github.com/repos/user123/testRepo2/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/user123/testRepo2/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/user123/testRepo2/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/user123/testRepo2/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/user123/testRepo2/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/user123/testRepo2/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/user123/testRepo2/merges",
+      "archive_url": "https://api.github.com/repos/user123/testRepo2/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/user123/testRepo2/downloads",
+      "issues_url": "https://api.github.com/repos/user123/testRepo2/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/user123/testRepo2/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/user123/testRepo2/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/user123/testRepo2/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/user123/testRepo2/labels{/name}",
+      "releases_url": "https://api.github.com/repos/user123/testRepo2/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/user123/testRepo2/deployments",
+      "created_at": "2022-06-29T14:11:36Z",
+      "updated_at": "2023-06-27T07:10:05Z",
+      "pushed_at": "2023-06-07T05:36:36Z",
+      "git_url": "git://github.com/user123/testRepo2.git",
+      "ssh_url": "git@github.com:user123/testRepo2.git",
+      "clone_url": "https://github.com/user123/testRepo2.git",
+      "svn_url": "https://github.com/user123/testRepo2",
+      "homepage": null,
+      "size": 53,
+      "stargazers_count": 0,
+      "watchers_count": 0,
+      "language": null,
+      "has_issues": true,
+      "has_projects": true,
+      "has_downloads": true,
+      "has_wiki": true,
+      "has_pages": false,
+      "has_discussions": false,
+      "forks_count": 0,
+      "mirror_url": null,
+      "archived": false,
+      "disabled": false,
+      "open_issues_count": 2,
+      "license": null,
+      "allow_forking": true,
+      "is_template": false,
+      "web_commit_signoff_required": false,
+      "topics": [],
+      "visibility": "public",
+      "forks": 0,
+      "open_issues": 2,
+      "watchers": 0,
+      "default_branch": "main",
+      "score": 1.0
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/repos_filter.json.golden b/scm/driver/github/testdata/repos_filter.json.golden
new file mode 100644
index 000000000..bb164132f
--- /dev/null
+++ b/scm/driver/github/testdata/repos_filter.json.golden
@@ -0,0 +1,20 @@
+[
+    {
+        "ID": "508719340",
+        "Namespace": "user123",
+        "Name": "testRepo2",
+        "Perm": {
+            "Pull": false,
+            "Push": false,
+            "Admin": false
+        },
+        "Branch": "main",
+        "Private": false,
+        "Visibility": 1,
+        "Clone": "https://github.com/user123/testRepo2.git",
+        "CloneSSH": "git@github.com:user123/testRepo2.git",
+        "Link": "https://github.com/user123/testRepo2",
+        "Created": "2022-06-29T14:11:36Z",
+        "Updated": "2023-06-27T07:10:05Z"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/tags.json b/scm/driver/github/testdata/tags.json
index 8eee0f95a..eac19260c 100644
--- a/scm/driver/github/testdata/tags.json
+++ b/scm/driver/github/testdata/tags.json
@@ -1,11 +1,12 @@
 [
     {
-        "name": "v0.1",
-        "commit": {
-            "sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc",
-            "url": "https://api.github.com/repos/octocat/Hello-World/commits/c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc"
-        },
-        "zipball_url": "https://github.com/octocat/Hello-World/zipball/v0.1",
-        "tarball_url": "https://github.com/octocat/Hello-World/tarball/v0.1"
-    }
+        "ref": "refs/tags/v1",
+        "node_id": "REF_kwDOGvVo46xyZWZzL3RhZ3MvdjE",
+        "url": "https://api.github.com/repos/vitsafronovici/myrepo/git/refs/tags/v1",
+        "object": {
+            "sha": "85fd27bdfd87b962d1ed4a742613cbfacd5c3743",
+            "type": "tag",
+            "url": "https://api.github.com/repos/vitsafronovici/myrepo/git/tags/85fd27bdfd87b962d1ed4a742613cbfacd5c3743"
+        }
+    }    
 ]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/tags.json.golden b/scm/driver/github/testdata/tags.json.golden
index de3d2df17..c533fd7ee 100644
--- a/scm/driver/github/testdata/tags.json.golden
+++ b/scm/driver/github/testdata/tags.json.golden
@@ -1,7 +1,7 @@
 [
     {
-        "Name": "v0.1",
-        "Path": "refs/tags/v0.1",
-        "Sha": "c5b97d5ae6c19d5c5df71a34c7fbeeda2479ccbc"
+        "Name": "v1",
+        "Path": "refs/tags/v1",
+        "Sha": "85fd27bdfd87b962d1ed4a742613cbfacd5c3743"
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/github/testdata/webhooks/pipeline_hook.json b/scm/driver/github/testdata/webhooks/pipeline_hook.json
new file mode 100644
index 000000000..3a7fd0c92
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pipeline_hook.json
@@ -0,0 +1,389 @@
+{
+  "action": "in_progress",
+  "workflow_run": {
+    "id": 14030131450,
+    "name": "CI",
+    "node_id": "WFR_kwLOE_pLVc8AAAADRELQ-g",
+    "head_branch": "shivamnegi94-patch-27",
+    "head_sha": "f4229db4b70a8dadeb687f5ccce5fd327c04461a",
+    "path": ".github/workflows/blank.yml",
+    "display_title": "Update NewSre_v1.yaml",
+    "run_number": 2,
+    "event": "pull_request",
+    "status": "completed",
+    "conclusion": "success",
+    "workflow_id": 151617408,
+    "check_suite_id": 36111489428,
+    "check_suite_node_id": "CS_kwDOE_pLVc8AAAAIaGmZlA",
+    "url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450",
+    "html_url": "https://github.com/shivamnegi94/testrepo/actions/runs/14030131450",
+    "pull_requests": [
+      {
+        "url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/164",
+        "id": 2412967773,
+        "number": 164,
+        "head": {
+          "ref": "shivamnegi94-patch-27",
+          "sha": "f4229db4b70a8dadeb687f5ccce5fd327c04461a",
+          "repo": {
+            "id": 335170389,
+            "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+            "name": "testrepo"
+          }
+        },
+        "base": {
+          "ref": "main",
+          "sha": "ded75dda298abfbf9f1900092e92b43f23bdd6d2",
+          "repo": {
+            "id": 335170389,
+            "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+            "name": "testrepo"
+          }
+        }
+      }
+    ],
+    "created_at": "2025-03-24T08:03:42Z",
+    "updated_at": "2025-03-24T08:03:46Z",
+    "actor": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/shivamnegi94",
+      "html_url": "https://github.com/shivamnegi94",
+      "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+      "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+      "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+      "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+      "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+      "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+      "type": "User",
+      "user_view_type": "public",
+      "site_admin": false
+    },
+    "run_attempt": 1,
+    "referenced_workflows": [
+
+    ],
+    "run_started_at": "2025-03-24T08:03:42Z",
+    "triggering_actor": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/shivamnegi94",
+      "html_url": "https://github.com/shivamnegi94",
+      "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+      "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+      "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+      "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+      "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+      "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+      "type": "User",
+      "user_view_type": "public",
+      "site_admin": false
+    },
+    "jobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450/jobs",
+    "logs_url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450/logs",
+    "check_suite_url": "https://api.github.com/repos/shivamnegi94/testrepo/check-suites/36111489428",
+    "artifacts_url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450/artifacts",
+    "cancel_url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450/cancel",
+    "rerun_url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450/rerun",
+    "previous_attempt_url": null,
+    "workflow_url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/workflows/151617408",
+    "head_commit": {
+      "id": "f4229db4b70a8dadeb687f5ccce5fd327c04461a",
+      "tree_id": "36698bb567243f3d104f398e3b6e1a339570d3dc",
+      "message": "Update NewSre_v1.yaml",
+      "timestamp": "2025-03-24T08:03:33Z",
+      "author": {
+        "name": "Shivam Negi",
+        "email": "shivam.negi@harness.io"
+      },
+      "committer": {
+        "name": "GitHub",
+        "email": "noreply@github.com"
+      }
+    },
+    "repository": {
+      "id": 335170389,
+      "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+      "name": "testrepo",
+      "full_name": "shivamnegi94/testrepo",
+      "private": false,
+      "owner": {
+        "login": "shivamnegi94",
+        "id": 58415634,
+        "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+        "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/shivamnegi94",
+        "html_url": "https://github.com/shivamnegi94",
+        "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+        "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+        "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+        "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+        "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+        "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+        "type": "User",
+        "user_view_type": "public",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/shivamnegi94/testrepo",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+      "forks_url": "https://api.github.com/repos/shivamnegi94/testrepo/forks",
+      "keys_url": "https://api.github.com/repos/shivamnegi94/testrepo/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/shivamnegi94/testrepo/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/shivamnegi94/testrepo/teams",
+      "hooks_url": "https://api.github.com/repos/shivamnegi94/testrepo/hooks",
+      "issue_events_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/shivamnegi94/testrepo/events",
+      "assignees_url": "https://api.github.com/repos/shivamnegi94/testrepo/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/shivamnegi94/testrepo/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/tags",
+      "blobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/shivamnegi94/testrepo/languages",
+      "stargazers_url": "https://api.github.com/repos/shivamnegi94/testrepo/stargazers",
+      "contributors_url": "https://api.github.com/repos/shivamnegi94/testrepo/contributors",
+      "subscribers_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscribers",
+      "subscription_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscription",
+      "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/shivamnegi94/testrepo/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/shivamnegi94/testrepo/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/shivamnegi94/testrepo/merges",
+      "archive_url": "https://api.github.com/repos/shivamnegi94/testrepo/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/shivamnegi94/testrepo/downloads",
+      "issues_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/shivamnegi94/testrepo/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/shivamnegi94/testrepo/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/shivamnegi94/testrepo/labels{/name}",
+      "releases_url": "https://api.github.com/repos/shivamnegi94/testrepo/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/shivamnegi94/testrepo/deployments"
+    },
+    "head_repository": {
+      "id": 335170389,
+      "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+      "name": "testrepo",
+      "full_name": "shivamnegi94/testrepo",
+      "private": false,
+      "owner": {
+        "login": "shivamnegi94",
+        "id": 58415634,
+        "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+        "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/shivamnegi94",
+        "html_url": "https://github.com/shivamnegi94",
+        "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+        "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+        "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+        "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+        "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+        "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+        "type": "User",
+        "user_view_type": "public",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/shivamnegi94/testrepo",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+      "forks_url": "https://api.github.com/repos/shivamnegi94/testrepo/forks",
+      "keys_url": "https://api.github.com/repos/shivamnegi94/testrepo/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/shivamnegi94/testrepo/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/shivamnegi94/testrepo/teams",
+      "hooks_url": "https://api.github.com/repos/shivamnegi94/testrepo/hooks",
+      "issue_events_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/shivamnegi94/testrepo/events",
+      "assignees_url": "https://api.github.com/repos/shivamnegi94/testrepo/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/shivamnegi94/testrepo/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/tags",
+      "blobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/shivamnegi94/testrepo/languages",
+      "stargazers_url": "https://api.github.com/repos/shivamnegi94/testrepo/stargazers",
+      "contributors_url": "https://api.github.com/repos/shivamnegi94/testrepo/contributors",
+      "subscribers_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscribers",
+      "subscription_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscription",
+      "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/shivamnegi94/testrepo/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/shivamnegi94/testrepo/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/shivamnegi94/testrepo/merges",
+      "archive_url": "https://api.github.com/repos/shivamnegi94/testrepo/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/shivamnegi94/testrepo/downloads",
+      "issues_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/shivamnegi94/testrepo/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/shivamnegi94/testrepo/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/shivamnegi94/testrepo/labels{/name}",
+      "releases_url": "https://api.github.com/repos/shivamnegi94/testrepo/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/shivamnegi94/testrepo/deployments"
+    }
+  },
+  "workflow": {
+    "id": 151617408,
+    "node_id": "W_kwDOE_pLVc4JCX-A",
+    "name": "CI",
+    "path": ".github/workflows/blank.yml",
+    "state": "active",
+    "created_at": "2025-03-24T08:01:34.000Z",
+    "updated_at": "2025-03-24T08:01:34.000Z",
+    "url": "https://api.github.com/repos/shivamnegi94/testrepo/actions/workflows/151617408",
+    "html_url": "https://github.com/shivamnegi94/testrepo/blob/main/.github/workflows/blank.yml",
+    "badge_url": "https://github.com/shivamnegi94/testrepo/workflows/CI/badge.svg"
+  },
+  "repository": {
+    "id": 335170389,
+    "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+    "name": "testrepo",
+    "full_name": "shivamnegi94/testrepo",
+    "private": false,
+    "owner": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/shivamnegi94",
+      "html_url": "https://github.com/shivamnegi94",
+      "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+      "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+      "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+      "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+      "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+      "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+      "type": "User",
+      "user_view_type": "public",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/shivamnegi94/testrepo",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+    "forks_url": "https://api.github.com/repos/shivamnegi94/testrepo/forks",
+    "keys_url": "https://api.github.com/repos/shivamnegi94/testrepo/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/shivamnegi94/testrepo/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/shivamnegi94/testrepo/teams",
+    "hooks_url": "https://api.github.com/repos/shivamnegi94/testrepo/hooks",
+    "issue_events_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/shivamnegi94/testrepo/events",
+    "assignees_url": "https://api.github.com/repos/shivamnegi94/testrepo/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/shivamnegi94/testrepo/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/tags",
+    "blobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/shivamnegi94/testrepo/languages",
+    "stargazers_url": "https://api.github.com/repos/shivamnegi94/testrepo/stargazers",
+    "contributors_url": "https://api.github.com/repos/shivamnegi94/testrepo/contributors",
+    "subscribers_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscribers",
+    "subscription_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscription",
+    "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/shivamnegi94/testrepo/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/shivamnegi94/testrepo/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/shivamnegi94/testrepo/merges",
+    "archive_url": "https://api.github.com/repos/shivamnegi94/testrepo/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/shivamnegi94/testrepo/downloads",
+    "issues_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/shivamnegi94/testrepo/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/shivamnegi94/testrepo/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/shivamnegi94/testrepo/labels{/name}",
+    "releases_url": "https://api.github.com/repos/shivamnegi94/testrepo/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/shivamnegi94/testrepo/deployments",
+    "created_at": "2021-02-02T04:41:22Z",
+    "updated_at": "2025-03-24T08:01:37Z",
+    "pushed_at": "2025-03-24T08:03:33Z",
+    "git_url": "git://github.com/shivamnegi94/testrepo.git",
+    "ssh_url": "git@github.com:shivamnegi94/testrepo.git",
+    "clone_url": "https://github.com/shivamnegi94/testrepo.git",
+    "svn_url": "https://github.com/shivamnegi94/testrepo",
+    "homepage": null,
+    "size": 209,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "has_discussions": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 56,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "visibility": "public",
+    "forks": 0,
+    "open_issues": 56,
+    "watchers": 0,
+    "default_branch": "main"
+  },
+  "sender": {
+    "login": "shivamnegi94",
+    "id": 58415634,
+    "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+    "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/shivamnegi94",
+    "html_url": "https://github.com/shivamnegi94",
+    "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+    "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+    "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+    "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+    "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+    "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+    "type": "User",
+    "user_view_type": "public",
+    "site_admin": false
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/github/testdata/webhooks/pipeline_hook.json.golden b/scm/driver/github/testdata/webhooks/pipeline_hook.json.golden
new file mode 100644
index 000000000..b56869de0
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pipeline_hook.json.golden
@@ -0,0 +1,50 @@
+{
+  "Repo": {
+    "ID": "335170389",
+    "Namespace": "shivamnegi94",
+    "Name": "testrepo",
+    "Clone": "https://github.com/shivamnegi94/testrepo.git",
+    "CloneSSH": "git@github.com:shivamnegi94/testrepo.git",
+    "Link": "https://github.com/shivamnegi94/testrepo",
+    "Branch": "main",
+    "Created": "2021-02-02T04:41:22Z",
+    "Updated": "2025-03-24T08:01:37Z",
+    "Private": false,
+    "Visibility": 1,
+    "Perm": {}
+  },
+  "Commit": {
+    "Sha": "f4229db4b70a8dadeb687f5ccce5fd327c04461a",
+    "Message": "Update NewSre_v1.yaml",
+    "Author": {
+      "Name": "Shivam Negi",
+      "Email": "shivam.negi@harness.io"
+    },
+    "Committer": {
+      "Name": "GitHub",
+      "Email": "noreply@github.com"
+    }
+  },
+  "Execution": {
+    "Number": 2,
+    "Status": "success",
+    "Created": "2025-03-24T08:03:42Z",
+    "URL": "https://api.github.com/repos/shivamnegi94/testrepo/actions/runs/14030131450" 
+  },
+  "Sender": {
+    "Login": "shivamnegi94",
+    "Avatar": "https://avatars.githubusercontent.com/u/58415634?v=4"
+  },
+  "PullRequest": {
+    "Number": 164,
+    "Sha": "f4229db4b70a8dadeb687f5ccce5fd327c04461a",
+    "Ref": "shivamnegi94-patch-27",
+    "Source": "shivamnegi94-patch-27",
+    "Target": "main",
+    "Link": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/164",
+    "Created": "2025-03-24T08:03:42Z",
+    "Draft": false,
+    "Closed": false,
+    "Merged": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/pr_ready_for_review.json b/scm/driver/github/testdata/webhooks/pr_ready_for_review.json
new file mode 100644
index 000000000..b9a7e1fdc
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pr_ready_for_review.json
@@ -0,0 +1,528 @@
+{
+  "action": "ready_for_review",
+  "number": 38,
+  "pull_request": {
+    "url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/38",
+    "id": 1492580810,
+    "node_id": "PR_kwDOHbOleM5Y9vnK",
+    "html_url": "https://github.com/wings-software/meet-git-sync-test/pull/38",
+    "diff_url": "https://github.com/wings-software/meet-git-sync-test/pull/38.diff",
+    "patch_url": "https://github.com/wings-software/meet-git-sync-test/pull/38.patch",
+    "issue_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/38",
+    "number": 38,
+    "state": "open",
+    "locked": false,
+    "title": "Update dgdggd.me",
+    "user": {
+      "login": "rathodmeetsatish",
+      "id": 84321134,
+      "node_id": "MDQ6VXNlcjg0MzIxMTM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/84321134?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/rathodmeetsatish",
+      "html_url": "https://github.com/rathodmeetsatish",
+      "followers_url": "https://api.github.com/users/rathodmeetsatish/followers",
+      "following_url": "https://api.github.com/users/rathodmeetsatish/following{/other_user}",
+      "gists_url": "https://api.github.com/users/rathodmeetsatish/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/rathodmeetsatish/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/rathodmeetsatish/subscriptions",
+      "organizations_url": "https://api.github.com/users/rathodmeetsatish/orgs",
+      "repos_url": "https://api.github.com/users/rathodmeetsatish/repos",
+      "events_url": "https://api.github.com/users/rathodmeetsatish/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/rathodmeetsatish/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "body": null,
+    "created_at": "2023-08-28T19:15:53Z",
+    "updated_at": "2023-08-28T19:16:06Z",
+    "closed_at": null,
+    "merged_at": null,
+    "merge_commit_sha": "d427e98e038a88f10e57d48c34f287e3ad20a0bf",
+    "assignee": null,
+    "assignees": [
+
+    ],
+    "requested_reviewers": [
+
+    ],
+    "requested_teams": [
+
+    ],
+    "labels": [
+
+    ],
+    "milestone": null,
+    "draft": false,
+    "commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/38/commits",
+    "review_comments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/38/comments",
+    "review_comment_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/comments{/number}",
+    "comments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/38/comments",
+    "statuses_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/statuses/212015fb011c49a2913c4a1a860ce07c0821c362",
+    "head": {
+      "label": "wings-software:rathodmeetsatish-patch-25",
+      "ref": "rathodmeetsatish-patch-25",
+      "sha": "212015fb011c49a2913c4a1a860ce07c0821c362",
+      "user": {
+        "login": "wings-software",
+        "id": 18273000,
+        "node_id": "MDEyOk9yZ2FuaXphdGlvbjE4MjczMDAw",
+        "avatar_url": "https://avatars.githubusercontent.com/u/18273000?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/wings-software",
+        "html_url": "https://github.com/wings-software",
+        "followers_url": "https://api.github.com/users/wings-software/followers",
+        "following_url": "https://api.github.com/users/wings-software/following{/other_user}",
+        "gists_url": "https://api.github.com/users/wings-software/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/wings-software/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/wings-software/subscriptions",
+        "organizations_url": "https://api.github.com/users/wings-software/orgs",
+        "repos_url": "https://api.github.com/users/wings-software/repos",
+        "events_url": "https://api.github.com/users/wings-software/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/wings-software/received_events",
+        "type": "Organization",
+        "site_admin": false
+      },
+      "repo": {
+        "id": 498312568,
+        "node_id": "R_kgDOHbOleA",
+        "name": "meet-git-sync-test",
+        "full_name": "wings-software/meet-git-sync-test",
+        "private": true,
+        "owner": {
+          "login": "wings-software",
+          "id": 18273000,
+          "node_id": "MDEyOk9yZ2FuaXphdGlvbjE4MjczMDAw",
+          "avatar_url": "https://avatars.githubusercontent.com/u/18273000?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/wings-software",
+          "html_url": "https://github.com/wings-software",
+          "followers_url": "https://api.github.com/users/wings-software/followers",
+          "following_url": "https://api.github.com/users/wings-software/following{/other_user}",
+          "gists_url": "https://api.github.com/users/wings-software/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/wings-software/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/wings-software/subscriptions",
+          "organizations_url": "https://api.github.com/users/wings-software/orgs",
+          "repos_url": "https://api.github.com/users/wings-software/repos",
+          "events_url": "https://api.github.com/users/wings-software/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/wings-software/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/wings-software/meet-git-sync-test",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/wings-software/meet-git-sync-test",
+        "forks_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/forks",
+        "keys_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/teams",
+        "hooks_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/hooks",
+        "issue_events_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/events",
+        "assignees_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/tags",
+        "blobs_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/languages",
+        "stargazers_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/stargazers",
+        "contributors_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/contributors",
+        "subscribers_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/subscribers",
+        "subscription_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/subscription",
+        "commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/merges",
+        "archive_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/downloads",
+        "issues_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/labels{/name}",
+        "releases_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/deployments",
+        "created_at": "2022-05-31T11:47:10Z",
+        "updated_at": "2022-05-31T11:47:35Z",
+        "pushed_at": "2023-08-28T19:15:54Z",
+        "git_url": "git://github.com/wings-software/meet-git-sync-test.git",
+        "ssh_url": "git@github.com:wings-software/meet-git-sync-test.git",
+        "clone_url": "https://github.com/wings-software/meet-git-sync-test.git",
+        "svn_url": "https://github.com/wings-software/meet-git-sync-test",
+        "homepage": null,
+        "size": 171,
+        "stargazers_count": 0,
+        "watchers_count": 0,
+        "language": "Roff",
+        "has_issues": true,
+        "has_projects": true,
+        "has_downloads": true,
+        "has_wiki": true,
+        "has_pages": false,
+        "has_discussions": false,
+        "forks_count": 0,
+        "mirror_url": null,
+        "archived": false,
+        "disabled": false,
+        "open_issues_count": 23,
+        "license": null,
+        "allow_forking": false,
+        "is_template": false,
+        "web_commit_signoff_required": false,
+        "topics": [
+
+        ],
+        "visibility": "private",
+        "forks": 0,
+        "open_issues": 23,
+        "watchers": 0,
+        "default_branch": "main",
+        "allow_squash_merge": true,
+        "allow_merge_commit": true,
+        "allow_rebase_merge": true,
+        "allow_auto_merge": false,
+        "delete_branch_on_merge": false,
+        "allow_update_branch": false,
+        "use_squash_pr_title_as_default": false,
+        "squash_merge_commit_message": "COMMIT_MESSAGES",
+        "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+        "merge_commit_message": "PR_TITLE",
+        "merge_commit_title": "MERGE_MESSAGE"
+      }
+    },
+    "base": {
+      "label": "wings-software:main",
+      "ref": "main",
+      "sha": "8b8d2b3e6a2ee6df108c8429e86c9a750e728f9d",
+      "user": {
+        "login": "wings-software",
+        "id": 18273000,
+        "node_id": "MDEyOk9yZ2FuaXphdGlvbjE4MjczMDAw",
+        "avatar_url": "https://avatars.githubusercontent.com/u/18273000?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/wings-software",
+        "html_url": "https://github.com/wings-software",
+        "followers_url": "https://api.github.com/users/wings-software/followers",
+        "following_url": "https://api.github.com/users/wings-software/following{/other_user}",
+        "gists_url": "https://api.github.com/users/wings-software/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/wings-software/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/wings-software/subscriptions",
+        "organizations_url": "https://api.github.com/users/wings-software/orgs",
+        "repos_url": "https://api.github.com/users/wings-software/repos",
+        "events_url": "https://api.github.com/users/wings-software/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/wings-software/received_events",
+        "type": "Organization",
+        "site_admin": false
+      },
+      "repo": {
+        "id": 498312568,
+        "node_id": "R_kgDOHbOleA",
+        "name": "meet-git-sync-test",
+        "full_name": "wings-software/meet-git-sync-test",
+        "private": true,
+        "owner": {
+          "login": "wings-software",
+          "id": 18273000,
+          "node_id": "MDEyOk9yZ2FuaXphdGlvbjE4MjczMDAw",
+          "avatar_url": "https://avatars.githubusercontent.com/u/18273000?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/wings-software",
+          "html_url": "https://github.com/wings-software",
+          "followers_url": "https://api.github.com/users/wings-software/followers",
+          "following_url": "https://api.github.com/users/wings-software/following{/other_user}",
+          "gists_url": "https://api.github.com/users/wings-software/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/wings-software/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/wings-software/subscriptions",
+          "organizations_url": "https://api.github.com/users/wings-software/orgs",
+          "repos_url": "https://api.github.com/users/wings-software/repos",
+          "events_url": "https://api.github.com/users/wings-software/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/wings-software/received_events",
+          "type": "Organization",
+          "site_admin": false
+        },
+        "html_url": "https://github.com/wings-software/meet-git-sync-test",
+        "description": null,
+        "fork": false,
+        "url": "https://api.github.com/repos/wings-software/meet-git-sync-test",
+        "forks_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/forks",
+        "keys_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/keys{/key_id}",
+        "collaborators_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/collaborators{/collaborator}",
+        "teams_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/teams",
+        "hooks_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/hooks",
+        "issue_events_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/events{/number}",
+        "events_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/events",
+        "assignees_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/assignees{/user}",
+        "branches_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/branches{/branch}",
+        "tags_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/tags",
+        "blobs_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/blobs{/sha}",
+        "git_tags_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/tags{/sha}",
+        "git_refs_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/refs{/sha}",
+        "trees_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/trees{/sha}",
+        "statuses_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/statuses/{sha}",
+        "languages_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/languages",
+        "stargazers_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/stargazers",
+        "contributors_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/contributors",
+        "subscribers_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/subscribers",
+        "subscription_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/subscription",
+        "commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/commits{/sha}",
+        "git_commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/commits{/sha}",
+        "comments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/comments{/number}",
+        "issue_comment_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/comments{/number}",
+        "contents_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/contents/{+path}",
+        "compare_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/compare/{base}...{head}",
+        "merges_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/merges",
+        "archive_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/{archive_format}{/ref}",
+        "downloads_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/downloads",
+        "issues_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues{/number}",
+        "pulls_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls{/number}",
+        "milestones_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/milestones{/number}",
+        "notifications_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/notifications{?since,all,participating}",
+        "labels_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/labels{/name}",
+        "releases_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/releases{/id}",
+        "deployments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/deployments",
+        "created_at": "2022-05-31T11:47:10Z",
+        "updated_at": "2022-05-31T11:47:35Z",
+        "pushed_at": "2023-08-28T19:15:54Z",
+        "git_url": "git://github.com/wings-software/meet-git-sync-test.git",
+        "ssh_url": "git@github.com:wings-software/meet-git-sync-test.git",
+        "clone_url": "https://github.com/wings-software/meet-git-sync-test.git",
+        "svn_url": "https://github.com/wings-software/meet-git-sync-test",
+        "homepage": null,
+        "size": 171,
+        "stargazers_count": 0,
+        "watchers_count": 0,
+        "language": "Roff",
+        "has_issues": true,
+        "has_projects": true,
+        "has_downloads": true,
+        "has_wiki": true,
+        "has_pages": false,
+        "has_discussions": false,
+        "forks_count": 0,
+        "mirror_url": null,
+        "archived": false,
+        "disabled": false,
+        "open_issues_count": 23,
+        "license": null,
+        "allow_forking": false,
+        "is_template": false,
+        "web_commit_signoff_required": false,
+        "topics": [
+
+        ],
+        "visibility": "private",
+        "forks": 0,
+        "open_issues": 23,
+        "watchers": 0,
+        "default_branch": "main",
+        "allow_squash_merge": true,
+        "allow_merge_commit": true,
+        "allow_rebase_merge": true,
+        "allow_auto_merge": false,
+        "delete_branch_on_merge": false,
+        "allow_update_branch": false,
+        "use_squash_pr_title_as_default": false,
+        "squash_merge_commit_message": "COMMIT_MESSAGES",
+        "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+        "merge_commit_message": "PR_TITLE",
+        "merge_commit_title": "MERGE_MESSAGE"
+      }
+    },
+    "_links": {
+      "self": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/38"
+      },
+      "html": {
+        "href": "https://github.com/wings-software/meet-git-sync-test/pull/38"
+      },
+      "issue": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/38"
+      },
+      "comments": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/38/comments"
+      },
+      "review_comments": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/38/comments"
+      },
+      "review_comment": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/comments{/number}"
+      },
+      "commits": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls/38/commits"
+      },
+      "statuses": {
+        "href": "https://api.github.com/repos/wings-software/meet-git-sync-test/statuses/212015fb011c49a2913c4a1a860ce07c0821c362"
+      }
+    },
+    "author_association": "COLLABORATOR",
+    "auto_merge": null,
+    "active_lock_reason": null,
+    "merged": false,
+    "mergeable": true,
+    "rebaseable": true,
+    "mergeable_state": "clean",
+    "merged_by": null,
+    "comments": 0,
+    "review_comments": 0,
+    "maintainer_can_modify": false,
+    "commits": 1,
+    "additions": 4,
+    "deletions": 1,
+    "changed_files": 1
+  },
+  "repository": {
+    "id": 498312568,
+    "node_id": "R_kgDOHbOleA",
+    "name": "meet-git-sync-test",
+    "full_name": "wings-software/meet-git-sync-test",
+    "private": true,
+    "owner": {
+      "login": "wings-software",
+      "id": 18273000,
+      "node_id": "MDEyOk9yZ2FuaXphdGlvbjE4MjczMDAw",
+      "avatar_url": "https://avatars.githubusercontent.com/u/18273000?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/wings-software",
+      "html_url": "https://github.com/wings-software",
+      "followers_url": "https://api.github.com/users/wings-software/followers",
+      "following_url": "https://api.github.com/users/wings-software/following{/other_user}",
+      "gists_url": "https://api.github.com/users/wings-software/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/wings-software/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/wings-software/subscriptions",
+      "organizations_url": "https://api.github.com/users/wings-software/orgs",
+      "repos_url": "https://api.github.com/users/wings-software/repos",
+      "events_url": "https://api.github.com/users/wings-software/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/wings-software/received_events",
+      "type": "Organization",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/wings-software/meet-git-sync-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/wings-software/meet-git-sync-test",
+    "forks_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/forks",
+    "keys_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/teams",
+    "hooks_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/events",
+    "assignees_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/tags",
+    "blobs_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/languages",
+    "stargazers_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/subscription",
+    "commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/merges",
+    "archive_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/downloads",
+    "issues_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/wings-software/meet-git-sync-test/deployments",
+    "created_at": "2022-05-31T11:47:10Z",
+    "updated_at": "2022-05-31T11:47:35Z",
+    "pushed_at": "2023-08-28T19:15:54Z",
+    "git_url": "git://github.com/wings-software/meet-git-sync-test.git",
+    "ssh_url": "git@github.com:wings-software/meet-git-sync-test.git",
+    "clone_url": "https://github.com/wings-software/meet-git-sync-test.git",
+    "svn_url": "https://github.com/wings-software/meet-git-sync-test",
+    "homepage": null,
+    "size": 171,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": "Roff",
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "has_discussions": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 23,
+    "license": null,
+    "allow_forking": false,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "visibility": "private",
+    "forks": 0,
+    "open_issues": 23,
+    "watchers": 0,
+    "default_branch": "main"
+  },
+  "organization": {
+    "login": "wings-software",
+    "id": 18273000,
+    "node_id": "MDEyOk9yZ2FuaXphdGlvbjE4MjczMDAw",
+    "url": "https://api.github.com/orgs/wings-software",
+    "repos_url": "https://api.github.com/orgs/wings-software/repos",
+    "events_url": "https://api.github.com/orgs/wings-software/events",
+    "hooks_url": "https://api.github.com/orgs/wings-software/hooks",
+    "issues_url": "https://api.github.com/orgs/wings-software/issues",
+    "members_url": "https://api.github.com/orgs/wings-software/members{/member}",
+    "public_members_url": "https://api.github.com/orgs/wings-software/public_members{/member}",
+    "avatar_url": "https://avatars.githubusercontent.com/u/18273000?v=4",
+    "description": ""
+  },
+  "enterprise": {
+    "id": 6657,
+    "slug": "harness",
+    "name": "Harness",
+    "node_id": "MDEwOkVudGVycHJpc2U2NjU3",
+    "avatar_url": "https://avatars.githubusercontent.com/b/6657?v=4",
+    "description": "",
+    "website_url": "https://harness.io",
+    "html_url": "https://github.com/enterprises/harness",
+    "created_at": "2021-04-13T17:56:36Z",
+    "updated_at": "2023-02-06T20:48:35Z"
+  },
+  "sender": {
+    "login": "rathodmeetsatish",
+    "id": 84321134,
+    "node_id": "MDQ6VXNlcjg0MzIxMTM0",
+    "avatar_url": "https://avatars.githubusercontent.com/u/84321134?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/rathodmeetsatish",
+    "html_url": "https://github.com/rathodmeetsatish",
+    "followers_url": "https://api.github.com/users/rathodmeetsatish/followers",
+    "following_url": "https://api.github.com/users/rathodmeetsatish/following{/other_user}",
+    "gists_url": "https://api.github.com/users/rathodmeetsatish/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/rathodmeetsatish/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/rathodmeetsatish/subscriptions",
+    "organizations_url": "https://api.github.com/users/rathodmeetsatish/orgs",
+    "repos_url": "https://api.github.com/users/rathodmeetsatish/repos",
+    "events_url": "https://api.github.com/users/rathodmeetsatish/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/rathodmeetsatish/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/pr_ready_for_review.json.golden b/scm/driver/github/testdata/webhooks/pr_ready_for_review.json.golden
new file mode 100644
index 000000000..653a28b1e
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pr_ready_for_review.json.golden
@@ -0,0 +1,55 @@
+{
+  "Action": "review_ready",
+  "Repo": {
+    "ID": "498312568",
+    "Namespace": "wings-software",
+    "Name": "meet-git-sync-test",
+    "Perm": null,
+    "Branch": "main",
+    "Private": true,
+    "Visibility": 3,
+    "Clone": "https://github.com/wings-software/meet-git-sync-test.git",
+    "CloneSSH": "git@github.com:wings-software/meet-git-sync-test.git",
+    "Link": "https://github.com/wings-software/meet-git-sync-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 38,
+    "Title": "Update dgdggd.me",
+    "Body": null,
+    "Sha": "212015fb011c49a2913c4a1a860ce07c0821c362",
+    "Ref": "refs/pull/38/head",
+    "Target": "main",
+    "Source": "rathodmeetsatish-patch-25",
+    "Fork": "wings-software/meet-git-sync-test",
+    "Link": "https://github.com/wings-software/meet-git-sync-test/pull/38",
+    "Diff": "https://github.com/wings-software/meet-git-sync-test/pull/38.diff",
+    "Closed": null,
+    "Merged": null,
+    "Base": {
+      "Sha": "8b8d2b3e6a2ee6df108c8429e86c9a750e728f9d",
+      "Path": "refs/heads/main",
+      "Name": "main"
+    },
+    "Head": {
+        "Sha": "212015fb011c49a2913c4a1a860ce07c0821c362",
+        "Path": "refs/heads/rathodmeetsatish-patch-25",
+        "Name": "rathodmeetsatish-patch-25"
+    },
+    "Author": {
+      "Login": "rathodmeetsatish",
+      "Name": "",
+      "Email": "",
+      "Avatar": "https://avatars.githubusercontent.com/u/84321134?v=4"
+    },
+    "Created": "2023-08-28T19:15:53Z",
+    "Updated": "2023-08-28T19:16:06Z"
+  },
+  "Sender": {
+    "Login": "rathodmeetsatish",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/84321134?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/pr_review_edited.json b/scm/driver/github/testdata/webhooks/pr_review_edited.json
new file mode 100644
index 000000000..3d2bfb406
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pr_review_edited.json
@@ -0,0 +1,541 @@
+{
+    "action": "edited",
+    "review": {
+      "id": 3065773662,
+      "node_id": "PRR_kwDOE_pLVc62u_5e",
+      "user": {
+        "login": "shivamnegi94",
+        "id": 58415634,
+        "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+        "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/shivamnegi94",
+        "html_url": "https://github.com/shivamnegi94",
+        "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+        "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+        "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+        "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+        "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+        "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+        "type": "User",
+        "user_view_type": "public",
+        "site_admin": false
+      },
+      "body": "Gfhdddd",
+      "commit_id": "9dc1b02c9b25bac779bdb2f31034f3f2421aa71d",
+      "submitted_at": "2025-07-29T06:49:39Z",
+      "state": "commented",
+      "html_url": "https://github.com/shivamnegi94/testrepo/pull/206#pullrequestreview-3065773662",
+      "pull_request_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206",
+      "author_association": "OWNER",
+      "_links": {
+        "html": {
+          "href": "https://github.com/shivamnegi94/testrepo/pull/206#pullrequestreview-3065773662"
+        },
+        "pull_request": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206"
+        }
+      }
+    },
+    "pull_request": {
+      "url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206",
+      "id": 2702958367,
+      "node_id": "PR_kwDOE_pLVc6hG98f",
+      "html_url": "https://github.com/shivamnegi94/testrepo/pull/206",
+      "diff_url": "https://github.com/shivamnegi94/testrepo/pull/206.diff",
+      "patch_url": "https://github.com/shivamnegi94/testrepo/pull/206.patch",
+      "issue_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/206",
+      "number": 206,
+      "state": "open",
+      "locked": false,
+      "title": "Update sacsac.yaml",
+      "user": {
+        "login": "utkarshchoubey-harness",
+        "id": 101091340,
+        "node_id": "U_kgDOBgaIDA",
+        "avatar_url": "https://avatars.githubusercontent.com/u/101091340?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/utkarshchoubey-harness",
+        "html_url": "https://github.com/utkarshchoubey-harness",
+        "followers_url": "https://api.github.com/users/utkarshchoubey-harness/followers",
+        "following_url": "https://api.github.com/users/utkarshchoubey-harness/following{/other_user}",
+        "gists_url": "https://api.github.com/users/utkarshchoubey-harness/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/utkarshchoubey-harness/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/utkarshchoubey-harness/subscriptions",
+        "organizations_url": "https://api.github.com/users/utkarshchoubey-harness/orgs",
+        "repos_url": "https://api.github.com/users/utkarshchoubey-harness/repos",
+        "events_url": "https://api.github.com/users/utkarshchoubey-harness/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/utkarshchoubey-harness/received_events",
+        "type": "User",
+        "user_view_type": "public",
+        "site_admin": false
+      },
+      "body": null,
+      "created_at": "2025-07-29T06:44:39Z",
+      "updated_at": "2025-07-29T06:50:15Z",
+      "closed_at": null,
+      "merged_at": null,
+      "merge_commit_sha": "10ff44855ecbf2933d09f92939d9fc6a205d335b",
+      "assignee": null,
+      "assignees": [
+  
+      ],
+      "requested_reviewers": [
+  
+      ],
+      "requested_teams": [
+  
+      ],
+      "labels": [
+  
+      ],
+      "milestone": null,
+      "draft": false,
+      "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206/commits",
+      "review_comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206/comments",
+      "review_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/comments{/number}",
+      "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/206/comments",
+      "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/9dc1b02c9b25bac779bdb2f31034f3f2421aa71d",
+      "head": {
+        "label": "shivamnegi94:ut",
+        "ref": "ut",
+        "sha": "9dc1b02c9b25bac779bdb2f31034f3f2421aa71d",
+        "user": {
+          "login": "shivamnegi94",
+          "id": 58415634,
+          "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+          "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/shivamnegi94",
+          "html_url": "https://github.com/shivamnegi94",
+          "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+          "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+          "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+          "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+          "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+          "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+          "type": "User",
+          "user_view_type": "public",
+          "site_admin": false
+        },
+        "repo": {
+          "id": 335170389,
+          "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+          "name": "testrepo",
+          "full_name": "shivamnegi94/testrepo",
+          "private": false,
+          "owner": {
+            "login": "shivamnegi94",
+            "id": 58415634,
+            "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+            "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+            "gravatar_id": "",
+            "url": "https://api.github.com/users/shivamnegi94",
+            "html_url": "https://github.com/shivamnegi94",
+            "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+            "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+            "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+            "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+            "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+            "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+            "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+            "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+            "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+            "type": "User",
+            "user_view_type": "public",
+            "site_admin": false
+          },
+          "html_url": "https://github.com/shivamnegi94/testrepo",
+          "description": null,
+          "fork": false,
+          "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+          "forks_url": "https://api.github.com/repos/shivamnegi94/testrepo/forks",
+          "keys_url": "https://api.github.com/repos/shivamnegi94/testrepo/keys{/key_id}",
+          "collaborators_url": "https://api.github.com/repos/shivamnegi94/testrepo/collaborators{/collaborator}",
+          "teams_url": "https://api.github.com/repos/shivamnegi94/testrepo/teams",
+          "hooks_url": "https://api.github.com/repos/shivamnegi94/testrepo/hooks",
+          "issue_events_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/events{/number}",
+          "events_url": "https://api.github.com/repos/shivamnegi94/testrepo/events",
+          "assignees_url": "https://api.github.com/repos/shivamnegi94/testrepo/assignees{/user}",
+          "branches_url": "https://api.github.com/repos/shivamnegi94/testrepo/branches{/branch}",
+          "tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/tags",
+          "blobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/blobs{/sha}",
+          "git_tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/tags{/sha}",
+          "git_refs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/refs{/sha}",
+          "trees_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/trees{/sha}",
+          "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/{sha}",
+          "languages_url": "https://api.github.com/repos/shivamnegi94/testrepo/languages",
+          "stargazers_url": "https://api.github.com/repos/shivamnegi94/testrepo/stargazers",
+          "contributors_url": "https://api.github.com/repos/shivamnegi94/testrepo/contributors",
+          "subscribers_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscribers",
+          "subscription_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscription",
+          "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/commits{/sha}",
+          "git_commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/commits{/sha}",
+          "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/comments{/number}",
+          "issue_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/comments{/number}",
+          "contents_url": "https://api.github.com/repos/shivamnegi94/testrepo/contents/{+path}",
+          "compare_url": "https://api.github.com/repos/shivamnegi94/testrepo/compare/{base}...{head}",
+          "merges_url": "https://api.github.com/repos/shivamnegi94/testrepo/merges",
+          "archive_url": "https://api.github.com/repos/shivamnegi94/testrepo/{archive_format}{/ref}",
+          "downloads_url": "https://api.github.com/repos/shivamnegi94/testrepo/downloads",
+          "issues_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues{/number}",
+          "pulls_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls{/number}",
+          "milestones_url": "https://api.github.com/repos/shivamnegi94/testrepo/milestones{/number}",
+          "notifications_url": "https://api.github.com/repos/shivamnegi94/testrepo/notifications{?since,all,participating}",
+          "labels_url": "https://api.github.com/repos/shivamnegi94/testrepo/labels{/name}",
+          "releases_url": "https://api.github.com/repos/shivamnegi94/testrepo/releases{/id}",
+          "deployments_url": "https://api.github.com/repos/shivamnegi94/testrepo/deployments",
+          "created_at": "2021-02-02T04:41:22Z",
+          "updated_at": "2025-07-29T06:43:46Z",
+          "pushed_at": "2025-07-29T06:45:16Z",
+          "git_url": "git://github.com/shivamnegi94/testrepo.git",
+          "ssh_url": "git@github.com:shivamnegi94/testrepo.git",
+          "clone_url": "https://github.com/shivamnegi94/testrepo.git",
+          "svn_url": "https://github.com/shivamnegi94/testrepo",
+          "homepage": null,
+          "size": 333,
+          "stargazers_count": 0,
+          "watchers_count": 0,
+          "language": null,
+          "has_issues": true,
+          "has_projects": true,
+          "has_downloads": true,
+          "has_wiki": true,
+          "has_pages": false,
+          "has_discussions": false,
+          "forks_count": 0,
+          "mirror_url": null,
+          "archived": false,
+          "disabled": false,
+          "open_issues_count": 71,
+          "license": null,
+          "allow_forking": true,
+          "is_template": false,
+          "web_commit_signoff_required": false,
+          "topics": [
+  
+          ],
+          "visibility": "public",
+          "forks": 0,
+          "open_issues": 71,
+          "watchers": 0,
+          "default_branch": "main",
+          "allow_squash_merge": true,
+          "allow_merge_commit": true,
+          "allow_rebase_merge": true,
+          "allow_auto_merge": false,
+          "delete_branch_on_merge": false,
+          "allow_update_branch": false,
+          "use_squash_pr_title_as_default": false,
+          "squash_merge_commit_message": "COMMIT_MESSAGES",
+          "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+          "merge_commit_message": "PR_TITLE",
+          "merge_commit_title": "MERGE_MESSAGE"
+        }
+      },
+      "base": {
+        "label": "shivamnegi94:main",
+        "ref": "main",
+        "sha": "8115fbff29ce287c1c080ab132ccbb48dee76dcd",
+        "user": {
+          "login": "shivamnegi94",
+          "id": 58415634,
+          "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+          "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+          "gravatar_id": "",
+          "url": "https://api.github.com/users/shivamnegi94",
+          "html_url": "https://github.com/shivamnegi94",
+          "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+          "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+          "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+          "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+          "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+          "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+          "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+          "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+          "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+          "type": "User",
+          "user_view_type": "public",
+          "site_admin": false
+        },
+        "repo": {
+          "id": 335170389,
+          "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+          "name": "testrepo",
+          "full_name": "shivamnegi94/testrepo",
+          "private": false,
+          "owner": {
+            "login": "shivamnegi94",
+            "id": 58415634,
+            "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+            "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+            "gravatar_id": "",
+            "url": "https://api.github.com/users/shivamnegi94",
+            "html_url": "https://github.com/shivamnegi94",
+            "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+            "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+            "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+            "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+            "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+            "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+            "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+            "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+            "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+            "type": "User",
+            "user_view_type": "public",
+            "site_admin": false
+          },
+          "html_url": "https://github.com/shivamnegi94/testrepo",
+          "description": null,
+          "fork": false,
+          "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+          "forks_url": "https://api.github.com/repos/shivamnegi94/testrepo/forks",
+          "keys_url": "https://api.github.com/repos/shivamnegi94/testrepo/keys{/key_id}",
+          "collaborators_url": "https://api.github.com/repos/shivamnegi94/testrepo/collaborators{/collaborator}",
+          "teams_url": "https://api.github.com/repos/shivamnegi94/testrepo/teams",
+          "hooks_url": "https://api.github.com/repos/shivamnegi94/testrepo/hooks",
+          "issue_events_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/events{/number}",
+          "events_url": "https://api.github.com/repos/shivamnegi94/testrepo/events",
+          "assignees_url": "https://api.github.com/repos/shivamnegi94/testrepo/assignees{/user}",
+          "branches_url": "https://api.github.com/repos/shivamnegi94/testrepo/branches{/branch}",
+          "tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/tags",
+          "blobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/blobs{/sha}",
+          "git_tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/tags{/sha}",
+          "git_refs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/refs{/sha}",
+          "trees_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/trees{/sha}",
+          "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/{sha}",
+          "languages_url": "https://api.github.com/repos/shivamnegi94/testrepo/languages",
+          "stargazers_url": "https://api.github.com/repos/shivamnegi94/testrepo/stargazers",
+          "contributors_url": "https://api.github.com/repos/shivamnegi94/testrepo/contributors",
+          "subscribers_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscribers",
+          "subscription_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscription",
+          "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/commits{/sha}",
+          "git_commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/commits{/sha}",
+          "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/comments{/number}",
+          "issue_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/comments{/number}",
+          "contents_url": "https://api.github.com/repos/shivamnegi94/testrepo/contents/{+path}",
+          "compare_url": "https://api.github.com/repos/shivamnegi94/testrepo/compare/{base}...{head}",
+          "merges_url": "https://api.github.com/repos/shivamnegi94/testrepo/merges",
+          "archive_url": "https://api.github.com/repos/shivamnegi94/testrepo/{archive_format}{/ref}",
+          "downloads_url": "https://api.github.com/repos/shivamnegi94/testrepo/downloads",
+          "issues_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues{/number}",
+          "pulls_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls{/number}",
+          "milestones_url": "https://api.github.com/repos/shivamnegi94/testrepo/milestones{/number}",
+          "notifications_url": "https://api.github.com/repos/shivamnegi94/testrepo/notifications{?since,all,participating}",
+          "labels_url": "https://api.github.com/repos/shivamnegi94/testrepo/labels{/name}",
+          "releases_url": "https://api.github.com/repos/shivamnegi94/testrepo/releases{/id}",
+          "deployments_url": "https://api.github.com/repos/shivamnegi94/testrepo/deployments",
+          "created_at": "2021-02-02T04:41:22Z",
+          "updated_at": "2025-07-29T06:43:46Z",
+          "pushed_at": "2025-07-29T06:45:16Z",
+          "git_url": "git://github.com/shivamnegi94/testrepo.git",
+          "ssh_url": "git@github.com:shivamnegi94/testrepo.git",
+          "clone_url": "https://github.com/shivamnegi94/testrepo.git",
+          "svn_url": "https://github.com/shivamnegi94/testrepo",
+          "homepage": null,
+          "size": 333,
+          "stargazers_count": 0,
+          "watchers_count": 0,
+          "language": null,
+          "has_issues": true,
+          "has_projects": true,
+          "has_downloads": true,
+          "has_wiki": true,
+          "has_pages": false,
+          "has_discussions": false,
+          "forks_count": 0,
+          "mirror_url": null,
+          "archived": false,
+          "disabled": false,
+          "open_issues_count": 71,
+          "license": null,
+          "allow_forking": true,
+          "is_template": false,
+          "web_commit_signoff_required": false,
+          "topics": [
+  
+          ],
+          "visibility": "public",
+          "forks": 0,
+          "open_issues": 71,
+          "watchers": 0,
+          "default_branch": "main",
+          "allow_squash_merge": true,
+          "allow_merge_commit": true,
+          "allow_rebase_merge": true,
+          "allow_auto_merge": false,
+          "delete_branch_on_merge": false,
+          "allow_update_branch": false,
+          "use_squash_pr_title_as_default": false,
+          "squash_merge_commit_message": "COMMIT_MESSAGES",
+          "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
+          "merge_commit_message": "PR_TITLE",
+          "merge_commit_title": "MERGE_MESSAGE"
+        }
+      },
+      "_links": {
+        "self": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206"
+        },
+        "html": {
+          "href": "https://github.com/shivamnegi94/testrepo/pull/206"
+        },
+        "issue": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/issues/206"
+        },
+        "comments": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/issues/206/comments"
+        },
+        "review_comments": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206/comments"
+        },
+        "review_comment": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/comments{/number}"
+        },
+        "commits": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/206/commits"
+        },
+        "statuses": {
+          "href": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/9dc1b02c9b25bac779bdb2f31034f3f2421aa71d"
+        }
+      },
+      "author_association": "COLLABORATOR",
+      "auto_merge": null,
+      "active_lock_reason": null
+    },
+    "changes": {
+      "body": {
+        "from": "Gfh"
+      }
+    },
+    "repository": {
+      "id": 335170389,
+      "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+      "name": "testrepo",
+      "full_name": "shivamnegi94/testrepo",
+      "private": false,
+      "owner": {
+        "login": "shivamnegi94",
+        "id": 58415634,
+        "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+        "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+        "gravatar_id": "",
+        "url": "https://api.github.com/users/shivamnegi94",
+        "html_url": "https://github.com/shivamnegi94",
+        "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+        "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+        "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+        "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+        "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+        "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+        "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+        "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+        "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+        "type": "User",
+        "user_view_type": "public",
+        "site_admin": false
+      },
+      "html_url": "https://github.com/shivamnegi94/testrepo",
+      "description": null,
+      "fork": false,
+      "url": "https://api.github.com/repos/shivamnegi94/testrepo",
+      "forks_url": "https://api.github.com/repos/shivamnegi94/testrepo/forks",
+      "keys_url": "https://api.github.com/repos/shivamnegi94/testrepo/keys{/key_id}",
+      "collaborators_url": "https://api.github.com/repos/shivamnegi94/testrepo/collaborators{/collaborator}",
+      "teams_url": "https://api.github.com/repos/shivamnegi94/testrepo/teams",
+      "hooks_url": "https://api.github.com/repos/shivamnegi94/testrepo/hooks",
+      "issue_events_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/events{/number}",
+      "events_url": "https://api.github.com/repos/shivamnegi94/testrepo/events",
+      "assignees_url": "https://api.github.com/repos/shivamnegi94/testrepo/assignees{/user}",
+      "branches_url": "https://api.github.com/repos/shivamnegi94/testrepo/branches{/branch}",
+      "tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/tags",
+      "blobs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/blobs{/sha}",
+      "git_tags_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/tags{/sha}",
+      "git_refs_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/refs{/sha}",
+      "trees_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/trees{/sha}",
+      "statuses_url": "https://api.github.com/repos/shivamnegi94/testrepo/statuses/{sha}",
+      "languages_url": "https://api.github.com/repos/shivamnegi94/testrepo/languages",
+      "stargazers_url": "https://api.github.com/repos/shivamnegi94/testrepo/stargazers",
+      "contributors_url": "https://api.github.com/repos/shivamnegi94/testrepo/contributors",
+      "subscribers_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscribers",
+      "subscription_url": "https://api.github.com/repos/shivamnegi94/testrepo/subscription",
+      "commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/commits{/sha}",
+      "git_commits_url": "https://api.github.com/repos/shivamnegi94/testrepo/git/commits{/sha}",
+      "comments_url": "https://api.github.com/repos/shivamnegi94/testrepo/comments{/number}",
+      "issue_comment_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/comments{/number}",
+      "contents_url": "https://api.github.com/repos/shivamnegi94/testrepo/contents/{+path}",
+      "compare_url": "https://api.github.com/repos/shivamnegi94/testrepo/compare/{base}...{head}",
+      "merges_url": "https://api.github.com/repos/shivamnegi94/testrepo/merges",
+      "archive_url": "https://api.github.com/repos/shivamnegi94/testrepo/{archive_format}{/ref}",
+      "downloads_url": "https://api.github.com/repos/shivamnegi94/testrepo/downloads",
+      "issues_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues{/number}",
+      "pulls_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls{/number}",
+      "milestones_url": "https://api.github.com/repos/shivamnegi94/testrepo/milestones{/number}",
+      "notifications_url": "https://api.github.com/repos/shivamnegi94/testrepo/notifications{?since,all,participating}",
+      "labels_url": "https://api.github.com/repos/shivamnegi94/testrepo/labels{/name}",
+      "releases_url": "https://api.github.com/repos/shivamnegi94/testrepo/releases{/id}",
+      "deployments_url": "https://api.github.com/repos/shivamnegi94/testrepo/deployments",
+      "created_at": "2021-02-02T04:41:22Z",
+      "updated_at": "2025-07-29T06:43:46Z",
+      "pushed_at": "2025-07-29T06:45:16Z",
+      "git_url": "git://github.com/shivamnegi94/testrepo.git",
+      "ssh_url": "git@github.com:shivamnegi94/testrepo.git",
+      "clone_url": "https://github.com/shivamnegi94/testrepo.git",
+      "svn_url": "https://github.com/shivamnegi94/testrepo",
+      "homepage": null,
+      "size": 333,
+      "stargazers_count": 0,
+      "watchers_count": 0,
+      "language": null,
+      "has_issues": true,
+      "has_projects": true,
+      "has_downloads": true,
+      "has_wiki": true,
+      "has_pages": false,
+      "has_discussions": false,
+      "forks_count": 0,
+      "mirror_url": null,
+      "archived": false,
+      "disabled": false,
+      "open_issues_count": 71,
+      "license": null,
+      "allow_forking": true,
+      "is_template": false,
+      "web_commit_signoff_required": false,
+      "topics": [
+  
+      ],
+      "visibility": "public",
+      "forks": 0,
+      "open_issues": 71,
+      "watchers": 0,
+      "default_branch": "main"
+    },
+    "sender": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/shivamnegi94",
+      "html_url": "https://github.com/shivamnegi94",
+      "followers_url": "https://api.github.com/users/shivamnegi94/followers",
+      "following_url": "https://api.github.com/users/shivamnegi94/following{/other_user}",
+      "gists_url": "https://api.github.com/users/shivamnegi94/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/shivamnegi94/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/shivamnegi94/subscriptions",
+      "organizations_url": "https://api.github.com/users/shivamnegi94/orgs",
+      "repos_url": "https://api.github.com/users/shivamnegi94/repos",
+      "events_url": "https://api.github.com/users/shivamnegi94/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/shivamnegi94/received_events",
+      "type": "User",
+      "user_view_type": "public",
+      "site_admin": false
+    }
+  }
\ No newline at end of file
diff --git a/scm/driver/github/testdata/webhooks/pr_review_edited.json.golden b/scm/driver/github/testdata/webhooks/pr_review_edited.json.golden
new file mode 100644
index 000000000..4ec99ebc6
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pr_review_edited.json.golden
@@ -0,0 +1,55 @@
+{
+  "Action": "edited",
+  "Repo": {
+    "ID": "335170389",
+    "Namespace": "shivamnegi94",
+    "Name": "testrepo",
+    "Branch": "main",
+    "Private": false,
+    "Link": "https://github.com/shivamnegi94/testrepo",
+    "Created": "2021-02-02T04:41:22Z",
+    "Updated": "2025-07-29T06:43:46Z",
+    "Visibility": 1,
+    "Perm": {},
+    "CloneSSH": "git@github.com:shivamnegi94/testrepo.git",
+    "Clone": "https://github.com/shivamnegi94/testrepo.git"
+  },
+  "PullRequest": {
+    "Number": 206,
+    "Title": "Update sacsac.yaml",
+    "Body": null,
+    "Sha": "9dc1b02c9b25bac779bdb2f31034f3f2421aa71d",
+    "Ref": "refs/pull/206/head",
+    "Source": "ut",
+    "Target": "main",
+    "Fork": "shivamnegi94/testrepo",
+    "Link": "https://github.com/shivamnegi94/testrepo/pull/206",
+    "Diff": "https://github.com/shivamnegi94/testrepo/pull/206.diff",
+    "Closed": false,
+    "Merged": false,
+    "Base": {
+      "Sha": "8115fbff29ce287c1c080ab132ccbb48dee76dcd",
+      "Path": "refs/heads/main",
+      "Name": "main"
+    },
+    "Head": {
+      "Sha": "9dc1b02c9b25bac779bdb2f31034f3f2421aa71d",
+      "Path": "refs/heads/ut",
+      "Name": "ut"
+    },
+    "Author": {
+      "Login": "utkarshchoubey-harness",
+      "Name": "",
+      "Email": "",
+      "Avatar": "https://avatars.githubusercontent.com/u/101091340?v=4"
+    },
+    "Created": "2025-07-29T06:44:39Z",
+    "Updated": "2025-07-29T06:50:15Z"
+  },
+  "Sender": {
+    "Login": "shivamnegi94",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/58415634?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/pr_review_submitted.json b/scm/driver/github/testdata/webhooks/pr_review_submitted.json
new file mode 100644
index 000000000..127145d8b
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pr_review_submitted.json
@@ -0,0 +1,127 @@
+{
+  "action": "submitted",
+  "review": {
+    "id": 3060633379,
+    "node_id": "PRR_kwDOE_pLVc62bY8j",
+    "user": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/shivamnegi94",
+      "html_url": "https://github.com/shivamnegi94",
+      "type": "User",
+      "site_admin": false
+    },
+    "body": "go",
+    "commit_id": "adccd6c5030261b47e35d0d3798d4ed31a051f6d",
+    "submitted_at": "2025-07-28T06:35:59Z",
+    "state": "commented",
+    "html_url": "https://github.com/shivamnegi94/testrepo/pull/205#pullrequestreview-3060633379",
+    "pull_request_url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/205",
+    "author_association": "OWNER"
+  },
+  "pull_request": {
+    "url": "https://api.github.com/repos/shivamnegi94/testrepo/pulls/205",
+    "id": 2699141628,
+    "node_id": "PR_kwDOE_pLVc6g4aH8",
+    "html_url": "https://github.com/shivamnegi94/testrepo/pull/205",
+    "diff_url": "https://github.com/shivamnegi94/testrepo/pull/205.diff",
+    "patch_url": "https://github.com/shivamnegi94/testrepo/pull/205.patch",
+    "issue_url": "https://api.github.com/repos/shivamnegi94/testrepo/issues/205",
+    "number": 205,
+    "state": "open",
+    "locked": false,
+    "title": "Update NewSre_v1.yaml",
+    "user": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4"
+    },
+    "body": null,
+    "created_at": "2025-07-28T04:57:11Z",
+    "updated_at": "2025-07-28T06:35:59Z",
+    "closed_at": null,
+    "merged_at": null,
+    "merge_commit_sha": "728c82839639b8693c56c4237675deeca6b82163",
+    "draft": false,
+    "head": {
+      "label": "shivamnegi94:shivamnegi94-patch-66",
+      "ref": "shivamnegi94-patch-66",
+      "sha": "adccd6c5030261b47e35d0d3798d4ed31a051f6d",
+      "user": {
+        "login": "shivamnegi94",
+        "id": 58415634,
+        "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+        "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4"
+      },
+      "repo": {
+        "id": 335170389,
+        "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+        "name": "testrepo",
+        "full_name": "shivamnegi94/testrepo",
+        "private": false,
+        "owner": {
+          "login": "shivamnegi94",
+          "id": 58415634,
+          "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4"
+        },
+        "html_url": "https://github.com/shivamnegi94/testrepo",
+        "default_branch": "main",
+        "visibility": "public"
+      }
+    },
+    "base": {
+      "label": "shivamnegi94:main",
+      "ref": "main",
+      "sha": "28e98f8dc20d4b20eb73b659e16f9fddeb860db5",
+      "user": {
+        "login": "shivamnegi94",
+        "id": 58415634,
+        "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+        "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4"
+      },
+      "repo": {
+        "id": 335170389,
+        "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+        "name": "testrepo",
+        "full_name": "shivamnegi94/testrepo",
+        "private": false,
+        "owner": {
+          "login": "shivamnegi94",
+          "id": 58415634,
+          "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4"
+        },
+        "html_url": "https://github.com/shivamnegi94/testrepo",
+        "default_branch": "main",
+        "visibility": "public"
+      }
+    }
+  },
+  "repository": {
+    "id": 335170389,
+    "node_id": "MDEwOlJlcG9zaXRvcnkzMzUxNzAzODk=",
+    "name": "testrepo",
+    "full_name": "shivamnegi94/testrepo",
+    "private": false,
+    "owner": {
+      "login": "shivamnegi94",
+      "id": 58415634,
+      "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+      "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4"
+    },
+    "html_url": "https://github.com/shivamnegi94/testrepo",
+    "default_branch": "main",
+    "visibility": "public"
+  },
+  "sender": {
+    "login": "shivamnegi94",
+    "id": 58415634,
+    "node_id": "MDQ6VXNlcjU4NDE1NjM0",
+    "avatar_url": "https://avatars.githubusercontent.com/u/58415634?v=4",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/pr_review_submitted.json.golden b/scm/driver/github/testdata/webhooks/pr_review_submitted.json.golden
new file mode 100644
index 000000000..b9e46458f
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/pr_review_submitted.json.golden
@@ -0,0 +1,53 @@
+{
+  "Action": "submitted",
+  "Repo": {
+    "ID": "335170389",
+    "Namespace": "shivamnegi94",
+    "Name": "testrepo",
+    "Branch": "main",
+    "Private": false,
+    "Link": "https://github.com/shivamnegi94/testrepo",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z",
+    "Visibility": 1,
+    "Perm": {}
+  },
+  "PullRequest": {
+    "Number": 205,
+    "Title": "Update NewSre_v1.yaml",
+    "Body": null,
+    "Sha": "adccd6c5030261b47e35d0d3798d4ed31a051f6d",
+    "Ref": "refs/pull/205/head",
+    "Source": "shivamnegi94-patch-66",
+    "Target": "main",
+    "Fork": "shivamnegi94/testrepo",
+    "Link": "https://github.com/shivamnegi94/testrepo/pull/205",
+    "Diff": "https://github.com/shivamnegi94/testrepo/pull/205.diff",
+    "Closed": false,
+    "Merged": false,
+    "Base": {
+      "Sha": "28e98f8dc20d4b20eb73b659e16f9fddeb860db5",
+      "Path": "refs/heads/main",
+      "Name": "main"
+    },
+    "Head": {
+      "Sha": "adccd6c5030261b47e35d0d3798d4ed31a051f6d",
+      "Path": "refs/heads/shivamnegi94-patch-66",
+      "Name": "shivamnegi94-patch-66"
+    },
+    "Author": {
+      "Login": "shivamnegi94",
+      "Name": "",
+      "Email": "",
+      "Avatar": "https://avatars.githubusercontent.com/u/58415634?v=4"
+    },
+    "Created": "2025-07-28T04:57:11Z",
+    "Updated": "2025-07-28T06:35:59Z"
+  },
+  "Sender": {
+    "Login": "shivamnegi94",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/58415634?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_created.json b/scm/driver/github/testdata/webhooks/release_created.json
new file mode 100644
index 000000000..39a6d8d78
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_created.json
@@ -0,0 +1,164 @@
+{
+  "action": "created",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_created.json.golden b/scm/driver/github/testdata/webhooks/release_created.json.golden
new file mode 100644
index 000000000..b7935c46d
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_created.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "created",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_deleted.json b/scm/driver/github/testdata/webhooks/release_deleted.json
new file mode 100644
index 000000000..a5ba0fef2
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_deleted.json
@@ -0,0 +1,164 @@
+{
+  "action": "deleted",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_deleted.json.golden b/scm/driver/github/testdata/webhooks/release_deleted.json.golden
new file mode 100644
index 000000000..30dee53c1
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_deleted.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "deleted",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_edited.json b/scm/driver/github/testdata/webhooks/release_edited.json
new file mode 100644
index 000000000..45f7511b4
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_edited.json
@@ -0,0 +1,164 @@
+{
+  "action": "edited",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_edited.json.golden b/scm/driver/github/testdata/webhooks/release_edited.json.golden
new file mode 100644
index 000000000..0b1e387ad
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_edited.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "edited",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_prereleased.json b/scm/driver/github/testdata/webhooks/release_prereleased.json
new file mode 100644
index 000000000..94ab938c8
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_prereleased.json
@@ -0,0 +1,164 @@
+{
+  "action": "prereleased",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_prereleased.json.golden b/scm/driver/github/testdata/webhooks/release_prereleased.json.golden
new file mode 100644
index 000000000..996beb3a6
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_prereleased.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "prereleased",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_published.json b/scm/driver/github/testdata/webhooks/release_published.json
new file mode 100644
index 000000000..23b9bad66
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_published.json
@@ -0,0 +1,164 @@
+{
+  "action": "published",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_published.json.golden b/scm/driver/github/testdata/webhooks/release_published.json.golden
new file mode 100644
index 000000000..a1f070b04
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_published.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "published",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_released.json b/scm/driver/github/testdata/webhooks/release_released.json
new file mode 100644
index 000000000..b4af3b457
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_released.json
@@ -0,0 +1,164 @@
+{
+  "action": "released",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_released.json.golden b/scm/driver/github/testdata/webhooks/release_released.json.golden
new file mode 100644
index 000000000..3e93aa08e
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_released.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "released",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_unpublished.json b/scm/driver/github/testdata/webhooks/release_unpublished.json
new file mode 100644
index 000000000..6ee38d4e2
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_unpublished.json
@@ -0,0 +1,164 @@
+{
+  "action": "unpublished",
+  "release": {
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309",
+    "assets_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets",
+    "upload_url": "https://uploads.github.com/repos/vcalasansh/harness-ngtriggers-test/releases/81372309/assets{?name,label}",
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+    "id": 81372309,
+    "author": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "node_id": "RE_kwDOH5utuc4E2aSV",
+    "tag_name": "v1.0.0",
+    "target_commitish": "test-branch",
+    "name": "New release!",
+    "draft": false,
+    "prerelease": false,
+    "created_at": "2022-10-28T13:03:11Z",
+    "published_at": "2022-10-28T16:36:52Z",
+    "assets": [
+
+    ],
+    "tarball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tarball/v1.0.0",
+    "zipball_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/zipball/v1.0.0",
+    "body": "New release!"
+  },
+  "repository": {
+    "id": 530296249,
+    "node_id": "R_kgDOH5utuQ",
+    "name": "harness-ngtriggers-test",
+    "full_name": "vcalasansh/harness-ngtriggers-test",
+    "private": true,
+    "owner": {
+      "login": "vcalasansh",
+      "id": 109106581,
+      "node_id": "U_kgDOBoDVlQ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/vcalasansh",
+      "html_url": "https://github.com/vcalasansh",
+      "followers_url": "https://api.github.com/users/vcalasansh/followers",
+      "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+      "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+      "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+      "repos_url": "https://api.github.com/users/vcalasansh/repos",
+      "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "description": null,
+    "fork": false,
+    "url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test",
+    "forks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/forks",
+    "keys_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/teams",
+    "hooks_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/hooks",
+    "issue_events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/events",
+    "assignees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/tags",
+    "blobs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/languages",
+    "stargazers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/stargazers",
+    "contributors_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contributors",
+    "subscribers_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscribers",
+    "subscription_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/subscription",
+    "commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/merges",
+    "archive_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/downloads",
+    "issues_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/labels{/name}",
+    "releases_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/vcalasansh/harness-ngtriggers-test/deployments",
+    "created_at": "2022-08-29T16:12:07Z",
+    "updated_at": "2022-08-29T16:12:07Z",
+    "pushed_at": "2022-10-28T16:36:27Z",
+    "git_url": "git://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "ssh_url": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "clone_url": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "svn_url": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "homepage": null,
+    "size": 11,
+    "stargazers_count": 0,
+    "watchers_count": 0,
+    "language": null,
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "forks_count": 0,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 0,
+    "license": null,
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+
+    ],
+    "forks": 0,
+    "open_issues": 0,
+    "watchers": 0,
+    "default_branch": "test-branch"
+  },
+  "sender": {
+    "login": "vcalasansh",
+    "id": 109106581,
+    "node_id": "U_kgDOBoDVlQ",
+    "avatar_url": "https://avatars.githubusercontent.com/u/109106581?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/vcalasansh",
+    "html_url": "https://github.com/vcalasansh",
+    "followers_url": "https://api.github.com/users/vcalasansh/followers",
+    "following_url": "https://api.github.com/users/vcalasansh/following{/other_user}",
+    "gists_url": "https://api.github.com/users/vcalasansh/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/vcalasansh/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/vcalasansh/subscriptions",
+    "organizations_url": "https://api.github.com/users/vcalasansh/orgs",
+    "repos_url": "https://api.github.com/users/vcalasansh/repos",
+    "events_url": "https://api.github.com/users/vcalasansh/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/vcalasansh/received_events",
+    "type": "User",
+    "site_admin": false
+  }
+}
diff --git a/scm/driver/github/testdata/webhooks/release_unpublished.json.golden b/scm/driver/github/testdata/webhooks/release_unpublished.json.golden
new file mode 100644
index 000000000..21ea08eea
--- /dev/null
+++ b/scm/driver/github/testdata/webhooks/release_unpublished.json.golden
@@ -0,0 +1,34 @@
+{
+  "Action": "unpublished",
+  "Release": {
+		"ID": 81372309,
+		"Title": "New release!",
+		"Description": "New release!",
+		"Link": "https://github.com/vcalasansh/harness-ngtriggers-test/releases/tag/v1.0.0",
+		"Tag": "v1.0.0",
+		"Commitish": "test-branch",
+		"Draft": false,
+		"Prerelease": false,
+		"Created": "2022-10-28T13:03:11Z",
+		"Published": "2022-10-28T16:36:52Z"
+  },
+  "Repo": {
+    "ID": "530296249",
+    "Namespace": "vcalasansh",
+    "Name": "harness-ngtriggers-test",
+    "Perm": null,
+    "Branch": "test-branch",
+    "Private": true,
+    "Clone": "https://github.com/vcalasansh/harness-ngtriggers-test.git",
+    "CloneSSH": "git@github.com:vcalasansh/harness-ngtriggers-test.git",
+    "Link": "https://github.com/vcalasansh/harness-ngtriggers-test",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "Login": "vcalasansh",
+    "Name": "",
+    "Email": "",
+    "Avatar": "https://avatars.githubusercontent.com/u/109106581?v=4"
+  }
+}
diff --git a/scm/driver/github/user.go b/scm/driver/github/user.go
index 4b04b38a8..6da295679 100644
--- a/scm/driver/github/user.go
+++ b/scm/driver/github/user.go
@@ -31,8 +31,15 @@ func (s *userService) FindLogin(ctx context.Context, login string) (*scm.User, *
 }
 
 func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, error) {
-	user, res, err := s.Find(ctx)
-	return user.Email, res, err
+	out, res, err := s.ListEmail(ctx, scm.ListOptions{})
+	return returnPrimaryEmail(out), res, err
+}
+
+func (s *userService) ListEmail(ctx context.Context, opts scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	path := fmt.Sprintf("user/emails?%s", encodeListOptions(opts))
+	out := []*email{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertEmailList(out), res, err
 }
 
 type user struct {
@@ -45,6 +52,12 @@ type user struct {
 	Updated time.Time   `json:"updated_at"`
 }
 
+type email struct {
+	Email    string `json:"email"`
+	Primary  bool   `json:"primary"`
+	Verified bool   `json:"verified"`
+}
+
 func convertUser(from *user) *scm.User {
 	return &scm.User{
 		Avatar:  from.Avatar,
@@ -55,3 +68,32 @@ func convertUser(from *user) *scm.User {
 		Updated: from.Updated,
 	}
 }
+
+func returnPrimaryEmail(from []*scm.Email) string {
+	for _, v := range from {
+		if v.Primary == true {
+			return v.Value
+		}
+	}
+	return ""
+}
+
+// helper function to convert from the github email list to
+// the common email structure.
+func convertEmailList(from []*email) []*scm.Email {
+	to := []*scm.Email{}
+	for _, v := range from {
+		to = append(to, convertEmail(v))
+	}
+	return to
+}
+
+// helper function to convert from the github email structure to
+// the common email structure.
+func convertEmail(from *email) *scm.Email {
+	return &scm.Email{
+		Value:    from.Email,
+		Primary:  from.Primary,
+		Verified: from.Verified,
+	}
+}
diff --git a/scm/driver/github/user_test.go b/scm/driver/github/user_test.go
index 86e5b709d..8888d18a5 100644
--- a/scm/driver/github/user_test.go
+++ b/scm/driver/github/user_test.go
@@ -39,7 +39,7 @@ func TestUserFind(t *testing.T) {
 
 	want := new(scm.User)
 	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -71,12 +71,12 @@ func TestUserLoginFind(t *testing.T) {
 
 	want := new(scm.User)
 	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
 		t.Log(diff)
-		json.NewEncoder(os.Stdout).Encode(got)
+		_ = json.NewEncoder(os.Stdout).Encode(got)
 	}
 
 	t.Run("Request", testRequest(res))
@@ -87,14 +87,14 @@ func TestUserEmailFind(t *testing.T) {
 	defer gock.Off()
 
 	gock.New("https://api.github.com").
-		Get("/user").
+		Get("/user/emails").
 		Reply(200).
 		Type("application/json").
 		SetHeader("X-GitHub-Request-Id", "DD0E:6011:12F21A8:1926790:5A2064E2").
 		SetHeader("X-RateLimit-Limit", "60").
 		SetHeader("X-RateLimit-Remaining", "59").
 		SetHeader("X-RateLimit-Reset", "1512076018").
-		File("testdata/user.json")
+		File("testdata/emails.json")
 
 	client := NewDefault()
 	result, res, err := client.Users.FindEmail(context.Background())
@@ -108,3 +108,37 @@ func TestUserEmailFind(t *testing.T) {
 	t.Run("Request", testRequest(res))
 	t.Run("Rate", testRate(res))
 }
+
+func TestUserEmailList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://api.github.com").
+		Get("/user/emails").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/emails.json")
+
+	client := NewDefault()
+	got, res, err := client.Users.ListEmail(context.Background(), scm.ListOptions{Size: 30, Page: 1})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Email{}
+	raw, _ := ioutil.ReadFile("testdata/emails.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
diff --git a/scm/driver/github/util.go b/scm/driver/github/util.go
index a6b3c55a7..24a0c858c 100644
--- a/scm/driver/github/util.go
+++ b/scm/driver/github/util.go
@@ -7,6 +7,7 @@ package github
 import (
 	"net/url"
 	"strconv"
+	"strings"
 
 	"github.com/drone/go-scm/scm"
 )
@@ -22,6 +23,33 @@ func encodeListOptions(opts scm.ListOptions) string {
 	return params.Encode()
 }
 
+func encodeRepoListOptions(opts scm.RepoListOptions) string {
+	var sb strings.Builder
+	if opts.RepoSearchTerm != (scm.RepoSearchTerm{}) {
+		if opts.RepoSearchTerm.RepoName != "" {
+			sb.WriteString("q=")
+			sb.WriteString(opts.RepoSearchTerm.RepoName)
+			sb.WriteString("+in:name+user:")
+			sb.WriteString(opts.RepoSearchTerm.User)
+		} else {
+			sb.WriteString("q=")
+			sb.WriteString("user:")
+			sb.WriteString(opts.RepoSearchTerm.User)
+		}
+	}
+	if opts.ListOptions != (scm.ListOptions{}) {
+		if opts.ListOptions.Page != 0 {
+			sb.WriteString("&page=")
+			sb.WriteString(strconv.Itoa(opts.ListOptions.Page))
+		}
+		if opts.ListOptions.Size != 0 {
+			sb.WriteString("&per_page=")
+			sb.WriteString(strconv.Itoa(opts.ListOptions.Size))
+		}
+	}
+	return sb.String()
+}
+
 func encodeCommitListOptions(opts scm.CommitListOptions) string {
 	params := url.Values{}
 	if opts.Page != 0 {
@@ -31,7 +59,10 @@ func encodeCommitListOptions(opts scm.CommitListOptions) string {
 		params.Set("per_page", strconv.Itoa(opts.Size))
 	}
 	if opts.Ref != "" {
-		params.Set("ref", opts.Ref)
+		params.Set("sha", opts.Ref)
+	}
+	if opts.Path != "" {
+		params.Set("path", opts.Path)
 	}
 	return params.Encode()
 }
@@ -67,3 +98,35 @@ func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
 	}
 	return params.Encode()
 }
+
+func encodeMilestoneListOptions(opts scm.MilestoneListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
+
+func encodeReleaseListOptions(opts scm.ReleaseListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
diff --git a/scm/driver/github/util_test.go b/scm/driver/github/util_test.go
index c6a266433..46a87888e 100644
--- a/scm/driver/github/util_test.go
+++ b/scm/driver/github/util_test.go
@@ -27,8 +27,9 @@ func Test_encodeCommitListOptions(t *testing.T) {
 		Page: 10,
 		Size: 30,
 		Ref:  "master",
+		Path: "readme.md",
 	}
-	want := "page=10&per_page=30&ref=master"
+	want := "page=10&path=readme.md&per_page=30&sha=master"
 	got := encodeCommitListOptions(opts)
 	if got != want {
 		t.Errorf("Want encoded commit list options %q, got %q", want, got)
diff --git a/scm/driver/github/webhook.go b/scm/driver/github/webhook.go
index 4aa87f5c4..39fa2531b 100644
--- a/scm/driver/github/webhook.go
+++ b/scm/driver/github/webhook.go
@@ -46,6 +46,13 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 	// case "issues":
 	case "issue_comment":
 		hook, err = s.parseIssueCommentHook(data)
+	case "release":
+		hook, err = s.parseReleaseHook(data)
+	case "workflow_run":
+		hook, err = s.parsePipelineHook(data)
+	case "pull_request_review":
+		hook, err = s.parsePullRequestReviewHook(data)
+
 	default:
 		return nil, scm.ErrUnknownEvent
 	}
@@ -63,7 +70,10 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 		return hook, nil
 	}
 
-	sig := req.Header.Get("X-Hub-Signature")
+	sig := req.Header.Get("X-Hub-Signature-256")
+	if sig == "" {
+		sig = req.Header.Get("X-Hub-Signature")
+	}
 	if !hmac.ValidatePrefix(data, []byte(key), sig) {
 		return hook, scm.ErrSignatureInvalid
 	}
@@ -167,8 +177,114 @@ func (s *webhookService) parsePullRequestHook(data []byte) (scm.Webhook, error)
 		dst.Action = scm.ActionReopen
 	case "synchronize":
 		dst.Action = scm.ActionSync
-	case "assigned", "unassigned", "review_requested", "review_request_removed", "ready_for_review", "locked", "unlocked":
+	case "ready_for_review":
+		dst.Action = scm.ActionReviewReady
+	case "assigned", "unassigned", "review_requested", "review_request_removed", "locked", "unlocked":
+		dst.Action = scm.ActionUnknown
+	default:
 		dst.Action = scm.ActionUnknown
+	}
+	return dst, nil
+}
+
+func (s *webhookService) parsePullRequestReviewHook(data []byte) (scm.Webhook, error) {
+	src := new(pullRequestHook)
+	err := json.Unmarshal(data, src)
+	if err != nil {
+		return nil, err
+	}
+	dst := convertPullRequestReviewHook(src)
+	switch src.Action {
+	case "submitted":
+		dst.Action = scm.ActionSubmitted
+	case "edited":
+		dst.Action = scm.ActionEdit
+	case "dismissed":
+		dst.Action = scm.ActionDismissed
+	default:
+		dst.Action = scm.ActionUnknown
+	}
+	return dst, nil
+}
+
+func (s *webhookService) parsePipelineHook(data []byte) (scm.Webhook, error) {
+	src := new(pipelineHook)
+	err := json.Unmarshal(data, src)
+	if err != nil {
+		return nil, err
+	}
+	dst, err := convertPipelineHook(src), nil
+	return dst, nil
+}
+
+func convertPipelineHook(src *pipelineHook) *scm.PipelineHook {
+	pr := scm.PullRequest{}
+	if len(src.WorkflowRun.PullRequests) > 0 {
+		pr = scm.PullRequest{
+			Number:  src.WorkflowRun.PullRequests[0].Number,
+			Sha:     src.WorkflowRun.PullRequests[0].Head.SHA,
+			Ref:     src.WorkflowRun.PullRequests[0].Head.Ref,
+			Source:  src.WorkflowRun.PullRequests[0].Head.Ref,
+			Target:  src.WorkflowRun.PullRequests[0].Base.Ref,
+			Link:    src.WorkflowRun.PullRequests[0].URL,
+			Created: src.WorkflowRun.CreatedAt,
+		}
+	}
+
+	var execution_status string
+	if src.WorkflowRun.Status == "completed" {
+		execution_status = src.WorkflowRun.Conclusion.String
+	} else {
+		execution_status = src.WorkflowRun.Status
+	}
+
+	return &scm.PipelineHook{
+		Repo: *convertRepository(&src.Repository),
+		Commit: scm.Commit{
+			Sha:     src.WorkflowRun.HeadCommit.ID,
+			Message: src.WorkflowRun.HeadCommit.Message,
+			Author: scm.Signature{
+				Name:  src.WorkflowRun.HeadCommit.Author.Name,
+				Email: src.WorkflowRun.HeadCommit.Author.Email,
+			},
+			Committer: scm.Signature{
+				Name:  src.WorkflowRun.HeadCommit.Committer.Name,
+				Email: src.WorkflowRun.HeadCommit.Committer.Email,
+			},
+		},
+		Execution: scm.Execution{
+			Number:  int(src.WorkflowRun.RunNumber),
+			Status:  scm.ConvertExecutionStatus(execution_status),
+			Created: src.WorkflowRun.CreatedAt,
+			URL:     src.WorkflowRun.URL,
+		},
+		Sender:      *convertUser(&src.Sender),
+		PullRequest: pr,
+	}
+}
+
+func (s *webhookService) parseReleaseHook(data []byte) (scm.Webhook, error) {
+	src := new(releaseHook)
+	err := json.Unmarshal(data, src)
+	if err != nil {
+		return nil, err
+	}
+	dst := convertReleaseHook(src)
+	switch src.Action {
+	case "created":
+		dst.Action = scm.ActionCreate
+	case "edited":
+		dst.Action = scm.ActionEdit
+	case "deleted":
+		dst.Action = scm.ActionDelete
+	case "published":
+		dst.Action = scm.ActionPublish
+	case "unpublished":
+		dst.Action = scm.ActionUnpublish
+	case "prereleased":
+		dst.Action = scm.ActionPrerelease
+	case "released":
+		dst.Action = scm.ActionRelease
 	default:
 		dst.Action = scm.ActionUnknown
 	}
@@ -298,6 +414,99 @@ type (
 			Updated time.Time `json:"updated_at"`
 		} `json:"comment"`
 	}
+
+	// github release webhook payload
+	releaseHook struct {
+		Action  string `json:"action"`
+		Release struct {
+			ID          int       `json:"id"`
+			Title       string    `json:"name"`
+			Description string    `json:"body"`
+			Link        string    `json:"html_url,omitempty"`
+			Tag         string    `json:"tag_name,omitempty"`
+			Commitish   string    `json:"target_commitish,omitempty"`
+			Draft       bool      `json:"draft"`
+			Prerelease  bool      `json:"prerelease"`
+			Created     time.Time `json:"created_at"`
+			Published   time.Time `json:"published_at"`
+		} `json:"release"`
+		Repository repository `json:"repository"`
+		Sender     user       `json:"sender"`
+	}
+
+	pipelineHook struct {
+		Action      string `json:"action"`
+		Sender      user
+		WorkflowRun struct {
+			ID               int64       `json:"id"`
+			Name             string      `json:"name"`
+			NodeID           string      `json:"node_id"`
+			HeadBranch       string      `json:"head_branch"`
+			HeadSHA          string      `json:"head_sha"`
+			Path             string      `json:"path"`
+			DisplayTitle     string      `json:"display_title"`
+			RunNumber        int         `json:"run_number"`
+			Event            string      `json:"event"`
+			Status           string      `json:"status"`
+			Conclusion       null.String `json:"conclusion"`
+			WorkflowID       int64       `json:"workflow_id"`
+			CheckSuiteID     int64       `json:"check_suite_id"`
+			CheckSuiteNodeID string      `json:"check_suite_node_id"`
+			URL              string      `json:"url"`
+			HtmlURL          string      `json:"html_url"`
+			PullRequests     []struct {
+				URL    string `json:"url"`
+				ID     int64  `json:"id"`
+				Number int    `json:"number"`
+				Head   gitRef `json:"head"`
+				Base   gitRef `json:"base"`
+			} `json:"pull_requests"`
+			CreatedAt          time.Time   `json:"created_at"`
+			UpdatedAt          time.Time   `json:"updated_at"`
+			Actor              owner       `json:"actor"`
+			RunAttempt         int         `json:"run_attempt"`
+			RunStartedAt       time.Time   `json:"run_started_at"`
+			TriggeringActor    owner       `json:"triggering_actor"`
+			JobsURL            string      `json:"jobs_url"`
+			LogsURL            string      `json:"logs_url"`
+			CheckSuiteURL      string      `json:"check_suite_url"`
+			ArtifactsURL       string      `json:"artifacts_url"`
+			CancelURL          string      `json:"cancel_url"`
+			RerunURL           string      `json:"rerun_url"`
+			PreviousAttemptURL null.String `json:"previous_attempt_url"`
+			WorkflowURL        string      `json:"workflow_url"`
+			HeadCommit         struct {
+				ID        string    `json:"id"`
+				TreeID    string    `json:"tree_id"`
+				Message   string    `json:"message"`
+				Timestamp time.Time `json:"timestamp"`
+				Author    author    `json:"author"`
+				Committer author    `json:"committer"`
+			} `json:"head_commit"`
+		} `json:"workflow_run"`
+		Repository repository `json:"repository"`
+	}
+
+	gitRef struct {
+		Ref  string     `json:"ref"`
+		SHA  string     `json:"sha"`
+		Repo repository `json:"repo"`
+	}
+
+	owner struct {
+		Login     string `json:"login"`
+		ID        int64  `json:"id"`
+		NodeID    string `json:"node_id"`
+		AvatarURL string `json:"avatar_url"`
+		HTMLURL   string `json:"html_url"`
+		Type      string `json:"type"`
+		SiteAdmin bool   `json:"site_admin"`
+	}
+
+	author struct {
+		Name  string `json:"name"`
+		Email string `json:"email"`
+	}
 )
 
 //
@@ -354,7 +563,7 @@ func convertPushHook(src *pushHook) *scm.PushHook {
 			Name:       src.Repository.Name,
 			Branch:     src.Repository.DefaultBranch,
 			Private:    src.Repository.Private,
-			Visibility: convertVisibility(src.Repository.Visibility),
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
 			Clone:      src.Repository.CloneURL,
 			CloneSSH:   src.Repository.SSHURL,
 			Link:       src.Repository.HTMLURL,
@@ -381,7 +590,7 @@ func convertBranchHook(src *createDeleteHook) *scm.BranchHook {
 			Name:       src.Repository.Name,
 			Branch:     src.Repository.DefaultBranch,
 			Private:    src.Repository.Private,
-			Visibility: convertVisibility(src.Repository.Visibility),
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
 			Clone:      src.Repository.CloneURL,
 			CloneSSH:   src.Repository.SSHURL,
 			Link:       src.Repository.HTMLURL,
@@ -401,7 +610,7 @@ func convertTagHook(src *createDeleteHook) *scm.TagHook {
 			Name:       src.Repository.Name,
 			Branch:     src.Repository.DefaultBranch,
 			Private:    src.Repository.Private,
-			Visibility: convertVisibility(src.Repository.Visibility),
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
 			Clone:      src.Repository.CloneURL,
 			CloneSSH:   src.Repository.SSHURL,
 			Link:       src.Repository.HTMLURL,
@@ -419,7 +628,7 @@ func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
 			Name:       src.Repository.Name,
 			Branch:     src.Repository.DefaultBranch,
 			Private:    src.Repository.Private,
-			Visibility: convertVisibility(src.Repository.Visibility),
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
 			Clone:      src.Repository.CloneURL,
 			CloneSSH:   src.Repository.SSHURL,
 			Link:       src.Repository.HTMLURL,
@@ -429,6 +638,15 @@ func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
 	}
 }
 
+func convertPullRequestReviewHook(src *pullRequestHook) *scm.PullRequestHook {
+	return &scm.PullRequestHook{
+		// Action        Action
+		Repo:        *convertRepository(&src.Repository),
+		PullRequest: *convertPullRequest(&src.PullRequest),
+		Sender:      *convertUser(&src.Sender),
+	}
+}
+
 func convertDeploymentHook(src *deploymentHook) *scm.DeployHook {
 	dst := &scm.DeployHook{
 		Number: src.Deployment.ID,
@@ -445,7 +663,7 @@ func convertDeploymentHook(src *deploymentHook) *scm.DeployHook {
 			Name:       src.Repository.Name,
 			Branch:     src.Repository.DefaultBranch,
 			Private:    src.Repository.Private,
-			Visibility: convertVisibility(src.Repository.Visibility),
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
 			Clone:      src.Repository.CloneURL,
 			CloneSSH:   src.Repository.SSHURL,
 			Link:       src.Repository.HTMLURL,
@@ -479,7 +697,7 @@ func convertIssueCommentHook(src *issueCommentHook) *scm.IssueCommentHook {
 			Name:       src.Repository.Name,
 			Branch:     src.Repository.DefaultBranch,
 			Private:    src.Repository.Private,
-			Visibility: convertVisibility(src.Repository.Visibility),
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
 			Clone:      src.Repository.CloneURL,
 			CloneSSH:   src.Repository.SSHURL,
 			Link:       src.Repository.HTMLURL,
@@ -497,6 +715,36 @@ func convertIssueCommentHook(src *issueCommentHook) *scm.IssueCommentHook {
 	return dst
 }
 
+func convertReleaseHook(src *releaseHook) *scm.ReleaseHook {
+	dst := &scm.ReleaseHook{
+		Release: scm.Release{
+			ID:          src.Release.ID,
+			Title:       src.Release.Title,
+			Description: src.Release.Description,
+			Link:        src.Release.Link,
+			Tag:         src.Release.Tag,
+			Commitish:   src.Release.Commitish,
+			Draft:       src.Release.Draft,
+			Prerelease:  src.Release.Prerelease,
+			Created:     src.Release.Created,
+			Published:   src.Release.Published,
+		},
+		Repo: scm.Repository{
+			ID:         fmt.Sprint(src.Repository.ID),
+			Namespace:  src.Repository.Owner.Login,
+			Name:       src.Repository.Name,
+			Branch:     src.Repository.DefaultBranch,
+			Private:    src.Repository.Private,
+			Visibility: scm.ConvertVisibility(src.Repository.Visibility),
+			Clone:      src.Repository.CloneURL,
+			CloneSSH:   src.Repository.SSHURL,
+			Link:       src.Repository.HTMLURL,
+		},
+		Sender: *convertUser(&src.Sender),
+	}
+	return dst
+}
+
 // regexp help determine if the named git object is a tag.
 // this is not meant to be 100% accurate.
 var tagRE = regexp.MustCompile("^v?(\\d+).(.+)")
diff --git a/scm/driver/github/webhook_test.go b/scm/driver/github/webhook_test.go
index 3f62cb8c6..eacda18fa 100644
--- a/scm/driver/github/webhook_test.go
+++ b/scm/driver/github/webhook_test.go
@@ -25,9 +25,7 @@ func TestWebhooks(t *testing.T) {
 		after  string
 		obj    interface{}
 	}{
-		//
 		// push events
-		//
 
 		// push hooks
 		{
@@ -126,6 +124,13 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pr_sync.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
+		// pull request ready for review
+		{
+			event:  "pull_request",
+			before: "testdata/webhooks/pr_ready_for_review.json",
+			after:  "testdata/webhooks/pr_ready_for_review.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
 		// pull request opened
 		{
 			event:  "pull_request",
@@ -168,6 +173,19 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pr_unlabeled.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
+		{
+			event:  "pull_request_review",
+			before: "testdata/webhooks/pr_review_submitted.json",
+			after:  "testdata/webhooks/pr_review_submitted.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+
+		{
+			event:  "pull_request_review",
+			before: "testdata/webhooks/pr_review_edited.json",
+			after:  "testdata/webhooks/pr_review_edited.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
 
 		//
 		// deployment
@@ -185,6 +203,57 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/deployment_commit.json.golden",
 			obj:    new(scm.DeployHook),
 		},
+		//
+		// release
+		//
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_published.json",
+			after:  "testdata/webhooks/release_published.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_unpublished.json",
+			after:  "testdata/webhooks/release_unpublished.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_created.json",
+			after:  "testdata/webhooks/release_created.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_edited.json",
+			after:  "testdata/webhooks/release_edited.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_deleted.json",
+			after:  "testdata/webhooks/release_deleted.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_prereleased.json",
+			after:  "testdata/webhooks/release_prereleased.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "release",
+			before: "testdata/webhooks/release_released.json",
+			after:  "testdata/webhooks/release_released.json.golden",
+			obj:    new(scm.ReleaseHook),
+		},
+		{
+			event:  "workflow_run",
+			before: "testdata/webhooks/pipeline_hook.json",
+			after:  "testdata/webhooks/pipeline_hook.json.golden",
+			obj:    new(scm.PipelineHook),
+		},
 	}
 
 	for _, test := range tests {
@@ -202,7 +271,7 @@ func TestWebhooks(t *testing.T) {
 		buf := bytes.NewBuffer(before)
 		r, _ := http.NewRequest("GET", "/", buf)
 		r.Header.Set("X-GitHub-Event", test.event)
-		r.Header.Set("X-Hub-Signature", "sha1=380f462cd2e160b84765144beabdad2e930a7ec5")
+		r.Header.Set("X-Hub-Signature-256", "sha256=3bfbbc3bfc44498db2254f577b2e4bed201ece6163518ba91cb2c21f0f59d512")
 		r.Header.Set("X-GitHub-Delivery", "f2467dea-70d6-11e8-8955-3c83993e0aef")
 
 		s := new(webhookService)
@@ -223,7 +292,7 @@ func TestWebhooks(t *testing.T) {
 			t.Log(diff)
 
 			// debug only. remove once implemented
-			json.NewEncoder(os.Stdout).Encode(o)
+			_ = json.NewEncoder(os.Stdout).Encode(o)
 		}
 
 		switch event := o.(type) {
@@ -259,7 +328,7 @@ func TestWebhookInvalid(t *testing.T) {
 	r, _ := http.NewRequest("GET", "/", bytes.NewBuffer(f))
 	r.Header.Set("X-GitHub-Event", "push")
 	r.Header.Set("X-GitHub-Delivery", "ee8d97b4-1479-43f1-9cac-fbbd1b80da55")
-	r.Header.Set("X-Hub-Signature", "sha1=380f462cd2e160b84765144beabdad2e930a7ec5")
+	r.Header.Set("X-Hub-Signature-256", "sha256=3bfbbc3bfc44498db2254f577b2e4bed201ece6163518ba91cb2c21f0f59d512")
 
 	s := new(webhookService)
 	_, err := s.Parse(r, secretFunc)
@@ -269,6 +338,23 @@ func TestWebhookInvalid(t *testing.T) {
 }
 
 func TestWebhookValid(t *testing.T) {
+	// the sha can be recalculated with the below command
+	// openssl dgst -sha256 -hmac <secret> <file>
+
+	f, _ := ioutil.ReadFile("testdata/webhooks/push.json")
+	r, _ := http.NewRequest("GET", "/", bytes.NewBuffer(f))
+	r.Header.Set("X-GitHub-Event", "push")
+	r.Header.Set("X-GitHub-Delivery", "ee8d97b4-1479-43f1-9cac-fbbd1b80da55")
+	r.Header.Set("X-Hub-Signature-256", "sha256=e3bfe744d4e2e29ed990bde8acfb8255ca51ef65f99657767989fb6349f32957")
+
+	s := new(webhookService)
+	_, err := s.Parse(r, secretFunc)
+	if err != nil {
+		t.Errorf("Expect valid signature, got %v", err)
+	}
+}
+
+func TestWebhookSignatureFallback(t *testing.T) {
 	// the sha can be recalculated with the below command
 	// openssl dgst -sha1 -hmac <secret> <file>
 
diff --git a/scm/driver/gitlab/content.go b/scm/driver/gitlab/content.go
index f4263489c..118802220 100644
--- a/scm/driver/gitlab/content.go
+++ b/scm/driver/gitlab/content.go
@@ -19,7 +19,8 @@ type contentService struct {
 }
 
 func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
-	endpoint := fmt.Sprintf("api/v4/projects/%s/repository/files/%s?ref=%s", encode(repo), encodePath(path), ref)
+	urlEncodedRef := url.QueryEscape(ref)
+	endpoint := fmt.Sprintf("api/v4/projects/%s/repository/files/%s?ref=%s", encode(repo), encodePath(path), urlEncodedRef)
 	out := new(content)
 	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
 	raw, berr := base64.StdEncoding.DecodeString(out.Content)
@@ -32,8 +33,10 @@ func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm
 		}
 	}
 	return &scm.Content{
-		Path: out.FilePath,
-		Data: raw,
+		Path:   out.FilePath,
+		Data:   raw,
+		Sha:    out.LastCommitID,
+		BlobID: out.BlobID,
 	}, res, err
 }
 
@@ -61,13 +64,24 @@ func (s *contentService) Update(ctx context.Context, repo, path string, params *
 		Encoding:      "base64",
 		AuthorName:    params.Signature.Name,
 		AuthorEmail:   params.Signature.Email,
+		LastCommitID:  params.Sha,
 	}
 	res, err := s.client.do(ctx, "PUT", endpoint, in, nil)
 	return res, err
 }
 
-func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	endpoint := fmt.Sprintf("api/v4/projects/%s/repository/files/%s", encode(repo), encodePath(path))
+	in := &createUpdateContent{
+		Branch:        params.Branch,
+		CommitMessage: params.Message,
+		Encoding:      "base64",
+		AuthorName:    params.Signature.Name,
+		AuthorEmail:   params.Signature.Email,
+		LastCommitID:  params.Sha,
+	}
+	res, err := s.client.do(ctx, "DELETE", endpoint, in, nil)
+	return res, err
 }
 
 func (s *contentService) List(ctx context.Context, repo, path, ref string, opts scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
@@ -96,6 +110,7 @@ type createUpdateContent struct {
 	Encoding      string `json:"encoding"`
 	AuthorEmail   string `json:"author_email"`
 	AuthorName    string `json:"author_name"`
+	LastCommitID  string `json:"last_commit_id"`
 }
 
 type object struct {
diff --git a/scm/driver/gitlab/content_test.go b/scm/driver/gitlab/content_test.go
index 16e7b44a1..11cfa50a7 100644
--- a/scm/driver/gitlab/content_test.go
+++ b/scm/driver/gitlab/content_test.go
@@ -48,6 +48,35 @@ func TestContentFind(t *testing.T) {
 		t.Log(diff)
 	}
 
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/repository/files/app/models/key.rb").
+		MatchParam("ref", "b1&b2").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content.json")
+
+	client = NewDefault()
+	got, res, err = client.Contents.Find(
+		context.Background(),
+		"diaspora/diaspora",
+		"app/models/key.rb",
+		"b1&b2",
+	)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want = new(scm.Content)
+	raw, _ = ioutil.ReadFile("testdata/content.json.golden")
+	json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
 	t.Run("Request", testRequest(res))
 	t.Run("Rate", testRate(res))
 }
@@ -114,11 +143,59 @@ func TestContentUpdate(t *testing.T) {
 	}
 }
 
+func TestContentUpdateBadCommitID(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Put("/api/v4/projects/diaspora/diaspora/repository/files/app/project.rb").
+		Reply(400).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/content_update.json.fail")
+
+	client := NewDefault()
+	params := &scm.ContentParams{
+		Message: "update file",
+		Data:    []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
+		Sha:     "bad sha",
+		Signature: scm.Signature{
+			Name:  "Firstname Lastname",
+			Email: "kubesphere@example.com",
+		},
+	}
+
+	_, err := client.Contents.Update(context.Background(), "diaspora/diaspora", "app/project.rb", params)
+	if err.Error() != "You are attempting to update a file that has changed since you started editing it." {
+		t.Errorf("Expecting error 'You are attempting to update a file that has changed since you started editing it.'")
+	}
+}
+
 func TestContentDelete(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Delete(context.Background(), "octocat/hello-world", "README", "master")
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Delete("/api/v4/projects/diaspora/diaspora/repository/files/app/project.rb").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	params := &scm.ContentParams{
+		Message: "update file",
+		Signature: scm.Signature{
+			Name:  "Firstname Lastname",
+			Email: "kubesphere@example.com",
+		},
+	}
+
+	res, err := client.Contents.Delete(context.Background(), "diaspora/diaspora", "app/project.rb", params)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
 	}
 }
 
diff --git a/scm/driver/gitlab/git.go b/scm/driver/gitlab/git.go
index 7b7e78d09..3bc37ef7d 100644
--- a/scm/driver/gitlab/git.go
+++ b/scm/driver/gitlab/git.go
@@ -7,6 +7,8 @@ package gitlab
 import (
 	"context"
 	"fmt"
+	"net/url"
+	"strings"
 	"time"
 
 	"github.com/drone/go-scm/scm"
@@ -16,6 +18,15 @@ type gitService struct {
 	client *wrapper
 }
 
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/repository/branches", encode(repo))
+	in := &createBranch{
+		Branch: params.Name,
+		Ref:    params.Sha,
+	}
+	return s.client.do(ctx, "POST", path, in, nil)
+}
+
 func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
 	path := fmt.Sprintf("api/v4/projects/%s/repository/branches/%s", encode(repo), name)
 	out := new(branch)
@@ -24,6 +35,11 @@ func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Re
 }
 
 func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
+	// if the reference is a branch, ensure forward slashes
+	// in the branch name are escaped.
+	if strings.Contains("ref", "/") {
+		ref = url.PathEscape(ref)
+	}
 	path := fmt.Sprintf("api/v4/projects/%s/repository/commits/%s", encode(repo), scm.TrimRef(ref))
 	out := new(commit)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
@@ -44,6 +60,13 @@ func (s *gitService) ListBranches(ctx context.Context, repo string, opts scm.Lis
 	return convertBranchList(out), res, err
 }
 
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/repository/branches?%s", encode(repo), encodeBranchListOptions(opts))
+	out := []*branch{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertBranchList(out), res, err
+}
+
 func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
 	path := fmt.Sprintf("api/v4/projects/%s/repository/commits?%s", encode(repo), encodeCommitListOptions(opts))
 	out := []*commit{}
@@ -79,6 +102,11 @@ type branch struct {
 	}
 }
 
+type createBranch struct {
+	Branch string `json:"branch"`
+	Ref    string `json:"ref"`
+}
+
 type commit struct {
 	ID             string    `json:"id"`
 	Title          string    `json:"title"`
diff --git a/scm/driver/gitlab/git_test.go b/scm/driver/gitlab/git_test.go
index dab63658b..49fefee66 100644
--- a/scm/driver/gitlab/git_test.go
+++ b/scm/driver/gitlab/git_test.go
@@ -80,6 +80,33 @@ func TestGitFindBranch(t *testing.T) {
 	t.Run("Rate", testRate(res))
 }
 
+func TestGitCreateBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Post("/api/v4/projects/diaspora/diaspora/repository/branches").
+		Reply(201).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/branch_create.json")
+
+	params := &scm.ReferenceInput{
+		Name: "yooo",
+		Sha:  "0efb1bed7c6a4871cb4ddb862ecc2111e11f31ee",
+	}
+
+	client := NewDefault()
+	res, err := client.Git.CreateBranch(context.Background(), "diaspora/diaspora", params)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 201 {
+		t.Errorf("Unexpected Results")
+	}
+}
 func TestGitFindTag(t *testing.T) {
 	defer gock.Off()
 
@@ -179,6 +206,38 @@ func TestGitListBranches(t *testing.T) {
 	t.Run("Page", testPage(res))
 }
 
+func TestGitListBranchesWithBranchFilter(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/repository/branches").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/branches_filter.json")
+
+	client := NewDefault()
+	got, res, err := client.Git.ListBranchesV2(context.Background(), "diaspora/diaspora", scm.BranchListOptions{SearchTerm: "mast"})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches_filter.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
+
 func TestGitListTags(t *testing.T) {
 	defer gock.Off()
 
diff --git a/scm/driver/gitlab/gitlab.go b/scm/driver/gitlab/gitlab.go
index 1a907e511..4d92016cd 100644
--- a/scm/driver/gitlab/gitlab.go
+++ b/scm/driver/gitlab/gitlab.go
@@ -34,8 +34,10 @@ func New(uri string) (*scm.Client, error) {
 	client.Git = &gitService{client}
 	client.Issues = &issueService{client}
 	client.Organizations = &organizationService{client}
+	client.Milestones = &milestoneService{client}
 	client.PullRequests = &pullService{client}
 	client.Repositories = &repositoryService{client}
+	client.Releases = &releaseService{client}
 	client.Reviews = &reviewService{client}
 	client.Users = &userService{client}
 	client.Webhooks = &webhookService{client}
diff --git a/scm/driver/gitlab/integration/pr_test.go b/scm/driver/gitlab/integration/pr_test.go
index c11623066..64c64d05b 100644
--- a/scm/driver/gitlab/integration/pr_test.go
+++ b/scm/driver/gitlab/integration/pr_test.go
@@ -22,6 +22,7 @@ func testPullRequests(client *scm.Client) func(t *testing.T) {
 		t.Run("Find", testPullRequestFind(client))
 		t.Run("Changes", testPullRequestChanges(client))
 		t.Run("Comments", testPullRequestComments(client))
+		t.Run("Commits", testPullRequestCommitList(client))
 	}
 }
 
@@ -120,6 +121,22 @@ func testPullRequestChanges(client *scm.Client) func(t *testing.T) {
 	}
 }
 
+func testPullRequestCommitList(client *scm.Client) func(t *testing.T) {
+	return func(t *testing.T) {
+		t.Parallel()
+		opts := scm.ListOptions{}
+		result, _, err := client.PullRequests.ListCommits(context.Background(), "gitlab-org/testme", 1, opts)
+		if err != nil {
+			t.Error(err)
+		}
+		if len(result) == 0 {
+			t.Errorf("Got empty pull request commit list")
+			return
+		}
+		t.Run("Commit", testPullRequestCommit(result[0]))
+	}
+}
+
 //
 // struct sub-tests
 //
@@ -216,3 +233,32 @@ func testChange(change *scm.Change) func(t *testing.T) {
 		}
 	}
 }
+
+func testPullRequestCommit(commit *scm.Commit) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := commit.Message, "JS fix\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n"; got != want {
+			t.Errorf("Want commit Message %q, got %q", want, got)
+		}
+		if got, want := commit.Sha, "12d65c8dd2b2676fa3ac47d955accc085a37a9c1"; got != want {
+			t.Errorf("Want commit Sha %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Name, "Dmitriy Zaporozhets"; got != want {
+			t.Errorf("Want commit author Name %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Email, "dmitriy.zaporozhets@gmail.com"; got != want {
+			t.Errorf("Want commit author Email %q, got %q", want, got)
+		}
+		if got, want := commit.Author.Date.Unix(), int64(1393489620); got != want {
+			t.Errorf("Want commit author Date %d, got %d", want, got)
+		}
+		if got, want := commit.Committer.Name, "Dmitriy Zaporozhets"; got != want {
+			t.Errorf("Want commit author Name %q, got %q", want, got)
+		}
+		if got, want := commit.Committer.Email, "dmitriy.zaporozhets@gmail.com"; got != want {
+			t.Errorf("Want commit author Email %q, got %q", want, got)
+		}
+		if got, want := commit.Committer.Date.Unix(), int64(1393489620); got != want {
+			t.Errorf("Want commit author Date %d, got %d", want, got)
+		}
+	}
+}
diff --git a/scm/driver/gitlab/milestone.go b/scm/driver/gitlab/milestone.go
new file mode 100644
index 000000000..111897508
--- /dev/null
+++ b/scm/driver/gitlab/milestone.go
@@ -0,0 +1,167 @@
+package gitlab
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+	"net/url"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+// isoTime represents an ISO 8601 formatted date
+type isoTime time.Time
+
+// ISO 8601 date format
+const iso8601 = "2006-01-02"
+
+// MarshalJSON implements the json.Marshaler interface
+func (t isoTime) MarshalJSON() ([]byte, error) {
+	if y := time.Time(t).Year(); y < 0 || y >= 10000 {
+		// ISO 8901 uses 4 digits for the years
+		return nil, errors.New("json: ISOTime year outside of range [0,9999]")
+	}
+
+	b := make([]byte, 0, len(iso8601)+2)
+	b = append(b, '"')
+	b = time.Time(t).AppendFormat(b, iso8601)
+	b = append(b, '"')
+
+	return b, nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface
+func (t *isoTime) UnmarshalJSON(data []byte) error {
+	// Ignore null, like in the main JSON package
+	if string(data) == "null" {
+		return nil
+	}
+
+	isotime, err := time.Parse(`"`+iso8601+`"`, string(data))
+	*t = isoTime(isotime)
+
+	return err
+}
+
+// EncodeValues implements the query.Encoder interface
+func (t *isoTime) EncodeValues(key string, v *url.Values) error {
+	if t == nil || (time.Time(*t)).IsZero() {
+		return nil
+	}
+	v.Add(key, t.String())
+	return nil
+}
+
+// String implements the Stringer interface
+func (t isoTime) String() string {
+	return time.Time(t).Format(iso8601)
+}
+
+type milestone struct {
+	ID          int       `json:"id"`
+	IID         int       `json:"iid"`
+	ProjectID   int       `json:"project_id"`
+	Title       string    `json:"title"`
+	Description string    `json:"description"`
+	State       string    `json:"state"`
+	DueDate     isoTime   `json:"due_date"`
+	StartDate   isoTime   `json:"start_date"`
+	CreatedAt   null.Time `json:"created_at"`
+	UpdatedAt   null.Time `json:"updated_at"`
+	Expired     bool      `json:"expired"`
+}
+
+type milestoneInput struct {
+	Title       *string `json:"title"`
+	StateEvent  *string `json:"state_event,omitempty"`
+	Description *string `json:"description"`
+	DueDate     isoTime `json:"due_date"`
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/milestones/%d", encode(repo), id)
+	out := new(milestone)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertMilestone(out), res, err
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/milestones?%s", encode(repo), encodeMilestoneListOptions(opts))
+	out := []*milestone{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertMilestoneList(out), res, err
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/milestones", encode(repo))
+	dueDateIso := isoTime(input.DueDate)
+	in := &milestoneInput{
+		Title:       &input.Title,
+		Description: &input.Description,
+		DueDate:     dueDateIso,
+	}
+	out := new(milestone)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertMilestone(out), res, err
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/milestones/%d", encode(repo), id)
+	res, err := s.client.do(ctx, "DELETE", path, nil, nil)
+	return res, err
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/milestones/%d", encode(repo), id)
+	in := &milestoneInput{}
+	if input.Title != "" {
+		in.Title = &input.Title
+	}
+	if input.State != "" {
+		if input.State == "open" {
+			activate := "activate"
+			in.StateEvent = &activate
+		} else {
+			in.StateEvent = &input.State
+		}
+	}
+	if input.Description != "" {
+		in.Description = &input.Description
+	}
+	if !input.DueDate.IsZero() {
+		dueDateIso := isoTime(input.DueDate)
+		in.DueDate = dueDateIso
+	}
+	out := new(milestone)
+	res, err := s.client.do(ctx, "PATCH", path, in, out)
+	return convertMilestone(out), res, err
+}
+
+func convertMilestoneList(from []*milestone) []*scm.Milestone {
+	var to []*scm.Milestone
+	for _, m := range from {
+		to = append(to, convertMilestone(m))
+	}
+	return to
+}
+
+func convertMilestone(from *milestone) *scm.Milestone {
+	if from == nil || from.Title == "" {
+		return nil
+	}
+	dueDate := time.Time(from.DueDate)
+	return &scm.Milestone{
+		Number:      from.ID,
+		ID:          from.ID,
+		Title:       from.Title,
+		Description: from.Description,
+		State:       from.State,
+		DueDate:     dueDate,
+	}
+}
diff --git a/scm/driver/gitlab/milestone_test.go b/scm/driver/gitlab/milestone_test.go
new file mode 100644
index 000000000..98dfaa65e
--- /dev/null
+++ b/scm/driver/gitlab/milestone_test.go
@@ -0,0 +1,187 @@
+package gitlab
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestMilestoneFind(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/milestones/1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestone.json")
+
+	client := NewDefault()
+	got, res, err := client.Milestones.Find(context.Background(), "diaspora/diaspora", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Milestone)
+	raw, err := ioutil.ReadFile("testdata/milestone.json.golden")
+	if err != nil {
+		t.Fatalf("ioutil.ReadFile: %v", err)
+	}
+	if err := json.Unmarshal(raw, want); err != nil {
+		t.Fatalf("json.Unmarshal: %v", err)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/milestones").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestones.json")
+
+	client := NewDefault()
+	got, res, err := client.Milestones.List(context.Background(), "diaspora/diaspora", scm.MilestoneListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Milestone{}
+	raw, err := ioutil.ReadFile("testdata/milestones.json.golden")
+	if err != nil {
+		t.Fatalf("ioutil.ReadFile: %v", err)
+	}
+	if err := json.Unmarshal(raw, &want); err != nil {
+		t.Fatalf("json.Unmarshal: %v", err)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Post("/api/v4/projects/diaspora/diaspora/milestones").
+		File("testdata/milestone_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestone.json")
+
+	client := NewDefault()
+	dueDate, _ := time.Parse(scm.SearchTimeFormat, "2012-10-09T23:39:01Z")
+	input := &scm.MilestoneInput{
+		Title:       "v1.0",
+		Description: "Tracking milestone for version 1.0",
+		State:       "open",
+		DueDate:     dueDate,
+	}
+	got, res, err := client.Milestones.Create(context.Background(), "diaspora/diaspora", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Milestone)
+	raw, err := ioutil.ReadFile("testdata/milestone.json.golden")
+	if err != nil {
+		t.Fatalf("ioutil.ReadFile: %v", err)
+	}
+	if err := json.Unmarshal(raw, &want); err != nil {
+		t.Fatalf("json.Unmarshal: %v", err)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneUpdate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Patch("/api/v4/projects/diaspora/diaspora/milestones/1").
+		File("testdata/milestone_update.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/milestone.json")
+
+	client := NewDefault()
+	dueDate, _ := time.Parse(scm.SearchTimeFormat, "2012-10-09T23:39:01Z")
+	input := &scm.MilestoneInput{
+		Title:       "v1.0",
+		Description: "Tracking milestone for version 1.0",
+		State:       "close",
+		DueDate:     dueDate,
+	}
+	got, res, err := client.Milestones.Update(context.Background(), "diaspora/diaspora", 1, input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Milestone)
+	raw, err := ioutil.ReadFile("testdata/milestone.json.golden")
+	if err != nil {
+		t.Fatalf("ioutil.ReadFile: %v", err)
+	}
+	if err := json.Unmarshal(raw, &want); err != nil {
+		t.Fatalf("json.Unmarshal: %v", err)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestMilestoneDelete(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Delete("/api/v4/projects/diaspora/diaspora/milestones/1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	_, err := client.Milestones.Delete(context.Background(), "diaspora/diaspora", 1)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+}
diff --git a/scm/driver/gitlab/pr.go b/scm/driver/gitlab/pr.go
index 9a6ca0d6e..bb63590a6 100644
--- a/scm/driver/gitlab/pr.go
+++ b/scm/driver/gitlab/pr.go
@@ -52,15 +52,31 @@ func (s *pullService) ListComments(ctx context.Context, repo string, index int,
 	return convertIssueCommentList(out), res, err
 }
 
+func (s *pullService) ListCommits(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/merge_requests/%d/commits?%s", encode(repo), number, encodeListOptions(opts))
+	out := []*commit{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertCommitList(out), res, err
+}
+
 func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
-	in := url.Values{}
-	in.Set("title", input.Title)
-	in.Set("description", input.Body)
-	in.Set("source_branch", input.Source)
-	in.Set("target_branch", input.Target)
-	path := fmt.Sprintf("api/v4/projects/%s/merge_requests?%s", encode(repo), in.Encode())
+	// https://docs.gitlab.com/ee/api/merge_requests.html#create-mr
+	in := struct {
+		Title        string `json:"title"`
+		Description  string `json:"description"`
+		SourceBranch string `json:"source_branch"`
+		TargetBranch string `json:"target_branch"`
+	}{
+		Title:        input.Title,
+		Description:  input.Body,
+		SourceBranch: input.Source,
+		TargetBranch: input.Target,
+	}
+
+	path := fmt.Sprintf("api/v4/projects/%s/merge_requests", encode(repo))
+
 	out := new(pr)
-	res, err := s.client.do(ctx, "POST", path, nil, out)
+	res, err := s.client.do(ctx, "POST", path, in, out)
 	return convertPullRequest(out), res, err
 }
 
@@ -92,13 +108,14 @@ func (s *pullService) Close(ctx context.Context, repo string, number int) (*scm.
 }
 
 type pr struct {
-	Number int    `json:"iid"`
-	Sha    string `json:"sha"`
-	Title  string `json:"title"`
-	Desc   string `json:"description"`
-	State  string `json:"state"`
-	Link   string `json:"web_url"`
-	Author struct {
+	Number         int    `json:"iid"`
+	Sha            string `json:"sha"`
+	Title          string `json:"title"`
+	Desc           string `json:"description"`
+	State          string `json:"state"`
+	WorkInProgress bool   `json:"work_in_progress"`
+	Link           string `json:"web_url"`
+	Author         struct {
 		Username string `json:"username"`
 		Email    string `json:"email"`
 		Name     string `json:"name"`
@@ -110,6 +127,11 @@ type pr struct {
 	Updated      time.Time `json:"updated_at"`
 	Closed       time.Time
 	Labels       []string `json:"labels"`
+	DiffRefs     struct {
+		BaseSha  string `json:"base_sha"`
+		HeadSha  string `json:"head_sha"`
+		StartSha string `json:"start_sha"`
+	} `json:"diff_refs"`
 }
 
 type changes struct {
@@ -148,6 +170,7 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Source: from.SourceBranch,
 		Target: from.TargetBranch,
 		Link:   from.Link,
+		Draft:  from.WorkInProgress,
 		Closed: from.State != "opened",
 		Merged: from.State == "merged",
 		Author: scm.User{
@@ -158,6 +181,9 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Created: from.Created,
 		Updated: from.Updated,
 		Labels:  labels,
+		Base: scm.Reference{
+			Sha: from.DiffRefs.BaseSha,
+		},
 	}
 }
 
diff --git a/scm/driver/gitlab/pr_test.go b/scm/driver/gitlab/pr_test.go
index 032e9a382..c36ab067e 100644
--- a/scm/driver/gitlab/pr_test.go
+++ b/scm/driver/gitlab/pr_test.go
@@ -162,10 +162,6 @@ func TestPullCreate(t *testing.T) {
 
 	gock.New("https://gitlab.com").
 		Post("/api/v4/projects/diaspora/diaspora/merge_requests").
-		MatchParam("title", input.Title).
-		MatchParam("description", input.Body).
-		MatchParam("source_branch", input.Source).
-		MatchParam("target_branch", input.Target).
 		Reply(201).
 		Type("application/json").
 		SetHeaders(mockHeaders).
@@ -309,3 +305,32 @@ func TestPullCommentDelete(t *testing.T) {
 	t.Run("Request", testRequest(res))
 	t.Run("Rate", testRate(res))
 }
+
+func TestPullListCommits(t *testing.T) {
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/merge_requests/1347/commits").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/commits.json")
+
+	client := NewDefault()
+	got, res, err := client.PullRequests.ListCommits(context.Background(), "diaspora/diaspora", 1347, scm.ListOptions{Page: 1, Size: 30})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
diff --git a/scm/driver/gitlab/release.go b/scm/driver/gitlab/release.go
new file mode 100644
index 000000000..874c728ee
--- /dev/null
+++ b/scm/driver/gitlab/release.go
@@ -0,0 +1,109 @@
+package gitlab
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+type release struct {
+	Title       string `json:"name"`
+	Description string `json:"description"`
+	Tag         string `json:"tag_name"`
+	Commit      struct {
+		ID string `json:"id"`
+	} `json:"commit"`
+}
+
+type releaseInput struct {
+	Title       string `json:"name"`
+	Description string `json:"description"`
+	Tag         string `json:"tag_name"`
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/releases/%s", encode(repo), tag)
+	out := new(release)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/releases", encode(repo))
+	out := []*release{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertReleaseList(out), res, err
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/releases", encode(repo))
+	in := &releaseInput{
+		Title:       input.Title,
+		Description: input.Description,
+		Tag:         input.Tag,
+	}
+	out := new(release)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertRelease(out), res, err
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/releases/%s", encode(repo), tag)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	// this could be implemented by List and filter but would be to expensive
+	panic("gitlab only allows to update a release by tag")
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/projects/%s/releases/%s", encode(repo), tag)
+	in := &releaseInput{}
+	if input.Title != "" {
+		in.Title = input.Title
+	}
+	if input.Description != "" {
+		in.Description = input.Description
+	}
+	if input.Tag != "" {
+		in.Tag = input.Tag
+	}
+	out := new(release)
+	res, err := s.client.do(ctx, "PUT", path, in, out)
+	return convertRelease(out), res, err
+}
+
+func convertReleaseList(from []*release) []*scm.Release {
+	var to []*scm.Release
+	for _, m := range from {
+		to = append(to, convertRelease(m))
+	}
+	return to
+}
+
+func convertRelease(from *release) *scm.Release {
+	return &scm.Release{
+		ID:          0,
+		Title:       from.Title,
+		Description: from.Description,
+		Link:        "",
+		Tag:         from.Tag,
+		Commitish:   from.Commit.ID,
+		Draft:       false, // not supported by gitlab
+		Prerelease:  false, // not supported by gitlab
+	}
+}
diff --git a/scm/driver/gitlab/release_test.go b/scm/driver/gitlab/release_test.go
new file mode 100644
index 000000000..998654923
--- /dev/null
+++ b/scm/driver/gitlab/release_test.go
@@ -0,0 +1,191 @@
+package gitlab
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestReleaseFindByTag(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/releases/v1.0.1").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/release.json")
+
+	client := NewDefault()
+	got, res, err := client.Releases.FindByTag(context.Background(), "diaspora/diaspora", "v1.0.1")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects/diaspora/diaspora/releases").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/releases.json")
+
+	client := NewDefault()
+	got, res, err := client.Releases.List(context.Background(), "diaspora/diaspora", scm.ReleaseListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Release{}
+	raw, _ := ioutil.ReadFile("testdata/releases.json.golden")
+	err = json.Unmarshal(raw, &want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseCreate(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Post("/api/v4/projects/diaspora/diaspora/releases").
+		File("testdata/release_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/release.json")
+
+	client := NewDefault()
+	input := &scm.ReleaseInput{
+		Title:       "v1.0",
+		Description: "Tracking release for version 1.0",
+		Tag:         "v1.0",
+	}
+
+	got, res, err := client.Releases.Create(context.Background(), "diaspora/diaspora", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+
+		data, _ := json.Marshal(got)
+		t.Log("got JSON:")
+		t.Log(string(data))
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseUpdateByTag(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Put("/api/v4/projects/diaspora/diaspora/releases/v1.0").
+		File("testdata/release_create.json").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		File("testdata/release.json")
+
+	client := NewDefault()
+	input := &scm.ReleaseInput{
+		Title:       "v1.0",
+		Description: "Tracking release for version 1.0",
+		Tag:         "v1.0",
+	}
+
+	got, res, err := client.Releases.UpdateByTag(context.Background(), "diaspora/diaspora", "v1.0", input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Release)
+	raw, _ := ioutil.ReadFile("testdata/release.json.golden")
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
+
+func TestReleaseDeleteByTag(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Delete("/api/v4/projects/diaspora/diaspora/releases/v1.0").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders)
+
+	client := NewDefault()
+	res, err := client.Releases.DeleteByTag(context.Background(), "diaspora/diaspora", "v1.0")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+}
diff --git a/scm/driver/gitlab/repo.go b/scm/driver/gitlab/repo.go
index 4ed8b39e5..7a041a8fb 100644
--- a/scm/driver/gitlab/repo.go
+++ b/scm/driver/gitlab/repo.go
@@ -22,6 +22,7 @@ type repository struct {
 	PathNamespace string      `json:"path_with_namespace"`
 	DefaultBranch string      `json:"default_branch"`
 	Visibility    string      `json:"visibility"`
+	Archived      bool        `json:"archived"`
 	WebURL        string      `json:"web_url"`
 	SSHURL        string      `json:"ssh_url_to_repo"`
 	HTTPURL       string      `json:"http_url_to_repo"`
@@ -93,6 +94,21 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
 	return convertRepositoryList(out), res, err
 }
 
+func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// We pass the repo searchTerm in the query params and gitlab filters repos based on this search term
+	path := fmt.Sprintf("api/v4/projects?%s", encodeRepoListOptions(opts))
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+
+func (s *repositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/groups/%s/projects?%s", namespace, encodeMemberListOptions(opts))
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+
 func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
 	path := fmt.Sprintf("api/v4/projects/%s/hooks?%s", encode(repo), encodeListOptions(opts))
 	out := []*hook{}
@@ -135,6 +151,9 @@ func (s *repositoryService) CreateHook(ctx context.Context, repo string, input *
 	if input.Events.Tag {
 		params.Set("tag_push_events", "true")
 	}
+	if input.Events.Pipeline {
+		params.Set("pipeline_events", "true")
+	}
 
 	path := fmt.Sprintf("api/v4/projects/%s/hooks?%s", encode(repo), params.Encode())
 	out := new(hook)
@@ -162,7 +181,7 @@ func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id stri
 	return s.client.do(ctx, "DELETE", path, nil, nil)
 }
 
-// helper function to convert from the gogs repository list to
+// helper function to convert from the gitlab repository list to
 // the common repository structure.
 func convertRepositoryList(from []*repository) []*scm.Repository {
 	to := []*scm.Repository{}
@@ -172,7 +191,7 @@ func convertRepositoryList(from []*repository) []*scm.Repository {
 	return to
 }
 
-// helper function to convert from the gogs repository structure
+// helper function to convert from the gitlab repository structure
 // to the common repository structure.
 func convertRepository(from *repository) *scm.Repository {
 	to := &scm.Repository{
@@ -180,8 +199,9 @@ func convertRepository(from *repository) *scm.Repository {
 		Namespace:  from.Namespace.Path,
 		Name:       from.Path,
 		Branch:     from.DefaultBranch,
-		Private:    convertPrivate(from.Visibility),
-		Visibility: convertVisibility(from.Visibility),
+		Archived:   from.Archived,
+		Private:    scm.ConvertPrivate(from.Visibility),
+		Visibility: scm.ConvertVisibility(from.Visibility),
 		Clone:      from.HTTPURL,
 		CloneSSH:   from.SSHURL,
 		Link:       from.WebURL,
@@ -300,28 +320,6 @@ func convertFromState(from scm.State) string {
 	}
 }
 
-func convertPrivate(from string) bool {
-	switch from {
-	case "public", "":
-		return false
-	default:
-		return true
-	}
-}
-
-func convertVisibility(from string) scm.Visibility {
-	switch from {
-	case "public":
-		return scm.VisibilityPublic
-	case "private":
-		return scm.VisibilityPrivate
-	case "internal":
-		return scm.VisibilityInternal
-	default:
-		return scm.VisibilityUndefined
-	}
-}
-
 func canPush(proj *repository) bool {
 	switch {
 	case proj.Permissions.ProjectAccess.AccessLevel >= 30:
diff --git a/scm/driver/gitlab/repo_test.go b/scm/driver/gitlab/repo_test.go
index 48e21db6b..65c175176 100644
--- a/scm/driver/gitlab/repo_test.go
+++ b/scm/driver/gitlab/repo_test.go
@@ -163,6 +163,45 @@ func TestRepositoryList(t *testing.T) {
 	t.Run("Page", testPage(res))
 }
 
+func TestRepositoryListV2(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/projects").
+		MatchParam("search", "diaspora").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		MatchParam("membership", "true").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/repos_filter.json")
+
+	client := NewDefault()
+	got, res, err := client.Repositories.ListV2(context.Background(), scm.RepoListOptions{
+		ListOptions:    scm.ListOptions{Page: 1, Size: 30},
+		RepoSearchTerm: scm.RepoSearchTerm{RepoName: "diaspora"},
+	})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos_filter.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
+
 func TestStatusList(t *testing.T) {
 	defer gock.Off()
 
@@ -482,25 +521,6 @@ func TestConvertFromState(t *testing.T) {
 	}
 }
 
-func TestConvertPrivate(t *testing.T) {
-	tests := []struct {
-		in  string
-		out bool
-	}{
-		{"public", false},
-		{"", false},
-		{"private", true},
-		{"internal", true},
-		{"invalid", true},
-	}
-
-	for _, test := range tests {
-		if got, want := convertPrivate(test.in), test.out; got != want {
-			t.Errorf("Want private %v, got %v", want, got)
-		}
-	}
-}
-
 func TestCanPush(t *testing.T) {
 	tests := []struct {
 		in  *repository
diff --git a/scm/driver/gitlab/testdata/branch_create.json b/scm/driver/gitlab/testdata/branch_create.json
new file mode 100644
index 000000000..2c82cc7a3
--- /dev/null
+++ b/scm/driver/gitlab/testdata/branch_create.json
@@ -0,0 +1,27 @@
+{
+    "name": "yooo",
+    "commit": {
+        "id": "0efb1bed7c6a4871cb4ddb862ecc2111e11f31ee",
+        "short_id": "0efb1bed",
+        "created_at": "2021-04-07T08:16:54.000+00:00",
+        "parent_ids": [
+            "4d8cf0b0d2a3f9dd54ec1f204a95f08a0f318199"
+        ],
+        "title": "message1",
+        "message": "message1",
+        "author_name": "tp honey",
+        "author_email": "tp@harness.io",
+        "authored_date": "2021-04-07T08:16:54.000+00:00",
+        "committer_name": "TP Honey",
+        "committer_email": "tp@harness.io",
+        "committed_date": "2021-04-07T08:16:54.000+00:00",
+        "web_url": "https://gitlab.com/tphoney/test_repo/-/commit/0efb1bed7c6a4871cb4ddb862ecc2111e11f31ee"
+    },
+    "merged": true,
+    "protected": false,
+    "developers_can_push": false,
+    "developers_can_merge": false,
+    "can_push": true,
+    "default": false,
+    "web_url": "https://gitlab.com/tphoney/test_repo/-/tree/yooo"
+}
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/branches_filter.json b/scm/driver/gitlab/testdata/branches_filter.json
new file mode 100644
index 000000000..c5072e736
--- /dev/null
+++ b/scm/driver/gitlab/testdata/branches_filter.json
@@ -0,0 +1,46 @@
+[
+  {
+    "name": "master",
+    "merged": false,
+    "protected": true,
+    "developers_can_push": false,
+    "developers_can_merge": false,
+    "commit": {
+      "author_email": "john@example.com",
+      "author_name": "John Smith",
+      "authored_date": "2012-06-27T05:51:39-07:00",
+      "committed_date": "2012-06-28T03:44:20-07:00",
+      "committer_email": "john@example.com",
+      "committer_name": "John Smith",
+      "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c",
+      "short_id": "7b5c3cc",
+      "title": "add projects API",
+      "message": "add projects API",
+      "parent_ids": [
+        "4ad91d3c1144c406e50c7b33bae684bd6837faf8"
+      ]
+    }
+  },
+  {
+    "name": "master-patch",
+    "merged": false,
+    "protected": true,
+    "developers_can_push": false,
+    "developers_can_merge": false,
+    "commit": {
+      "author_email": "john@example.com",
+      "author_name": "John Smith",
+      "authored_date": "2012-06-27T05:51:39-07:00",
+      "committed_date": "2012-06-28T03:44:20-07:00",
+      "committer_email": "john@example.com",
+      "committer_name": "John Smith",
+      "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0d",
+      "short_id": "7b5c3cc",
+      "title": "add projects API",
+      "message": "add projects API",
+      "parent_ids": [
+        "4ad91d3c1144c406e50c7b33bae684bd6837faf9"
+      ]
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/branches_filter.json.golden b/scm/driver/gitlab/testdata/branches_filter.json.golden
new file mode 100644
index 000000000..d6f1a9210
--- /dev/null
+++ b/scm/driver/gitlab/testdata/branches_filter.json.golden
@@ -0,0 +1,12 @@
+[
+    {
+        "Name": "master",
+        "Path": "refs/heads/master",
+        "Sha": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c"
+    },
+    {
+        "Name": "master-patch",
+        "Path": "refs/heads/master-patch",
+        "Sha": "7b5c3cc8be40ee161ae89a06bba6229da1032a0d"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/content.json.golden b/scm/driver/gitlab/testdata/content.json.golden
index 73abcc029..0d3df102b 100644
--- a/scm/driver/gitlab/testdata/content.json.golden
+++ b/scm/driver/gitlab/testdata/content.json.golden
@@ -1,4 +1,6 @@
 {
     "Path": "app/models/key.rb",
-    "Data": "cmVxdWlyZSAnZGlnZXN0L21kNScKCmNsYXNzIEtleSA8IEFjdGl2ZVJlY29yZDo6QmFzZQogIGluY2x1ZGUgR2l0bGFiOjpDdXJyZW50U2V0dGluZ3MKICBpbmNsdWRlIFNvcnRhYmxlCgogIGJlbG9uZ3NfdG8gOnVzZXIKCiAgYmVmb3JlX3ZhbGlkYXRpb24gOmdlbmVyYXRlX2ZpbmdlcnByaW50CgogIHZhbGlkYXRlcyA6dGl0bGUsCiAgICBwcmVzZW5jZTogdHJ1ZSwKICAgIGxlbmd0aDogeyBtYXhpbXVtOiAyNTUgfQoKICB2YWxpZGF0ZXMgOmtleSwKICAgIHByZXNlbmNlOiB0cnVlLAogICAgbGVuZ3RoOiB7IG1heGltdW06IDUwMDAgfSwKICAgIGZvcm1hdDogeyB3aXRoOiAvXEEoc3NofGVjZHNhKS0uKlxaLyB9CgogIHZhbGlkYXRlcyA6ZmluZ2VycHJpbnQsCiAgICB1bmlxdWVuZXNzOiB0cnVlLAogICAgcHJlc2VuY2U6IHsgbWVzc2FnZTogJ2Nhbm5vdCBiZSBnZW5lcmF0ZWQnIH0KCiAgdmFsaWRhdGUgOmtleV9tZWV0c19yZXN0cmljdGlvbnMKCiAgZGVsZWdhdGUgOm5hbWUsIDplbWFpbCwgdG86IDp1c2VyLCBwcmVmaXg6IHRydWUKCiAgYWZ0ZXJfY29tbWl0IDphZGRfdG9fc2hlbGwsIG9uOiA6Y3JlYXRlCiAgYWZ0ZXJfY3JlYXRlIDpwb3N0X2NyZWF0ZV9ob29rCiAgYWZ0ZXJfY3JlYXRlIDpyZWZyZXNoX3VzZXJfY2FjaGUKICBhZnRlcl9jb21taXQgOnJlbW92ZV9mcm9tX3NoZWxsLCBvbjogOmRlc3Ryb3kKICBhZnRlcl9kZXN0cm95IDpwb3N0X2Rlc3Ryb3lfaG9vawogIGFmdGVyX2Rlc3Ryb3kgOnJlZnJlc2hfdXNlcl9jYWNoZQoKICBkZWYga2V5PSh2YWx1ZSkKICAgIHZhbHVlJi5kZWxldGUhKCJcblxyIikKICAgIHZhbHVlLnN0cmlwISB1bmxlc3MgdmFsdWUuYmxhbms="
+    "Data": "cmVxdWlyZSAnZGlnZXN0L21kNScKCmNsYXNzIEtleSA8IEFjdGl2ZVJlY29yZDo6QmFzZQogIGluY2x1ZGUgR2l0bGFiOjpDdXJyZW50U2V0dGluZ3MKICBpbmNsdWRlIFNvcnRhYmxlCgogIGJlbG9uZ3NfdG8gOnVzZXIKCiAgYmVmb3JlX3ZhbGlkYXRpb24gOmdlbmVyYXRlX2ZpbmdlcnByaW50CgogIHZhbGlkYXRlcyA6dGl0bGUsCiAgICBwcmVzZW5jZTogdHJ1ZSwKICAgIGxlbmd0aDogeyBtYXhpbXVtOiAyNTUgfQoKICB2YWxpZGF0ZXMgOmtleSwKICAgIHByZXNlbmNlOiB0cnVlLAogICAgbGVuZ3RoOiB7IG1heGltdW06IDUwMDAgfSwKICAgIGZvcm1hdDogeyB3aXRoOiAvXEEoc3NofGVjZHNhKS0uKlxaLyB9CgogIHZhbGlkYXRlcyA6ZmluZ2VycHJpbnQsCiAgICB1bmlxdWVuZXNzOiB0cnVlLAogICAgcHJlc2VuY2U6IHsgbWVzc2FnZTogJ2Nhbm5vdCBiZSBnZW5lcmF0ZWQnIH0KCiAgdmFsaWRhdGUgOmtleV9tZWV0c19yZXN0cmljdGlvbnMKCiAgZGVsZWdhdGUgOm5hbWUsIDplbWFpbCwgdG86IDp1c2VyLCBwcmVmaXg6IHRydWUKCiAgYWZ0ZXJfY29tbWl0IDphZGRfdG9fc2hlbGwsIG9uOiA6Y3JlYXRlCiAgYWZ0ZXJfY3JlYXRlIDpwb3N0X2NyZWF0ZV9ob29rCiAgYWZ0ZXJfY3JlYXRlIDpyZWZyZXNoX3VzZXJfY2FjaGUKICBhZnRlcl9jb21taXQgOnJlbW92ZV9mcm9tX3NoZWxsLCBvbjogOmRlc3Ryb3kKICBhZnRlcl9kZXN0cm95IDpwb3N0X2Rlc3Ryb3lfaG9vawogIGFmdGVyX2Rlc3Ryb3kgOnJlZnJlc2hfdXNlcl9jYWNoZQoKICBkZWYga2V5PSh2YWx1ZSkKICAgIHZhbHVlJi5kZWxldGUhKCJcblxyIikKICAgIHZhbHVlLnN0cmlwISB1bmxlc3MgdmFsdWUuYmxhbms=",
+    "Sha": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d",
+    "BlobID": "79f7bbd25901e8334750839545a9bd021f0e4c83"
 }
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/content_list.json b/scm/driver/gitlab/testdata/content_list.json
index 333176ee6..7a4d1b60b 100644
--- a/scm/driver/gitlab/testdata/content_list.json
+++ b/scm/driver/gitlab/testdata/content_list.json
@@ -1 +1,142 @@
-[{"id":"ff85b01e96cca1db128a3ea0b3643667f50e464f","name":"ansi2json","type":"tree","path":"lib/gitlab/ci/ansi2json","mode":"040000"},{"id":"b044e0902a1d468d0e59ca733da6c22c4710af20","name":"build","type":"tree","path":"lib/gitlab/ci/build","mode":"040000"},{"id":"80cecd7647164c63768f52b431509d04d7f7dcf6","name":"config","type":"tree","path":"lib/gitlab/ci/config","mode":"040000"},{"id":"1d53ca741903e40610728c07b637da21fbf9f394","name":"parsers","type":"tree","path":"lib/gitlab/ci/parsers","mode":"040000"},{"id":"1a8b0f4be2707c3d7bdede2865c4a2c8a4af99a9","name":"pipeline","type":"tree","path":"lib/gitlab/ci/pipeline","mode":"040000"},{"id":"42afb52861c8f807f6ccf95e98607638b6d30f22","name":"reports","type":"tree","path":"lib/gitlab/ci/reports","mode":"040000"},{"id":"1cf9d5d945923d8a3f820b5b39aa65431b271375","name":"status","type":"tree","path":"lib/gitlab/ci/status","mode":"040000"},{"id":"f02f0b133c976074cec2b3babc88966f2ef32374","name":"templates","type":"tree","path":"lib/gitlab/ci/templates","mode":"040000"},{"id":"f2f31cbc4e89da99530cd30d7fc5f4d989d05b90","name":"trace","type":"tree","path":"lib/gitlab/ci/trace","mode":"040000"},{"id":"8d06f84b4a11604dacb1e37eb3c99094f2bccbd8","name":"variables","type":"tree","path":"lib/gitlab/ci/variables","mode":"040000"},{"id":"eb5d78ebcd41aee923ad02c8fe4b340d87254d59","name":"ansi2html.rb","type":"blob","path":"lib/gitlab/ci/ansi2html.rb","mode":"100644"},{"id":"79114d35916cc46aff83b225c3fcef3e47fe0719","name":"ansi2json.rb","type":"blob","path":"lib/gitlab/ci/ansi2json.rb","mode":"100644"},{"id":"3fbfdffe277f296e8f116f078e80adbe072a158b","name":"charts.rb","type":"blob","path":"lib/gitlab/ci/charts.rb","mode":"100644"},{"id":"9c1e6277e95ee74775a45cadd3628bc05e777660","name":"config.rb","type":"blob","path":"lib/gitlab/ci/config.rb","mode":"100644"},{"id":"1d7e7ea0f9af2839512d89488f4873f0edd4a6dc","name":"cron_parser.rb","type":"blob","path":"lib/gitlab/ci/cron_parser.rb","mode":"100644"},{"id":"58d55b1bd6f394aeff2cb579efa48970b52f095b","name":"mask_secret.rb","type":"blob","path":"lib/gitlab/ci/mask_secret.rb","mode":"100644"},{"id":"1625cb841b694deea23c816761e09b4337affc03","name":"model.rb","type":"blob","path":"lib/gitlab/ci/model.rb","mode":"100644"},{"id":"c76cd5ff28512ed90616be13d9e776b83124eab2","name":"parsers.rb","type":"blob","path":"lib/gitlab/ci/parsers.rb","mode":"100644"},{"id":"941f7178dacf36fce67fdfa538f1d8ef50e5d6da","name":"trace.rb","type":"blob","path":"lib/gitlab/ci/trace.rb","mode":"100644"},{"id":"f6a3abefcfb350d9ff69560a579b3c9473612a8b","name":"yaml_processor.rb","type":"blob","path":"lib/gitlab/ci/yaml_processor.rb","mode":"100644"}]
\ No newline at end of file
+[
+    {
+        "id": "ff85b01e96cca1db128a3ea0b3643667f50e464f",
+        "name": "ansi2json",
+        "type": "tree",
+        "path": "lib/gitlab/ci/ansi2json",
+        "mode": "040000"
+    },
+    {
+        "id": "b044e0902a1d468d0e59ca733da6c22c4710af20",
+        "name": "build",
+        "type": "tree",
+        "path": "lib/gitlab/ci/build",
+        "mode": "040000"
+    },
+    {
+        "id": "80cecd7647164c63768f52b431509d04d7f7dcf6",
+        "name": "config",
+        "type": "tree",
+        "path": "lib/gitlab/ci/config",
+        "mode": "040000"
+    },
+    {
+        "id": "1d53ca741903e40610728c07b637da21fbf9f394",
+        "name": "parsers",
+        "type": "tree",
+        "path": "lib/gitlab/ci/parsers",
+        "mode": "040000"
+    },
+    {
+        "id": "1a8b0f4be2707c3d7bdede2865c4a2c8a4af99a9",
+        "name": "pipeline",
+        "type": "tree",
+        "path": "lib/gitlab/ci/pipeline",
+        "mode": "040000"
+    },
+    {
+        "id": "42afb52861c8f807f6ccf95e98607638b6d30f22",
+        "name": "reports",
+        "type": "tree",
+        "path": "lib/gitlab/ci/reports",
+        "mode": "040000"
+    },
+    {
+        "id": "1cf9d5d945923d8a3f820b5b39aa65431b271375",
+        "name": "status",
+        "type": "tree",
+        "path": "lib/gitlab/ci/status",
+        "mode": "040000"
+    },
+    {
+        "id": "f02f0b133c976074cec2b3babc88966f2ef32374",
+        "name": "templates",
+        "type": "tree",
+        "path": "lib/gitlab/ci/templates",
+        "mode": "040000"
+    },
+    {
+        "id": "f2f31cbc4e89da99530cd30d7fc5f4d989d05b90",
+        "name": "trace",
+        "type": "tree",
+        "path": "lib/gitlab/ci/trace",
+        "mode": "040000"
+    },
+    {
+        "id": "8d06f84b4a11604dacb1e37eb3c99094f2bccbd8",
+        "name": "variables",
+        "type": "tree",
+        "path": "lib/gitlab/ci/variables",
+        "mode": "040000"
+    },
+    {
+        "id": "eb5d78ebcd41aee923ad02c8fe4b340d87254d59",
+        "name": "ansi2html.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/ansi2html.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "79114d35916cc46aff83b225c3fcef3e47fe0719",
+        "name": "ansi2json.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/ansi2json.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "3fbfdffe277f296e8f116f078e80adbe072a158b",
+        "name": "charts.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/charts.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "9c1e6277e95ee74775a45cadd3628bc05e777660",
+        "name": "config.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/config.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "1d7e7ea0f9af2839512d89488f4873f0edd4a6dc",
+        "name": "cron_parser.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/cron_parser.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "58d55b1bd6f394aeff2cb579efa48970b52f095b",
+        "name": "mask_secret.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/mask_secret.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "1625cb841b694deea23c816761e09b4337affc03",
+        "name": "model.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/model.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "c76cd5ff28512ed90616be13d9e776b83124eab2",
+        "name": "parsers.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/parsers.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "941f7178dacf36fce67fdfa538f1d8ef50e5d6da",
+        "name": "trace.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/trace.rb",
+        "mode": "100644"
+    },
+    {
+        "id": "f6a3abefcfb350d9ff69560a579b3c9473612a8b",
+        "name": "yaml_processor.rb",
+        "type": "blob",
+        "path": "lib/gitlab/ci/yaml_processor.rb",
+        "mode": "100644"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/content_update.json.fail b/scm/driver/gitlab/testdata/content_update.json.fail
new file mode 100644
index 000000000..a92bd370f
--- /dev/null
+++ b/scm/driver/gitlab/testdata/content_update.json.fail
@@ -0,0 +1,3 @@
+{
+  "message": "You are attempting to update a file that has changed since you started editing it."
+}
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/emails.json b/scm/driver/gitlab/testdata/emails.json
new file mode 100644
index 000000000..15ff44bab
--- /dev/null
+++ b/scm/driver/gitlab/testdata/emails.json
@@ -0,0 +1,12 @@
+[
+    {
+        "id": 1,
+        "email": "john@company.com",
+        "confirmed_at": "2021-03-26T19:07:56.248Z"
+    },
+    {
+        "id": 3,
+        "email": "jane@company.com",
+        "confirmed_at": null
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/emails.json.golden b/scm/driver/gitlab/testdata/emails.json.golden
new file mode 100644
index 000000000..595eea801
--- /dev/null
+++ b/scm/driver/gitlab/testdata/emails.json.golden
@@ -0,0 +1,12 @@
+[
+    {
+        "Value": "john@company.com",
+        "verified": true,
+        "primary": false
+    },
+    {
+        "Value": "jane@company.com",
+        "verified": false,
+        "primary": false
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/merge.json.golden b/scm/driver/gitlab/testdata/merge.json.golden
index 4df0d2492..5864162c3 100644
--- a/scm/driver/gitlab/testdata/merge.json.golden
+++ b/scm/driver/gitlab/testdata/merge.json.golden
@@ -7,6 +7,7 @@
     "Source": "fix",
     "Target": "master",
     "Link": "https://gitlab.com/gitlab-org/testme/merge_requests/1",
+    "Draft": false,
     "Closed": true,
     "Merged": false,
     "Author": {
diff --git a/scm/driver/gitlab/testdata/merges.json.golden b/scm/driver/gitlab/testdata/merges.json.golden
index 46cb6d7c4..cf605b9ee 100644
--- a/scm/driver/gitlab/testdata/merges.json.golden
+++ b/scm/driver/gitlab/testdata/merges.json.golden
@@ -8,6 +8,7 @@
         "Source": "fix",
         "Target": "master",
         "Link": "https://gitlab.com/gitlab-org/testme/merge_requests/1",
+        "Draft": false,
         "Closed": true,
         "Merged": false,
         "Author": {
diff --git a/scm/driver/gitlab/testdata/milestone.json b/scm/driver/gitlab/testdata/milestone.json
new file mode 100644
index 000000000..0cbf520b3
--- /dev/null
+++ b/scm/driver/gitlab/testdata/milestone.json
@@ -0,0 +1,13 @@
+{
+  "id": 12,
+  "iid": 3,
+  "project_id": 16,
+  "title": "10.0",
+  "description": "Version",
+  "due_date": "2013-11-29",
+  "start_date": "2013-11-10",
+  "state": "active",
+  "updated_at": "2013-10-02T09:24:18Z",
+  "created_at": "2013-10-02T09:24:18Z",
+  "expired": false
+}
diff --git a/scm/driver/gitlab/testdata/milestone.json.golden b/scm/driver/gitlab/testdata/milestone.json.golden
new file mode 100644
index 000000000..186481374
--- /dev/null
+++ b/scm/driver/gitlab/testdata/milestone.json.golden
@@ -0,0 +1,8 @@
+{
+  "ID": 12,
+  "Number": 12,
+  "Title": "10.0",
+  "Description": "Version",
+  "DueDate": "2013-11-29T00:00:00Z",
+  "State": "active"
+}
diff --git a/scm/driver/gitlab/testdata/milestone_create.json b/scm/driver/gitlab/testdata/milestone_create.json
new file mode 100644
index 000000000..efb5309fd
--- /dev/null
+++ b/scm/driver/gitlab/testdata/milestone_create.json
@@ -0,0 +1,5 @@
+{
+  "title": "v1.0",
+  "description": "Tracking milestone for version 1.0",
+  "due_date": "2012-10-09"
+}
diff --git a/scm/driver/gitlab/testdata/milestone_update.json b/scm/driver/gitlab/testdata/milestone_update.json
new file mode 100644
index 000000000..a91bbbd16
--- /dev/null
+++ b/scm/driver/gitlab/testdata/milestone_update.json
@@ -0,0 +1,6 @@
+{
+  "title": "v1.0",
+  "description": "Tracking milestone for version 1.0",
+  "state_event": "close",
+  "due_date": "2012-10-09"
+}
diff --git a/scm/driver/gitlab/testdata/milestones.json b/scm/driver/gitlab/testdata/milestones.json
new file mode 100644
index 000000000..f6c9e76cf
--- /dev/null
+++ b/scm/driver/gitlab/testdata/milestones.json
@@ -0,0 +1,15 @@
+[
+  {
+    "id": 12,
+    "iid": 3,
+    "project_id": 16,
+    "title": "10.0",
+    "description": "Version",
+    "due_date": "2013-11-29",
+    "start_date": "2013-11-10",
+    "state": "active",
+    "updated_at": "2013-10-02T09:24:18Z",
+    "created_at": "2013-10-02T09:24:18Z",
+    "expired": false
+  }
+]
diff --git a/scm/driver/gitlab/testdata/milestones.json.golden b/scm/driver/gitlab/testdata/milestones.json.golden
new file mode 100644
index 000000000..9fd157cca
--- /dev/null
+++ b/scm/driver/gitlab/testdata/milestones.json.golden
@@ -0,0 +1,10 @@
+[
+  {
+    "ID": 12,
+    "Number": 12,
+    "Title": "10.0",
+    "Description": "Version",
+    "DueDate": "2013-11-29T00:00:00Z",
+    "State": "active"
+  }
+]
diff --git a/scm/driver/gitlab/testdata/release.json b/scm/driver/gitlab/testdata/release.json
new file mode 100644
index 000000000..2f32681eb
--- /dev/null
+++ b/scm/driver/gitlab/testdata/release.json
@@ -0,0 +1,100 @@
+{
+  "tag_name":"v0.1",
+  "description":"## CHANGELOG\r\n\r\n- Remove limit of 100 when searching repository code. !8671\r\n- Show error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\r\n- Fix a bug where internal email pattern wasn't respected. !22516",
+  "name":"Awesome app v0.1 alpha",
+  "description_html":"\u003ch2 dir=\"auto\"\u003e\n\u003ca id=\"user-content-changelog\" class=\"anchor\" href=\"#changelog\" aria-hidden=\"true\"\u003e\u003c/a\u003eCHANGELOG\u003c/h2\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eRemove limit of 100 when searching repository code. !8671\u003c/li\u003e\n\u003cli\u003eShow error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\u003c/li\u003e\n\u003cli\u003eFix a bug where internal email pattern wasn't respected. !22516\u003c/li\u003e\n\u003c/ul\u003e",
+  "created_at":"2019-01-03T01:55:18.203Z",
+  "released_at":"2019-01-03T01:55:18.203Z",
+  "author":{
+    "id":1,
+    "name":"Administrator",
+    "username":"root",
+    "state":"active",
+    "avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+    "web_url":"https://gitlab.example.com/root"
+  },
+  "commit":{
+    "id":"f8d3d94cbd347e924aa7b715845e439d00e80ca4",
+    "short_id":"f8d3d94c",
+    "title":"Initial commit",
+    "created_at":"2019-01-03T01:53:28.000Z",
+    "parent_ids":[
+
+    ],
+    "message":"Initial commit",
+    "author_name":"Administrator",
+    "author_email":"admin@example.com",
+    "authored_date":"2019-01-03T01:53:28.000Z",
+    "committer_name":"Administrator",
+    "committer_email":"admin@example.com",
+    "committed_date":"2019-01-03T01:53:28.000Z"
+  },
+  "milestones": [
+    {
+      "id":51,
+      "iid":1,
+      "project_id":24,
+      "title":"v1.0-rc",
+      "description":"Voluptate fugiat possimus quis quod aliquam expedita.",
+      "state":"closed",
+      "created_at":"2019-07-12T19:45:44.256Z",
+      "updated_at":"2019-07-12T19:45:44.256Z",
+      "due_date":"2019-08-16T11:00:00.256Z",
+      "start_date":"2019-07-30T12:00:00.256Z",
+      "web_url":"https://gitlab.example.com/root/awesome-app/-/milestones/1",
+      "issue_stats": {
+        "total": 98,
+        "closed": 76
+      }
+    },
+    {
+      "id":52,
+      "iid":2,
+      "project_id":24,
+      "title":"v1.0",
+      "description":"Voluptate fugiat possimus quis quod aliquam expedita.",
+      "state":"closed",
+      "created_at":"2019-07-16T14:00:12.256Z",
+      "updated_at":"2019-07-16T14:00:12.256Z",
+      "due_date":"2019-08-16T11:00:00.256Z",
+      "start_date":"2019-07-30T12:00:00.256Z",
+      "web_url":"https://gitlab.example.com/root/awesome-app/-/milestones/2",
+      "issue_stats": {
+        "total": 24,
+        "closed": 21
+      }
+    }
+  ],
+  "commit_path":"/root/awesome-app/commit/588440f66559714280628a4f9799f0c4eb880a4a",
+  "tag_path":"/root/awesome-app/-/tags/v0.11.1",
+  "assets":{
+    "count":5,
+    "sources":[
+      {
+        "format":"zip",
+        "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.zip"
+      },
+      {
+        "format":"tar.gz",
+        "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar.gz"
+      },
+      {
+        "format":"tar.bz2",
+        "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar.bz2"
+      },
+      {
+        "format":"tar",
+        "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar"
+      }
+    ],
+    "links":[
+      {
+        "id":3,
+        "name":"hoge",
+        "url":"https://gitlab.example.com/root/awesome-app/-/tags/v0.11.1/binaries/linux-amd64",
+        "external":true,
+        "link_type":"other"
+      }
+    ]
+  }
+}
diff --git a/scm/driver/gitlab/testdata/release.json.golden b/scm/driver/gitlab/testdata/release.json.golden
new file mode 100644
index 000000000..3ac087ded
--- /dev/null
+++ b/scm/driver/gitlab/testdata/release.json.golden
@@ -0,0 +1,10 @@
+{
+  "ID": 0,
+  "Title": "Awesome app v0.1 alpha",
+  "Description": "## CHANGELOG\r\n\r\n- Remove limit of 100 when searching repository code. !8671\r\n- Show error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\r\n- Fix a bug where internal email pattern wasn't respected. !22516",
+  "Link": "",
+  "Tag": "v0.1",
+  "Commitish": "f8d3d94cbd347e924aa7b715845e439d00e80ca4",
+  "Draft": false,
+  "Prerelease": false
+}
diff --git a/scm/driver/gitlab/testdata/releases.json b/scm/driver/gitlab/testdata/releases.json
new file mode 100644
index 000000000..c0a708965
--- /dev/null
+++ b/scm/driver/gitlab/testdata/releases.json
@@ -0,0 +1,167 @@
+[
+  {
+    "tag_name":"v0.2",
+    "description":"## CHANGELOG\r\n\r\n- Escape label and milestone titles to prevent XSS in GFM autocomplete. !2740\r\n- Prevent private snippets from being embeddable.\r\n- Add subresources removal to member destroy service.",
+    "name":"Awesome app v0.2 beta",
+    "description_html":"\u003ch2 dir=\"auto\"\u003e\n\u003ca id=\"user-content-changelog\" class=\"anchor\" href=\"#changelog\" aria-hidden=\"true\"\u003e\u003c/a\u003eCHANGELOG\u003c/h2\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eEscape label and milestone titles to prevent XSS in GFM autocomplete. !2740\u003c/li\u003e\n\u003cli\u003ePrevent private snippets from being embeddable.\u003c/li\u003e\n\u003cli\u003eAdd subresources removal to member destroy service.\u003c/li\u003e\n\u003c/ul\u003e",
+    "created_at":"2019-01-03T01:56:19.539Z",
+    "released_at":"2019-01-03T01:56:19.539Z",
+    "author":{
+      "id":1,
+      "name":"Administrator",
+      "username":"root",
+      "state":"active",
+      "avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+      "web_url":"https://gitlab.example.com/root"
+    },
+    "commit":{
+      "id":"079e90101242458910cccd35eab0e211dfc359c0",
+      "short_id":"079e9010",
+      "title":"Update README.md",
+      "created_at":"2019-01-03T01:55:38.000Z",
+      "parent_ids":[
+        "f8d3d94cbd347e924aa7b715845e439d00e80ca4"
+      ],
+      "message":"Update README.md",
+      "author_name":"Administrator",
+      "author_email":"admin@example.com",
+      "authored_date":"2019-01-03T01:55:38.000Z",
+      "committer_name":"Administrator",
+      "committer_email":"admin@example.com",
+      "committed_date":"2019-01-03T01:55:38.000Z"
+    },
+    "milestones": [
+      {
+        "id":51,
+        "iid":1,
+        "project_id":24,
+        "title":"v1.0-rc",
+        "description":"Voluptate fugiat possimus quis quod aliquam expedita.",
+        "state":"closed",
+        "created_at":"2019-07-12T19:45:44.256Z",
+        "updated_at":"2019-07-12T19:45:44.256Z",
+        "due_date":"2019-08-16T11:00:00.256Z",
+        "start_date":"2019-07-30T12:00:00.256Z",
+        "web_url":"https://gitlab.example.com/root/awesome-app/-/milestones/1",
+        "issue_stats": {
+          "total": 98,
+          "closed": 76
+        }
+      },
+      {
+        "id":52,
+        "iid":2,
+        "project_id":24,
+        "title":"v1.0",
+        "description":"Voluptate fugiat possimus quis quod aliquam expedita.",
+        "state":"closed",
+        "created_at":"2019-07-16T14:00:12.256Z",
+        "updated_at":"2019-07-16T14:00:12.256Z",
+        "due_date":"2019-08-16T11:00:00.256Z",
+        "start_date":"2019-07-30T12:00:00.256Z",
+        "web_url":"https://gitlab.example.com/root/awesome-app/-/milestones/2",
+        "issue_stats": {
+          "total": 24,
+          "closed": 21
+        }
+      }
+    ],
+    "commit_path":"/root/awesome-app/commit/588440f66559714280628a4f9799f0c4eb880a4a",
+    "tag_path":"/root/awesome-app/-/tags/v0.11.1",
+    "assets":{
+      "count":6,
+      "sources":[
+        {
+          "format":"zip",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.zip"
+        },
+        {
+          "format":"tar.gz",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.tar.gz"
+        },
+        {
+          "format":"tar.bz2",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.tar.bz2"
+        },
+        {
+          "format":"tar",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.2/awesome-app-v0.2.tar"
+        }
+      ],
+      "links":[
+        {
+          "id":2,
+          "name":"awesome-v0.2.msi",
+          "url":"http://192.168.10.15:3000/msi",
+          "external":true,
+          "link_type":"other"
+        },
+        {
+          "id":1,
+          "name":"awesome-v0.2.dmg",
+          "url":"http://192.168.10.15:3000",
+          "external":true,
+          "link_type":"other"
+        }
+      ],
+      "evidence_file_path":"https://gitlab.example.com/root/awesome-app/-/releases/v0.2/evidence.json"
+    }
+  },
+  {
+    "tag_name":"v0.1",
+    "description":"## CHANGELOG\r\n\r\n-Remove limit of 100 when searching repository code. !8671\r\n- Show error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\r\n- Fix a bug where internal email pattern wasn't respected. !22516",
+    "name":"Awesome app v0.1 alpha",
+    "description_html":"\u003ch2 dir=\"auto\"\u003e\n\u003ca id=\"user-content-changelog\" class=\"anchor\" href=\"#changelog\" aria-hidden=\"true\"\u003e\u003c/a\u003eCHANGELOG\u003c/h2\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eRemove limit of 100 when searching repository code. !8671\u003c/li\u003e\n\u003cli\u003eShow error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\u003c/li\u003e\n\u003cli\u003eFix a bug where internal email pattern wasn't respected. !22516\u003c/li\u003e\n\u003c/ul\u003e",
+    "created_at":"2019-01-03T01:55:18.203Z",
+    "released_at":"2019-01-03T01:55:18.203Z",
+    "author":{
+      "id":1,
+      "name":"Administrator",
+      "username":"root",
+      "state":"active",
+      "avatar_url":"https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
+      "web_url":"https://gitlab.example.com/root"
+    },
+    "commit":{
+      "id":"f8d3d94cbd347e924aa7b715845e439d00e80ca4",
+      "short_id":"f8d3d94c",
+      "title":"Initial commit",
+      "created_at":"2019-01-03T01:53:28.000Z",
+      "parent_ids":[
+
+      ],
+      "message":"Initial commit",
+      "author_name":"Administrator",
+      "author_email":"admin@example.com",
+      "authored_date":"2019-01-03T01:53:28.000Z",
+      "committer_name":"Administrator",
+      "committer_email":"admin@example.com",
+      "committed_date":"2019-01-03T01:53:28.000Z"
+    },
+    "assets":{
+      "count":4,
+      "sources":[
+        {
+          "format":"zip",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.zip"
+        },
+        {
+          "format":"tar.gz",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar.gz"
+        },
+        {
+          "format":"tar.bz2",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar.bz2"
+        },
+        {
+          "format":"tar",
+          "url":"https://gitlab.example.com/root/awesome-app/-/archive/v0.1/awesome-app-v0.1.tar"
+        }
+      ],
+      "links":[
+
+      ],
+      "evidence_file_path":"https://gitlab.example.com/root/awesome-app/-/releases/v0.1/evidence.json"
+    }
+  }
+]
diff --git a/scm/driver/gitlab/testdata/releases.json.golden b/scm/driver/gitlab/testdata/releases.json.golden
new file mode 100644
index 000000000..640ae4b7f
--- /dev/null
+++ b/scm/driver/gitlab/testdata/releases.json.golden
@@ -0,0 +1,14 @@
+[
+    {
+        "Title":       "Awesome app v0.2 beta",
+        "Description": "## CHANGELOG\r\n\r\n- Escape label and milestone titles to prevent XSS in GFM autocomplete. !2740\r\n- Prevent private snippets from being embeddable.\r\n- Add subresources removal to member destroy service.",
+        "Tag":         "v0.2",
+        "Commitish":   "079e90101242458910cccd35eab0e211dfc359c0"
+    },
+    {
+        "Title":       "Awesome app v0.1 alpha",
+        "Description": "## CHANGELOG\r\n\r\n-Remove limit of 100 when searching repository code. !8671\r\n- Show error message when attempting to reopen an MR and there is an open MR for the same branch. !16447 (Akos Gyimesi)\r\n- Fix a bug where internal email pattern wasn't respected. !22516",
+        "Tag":         "v0.1",
+        "Commitish":   "f8d3d94cbd347e924aa7b715845e439d00e80ca4"
+    }
+]
diff --git a/scm/driver/gitlab/testdata/repos_filter.json b/scm/driver/gitlab/testdata/repos_filter.json
new file mode 100644
index 000000000..13f8b4de3
--- /dev/null
+++ b/scm/driver/gitlab/testdata/repos_filter.json
@@ -0,0 +1,59 @@
+[
+  {
+    "id": 178504,
+    "description": "",
+    "default_branch": "master",
+    "tag_list": [],
+    "ssh_url_to_repo": "git@gitlab.com:diaspora/diaspora.git",
+    "http_url_to_repo": "https://gitlab.com/diaspora/diaspora.git",
+    "web_url": "https://gitlab.com/diaspora/diaspora",
+    "name": "Diaspora",
+    "name_with_namespace": "diaspora / Diaspora",
+    "path": "diaspora",
+    "path_with_namespace": "diaspora/diaspora",
+    "avatar_url": null,
+    "star_count": 0,
+    "forks_count": 0,
+    "created_at": "2015-03-03T18:37:05.387Z",
+    "last_activity_at": "2015-03-03T18:37:20.795Z",
+    "_links": {
+      "self": "http://gitlab.com/api/v4/projects/178504",
+      "issues": "http://gitlab.com/api/v4/projects/178504/issues",
+      "merge_requests": "http://gitlab.com/api/v4/projects/178504/merge_requests",
+      "repo_branches": "http://gitlab.com/api/v4/projects/178504/repository/branches",
+      "labels": "http://gitlab.com/api/v4/projects/178504/labels",
+      "events": "http://gitlab.com/api/v4/projects/178504/events",
+      "members": "http://gitlab.com/api/v4/projects/178504/members"
+    },
+    "archived": false,
+    "visibility": "public",
+    "resolve_outdated_diff_discussions": null,
+    "container_registry_enabled": null,
+    "issues_enabled": true,
+    "merge_requests_enabled": true,
+    "wiki_enabled": true,
+    "jobs_enabled": true,
+    "snippets_enabled": false,
+    "shared_runners_enabled": true,
+    "lfs_enabled": true,
+    "creator_id": 57658,
+    "namespace": {
+      "id": 120836,
+      "name": "diaspora",
+      "path": "diaspora",
+      "kind": "group",
+      "full_path": "diaspora",
+      "parent_id": null
+    },
+    "import_status": "finished",
+    "open_issues_count": 0,
+    "public_jobs": true,
+    "ci_config_path": null,
+    "shared_with_groups": [],
+    "only_allow_merge_if_pipeline_succeeds": false,
+    "request_access_enabled": true,
+    "only_allow_merge_if_all_discussions_are_resolved": null,
+    "printing_merge_request_link_enabled": true,
+    "approvals_before_merge": 0
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/repos_filter.json.golden b/scm/driver/gitlab/testdata/repos_filter.json.golden
new file mode 100644
index 000000000..5780a5ebd
--- /dev/null
+++ b/scm/driver/gitlab/testdata/repos_filter.json.golden
@@ -0,0 +1,20 @@
+[
+    {
+        "ID": "178504",
+        "Namespace": "diaspora",
+        "Name": "diaspora",
+        "Perm": {
+            "Pull": true,
+            "Push": false,
+            "Admin": false
+        },
+        "Branch": "master",
+        "Private": false,
+        "Visibility": 1,
+        "Clone": "https://gitlab.com/diaspora/diaspora.git",
+        "CloneSSH": "git@gitlab.com:diaspora/diaspora.git",
+        "Link": "https://gitlab.com/diaspora/diaspora",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/webhooks/branch_create.json b/scm/driver/gitlab/testdata/webhooks/branch_create.json
index 9abeda061..9c99d83b2 100644
--- a/scm/driver/gitlab/testdata/webhooks/branch_create.json
+++ b/scm/driver/gitlab/testdata/webhooks/branch_create.json
@@ -34,7 +34,7 @@
     {
       "id": "c4c79227ed610f1151f05bbc5be33b4f340d39c8",
       "message": "update readme\n",
-      "timestamp": "2017-12-10T08:28:36-08:00",
+      "timestamp": "2017-12-10T08:28:36+00:00",
       "url": "https://gitlab.com/gitlab-org/hello-world/commit/c4c79227ed610f1151f05bbc5be33b4f340d39c8",
       "author": {
         "name": "Sid Sijbrandij",
diff --git a/scm/driver/gitlab/testdata/webhooks/branch_create.json.golden b/scm/driver/gitlab/testdata/webhooks/branch_create.json.golden
index 010c28001..1a618b2a9 100644
--- a/scm/driver/gitlab/testdata/webhooks/branch_create.json.golden
+++ b/scm/driver/gitlab/testdata/webhooks/branch_create.json.golden
@@ -41,14 +41,14 @@
             "Author": {
                 "Name": "Sid Sijbrandij",
                 "Email": "noreply@gitlab.com",
-                "Date": "0001-01-01T00:00:00Z",
+                "Date": "2017-12-10T08:28:36Z",
                 "Login": "",
                 "Avatar": ""
             },
             "Committer": {
                 "Name": "Sid Sijbrandij",
                 "Email": "noreply@gitlab.com",
-                "Date": "0001-01-01T00:00:00Z",
+                "Date": "2017-12-10T08:28:36Z",
                 "Login": "",
                 "Avatar": ""
             },
diff --git a/scm/driver/gitlab/testdata/webhooks/pull_request_comment_create.json b/scm/driver/gitlab/testdata/webhooks/merge_request_comment_create.json
similarity index 100%
rename from scm/driver/gitlab/testdata/webhooks/pull_request_comment_create.json
rename to scm/driver/gitlab/testdata/webhooks/merge_request_comment_create.json
diff --git a/scm/driver/gitlab/testdata/webhooks/merge_request_comment_create.json.golden b/scm/driver/gitlab/testdata/webhooks/merge_request_comment_create.json.golden
new file mode 100644
index 000000000..dcc3e2448
--- /dev/null
+++ b/scm/driver/gitlab/testdata/webhooks/merge_request_comment_create.json.golden
@@ -0,0 +1,94 @@
+{
+    "Action": "created",
+    "Repo": {
+        "ID": "4861503",
+        "Namespace": "gitlab-org",
+        "Name": "hello-world",
+        "Perm": null,
+        "Branch": "master",
+        "Archived": false,
+        "Private": false,
+        "Visibility": 0,
+        "Clone": "https://gitlab.com/gitlab-org/hello-world.git",
+        "CloneSSH": "git@gitlab.com:gitlab-org/hello-world.git",
+        "Link": "https://gitlab.com/gitlab-org/hello-world",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Issue": {
+        "Number": 1,
+        "Title": "update readme",
+        "Body": "update readme",
+        "Link": "https://gitlab.com/gitlab-org/hello-world",
+        "Labels": null,
+        "Closed": false,
+        "Locked": false,
+        "Author": {
+            "Login": "sytses",
+            "Name": "Sid Sijbrandij",
+            "Email": "",
+            "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "PullRequest": {
+            "Number": 1,
+            "Title": "update readme",
+            "Body": "adding build instructions to readme",
+            "Sha": "c4c79227ed610f1151f05bbc5be33b4f340d39c8",
+            "Ref": "refs/merge-requests/1/head",
+            "Source": "feature",
+            "Target": "master",
+            "Fork": "",
+            "Link": "https://gitlab.com/gitlab-org/hello-world/merge_requests/1",
+            "Diff": "",
+            "Closed": false,
+            "Merged": false,
+            "Base": {
+                    "Name": "",
+                    "Path": "",
+                    "Sha": ""
+            },
+            "Head": {
+                    "Name": "",
+                    "Path": "",
+                    "Sha": ""
+            },
+            "Author": {
+                    "Login": "sytses",
+                    "Name": "Sid Sijbrandij",
+                    "Email": "",
+                    "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon",
+                    "Created": "0001-01-01T00:00:00Z",
+                    "Updated": "0001-01-01T00:00:00Z"
+            },
+            "Created": "2017-12-10T17:01:11Z",
+            "Updated": "2017-12-10T17:05:14Z",
+            "Labels": null
+        },
+        "Created": "2017-12-10T17:05:14Z",
+        "Updated": "2017-12-10T17:05:14Z"
+    },
+    "Comment": {
+        "ID": 50772616,
+        "Body": "lgtm",
+        "Author": {
+            "Login": "sytses",
+            "Name": "Sid Sijbrandij",
+            "Email": "",
+            "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon",
+            "Created": "0001-01-01T00:00:00Z",
+            "Updated": "0001-01-01T00:00:00Z"
+        },
+        "Created": "2017-12-10T17:05:14Z",
+        "Updated": "2017-12-10T17:05:14Z"
+    },
+    "Sender": {
+        "Login": "sytses",
+        "Name": "Sid Sijbrandij",
+        "Email": "",
+        "Avatar": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87?s=80\u0026d=identicon",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/webhooks/pipeline_hook.json b/scm/driver/gitlab/testdata/webhooks/pipeline_hook.json
new file mode 100644
index 000000000..6deec0b0e
--- /dev/null
+++ b/scm/driver/gitlab/testdata/webhooks/pipeline_hook.json
@@ -0,0 +1,115 @@
+{
+  "object_kind": "pipeline",
+  "object_attributes":{
+     "id": 31,
+     "iid": 3,
+     "name": "Pipeline for branch: master",
+     "ref": "master",
+     "tag": false,
+     "sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
+     "before_sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
+     "source": "merge_request_event",
+     "status": "success",
+     "stages":[
+        "build",
+        "test",
+        "deploy"
+     ],
+     "created_at": "2016-08-12 15:23:28 UTC",
+     "finished_at": "2016-08-12 15:26:29 UTC",
+     "duration": 63,
+     "variables": [
+       {
+         "key": "NESTOR_PROD_ENVIRONMENT",
+         "value": "us-west-1"
+       }
+     ],
+     "url": "http://example.com/gitlab-org/gitlab-test/-/pipelines/31"
+  },
+  "merge_request": {
+      "id": 371997275,
+      "iid": 15,
+      "title": "Edit test.txt",
+      "source_branch": "shivamnegi94-main-patch-62619",
+      "source_project_id": 27743392,
+      "target_branch": "main",
+      "target_project_id": 27743392,
+      "state": "opened",
+      "merge_status": "can_be_merged",
+      "detailed_merge_status": "mergeable",
+      "url": "https://gitlab.com/shivamnegi94/test/-/merge_requests/15"
+   },
+  "user":{
+     "id": 1,
+     "name": "Administrator",
+     "username": "root",
+     "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon",
+     "email": "user_email@gitlab.com"
+  },
+  "project":{
+     "id": 1,
+     "name": "Gitlab Test",
+     "description": "Atque in sunt eos similique dolores voluptatem.",
+     "web_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
+     "avatar_url": null,
+     "git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
+     "git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git",
+     "namespace": "Gitlab Org",
+     "visibility_level": 20,
+     "path_with_namespace": "gitlab-org/gitlab-test",
+     "default_branch": "master"
+  },
+  "commit":{
+     "id": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
+     "message": "test\n",
+     "timestamp": "2016-08-12T17:23:21+02:00",
+     "url": "http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2",
+     "author":{
+        "name": "User",
+        "email": "user@gitlab.com"
+     }
+  },
+  "source_pipeline":{
+     "project":{
+       "id": 41,
+       "web_url": "https://gitlab.example.com/gitlab-org/upstream-project",
+       "path_with_namespace": "gitlab-org/upstream-project"
+     },
+     "pipeline_id": 30,
+     "job_id": 3401
+  },
+  "builds":[
+     {
+        "id": 380,
+        "stage": "deploy",
+        "name": "production",
+        "status": "skipped",
+        "created_at": "2016-08-12 15:23:28 UTC",
+        "started_at": null,
+        "finished_at": null,
+        "duration": null,
+        "queued_duration": null,
+        "failure_reason": null,
+        "when": "manual",
+        "manual": true,
+        "allow_failure": false,
+        "user":{
+           "id": 1,
+           "name": "Administrator",
+           "username": "root",
+           "avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon",
+           "email": "admin@example.com"
+        },
+        "runner": null,
+        "artifacts_file":{
+           "filename": null,
+           "size": null
+        },
+        "environment": {
+          "name": "production",
+          "action": "start",
+          "deployment_tier": "production"
+        }
+     }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/gitlab/testdata/webhooks/pipeline_hook.json.golden b/scm/driver/gitlab/testdata/webhooks/pipeline_hook.json.golden
new file mode 100644
index 000000000..8e70f4b6b
--- /dev/null
+++ b/scm/driver/gitlab/testdata/webhooks/pipeline_hook.json.golden
@@ -0,0 +1,50 @@
+{
+  "repo": {
+    "id": "1",
+    "namespace": "gitlab-org",
+    "name": "gitlab-test",
+    "clone": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git",
+    "cloneSSH": "git@192.168.64.1:gitlab-org/gitlab-test.git",
+    "link": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
+    "branch": "master",
+    "private": false
+  },
+  "commit": {
+    "sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
+    "message": "test\n",
+    "author": {
+      "name": "User",
+      "email": "user@gitlab.com"
+    },
+    "committer": {
+      "name": "User",
+      "email": "user@gitlab.com"
+    },
+    "link": "http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2"
+  },
+  "Execution": {
+    "Number": 31,
+    "status": "success",
+    "Created": "2016-08-12T15:23:28Z",
+    "URL": "http://example.com/gitlab-org/gitlab-test/-/pipelines/31"
+  },
+  "sender": {
+    "login": "root",
+    "name": "Administrator",
+    "email": "user_email@gitlab.com",
+    "avatar": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80&d=identicon"
+  },
+  "PullRequest": {
+    "Number": 371997275,
+    "Title": "Edit test.txt",
+    "Sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
+    "Ref": "master",
+    "Source": "shivamnegi94-main-patch-62619",
+    "Target": "main",
+    "Link": "https://gitlab.com/shivamnegi94/test/-/merge_requests/15",
+    "Created": "2016-08-12T15:23:28Z",
+    "Draft": false,
+    "Closed": false,
+    "Merged": false
+  }
+}
diff --git a/scm/driver/gitlab/testdata/webhooks/pull_request_comment_create.json.golden b/scm/driver/gitlab/testdata/webhooks/pull_request_comment_create.json.golden
deleted file mode 100644
index e69de29bb..000000000
diff --git a/scm/driver/gitlab/testdata/webhooks/pull_request_review_ready.json b/scm/driver/gitlab/testdata/webhooks/pull_request_review_ready.json
new file mode 100644
index 000000000..5a25b03ef
--- /dev/null
+++ b/scm/driver/gitlab/testdata/webhooks/pull_request_review_ready.json
@@ -0,0 +1,154 @@
+{
+  "object_kind": "merge_request",
+  "event_type": "merge_request",
+  "user": {
+    "id": 13900456,
+    "name": "Meet Rathod",
+    "username": "rathod.meetsatish",
+    "avatar_url": "https://secure.gravatar.com/avatar/0e68aaeb6c49dd6ba280370c96280803?s=80&d=identicon",
+    "email": "[REDACTED]"
+  },
+  "project": {
+    "id": 44067058,
+    "name": "meet",
+    "description": null,
+    "web_url": "https://gitlab.com/rathod.meetsatish/meet",
+    "avatar_url": null,
+    "git_ssh_url": "git@gitlab.com:rathod.meetsatish/meet.git",
+    "git_http_url": "https://gitlab.com/rathod.meetsatish/meet.git",
+    "namespace": "Meet Rathod",
+    "visibility_level": 0,
+    "path_with_namespace": "rathod.meetsatish/meet",
+    "default_branch": "main",
+    "ci_config_path": "",
+    "homepage": "https://gitlab.com/rathod.meetsatish/meet",
+    "url": "git@gitlab.com:rathod.meetsatish/meet.git",
+    "ssh_url": "git@gitlab.com:rathod.meetsatish/meet.git",
+    "http_url": "https://gitlab.com/rathod.meetsatish/meet.git"
+  },
+  "object_attributes": {
+    "assignee_id": null,
+    "author_id": 13900456,
+    "created_at": "2023-09-07 19:42:34 UTC",
+    "description": "",
+    "draft": true,
+    "head_pipeline_id": null,
+    "id": 248466002,
+    "iid": 3,
+    "last_edited_at": null,
+    "last_edited_by_id": null,
+    "merge_commit_sha": null,
+    "merge_error": null,
+    "merge_params": {
+      "force_remove_source_branch": "1"
+    },
+    "merge_status": "can_be_merged",
+    "merge_user_id": null,
+    "merge_when_pipeline_succeeds": false,
+    "milestone_id": null,
+    "source_branch": "main1234",
+    "source_project_id": 44067058,
+    "state_id": 1,
+    "target_branch": "main",
+    "target_project_id": 44067058,
+    "time_estimate": 0,
+    "title": "Draft: Update README.md",
+    "updated_at": "2023-09-07 19:42:45 UTC",
+    "updated_by_id": 13900456,
+    "url": "https://gitlab.com/rathod.meetsatish/meet/-/merge_requests/3",
+    "source": {
+      "id": 44067058,
+      "name": "meet",
+      "description": null,
+      "web_url": "https://gitlab.com/rathod.meetsatish/meet",
+      "avatar_url": null,
+      "git_ssh_url": "git@gitlab.com:rathod.meetsatish/meet.git",
+      "git_http_url": "https://gitlab.com/rathod.meetsatish/meet.git",
+      "namespace": "Meet Rathod",
+      "visibility_level": 0,
+      "path_with_namespace": "rathod.meetsatish/meet",
+      "default_branch": "main",
+      "ci_config_path": "",
+      "homepage": "https://gitlab.com/rathod.meetsatish/meet",
+      "url": "git@gitlab.com:rathod.meetsatish/meet.git",
+      "ssh_url": "git@gitlab.com:rathod.meetsatish/meet.git",
+      "http_url": "https://gitlab.com/rathod.meetsatish/meet.git"
+    },
+    "target": {
+      "id": 44067058,
+      "name": "meet",
+      "description": null,
+      "web_url": "https://gitlab.com/rathod.meetsatish/meet",
+      "avatar_url": null,
+      "git_ssh_url": "git@gitlab.com:rathod.meetsatish/meet.git",
+      "git_http_url": "https://gitlab.com/rathod.meetsatish/meet.git",
+      "namespace": "Meet Rathod",
+      "visibility_level": 0,
+      "path_with_namespace": "rathod.meetsatish/meet",
+      "default_branch": "main",
+      "ci_config_path": "",
+      "homepage": "https://gitlab.com/rathod.meetsatish/meet",
+      "url": "git@gitlab.com:rathod.meetsatish/meet.git",
+      "ssh_url": "git@gitlab.com:rathod.meetsatish/meet.git",
+      "http_url": "https://gitlab.com/rathod.meetsatish/meet.git"
+    },
+    "last_commit": {
+      "id": "ffd71529ecdfb41a530ee13f91b8fdd3e743c754",
+      "message": "Update README.md",
+      "title": "Update README.md",
+      "timestamp": "2023-09-07T19:42:21+00:00",
+      "url": "https://gitlab.com/rathod.meetsatish/meet/-/commit/ffd71529ecdfb41a530ee13f91b8fdd3e743c754",
+      "author": {
+        "name": "Meet Rathod",
+        "email": "[REDACTED]"
+      }
+    },
+    "work_in_progress": true,
+    "total_time_spent": 0,
+    "time_change": 0,
+    "human_total_time_spent": null,
+    "human_time_change": null,
+    "human_time_estimate": null,
+    "assignee_ids": [
+
+    ],
+    "reviewer_ids": [
+
+    ],
+    "labels": [
+
+    ],
+    "state": "opened",
+    "blocking_discussions_resolved": true,
+    "first_contribution": true,
+    "detailed_merge_status": "draft_status",
+    "action": "update"
+  },
+  "labels": [
+
+  ],
+  "changes": {
+    "draft": {
+      "previous": false,
+      "current": true
+    },
+    "title": {
+      "previous": "Update README.md",
+      "current": "Draft: Update README.md"
+    },
+    "updated_at": {
+      "previous": "2023-09-07 19:42:35 UTC",
+      "current": "2023-09-07 19:42:45 UTC"
+    },
+    "updated_by_id": {
+      "previous": null,
+      "current": 13900456
+    }
+  },
+  "repository": {
+    "name": "meet",
+    "url": "git@gitlab.com:rathod.meetsatish/meet.git",
+    "description": null,
+    "homepage": "https://gitlab.com/rathod.meetsatish/meet"
+  }
+}
diff --git a/scm/driver/gitlab/testdata/webhooks/pull_request_review_ready.json.golden b/scm/driver/gitlab/testdata/webhooks/pull_request_review_ready.json.golden
new file mode 100644
index 000000000..059b6bbd7
--- /dev/null
+++ b/scm/driver/gitlab/testdata/webhooks/pull_request_review_ready.json.golden
@@ -0,0 +1,44 @@
+{
+    "Action": "review_ready",
+    "Repo": {
+        "ID": "44067058",
+        "Namespace": "rathod.meetsatish",
+        "Name": "meet",
+        "Perm": null,
+        "Branch": "main",
+        "Private": false,
+        "Clone": "https://gitlab.com/rathod.meetsatish/meet.git",
+        "CloneSSH": "git@gitlab.com:rathod.meetsatish/meet.git",
+        "Link": "https://gitlab.com/rathod.meetsatish/meet",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 3,
+        "Title": "Draft: Update README.md",
+        "Body": "",
+        "Sha": "ffd71529ecdfb41a530ee13f91b8fdd3e743c754",
+        "Ref": "refs/merge-requests/3/head",
+        "Source": "main1234",
+        "Target": "main",
+        "Fork": "Meet Rathod/meet",
+        "Link": "https://gitlab.com/rathod.meetsatish/meet/-/merge_requests/3",
+        "Draft": true,
+        "Closed": false,
+        "Merged": false,
+        "Author": {
+            "Login": "rathod.meetsatish",
+            "Name": "Meet Rathod",
+            "Email": "",
+            "Avatar": "https://secure.gravatar.com/avatar/0e68aaeb6c49dd6ba280370c96280803?s=80&d=identicon"
+        },
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Sender": {
+        "Login": "rathod.meetsatish",
+        "Name": "Meet Rathod",
+        "Email": "",
+        "Avatar": "https://secure.gravatar.com/avatar/0e68aaeb6c49dd6ba280370c96280803?s=80&d=identicon"
+    }
+}
diff --git a/scm/driver/gitlab/testdata/webhooks/push.json b/scm/driver/gitlab/testdata/webhooks/push.json
index dde1a9a84..40801e1d4 100644
--- a/scm/driver/gitlab/testdata/webhooks/push.json
+++ b/scm/driver/gitlab/testdata/webhooks/push.json
@@ -34,7 +34,7 @@
     {
       "id": "2adc9465c4edfc33834e173fe89436a7cb899a1d",
       "message": "added readme\n",
-      "timestamp": "2017-12-10T08:26:38-08:00",
+      "timestamp": "2017-12-10T08:26:38+00:00",
       "url": "https://gitlab.com/gitlab-org/hello-world/commit/2adc9465c4edfc33834e173fe89436a7cb899a1d",
       "author": {
         "name": "Sid Sijbrandij",
diff --git a/scm/driver/gitlab/testdata/webhooks/push.json.golden b/scm/driver/gitlab/testdata/webhooks/push.json.golden
index 2b6863cbb..8c8bbf9e8 100644
--- a/scm/driver/gitlab/testdata/webhooks/push.json.golden
+++ b/scm/driver/gitlab/testdata/webhooks/push.json.golden
@@ -41,14 +41,14 @@
             "Author": {
                 "Name": "Sid Sijbrandij",
                 "Email": "noreply@gitlab.com",
-                "Date": "0001-01-01T00:00:00Z",
+                "Date": "2017-12-10T08:26:38Z",
                 "Login": "",
                 "Avatar": ""
             },
             "Committer": {
                 "Name": "Sid Sijbrandij",
                 "Email": "noreply@gitlab.com",
-                "Date": "0001-01-01T00:00:00Z",
+                "Date": "2017-12-10T08:26:38Z",
                 "Login": "",
                 "Avatar": ""
             },
diff --git a/scm/driver/gitlab/testdata/webhooks/tag_create.json b/scm/driver/gitlab/testdata/webhooks/tag_create.json
index f28bdc160..4544eec7c 100644
--- a/scm/driver/gitlab/testdata/webhooks/tag_create.json
+++ b/scm/driver/gitlab/testdata/webhooks/tag_create.json
@@ -34,7 +34,7 @@
     {
       "id": "2adc9465c4edfc33834e173fe89436a7cb899a1d",
       "message": "added readme\n",
-      "timestamp": "2017-12-10T08:26:38-08:00",
+      "timestamp": "2017-12-10T08:26:38+00:00",
       "url": "https://gitlab.com/gitlab-org/hello-world/commit/2adc9465c4edfc33834e173fe89436a7cb899a1d",
       "author": {
         "name": "Sid Sijbrandij",
diff --git a/scm/driver/gitlab/testdata/webhooks/tag_create.json.golden b/scm/driver/gitlab/testdata/webhooks/tag_create.json.golden
index b96828528..963085c35 100644
--- a/scm/driver/gitlab/testdata/webhooks/tag_create.json.golden
+++ b/scm/driver/gitlab/testdata/webhooks/tag_create.json.golden
@@ -41,14 +41,14 @@
             "Author": {
                 "Name": "Sid Sijbrandij",
                 "Email": "noreply@gitlab.com",
-                "Date": "0001-01-01T00:00:00Z",
+                "Date": "2017-12-10T08:26:38Z",
                 "Login": "",
                 "Avatar": ""
             },
             "Committer": {
                 "Name": "Sid Sijbrandij",
                 "Email": "noreply@gitlab.com",
-                "Date": "0001-01-01T00:00:00Z",
+                "Date": "2017-12-10T08:26:38Z",
                 "Login": "",
                 "Avatar": ""
             },
diff --git a/scm/driver/gitlab/user.go b/scm/driver/gitlab/user.go
index 454aea5c7..7f182bfca 100644
--- a/scm/driver/gitlab/user.go
+++ b/scm/driver/gitlab/user.go
@@ -41,13 +41,28 @@ func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, err
 	return user.Email, res, err
 }
 
+func (s *userService) ListEmail(ctx context.Context, opts scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	path := fmt.Sprintf("api/v4/user/emails?%s", encodeListOptions(opts))
+	out := []*email{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertEmailList(out), res, err
+}
+
 type user struct {
+	ID       int         `json:"id"`
 	Username string      `json:"username"`
 	Name     string      `json:"name"`
 	Email    null.String `json:"email"`
 	Avatar   string      `json:"avatar_url"`
 }
 
+type email struct {
+	Email     string      `json:"email"`
+	Confirmed null.String `json:"confirmed_at"`
+}
+
+// helper function to convert from the gitlab user structure to
+// the common user structure.
 func convertUser(from *user) *scm.User {
 	return &scm.User{
 		Avatar: from.Avatar,
@@ -56,3 +71,22 @@ func convertUser(from *user) *scm.User {
 		Name:   from.Name,
 	}
 }
+
+// helper function to convert from the gitlab email list to
+// the common email structure.
+func convertEmailList(from []*email) []*scm.Email {
+	to := []*scm.Email{}
+	for _, v := range from {
+		to = append(to, convertEmail(v))
+	}
+	return to
+}
+
+// helper function to convert from the gitlab email structure to
+// the common email structure.
+func convertEmail(from *email) *scm.Email {
+	return &scm.Email{
+		Value:    from.Email,
+		Verified: !from.Confirmed.IsZero(),
+	}
+}
diff --git a/scm/driver/gitlab/user_test.go b/scm/driver/gitlab/user_test.go
index 7729592ef..217ae319c 100644
--- a/scm/driver/gitlab/user_test.go
+++ b/scm/driver/gitlab/user_test.go
@@ -143,3 +143,37 @@ func TestUserEmailFind(t *testing.T) {
 	t.Run("Request", testRequest(res))
 	t.Run("Rate", testRate(res))
 }
+
+func TestUserEmailList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://gitlab.com").
+		Get("/api/v4/user/emails").
+		MatchParam("page", "1").
+		MatchParam("per_page", "30").
+		Reply(200).
+		Type("application/json").
+		SetHeaders(mockHeaders).
+		SetHeaders(mockPageHeaders).
+		File("testdata/emails.json")
+
+	client := NewDefault()
+	got, res, err := client.Users.ListEmail(context.Background(), scm.ListOptions{Size: 30, Page: 1})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Email{}
+	raw, _ := ioutil.ReadFile("testdata/emails.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	t.Run("Request", testRequest(res))
+	t.Run("Rate", testRate(res))
+	t.Run("Page", testPage(res))
+}
diff --git a/scm/driver/gitlab/util.go b/scm/driver/gitlab/util.go
index adc28ae30..eb3f06893 100644
--- a/scm/driver/gitlab/util.go
+++ b/scm/driver/gitlab/util.go
@@ -24,6 +24,22 @@ func encodePath(s string) string {
 	return strings.Replace(url.PathEscape(s), ".", "%2E", -1)
 }
 
+func encodeBranchListOptions(opts scm.BranchListOptions) string {
+	params := url.Values{}
+	if opts.SearchTerm != "" {
+		params.Set("search", opts.SearchTerm)
+	}
+	if opts.PageListOptions != (scm.ListOptions{}) {
+		if opts.PageListOptions.Page != 0 {
+			params.Set("page", strconv.Itoa(opts.PageListOptions.Page))
+		}
+		if opts.PageListOptions.Size != 0 {
+			params.Set("per_page", strconv.Itoa(opts.PageListOptions.Size))
+		}
+	}
+	return params.Encode()
+}
+
 func encodeListOptions(opts scm.ListOptions) string {
 	params := url.Values{}
 	if opts.Page != 0 {
@@ -47,6 +63,25 @@ func encodeMemberListOptions(opts scm.ListOptions) string {
 	return params.Encode()
 }
 
+func encodeRepoListOptions(opts scm.RepoListOptions) string {
+	params := url.Values{}
+	params.Set("membership", "true")
+	if opts.RepoSearchTerm != (scm.RepoSearchTerm{}) {
+		if opts.RepoSearchTerm.RepoName != "" {
+			params.Set("search", opts.RepoSearchTerm.RepoName)
+		}
+	}
+	if opts.ListOptions != (scm.ListOptions{}) {
+		if opts.ListOptions.Page != 0 {
+			params.Set("page", strconv.Itoa(opts.ListOptions.Page))
+		}
+		if opts.ListOptions.Size != 0 {
+			params.Set("per_page", strconv.Itoa(opts.ListOptions.Size))
+		}
+	}
+	return params.Encode()
+}
+
 func encodeCommitListOptions(opts scm.CommitListOptions) string {
 	params := url.Values{}
 	if opts.Page != 0 {
@@ -58,6 +93,9 @@ func encodeCommitListOptions(opts scm.CommitListOptions) string {
 	if opts.Ref != "" {
 		params.Set("ref_name", opts.Ref)
 	}
+	if opts.Path != "" {
+		params.Set("path", opts.Path)
+	}
 	return params.Encode()
 }
 
@@ -96,3 +134,19 @@ func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
 	}
 	return params.Encode()
 }
+
+func encodeMilestoneListOptions(opts scm.MilestoneListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
diff --git a/scm/driver/gitlab/util_test.go b/scm/driver/gitlab/util_test.go
index 6eedbac27..25fcbf933 100644
--- a/scm/driver/gitlab/util_test.go
+++ b/scm/driver/gitlab/util_test.go
@@ -39,8 +39,9 @@ func Test_encodeCommitListOptions(t *testing.T) {
 		Page: 10,
 		Size: 30,
 		Ref:  "master",
+		Path: "Readme.md",
 	}
-	want := "page=10&per_page=30&ref_name=master"
+	want := "page=10&path=Readme.md&per_page=30&ref_name=master"
 	got := encodeCommitListOptions(opts)
 	if got != want {
 		t.Errorf("Want encoded commit list options %q, got %q", want, got)
diff --git a/scm/driver/gitlab/webhook.go b/scm/driver/gitlab/webhook.go
index e3ee5ad73..c76f33f79 100644
--- a/scm/driver/gitlab/webhook.go
+++ b/scm/driver/gitlab/webhook.go
@@ -11,8 +11,10 @@ import (
 	"io/ioutil"
 	"net/http"
 	"strconv"
+	"time"
 
 	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
 )
 
 type webhookService struct {
@@ -35,6 +37,12 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 		return nil, scm.ErrUnknownEvent
 	case "Merge Request Hook":
 		hook, err = parsePullRequestHook(data)
+	case "Note Hook":
+		hook, err = parseIssueCommentHook(data)
+	case "System Hook":
+		hook, err = parseSystemHook(data)
+	case "Pipeline Hook":
+		return parsePipelineHook(data)
 	default:
 		return nil, scm.ErrUnknownEvent
 	}
@@ -59,6 +67,49 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 	return hook, nil
 }
 
+func parseSystemHook(data []byte) (scm.Webhook, error) {
+	src := new(event)
+	err := json.Unmarshal(data, src)
+	if err != nil {
+		return nil, err
+	}
+	switch src.ObjectKind {
+	case "push", "tag_push":
+		return parsePushHook(data)
+	case "issue":
+		return nil, scm.ErrUnknownEvent
+	case "merge_request":
+		return parsePullRequestHook(data)
+	case "note":
+		return parseIssueCommentHook(data)
+	default:
+		return nil, scm.ErrUnknownEvent
+	}
+}
+
+func parseIssueCommentHook(data []byte) (scm.Webhook, error) {
+	src := new(commentHook)
+	err := json.Unmarshal(data, src)
+	if err != nil {
+		return nil, err
+	}
+	dst, err := convertCommentHook(src)
+	if err != nil {
+		return nil, err
+	}
+	return dst, nil
+}
+
+func parsePipelineHook(data []byte) (scm.Webhook, error) {
+	src := new(pipelineHook)
+	err := json.Unmarshal(data, src)
+	if err != nil {
+		return nil, err
+	}
+	dst, err := convertPipelineHook(src), nil
+	return dst, nil
+}
+
 func parsePushHook(data []byte) (scm.Webhook, error) {
 	src := new(pushHook)
 	err := json.Unmarshal(data, src)
@@ -114,10 +165,12 @@ func convertPushHook(src *pushHook) *scm.PushHook {
 				Author: scm.Signature{
 					Name:  c.Author.Name,
 					Email: c.Author.Email,
+					Date:  c.Timestamp.ValueOrZero(),
 				},
 				Committer: scm.Signature{
 					Name:  c.Author.Name,
 					Email: c.Author.Email,
+					Date:  c.Timestamp.ValueOrZero(),
 				},
 			})
 	}
@@ -202,6 +255,78 @@ func converBranchHook(src *pushHook) *scm.BranchHook {
 	}
 }
 
+func convertCommentHook(src *commentHook) (*scm.IssueCommentHook, error) {
+	var issue scm.Issue
+	var comment scm.Comment
+
+	switch src.ObjectAttributes.NoteableType {
+	case "Commit", "Issue", "Snippet":
+		return nil, scm.ErrUnknownEvent
+	case "MergeRequest":
+		pr := scm.PullRequest{
+			Number:  src.MergeRequest.Iid,
+			Title:   src.MergeRequest.Title,
+			Body:    src.MergeRequest.Description,
+			Sha:     src.MergeRequest.LastCommit.ID,
+			Ref:     fmt.Sprintf("refs/merge-requests/%d/head", src.MergeRequest.Iid),
+			Source:  src.MergeRequest.SourceBranch,
+			Target:  src.MergeRequest.TargetBranch,
+			Link:    src.MergeRequest.URL,
+			Draft:   src.MergeRequest.WorkInProgress,
+			Closed:  src.MergeRequest.State != "opened",
+			Merged:  src.MergeRequest.State == "merged",
+			Author:  *convertUser(&src.User),
+			Created: parseTimeString(src.MergeRequest.CreatedAt),
+			Updated: parseTimeString(src.MergeRequest.UpdatedAt),
+		}
+		for _, l := range src.MergeRequest.Labels {
+			label := scm.Label{
+				Name:  l.Title,
+				Color: l.Color,
+			}
+			pr.Labels = append(pr.Labels, label)
+		}
+		issue = scm.Issue{
+			Number:      src.MergeRequest.Iid,
+			Title:       src.MergeRequest.Title,
+			Body:        src.MergeRequest.Title,
+			Link:        src.Project.WebURL,
+			Author:      *convertUser(&src.User),
+			PullRequest: pr,
+			Created:     parseTimeString(src.ObjectAttributes.CreatedAt),
+			Updated:     parseTimeString(src.ObjectAttributes.UpdatedAt),
+		}
+		comment = scm.Comment{
+			ID:      src.ObjectAttributes.ID,
+			Body:    src.ObjectAttributes.Note,
+			Author:  *convertUser(&src.User),
+			Created: parseTimeString(src.ObjectAttributes.CreatedAt),
+			Updated: parseTimeString(src.ObjectAttributes.UpdatedAt),
+		}
+	default:
+		return nil, scm.ErrUnknownEvent
+	}
+
+	namespace, _ := scm.Split(src.Project.PathWithNamespace)
+	dst := scm.IssueCommentHook{
+		Action: scm.ActionCreate,
+		Repo: scm.Repository{
+			ID:        strconv.Itoa(src.Project.ID),
+			Namespace: namespace,
+			Name:      src.Repository.Name,
+			Clone:     src.Project.GitHTTPURL,
+			CloneSSH:  src.Project.GitSSHURL,
+			Link:      src.Project.WebURL,
+			Branch:    src.Project.DefaultBranch,
+			Private:   false, // TODO how do we correctly set Private vs Public?
+		},
+		Issue:   issue,
+		Comment: comment,
+		Sender:  *convertUser(&src.User),
+	}
+	return &dst, nil
+}
+
 func convertTagHook(src *pushHook) *scm.TagHook {
 	action := scm.ActionCreate
 	commit := src.After
@@ -248,6 +373,9 @@ func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
 		action = scm.ActionMerge
 	case "update":
 		action = scm.ActionSync
+		if src.Changes.Draft.Previous.Bool == false && src.Changes.Draft.Current.Bool == true {
+			action = scm.ActionReviewReady
+		}
 	}
 	fork := scm.Join(
 		src.ObjectAttributes.Source.Namespace,
@@ -266,10 +394,9 @@ func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
 			Target: src.ObjectAttributes.TargetBranch,
 			Fork:   fork,
 			Link:   src.ObjectAttributes.URL,
+			Draft:  src.ObjectAttributes.WorkInProgress,
 			Closed: src.ObjectAttributes.State != "opened",
 			Merged: src.ObjectAttributes.State == "merged",
-			// Created   : src.ObjectAttributes.CreatedAt,
-			// Updated  : src.ObjectAttributes.UpdatedAt, // 2017-12-10 17:01:11 UTC
 			Author: scm.User{
 				Login:  src.User.Username,
 				Name:   src.User.Name,
@@ -296,7 +423,70 @@ func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
 	}
 }
 
+func parseTimeString(timeString string) time.Time {
+	layout := "2006-01-02 15:04:05 UTC"
+	// Returns zero value of time in case of an error 0001-01-01 00:00:00 +0000 UTC
+	t, _ := time.Parse(layout, timeString)
+	return t
+}
+
+func convertPipelineHook(src *pipelineHook) *scm.PipelineHook {
+	namespace, name := scm.Split(src.Project.PathWithNamespace)
+	return &scm.PipelineHook{
+		Repo: scm.Repository{
+			ID:        strconv.Itoa(src.Project.ID),
+			Namespace: namespace,
+			Name:      name,
+			Clone:     src.Project.GitHTTPURL,
+			CloneSSH:  src.Project.GitSSHURL,
+			Link:      src.Project.WebURL,
+			Branch:    src.Project.DefaultBranch,
+			Private:   false,
+		},
+		Commit: scm.Commit{
+			Sha:     src.ObjectAttributes.SHA,
+			Message: src.Commit.Message,
+			Author: scm.Signature{
+				Name:  src.Commit.Author.Name,
+				Email: src.Commit.Author.Email,
+			},
+			Committer: scm.Signature{
+				Name:  src.Commit.Author.Name,
+				Email: src.Commit.Author.Email,
+			},
+			Link: src.Commit.URL,
+		},
+		Execution: scm.Execution{
+			Number:  src.ObjectAttributes.ID,
+			Status:  scm.ConvertExecutionStatus(src.ObjectAttributes.Status),
+			Created: parseTimeString(src.ObjectAttributes.CreatedAt),
+			URL:     src.ObjectAttributes.URL,
+		},
+		Sender: scm.User{
+			Login:  src.User.Username,
+			Name:   src.User.Name,
+			Email:  src.User.Email,
+			Avatar: src.User.AvatarURL,
+		},
+		PullRequest: scm.PullRequest{
+			Number:  src.MergeRequest.ID,
+			Title:   src.MergeRequest.Title,
+			Sha:     src.ObjectAttributes.SHA,
+			Ref:     src.ObjectAttributes.Ref,
+			Source:  src.MergeRequest.SourceBranch,
+			Target:  src.MergeRequest.TargetBranch,
+			Link:    src.MergeRequest.URL,
+			Created: parseTimeString(src.ObjectAttributes.CreatedAt),
+		},
+	}
+}
+
 type (
+	// Generic struct to detect event type
+	event struct {
+		ObjectKind string `json:"object_kind"`
+	}
+
 	pushHook struct {
 		ObjectKind   string      `json:"object_kind"`
 		EventName    string      `json:"event_name"`
@@ -330,10 +520,10 @@ type (
 			HTTPURL           string      `json:"http_url"`
 		} `json:"project"`
 		Commits []struct {
-			ID        string `json:"id"`
-			Message   string `json:"message"`
-			Timestamp string `json:"timestamp"`
-			URL       string `json:"url"`
+			ID        string    `json:"id"`
+			Message   string    `json:"message"`
+			Timestamp null.Time `json:"timestamp"`
+			URL       string    `json:"url"`
 			Author    struct {
 				Name  string `json:"name"`
 				Email string `json:"email"`
@@ -356,13 +546,10 @@ type (
 
 	commentHook struct {
 		ObjectKind string `json:"object_kind"`
-		User       struct {
-			Name      string `json:"name"`
-			Username  string `json:"username"`
-			AvatarURL string `json:"avatar_url"`
-		} `json:"user"`
-		ProjectID int `json:"project_id"`
-		Project   struct {
+		EventType  string `json:"event_type"`
+		User       user   `json:"user"`
+		ProjectID  int    `json:"project_id"`
+		Project    struct {
 			ID                int         `json:"id"`
 			Name              string      `json:"name"`
 			Description       string      `json:"description"`
@@ -381,56 +568,29 @@ type (
 			HTTPURL           string      `json:"http_url"`
 		} `json:"project"`
 		ObjectAttributes struct {
-			ID           int         `json:"id"`
-			Note         string      `json:"note"`
-			NoteableType string      `json:"noteable_type"`
-			AuthorID     int         `json:"author_id"`
-			CreatedAt    string      `json:"created_at"`
-			UpdatedAt    string      `json:"updated_at"`
-			ProjectID    int         `json:"project_id"`
-			Attachment   interface{} `json:"attachment"`
-			LineCode     string      `json:"line_code"`
-			CommitID     string      `json:"commit_id"`
-			NoteableID   int         `json:"noteable_id"`
-			StDiff       interface{} `json:"st_diff"`
-			System       bool        `json:"system"`
-			UpdatedByID  interface{} `json:"updated_by_id"`
-			Type         string      `json:"type"`
-			Position     struct {
-				BaseSha      string      `json:"base_sha"`
-				StartSha     string      `json:"start_sha"`
-				HeadSha      string      `json:"head_sha"`
-				OldPath      string      `json:"old_path"`
-				NewPath      string      `json:"new_path"`
-				PositionType string      `json:"position_type"`
-				OldLine      interface{} `json:"old_line"`
-				NewLine      int         `json:"new_line"`
-			} `json:"position"`
-			OriginalPosition struct {
-				BaseSha      string      `json:"base_sha"`
-				StartSha     string      `json:"start_sha"`
-				HeadSha      string      `json:"head_sha"`
-				OldPath      string      `json:"old_path"`
-				NewPath      string      `json:"new_path"`
-				PositionType string      `json:"position_type"`
-				OldLine      interface{} `json:"old_line"`
-				NewLine      int         `json:"new_line"`
-			} `json:"original_position"`
-			ResolvedAt     interface{} `json:"resolved_at"`
-			ResolvedByID   interface{} `json:"resolved_by_id"`
-			DiscussionID   string      `json:"discussion_id"`
-			ChangePosition struct {
-				BaseSha      interface{} `json:"base_sha"`
-				StartSha     interface{} `json:"start_sha"`
-				HeadSha      interface{} `json:"head_sha"`
-				OldPath      interface{} `json:"old_path"`
-				NewPath      interface{} `json:"new_path"`
-				PositionType string      `json:"position_type"`
-				OldLine      interface{} `json:"old_line"`
-				NewLine      interface{} `json:"new_line"`
-			} `json:"change_position"`
-			ResolvedByPush interface{} `json:"resolved_by_push"`
-			URL            string      `json:"url"`
+			ID               int         `json:"id"`
+			Note             string      `json:"note"`
+			NoteableType     string      `json:"noteable_type"`
+			AuthorID         int         `json:"author_id"`
+			CreatedAt        string      `json:"created_at"`
+			UpdatedAt        string      `json:"updated_at"`
+			ProjectID        int         `json:"project_id"`
+			Attachment       interface{} `json:"attachment"`
+			LineCode         string      `json:"line_code"`
+			CommitID         string      `json:"commit_id"`
+			NoteableID       int         `json:"noteable_id"`
+			StDiff           interface{} `json:"st_diff"`
+			System           bool        `json:"system"`
+			ResolvedAt       interface{} `json:"resolved_at"`
+			ResolvedByID     interface{} `json:"resolved_by_id"`
+			ResolvedByPush   interface{} `json:"resolved_by_push"`
+			DiscussionID     string      `json:"discussion_id"`
+			URL              string      `json:"url"`
+			Position         interface{} `json:"position"`
+			OriginalPosition interface{} `json:"original_position"`
+			ChangePosition   interface{} `json:"change_position"`
+			Type             interface{} `json:"type"`
+			Description      string      `json:"description"`
 		} `json:"object_attributes"`
 		Repository struct {
 			Name        string `json:"name"`
@@ -439,34 +599,44 @@ type (
 			Homepage    string `json:"homepage"`
 		} `json:"repository"`
 		MergeRequest struct {
-			AssigneeID                interface{} `json:"assignee_id"`
-			AuthorID                  int         `json:"author_id"`
-			CreatedAt                 string      `json:"created_at"`
-			DeletedAt                 interface{} `json:"deleted_at"`
-			Description               string      `json:"description"`
-			HeadPipelineID            interface{} `json:"head_pipeline_id"`
-			ID                        int         `json:"id"`
-			Iid                       int         `json:"iid"`
-			LastEditedAt              interface{} `json:"last_edited_at"`
-			LastEditedByID            interface{} `json:"last_edited_by_id"`
-			MergeCommitSha            interface{} `json:"merge_commit_sha"`
-			MergeError                interface{} `json:"merge_error"`
-			MergeParams               interface{} `json:"-"`
-			MergeStatus               string      `json:"merge_status"`
-			MergeUserID               interface{} `json:"merge_user_id"`
-			MergeWhenPipelineSucceeds bool        `json:"merge_when_pipeline_succeeds"`
-			MilestoneID               interface{} `json:"milestone_id"`
-			SourceBranch              string      `json:"source_branch"`
-			SourceProjectID           int         `json:"source_project_id"`
-			State                     string      `json:"state"`
-			TargetBranch              string      `json:"target_branch"`
-			TargetProjectID           int         `json:"target_project_id"`
-			TimeEstimate              int         `json:"time_estimate"`
-			Title                     string      `json:"title"`
-			UpdatedAt                 string      `json:"updated_at"`
-			UpdatedByID               interface{} `json:"updated_by_id"`
-			URL                       string      `json:"url"`
-			Source                    struct {
+			AssigneeID                 interface{} `json:"assignee_id"`
+			AuthorID                   int         `json:"author_id"`
+			CreatedAt                  string      `json:"created_at"`
+			DeletedAt                  interface{} `json:"deleted_at"`
+			Description                string      `json:"description"`
+			HeadPipelineID             interface{} `json:"head_pipeline_id"`
+			ID                         int         `json:"id"`
+			Iid                        int         `json:"iid"`
+			LastEditedAt               interface{} `json:"last_edited_at"`
+			LastEditedByID             interface{} `json:"last_edited_by_id"`
+			MergeCommitSha             interface{} `json:"merge_commit_sha"`
+			MergeError                 interface{} `json:"merge_error"`
+			MergeParams                interface{} `json:"-"`
+			MergeStatus                string      `json:"merge_status"`
+			MergeUserID                interface{} `json:"merge_user_id"`
+			MergeWhenPipelineSucceeds  bool        `json:"merge_when_pipeline_succeeds"`
+			MilestoneID                interface{} `json:"milestone_id"`
+			SourceBranch               string      `json:"source_branch"`
+			SourceProjectID            int         `json:"source_project_id"`
+			StateID                    int         `json:"state_id"`
+			State                      string      `json:"state"`
+			TargetBranch               string      `json:"target_branch"`
+			TargetProjectID            int         `json:"target_project_id"`
+			TimeEstimate               int         `json:"time_estimate"`
+			Title                      string      `json:"title"`
+			UpdatedAt                  string      `json:"updated_at"`
+			UpdatedByID                interface{} `json:"updated_by_id"`
+			URL                        string      `json:"url"`
+			WorkInProgress             bool        `json:"work_in_progress"`
+			TimeChange                 int         `json:"time_change"`
+			HumanTimeChange            int         `json:"human_time_change"`
+			TotalTimeSpent             int         `json:"total_time_spent"`
+			HumanTotalTimeSpent        interface{} `json:"human_total_time_spent"`
+			HumanTimeEstimate          interface{} `json:"human_time_estimate"`
+			Action                     string      `json:"action"`
+			AssigneeIDs                interface{} `json:"assignee_ids"`
+			BlockingDiscussionResolved bool        `json:"blocking_discussions_resolved"`
+			Source                     struct {
 				ID                int         `json:"id"`
 				Name              string      `json:"name"`
 				Description       string      `json:"description"`
@@ -512,10 +682,18 @@ type (
 					Email string `json:"email"`
 				} `json:"author"`
 			} `json:"last_commit"`
-			WorkInProgress      bool        `json:"work_in_progress"`
-			TotalTimeSpent      int         `json:"total_time_spent"`
-			HumanTotalTimeSpent interface{} `json:"human_total_time_spent"`
-			HumanTimeEstimate   interface{} `json:"human_time_estimate"`
+			Labels []struct {
+				ID          int         `json:"id"`
+				Title       string      `json:"title"`
+				Color       string      `json:"color"`
+				ProjectID   int         `json:"project_id"`
+				CreatedAt   string      `json:"created_at"`
+				UpdatedAt   string      `json:"updated_at"`
+				Template    bool        `json:"template"`
+				Description string      `json:"description"`
+				Type        string      `json:"type"`
+				GroupID     interface{} `json:"group_id"`
+			} `json:"labels"`
 		} `json:"merge_request"`
 	}
 
@@ -775,6 +953,10 @@ type (
 		} `json:"object_attributes"`
 		Labels  []interface{} `json:"labels"`
 		Changes struct {
+			Draft struct {
+				Previous null.Bool `json:"previous"`
+				Current  null.Bool `json:"current"`
+			} `json:"draft"`
 		} `json:"changes"`
 		Repository struct {
 			Name        string `json:"name"`
@@ -783,4 +965,131 @@ type (
 			Homepage    string `json:"homepage"`
 		} `json:"repository"`
 	}
+
+	pipelineHook struct {
+		ObjectKind       string `json:"object_kind"`
+		ObjectAttributes struct {
+			ID         int        `json:"id"`
+			IID        int        `json:"iid"`
+			Name       string     `json:"name"`
+			Ref        string     `json:"ref"`
+			Tag        bool       `json:"tag"`
+			SHA        string     `json:"sha"`
+			BeforeSHA  string     `json:"before_sha"`
+			Source     string     `json:"source"`
+			Status     string     `json:"status"`
+			Stages     []string   `json:"stages"`
+			CreatedAt  string     `json:"created_at"`
+			FinishedAt string     `json:"finished_at"`
+			Duration   int        `json:"duration"`
+			Variables  []variable `json:"variables"`
+			URL        string     `json:"url"`
+		} `json:"object_attributes"`
+		MergeRequest struct {
+			ID                  int    `json:"id"`
+			IID                 int    `json:"iid"`
+			Title               string `json:"title"`
+			SourceBranch        string `json:"source_branch"`
+			SourceProjectID     int    `json:"source_project_id"`
+			TargetBranch        string `json:"target_branch"`
+			TargetProjectID     int    `json:"target_project_id"`
+			State               string `json:"state"`
+			MergeStatus         string `json:"merge_status"`
+			DetailedMergeStatus string `json:"detailed_merge_status"`
+			URL                 string `json:"url"`
+		} `json:"merge_request"`
+		User struct {
+			ID        int    `json:"id"`
+			Name      string `json:"name"`
+			Username  string `json:"username"`
+			AvatarURL string `json:"avatar_url"`
+			Email     string `json:"email"`
+		} `json:"user"`
+		Project struct {
+			ID                int         `json:"id"`
+			Name              string      `json:"name"`
+			Description       string      `json:"description"`
+			WebURL            string      `json:"web_url"`
+			AvatarURL         null.String `json:"avatar_url"`
+			GitSSHURL         string      `json:"git_ssh_url"`
+			GitHTTPURL        string      `json:"git_http_url"`
+			Namespace         string      `json:"namespace"`
+			VisibilityLevel   int         `json:"visibility_level"`
+			PathWithNamespace string      `json:"path_with_namespace"`
+			DefaultBranch     string      `json:"default_branch"`
+		} `json:"project"`
+		Commit struct {
+			ID        string `json:"id"`
+			Message   string `json:"message"`
+			Timestamp string `json:"timestamp"`
+			URL       string `json:"url"`
+			Author    author `json:"author"`
+		} `json:"commit"`
+		SourcePipeline struct {
+			Project struct {
+				ID                int    `json:"id"`
+				WebURL            string `json:"web_url"`
+				PathWithNamespace string `json:"path_with_namespace"`
+			} `json:"project"`
+			PipelineID int `json:"pipeline_id"`
+			JobID      int `json:"job_id"`
+		} `json:"source_pipeline"`
+		Builds []build `json:"builds"`
+	}
+
+	variable struct {
+		Key   string `json:"key"`
+		Value string `json:"value"`
+	}
+
+	author struct {
+		Name  string `json:"name"`
+		Email string `json:"email"`
+	}
+
+	build struct {
+		ID             int         `json:"id"`
+		Stage          string      `json:"stage"`
+		Name           string      `json:"name"`
+		Status         string      `json:"status"`
+		CreatedAt      string      `json:"created_at"`
+		StartedAt      null.String `json:"started_at"`
+		FinishedAt     null.String `json:"finished_at"`
+		Duration       float64     `json:"duration"`
+		QueuedDuration float64     `json:"queued_duration"`
+		FailureReason  null.String `json:"failure_reason"`
+		When           string      `json:"when"`
+		Manual         bool        `json:"manual"`
+		AllowFailure   bool        `json:"allow_failure"`
+		User           struct {
+			ID        int    `json:"id"`
+			Name      string `json:"name"`
+			Username  string `json:"username"`
+			AvatarURL string `json:"avatar_url"`
+			Email     string `json:"email"`
+		} `json:"user"`
+		Runner        runner      `json:"runner"`
+		ArtifactsFile artifacts   `json:"artifacts_file"`
+		Environment   environment `json:"environment"`
+	}
+
+	runner struct {
+		ID          int      `json:"id"`
+		Description string   `json:"description"`
+		Active      bool     `json:"active"`
+		RunnerType  string   `json:"runner_type"`
+		IsShared    bool     `json:"is_shared"`
+		Tags        []string `json:"tags"`
+	}
+
+	artifacts struct {
+		Filename null.String `json:"filename"`
+		Size     null.Int    `json:"size"`
+	}
+
+	environment struct {
+		Name           string `json:"name"`
+		Action         string `json:"action"`
+		DeploymentTier string `json:"deployment_tier"`
+	}
 )
diff --git a/scm/driver/gitlab/webhook_test.go b/scm/driver/gitlab/webhook_test.go
index 1f153f20d..e238a615c 100644
--- a/scm/driver/gitlab/webhook_test.go
+++ b/scm/driver/gitlab/webhook_test.go
@@ -38,6 +38,18 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/branch_delete.json.golden",
 			obj:    new(scm.BranchHook),
 		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/branch_create.json",
+			after:  "testdata/webhooks/branch_create.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/branch_delete.json",
+			after:  "testdata/webhooks/branch_delete.json.golden",
+			obj:    new(scm.BranchHook),
+		},
 		// tag hooks
 		{
 			event:  "Tag Push Hook",
@@ -51,6 +63,18 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/tag_delete.json.golden",
 			obj:    new(scm.TagHook),
 		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/tag_create.json",
+			after:  "testdata/webhooks/tag_create.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/tag_delete.json",
+			after:  "testdata/webhooks/tag_delete.json.golden",
+			obj:    new(scm.TagHook),
+		},
 		// push hooks
 		{
 			event:  "Push Hook",
@@ -58,6 +82,12 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/push.json.golden",
 			obj:    new(scm.PushHook),
 		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/push.json",
+			after:  "testdata/webhooks/push.json.golden",
+			obj:    new(scm.PushHook),
+		},
 		// // issue hooks
 		// {
 		// 	event:  "issues",
@@ -79,6 +109,13 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pull_request_create.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/pull_request_create.json",
+			after:  "testdata/webhooks/pull_request_create.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+
 		// {
 		// 	event:  "Merge Request Hook",
 		// 	before: "testdata/webhooks/pull_request_edited.json",
@@ -97,6 +134,12 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pull_request_close.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
+		{
+			event:  "Merge Request Hook",
+			before: "testdata/webhooks/pull_request_review_ready.json",
+			after:  "testdata/webhooks/pull_request_review_ready.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
 		{
 			event:  "Merge Request Hook",
 			before: "testdata/webhooks/pull_request_reopen.json",
@@ -109,13 +152,49 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pull_request_merge.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
-		// // pull request comment hooks
-		// {
-		// 	event:  "issue_comment",
-		// 	before: "testdata/webhooks/pull_request_comment_created.json",
-		// 	after:  "testdata/webhooks/pull_request_comment_created.json.golden",
-		// 	obj:    new(scm.PullRequestCommentHook),
-		// },
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/pull_request_close.json",
+			after:  "testdata/webhooks/pull_request_close.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/pull_request_review_ready.json",
+			after:  "testdata/webhooks/pull_request_review_ready.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/pull_request_reopen.json",
+			after:  "testdata/webhooks/pull_request_reopen.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/pull_request_merge.json",
+			after:  "testdata/webhooks/pull_request_merge.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// Note hook for Gitlab Merge Request comment
+		{
+			event:  "Note Hook",
+			before: "testdata/webhooks/merge_request_comment_create.json",
+			after:  "testdata/webhooks/merge_request_comment_create.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		{
+			event:  "System Hook",
+			before: "testdata/webhooks/merge_request_comment_create.json",
+			after:  "testdata/webhooks/merge_request_comment_create.json.golden",
+			obj:    new(scm.IssueCommentHook),
+		},
+		{
+			event:  "Pipeline Hook",
+			before: "testdata/webhooks/pipeline_hook.json",
+			after:  "testdata/webhooks/pipeline_hook.json.golden",
+			obj:    new(scm.PipelineHook),
+		},
 	}
 
 	for _, test := range tests {
diff --git a/scm/driver/gogs/content.go b/scm/driver/gogs/content.go
index b6ebe830a..44918ccab 100644
--- a/scm/driver/gogs/content.go
+++ b/scm/driver/gogs/content.go
@@ -34,7 +34,7 @@ func (s *contentService) Update(ctx context.Context, repo, path string, params *
 	return nil, scm.ErrNotSupported
 }
 
-func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
 	return nil, scm.ErrNotSupported
 }
 
diff --git a/scm/driver/gogs/content_test.go b/scm/driver/gogs/content_test.go
index 0e3745703..8dd95de41 100644
--- a/scm/driver/gogs/content_test.go
+++ b/scm/driver/gogs/content_test.go
@@ -58,7 +58,7 @@ func TestContentUpdate(t *testing.T) {
 
 func TestContentDelete(t *testing.T) {
 	client, _ := New("https://try.gogs.io")
-	_, err := client.Contents.Delete(context.Background(), "gogits/gogs", "README.md", "master")
+	_, err := client.Contents.Delete(context.Background(), "gogits/gogs", "README.md", &scm.ContentParams{})
 	if err != scm.ErrNotSupported {
 		t.Errorf("Expect Not Supported error")
 	}
diff --git a/scm/driver/gogs/git.go b/scm/driver/gogs/git.go
index 3956547de..30103cc7b 100644
--- a/scm/driver/gogs/git.go
+++ b/scm/driver/gogs/git.go
@@ -16,6 +16,10 @@ type gitService struct {
 	client *wrapper
 }
 
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
 func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
 	path := fmt.Sprintf("api/v1/repos/%s/branches/%s", repo, name)
 	out := new(branch)
@@ -24,6 +28,15 @@ func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Re
 }
 
 func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
+	// github and gitlab permit fetching a commit by sha
+	// or branch. This code emulates the github and gitlab
+	// behavior for gogs by fetching the commit sha for the
+	// branch and using in the subsequent API call.
+	if scm.IsHash(ref) == false {
+		if branch, _, err := s.FindBranch(ctx, repo, scm.TrimRef(ref)); err == nil {
+			ref = branch.Sha // replace ref with sha
+		}
+	}
 	path := fmt.Sprintf("api/v1/repos/%s/commits/%s", repo, ref)
 	out := new(commitDetail)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
@@ -41,6 +54,12 @@ func (s *gitService) ListBranches(ctx context.Context, repo string, _ scm.ListOp
 	return convertBranchList(out), res, err
 }
 
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// Gogs doesnt provide support listing based on searchTerm
+	// Hence calling the ListBranches
+	return s.ListBranches(ctx, repo, opts.PageListOptions)
+}
+
 func (s *gitService) ListCommits(ctx context.Context, repo string, _ scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
 	return nil, nil, scm.ErrNotSupported
 }
diff --git a/scm/driver/gogs/git_test.go b/scm/driver/gogs/git_test.go
index 5b226e1db..9fe8d1322 100644
--- a/scm/driver/gogs/git_test.go
+++ b/scm/driver/gogs/git_test.go
@@ -44,6 +44,40 @@ func TestCommitFind(t *testing.T) {
 	}
 }
 
+func TestCommitFindBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("https://try.gogs.io").
+		Get("/api/v1/repos/gogits/gogs/branches/master").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branch.json")
+
+	gock.New("https://try.gogs.io").
+		Get("/api/v1/repos/gogits/gogs/commits/f05f642b892d59a0a9ef6a31f6c905a24b5db13a").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
+	client, _ := New("https://try.gogs.io")
+	got, _, err := client.Git.FindCommit(
+		context.Background(),
+		"gogits/gogs",
+		"master",
+	)
+	if err != nil {
+		t.Error(err)
+	}
+	want := new(scm.Commit)
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
 func TestCommitList(t *testing.T) {
 	client, _ := New("https://try.gogs.io")
 	_, _, err := client.Git.ListCommits(context.Background(), "gogits/gogs", scm.CommitListOptions{})
diff --git a/scm/driver/gogs/gogs.go b/scm/driver/gogs/gogs.go
index 073dc5e0c..41e3f429b 100644
--- a/scm/driver/gogs/gogs.go
+++ b/scm/driver/gogs/gogs.go
@@ -36,8 +36,10 @@ func New(uri string) (*scm.Client, error) {
 	client.Git = &gitService{client}
 	client.Issues = &issueService{client}
 	client.Organizations = &organizationService{client}
+	client.Milestones = &milestoneService{client}
 	client.PullRequests = &pullService{client}
 	client.Repositories = &repositoryService{client}
+	client.Releases = &releaseService{client}
 	client.Reviews = &reviewService{client}
 	client.Users = &userService{client}
 	client.Webhooks = &webhookService{client}
diff --git a/scm/driver/gogs/milestone.go b/scm/driver/gogs/milestone.go
new file mode 100644
index 000000000..71a78794b
--- /dev/null
+++ b/scm/driver/gogs/milestone.go
@@ -0,0 +1,31 @@
+package gogs
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/gogs/pr.go b/scm/driver/gogs/pr.go
index 1e851fdea..c2556ceb1 100644
--- a/scm/driver/gogs/pr.go
+++ b/scm/driver/gogs/pr.go
@@ -34,6 +34,10 @@ func (s *pullService) ListChanges(context.Context, string, int, scm.ListOptions)
 	return nil, nil, scm.ErrNotSupported
 }
 
+func (s *pullService) ListCommits(context.Context, string, int, scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
 func (s *pullService) Create(context.Context, string, *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
 	return nil, nil, scm.ErrNotSupported
 }
diff --git a/scm/driver/gogs/pr_test.go b/scm/driver/gogs/pr_test.go
index 3495ae165..88a4ae0a3 100644
--- a/scm/driver/gogs/pr_test.go
+++ b/scm/driver/gogs/pr_test.go
@@ -102,3 +102,11 @@ func TestPullRequestCommentDelete(t *testing.T) {
 		t.Errorf("Expect Not Supported error")
 	}
 }
+
+func TestPullRequestCommits(t *testing.T) {
+	client, _ := New("https://try.gogs.io")
+	_, _, err := client.PullRequests.ListCommits(context.Background(), "gogits/gogs", 1, scm.ListOptions{})
+	if err != scm.ErrNotSupported {
+		t.Errorf("Expect Not Supported error")
+	}
+}
diff --git a/scm/driver/gogs/release.go b/scm/driver/gogs/release.go
new file mode 100644
index 000000000..cf8ce092a
--- /dev/null
+++ b/scm/driver/gogs/release.go
@@ -0,0 +1,42 @@
+package gogs
+
+import (
+	"context"
+	"github.com/drone/go-scm/scm"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/gogs/repo.go b/scm/driver/gogs/repo.go
index d2a29fb3c..0f645c1fd 100644
--- a/scm/driver/gogs/repo.go
+++ b/scm/driver/gogs/repo.go
@@ -45,6 +45,18 @@ func (s *repositoryService) List(ctx context.Context, _ scm.ListOptions) ([]*scm
 	return convertRepositoryList(out), res, err
 }
 
+func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// Azure does not support search filters, hence calling List api without search filtering
+	return s.List(ctx, opts.ListOptions)
+}
+
+func (s *repositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("api/v1/orgs/%s/repos", namespace)
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+
 func (s *repositoryService) ListHooks(ctx context.Context, repo string, _ scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
 	path := fmt.Sprintf("api/v1/repos/%s/hooks", repo)
 	out := []*hook{}
diff --git a/scm/driver/gogs/user.go b/scm/driver/gogs/user.go
index 77abd3c76..fa0618c92 100644
--- a/scm/driver/gogs/user.go
+++ b/scm/driver/gogs/user.go
@@ -33,6 +33,10 @@ func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, err
 	return user.Email, res, err
 }
 
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
 //
 // native data structures
 //
diff --git a/scm/driver/harness/content.go b/scm/driver/harness/content.go
new file mode 100644
index 000000000..16159cb53
--- /dev/null
+++ b/scm/driver/harness/content.go
@@ -0,0 +1,255 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"encoding/base64"
+	"fmt"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type contentService struct {
+	client *wrapper
+}
+
+func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
+	slug := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(slug)
+	if err != nil {
+		return nil, nil, err
+	}
+	endpoint := fmt.Sprintf("api/v1/repos/%s/content/%s?git_ref=%s&include_commit=true&%s", repoId, path, ref, queryParams)
+	out := new(fileContent)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
+	// decode raw output content
+	raw, _ := base64.StdEncoding.DecodeString(out.Content.Data)
+	return &scm.Content{
+		Path:   path,
+		Sha:    out.LatestCommit.Sha,
+		BlobID: out.Sha,
+		Data:   raw,
+	}, res, err
+}
+
+func (s *contentService) Create(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	slug := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(slug)
+	if err != nil {
+		return nil, err
+	}
+	endpoint := fmt.Sprintf("api/v1/repos/%s/commits?%s", repoId, queryParams)
+	a := action{
+		Action:   "CREATE",
+		Path:     path,
+		Payload:  string(params.Data),
+		Encoding: "string",
+	}
+	in := editFile{
+		Branch:      params.Branch,
+		Message:     params.Message,
+		Title:       params.Message,
+		Actions:     []action{a},
+		BypassRules: true,
+		Author: identity{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) Update(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	slug := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(slug)
+	if err != nil {
+		return nil, err
+	}
+	endpoint := fmt.Sprintf("api/v1/repos/%s/commits?%s", repoId, queryParams)
+	a := action{
+		Action:   "UPDATE",
+		Path:     path,
+		Payload:  string(params.Data),
+		Encoding: "string",
+		Sha:      params.BlobID,
+	}
+	in := editFile{
+		Branch:      params.Branch,
+		Message:     params.Message,
+		Title:       params.Message,
+		Actions:     []action{a},
+		BypassRules: true,
+		Author: identity{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
+	slug := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(slug)
+	if err != nil {
+		return nil, err
+	}
+	endpoint := fmt.Sprintf("api/v1/repos/%s/commits?%s", repoId, queryParams)
+	a := action{
+		Action:   "DELETE",
+		Path:     path,
+		Encoding: "string",
+	}
+	in := editFile{
+		Branch:      params.Branch,
+		Message:     params.Message,
+		Title:       params.Message,
+		Actions:     []action{a},
+		BypassRules: true,
+		Author: identity{
+			Name:  params.Signature.Name,
+			Email: params.Signature.Email,
+		},
+	}
+
+	res, err := s.client.do(ctx, "POST", endpoint, in, nil)
+	return res, err
+}
+
+func (s *contentService) List(ctx context.Context, repo, path, ref string, _ scm.ListOptions) ([]*scm.ContentInfo, *scm.Response, error) {
+	slug := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(slug)
+	if err != nil {
+		return nil, nil, err
+	}
+	endpoint := fmt.Sprintf("api/v1/repos/%s/content/%s?git_ref=%s&include_commit=true&%s", repoId, path, ref, queryParams)
+	out := new(contentList)
+	res, err := s.client.do(ctx, "GET", endpoint, nil, &out)
+	return convertContentInfoList(out.Content.Entries), res, err
+}
+
+type (
+	identity struct {
+		Name  string `json:"name"`
+		Email string `json:"email"`
+	}
+
+	editFile struct {
+		Actions   []action `json:"actions"`
+		Author    identity `json:"author"`
+		Branch    string   `json:"branch"`
+		Message   string   `json:"message"`
+		NewBranch string   `json:"new_branch"`
+		Title     string   `json:"title"`
+
+		BypassRules bool `json:"bypass_rules"`
+	}
+
+	action struct {
+		Action   string `json:"action"`
+		Encoding string `json:"encoding"`
+		Path     string `json:"path"`
+		Payload  string `json:"payload"`
+		Sha      string `json:"sha"`
+	}
+
+	fileContent struct {
+		Type         string `json:"type"`
+		Sha          string `json:"sha"`
+		Name         string `json:"name"`
+		Path         string `json:"path"`
+		LatestCommit struct {
+			Sha     string `json:"sha"`
+			Title   string `json:"title"`
+			Message string `json:"message"`
+			Author  struct {
+				Identity identity  `json:"identity"`
+				When     time.Time `json:"when"`
+			} `json:"author"`
+			Committer struct {
+				Identity identity  `json:"identity"`
+				When     time.Time `json:"when"`
+			} `json:"committer"`
+		} `json:"latest_commit"`
+		Content struct {
+			Encoding string `json:"encoding"`
+			Data     string `json:"data"`
+			Size     int    `json:"size"`
+		} `json:"content"`
+	}
+
+	contentList struct {
+		Type         string `json:"type"`
+		Sha          string `json:"sha"`
+		Name         string `json:"name"`
+		Path         string `json:"path"`
+		LatestCommit struct {
+			Sha     string `json:"sha"`
+			Title   string `json:"title"`
+			Message string `json:"message"`
+			Author  struct {
+				Identity identity  `json:"identity"`
+				When     time.Time `json:"when"`
+			} `json:"author"`
+			Committer struct {
+				Identity identity  `json:"identity"`
+				When     time.Time `json:"when"`
+			} `json:"committer"`
+		} `json:"latest_commit"`
+		Content struct {
+			Entries []fileEntry `json:"entries"`
+		} `json:"content"`
+	}
+
+	fileEntry struct {
+		Type         string `json:"type"`
+		Sha          string `json:"sha"`
+		Name         string `json:"name"`
+		Path         string `json:"path"`
+		LatestCommit struct {
+			Sha     string `json:"sha"`
+			Title   string `json:"title"`
+			Message string `json:"message"`
+			Author  struct {
+				Identity identity  `json:"identity"`
+				When     time.Time `json:"when"`
+			} `json:"author"`
+			Committer struct {
+				Identity identity  `json:"identity"`
+				When     time.Time `json:"when"`
+			} `json:"committer"`
+		} `json:"latest_commit"`
+	}
+)
+
+func convertContentInfoList(from []fileEntry) []*scm.ContentInfo {
+	to := []*scm.ContentInfo{}
+	for _, v := range from {
+		to = append(to, convertContentInfo(v))
+	}
+	return to
+}
+
+func convertContentInfo(from fileEntry) *scm.ContentInfo {
+	to := &scm.ContentInfo{
+		Path:   from.Path,
+		Sha:    from.LatestCommit.Sha,
+		BlobID: from.Sha,
+	}
+	switch from.Type {
+	case "file":
+		to.Kind = scm.ContentKindFile
+	case "dir":
+		to.Kind = scm.ContentKindDirectory
+	default:
+		to.Kind = scm.ContentKindUnsupported
+	}
+	return to
+}
diff --git a/scm/driver/harness/content_test.go b/scm/driver/harness/content_test.go
new file mode 100644
index 000000000..f59776f2c
--- /dev/null
+++ b/scm/driver/harness/content_test.go
@@ -0,0 +1,233 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/transport"
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+const (
+	gockOrigin     = "https://qa.harness.io/gateway/code"
+	harnessOrg     = "px7xd_BFRCi-pfWPYXVjvw"
+	harnessAccount = "default"
+	harnessProject = "codeciintegration"
+	harnessRepo    = "thomas"
+	harnessPAT     = ""
+)
+
+func TestContentFind(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/content/README.md").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("plain/text").
+			File("testdata/content.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	result, _, err := client.Contents.Find(
+		context.Background(),
+		harnessRepo,
+		"README.md",
+		"98189d5cf2a751a6246c24a72945ba70839f1b20",
+	)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if got, want := result.Path, "README.md"; got != want {
+		t.Errorf("Want file Path %q, got %q", want, got)
+	}
+	if !strings.Contains(string(result.Data), "project") {
+		t.Errorf("Want file Data %q, must contain 'project'", result.Data)
+	}
+}
+
+func TestContentCreate(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Post("/gateway/code/api/v1/repos/thomas/commits").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("plain/text").
+			BodyString("{\"commit_id\":\"20ecde1f8c277da0e91750bef9f3b88f228d86db\"}")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	result, err := client.Contents.Create(
+		context.Background(),
+		harnessRepo,
+		"README.2",
+		&scm.ContentParams{
+			Data:    []byte("hello world"),
+			Message: "create README.2",
+			Branch:  "main",
+		},
+	)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if result.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentUpdate(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Post("/gateway/code/api/v1/repos/thomas/commits").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("plain/text").
+			BodyString("{\"commit_id\":\"20ecde1f8c277da0e91750bef9f3b88f228d86db\"}")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	result, err := client.Contents.Update(
+		context.Background(),
+		harnessRepo,
+		"README.2",
+		&scm.ContentParams{
+			Data:    []byte("hello world 2"),
+			Message: "update README.2",
+			Branch:  "main",
+			BlobID:  "95d09f2b10159347eece71399a7e2e907ea3df4f",
+		},
+	)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if result.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentDelete(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Post("/gateway/code/api/v1/repos/thomas/commits").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("plain/text").
+			BodyString("{\"commit_id\":\"20ecde1f8c277da0e91750bef9f3b88f228d86db\"}")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	result, err := client.Contents.Delete(
+		context.Background(),
+		harnessRepo,
+		"README.2",
+		&scm.ContentParams{
+			Message: "delete README.2",
+			Branch:  "main",
+		},
+	)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if result.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+}
+
+func TestContentList(t *testing.T) {
+	defer gock.Off()
+
+	gock.New(gockOrigin).
+		Get("/gateway/code/api/v1/repos/thomas/content/docker").
+		MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+		MatchParam("orgIdentifier", "default").
+		MatchParam("projectIdentifier", "codeciintegration").
+		MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_list.json")
+
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Contents.List(
+		context.Background(),
+		harnessRepo,
+		"docker",
+		"",
+		scm.ListOptions{},
+	)
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.ContentInfo{}
+	raw, _ := ioutil.ReadFile("testdata/content_list.json.golden")
+	json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/harness/git.go b/scm/driver/harness/git.go
new file mode 100644
index 000000000..f261f7756
--- /dev/null
+++ b/scm/driver/harness/git.go
@@ -0,0 +1,259 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type gitService struct {
+	client *wrapper
+}
+
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/branches?%s", repoID, queryParams)
+	in := &branchInput{
+		Name:        params.Name,
+		Target:      params.Sha,
+		BypassRules: true,
+	}
+	return s.client.do(ctx, "POST", path, in, nil)
+}
+
+func (s *gitService) FindBranch(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/branches/%s?%s", repoID, name, queryParams)
+	out := new(branch)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertBranch(out), res, err
+}
+
+func (s *gitService) FindCommit(ctx context.Context, repo, ref string) (*scm.Commit, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/commits/%s?%s", repoID, ref, queryParams)
+	out := new(commitInfo)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertCommitInfo(out), res, err
+}
+
+func (s *gitService) FindTag(ctx context.Context, repo, name string) (*scm.Reference, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *gitService) ListBranches(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/branches?%s&%s", repoID, encodeListOptions(opts), queryParams)
+	out := []*branch{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertBranchList(out), res, err
+}
+
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	// Harness doesnt provide support listing based on searchTerm
+	// Hence calling the ListBranches
+	return s.ListBranches(ctx, repo, opts.PageListOptions)
+}
+
+func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/commits?%s&%s", repoID, encodeCommitListOptions(opts), queryParams)
+	out := new(commits)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertCommitList(out), res, err
+}
+
+func (s *gitService) ListTags(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/tags?%s&%s", repoID, encodeListOptions(opts), queryParams)
+	out := []*branch{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertBranchList(out), res, err
+}
+
+func (s *gitService) ListChanges(ctx context.Context, repo, ref string, opts scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/commits/%s/diff?%s&%s", repoID, ref, encodeListOptions(opts), queryParams)
+	out := []*fileDiff{}
+	res, err := s.client.do(ctx, "POST", path, nil, &out)
+	return convertFileDiffs(out), res, err
+}
+
+func (s *gitService) CompareChanges(ctx context.Context, repo, source, target string, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoID, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/diff/%s...%s?%s", repoID, source, target, queryParams)
+	out := []*fileDiff{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertChangeList(out), res, err
+}
+
+// native data structures
+type (
+	commits struct {
+		Commits []commitInfo `json:"commits"`
+	}
+
+	commitInfo struct {
+		Author struct {
+			Identity struct {
+				Email string `json:"email"`
+				Name  string `json:"name"`
+			} `json:"identity"`
+			When time.Time `json:"when"`
+		} `json:"author"`
+		Committer struct {
+			Identity struct {
+				Email string `json:"email"`
+				Name  string `json:"name"`
+			} `json:"identity"`
+			When time.Time `json:"when"`
+		} `json:"committer"`
+		Message string `json:"message"`
+		Sha     string `json:"sha"`
+		Title   string `json:"title"`
+	}
+	branchInput struct {
+		Name        string `json:"name"`
+		Target      string `json:"target"`
+		BypassRules bool   `json:"bypass_rules"`
+	}
+	branch struct {
+		Commit struct {
+			Author struct {
+				Identity struct {
+					Email string `json:"email"`
+					Name  string `json:"name"`
+				} `json:"identity"`
+				When time.Time `json:"when"`
+			} `json:"author"`
+			Committer struct {
+				Identity struct {
+					Email string `json:"email"`
+					Name  string `json:"name"`
+				} `json:"identity"`
+				When time.Time `json:"when"`
+			} `json:"committer"`
+			Message string `json:"message"`
+			Sha     string `json:"sha"`
+			Title   string `json:"title"`
+		} `json:"commit"`
+		Name string `json:"name"`
+		Sha  string `json:"sha"`
+	}
+	fileDiff struct {
+		SHA         string `json:"sha"`
+		OldSHA      string `json:"old_sha,omitempty"`
+		Path        string `json:"path"`
+		OldPath     string `json:"old_path,omitempty"`
+		Status      string `json:"status"`
+		Additions   int64  `json:"additions"`
+		Deletions   int64  `json:"deletions"`
+		Changes     int64  `json:"changes"`
+		ContentURL  string `json:"content_url"`
+		Patch       []byte `json:"patch,omitempty"`
+		IsBinary    bool   `json:"is_binary"`
+		IsSubmodule bool   `json:"is_submodule"`
+	}
+)
+
+//
+// native data structure conversion
+//
+
+func convertBranchList(src []*branch) []*scm.Reference {
+	dst := []*scm.Reference{}
+	for _, v := range src {
+		dst = append(dst, convertBranch(v))
+	}
+	return dst
+}
+
+func convertBranch(src *branch) *scm.Reference {
+	return &scm.Reference{
+		Name: src.Name,
+		Path: scm.ExpandRef(src.Name, "refs/heads/"),
+		Sha:  src.Sha,
+	}
+}
+
+func convertCommitList(src *commits) []*scm.Commit {
+	var dst []*scm.Commit
+	for _, v := range src.Commits {
+		dst = append(dst, convertCommitInfo(&v))
+	}
+	return dst
+}
+
+func convertChangeList(src []*fileDiff) []*scm.Change {
+	dst := []*scm.Change{}
+	for _, v := range src {
+		dst = append(dst, convertChange(v))
+	}
+	return dst
+}
+
+func convertCommitInfo(src *commitInfo) *scm.Commit {
+	return &scm.Commit{
+		Sha:     src.Sha,
+		Message: src.Message,
+		Author: scm.Signature{
+			Name:  src.Author.Identity.Name,
+			Email: src.Author.Identity.Email,
+			Date:  src.Author.When,
+		},
+		Committer: scm.Signature{
+			Name:  src.Committer.Identity.Name,
+			Email: src.Committer.Identity.Email,
+			Date:  src.Committer.When,
+		},
+	}
+}
+
+func convertChange(src *fileDiff) *scm.Change {
+	return &scm.Change{
+		Path:         src.Path,
+		PrevFilePath: src.OldPath,
+		Added:        strings.EqualFold(src.Status, "ADDED"),
+		Renamed:      strings.EqualFold(src.Status, "RENAMED"),
+		Deleted:      strings.EqualFold(src.Status, "DELETED"),
+	}
+}
diff --git a/scm/driver/harness/git_test.go b/scm/driver/harness/git_test.go
new file mode 100644
index 000000000..dfbd5a99e
--- /dev/null
+++ b/scm/driver/harness/git_test.go
@@ -0,0 +1,277 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/transport"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/h2non/gock"
+)
+
+func TestListCommits(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/commits").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/commits.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Git.ListCommits(context.Background(), harnessRepo, scm.CommitListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	wantErr := json.Unmarshal(raw, &want)
+	if wantErr != nil {
+		t.Error(wantErr)
+		return
+	}
+	if harnessPAT != "" && len(got) > 0 {
+		// if testing against a real system and we get commits
+		return
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestFindCommit(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/commits/1d640265d8bdd818175fa736f0fcbad2c9b716c9").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/commit.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Git.FindCommit(context.Background(), harnessRepo, "1d640265d8bdd818175fa736f0fcbad2c9b716c9")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Commit)
+	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
+	wantErr := json.Unmarshal(raw, &want)
+	if wantErr != nil {
+		t.Error(wantErr)
+		return
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestFindBranch(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/branches/main").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/branch.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Git.FindBranch(context.Background(), harnessRepo, "main")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Reference)
+	raw, _ := ioutil.ReadFile("testdata/branch.json.golden")
+	wantErr := json.Unmarshal(raw, &want)
+	if wantErr != nil {
+		t.Error(wantErr)
+		return
+	}
+
+	if diff := cmp.Diff(got, want, cmpopts.IgnoreFields(scm.Reference{}, "Sha")); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestListBranches(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/branches").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/branches.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Git.ListBranches(context.Background(), harnessRepo, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches.json.golden")
+	wantErr := json.Unmarshal(raw, &want)
+	if wantErr != nil {
+		t.Error(wantErr)
+		return
+	}
+
+	if diff := cmp.Diff(got, want, cmpopts.IgnoreFields(scm.Reference{}, "Sha")); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestCreateBranch(t *testing.T) {
+
+	defer gock.Off()
+
+	gock.New(gockOrigin).
+		Post("/gateway/code/api/v1/repos/thomas/branches").
+		MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+		MatchParam("orgIdentifier", "default").
+		MatchParam("projectIdentifier", "codeciintegration").
+		MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branch.json")
+
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	input := &scm.ReferenceInput{
+		Name: "test",
+		Sha:  "e8ef0374ca0cee8048e94b28eaf0d9e2e2515a14",
+	}
+	result, err := client.Git.CreateBranch(context.Background(), harnessRepo, input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if result.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+
+}
+
+func TestCompareChanges(t *testing.T) {
+	source := "542ddabd47d7bfa79359b7b4e2af7f975354e35f"
+	target := "c7d0d4b21d5cfdf47475ff1f6281ef1a91883d"
+	defer gock.Off()
+
+	gock.New(gockOrigin).
+		Get(fmt.Sprintf("/gateway/code/api/v1/repos/thomas/diff/%s...%s", source, target)).
+		MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+		MatchParam("orgIdentifier", "default").
+		MatchParam("projectIdentifier", "codeciintegration").
+		MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+		Reply(200).
+		Type("application/json").
+		File("testdata/gitdiff.json")
+
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, result, err := client.Git.CompareChanges(context.Background(), harnessRepo, source, target, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if result.Status != 200 {
+		t.Errorf("Unexpected Results")
+	}
+
+	want := []*scm.Change{}
+	raw, _ := ioutil.ReadFile("testdata/gitdiff.json.golden")
+	wantErr := json.Unmarshal(raw, &want)
+	if wantErr != nil {
+		t.Error(wantErr)
+		return
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/harness/harness.go b/scm/driver/harness/harness.go
new file mode 100644
index 000000000..cf71dd7f5
--- /dev/null
+++ b/scm/driver/harness/harness.go
@@ -0,0 +1,111 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"io"
+	"net/url"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+// New returns a new gitness API client.
+func New(uri, account, organization, project string) (*scm.Client, error) {
+	base, err := url.Parse(uri)
+	if err != nil {
+		return nil, err
+	}
+	if !strings.HasSuffix(base.Path, "/") {
+		base.Path = base.Path + "/"
+	}
+	client := &wrapper{new(scm.Client), account, organization, project}
+	client.BaseURL = base
+	// initialize services
+	client.Driver = scm.DriverHarness
+	client.Linker = &linker{base.String()}
+	client.Contents = &contentService{client}
+	client.Git = &gitService{client}
+	client.Issues = &issueService{client}
+	client.Milestones = &milestoneService{client}
+	client.Organizations = &organizationService{client}
+	client.PullRequests = &pullService{client}
+	client.Repositories = &repositoryService{client}
+	client.Releases = &releaseService{client}
+	client.Reviews = &reviewService{client}
+	client.Users = &userService{client}
+	client.Webhooks = &webhookService{client}
+	return client.Client, nil
+}
+
+// wraper wraps the Client to provide high level helper functions
+// for making http requests and unmarshaling the response.
+type wrapper struct {
+	*scm.Client
+	account      string
+	organization string
+	project      string
+}
+
+// do wraps the Client.Do function by creating the Request and
+// unmarshalling the response.
+func (c *wrapper) do(ctx context.Context, method, path string, in, out interface{}) (*scm.Response, error) {
+	req := &scm.Request{
+		Method: method,
+		Path:   path,
+	}
+	// if we are posting or putting data, we need to
+	// write it to the body of the request.
+	if in != nil {
+		buf := new(bytes.Buffer)
+		json.NewEncoder(buf).Encode(in)
+		req.Header = map[string][]string{
+			"Content-Type": {"application/json"},
+		}
+		req.Body = buf
+	}
+
+	// execute the http request
+	res, err := c.Client.Do(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	defer res.Body.Close()
+
+	// if an error is encountered, unmarshal and return the
+	// error response.
+	if res.Status > 300 {
+		err := new(Error)
+		json.NewDecoder(res.Body).Decode(err)
+		return res, err
+	}
+
+	if out == nil {
+		return res, nil
+	}
+
+	// if raw output is expected, copy to the provided
+	// buffer and exit.
+	if w, ok := out.(io.Writer); ok {
+		io.Copy(w, res.Body)
+		return res, nil
+	}
+
+	// if a json response is expected, parse and return
+	// the json response.
+	return res, json.NewDecoder(res.Body).Decode(out)
+}
+
+// Error represents a Harness CODE error.
+type Error struct {
+	Message string `json:"message"`
+}
+
+func (e *Error) Error() string {
+	return e.Message
+}
diff --git a/scm/driver/harness/harness_test.go b/scm/driver/harness/harness_test.go
new file mode 100644
index 000000000..a1ccc6f90
--- /dev/null
+++ b/scm/driver/harness/harness_test.go
@@ -0,0 +1,47 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// package gitea implements a Gogs client.
+package harness
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func TestClient(t *testing.T) {
+	client, err := New(gockOrigin, "", "", "")
+	if err != nil {
+		t.Error(err)
+	}
+	if got, want := client.BaseURL.String(), fmt.Sprintf("%s/", gockOrigin); got != want {
+		t.Errorf("Want Client URL %q, got %q", want, got)
+	}
+}
+
+func TestClient_Error(t *testing.T) {
+	_, err := New("http://a b.com/", "", "", "")
+	if err == nil {
+		t.Errorf("Expect error when invalid URL")
+	}
+}
+
+func testPage(res *scm.Response) func(t *testing.T) {
+	return func(t *testing.T) {
+		if got, want := res.Page.Next, 2; got != want {
+			t.Errorf("Want next page %d, got %d", want, got)
+		}
+		if got, want := res.Page.Prev, 1; got != want {
+			t.Errorf("Want prev page %d, got %d", want, got)
+		}
+		if got, want := res.Page.First, 1; got != want {
+			t.Errorf("Want first page %d, got %d", want, got)
+		}
+		if got, want := res.Page.Last, 5; got != want {
+			t.Errorf("Want last page %d, got %d", want, got)
+		}
+	}
+}
diff --git a/scm/driver/harness/issue.go b/scm/driver/harness/issue.go
new file mode 100644
index 000000000..99d904791
--- /dev/null
+++ b/scm/driver/harness/issue.go
@@ -0,0 +1,55 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type issueService struct {
+	client *wrapper
+}
+
+func (s *issueService) Find(ctx context.Context, repo string, number int) (*scm.Issue, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) FindComment(ctx context.Context, repo string, index, id int) (*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) List(ctx context.Context, repo string, opts scm.IssueListOptions) ([]*scm.Issue, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) ListComments(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Create(ctx context.Context, repo string, input *scm.IssueInput) (*scm.Issue, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) CreateComment(ctx context.Context, repo string, index int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *issueService) DeleteComment(ctx context.Context, repo string, index, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Close(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Lock(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *issueService) Unlock(ctx context.Context, repo string, number int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/harness/issue_test.go b/scm/driver/harness/issue_test.go
new file mode 100644
index 000000000..5d232e2ff
--- /dev/null
+++ b/scm/driver/harness/issue_test.go
@@ -0,0 +1,5 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
diff --git a/scm/driver/harness/linker.go b/scm/driver/harness/linker.go
new file mode 100644
index 000000000..e0c01d820
--- /dev/null
+++ b/scm/driver/harness/linker.go
@@ -0,0 +1,27 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type linker struct {
+	base string
+}
+
+// Resource returns a link to the resource.
+func (l *linker) Resource(ctx context.Context, repo string, ref scm.Reference) (string, error) {
+	return "", scm.ErrNotSupported
+
+}
+
+// Diff returns a link to the diff.
+func (l *linker) Diff(ctx context.Context, repo string, source, target scm.Reference) (string, error) {
+	return "", scm.ErrNotSupported
+
+}
diff --git a/scm/driver/harness/linker_test.go b/scm/driver/harness/linker_test.go
new file mode 100644
index 000000000..5d232e2ff
--- /dev/null
+++ b/scm/driver/harness/linker_test.go
@@ -0,0 +1,5 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
diff --git a/scm/driver/harness/milestone.go b/scm/driver/harness/milestone.go
new file mode 100644
index 000000000..34784287a
--- /dev/null
+++ b/scm/driver/harness/milestone.go
@@ -0,0 +1,90 @@
+package harness
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+// stateType issue state type
+type stateType string
+
+const (
+	// stateOpen pr/issue is open
+	stateOpen stateType = "open"
+	// stateClosed pr/issue is closed
+	stateClosed stateType = "closed"
+	// stateAll is all
+	stateAll stateType = "all"
+)
+
+type milestone struct {
+	ID           int64     `json:"id"`
+	Title        string    `json:"title"`
+	Description  string    `json:"description"`
+	State        stateType `json:"state"`
+	OpenIssues   int       `json:"open_issues"`
+	ClosedIssues int       `json:"closed_issues"`
+	Created      null.Time `json:"created_at"`
+	Updated      null.Time `json:"updated_at"`
+	Closed       null.Time `json:"closed_at"`
+	Deadline     null.Time `json:"due_on"`
+}
+
+type milestoneInput struct {
+	Title       string    `json:"title"`
+	Description string    `json:"description"`
+	State       stateType `json:"state"`
+	Deadline    null.Time `json:"due_on"`
+}
+
+func convertMilestoneList(src []*milestone) []*scm.Milestone {
+	var dst []*scm.Milestone
+	for _, v := range src {
+		dst = append(dst, convertMilestone(v))
+	}
+	return dst
+}
+
+func convertMilestone(src *milestone) *scm.Milestone {
+	if src == nil || src.Deadline.IsZero() {
+		return nil
+	}
+	return &scm.Milestone{
+		Number:      int(src.ID),
+		ID:          int(src.ID),
+		Title:       src.Title,
+		Description: src.Description,
+		State:       string(src.State),
+		DueDate:     src.Deadline.ValueOrZero(),
+	}
+}
diff --git a/scm/driver/harness/milestone_test.go b/scm/driver/harness/milestone_test.go
new file mode 100644
index 000000000..1815bffed
--- /dev/null
+++ b/scm/driver/harness/milestone_test.go
@@ -0,0 +1 @@
+package harness
diff --git a/scm/driver/harness/org.go b/scm/driver/harness/org.go
new file mode 100644
index 000000000..28239d8d6
--- /dev/null
+++ b/scm/driver/harness/org.go
@@ -0,0 +1,57 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type organizationService struct {
+	client *wrapper
+}
+
+func (s *organizationService) Find(ctx context.Context, name string) (*scm.Organization, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+
+}
+
+func (s *organizationService) FindMembership(ctx context.Context, name, username string) (*scm.Membership, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *organizationService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Organization, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+
+}
+
+//
+// native data structures
+//
+
+type org struct {
+	Name   string `json:"username"`
+	Avatar string `json:"avatar_url"`
+}
+
+//
+// native data structure conversion
+//
+
+func convertOrgList(from []*org) []*scm.Organization {
+	to := []*scm.Organization{}
+	for _, v := range from {
+		to = append(to, convertOrg(v))
+	}
+	return to
+}
+
+func convertOrg(from *org) *scm.Organization {
+	return &scm.Organization{
+		Name:   from.Name,
+		Avatar: from.Avatar,
+	}
+}
diff --git a/scm/driver/harness/org_test.go b/scm/driver/harness/org_test.go
new file mode 100644
index 000000000..5d232e2ff
--- /dev/null
+++ b/scm/driver/harness/org_test.go
@@ -0,0 +1,5 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
diff --git a/scm/driver/harness/pr.go b/scm/driver/harness/pr.go
new file mode 100644
index 000000000..167ba9bf7
--- /dev/null
+++ b/scm/driver/harness/pr.go
@@ -0,0 +1,341 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"fmt"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type pullService struct {
+	client *wrapper
+}
+
+func (s *pullService) Find(ctx context.Context, repo string, index int) (*scm.PullRequest, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/pullreq/%d?%s", repoId, index, queryParams)
+	out := new(pr)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertPullRequest(out), res, err
+
+}
+
+func (s *pullService) FindComment(context.Context, string, int, int) (*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *pullService) List(ctx context.Context, repo string, opts scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/pullreq?%s&%s", repoId, encodePullRequestListOptions(opts), queryParams)
+	out := []*pr{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertPullRequestList(out), res, err
+}
+
+func (s *pullService) ListComments(context.Context, string, int, scm.ListOptions) ([]*scm.Comment, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *pullService) ListCommits(ctx context.Context, repo string, index int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/pullreq/%d/commits?%s&%s", repoId, index, encodeListOptions(opts), queryParams)
+	out := []*commit{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertCommits(out), res, err
+}
+
+func (s *pullService) ListChanges(ctx context.Context, repo string, number int, _ scm.ListOptions) ([]*scm.Change, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/pullreq/%d/diff?%s", repoId, number, queryParams)
+	out := []*fileDiff{}
+	res, err := s.client.do(ctx, "POST", path, nil, &out)
+	return convertFileDiffs(out), res, err
+}
+
+func (s *pullService) Create(ctx context.Context, repo string, input *scm.PullRequestInput) (*scm.PullRequest, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/pullreq?%s", repoId, queryParams)
+	in := &prInput{
+		Title:        input.Title,
+		Description:  input.Body,
+		SourceBranch: input.Source,
+		TargetBranch: input.Target,
+	}
+	out := new(pr)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertPullRequest(out), res, err
+}
+
+func (s *pullService) CreateComment(ctx context.Context, repo string, prNumber int, input *scm.CommentInput) (*scm.Comment, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/pullreq/%d/comments?%s", repoId, prNumber, queryParams)
+	in := &prComment{
+		Text: input.Body,
+	}
+	out := new(prCommentResponse)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertComment(out), res, err
+}
+
+func (s *pullService) DeleteComment(context.Context, string, int, int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *pullService) Merge(ctx context.Context, repo string, index int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *pullService) Close(context.Context, string, int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+// native data structures
+type (
+	pr struct {
+		Author      principal `json:"author"`
+		Created     int64     `json:"created"`
+		Description string    `json:"description"`
+		Edited      int64     `json:"edited"`
+		IsDraft     bool      `json:"is_draft"`
+
+		MergeTargetSHA   null.String `json:"merge_target_sha"`
+		MergeBaseSha     string      `json:"merge_base_sha"`
+		Merged           null.Int    `json:"merged"`
+		MergeMethod      null.String `json:"merge_method"`
+		MergeSHA         null.String `json:"merge_sha"`
+		MergeCheckStatus string      `json:"merge_check_status"`
+		MergeConflicts   []string    `json:"merge_conflicts,omitempty"`
+		Merger           *principal  `json:"merger"`
+
+		Number int64 `json:"number"`
+
+		SourceBranch string `json:"source_branch"`
+		SourceRepoID int64  `json:"source_repo_id"`
+		SourceSHA    string `json:"source_sha"`
+		TargetBranch string `json:"target_branch"`
+		TargetRepoID int64  `json:"target_repo_id"`
+
+		State string `json:"state"`
+		Stats struct {
+			Commits         null.Int `json:"commits,omitempty"`
+			Conversations   int      `json:"conversations,omitempty"`
+			FilesChanged    null.Int `json:"files_changed,omitempty"`
+			UnresolvedCount int      `json:"unresolved_count,omitempty"`
+		} `json:"stats"`
+
+		Title string `json:"title"`
+	}
+
+	reference struct {
+		Repo repository `json:"repo"`
+		Name string     `json:"ref"`
+		Sha  string     `json:"sha"`
+	}
+
+	prInput struct {
+		Description   string `json:"description"`
+		IsDraft       bool   `json:"is_draft"`
+		SourceBranch  string `json:"source_branch"`
+		SourceRepoRef string `json:"source_repo_ref"`
+		TargetBranch  string `json:"target_branch"`
+		Title         string `json:"title"`
+	}
+
+	commit struct {
+		Author struct {
+			Identity struct {
+				Email string `json:"email"`
+				Name  string `json:"name"`
+			} `json:"identity"`
+			When time.Time `json:"when"`
+		} `json:"author"`
+		Committer struct {
+			Identity struct {
+				Email string `json:"email"`
+				Name  string `json:"name"`
+			} `json:"identity"`
+			When time.Time `json:"when"`
+		} `json:"committer"`
+		Message string `json:"message"`
+		Sha     string `json:"sha"`
+		Title   string `json:"title"`
+	}
+	prComment struct {
+		LineEnd         int    `json:"line_end"`
+		LineEndNew      bool   `json:"line_end_new"`
+		LineStart       int    `json:"line_start"`
+		LineStartNew    bool   `json:"line_start_new"`
+		ParentID        int    `json:"parent_id"`
+		Path            string `json:"path"`
+		SourceCommitSha string `json:"source_commit_sha"`
+		TargetCommitSha string `json:"target_commit_sha"`
+		Text            string `json:"text"`
+	}
+	prCommentResponse struct {
+		Id        int         `json:"id"`
+		Created   int64       `json:"created"`
+		Updated   int64       `json:"updated"`
+		Edited    int64       `json:"edited"`
+		ParentId  interface{} `json:"parent_id"`
+		RepoId    int         `json:"repo_id"`
+		PullreqId int         `json:"pullreq_id"`
+		Order     int         `json:"order"`
+		SubOrder  int         `json:"sub_order"`
+		Type      string      `json:"type"`
+		Kind      string      `json:"kind"`
+		Text      string      `json:"text"`
+		Payload   struct{}    `json:"payload"`
+		Metadata  interface{} `json:"metadata"`
+		Author    struct {
+			Id          int    `json:"id"`
+			Uid         string `json:"uid"`
+			DisplayName string `json:"display_name"`
+			Email       string `json:"email"`
+			Type        string `json:"type"`
+			Created     int64  `json:"created"`
+			Updated     int64  `json:"updated"`
+		} `json:"author"`
+	}
+)
+
+// native data structure conversion
+func convertPullRequests(src []*pr) []*scm.PullRequest {
+	dst := []*scm.PullRequest{}
+	for _, v := range src {
+		dst = append(dst, convertPullRequest(v))
+	}
+	return dst
+}
+
+func convertPullRequest(src *pr) *scm.PullRequest {
+	return &scm.PullRequest{
+		Number: int(src.Number),
+		Title:  src.Title,
+		Body:   src.Description,
+		Sha:    src.SourceSHA,
+		Source: src.SourceBranch,
+		Target: src.TargetBranch,
+		Merged: src.Merged.Valid,
+		Author: scm.User{
+			Login: src.Author.Email,
+			Name:  src.Author.DisplayName,
+			ID:    src.Author.UID,
+			Email: src.Author.Email,
+		},
+		Head: scm.Reference{
+			Name: src.SourceBranch,
+			Path: scm.ExpandRef(src.SourceBranch, "refs/heads"),
+			Sha:  src.SourceSHA,
+		},
+		Base: scm.Reference{
+			Name: src.TargetBranch,
+			Path: scm.ExpandRef(src.TargetBranch, "refs/heads"),
+			Sha:  src.MergeTargetSHA.String,
+		},
+		Fork:    "fork",
+		Ref:     fmt.Sprintf("refs/pullreq/%d/head", src.Number),
+		Closed:  src.State == "closed",
+		Created: time.UnixMilli(src.Created),
+		Updated: time.UnixMilli(src.Edited),
+	}
+}
+
+func convertCommits(src []*commit) []*scm.Commit {
+	dst := []*scm.Commit{}
+	for _, v := range src {
+		dst = append(dst, convertCommit(v))
+	}
+	return dst
+}
+
+func convertCommit(src *commit) *scm.Commit {
+	return &scm.Commit{
+		Message: src.Message,
+		Sha:     src.Sha,
+		Author: scm.Signature{
+			Name:  src.Author.Identity.Name,
+			Email: src.Author.Identity.Email,
+		},
+		Committer: scm.Signature{
+			Name:  src.Committer.Identity.Name,
+			Email: src.Committer.Identity.Email,
+		},
+	}
+}
+
+func convertFileDiffs(diff []*fileDiff) []*scm.Change {
+	var dst []*scm.Change
+	for _, v := range diff {
+		dst = append(dst, convertFileDiff(v))
+	}
+	return dst
+}
+
+func convertFileDiff(diff *fileDiff) *scm.Change {
+	return &scm.Change{
+		Path:         diff.Path,
+		Added:        strings.EqualFold(diff.Status, "ADDED"),
+		Renamed:      strings.EqualFold(diff.Status, "RENAMED"),
+		Deleted:      strings.EqualFold(diff.Status, "DELETED"),
+		Sha:          diff.SHA,
+		BlobID:       "",
+		PrevFilePath: diff.OldPath,
+	}
+}
+
+func convertPullRequestList(from []*pr) []*scm.PullRequest {
+	to := []*scm.PullRequest{}
+	for _, v := range from {
+		to = append(to, convertPullRequest(v))
+	}
+	return to
+}
+
+func convertComment(comment *prCommentResponse) *scm.Comment {
+	return &scm.Comment{
+		ID:   comment.Id,
+		Body: comment.Text,
+		Author: scm.User{
+			Login:   comment.Author.Uid,
+			Name:    comment.Author.DisplayName,
+			ID:      strconv.Itoa(comment.Author.Id),
+			Email:   comment.Author.Email,
+			Created: time.UnixMilli(comment.Author.Created),
+			Updated: time.UnixMilli(comment.Author.Updated),
+		},
+		Created: time.UnixMilli(comment.Created),
+		Updated: time.UnixMilli(comment.Updated),
+	}
+}
diff --git a/scm/driver/harness/pr_test.go b/scm/driver/harness/pr_test.go
new file mode 100644
index 000000000..fa26ebb4c
--- /dev/null
+++ b/scm/driver/harness/pr_test.go
@@ -0,0 +1,193 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-cmp/cmp/cmpopts"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/transport"
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestPRFind(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/pullreq/1").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("plain/text").
+			File("testdata/pr.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.PullRequests.Find(context.Background(), harnessRepo, 1)
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := new(scm.PullRequest)
+	raw, err := ioutil.ReadFile("testdata/pr.json.golden")
+	if err != nil {
+		t.Error(err)
+	}
+	err = json.Unmarshal(raw, want)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPRCommits(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/pullreq/1/commits").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("plain/text").
+			File("testdata/pr_commits.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.PullRequests.ListCommits(context.Background(), harnessRepo, 1, scm.ListOptions{})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Commit{}
+	raw, err := ioutil.ReadFile("testdata/pr_commits.json.golden")
+	if err != nil {
+		t.Error(err)
+	}
+	err = json.Unmarshal(raw, &want)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullCreate(t *testing.T) {
+	defer gock.Off()
+	gock.New(gockOrigin).
+		Post("/gateway/code/api/v1/repos/thomas/pullreq").
+		MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+		MatchParam("orgIdentifier", "default").
+		MatchParam("projectIdentifier", "codeciintegration").
+		MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+		Reply(200).
+		Type("plain/text").
+		File("testdata/pr.json")
+
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+
+	input := scm.PullRequestInput{
+		Title:  "pull title",
+		Body:   "pull description",
+		Source: "bla",
+		Target: "main",
+	}
+
+	got, _, err := client.PullRequests.Create(context.Background(), harnessRepo, &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.PullRequest)
+	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPRComment(t *testing.T) {
+	defer gock.Off()
+	gock.New(gockOrigin).
+		Post(fmt.Sprintf("/gateway/code/api/v1/repos/%s/pullreq/1/comments", harnessRepo)).
+		MatchParam("accountIdentifier", harnessAccount).
+		MatchParam("orgIdentifier", harnessOrg).
+		MatchParam("projectIdentifier", harnessProject).
+		Reply(201).
+		Type("plain/text").
+		File("testdata/comment.json")
+
+	client, _ := New(gockOrigin, harnessAccount, harnessOrg, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+
+	input := scm.CommentInput{
+		Body: "Comment to be created in the PR",
+	}
+
+	got, _, err := client.PullRequests.CreateComment(context.Background(), harnessRepo, 1, &input)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Comment)
+	raw, _ := ioutil.ReadFile("testdata/comment.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want,
+		cmpopts.IgnoreFields(scm.Comment{}, "Created", "Updated"),
+		cmpopts.IgnoreFields(scm.User{}, "Created", "Updated")); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/harness/release.go b/scm/driver/harness/release.go
new file mode 100644
index 000000000..66d62cdb3
--- /dev/null
+++ b/scm/driver/harness/release.go
@@ -0,0 +1,112 @@
+package harness
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/null"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+type ReleaseInput struct {
+	TagName      string `json:"tag_name"`
+	Target       string `json:"target_commitish"`
+	Title        string `json:"name"`
+	Note         string `json:"body"`
+	IsDraft      bool   `json:"draft"`
+	IsPrerelease bool   `json:"prerelease"`
+}
+
+// release represents a repository release
+type release struct {
+	ID           int64         `json:"id"`
+	TagName      string        `json:"tag_name"`
+	Target       string        `json:"target_commitish"`
+	Title        string        `json:"name"`
+	Note         string        `json:"body"`
+	URL          string        `json:"url"`
+	HTMLURL      string        `json:"html_url"`
+	TarURL       string        `json:"tarball_url"`
+	ZipURL       string        `json:"zipball_url"`
+	IsDraft      bool          `json:"draft"`
+	IsPrerelease bool          `json:"prerelease"`
+	CreatedAt    null.Time     `json:"created_at"`
+	PublishedAt  null.Time     `json:"published_at"`
+	Publisher    *string       `json:"author"`
+	Attachments  []*Attachment `json:"assets"`
+}
+
+type Attachment struct {
+	ID            int64     `json:"id"`
+	Name          string    `json:"name"`
+	Size          int64     `json:"size"`
+	DownloadCount int64     `json:"download_count"`
+	Created       null.Time `json:"created_at"`
+	UUID          string    `json:"uuid"`
+	DownloadURL   string    `json:"browser_download_url"`
+}
+
+func convertRelease(src *release) *scm.Release {
+	return &scm.Release{
+		ID:          int(src.ID),
+		Title:       src.Title,
+		Description: src.Note,
+		Link:        convertAPIURLToHTMLURL(src.URL, src.TagName),
+		Tag:         src.TagName,
+		Commitish:   src.Target,
+		Draft:       src.IsDraft,
+		Prerelease:  src.IsPrerelease,
+		Created:     src.CreatedAt.ValueOrZero(),
+		Published:   src.PublishedAt.ValueOrZero(),
+	}
+}
+
+func convertReleaseList(src []*release) []*scm.Release {
+	var dst []*scm.Release
+	for _, v := range src {
+		dst = append(dst, convertRelease(v))
+	}
+	return dst
+}
+
+func releaseListOptionsToGiteaListOptions(in scm.ReleaseListOptions) ListOptions {
+	return ListOptions{
+		Page:     in.Page,
+		PageSize: in.Size,
+	}
+}
diff --git a/scm/driver/harness/release_test.go b/scm/driver/harness/release_test.go
new file mode 100644
index 000000000..1815bffed
--- /dev/null
+++ b/scm/driver/harness/release_test.go
@@ -0,0 +1 @@
+package harness
diff --git a/scm/driver/harness/repo.go b/scm/driver/harness/repo.go
new file mode 100644
index 000000000..82f095d21
--- /dev/null
+++ b/scm/driver/harness/repo.go
@@ -0,0 +1,218 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"strconv"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type repositoryService struct {
+	client *wrapper
+}
+
+func (s *repositoryService) Find(ctx context.Context, repo string) (*scm.Repository, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s?%s", repoId, queryParams)
+	out := new(repository)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	if err != nil {
+		return nil, res, err
+	}
+	convertedRepo := convertRepository(out)
+	if convertedRepo == nil {
+		return nil, res, errors.New("Harness returned an unexpected null repository")
+	}
+	return convertedRepo, res, err
+}
+
+func (s *repositoryService) FindHook(ctx context.Context, repo string, id string) (*scm.Hook, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/webhooks/%s?%s", repoId, id, queryParams)
+	out := new(hook)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertHook(out), res, err
+}
+
+func (s *repositoryService) FindPerms(ctx context.Context, repo string) (*scm.Perm, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	queryParams := fmt.Sprintf("%s=%s&%s=%s&%s=%s&%s=%s",
+		projectIdentifier, s.client.project, orgIdentifier, s.client.organization, accountIdentifier, s.client.account,
+		routingId, s.client.account)
+
+	path := fmt.Sprintf("api/v1/repos?sort=path&order=asc&%s&%s", encodeListOptions(opts), queryParams)
+	out := []*repository{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertRepositoryList(out), res, err
+}
+
+func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// harness does not support search filters, hence calling List api without search filtering
+	return s.List(ctx, opts.ListOptions)
+}
+
+func (s *repositoryService) ListNamespace(ctx context.Context, _ string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	// Client already has context about namespace
+	return s.List(ctx, opts)
+}
+
+func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Hook, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/webhooks?sort=display_name&order=asc&%s&%s", repoId, encodeListOptions(opts), queryParams)
+	out := []*hook{}
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	return convertHookList(out), res, err
+}
+
+func (s *repositoryService) ListStatus(ctx context.Context, repo string, ref string, opts scm.ListOptions) ([]*scm.Status, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *repositoryService) CreateHook(ctx context.Context, repo string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/webhooks?%s", repoId, queryParams)
+	in := new(hook)
+	in.Enabled = true
+	in.Identifier = input.Name
+	in.Secret = input.Secret
+	in.Insecure = input.SkipVerify
+	in.URL = input.Target
+	in.Triggers = input.NativeEvents
+	out := new(hook)
+	res, err := s.client.do(ctx, "POST", path, in, out)
+	return convertHook(out), res, err
+}
+
+func (s *repositoryService) CreateStatus(ctx context.Context, repo string, ref string, input *scm.StatusInput) (*scm.Status, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *repositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
+	harnessURI := buildHarnessURI(s.client.account, s.client.organization, s.client.project, repo)
+	repoId, queryParams, err := getRepoAndQueryParams(harnessURI)
+	if err != nil {
+		return nil, err
+	}
+	path := fmt.Sprintf("api/v1/repos/%s/webhooks/%s?%s", repoId, id, queryParams)
+	return s.client.do(ctx, "DELETE", path, nil, nil)
+}
+
+//
+// native data structures
+//
+
+type (
+	// harness repository resource.
+	repository struct {
+		ID             int    `json:"id"`
+		ParentID       int    `json:"parent_id"`
+		UID            string `json:"uid"`
+		Path           string `json:"path"`
+		Description    string `json:"description"`
+		IsPublic       bool   `json:"is_public"`
+		CreatedBy      int    `json:"created_by"`
+		Created        int64  `json:"created"`
+		Updated        int64  `json:"updated"`
+		DefaultBranch  string `json:"default_branch"`
+		ForkID         int    `json:"fork_id"`
+		NumForks       int    `json:"num_forks"`
+		NumPulls       int    `json:"num_pulls"`
+		NumClosedPulls int    `json:"num_closed_pulls"`
+		NumOpenPulls   int    `json:"num_open_pulls"`
+		NumMergedPulls int    `json:"num_merged_pulls"`
+		GitURL         string `json:"git_url"`
+	}
+	hook struct {
+		Created               int      `json:"created"`
+		CreatedBy             int      `json:"created_by"`
+		Description           string   `json:"description"`
+		Enabled               bool     `json:"enabled"`
+		HasSecret             bool     `json:"has_secret"`
+		Secret                string   `json:"secret"`
+		Identifier            string   `json:"identifier"`
+		Insecure              bool     `json:"insecure"`
+		LatestExecutionResult string   `json:"latest_execution_result"`
+		ParentID              int      `json:"parent_id"`
+		ParentType            string   `json:"parent_type"`
+		Triggers              []string `json:"triggers"`
+		Updated               int      `json:"updated"`
+		URL                   string   `json:"url"`
+		Version               int      `json:"version"`
+	}
+)
+
+//
+// native data structure conversion
+//
+
+func convertRepositoryList(src []*repository) []*scm.Repository {
+	var dst []*scm.Repository
+	for _, v := range src {
+		dst = append(dst, convertRepository(v))
+	}
+	return dst
+}
+
+func convertRepository(src *repository) *scm.Repository {
+	return &scm.Repository{
+		ID:        strconv.Itoa(src.ID),
+		Namespace: src.Path,
+		Name:      src.UID,
+		Branch:    src.DefaultBranch,
+		Private:   !src.IsPublic,
+		Clone:     src.GitURL,
+		CloneSSH:  src.GitURL,
+		Link:      src.GitURL,
+		// Created:   time.Unix(src.Created, 0),
+		//		Updated:   time.Unix(src.Updated, 0),
+	}
+}
+
+func convertHookList(from []*hook) []*scm.Hook {
+	to := []*scm.Hook{}
+	for _, v := range from {
+		to = append(to, convertHook(v))
+	}
+	return to
+}
+
+func convertHook(from *hook) *scm.Hook {
+	return &scm.Hook{
+		// keeping id same as name
+		ID:         from.Identifier,
+		Name:       from.Identifier,
+		Active:     from.Enabled,
+		Target:     from.URL,
+		Events:     from.Triggers,
+		SkipVerify: from.Insecure,
+	}
+}
diff --git a/scm/driver/harness/repo_test.go b/scm/driver/harness/repo_test.go
new file mode 100644
index 000000000..1fc861084
--- /dev/null
+++ b/scm/driver/harness/repo_test.go
@@ -0,0 +1,254 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/transport"
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+	"github.com/h2non/gock"
+)
+
+func TestRepositoryFind(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/demo").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/repo.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Repositories.Find(context.Background(), "demo")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Repository)
+	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryList(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos").
+			MatchParam("page", "1").
+			MatchParam("limit", "20").
+			MatchParam("sort", "path").
+			MatchParam("order", "asc").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/repos.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Repositories.List(context.Background(), scm.ListOptions{Page: 1, Size: 20})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if harnessPAT != "" && len(got) > 0 {
+		// pass when running against a live harness instance and we get more than one repo
+		return
+	}
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryHookList(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/webhooks").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("page", "1").
+			MatchParam("limit", "30").
+			MatchParam("sort", "display_name").
+			MatchParam("order", "asc").
+			Reply(200).
+			Type("application/json").
+			File("testdata/hooks.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Repositories.ListHooks(context.Background(), harnessRepo, scm.ListOptions{Page: 1, Size: 30})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := []*scm.Hook{}
+	raw, _ := ioutil.ReadFile("testdata/hooks.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryFindHook(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Get("/gateway/code/api/v1/repos/thomas/webhooks/6").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/hook.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Repositories.FindHook(context.Background(), harnessRepo, "6")
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Hook)
+	raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryHookCreateDelete(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Post("/gateway/code/api/v1/repos/thomas/webhooks").
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(200).
+			Type("application/json").
+			File("testdata/hook_create.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	in := &scm.HookInput{
+		Name:       "drone",
+		Target:     "https://example.com",
+		Secret:     "topsecret",
+		SkipVerify: true,
+	}
+	got, _, err := client.Repositories.CreateHook(context.Background(), harnessRepo, in)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.Hook)
+	raw, _ := ioutil.ReadFile("testdata/hook_create.json.golden")
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want, cmpopts.IgnoreFields(scm.Hook{}, "ID")); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+	// delete webhook
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		gock.New(gockOrigin).
+			Delete(fmt.Sprintf("/gateway/code/api/v1/repos/thomas/webhooks/%s", got.ID)).
+			MatchParam("accountIdentifier", "px7xd_BFRCi-pfWPYXVjvw").
+			MatchParam("orgIdentifier", "default").
+			MatchParam("projectIdentifier", "codeciintegration").
+			MatchParam("routingId", "px7xd_BFRCi-pfWPYXVjvw").
+			Reply(204)
+	}
+	client, _ = New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+
+	_, deleteErr := client.Repositories.DeleteHook(context.Background(), harnessRepo, got.ID)
+	if deleteErr != nil {
+		t.Error(deleteErr)
+		return
+	}
+}
diff --git a/scm/driver/harness/review.go b/scm/driver/harness/review.go
new file mode 100644
index 000000000..898595bd4
--- /dev/null
+++ b/scm/driver/harness/review.go
@@ -0,0 +1,31 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type reviewService struct {
+	client *wrapper
+}
+
+func (s *reviewService) Find(ctx context.Context, repo string, number, id int) (*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) List(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) Create(ctx context.Context, repo string, number int, input *scm.ReviewInput) (*scm.Review, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *reviewService) Delete(ctx context.Context, repo string, number, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/harness/review_test.go b/scm/driver/harness/review_test.go
new file mode 100644
index 000000000..5d232e2ff
--- /dev/null
+++ b/scm/driver/harness/review_test.go
@@ -0,0 +1,5 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
diff --git a/scm/driver/harness/testdata/branch.json b/scm/driver/harness/testdata/branch.json
new file mode 100644
index 000000000..79159c0fc
--- /dev/null
+++ b/scm/driver/harness/testdata/branch.json
@@ -0,0 +1,23 @@
+{
+  "name": "main",
+  "sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9",
+  "commit": {
+    "sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9",
+    "title": "delete README.2",
+    "message": "delete README.2\n\ndelete README.2",
+    "author": {
+      "identity": {
+        "name": "thomas.honey",
+        "email": "thomas.honey@harness.io"
+      },
+      "when": "2023-02-08T16:17:50Z"
+    },
+    "committer": {
+      "identity": {
+        "name": "Harness",
+        "email": "noreply@harness.io"
+      },
+      "when": "2023-02-08T16:17:50Z"
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/branch.json.golden b/scm/driver/harness/testdata/branch.json.golden
new file mode 100644
index 000000000..0b5426713
--- /dev/null
+++ b/scm/driver/harness/testdata/branch.json.golden
@@ -0,0 +1,5 @@
+{
+    "Name": "main",
+    "Path": "refs/heads/main",
+    "Sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9"
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/branches.json b/scm/driver/harness/testdata/branches.json
new file mode 100644
index 000000000..2490e4ae3
--- /dev/null
+++ b/scm/driver/harness/testdata/branches.json
@@ -0,0 +1,71 @@
+[
+  {
+    "name": "bla",
+    "sha": "0c221fd126b9457d0ad2037641416083549f59c5",
+    "commit": {
+      "sha": "0c221fd126b9457d0ad2037641416083549f59c5",
+      "title": "Create bla_file",
+      "message": "Create bla_file",
+      "author": {
+        "identity": {
+          "name": "thomas.honey",
+          "email": "thomas.honey@harness.io"
+        },
+        "when": "2023-02-09T11:20:14Z"
+      },
+      "committer": {
+        "identity": {
+          "name": "Harness",
+          "email": "noreply@harness.io"
+        },
+        "when": "2023-02-09T11:20:14Z"
+      }
+    }
+  },
+  {
+    "name": "branch3",
+    "sha": "59e1cdf0e421fd14b106f4861fb02dfd56b4dc34",
+    "commit": {
+      "sha": "59e1cdf0e421fd14b106f4861fb02dfd56b4dc34",
+      "title": "delete README.2",
+      "message": "delete README.2\n\ndelete README.2",
+      "author": {
+        "identity": {
+          "name": "thomas.honey",
+          "email": "thomas.honey@harness.io"
+        },
+        "when": "2023-02-09T12:39:37Z"
+      },
+      "committer": {
+        "identity": {
+          "name": "Harness",
+          "email": "noreply@harness.io"
+        },
+        "when": "2023-02-09T12:39:37Z"
+      }
+    }
+  },
+  {
+    "name": "main",
+    "sha": "de2837f8911710cfb7bbb323d0de285fd2ef9155",
+    "commit": {
+      "sha": "de2837f8911710cfb7bbb323d0de285fd2ef9155",
+      "title": "delete README.2",
+      "message": "delete README.2\n\ndelete README.2",
+      "author": {
+        "identity": {
+          "name": "thomas.honey",
+          "email": "thomas.honey@harness.io"
+        },
+        "when": "2023-02-09T12:39:37Z"
+      },
+      "committer": {
+        "identity": {
+          "name": "Harness",
+          "email": "noreply@harness.io"
+        },
+        "when": "2023-02-09T12:39:37Z"
+      }
+    }
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/branches.json.golden b/scm/driver/harness/testdata/branches.json.golden
new file mode 100644
index 000000000..3f2458b08
--- /dev/null
+++ b/scm/driver/harness/testdata/branches.json.golden
@@ -0,0 +1,17 @@
+[
+    {
+        "Name": "bla",
+        "Path": "refs/heads/bla",
+        "Sha": "0c221fd126b9457d0ad2037641416083549f59c5"
+    },
+    {
+        "Name": "branch3",
+        "Path": "refs/heads/branch3",
+        "Sha": "59e1cdf0e421fd14b106f4861fb02dfd56b4dc34"
+    },
+    {
+        "Name": "main",
+        "Path": "refs/heads/main",
+        "Sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/comment.json b/scm/driver/harness/testdata/comment.json
new file mode 100644
index 000000000..fa1a823f5
--- /dev/null
+++ b/scm/driver/harness/testdata/comment.json
@@ -0,0 +1,25 @@
+{
+  "id": 123,
+  "created": 1708354973112,
+  "updated": 1708354973112,
+  "edited": 1708354973112,
+  "parent_id": null,
+  "repo_id": 123,
+  "pullreq_id": 123,
+  "order": 1,
+  "sub_order": 0,
+  "type": "comment",
+  "kind": "comment",
+  "text": "Comment to be created in the PR",
+  "payload": {},
+  "metadata": null,
+  "author": {
+    "id": 1,
+    "uid": "identifier",
+    "display_name": "displayName",
+    "email": "email@emailprovider.com",
+    "type": "service",
+    "created": 1695706039266,
+    "updated": 1695706039266
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/comment.json.golden b/scm/driver/harness/testdata/comment.json.golden
new file mode 100644
index 000000000..f2e347a95
--- /dev/null
+++ b/scm/driver/harness/testdata/comment.json.golden
@@ -0,0 +1,15 @@
+{
+  "ID": 123,
+  "Body": "Comment to be created in the PR",
+  "Author": {
+    "ID": "1",
+    "Login": "identifier",
+    "Name": "displayName",
+    "Email": "email@emailprovider.com",
+    "Avatar": "",
+    "Created": 1695706039266,
+    "Updated": 1695706039266
+  },
+   "Created": 1708354973112,
+    "Updated": 1708354973112
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/commit.json b/scm/driver/harness/testdata/commit.json
new file mode 100644
index 000000000..38a6066ad
--- /dev/null
+++ b/scm/driver/harness/testdata/commit.json
@@ -0,0 +1,19 @@
+{
+  "sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9",
+  "title": "delete README.2",
+  "message": "delete README.2\n\ndelete README.2",
+  "author": {
+    "identity": {
+      "name": "thomas.honey",
+      "email": "thomas.honey@harness.io"
+    },
+    "when": "2023-02-08T16:17:50Z"
+  },
+  "committer": {
+    "identity": {
+      "name": "Harness",
+      "email": "noreply@harness.io"
+    },
+    "when": "2023-02-08T16:17:50Z"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/commit.json.golden b/scm/driver/harness/testdata/commit.json.golden
new file mode 100644
index 000000000..01d7f0f40
--- /dev/null
+++ b/scm/driver/harness/testdata/commit.json.golden
@@ -0,0 +1,19 @@
+{
+    "Sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9",
+    "Message": "delete README.2\n\ndelete README.2",
+    "Author": {
+        "Name": "thomas.honey",
+        "Email": "thomas.honey@harness.io",
+        "Date": "2023-02-08T16:17:50Z",
+        "Login": "",
+        "Avatar": ""
+    },
+    "Committer": {
+        "Name": "Harness",
+        "Email": "noreply@harness.io",
+        "Date": "2023-02-08T16:17:50Z",
+        "Login": "",
+        "Avatar": ""
+    },
+    "Link": ""
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/commits.json b/scm/driver/harness/testdata/commits.json
new file mode 100644
index 000000000..4045985d6
--- /dev/null
+++ b/scm/driver/harness/testdata/commits.json
@@ -0,0 +1,23 @@
+{
+  "commits": [
+    {
+      "sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9",
+      "title": "delete README.2",
+      "message": "delete README.2\n\ndelete README.2",
+      "author": {
+        "identity": {
+          "name": "thomas.honey",
+          "email": "thomas.honey@harness.io"
+        },
+        "when": "2023-02-08T16:17:50Z"
+      },
+      "committer": {
+        "identity": {
+          "name": "Harness",
+          "email": "noreply@harness.io"
+        },
+        "when": "2023-02-08T16:17:50Z"
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/commits.json.golden b/scm/driver/harness/testdata/commits.json.golden
new file mode 100644
index 000000000..83570e704
--- /dev/null
+++ b/scm/driver/harness/testdata/commits.json.golden
@@ -0,0 +1,21 @@
+[
+    {
+        "Sha": "1d640265d8bdd818175fa736f0fcbad2c9b716c9",
+        "Message": "delete README.2\n\ndelete README.2",
+        "Author": {
+            "Name": "thomas.honey",
+            "Email": "thomas.honey@harness.io",
+            "Date": "2023-02-08T16:17:50Z",
+            "Login": "",
+            "Avatar": ""
+        },
+        "Committer": {
+            "Name": "Harness",
+            "Email": "noreply@harness.io",
+            "Date": "2023-02-08T16:17:50Z",
+            "Login": "",
+            "Avatar": ""
+        },
+        "Link": ""
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/content.json b/scm/driver/harness/testdata/content.json
new file mode 100644
index 000000000..fbcda4f5f
--- /dev/null
+++ b/scm/driver/harness/testdata/content.json
@@ -0,0 +1,30 @@
+{
+  "type": "file",
+  "sha": "7839122fc2ec4af9d5a6c14c779a82a05c5769b5",
+  "name": "README.md",
+  "path": "README.md",
+  "latest_commit": {
+    "sha": "98189d5cf2a751a6246c24a72945ba70839f1b20",
+    "title": "initial commit",
+    "message": "initial commit",
+    "author": {
+      "identity": {
+        "name": "gitness",
+        "email": "system@gitness"
+      },
+      "when": "2023-02-02T14:12:13Z"
+    },
+    "committer": {
+      "identity": {
+        "name": "gitness",
+        "email": "system@gitness"
+      },
+      "when": "2023-02-02T14:12:13Z"
+    }
+  },
+  "content": {
+    "encoding": "base64",
+    "data": "IyB0aG9tYXMKUGxheWdyb3VuZCBwcm9qZWN0IGZvciBUaG9tYXM=",
+    "size": 38
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/content_list.json b/scm/driver/harness/testdata/content_list.json
new file mode 100644
index 000000000..33575adee
--- /dev/null
+++ b/scm/driver/harness/testdata/content_list.json
@@ -0,0 +1,79 @@
+{
+    "type": "dir",
+    "sha": "4fdc5acb2f9074a6d6ec826f01518fb27fe20a06",
+    "name": "",
+    "path": "",
+    "latest_commit": {
+        "sha": "4381eecb9dd29c34972087c4185f392c20d9f7c0",
+        "title": "move to version 1.1.2",
+        "message": "move to version 1.1.2",
+        "author": {
+            "identity": {
+                "name": "Johannes Batzill",
+                "email": "johannes.batzill@harness.io"
+            },
+            "when": "2023-02-01T02:27:42Z"
+        },
+        "committer": {
+            "identity": {
+                "name": "Johannes Batzill",
+                "email": "johannes.batzill@harness.io"
+            },
+            "when": "2023-02-01T02:27:42Z"
+        }
+    },
+    "content": {
+        "entries": [
+            {
+                "type": "file",
+                "sha": "41fdf994e79e42bd4136c7e3004367f6d21d800d",
+                "name": ".gitignore",
+                "path": ".gitignore",
+                "latest_commit": {
+                    "sha": "f8de11b6f3e4499889e16b989977c66c84ad02c9",
+                    "title": "add initial version of demo application",
+                    "message": "add initial version of demo application",
+                    "author": {
+                        "identity": {
+                            "name": "Johannes Batzill",
+                            "email": "johannes.batzill@harness.io"
+                        },
+                        "when": "2023-02-01T00:08:20Z"
+                    },
+                    "committer": {
+                        "identity": {
+                            "name": "Johannes Batzill",
+                            "email": "johannes.batzill@harness.io"
+                        },
+                        "when": "2023-02-01T00:08:20Z"
+                    }
+                }
+            },
+            {
+                "type": "file",
+                "sha": "261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
+                "name": "LICENSE",
+                "path": "LICENSE",
+                "latest_commit": {
+                    "sha": "a2a1bb3664f096e69f4b15b80f4650cc9f8ae913",
+                    "title": "Initial commit",
+                    "message": "Initial commit",
+                    "author": {
+                        "identity": {
+                            "name": "Johannes Batzill",
+                            "email": "johannes.batzill@harness.io"
+                        },
+                        "when": "2023-01-31T23:54:24Z"
+                    },
+                    "committer": {
+                        "identity": {
+                            "name": "GitHub",
+                            "email": "noreply@github.com"
+                        },
+                        "when": "2023-01-31T23:54:24Z"
+                    }
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/content_list.json.golden b/scm/driver/harness/testdata/content_list.json.golden
new file mode 100644
index 000000000..c23fd5007
--- /dev/null
+++ b/scm/driver/harness/testdata/content_list.json.golden
@@ -0,0 +1,14 @@
+[
+  {
+    "Path": ".gitignore", 
+    "Sha": "f8de11b6f3e4499889e16b989977c66c84ad02c9", 
+    "BlobID": "41fdf994e79e42bd4136c7e3004367f6d21d800d",
+    "kind": "file"
+  },
+  {
+    "Path": "LICENSE", 
+    "Sha": "a2a1bb3664f096e69f4b15b80f4650cc9f8ae913", 
+    "BlobID": "261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64",
+    "kind": "file"
+  }
+]
diff --git a/scm/driver/harness/testdata/gitdiff.json b/scm/driver/harness/testdata/gitdiff.json
new file mode 100644
index 000000000..c4dd5768d
--- /dev/null
+++ b/scm/driver/harness/testdata/gitdiff.json
@@ -0,0 +1,53 @@
+[
+    {
+        "sha": "c4e897c1fcd1cae04abf761f034ae4c5e210caad",
+        "old_sha": "0000000000000000000000000000000000000000",
+        "path": "hello.go",
+        "old_path": "hello.go",
+        "status": "ADDED",
+        "additions": 8,
+        "deletions": 0,
+        "changes": 8,
+        "content_url": "/api/v1/hello.go",
+        "is_binary": false,
+        "is_submodule": false
+    },
+    {
+        "sha": "0000000000000000000000000000000000000000",
+        "old_sha": "d7fcbf28651697b00add519d8b4402a5ab46ffc2",
+        "path": "null.go",
+        "old_path": "null.go",
+        "status": "DELETED",
+        "additions": 0,
+        "deletions": 118,
+        "changes": 118,
+        "content_url": "/api/v1/null.go",
+        "is_binary": false,
+        "is_submodule": false
+    },
+    {
+        "sha": "ce5a2cd34b697f7a8507192e7074b33737c6740c",
+        "old_sha": "7697802e4d16b255e7ea22a86071cfbe9af6aa39",
+        "path": "version4.go",
+        "old_path": "version4.go",
+        "status": "MODIFIED",
+        "additions": 8,
+        "deletions": 7,
+        "changes": 15,
+        "content_url": "/api/v1/version4.go",
+        "is_binary": false,
+        "is_submodule": false
+    },
+    {
+        "sha": "",
+        "path": "version_1.go",
+        "old_path": "version1.go",
+        "status": "RENAMED",
+        "additions": 0,
+        "deletions": 0,
+        "changes": 0,
+        "content_url": "/api/v1/version_1.go",
+        "is_binary": false,
+        "is_submodule": false
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/gitdiff.json.golden b/scm/driver/harness/testdata/gitdiff.json.golden
new file mode 100644
index 000000000..c3258c315
--- /dev/null
+++ b/scm/driver/harness/testdata/gitdiff.json.golden
@@ -0,0 +1,38 @@
+[
+    {
+        "Path": "hello.go",
+        "Added": true,
+        "Renamed": false,
+        "Deleted": false,
+        "Sha": "",
+        "BlobID": "",
+        "PrevFilePath": "hello.go"
+    },
+    {
+        "Path": "null.go",
+        "Added": false,
+        "Renamed": false,
+        "Deleted": true,
+        "Sha": "",
+        "BlobID": "",
+        "PrevFilePath": "null.go"
+    },
+    {
+        "Path": "version4.go",
+        "Added": false,
+        "Renamed": false,
+        "Deleted": false,
+        "Sha": "",
+        "BlobID": "",
+        "PrevFilePath": "version4.go"
+    },
+    {
+        "Path": "version_1.go",
+        "Added": false,
+        "Renamed": true,
+        "Deleted": false,
+        "Sha": "",
+        "BlobID": "",
+        "PrevFilePath": "version1.go"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/hook.json b/scm/driver/harness/testdata/hook.json
new file mode 100644
index 000000000..cfae54972
--- /dev/null
+++ b/scm/driver/harness/testdata/hook.json
@@ -0,0 +1,16 @@
+{
+    "version": 1,
+    "parent_id": 11,
+    "parent_type": "repo",
+    "created_by": 14,
+    "created": 1675867490853,
+    "updated": 1675867531549,
+    "identifier": "webhookname",
+    "description": "webhookdescription",
+    "url": "http://1.1.1.1",
+    "enabled": true,
+    "insecure": true,
+    "triggers": [],
+    "latest_execution_result": "success",
+    "has_secret": true
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/hook.json.golden b/scm/driver/harness/testdata/hook.json.golden
new file mode 100644
index 000000000..686b5db97
--- /dev/null
+++ b/scm/driver/harness/testdata/hook.json.golden
@@ -0,0 +1,8 @@
+{
+    "ID": "webhookname",
+    "Name": "webhookname",
+    "Target": "http://1.1.1.1",
+    "Events": [],
+    "Active": true,
+    "SkipVerify": true
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/hook_create.json b/scm/driver/harness/testdata/hook_create.json
new file mode 100644
index 000000000..cd88b4357
--- /dev/null
+++ b/scm/driver/harness/testdata/hook_create.json
@@ -0,0 +1,17 @@
+{
+  "id": 9,
+  "version": 1,
+  "parent_id": 11,
+  "parent_type": "repo",
+  "created_by": 14,
+  "created": 1675872629243,
+  "updated": 1675872777592,
+  "identifier": "drone",
+  "description": "",
+  "url": "https://example.com",
+  "enabled": true,
+  "insecure": true,
+  "triggers": [],
+  "latest_execution_result": "success",
+  "has_secret": true
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/hook_create.json.golden b/scm/driver/harness/testdata/hook_create.json.golden
new file mode 100644
index 000000000..06e9a45dc
--- /dev/null
+++ b/scm/driver/harness/testdata/hook_create.json.golden
@@ -0,0 +1,8 @@
+{
+    "ID": "9",
+    "Name": "drone",
+    "Target": "https://example.com",
+    "Events": [],
+    "Active": true,
+    "SkipVerify": true
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/hooks.json b/scm/driver/harness/testdata/hooks.json
new file mode 100644
index 000000000..9e539cecd
--- /dev/null
+++ b/scm/driver/harness/testdata/hooks.json
@@ -0,0 +1,18 @@
+[
+    {
+        "version": 1,
+        "parent_id": 11,
+        "parent_type": "repo",
+        "created_by": 14,
+        "created": 1675867490853,
+        "updated": 1675867531549,
+        "identifier": "webhookname",
+        "description": "webhookdescription",
+        "url": "http://1.1.1.1",
+        "enabled": true,
+        "insecure": true,
+        "triggers": [],
+        "latest_execution_result": "success",
+        "has_secret": true
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/hooks.json.golden b/scm/driver/harness/testdata/hooks.json.golden
new file mode 100644
index 000000000..317aa8125
--- /dev/null
+++ b/scm/driver/harness/testdata/hooks.json.golden
@@ -0,0 +1,10 @@
+[
+    {
+        "ID": "webhookname",
+        "Name": "webhookname",
+        "Target": "http://1.1.1.1",
+        "Events": [],
+        "Active": true,
+        "SkipVerify": true
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/pr.json b/scm/driver/harness/testdata/pr.json
new file mode 100644
index 000000000..57fc7d9de
--- /dev/null
+++ b/scm/driver/harness/testdata/pr.json
@@ -0,0 +1,36 @@
+{
+  "number": 1,
+  "created": 1710818863810,
+  "edited": 1710818863810,
+  "state": "open",
+  "is_draft": false,
+  "title": "feat: self as codeowner can be skipped",
+  "description": "123",
+  "source_repo_id": 7,
+  "source_branch": "abhinav/CODE-1508",
+  "source_sha": "6c4ab583f5201ed0421d0ef93ee5b0925ac08f62",
+  "target_repo_id": 7,
+  "target_branch": "main",
+  "merged": null,
+  "merge_method": null,
+  "merge_check_status": "mergeable",
+  "merge_target_sha": "b772dca15553986cc90fc6254a0fa47f4047526c",
+  "merge_base_sha": "b772dca15553986cc90fc6254a0fa47f4047526c",
+  "merge_sha": "ff13c81504ab3b04bd76b6af71e9c368333181a5",
+  "author": {
+    "id": 27,
+    "uid": "aqm0RQXGQI6v3m_f7u7C5A",
+    "display_name": "Abhinav Singh",
+    "email": "abhinav.singh@harness.io",
+    "type": "user",
+    "created": 1681264009453,
+    "updated": 1681264009453
+  },
+  "merger": null,
+  "stats": {
+    "commits": 1,
+    "files_changed": 1,
+    "conversations": 1,
+    "unresolved_count": 1
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/pr.json.golden b/scm/driver/harness/testdata/pr.json.golden
new file mode 100644
index 000000000..ae5dc76da
--- /dev/null
+++ b/scm/driver/harness/testdata/pr.json.golden
@@ -0,0 +1,33 @@
+{
+	"Number": 1,
+	"Title": "feat: self as codeowner can be skipped",
+	"Body": "123",
+	"Sha": "6c4ab583f5201ed0421d0ef93ee5b0925ac08f62",
+    "Ref": "refs/pullreq/1/head",
+    "Source": "abhinav/CODE-1508",
+    "Target": "main",
+    "Fork": "fork",
+    "Closed": false,
+    "Merged": false,
+	"Fork": "fork",
+	"Link": "",
+	"Diff": "",
+    "Base": {
+        "Sha": "b772dca15553986cc90fc6254a0fa47f4047526c",
+        "Path": "refs/heads/main",
+        "Name": "main"
+    },
+    "Head": {
+        "Sha": "6c4ab583f5201ed0421d0ef93ee5b0925ac08f62",
+        "Path": "refs/heads/abhinav/CODE-1508",
+        "Name": "abhinav/CODE-1508"
+    },
+	"Author": {
+        "ID": "aqm0RQXGQI6v3m_f7u7C5A",
+        "Login": "abhinav.singh@harness.io",
+        "Name": "Abhinav Singh",
+        "Email": "abhinav.singh@harness.io"
+	},
+    "Created": "2024-03-18T20:27:43.81-07:00",
+    "Updated": "2024-03-18T20:27:43.81-07:00"
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/pr_commits.json b/scm/driver/harness/testdata/pr_commits.json
new file mode 100644
index 000000000..286654d14
--- /dev/null
+++ b/scm/driver/harness/testdata/pr_commits.json
@@ -0,0 +1,21 @@
+[
+  {
+    "author": {
+      "identity": {
+        "email": "thomas.honey@harness.io",
+        "name": "thomas.honey"
+      },
+      "when": "2023-02-09T17:12:10.976Z"
+    },
+    "committer": {
+      "identity": {
+        "email": "noreply@harness.io",
+        "name": "Harness"
+      },
+      "when": "2023-02-09T17:12:10.976Z"
+    },
+    "message": "Create bla_file",
+    "sha": "0c221fd126b9457d0ad2037641416083549f59c5",
+    "title": "string"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/pr_commits.json.golden b/scm/driver/harness/testdata/pr_commits.json.golden
new file mode 100644
index 000000000..aeceed89f
--- /dev/null
+++ b/scm/driver/harness/testdata/pr_commits.json.golden
@@ -0,0 +1,19 @@
+[
+    {
+        "Sha": "0c221fd126b9457d0ad2037641416083549f59c5",
+        "Message": "Create bla_file",
+        "Author": {
+            "Name": "thomas.honey",
+            "Email": "thomas.honey@harness.io",
+            "Login": "",
+            "Avatar": ""
+        },
+        "Committer": {
+            "Name": "Harness",
+            "Email": "noreply@harness.io",
+            "Login": "",
+            "Avatar": ""
+        },
+        "Link": ""
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/repo.json b/scm/driver/harness/testdata/repo.json
new file mode 100644
index 000000000..7e246e625
--- /dev/null
+++ b/scm/driver/harness/testdata/repo.json
@@ -0,0 +1,19 @@
+{
+  "id": 9,
+  "parent_id": 16,
+  "uid": "demo",
+  "path": "px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo",
+  "description": "",
+  "is_public": false,
+  "created_by": 10,
+  "created": 1675210116158,
+  "updated": 1675389801066,
+  "default_branch": "main",
+  "fork_id": 0,
+  "num_forks": 0,
+  "num_pulls": 4,
+  "num_closed_pulls": 0,
+  "num_open_pulls": 1,
+  "num_merged_pulls": 3,
+  "git_url": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git"
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/repo.json.golden b/scm/driver/harness/testdata/repo.json.golden
new file mode 100644
index 000000000..4e187a6d4
--- /dev/null
+++ b/scm/driver/harness/testdata/repo.json.golden
@@ -0,0 +1,10 @@
+{
+    "ID": "9",
+    "Namespace": "px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo",
+    "Name": "demo",
+    "Branch": "main",
+    "Private": true,
+    "Clone": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git",
+    "CloneSSH": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git",
+    "Link": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git"
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/repos.json b/scm/driver/harness/testdata/repos.json
new file mode 100644
index 000000000..77fb6f96e
--- /dev/null
+++ b/scm/driver/harness/testdata/repos.json
@@ -0,0 +1,40 @@
+[
+  {
+    "id": 9,
+    "parent_id": 16,
+    "uid": "demo",
+    "path": "px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo",
+    "description": "",
+    "is_public": false,
+    "created_by": 10,
+    "created": 1675210116158,
+    "updated": 1675389801066,
+    "default_branch": "main",
+    "fork_id": 0,
+    "num_forks": 0,
+    "num_pulls": 4,
+    "num_closed_pulls": 0,
+    "num_open_pulls": 1,
+    "num_merged_pulls": 3,
+    "git_url": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git"
+  },
+  {
+    "id": 11,
+    "parent_id": 16,
+    "uid": "thomas",
+    "path": "px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas",
+    "description": "Playground project for Thomas",
+    "is_public": true,
+    "created_by": 10,
+    "created": 1675458112548,
+    "updated": 1675458112548,
+    "default_branch": "main",
+    "fork_id": 0,
+    "num_forks": 0,
+    "num_pulls": 0,
+    "num_closed_pulls": 0,
+    "num_open_pulls": 0,
+    "num_merged_pulls": 0,
+    "git_url": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas.git"
+  }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/repos.json.golden b/scm/driver/harness/testdata/repos.json.golden
new file mode 100644
index 000000000..d58eb804b
--- /dev/null
+++ b/scm/driver/harness/testdata/repos.json.golden
@@ -0,0 +1,22 @@
+[
+    {
+        "ID": "9",
+        "Namespace": "px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo",
+        "Name": "demo",
+        "Branch": "main",
+        "Private": true,
+        "Clone": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git",
+        "CloneSSH": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git",
+        "Link": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/demo.git"
+    },
+    {
+        "ID": "11",
+        "Namespace": "px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas",
+        "Name": "thomas",
+        "Branch": "main",
+        "Private": false,
+        "Clone": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas.git",
+        "CloneSSH": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas.git",
+        "Link": "https://qa.harness.io/code/git/px7xd_BFRCi-pfWPYXVjvw/default/codeciintegration/thomas.git"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/user.json b/scm/driver/harness/testdata/user.json
new file mode 100644
index 000000000..21ea693f2
--- /dev/null
+++ b/scm/driver/harness/testdata/user.json
@@ -0,0 +1,47 @@
+{
+  "status": "SUCCESS",
+  "data": {
+    "uuid": "0Nnoezs6RGa_fOWvG_Ta4w",
+    "name": "thomas.honey",
+    "email": "thomas.honey@harness.io",
+    "token": null,
+    "defaultAccountId": "px7xd_BFRCi-pfWPYXVjvw",
+    "intent": null,
+    "accounts": [
+      {
+        "uuid": "px7xd_BFRCi-pfWPYXVjvw",
+        "accountName": "harness-dev",
+        "companyName": "harness-dev",
+        "defaultExperience": "NG",
+        "createdFromNG": false,
+        "nextGenEnabled": true
+      },
+      {
+        "uuid": "Ws0xvw71Sm2YmpSC7A8z4g",
+        "accountName": "OPA-Governance",
+        "companyName": "Feature-Flag",
+        "defaultExperience": "NG",
+        "createdFromNG": false,
+        "nextGenEnabled": true
+      }
+    ],
+    "admin": false,
+    "twoFactorAuthenticationEnabled": false,
+    "emailVerified": true,
+    "locked": false,
+    "disabled": false,
+    "signupAction": null,
+    "edition": null,
+    "billingFrequency": null,
+    "utmInfo": {
+      "utmSource": null,
+      "utmContent": null,
+      "utmMedium": null,
+      "utmTerm": null,
+      "utmCampaign": null
+    },
+    "externallyManaged": false
+  },
+  "metaData": null,
+  "correlationId": "c4014fdb-10a1-4dc4-ace0-6fad93544993"
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/user.json.golden b/scm/driver/harness/testdata/user.json.golden
new file mode 100644
index 000000000..5f81bfeba
--- /dev/null
+++ b/scm/driver/harness/testdata/user.json.golden
@@ -0,0 +1,7 @@
+{
+    "ID": "0Nnoezs6RGa_fOWvG_Ta4w",
+    "Login": "thomas.honey@harness.io",
+    "Name": "thomas.honey",
+    "Email": "thomas.honey@harness.io",
+    "Avatar": ""
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/branch_create.json b/scm/driver/harness/testdata/webhooks/branch_create.json
new file mode 100644
index 000000000..426f9c54e
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/branch_create.json
@@ -0,0 +1,68 @@
+{
+    "trigger": "branch_created",
+    "repo": {
+        "id": 13,
+        "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+        "uid": "aba",
+        "default_branch": "main",
+        "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+    },
+    "principal": {
+        "id": 8,
+        "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+        "display_name": "default",
+        "email": "default@harness.io",
+        "type": "user",
+        "created": 1675390885380,
+        "updated": 1675390885380
+    },
+    "ref": {
+        "name": "refs/heads/new2",
+        "repo": {
+            "id": 13,
+            "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+            "uid": "aba",
+            "default_branch": "main",
+            "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+        }
+    },
+    "sha": "aeafa0e2e4ec6909ad75cb8fad57c0b1eb5986e6",
+    "commit": {
+        "sha": "aeafa0e2e4ec6909ad75cb8fad57c0b1eb5986e6",
+        "message": "version 4",
+        "author": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:21:15-08:00"
+        },
+        "committer": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:21:15-08:00"
+        }
+    },
+    "head_commit": {
+        "sha": "aeafa0e2e4ec6909ad75cb8fad57c0b1eb5986e6",
+        "message": "version 4",
+        "author": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:21:15-08:00"
+        },
+        "committer": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:21:15-08:00"
+        }
+    },
+    "old_sha": "0000000000000000000000000000000000000000",
+    "forced": false
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/branch_create.json.golden b/scm/driver/harness/testdata/webhooks/branch_create.json.golden
new file mode 100644
index 000000000..967f51f17
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/branch_create.json.golden
@@ -0,0 +1,29 @@
+{
+    "Ref": {
+        "Name": "refs/heads/new2",
+        "Sha": "aeafa0e2e4ec6909ad75cb8fad57c0b1eb5986e6"
+     },
+    "Action": "created",
+    "Repo": {
+        "ID": "13",
+        "Namespace": "",
+        "Name": "aba",
+        "Perm": null,
+        "Branch": "main",
+        "Private": false,
+        "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+        "CloneSSH": "",
+        "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Sender": {
+        "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+        "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+        "Name": "default",
+        "Email": "default@harness.io",
+        "Avatar": "",
+        "Created": "2023-02-02T18:21:25.38-08:00",
+        "Updated": "2023-02-02T18:21:25.38-08:00"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/branch_delete.json b/scm/driver/harness/testdata/webhooks/branch_delete.json
new file mode 100644
index 000000000..ba496b75d
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/branch_delete.json
@@ -0,0 +1,34 @@
+{
+  "trigger": "branch_deleted",
+  "repo": {
+    "id": 16,
+    "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+    "identifier": "harness-core",
+    "default_branch": "develop",
+    "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "uid": "harness-core"
+  },
+  "principal": {
+    "id": 2,
+    "uid": "lv0euRhKRCyiXWzS7pOg6g",
+    "display_name": "Admin",
+    "email": "admin@harness.io",
+    "type": "user",
+    "created": 1701091219051,
+    "updated": 1701091219051
+  },
+  "ref": {
+    "name": "refs/heads/das",
+    "repo": {
+      "id": 16,
+      "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+      "identifier": "harness-core",
+      "default_branch": "develop",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+      "uid": "harness-core"
+    }
+  },
+  "sha": "0000000000000000000000000000000000000000",
+  "old_sha": "0f1835abe08473e07863540712d8389984b72dad",
+  "forced": false
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/branch_delete.json.golden b/scm/driver/harness/testdata/webhooks/branch_delete.json.golden
new file mode 100644
index 000000000..b9a0ad8f7
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/branch_delete.json.golden
@@ -0,0 +1,29 @@
+{
+  "Ref": {
+     "Name": "refs/heads/das",
+     "Sha": "0000000000000000000000000000000000000000"
+  },
+  "Action": "deleted",
+  "Repo": {
+    "ID": "16",
+    "Namespace": "",
+    "Name": "harness-core",
+    "Perm": null,
+    "Branch": "develop",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "lv0euRhKRCyiXWzS7pOg6g",
+    "Login": "lv0euRhKRCyiXWzS7pOg6g",
+    "Name": "Admin",
+    "Email": "admin@harness.io",
+    "Avatar": "",
+    "Created": "2023-11-27T05:20:19.051-08:00",
+    "Updated": "2023-11-27T05:20:19.051-08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/branch_updated.json b/scm/driver/harness/testdata/webhooks/branch_updated.json
new file mode 100644
index 000000000..844bcfd03
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/branch_updated.json
@@ -0,0 +1,68 @@
+{
+  "trigger": "branch_updated",
+  "repo": {
+    "id": 68,
+    "path": "vpCkHKsDSxK9_KYfjCTMKA/default/Repo_With_PR_checks/abhinav-git-sync",
+    "uid": "abhinav-git-sync",
+    "default_branch": "master",
+    "git_url": "https://git.harness.io/vpCkHKsDSxK9_KYfjCTMKA/default/Repo_With_PR_checks/abhinav-git-sync.git"
+  },
+  "principal": {
+    "id": 59,
+    "uid": "ec9UfvFwTf663F47Hlqxbg",
+    "display_name": "abhinav.singh@harness.io",
+    "email": "abhinav.singh@harness.io",
+    "type": "user",
+    "created": 1697617589873,
+    "updated": 1697617589873
+  },
+  "ref": {
+    "name": "refs/heads/master",
+    "repo": {
+      "id": 68,
+      "path": "vpCkHKsDSxK9_KYfjCTMKA/default/Repo_With_PR_checks/abhinav-git-sync",
+      "uid": "abhinav-git-sync",
+      "default_branch": "master",
+      "git_url": "https://git.harness.io/vpCkHKsDSxK9_KYfjCTMKA/default/Repo_With_PR_checks/abhinav-git-sync.git"
+    }
+  },
+  "sha": "92e21bfcddc1418079cddbb518ad6fd72917798a",
+  "commit": {
+    "sha": "92e21bfcddc1418079cddbb518ad6fd72917798a",
+    "message": "Create asdsad (#2)",
+    "author": {
+      "identity": {
+        "name": "abhinav.singh@harness.io",
+        "email": "abhinav.singh@harness.io"
+      },
+      "when": "2023-12-05T11:59:39Z"
+    },
+    "committer": {
+      "identity": {
+        "name": "Harness",
+        "email": "noreply@harness.io"
+      },
+      "when": "2023-12-05T11:59:39Z"
+    }
+  },
+  "head_commit": {
+    "sha": "92e21bfcddc1418079cddbb518ad6fd72917798a",
+    "message": "Create asdsad (#2)",
+    "author": {
+      "identity": {
+        "name": "abhinav.singh@harness.io",
+        "email": "abhinav.singh@harness.io"
+      },
+      "when": "2023-12-05T11:59:39Z"
+    },
+    "committer": {
+      "identity": {
+        "name": "Harness",
+        "email": "noreply@harness.io"
+      },
+      "when": "2023-12-05T11:59:39Z"
+    }
+  },
+  "old_sha": "a273c385628167932e10caaa58e12550c491f241",
+  "forced": false
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/branch_updated.json.golden b/scm/driver/harness/testdata/webhooks/branch_updated.json.golden
new file mode 100644
index 000000000..550cf3b1d
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/branch_updated.json.golden
@@ -0,0 +1,46 @@
+{
+    "Ref": "refs/heads/master",
+    "Before": "a273c385628167932e10caaa58e12550c491f241",
+    "After": "92e21bfcddc1418079cddbb518ad6fd72917798a",
+    "Repo": {
+        "ID": "68",
+        "Namespace": "",
+        "Name": "abhinav-git-sync",
+        "Perm": null,
+        "Branch": "master",
+        "Private": false,
+        "Clone": "https://git.harness.io/vpCkHKsDSxK9_KYfjCTMKA/default/Repo_With_PR_checks/abhinav-git-sync.git",
+        "CloneSSH": "",
+        "Link": "https://git.harness.io/vpCkHKsDSxK9_KYfjCTMKA/default/Repo_With_PR_checks/abhinav-git-sync.git",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Commit": {
+        "Sha": "92e21bfcddc1418079cddbb518ad6fd72917798a",
+        "Message": "Create asdsad (#2)",
+        "Author": {
+            "Name": "abhinav.singh@harness.io",
+            "Email": "abhinav.singh@harness.io",
+            "Date" : "0001-01-01T00:00:00Z",
+            "Login": "",
+            "Avatar": ""
+        },
+        "Committer": {
+            "Name": "Harness",
+            "Email": "noreply@harness.io",
+            "Date": "0001-01-01T00:00:00Z",
+            "Login": "",
+            "Avatar": ""
+        },
+        "Link": ""
+    },
+    "Sender": {
+        "ID": "ec9UfvFwTf663F47Hlqxbg",
+        "Login": "ec9UfvFwTf663F47Hlqxbg",
+        "Name": "abhinav.singh@harness.io",
+        "Email": "abhinav.singh@harness.io",
+        "Avatar": "",
+        "Created": "2023-10-18T13:56:29.873+05:30",
+        "Updated": "2023-10-18T13:56:29.873+05:30"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_branch_updated.json b/scm/driver/harness/testdata/webhooks/pull_request_branch_updated.json
new file mode 100644
index 000000000..dec4c06f6
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_branch_updated.json
@@ -0,0 +1,100 @@
+{
+    "trigger": "pullreq_branch_updated",
+    "repo": {
+        "id": 13,
+        "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+        "uid": "aba",
+        "default_branch": "main",
+        "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+    },
+    "principal": {
+        "id": 8,
+        "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+        "display_name": "default",
+        "email": "default@harness.io",
+        "type": "user",
+        "created": 1675390885380,
+        "updated": 1675390885380
+    },
+    "pull_req": {
+        "number": 4,
+        "state": "open",
+        "is_draft": false,
+        "title": "aw",
+        "source_repo_id": 13,
+        "source_branch": "b",
+        "target_repo_id": 13,
+        "target_branch": "main",
+        "merge_strategy": null,
+        "merge_base_sha": "5473eebbc0ce1d08981c955161b07a7989566b7b",
+        "author": {
+            "id": 8,
+            "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+            "display_name": "Admin",
+            "email": "admin@harness.io",
+            "type": "user",
+            "created": 1675390885380,
+            "updated": 1675390885380
+        },
+        "pr_url": "http://localhost:3000/codeowners/asdsad/pulls/14"
+    },
+    "target_ref": {
+        "name": "refs/heads/main",
+        "repo": {
+            "id": 13,
+            "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+            "uid": "aba",
+            "default_branch": "main",
+            "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+        }
+    },
+    "ref": {
+        "name": "refs/heads/b",
+        "repo": {
+            "id": 13,
+            "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+            "uid": "aba",
+            "default_branch": "main",
+            "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+        }
+    },
+    "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "commit": {
+        "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+        "message": "updated b2",
+        "author": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:28:55-08:00"
+        },
+        "committer": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:28:55-08:00"
+        }
+    },
+    "head_commit": {
+        "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+        "message": "updated b2",
+        "author": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:28:55-08:00"
+        },
+        "committer": {
+            "identity": {
+                "name": "Admin",
+                "email": "admin@harness.io"
+            },
+            "when": "2023-02-01T13:28:55-08:00"
+        }
+    },
+    "old_sha": "d74b1ebfe520ac01b209dd9085f005884cc9f4cd",
+    "forced": false
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_branch_updated.json.golden b/scm/driver/harness/testdata/webhooks/pull_request_branch_updated.json.golden
new file mode 100644
index 000000000..0cc22af45
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_branch_updated.json.golden
@@ -0,0 +1,58 @@
+{
+  "Action": "updated",
+  "Repo": {
+    "ID": "13",
+    "Namespace": "",
+    "Name": "aba",
+    "Branch": "main",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 4,
+    "Title": "aw",
+    "Body": "",
+    "Sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "Ref": "refs/heads/b",
+    "Source": "b",
+    "Target": "main",
+    "Fork": "fork",
+    "Link": "http://localhost:3000/codeowners/asdsad/pulls/14",
+    "Closed": false,
+    "Merged": false,
+    "Author": {
+      "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Name": "Admin",
+      "Email": "admin@harness.io",
+      "Avatar": "",
+      "Created": "2023-02-02T18:21:25.38-08:00",
+      "Updated": "2023-02-02T18:21:25.38-08:00"
+    },
+    "Base": {
+      "Name": "main",
+      "Path": "refs/heads/main",
+      "Sha": "5473eebbc0ce1d08981c955161b07a7989566b7b"
+    },
+    "Head": {
+      "Name": "b",
+      "Path": "refs/heads/b",
+      "Sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef"
+    },
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+    "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+    "Name": "default",
+    "Email": "default@harness.io",
+    "Avatar": "",
+    "Created": "2023-02-02T18:21:25.38-08:00",
+    "Updated": "2023-02-02T18:21:25.38-08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_closed.json b/scm/driver/harness/testdata/webhooks/pull_request_closed.json
new file mode 100644
index 000000000..4039bc208
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_closed.json
@@ -0,0 +1,97 @@
+{
+  "trigger": "pullreq_closed",
+  "repo": {
+    "id": 22,
+    "path": "codeowners/asdsad",
+    "uid": "asdsad",
+    "default_branch": "main",
+    "git_url": "http://localhost:3000/git/codeowners/asdsad.git"
+  },
+  "principal": {
+    "id": 3,
+    "uid": "admin",
+    "display_name": "Administrator",
+    "email": "admin@gitness.io",
+    "type": "user",
+    "created": 1696332021613,
+    "updated": 1696332021613
+  },
+  "pull_req": {
+    "number": 6,
+    "state": "closed",
+    "is_draft": false,
+    "title": "Create sad",
+    "source_repo_id": 22,
+    "source_branch": "asdxsa",
+    "target_repo_id": 22,
+    "target_branch": "main",
+    "merge_base_sha": "5473eebbc0ce1d08981c955161b07a7989566b7b",
+    "author": {
+      "id": 3,
+      "uid": "admin",
+      "display_name": "Administrator",
+      "email": "admin@gitness.io",
+      "type": "user",
+      "created": 1696332021613,
+      "updated": 1696332021613
+    },
+    "pr_url": "http://localhost:3000/codeowners/asdsad/pulls/14"
+  },
+  "target_ref": {
+    "name": "refs/heads/main",
+    "repo": {
+      "id": 22,
+      "path": "codeowners/asdsad",
+      "uid": "asdsad",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/codeowners/asdsad.git"
+    }
+  },
+  "ref": {
+    "name": "refs/heads/asdxsa",
+    "repo": {
+      "id": 22,
+      "path": "codeowners/asdsad",
+      "uid": "asdsad",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/codeowners/asdsad.git"
+    }
+  },
+  "sha": "27822dd2ec788f924c97b0b194c5bfd906f2e574",
+  "commit": {
+    "sha": "27822dd2ec788f924c97b0b194c5bfd906f2e574",
+    "message": "",
+    "author": {
+      "identity": {
+        "name": "Administrator",
+        "email": "admin@gitness.io"
+      },
+      "when": "2023-12-20T13:10:47+05:30"
+    },
+    "committer": {
+      "identity": {
+        "name": "Gitness",
+        "email": "system@gitness.io"
+      },
+      "when": "2023-12-20T13:10:47+05:30"
+    }
+  },
+  "head_commit": {
+    "sha": "27822dd2ec788f924c97b0b194c5bfd906f2e574",
+    "message": "",
+    "author": {
+      "identity": {
+        "name": "Administrator",
+        "email": "admin@gitness.io"
+      },
+      "when": "2023-12-20T13:10:47+05:30"
+    },
+    "committer": {
+      "identity": {
+        "name": "Gitness",
+        "email": "system@gitness.io"
+      },
+      "when": "2023-12-20T13:10:47+05:30"
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_closed.json.golden b/scm/driver/harness/testdata/webhooks/pull_request_closed.json.golden
new file mode 100644
index 000000000..bfd493649
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_closed.json.golden
@@ -0,0 +1,58 @@
+{
+  "Action": "closed",
+  "Repo": {
+    "ID": "22",
+    "Namespace": "",
+    "Name": "asdsad",
+    "Branch": "main",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/codeowners/asdsad.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/codeowners/asdsad.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 6,
+    "Title": "Create sad",
+    "Body": "",
+    "Sha": "27822dd2ec788f924c97b0b194c5bfd906f2e574",
+    "Ref": "refs/heads/asdxsa",
+    "Source": "asdxsa",
+    "Target": "main",
+    "Fork": "fork",
+    "Link": "http://localhost:3000/codeowners/asdsad/pulls/14",
+    "Closed": true,
+    "Merged": false,
+    "Author": {
+      "ID": "admin",
+      "Login": "admin",
+      "Name": "Administrator",
+      "Email": "admin@gitness.io",
+      "Avatar": "",
+      "Created": "2023-10-03T16:50:21.613+05:30",
+      "Updated": "2023-10-03T16:50:21.613+05:30"
+    },
+    "Base": {
+        "Name": "main",
+        "Path": "refs/heads/main",
+        "Sha": "5473eebbc0ce1d08981c955161b07a7989566b7b"
+     },
+    "Head": {
+          "Name": "asdxsa",
+          "Path": "refs/heads/asdxsa",
+          "Sha": "27822dd2ec788f924c97b0b194c5bfd906f2e574"
+     },
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "admin",
+    "Login": "admin",
+    "Name": "Administrator",
+    "Email": "admin@gitness.io",
+    "Avatar": "",
+    "Created": "2023-10-03T16:50:21.613+05:30",
+    "Updated": "2023-10-03T16:50:21.613+05:30"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_comment_created.json b/scm/driver/harness/testdata/webhooks/pull_request_comment_created.json
new file mode 100644
index 000000000..cc69241e8
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_comment_created.json
@@ -0,0 +1,105 @@
+{
+  "trigger": "pullreq_comment_created",
+  "repo": {
+    "id": 18,
+    "path": "asd/demo",
+    "uid": "demo",
+    "default_branch": "main",
+    "git_url": "http://localhost:3000/git/asd/demo.git"
+  },
+  "principal": {
+    "id": 3,
+    "uid": "admin",
+    "display_name": "Administrator",
+    "email": "admin@gitness.io",
+    "type": "user",
+    "created": 1696332021613,
+    "updated": 1696332021613
+  },
+  "pull_req": {
+    "number": 2,
+    "state": "open",
+    "is_draft": false,
+    "title": "Update test.txt",
+    "source_repo_id": 18,
+    "source_branch": "pr2",
+    "target_repo_id": 18,
+    "target_branch": "main",
+    "merge_strategy": null,
+    "merge_base_sha": "5473eebbc0ce1d08981c955161b07a7989566b7b",
+    "author": {
+      "id": 8,
+      "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+      "display_name": "Admin",
+      "email": "admin@harness.io",
+      "type": "user",
+      "created": 1675390885380,
+      "updated": 1675390885380
+    },
+    "pr_url": "http://localhost:3000/codeowners/asdsad/pulls/14"
+  },
+  "target_ref": {
+    "name": "refs/heads/main",
+    "repo": {
+      "id": 18,
+      "path": "asd/demo",
+      "uid": "demo",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/asd/demo.git"
+    }
+  },
+  "ref": {
+    "name": "refs/heads/pr2",
+    "repo": {
+      "id": 18,
+      "path": "asd/demo",
+      "uid": "demo",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/asd/demo.git"
+    }
+  },
+  "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+  "commit": {
+    "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "message": "updated b2",
+    "author": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    }
+  },
+  "head_commit": {
+    "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "message": "updated b2",
+    "author": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    }
+  },
+  "comment": {
+    "id": 1,
+    "text": "pr comment",
+    "created": 1745471063621,
+    "updated": 1745471063621,
+    "kind": "comment"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_comment_created.json.golden b/scm/driver/harness/testdata/webhooks/pull_request_comment_created.json.golden
new file mode 100644
index 000000000..a10e6efb6
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_comment_created.json.golden
@@ -0,0 +1,61 @@
+{
+  "Repo": {
+    "ID": "18",
+    "Namespace": "",
+    "Name": "demo",
+    "Branch": "main",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/asd/demo.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/asd/demo.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 2,
+    "Title": "Update test.txt",
+    "Body": "",
+    "Sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "Ref": "refs/heads/pr2",
+    "Source": "pr2",
+    "Target": "main",
+    "Fork": "fork",
+    "Link": "http://localhost:3000/codeowners/asdsad/pulls/14",
+    "Closed": false,
+    "Merged": false,
+    "Author": {
+      "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Name": "Admin",
+      "Email": "admin@harness.io",
+      "Avatar": "",
+      "Created": "2023-02-02T18:21:25.38-08:00",
+      "Updated": "2023-02-02T18:21:25.38-08:00"
+    },
+    "Base": {
+        "Name": "main",
+        "Path": "refs/heads/main",
+        "Sha": "5473eebbc0ce1d08981c955161b07a7989566b7b"
+      },
+    "Head": {
+          "Name": "pr2",
+          "Path": "refs/heads/pr2",
+          "Sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef"
+    },
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Comment": {
+    "ID": 1,
+    "Body": "pr comment"
+  },
+  "Sender": {
+    "ID": "admin",
+    "Login": "admin",
+    "Name": "Administrator",
+    "Email": "admin@gitness.io",
+    "Avatar": "",
+    "Created": "2023-10-03T04:20:21.613-07:00",
+    "Updated": "2023-10-03T04:20:21.613-07:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_merged.json b/scm/driver/harness/testdata/webhooks/pull_request_merged.json
new file mode 100644
index 000000000..dac50c97f
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_merged.json
@@ -0,0 +1,98 @@
+{
+  "trigger": "pullreq_merged",
+  "repo": {
+    "id": 22,
+    "path": "codeowners/asdsad",
+    "uid": "asdsad",
+    "default_branch": "main",
+    "git_url": "http://localhost:3000/git/codeowners/asdsad.git"
+  },
+  "principal": {
+    "id": 3,
+    "uid": "admin",
+    "display_name": "Administrator",
+    "email": "admin@gitness.io",
+    "type": "user",
+    "created": 1696332021613,
+    "updated": 1696332021613
+  },
+  "pull_req": {
+    "number": 10,
+    "state": "merged",
+    "is_draft": false,
+    "title": "Create xxasc",
+    "source_repo_id": 22,
+    "source_branch": "xas",
+    "target_repo_id": 22,
+    "target_branch": "main",
+    "merge_strategy": "squash",
+    "merge_base_sha": "5473eebbc0ce1d08981c955161b07a7989566b7b",
+    "author": {
+      "id": 3,
+      "uid": "admin",
+      "display_name": "Administrator",
+      "email": "admin@gitness.io",
+      "type": "user",
+      "created": 1696332021613,
+      "updated": 1696332021613
+    },
+    "pr_url": "http://localhost:3000/codeowners/asdsad/pulls/14"
+  },
+  "target_ref": {
+    "name": "refs/heads/main",
+    "repo": {
+      "id": 22,
+      "path": "codeowners/asdsad",
+      "uid": "asdsad",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/codeowners/asdsad.git"
+    }
+  },
+  "ref": {
+    "name": "refs/heads/xas",
+    "repo": {
+      "id": 22,
+      "path": "codeowners/asdsad",
+      "uid": "asdsad",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/codeowners/asdsad.git"
+    }
+  },
+  "sha": "4ec41187008f77222a60dfa21cdbd980f6490443",
+  "commit": {
+    "sha": "4ec41187008f77222a60dfa21cdbd980f6490443",
+    "message": "",
+    "author": {
+      "identity": {
+        "name": "Administrator",
+        "email": "admin@gitness.io"
+      },
+      "when": "2023-12-20T13:40:52+05:30"
+    },
+    "committer": {
+      "identity": {
+        "name": "Gitness",
+        "email": "system@gitness.io"
+      },
+      "when": "2023-12-20T13:40:52+05:30"
+    }
+  },
+  "head_commit": {
+    "sha": "4ec41187008f77222a60dfa21cdbd980f6490443",
+    "message": "",
+    "author": {
+      "identity": {
+        "name": "Administrator",
+        "email": "admin@gitness.io"
+      },
+      "when": "2023-12-20T13:40:52+05:30"
+    },
+    "committer": {
+      "identity": {
+        "name": "Gitness",
+        "email": "system@gitness.io"
+      },
+      "when": "2023-12-20T13:40:52+05:30"
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_merged.json.golden b/scm/driver/harness/testdata/webhooks/pull_request_merged.json.golden
new file mode 100644
index 000000000..7237cea0d
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_merged.json.golden
@@ -0,0 +1,58 @@
+{
+  "Action": "merged",
+  "Repo": {
+    "ID": "22",
+    "Namespace": "",
+    "Name": "asdsad",
+    "Branch": "main",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/codeowners/asdsad.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/codeowners/asdsad.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 10,
+    "Title": "Create xxasc",
+    "Body": "",
+    "Sha": "4ec41187008f77222a60dfa21cdbd980f6490443",
+    "Ref": "refs/heads/xas",
+    "Source": "xas",
+    "Target": "main",
+    "Fork": "fork",
+    "Link": "http://localhost:3000/codeowners/asdsad/pulls/14",
+    "Closed": true,
+    "Merged": true,
+    "Author": {
+      "ID": "admin",
+      "Login": "admin",
+      "Name": "Administrator",
+      "Email": "admin@gitness.io",
+      "Avatar": "",
+      "Created": "2023-10-03T16:50:21.613+05:30",
+      "Updated": "2023-10-03T16:50:21.613+05:30"
+    },
+    "Base": {
+        "Name": "main",
+        "Path": "refs/heads/main",
+        "Sha": "5473eebbc0ce1d08981c955161b07a7989566b7b"
+      },
+    "Head": {
+          "Name": "xas",
+          "Path": "refs/heads/xas",
+          "Sha": "4ec41187008f77222a60dfa21cdbd980f6490443"
+    },
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "admin",
+    "Login": "admin",
+    "Name": "Administrator",
+    "Email": "admin@gitness.io",
+    "Avatar": "",
+    "Created": "2023-10-03T16:50:21.613+05:30",
+    "Updated": "2023-10-03T16:50:21.613+05:30"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_opened.json b/scm/driver/harness/testdata/webhooks/pull_request_opened.json
new file mode 100644
index 000000000..bfafcef5a
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_opened.json
@@ -0,0 +1,98 @@
+{
+  "trigger": "pullreq_created",
+  "repo": {
+      "id": 13,
+      "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+      "uid": "aba",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+  },
+  "principal": {
+      "id": 8,
+      "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+      "display_name": "default",
+      "email": "default@harness.io",
+      "type": "user",
+      "created": 1675390885380,
+      "updated": 1675390885380
+  },
+  "pull_req": {
+      "number": 4,
+      "state": "open",
+      "is_draft": false,
+      "title": "aw",
+      "source_repo_id": 13,
+      "source_branch": "b",
+      "target_repo_id": 13,
+      "target_branch": "main",
+      "merge_strategy": null,
+      "merge_base_sha": "5473eebbc0ce1d08981c955161b07a7989566b7b",
+      "author": {
+        "id": 8,
+        "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+        "display_name": "Admin",
+        "email": "admin@harness.io",
+        "type": "user",
+        "created": 1675390885380,
+        "updated": 1675390885380
+      },
+    "pr_url": "http://localhost:3000/codeowners/asdsad/pulls/14"
+  },
+  "target_ref": {
+      "name": "refs/heads/main",
+      "repo": {
+          "id": 13,
+          "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+          "uid": "aba",
+          "default_branch": "main",
+          "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+      }
+  },
+  "ref": {
+      "name": "refs/heads/b",
+      "repo": {
+          "id": 13,
+          "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+          "uid": "aba",
+          "default_branch": "main",
+          "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+      }
+  },
+  "sha": "d74b1ebfe520ac01b209dd9085f005884cc9f4cd",
+  "commit": {
+      "sha": "d74b1ebfe520ac01b209dd9085f005884cc9f4cd",
+      "message": "Update b.txt",
+      "author": {
+          "identity": {
+              "name": "Admin",
+              "email": "admin@harness.io"
+          },
+          "when": "2023-01-31T22:01:55-08:00"
+      },
+      "committer": {
+          "identity": {
+              "name": "Admin",
+              "email": "admin@harness.io"
+          },
+          "when": "2023-01-31T22:01:55-08:00"
+      }
+  },
+  "head_commit": {
+    "sha": "d74b1ebfe520ac01b209dd9085f005884cc9f4cd",
+    "message": "Update b.txt",
+    "author": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-01-31T22:01:55-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-01-31T22:01:55-08:00"
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_opened.json.golden b/scm/driver/harness/testdata/webhooks/pull_request_opened.json.golden
new file mode 100644
index 000000000..a1fe53904
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_opened.json.golden
@@ -0,0 +1,58 @@
+{
+  "Action": "created",
+  "Repo": {
+    "ID": "13",
+    "Namespace": "",
+    "Name": "aba",
+    "Branch": "main",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 4,
+    "Title": "aw",
+    "Body": "",
+    "Sha": "d74b1ebfe520ac01b209dd9085f005884cc9f4cd",
+    "Ref": "refs/heads/b",
+    "Source": "b",
+    "Target": "main",
+    "Fork": "fork",
+    "Link": "http://localhost:3000/codeowners/asdsad/pulls/14",
+    "Closed": false,
+    "Merged": false,
+    "Author": {
+      "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Name": "Admin",
+      "Email": "admin@harness.io",
+      "Avatar": "",
+      "Created": "2023-02-02T18:21:25.38-08:00",
+      "Updated": "2023-02-02T18:21:25.38-08:00"
+    },
+    "Base": {
+        "Name": "main",
+        "Path": "refs/heads/main",
+        "Sha": "5473eebbc0ce1d08981c955161b07a7989566b7b"
+      },
+    "Head": {
+          "Name": "b",
+          "Path": "refs/heads/b",
+          "Sha": "d74b1ebfe520ac01b209dd9085f005884cc9f4cd"
+    },
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+    "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+    "Name": "default",
+    "Email": "default@harness.io",
+    "Avatar": "",
+    "Created": "2023-02-02T18:21:25.38-08:00",
+    "Updated": "2023-02-02T18:21:25.38-08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_reopened.json b/scm/driver/harness/testdata/webhooks/pull_request_reopened.json
new file mode 100644
index 000000000..3915d0b45
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_reopened.json
@@ -0,0 +1,98 @@
+{
+  "trigger": "pullreq_reopened",
+  "repo": {
+    "id": 13,
+    "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+    "uid": "aba",
+    "default_branch": "main",
+    "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+  },
+  "principal": {
+    "id": 8,
+    "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+    "display_name": "default",
+    "email": "default@harness.io",
+    "type": "user",
+    "created": 1675390885380,
+    "updated": 1675390885380
+  },
+  "pull_req": {
+    "number": 4,
+    "state": "open",
+    "is_draft": false,
+    "title": "aw",
+    "source_repo_id": 13,
+    "source_branch": "b",
+    "target_repo_id": 13,
+    "target_branch": "main",
+    "merge_strategy": null,
+    "merge_base_sha": "5473eebbc0ce1d08981c955161b07a7989566b7b",
+    "author": {
+      "id": 8,
+      "uid": "0osgWsTZRsSZ8RWfjLRkEg",
+      "display_name": "Admin",
+      "email": "admin@harness.io",
+      "type": "user",
+      "created": 1675390885380,
+      "updated": 1675390885380
+    },
+    "pr_url": "http://localhost:3000/codeowners/asdsad/pulls/14"
+  },
+  "target_ref": {
+    "name": "refs/heads/main",
+    "repo": {
+      "id": 13,
+      "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+      "uid": "aba",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+    }
+  },
+  "ref": {
+    "name": "refs/heads/b",
+    "repo": {
+      "id": 13,
+      "path": "kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba",
+      "uid": "aba",
+      "default_branch": "main",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git"
+    }
+  },
+  "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+  "commit": {
+    "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "message": "updated b2",
+    "author": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    }
+  },
+  "head_commit": {
+    "sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "message": "updated b2",
+    "author": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2023-02-01T13:28:55-08:00"
+    }
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/pull_request_reopened.json.golden b/scm/driver/harness/testdata/webhooks/pull_request_reopened.json.golden
new file mode 100644
index 000000000..3c00c475c
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/pull_request_reopened.json.golden
@@ -0,0 +1,58 @@
+{
+  "Action": "reopened",
+  "Repo": {
+    "ID": "13",
+    "Namespace": "",
+    "Name": "aba",
+    "Branch": "main",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/myOrg/myProject/aba.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "PullRequest": {
+    "Number": 4,
+    "Title": "aw",
+    "Body": "",
+    "Sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef",
+    "Ref": "refs/heads/b",
+    "Source": "b",
+    "Target": "main",
+    "Fork": "fork",
+    "Link": "http://localhost:3000/codeowners/asdsad/pulls/14",
+    "Closed": false,
+    "Merged": false,
+    "Author": {
+      "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+      "Name": "Admin",
+      "Email": "admin@harness.io",
+      "Avatar": "",
+      "Created": "2023-02-02T18:21:25.38-08:00",
+      "Updated": "2023-02-02T18:21:25.38-08:00"
+    },
+    "Base": {
+        "Name": "main",
+        "Path": "refs/heads/main",
+        "Sha": "5473eebbc0ce1d08981c955161b07a7989566b7b"
+      },
+    "Head": {
+          "Name": "b",
+          "Path": "refs/heads/b",
+          "Sha": "f74f3d2a88d1b7cb19ff3bf069aa423763d341ef"
+    },
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "0osgWsTZRsSZ8RWfjLRkEg",
+    "Login": "0osgWsTZRsSZ8RWfjLRkEg",
+    "Name": "default",
+    "Email": "default@harness.io",
+    "Avatar": "",
+    "Created": "2023-02-02T18:21:25.38-08:00",
+    "Updated": "2023-02-02T18:21:25.38-08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/tag_create.json b/scm/driver/harness/testdata/webhooks/tag_create.json
new file mode 100644
index 000000000..f67e9700b
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/tag_create.json
@@ -0,0 +1,76 @@
+{
+  "trigger": "tag_created",
+  "repo": {
+    "id": 16,
+    "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+    "identifier": "harness-core",
+    "default_branch": "develop",
+    "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "uid": "harness-core"
+  },
+  "principal": {
+    "id": 2,
+    "uid": "lv0euRhKRCyiXWzS7pOg6g",
+    "display_name": "Admin",
+    "email": "admin@harness.io",
+    "type": "user",
+    "created": 1701091219051,
+    "updated": 1701091219051
+  },
+  "ref": {
+    "name": "refs/tags/asd",
+    "repo": {
+      "id": 16,
+      "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+      "identifier": "harness-core",
+      "default_branch": "develop",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+      "uid": "harness-core"
+    }
+  },
+  "sha": "3e4da2d65c3631c3e84b52fabe714c978aff540b",
+  "head_commit": {
+    "sha": "0f1835abe08473e07863540712d8389984b72dad",
+    "message": "",
+    "author": {
+      "identity": {
+        "name": "admin",
+        "email": "admin@harness.io"
+      },
+      "when": "2024-03-01T07:54:35-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "GitHub",
+        "email": "noreply@github.com"
+      },
+      "when": "2024-03-01T07:54:35-08:00"
+    },
+    "added": [],
+    "removed": [],
+    "modified": []
+  },
+  "commit": {
+    "sha": "0f1835abe08473e07863540712d8389984b72dad",
+    "message": "",
+    "author": {
+      "identity": {
+        "name": "Jenny James",
+        "email": "jenny.james@harness.io"
+      },
+      "when": "2024-03-01T07:54:35-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "GitHub",
+        "email": "noreply@github.com"
+      },
+      "when": "2024-03-01T07:54:35-08:00"
+    },
+    "added": [],
+    "removed": [],
+    "modified": []
+  },
+  "old_sha": "0000000000000000000000000000000000000000",
+  "forced": false
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/tag_create.json.golden b/scm/driver/harness/testdata/webhooks/tag_create.json.golden
new file mode 100644
index 000000000..f6a8a53e4
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/tag_create.json.golden
@@ -0,0 +1,29 @@
+{
+    "Ref": {
+       "Name": "refs/tags/asd",
+       "Sha": "3e4da2d65c3631c3e84b52fabe714c978aff540b"
+    },
+    "Action": "created",
+    "Repo": {
+        "ID": "16",
+        "Namespace": "",
+        "Name": "harness-core",
+        "Perm": null,
+        "Branch": "develop",
+        "Private": false,
+        "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+        "CloneSSH": "",
+        "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Sender": {
+        "ID": "lv0euRhKRCyiXWzS7pOg6g",
+        "Login": "lv0euRhKRCyiXWzS7pOg6g",
+        "Name": "Admin",
+        "Email": "admin@harness.io",
+        "Avatar": "",
+        "Created": "2023-11-27T05:20:19.051-08:00",
+        "Updated": "2023-11-27T05:20:19.051-08:00"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/tag_delete.json b/scm/driver/harness/testdata/webhooks/tag_delete.json
new file mode 100644
index 000000000..3fa9348ee
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/tag_delete.json
@@ -0,0 +1,34 @@
+{
+  "trigger": "tag_deleted",
+  "repo": {
+    "id": 16,
+    "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+    "identifier": "harness-core",
+    "default_branch": "develop",
+    "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "uid": "harness-core"
+  },
+  "principal": {
+    "id": 2,
+    "uid": "lv0euRhKRCyiXWzS7pOg6g",
+    "display_name": "Admin",
+    "email": "admin@harness.io",
+    "type": "user",
+    "created": 1701091219051,
+    "updated": 1701091219051
+  },
+  "ref": {
+    "name": "refs/tags/asd",
+    "repo": {
+      "id": 16,
+      "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+      "identifier": "harness-core",
+      "default_branch": "develop",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+      "uid": "harness-core"
+    }
+  },
+  "sha": "0000000000000000000000000000000000000000",
+  "old_sha": "3e4da2d65c3631c3e84b52fabe714c978aff540b",
+  "forced": false
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/tag_delete.json.golden b/scm/driver/harness/testdata/webhooks/tag_delete.json.golden
new file mode 100644
index 000000000..cfd67a8b1
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/tag_delete.json.golden
@@ -0,0 +1,29 @@
+{
+  "Ref": {
+    "Name": "refs/tags/asd",
+    "Sha": "0000000000000000000000000000000000000000"
+  },
+  "Action": "deleted",
+  "Repo": {
+    "ID": "16",
+    "Namespace": "",
+    "Name": "harness-core",
+    "Perm": null,
+    "Branch": "develop",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Sender": {
+    "ID": "lv0euRhKRCyiXWzS7pOg6g",
+    "Login": "lv0euRhKRCyiXWzS7pOg6g",
+    "Name": "Admin",
+    "Email": "admin@harness.io",
+    "Avatar": "",
+    "Created": "2023-11-27T05:20:19.051-08:00",
+    "Updated": "2023-11-27T05:20:19.051-08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/tag_update.json b/scm/driver/harness/testdata/webhooks/tag_update.json
new file mode 100644
index 000000000..b16eac070
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/tag_update.json
@@ -0,0 +1,100 @@
+{
+  "trigger": "tag_updated",
+  "repo": {
+    "id": 16,
+    "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+    "identifier": "harness-core",
+    "default_branch": "develop",
+    "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "uid": "harness-core"
+  },
+  "principal": {
+    "id": 2,
+    "uid": "lv0euRhKRCyiXWzS7pOg6g",
+    "display_name": "Admin",
+    "email": "admin@harness.io",
+    "type": "user",
+    "created": 1701091219051,
+    "updated": 1701091219051
+  },
+  "ref": {
+    "name": "refs/tags/ddxas",
+    "repo": {
+      "id": 16,
+      "path": "kmpySmUISimoRrJL6NL73w/harness-core",
+      "identifier": "harness-core",
+      "default_branch": "develop",
+      "git_url": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+      "uid": "harness-core"
+    }
+  },
+  "sha": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+  "head_commit": {
+    "sha": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+    "message": "Asd",
+    "author": {
+      "identity": {
+        "name": "Abhinav Singh",
+        "email": "abhinav.singh@harness.io"
+      },
+      "when": "2024-03-07T03:18:51-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Abhinav Singh",
+        "email": "abhinav.singh@harness.io"
+      },
+      "when": "2024-03-07T03:18:51-08:00"
+    },
+    "added": [],
+    "removed": [],
+    "modified": []
+  },
+  "commits": [
+    {
+      "sha": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+      "message": "Asd",
+      "author": {
+        "identity": {
+          "name": "Abhinav Singh",
+          "email": "abhinav.singh@harness.io"
+        },
+        "when": "2024-03-07T03:18:51-08:00"
+      },
+      "committer": {
+        "identity": {
+          "name": "Abhinav Singh",
+          "email": "abhinav.singh@harness.io"
+        },
+        "when": "2024-03-07T03:18:51-08:00"
+      },
+      "added": [],
+      "removed": [],
+      "modified": []
+    }
+  ],
+  "total_commits_count": 1,
+  "commit": {
+    "sha": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+    "message": "Asd",
+    "author": {
+      "identity": {
+        "name": "Abhinav Singh",
+        "email": "abhinav.singh@harness.io"
+      },
+      "when": "2024-03-07T03:18:51-08:00"
+    },
+    "committer": {
+      "identity": {
+        "name": "Abhinav Singh",
+        "email": "abhinav.singh@harness.io"
+      },
+      "when": "2024-03-07T03:18:51-08:00"
+    },
+    "added": [],
+    "removed": [],
+    "modified": []
+  },
+  "old_sha": "0f1835abe08473e07863540712d8389984b72dad",
+  "forced": true
+}
\ No newline at end of file
diff --git a/scm/driver/harness/testdata/webhooks/tag_update.json.golden b/scm/driver/harness/testdata/webhooks/tag_update.json.golden
new file mode 100644
index 000000000..408447db1
--- /dev/null
+++ b/scm/driver/harness/testdata/webhooks/tag_update.json.golden
@@ -0,0 +1,67 @@
+{
+  "Ref": "refs/tags/ddxas",
+  "Before": "0f1835abe08473e07863540712d8389984b72dad",
+  "After": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+  "Repo": {
+    "ID": "16",
+    "Namespace": "",
+    "Name": "harness-core",
+    "Perm": null,
+    "Branch": "develop",
+    "Private": false,
+    "Clone": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "CloneSSH": "",
+    "Link": "http://localhost:3000/git/kmpySmUISimoRrJL6NL73w/harness-core.git",
+    "Created": "0001-01-01T00:00:00Z",
+    "Updated": "0001-01-01T00:00:00Z"
+  },
+  "Commit": {
+    "Sha": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+    "Message": "Asd",
+    "Author": {
+      "Name": "Abhinav Singh",
+      "Email": "abhinav.singh@harness.io",
+      "Date": "0001-01-01T00:00:00Z",
+      "Login": "",
+      "Avatar": ""
+    },
+    "Committer": {
+      "Name": "Abhinav Singh",
+      "Email": "abhinav.singh@harness.io",
+      "Date": "0001-01-01T00:00:00Z",
+      "Login": "",
+      "Avatar": ""
+    },
+    "Link": ""
+  },
+  "Commits": [
+    {
+      "Sha": "700b3dab8e7a5cebf5e1ce54e7dd5bde60099912",
+      "Message": "Asd",
+      "Author": {
+        "Name": "Abhinav Singh",
+        "Email": "abhinav.singh@harness.io",
+        "Date": "0001-01-01T00:00:00Z",
+        "Login": "",
+        "Avatar": ""
+      },
+      "Committer": {
+        "Name": "Abhinav Singh",
+        "Email": "abhinav.singh@harness.io",
+        "Date": "0001-01-01T00:00:00Z",
+        "Login": "",
+        "Avatar": ""
+      },
+      "Link": ""
+    }
+  ],
+  "Sender": {
+    "ID": "lv0euRhKRCyiXWzS7pOg6g",
+    "Login": "lv0euRhKRCyiXWzS7pOg6g",
+    "Name": "Admin",
+    "Email": "admin@harness.io",
+    "Avatar": "",
+    "Created": "2023-11-27T05:20:19.051-08:00",
+    "Updated": "2023-11-27T05:20:19.051-08:00"
+  }
+}
\ No newline at end of file
diff --git a/scm/driver/harness/user.go b/scm/driver/harness/user.go
new file mode 100644
index 000000000..500cad249
--- /dev/null
+++ b/scm/driver/harness/user.go
@@ -0,0 +1,100 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type userService struct {
+	client *wrapper
+}
+
+func (s *userService) Find(ctx context.Context) (*scm.User, *scm.Response, error) {
+	out := new(harnessUser)
+	// the following is for the corporate version of Harness code
+	tempUserService := *s
+	// get the basepath
+	basePath := tempUserService.client.BaseURL.Path
+	// use the NG user endpoint
+	basePath = strings.Replace(basePath, "code", "ng", 1)
+	// set the new basepath
+	tempUserService.client.BaseURL.Path = basePath
+	// set the path
+	path := fmt.Sprintf("api/user/currentUser")
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	return convertHarnessUser(out), res, err
+}
+
+func (s *userService) FindLogin(ctx context.Context, login string) (*scm.User, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, error) {
+	return "", nil, scm.ErrNotSupported
+}
+
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+//
+// native data structures
+//
+
+type harnessUser struct {
+	Status string `json:"status"`
+	Data   struct {
+		UUID             string      `json:"uuid"`
+		Name             string      `json:"name"`
+		Email            string      `json:"email"`
+		Token            interface{} `json:"token"`
+		Defaultaccountid string      `json:"defaultAccountId"`
+		Intent           interface{} `json:"intent"`
+		Accounts         []struct {
+			UUID              string `json:"uuid"`
+			Accountname       string `json:"accountName"`
+			Companyname       string `json:"companyName"`
+			Defaultexperience string `json:"defaultExperience"`
+			Createdfromng     bool   `json:"createdFromNG"`
+			Nextgenenabled    bool   `json:"nextGenEnabled"`
+		} `json:"accounts"`
+		Admin                          bool        `json:"admin"`
+		Twofactorauthenticationenabled bool        `json:"twoFactorAuthenticationEnabled"`
+		Emailverified                  bool        `json:"emailVerified"`
+		Locked                         bool        `json:"locked"`
+		Disabled                       bool        `json:"disabled"`
+		Signupaction                   interface{} `json:"signupAction"`
+		Edition                        interface{} `json:"edition"`
+		Billingfrequency               interface{} `json:"billingFrequency"`
+		Utminfo                        struct {
+			Utmsource   interface{} `json:"utmSource"`
+			Utmcontent  interface{} `json:"utmContent"`
+			Utmmedium   interface{} `json:"utmMedium"`
+			Utmterm     interface{} `json:"utmTerm"`
+			Utmcampaign interface{} `json:"utmCampaign"`
+		} `json:"utmInfo"`
+		Externallymanaged bool `json:"externallyManaged"`
+	} `json:"data"`
+	Metadata      interface{} `json:"metaData"`
+	Correlationid string      `json:"correlationId"`
+}
+
+//
+// native data structure conversion
+//
+
+func convertHarnessUser(src *harnessUser) *scm.User {
+	return &scm.User{
+		Login: src.Data.Email,
+		Email: src.Data.Email,
+		Name:  src.Data.Name,
+		ID:    src.Data.UUID,
+	}
+}
diff --git a/scm/driver/harness/user_test.go b/scm/driver/harness/user_test.go
new file mode 100644
index 000000000..886494c13
--- /dev/null
+++ b/scm/driver/harness/user_test.go
@@ -0,0 +1,59 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/transport"
+	"github.com/google/go-cmp/cmp"
+	"github.com/h2non/gock"
+)
+
+func TestUsersFind(t *testing.T) {
+	if harnessPAT == "" {
+		defer gock.Off()
+
+		harnessUserOrigin := strings.Replace(gockOrigin, "code", "ng", 1)
+
+		gock.New(harnessUserOrigin).
+			Get("/gateway/ng/api/user/currentUser").
+			Reply(200).
+			Type("application/json").
+			File("testdata/user.json")
+	}
+	client, _ := New(gockOrigin, harnessOrg, harnessAccount, harnessProject)
+	client.Client = &http.Client{
+		Transport: &transport.Custom{
+			Before: func(r *http.Request) {
+				r.Header.Set("x-api-key", harnessPAT)
+			},
+		},
+	}
+	got, _, err := client.Users.Find(context.Background())
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	want := new(scm.User)
+	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
+	wantErr := json.Unmarshal(raw, &want)
+	if wantErr != nil {
+		t.Error(wantErr)
+		return
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
diff --git a/scm/driver/harness/util.go b/scm/driver/harness/util.go
new file mode 100644
index 000000000..4be210068
--- /dev/null
+++ b/scm/driver/harness/util.go
@@ -0,0 +1,174 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"fmt"
+	"net/url"
+	"strconv"
+	"strings"
+
+	"github.com/drone/go-scm/scm"
+)
+
+const (
+	accountIdentifier = "accountIdentifier"
+	projectIdentifier = "projectIdentifier"
+	orgIdentifier     = "orgIdentifier"
+	routingId         = "routingId"
+)
+
+func buildHarnessURI(account, organization, project, repo string) (uri string) {
+	if account != "" {
+		if project != "" {
+			uri = fmt.Sprintf("%s/%s/%s/", account, organization, project)
+		} else if organization != "" {
+			uri = fmt.Sprintf("%s/%s/", account, organization)
+		} else {
+			uri = fmt.Sprintf("%s/", account)
+		}
+		if repo != "" {
+			uri += fmt.Sprintf("%s/+", repo)
+		} else {
+			uri += "+"
+		}
+		return uri
+	}
+	return repo
+}
+
+func getRepoAndQueryParams(slug string) (string, string, error) {
+	params := url.Values{}
+	s := strings.TrimSuffix(slug, "/+")
+	splitSlug := strings.Split(s, "/")
+	if len(splitSlug) == 0 || len(splitSlug) == 1 {
+		return "", "", fmt.Errorf("split length: %d is small for slug %s", len(splitSlug), slug)
+	}
+
+	params.Set(accountIdentifier, splitSlug[0])
+	params.Set(routingId, splitSlug[0])
+	var repoId string
+	switch len(splitSlug) {
+	case 2:
+		repoId = splitSlug[1]
+	case 3:
+		params.Set(orgIdentifier, splitSlug[1])
+		repoId = splitSlug[2]
+	case 4:
+		params.Set(orgIdentifier, splitSlug[1])
+		params.Set(projectIdentifier, splitSlug[2])
+		repoId = splitSlug[3]
+	default:
+		return "", "", fmt.Errorf("split length more than %d encountered for slug %s", len(splitSlug), slug)
+
+	}
+	return repoId, params.Encode(), nil
+}
+
+func encodeListOptions(opts scm.ListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("limit", strconv.Itoa(opts.Size))
+	}
+	return params.Encode()
+}
+
+func encodeIssueListOptions(opts scm.IssueListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("limit", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
+
+func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("limit", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
+
+// convertAPIURLToHTMLURL converts an release API endpoint into a html endpoint
+func convertAPIURLToHTMLURL(apiURL string, tagName string) string {
+	// "url": "https://try.gitea.com/api/v1/repos/octocat/Hello-World/123",
+	// "html_url": "https://try.gitea.com/octocat/Hello-World/releases/tag/v1.0.0",
+	// the url field is the API url, not the html url, so until go-sdk v0.13.3, build it ourselves
+	link, err := url.Parse(apiURL)
+	if err != nil {
+		return ""
+	}
+
+	pathParts := strings.Split(link.Path, "/")
+	if len(pathParts) != 7 {
+		return ""
+	}
+	link.Path = fmt.Sprintf("/%s/%s/releases/tag/%s", pathParts[4], pathParts[5], tagName)
+	return link.String()
+}
+
+func encodeMilestoneListOptions(opts scm.MilestoneListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("per_page", strconv.Itoa(opts.Size))
+	}
+	if opts.Open && opts.Closed {
+		params.Set("state", "all")
+	} else if opts.Closed {
+		params.Set("state", "closed")
+	}
+	return params.Encode()
+}
+
+type ListOptions struct {
+	Page     int
+	PageSize int
+}
+
+func encodeReleaseListOptions(o ListOptions) string {
+	query := make(url.Values)
+	query.Add("page", fmt.Sprintf("%d", o.Page))
+	query.Add("limit", fmt.Sprintf("%d", o.PageSize))
+	return query.Encode()
+}
+
+func encodeCommitListOptions(opts scm.CommitListOptions) string {
+	params := url.Values{}
+	if opts.Page != 0 {
+		params.Set("page", strconv.Itoa(opts.Page))
+	}
+	if opts.Size != 0 {
+		params.Set("limit", strconv.Itoa(opts.Size))
+	}
+	if opts.Ref != "" {
+		params.Set("git_ref", opts.Ref)
+	}
+	if opts.Path != "" {
+		params.Set("path", opts.Path)
+	}
+	return params.Encode()
+}
diff --git a/scm/driver/harness/util_test.go b/scm/driver/harness/util_test.go
new file mode 100644
index 000000000..2264536e0
--- /dev/null
+++ b/scm/driver/harness/util_test.go
@@ -0,0 +1,81 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+)
+
+func Test_encodeListOptions(t *testing.T) {
+	opts := scm.ListOptions{
+		Page: 10,
+		Size: 30,
+	}
+	want := "limit=30&page=10"
+	got := encodeListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodeIssueListOptions(t *testing.T) {
+	opts := scm.IssueListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   true,
+		Closed: true,
+	}
+	want := "limit=30&page=10&state=all"
+	got := encodeIssueListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded issue list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodeIssueListOptions_Closed(t *testing.T) {
+	opts := scm.IssueListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   false,
+		Closed: true,
+	}
+	want := "limit=30&page=10&state=closed"
+	got := encodeIssueListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded issue list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodePullRequestListOptions(t *testing.T) {
+	t.Parallel()
+	opts := scm.PullRequestListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   true,
+		Closed: true,
+	}
+	want := "limit=30&page=10&state=all"
+	got := encodePullRequestListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded pr list options %q, got %q", want, got)
+	}
+}
+
+func Test_encodePullRequestListOptions_Closed(t *testing.T) {
+	t.Parallel()
+	opts := scm.PullRequestListOptions{
+		Page:   10,
+		Size:   30,
+		Open:   false,
+		Closed: true,
+	}
+	want := "limit=30&page=10&state=closed"
+	got := encodePullRequestListOptions(opts)
+	if got != want {
+		t.Errorf("Want encoded pr list options %q, got %q", want, got)
+	}
+}
diff --git a/scm/driver/harness/webhook.go b/scm/driver/harness/webhook.go
new file mode 100644
index 000000000..cb79b71fa
--- /dev/null
+++ b/scm/driver/harness/webhook.go
@@ -0,0 +1,404 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"crypto/sha256"
+	"encoding/json"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/internal/hmac"
+)
+
+type webhookService struct {
+	client *wrapper
+}
+
+func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhook, error) {
+	data, err := ioutil.ReadAll(
+		io.LimitReader(req.Body, 10000000),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	var hook scm.Webhook
+	switch req.Header.Get("X-Harness-Trigger") {
+	// case "create":
+	// 	hook, err = s.parseCreateHook(data)
+	// case "delete":
+	// 	hook, err = s.parseDeleteHook(data)
+	// case "issues":
+	// 	hook, err = s.parseIssueHook(data)
+	case "branch_updated", "tag_updated":
+		hook, err = s.parsePushHook(data)
+	case "branch_created", "branch_deleted":
+		hook, err = s.parseBranchHook(data)
+	case "tag_created", "tag_deleted":
+		hook, err = s.parseTagHook(data)
+	case "pullreq_created", "pullreq_reopened", "pullreq_branch_updated", "pullreq_closed", "pullreq_merged":
+		hook, err = s.parsePullRequestHook(data)
+	case "pullreq_comment_created":
+		hook, err = s.parsePullRequestCommentHook(data)
+	default:
+		return nil, scm.ErrUnknownEvent
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	// get the gitea signature key to verify the payload
+	// signature. If no key is provided, no validation
+	// is performed.
+	key, err := fn(hook)
+	if err != nil {
+		return hook, err
+	} else if key == "" {
+		return hook, nil
+	}
+
+	secret := req.FormValue("secret")
+	signature := req.Header.Get("X-Harness-Signature")
+
+	// fail if no signature passed
+	if signature == "" && secret == "" {
+		return hook, scm.ErrSignatureInvalid
+	}
+
+	// test signature if header not set and secret is in payload
+	if signature == "" && secret != "" && secret != key {
+		return hook, scm.ErrSignatureInvalid
+	}
+
+	// test signature using header
+	if signature != "" && !hmac.Validate(sha256.New, data, []byte(key), signature) {
+		return hook, scm.ErrSignatureInvalid
+	}
+
+	return hook, nil
+}
+
+func (s *webhookService) parsePullRequestHook(data []byte) (scm.Webhook, error) {
+	dst := new(pullRequestHook)
+	err := json.Unmarshal(data, dst)
+	return convertPullRequestHook(dst), err
+}
+
+func (s *webhookService) parsePushHook(data []byte) (scm.Webhook, error) {
+	dst := new(pushHook)
+	err := json.Unmarshal(data, dst)
+	return convertPushHook(dst), err
+}
+
+func (s *webhookService) parsePullRequestCommentHook(data []byte) (scm.Webhook, error) {
+	dst := new(pullRequestCommentHook)
+	err := json.Unmarshal(data, dst)
+	return convertPullRequestCommentHook(dst), err
+}
+
+func (s *webhookService) parseBranchHook(data []byte) (scm.Webhook, error) {
+	// using pushHook object since it is same as branch events
+	dst := new(pushHook)
+	err := json.Unmarshal(data, dst)
+	return convertBranchHook(dst), err
+}
+
+func (s *webhookService) parseTagHook(data []byte) (scm.Webhook, error) {
+	// using pushHook object since it is same as tag events
+	dst := new(pushHook)
+	err := json.Unmarshal(data, dst)
+	return convertTagHook(dst), err
+}
+
+// native data structures
+type (
+	repo struct {
+		ID            int    `json:"id"`
+		Path          string `json:"path"`
+		UID           string `json:"uid"`
+		DefaultBranch string `json:"default_branch"`
+		GitURL        string `json:"git_url"`
+	}
+	principal struct {
+		ID          int    `json:"id"`
+		UID         string `json:"uid"`
+		DisplayName string `json:"display_name"`
+		Email       string `json:"email"`
+		Type        string `json:"type"`
+		Created     int64  `json:"created"`
+		Updated     int64  `json:"updated"`
+	}
+	pullReq struct {
+		Number        int         `json:"number"`
+		State         string      `json:"state"`
+		IsDraft       bool        `json:"is_draft"`
+		Title         string      `json:"title"`
+		Description   string      `json:"description"`
+		SourceRepoID  int         `json:"source_repo_id"`
+		SourceBranch  string      `json:"source_branch"`
+		TargetRepoID  int         `json:"target_repo_id"`
+		TargetBranch  string      `json:"target_branch"`
+		MergeBaseSHA  string      `json:"merge_base_sha"`
+		MergeStrategy interface{} `json:"merge_strategy"`
+		Author        principal   `json:"author"`
+		PrURL         string      `json:"pr_url"`
+	}
+	targetRef struct {
+		Name string `json:"name"`
+		Repo struct {
+			ID            int    `json:"id"`
+			Path          string `json:"path"`
+			UID           string `json:"uid"`
+			DefaultBranch string `json:"default_branch"`
+			GitURL        string `json:"git_url"`
+		} `json:"repo"`
+	}
+	ref struct {
+		Name string `json:"name"`
+		Repo struct {
+			ID            int    `json:"id"`
+			Path          string `json:"path"`
+			UID           string `json:"uid"`
+			DefaultBranch string `json:"default_branch"`
+			GitURL        string `json:"git_url"`
+		} `json:"repo"`
+	}
+	hookCommit struct {
+		Sha     string `json:"sha"`
+		Message string `json:"message"`
+		URL     string `json:"url"`
+		Author  struct {
+			Identity struct {
+				Name  string `json:"name"`
+				Email string `json:"email"`
+			} `json:"identity"`
+			When string `json:"when"`
+		} `json:"author"`
+		Committer struct {
+			Identity struct {
+				Name  string `json:"name"`
+				Email string `json:"email"`
+			} `json:"identity"`
+			When string `json:"when"`
+		} `json:"committer"`
+		Added    []string `json:"added"`
+		Modified []string `json:"modified"`
+		Removed  []string `json:"removed"`
+	}
+	comment struct {
+		ID   int    `json:"id"`
+		Text string `json:"text"`
+	}
+	// harness pull request webhook payload
+	pullRequestHook struct {
+		Trigger           string       `json:"trigger"`
+		Repo              repo         `json:"repo"`
+		Principal         principal    `json:"principal"`
+		PullReq           pullReq      `json:"pull_req"`
+		TargetRef         targetRef    `json:"target_ref"`
+		Ref               ref          `json:"ref"`
+		Sha               string       `json:"sha"`
+		HeadCommit        hookCommit   `json:"head_commit"`
+		Commits           []hookCommit `json:"commits"`
+		TotalCommitsCount int64        `json:"total_commits_count"`
+	}
+	// harness push webhook payload
+	pushHook struct {
+		Trigger           string       `json:"trigger"`
+		Repo              repo         `json:"repo"`
+		Principal         principal    `json:"principal"`
+		Ref               ref          `json:"ref"`
+		HeadCommit        hookCommit   `json:"head_commit"`
+		Sha               string       `json:"sha"`
+		OldSha            string       `json:"old_sha"`
+		Forced            bool         `json:"forced"`
+		Commits           []hookCommit `json:"commits"`
+		TotalCommitsCount int64        `json:"total_commits_count"`
+	}
+	// harness pull request comment webhook payload
+	pullRequestCommentHook struct {
+		Trigger    string     `json:"trigger"`
+		Repo       repo       `json:"repo"`
+		Principal  principal  `json:"principal"`
+		PullReq    pullReq    `json:"pull_req"`
+		TargetRef  targetRef  `json:"target_ref"`
+		Ref        ref        `json:"ref"`
+		Sha        string     `json:"sha"`
+		HeadCommit hookCommit `json:"head_commit"`
+		Comment    comment    `json:"comment"`
+	}
+)
+
+// native data structure conversion
+func convertPullRequestHook(src *pullRequestHook) *scm.PullRequestHook {
+	return &scm.PullRequestHook{
+		Action:      convertPRAction(src.Trigger),
+		PullRequest: convertPullReq(src.PullReq, src.Ref, src.HeadCommit),
+		Repo:        convertRepo(src.Repo),
+		Sender:      convertUser(src.Principal),
+	}
+}
+
+func convertPushHook(src *pushHook) *scm.PushHook {
+	var commits []scm.Commit
+	for _, c := range src.Commits {
+		commits = append(commits, convertHookCommit(c))
+	}
+	return &scm.PushHook{
+		Ref:     src.Ref.Name,
+		Before:  src.OldSha,
+		After:   src.Sha,
+		Repo:    convertRepo(src.Repo),
+		Commit:  convertHookCommit(src.HeadCommit),
+		Sender:  convertUser(src.Principal),
+		Commits: commits,
+	}
+}
+
+func convertHookCommit(c hookCommit) scm.Commit {
+	return scm.Commit{
+		Sha:     c.Sha,
+		Message: c.Message,
+		Author: scm.Signature{
+			Name:  c.Author.Identity.Name,
+			Email: c.Author.Identity.Email,
+		},
+		Committer: scm.Signature{
+			Name:  c.Committer.Identity.Name,
+			Email: c.Committer.Identity.Email,
+		},
+		Link: c.URL,
+	}
+}
+
+func convertPullRequestCommentHook(src *pullRequestCommentHook) *scm.PullRequestCommentHook {
+	return &scm.PullRequestCommentHook{
+		PullRequest: convertPullReq(src.PullReq, src.Ref, src.HeadCommit),
+		Repo:        convertRepo(src.Repo),
+		Comment: scm.Comment{
+			Body: src.Comment.Text,
+			ID:   src.Comment.ID,
+		},
+		Sender: convertUser(src.Principal),
+	}
+}
+func convertBranchHook(dst *pushHook) *scm.BranchHook {
+	return &scm.BranchHook{
+		Ref:    convertRef(dst),
+		Repo:   convertRepo(dst.Repo),
+		Action: convertBranchAction(dst.Trigger),
+		Sender: convertUser(dst.Principal),
+	}
+}
+
+func convertTagHook(dst *pushHook) *scm.TagHook {
+	return &scm.TagHook{
+		Ref:    convertRef(dst),
+		Repo:   convertRepo(dst.Repo),
+		Action: convertTagAction(dst.Trigger),
+		Sender: convertUser(dst.Principal),
+	}
+}
+
+func convertRef(dst *pushHook) scm.Reference {
+	return scm.Reference{
+		Name: dst.Ref.Name,
+		Sha:  dst.Sha,
+	}
+}
+
+func convertPRAction(src string) (action scm.Action) {
+	switch strings.ToLower(src) {
+	case "pullreq_created":
+		return scm.ActionCreate
+	case "pullreq_branch_updated":
+		return scm.ActionUpdate
+	case "pullreq_reopened":
+		return scm.ActionReopen
+	case "pullreq_closed":
+		return scm.ActionClose
+	case "pullreq_merged":
+		return scm.ActionMerge
+	default:
+		return scm.ActionUnknown
+	}
+}
+
+func convertBranchAction(src string) (action scm.Action) {
+	switch strings.ToLower(src) {
+	case "branch_created":
+		return scm.ActionCreate
+	case "branch_deleted":
+		return scm.ActionDelete
+	default:
+		return scm.ActionUnknown
+	}
+}
+
+func convertTagAction(src string) (action scm.Action) {
+	switch strings.ToLower(src) {
+	case "tag_created":
+		return scm.ActionCreate
+	case "tag_deleted":
+		return scm.ActionDelete
+	default:
+		return scm.ActionUnknown
+	}
+}
+
+func convertPullReq(pr pullReq, ref ref, commit hookCommit) scm.PullRequest {
+	return scm.PullRequest{
+		Number: pr.Number,
+		Title:  pr.Title,
+		Body:   pr.Description,
+		Closed: pr.State != "open",
+		Source: pr.SourceBranch,
+		Target: pr.TargetBranch,
+		Merged: pr.State == "merged",
+		Fork:   "fork",
+		Link:   pr.PrURL,
+		Draft:  pr.IsDraft,
+		Sha:    commit.Sha,
+		Ref:    ref.Name,
+		Author: convertUser(pr.Author),
+		Head: scm.Reference{
+			Name: pr.SourceBranch,
+			Path: scm.ExpandRef(pr.SourceBranch, "refs/heads"),
+			Sha:  commit.Sha,
+		},
+		Base: scm.Reference{
+			Name: pr.TargetBranch,
+			Path: scm.ExpandRef(pr.TargetBranch, "refs/heads"),
+			Sha:  pr.MergeBaseSHA,
+		},
+	}
+}
+
+func convertRepo(repo repo) scm.Repository {
+	return scm.Repository{
+		ID:     strconv.Itoa(repo.ID),
+		Name:   repo.UID,
+		Branch: repo.DefaultBranch,
+		Link:   repo.GitURL,
+		Clone:  repo.GitURL,
+	}
+}
+
+func convertUser(principal principal) scm.User {
+	return scm.User{
+		Name:    principal.DisplayName,
+		ID:      principal.UID,
+		Login:   principal.UID,
+		Email:   principal.Email,
+		Created: time.Unix(0, principal.Created*int64(time.Millisecond)),
+		Updated: time.Unix(0, principal.Updated*int64(time.Millisecond)),
+	}
+}
diff --git a/scm/driver/harness/webhook_test.go b/scm/driver/harness/webhook_test.go
new file mode 100644
index 000000000..a4cf093b1
--- /dev/null
+++ b/scm/driver/harness/webhook_test.go
@@ -0,0 +1,178 @@
+// Copyright 2017 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package harness
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestWebhooks(t *testing.T) {
+	tests := []struct {
+		event  string
+		before string
+		after  string
+		obj    interface{}
+	}{
+		//
+		// branch events
+		//
+		// push branch create
+		{
+			event:  "branch_created",
+			before: "testdata/webhooks/branch_create.json",
+			after:  "testdata/webhooks/branch_create.json.golden",
+			obj:    new(scm.BranchHook),
+		},
+		// push branch update
+		{
+			event:  "branch_updated",
+			before: "testdata/webhooks/branch_updated.json",
+			after:  "testdata/webhooks/branch_updated.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		// push branch delete
+		{
+			event:  "branch_deleted",
+			before: "testdata/webhooks/branch_delete.json",
+			after:  "testdata/webhooks/branch_delete.json.golden",
+			obj:    new(scm.BranchHook),
+		},
+		//
+		// tag events
+		//
+		// push tag create
+		{
+			event:  "tag_created",
+			before: "testdata/webhooks/tag_create.json",
+			after:  "testdata/webhooks/tag_create.json.golden",
+			obj:    new(scm.TagHook),
+		},
+		// push tag update
+		{
+			event:  "tag_updated",
+			before: "testdata/webhooks/tag_update.json",
+			after:  "testdata/webhooks/tag_update.json.golden",
+			obj:    new(scm.PushHook),
+		},
+		// push tag delete
+		{
+			event:  "tag_deleted",
+			before: "testdata/webhooks/tag_delete.json",
+			after:  "testdata/webhooks/tag_delete.json.golden",
+			obj:    new(scm.TagHook),
+		},
+
+		//
+		// pull request events
+		//
+		// pull request opened
+		{
+			event:  "pullreq_created",
+			before: "testdata/webhooks/pull_request_opened.json",
+			after:  "testdata/webhooks/pull_request_opened.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request reopened
+		{
+			event:  "pullreq_reopened",
+			before: "testdata/webhooks/pull_request_reopened.json",
+			after:  "testdata/webhooks/pull_request_reopened.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request branch updated
+		{
+			event:  "pullreq_branch_updated",
+			before: "testdata/webhooks/pull_request_branch_updated.json",
+			after:  "testdata/webhooks/pull_request_branch_updated.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request comment created
+		{
+			event:  "pullreq_comment_created",
+			before: "testdata/webhooks/pull_request_comment_created.json",
+			after:  "testdata/webhooks/pull_request_comment_created.json.golden",
+			obj:    new(scm.PullRequestCommentHook),
+		},
+		// pull request closed
+		{
+			event:  "pullreq_reopened",
+			before: "testdata/webhooks/pull_request_closed.json",
+			after:  "testdata/webhooks/pull_request_closed.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request merged
+		{
+			event:  "pullreq_reopened",
+			before: "testdata/webhooks/pull_request_merged.json",
+			after:  "testdata/webhooks/pull_request_merged.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+	}
+
+	for _, test := range tests {
+		before, err := ioutil.ReadFile(test.before)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		after, err := ioutil.ReadFile(test.after)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		buf := bytes.NewBuffer(before)
+		r, _ := http.NewRequest("GET", "/", buf)
+		r.Header.Set("X-Harness-Trigger", test.event)
+
+		s := new(webhookService)
+		o, err := s.Parse(r, secretFunc)
+		if err != nil && err != scm.ErrSignatureInvalid {
+			t.Error(err)
+			continue
+		}
+
+		err = json.Unmarshal(after, test.obj)
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+
+		if diff := cmp.Diff(test.obj, o); diff != "" {
+			t.Errorf("Error unmarshaling %s", test.before)
+			t.Log(diff)
+
+			// debug only. remove once implemented
+			_ = json.NewEncoder(os.Stdout).Encode(o)
+
+		}
+
+		// switch event := o.(type) {
+		// case *scm.PushHook:
+		// 	if !strings.HasPrefix(event.Ref, "refs/") {
+		// 		t.Errorf("Push hook reference must start with refs/")
+		// 	}
+		// case *scm.BranchHook:
+		// 	if strings.HasPrefix(event.Ref.Name, "refs/") {
+		// 		t.Errorf("Branch hook reference must not start with refs/")
+		// 	}
+		// case *scm.TagHook:
+		// 	if strings.HasPrefix(event.Ref.Name, "refs/") {
+		// 		t.Errorf("Branch hook reference must not start with refs/")
+		// 	}
+		// }
+	}
+}
+func secretFunc(scm.Webhook) (string, error) {
+	return "topsecret", nil
+}
diff --git a/scm/driver/internal/null/time.go b/scm/driver/internal/null/time.go
index 574209ad0..3182ce71f 100644
--- a/scm/driver/internal/null/time.go
+++ b/scm/driver/internal/null/time.go
@@ -63,3 +63,25 @@ func (t Time) ValueOrZero() time.Time {
 func (t Time) IsZero() bool {
 	return !t.Valid || t.Time.IsZero()
 }
+
+// Ptr returns a pointer to this Time's value,
+// or a nil pointer if this Time is zero.
+func (t Time) Ptr() *time.Time {
+	if !t.Valid {
+		return nil
+	}
+	return &t.Time
+}
+
+// NewTime creates a new Time.
+func NewTime(t time.Time, valid bool) Time {
+	return Time{
+		Time:  t,
+		Valid: valid,
+	}
+}
+
+// TimeFrom creates a new Time that will always be valid.
+func TimeFrom(t time.Time) Time {
+	return NewTime(t, !t.IsZero())
+}
\ No newline at end of file
diff --git a/scm/driver/stash/content.go b/scm/driver/stash/content.go
index b067825ec..fe921e528 100644
--- a/scm/driver/stash/content.go
+++ b/scm/driver/stash/content.go
@@ -8,6 +8,7 @@ import (
 	"bytes"
 	"context"
 	"fmt"
+	"net/url"
 
 	"github.com/drone/go-scm/scm"
 )
@@ -17,8 +18,9 @@ type contentService struct {
 }
 
 func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm.Content, *scm.Response, error) {
+	urlEncodedRef := url.QueryEscape(ref)
 	namespace, name := scm.Split(repo)
-	endpoint := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/raw/%s?at=%s", namespace, name, path, ref)
+	endpoint := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/raw/%s?at=%s", namespace, name, path, urlEncodedRef)
 	out := new(bytes.Buffer)
 	res, err := s.client.do(ctx, "GET", endpoint, nil, out)
 	return &scm.Content{
@@ -28,14 +30,29 @@ func (s *contentService) Find(ctx context.Context, repo, path, ref string) (*scm
 }
 
 func (s *contentService) Create(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+	namespace, repoName := scm.Split(repo)
+	endpoint := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/browse/%s", namespace, repoName, path)
+	in := &contentCreateUpdate{
+		Message: params.Message,
+		Branch:  params.Branch,
+		Content: params.Data,
+	}
+	return s.client.do(ctx, "PUT", endpoint, in, nil)
 }
 
 func (s *contentService) Update(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
-	return nil, scm.ErrNotSupported
+	namespace, repoName := scm.Split(repo)
+	endpoint := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/browse/%s", namespace, repoName, path)
+	in := &contentCreateUpdate{
+		Message: params.Message,
+		Branch:  params.Branch,
+		Content: params.Data,
+		Sha:     params.Sha,
+	}
+	return s.client.do(ctx, "PUT", endpoint, in, nil)
 }
 
-func (s *contentService) Delete(ctx context.Context, repo, path, ref string) (*scm.Response, error) {
+func (s *contentService) Delete(ctx context.Context, repo, path string, params *scm.ContentParams) (*scm.Response, error) {
 	return nil, scm.ErrNotSupported
 }
 
@@ -53,6 +70,13 @@ type contents struct {
 	Values []string `json:"values"`
 }
 
+type contentCreateUpdate struct {
+	Branch  string `json:"branch"`
+	Message string `json:"message"`
+	Content []byte `json:"content"`
+	Sha     string `json:"sourceCommitId"`
+}
+
 func convertContentInfoList(from *contents) []*scm.ContentInfo {
 	to := []*scm.ContentInfo{}
 	for _, v := range from.Values {
diff --git a/scm/driver/stash/content_test.go b/scm/driver/stash/content_test.go
index e958df5c1..68f84f050 100644
--- a/scm/driver/stash/content_test.go
+++ b/scm/driver/stash/content_test.go
@@ -34,7 +34,29 @@ func TestContentFind(t *testing.T) {
 
 	want := new(scm.Content)
 	raw, _ := ioutil.ReadFile("testdata/content.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/raw/README").
+		MatchParam("at", "b1&b2").
+		Reply(200).
+		Type("text/plain").
+		File("testdata/content.txt")
+
+	client, _ = New("http://example.com:7990")
+	got, _, err = client.Contents.Find(context.Background(), "PRJ/my-repo", "README", "b1&b2")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want = new(scm.Content)
+	raw, _ = ioutil.ReadFile("testdata/content.json.golden")
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -43,24 +65,81 @@ func TestContentFind(t *testing.T) {
 }
 
 func TestContentCreate(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Create(context.Background(), "atlassian/atlaskit", "README", nil)
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("http://localhost:7990").
+		Put("/rest/api/1.0/projects/octocat/repos/hello-world/browse/README").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_create.json")
+
+	params := &scm.ContentParams{
+		Message: "my commit message",
+		Data:    []byte("bXkgbmV3IGZpbGUgY29udGVudHM="),
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Create(
+		context.Background(),
+		"octocat/hello-world",
+		"README",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
 	}
 }
 
 func TestContentUpdate(t *testing.T) {
-	content := new(contentService)
-	_, err := content.Update(context.Background(), "atlassian/atlaskit", "README", nil)
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	defer gock.Off()
+
+	gock.New("http://localhost:7990").
+		Put("/rest/api/1.0/projects/octocat/repos/hello-world/browse/README").
+		Reply(200).
+		Type("application/json").
+		File("testdata/content_update.json")
+
+	params := &scm.ContentParams{
+		Message: "a new commit message",
+		Data:    []byte("bXkgdXBkYXRlZCBmaWxlIGNvbnRlbnRz"),
+		BlobID:  "95b966ae1c166bd92f8ae7d1c313e738c731dfc3",
+		Signature: scm.Signature{
+			Name:  "Monalisa Octocat",
+			Email: "octocat@github.com",
+		},
+	}
+
+	client := NewDefault()
+	res, err := client.Contents.Update(
+		context.Background(),
+		"octocat/hello-world",
+		"README",
+		params,
+	)
+
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if res.Status != 200 {
+		t.Errorf("Unexpected Results")
 	}
 }
 
 func TestContentDelete(t *testing.T) {
 	content := new(contentService)
-	_, err := content.Delete(context.Background(), "atlassian/atlaskit", "README", "master")
+	_, err := content.Delete(context.Background(), "atlassian/atlaskit", "README", &scm.ContentParams{})
 	if err != scm.ErrNotSupported {
 		t.Errorf("Expect Not Supported error")
 	}
@@ -90,7 +169,7 @@ func TestContentList(t *testing.T) {
 
 	want := []*scm.ContentInfo{}
 	raw, _ := ioutil.ReadFile("testdata/content_list.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/stash/git.go b/scm/driver/stash/git.go
index bd1080d0b..221b59911 100644
--- a/scm/driver/stash/git.go
+++ b/scm/driver/stash/git.go
@@ -19,6 +19,17 @@ type gitService struct {
 	client *wrapper
 }
 
+func (s *gitService) CreateBranch(ctx context.Context, repo string, params *scm.ReferenceInput) (*scm.Response, error) {
+	namespace, repoName := scm.Split(repo)
+	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/branches", namespace, repoName)
+	in := &createBranch{
+		Name:       params.Name,
+		StartPoint: params.Sha,
+	}
+	return s.client.do(ctx, "POST", path, in, nil)
+
+}
+
 func (s *gitService) FindBranch(ctx context.Context, repo, branch string) (*scm.Reference, *scm.Response, error) {
 	namespace, name := scm.Split(repo)
 	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/branches?filterText=%s", namespace, name, branch)
@@ -68,8 +79,27 @@ func (s *gitService) ListBranches(ctx context.Context, repo string, opts scm.Lis
 	return convertBranchList(out), res, err
 }
 
+func (s *gitService) ListBranchesV2(ctx context.Context, repo string, opts scm.BranchListOptions) ([]*scm.Reference, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/branches?%s", namespace, name, encodeBranchListOptions(opts))
+	out := new(branches)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	copyPagination(out.pagination, res)
+	return convertBranchList(out), res, err
+}
+
 func (s *gitService) ListCommits(ctx context.Context, repo string, opts scm.CommitListOptions) ([]*scm.Commit, *scm.Response, error) {
-	return nil, nil, scm.ErrNotSupported
+	namespace, name := scm.Split(repo)
+	var requestPath string
+	if opts.Path != "" {
+		requestPath = fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/commits?until=%s&path=%s", namespace, name, opts.Ref, opts.Path)
+	} else {
+		requestPath = fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/commits?until=%s", namespace, name, opts.Ref)
+	}
+	out := new(commits)
+	res, err := s.client.do(ctx, "GET", requestPath, nil, out)
+	copyPagination(out.pagination, res)
+	return convertCommitList(out), res, err
 }
 
 func (s *gitService) ListTags(ctx context.Context, repo string, opts scm.ListOptions) ([]*scm.Reference, *scm.Response, error) {
@@ -147,6 +177,11 @@ type diffstat struct {
 	} `json:"properties"`
 }
 
+type createBranch struct {
+	Name       string `json:"name"`
+	StartPoint string `json:"startPoint"`
+}
+
 type commit struct {
 	ID        string `json:"id"`
 	DisplayID string `json:"displayId"`
diff --git a/scm/driver/stash/git_test.go b/scm/driver/stash/git_test.go
index 9d8209327..f93952ace 100644
--- a/scm/driver/stash/git_test.go
+++ b/scm/driver/stash/git_test.go
@@ -33,7 +33,7 @@ func TestGitFindCommit(t *testing.T) {
 
 	want := new(scm.Commit)
 	raw, _ := ioutil.ReadFile("testdata/commit.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -59,7 +59,7 @@ func TestGitFindBranch(t *testing.T) {
 
 	want := new(scm.Reference)
 	raw, _ := ioutil.ReadFile("testdata/branch.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -85,7 +85,7 @@ func TestGitFindTag(t *testing.T) {
 
 	want := new(scm.Reference)
 	raw, _ := ioutil.ReadFile("testdata/tag.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -94,10 +94,28 @@ func TestGitFindTag(t *testing.T) {
 }
 
 func TestGitListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/commits").
+		MatchParam("until", "").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
 	client, _ := New("http://example.com:7990")
-	_, _, err := client.Git.ListCommits(context.Background(), "PRJ/my-repo", scm.CommitListOptions{Ref: "master", Page: 1, Size: 30})
-	if err != scm.ErrNotSupported {
-		t.Errorf("Expect Not Supported error")
+	got, _, err := client.Git.ListCommits(context.Background(), "PRJ/my-repo", scm.CommitListOptions{})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
 	}
 }
 
@@ -119,7 +137,35 @@ func TestGitListBranches(t *testing.T) {
 
 	want := []*scm.Reference{}
 	raw, _ := ioutil.ReadFile("testdata/branches.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+	//
+	// t.Run("Page", testPage(res))
+}
+
+func TestGitListBranchesWithBranchFilter(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/branches").
+		MatchParam("filterText", "mast").
+		Reply(200).
+		Type("application/json").
+		File("testdata/branches_filter.json")
+
+	client, _ := New("http://example.com:7990")
+	got, _, err := client.Git.ListBranchesV2(context.Background(), "PRJ/my-repo", scm.BranchListOptions{SearchTerm: "mast"})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Reference{}
+	raw, _ := ioutil.ReadFile("testdata/branches_filter.json.golden")
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -147,7 +193,7 @@ func TestGitListTags(t *testing.T) {
 
 	want := []*scm.Reference{}
 	raw, _ := ioutil.ReadFile("testdata/tags.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -175,7 +221,7 @@ func TestGitListChanges(t *testing.T) {
 
 	want := []*scm.Change{}
 	raw, _ := ioutil.ReadFile("testdata/changes.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -203,10 +249,35 @@ func TestGitCompareChanges(t *testing.T) {
 
 	want := []*scm.Change{}
 	raw, _ := ioutil.ReadFile("testdata/compare.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
 		t.Log(diff)
 	}
 }
+
+func TestCreateBranch(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("http://example.com:7990").
+		Post("/rest/api/1.0/projects/PRJ/repos/my-repo/branches").
+		Reply(201).
+		Type("application/json").
+		File("testdata/branch_create.json")
+
+	client, _ := New("http://example.com:7990")
+	params := &scm.ReferenceInput{
+		Name: "Hello",
+		Sha:  "312797ba52425353dec56871a255e2a36fc96344",
+	}
+	res, err := client.Git.CreateBranch(context.Background(), "PRJ/my-repo", params)
+
+	if err != nil {
+		t.Errorf("Encountered err while creating branch " + err.Error())
+	}
+
+	if res.Status != 201 {
+		t.Errorf("The error response of branch creation is not 201")
+	}
+}
diff --git a/scm/driver/stash/integration/content_test.go b/scm/driver/stash/integration/content_test.go
new file mode 100644
index 000000000..d6a22bbcb
--- /dev/null
+++ b/scm/driver/stash/integration/content_test.go
@@ -0,0 +1,62 @@
+package integration
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/stash"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestCreateUpdateDeleteFileStash(t *testing.T) {
+	if token == "" || username == "" {
+		t.Skip("Skipping, Acceptance test. Missing BITBUCKET_SERVER_TOKEN or BITBUCKET_USERNAME")
+	}
+	client, _ = stash.New(endpoint)
+	client.Client = &http.Client{
+		Transport: &transport.BasicAuth{
+			Username: username,
+			Password: token,
+		},
+	}
+	// get latest commit first
+	currentCommit, commitErr := GetCurrentCommitOfBranch(client, "master")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	// create a new file
+	createParams := scm.ContentParams{
+		Message: "go-scm create crud file",
+		Data:    []byte("hello"),
+		Branch:  "master",
+		Sha:     currentCommit,
+	}
+	createResponse, createErr := client.Contents.Create(context.Background(), repoID, "README5", &createParams)
+	if createErr != nil {
+		t.Errorf("Contents.Create we got an error %v", createErr)
+	}
+	if createResponse.Status != http.StatusOK {
+		t.Errorf("Contents.Create we did not get a 201 back %v", createResponse.Status)
+	}
+	// get latest commit first
+	currentCommit, commitErr = GetCurrentCommitOfBranch(client, "main")
+	if commitErr != nil {
+		t.Errorf("we got an error %v", commitErr)
+	}
+	// update the file
+	updateParams := scm.ContentParams{
+		Message: "go-scm update crud file",
+		Data:    []byte("updated test data"),
+		Branch:  "master",
+		Sha:     currentCommit,
+	}
+	updateResponse, updateErr := client.Contents.Update(context.Background(), repoID, "README5", &updateParams)
+	if updateErr != nil {
+		t.Errorf("Contents.Update we got an error %v", updateErr)
+	}
+	if updateResponse.Status != http.StatusOK {
+		t.Errorf("Contents.Update we did not get a 201 back %v", updateResponse.Status)
+	}
+}
diff --git a/scm/driver/stash/integration/git_test.go b/scm/driver/stash/integration/git_test.go
new file mode 100644
index 000000000..d1607f8de
--- /dev/null
+++ b/scm/driver/stash/integration/git_test.go
@@ -0,0 +1,118 @@
+package integration
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/stash"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestCreateBranch(t *testing.T) {
+	if token == "" || username == "" {
+		t.Skip("Skipping, Acceptance test. Missing BITBUCKET_SERVER_TOKEN or BITBUCKET_USERNAME")
+	}
+	client, _ = stash.New(endpoint)
+	client.Client = &http.Client{
+		Transport: &transport.BasicAuth{
+			Username: username,
+			Password: token,
+		},
+	}
+
+	commitId, _ := GetCurrentCommitOfBranch(client, "master")
+	input := &scm.ReferenceInput{
+		Name: "test_branch",
+		Sha:  commitId,
+	}
+	response, listerr := client.Git.CreateBranch(context.Background(), repoID, input)
+	if listerr != nil {
+		t.Errorf("CreateBranch got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("CreateBranch did not get a 200 back %v", response.Status)
+	}
+}
+
+func TestGetLatestCommitOfBranch(t *testing.T) {
+	if token == "" || username == "" {
+		t.Skip("Skipping, Acceptance test. Missing BITBUCKET_SERVER_TOKEN or BITBUCKET_USERNAME")
+	}
+	client, _ = stash.New(endpoint)
+	client.Client = &http.Client{
+		Transport: &transport.BasicAuth{
+			Username: username,
+			Password: token,
+		},
+	}
+
+	commits, response, err := client.Git.ListCommits(context.Background(), repoID, scm.CommitListOptions{Ref: "master", Path: "README"})
+
+	if err != nil {
+		t.Errorf("GetLatestCommitOfFile got an error %v", err)
+	} else {
+		if response.Status != http.StatusOK {
+			t.Errorf("GetLatestCommitOfFile did not get a 200 back %v", response.Status)
+		}
+
+		if commits[0].Sha != "2cc4dbe084f0d66761318b305c408cb0ea300c9a" {
+			t.Errorf("Got the commitId %s instead of the top commit of the file", commits[0].Sha)
+		}
+	}
+}
+
+func TestGetLatestCommitOfNonDefaultBranch(t *testing.T) {
+	if token == "" || username == "" {
+		t.Skip("Skipping, Acceptance test. Missing BITBUCKET_SERVER_TOKEN or BITBUCKET_USERNAME")
+	}
+	client, _ = stash.New(endpoint)
+	client.Client = &http.Client{
+		Transport: &transport.BasicAuth{
+			Username: username,
+			Password: token,
+		},
+	}
+
+	commits, response, err := client.Git.ListCommits(context.Background(), repoID, scm.CommitListOptions{Ref: "main", Path: "do-not-touch.txt"})
+
+	if err != nil {
+		t.Errorf("GetLatestCommitOfFile got an error %v", err)
+	} else {
+		if response.Status != http.StatusOK {
+			t.Errorf("GetLatestCommitOfFile did not get a 200 back %v", response.Status)
+		}
+
+		if commits[0].Sha != "76fb1762048a277596d3fa330b3da140cd12d361" {
+			t.Errorf("Got the commitId %s instead of the top commit of the file", commits[0].Sha)
+		}
+	}
+}
+
+func TestGetLatestCommitOfBranchWhenNoRefPassed(t *testing.T) {
+	if token == "" || username == "" {
+		t.Skip("Skipping, Acceptance test. Missing BITBUCKET_SERVER_TOKEN or BITBUCKET_USERNAME")
+	}
+	client, _ = stash.New(endpoint)
+	client.Client = &http.Client{
+		Transport: &transport.BasicAuth{
+			Username: username,
+			Password: token,
+		},
+	}
+
+	commits, response, err := client.Git.ListCommits(context.Background(), repoID, scm.CommitListOptions{Path: "README"})
+
+	if err != nil {
+		t.Errorf("GetLatestCommitOfFile got an error %v", err)
+	} else {
+		if response.Status != http.StatusOK {
+			t.Errorf("GetLatestCommitOfFile did not get a 200 back %v", response.Status)
+		}
+
+		if commits[0].Sha != "2cc4dbe084f0d66761318b305c408cb0ea300c9a" {
+			t.Errorf("Got the commitId %s instead of the top commit of the file", commits[0].Sha)
+		}
+	}
+}
diff --git a/scm/driver/stash/integration/integration.go b/scm/driver/stash/integration/integration.go
new file mode 100644
index 000000000..56cae697d
--- /dev/null
+++ b/scm/driver/stash/integration/integration.go
@@ -0,0 +1,25 @@
+package integration
+
+import (
+	"context"
+	"os"
+
+	"github.com/drone/go-scm/scm"
+)
+
+var (
+	client *scm.Client
+	token  = os.Getenv("BITBUCKET_SERVER_TOKEN")
+
+	endpoint = "https://bitbucket.dev.harness.io/"
+	repoID   = "har/scm-integration-test-repo"
+	username = os.Getenv("BITBUCKET_USERNAME")
+)
+
+func GetCurrentCommitOfBranch(client *scm.Client, branch string) (string, error) {
+	commits, _, err := client.Git.ListCommits(context.Background(), repoID, scm.CommitListOptions{Ref: branch})
+	if err != nil {
+		return "", err
+	}
+	return commits[0].Sha, nil
+}
diff --git a/scm/driver/stash/integration/repo_test.go b/scm/driver/stash/integration/repo_test.go
new file mode 100644
index 000000000..927d4e85e
--- /dev/null
+++ b/scm/driver/stash/integration/repo_test.go
@@ -0,0 +1,37 @@
+package integration
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/drone/go-scm/scm"
+	"github.com/drone/go-scm/scm/driver/stash"
+	"github.com/drone/go-scm/scm/transport"
+)
+
+func TestListRepos(t *testing.T) {
+	if token == "" {
+		t.Skip("Skipping, Acceptance test")
+	}
+	client, _ = stash.New(endpoint)
+	client.Client = &http.Client{
+		Transport: &transport.BasicAuth{
+			Username: username,
+			Password: token,
+		},
+	}
+
+	repos, response, listerr := client.Repositories.List(context.Background(), scm.ListOptions{})
+	if listerr != nil {
+		t.Errorf("List Repos got an error %v", listerr)
+	}
+	if response.Status != http.StatusOK {
+		t.Errorf("List Repos did not get a 200 back %v", response.Status)
+	}
+
+	if len(repos) == 0 {
+		t.Errorf("Got Empty repo list")
+	}
+
+}
diff --git a/scm/driver/stash/milestone.go b/scm/driver/stash/milestone.go
new file mode 100644
index 000000000..e873f7b4e
--- /dev/null
+++ b/scm/driver/stash/milestone.go
@@ -0,0 +1,31 @@
+package stash
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type milestoneService struct {
+	client *wrapper
+}
+
+func (s *milestoneService) Find(ctx context.Context, repo string, id int) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) List(ctx context.Context, repo string, opts scm.MilestoneListOptions) ([]*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Create(ctx context.Context, repo string, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *milestoneService) Update(ctx context.Context, repo string, id int, input *scm.MilestoneInput) (*scm.Milestone, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/stash/multipart.go b/scm/driver/stash/multipart.go
new file mode 100644
index 000000000..4da89fdb7
--- /dev/null
+++ b/scm/driver/stash/multipart.go
@@ -0,0 +1,26 @@
+package stash
+
+import "mime/multipart"
+
+type MultipartWriter struct {
+	Writer *multipart.Writer
+	Error  error
+}
+
+func (mw *MultipartWriter) Write(f, v string) {
+	if mw.Error != nil {
+		return
+	}
+	if v == "" {
+		return
+	}
+	mw.Error = mw.Writer.WriteField(f, v)
+}
+
+func (mw *MultipartWriter) Close() {
+	mw.Writer.Close()
+}
+
+func (mw *MultipartWriter) FormDataContentType() string {
+	return mw.Writer.FormDataContentType()
+}
diff --git a/scm/driver/stash/pr.go b/scm/driver/stash/pr.go
index a404c7acc..86f88c35c 100644
--- a/scm/driver/stash/pr.go
+++ b/scm/driver/stash/pr.go
@@ -34,7 +34,7 @@ func (s *pullService) FindComment(ctx context.Context, repo string, number int,
 
 func (s *pullService) List(ctx context.Context, repo string, opts scm.PullRequestListOptions) ([]*scm.PullRequest, *scm.Response, error) {
 	namespace, name := scm.Split(repo)
-	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests", namespace, name)
+	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests?%s", namespace, name, encodePullRequestListOptions(opts))
 	out := new(prs)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
 	if !out.pagination.LastPage.Bool {
@@ -65,6 +65,18 @@ func (s *pullService) ListComments(context.Context, string, int, scm.ListOptions
 	return nil, nil, scm.ErrNotSupported
 }
 
+func (s *pullService) ListCommits(ctx context.Context, repo string, number int, opts scm.ListOptions) ([]*scm.Commit, *scm.Response, error) {
+	namespace, name := scm.Split(repo)
+	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/commits?%s", namespace, name, number, encodeListOptionsV2(opts))
+	out := new(commits)
+	res, err := s.client.do(ctx, "GET", path, nil, out)
+	if !out.pagination.LastPage.Bool {
+		res.Page.First = 1
+		res.Page.Next = opts.Page + 1
+	}
+	return convertCommitList(out), res, err
+}
+
 func (s *pullService) Merge(ctx context.Context, repo string, number int) (*scm.Response, error) {
 	namespace, name := scm.Split(repo)
 	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/pull-requests/%d/merge", namespace, name, number)
@@ -161,6 +173,12 @@ type pr struct {
 	Links        struct {
 		Self []link `json:"self"`
 	} `json:"links"`
+	Properties struct {
+		MergeCommit struct {
+			ID        string `json:"id"`
+			DisplayID string `json:"displayId"`
+		} `json:"mergeCommit"`
+	} `json:"properties"`
 }
 
 type prs struct {
@@ -209,6 +227,7 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Title:   from.Title,
 		Body:    from.Description,
 		Sha:     from.FromRef.LatestCommit,
+		Merge:   from.Properties.MergeCommit.ID,
 		Ref:     fmt.Sprintf("refs/pull-requests/%d/from", from.ID),
 		Source:  from.FromRef.DisplayID,
 		Target:  from.ToRef.DisplayID,
@@ -218,6 +237,16 @@ func convertPullRequest(from *pr) *scm.PullRequest {
 		Merged:  from.State == "MERGED",
 		Created: time.Unix(from.CreatedDate/1000, 0),
 		Updated: time.Unix(from.UpdatedDate/1000, 0),
+		Head: scm.Reference{
+			Name: from.FromRef.DisplayID,
+			Path: from.FromRef.ID,
+			Sha:  from.FromRef.LatestCommit,
+		},
+		Base: scm.Reference{
+			Name: from.ToRef.DisplayID,
+			Path: from.ToRef.ID,
+			Sha:  from.ToRef.LatestCommit,
+		},
 		Author: scm.User{
 			Login:  from.Author.User.Slug,
 			Name:   from.Author.User.DisplayName,
diff --git a/scm/driver/stash/pr_test.go b/scm/driver/stash/pr_test.go
index b9695485e..4710f4867 100644
--- a/scm/driver/stash/pr_test.go
+++ b/scm/driver/stash/pr_test.go
@@ -33,7 +33,7 @@ func TestPullFind(t *testing.T) {
 
 	want := new(scm.PullRequest)
 	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -58,7 +58,7 @@ func TestPullFindComment(t *testing.T) {
 
 	want := new(scm.Comment)
 	raw, _ := ioutil.ReadFile("testdata/pr_comment.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -83,7 +83,7 @@ func TestPullList(t *testing.T) {
 
 	want := []*scm.PullRequest{}
 	raw, _ := ioutil.ReadFile("testdata/prs.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -110,7 +110,7 @@ func TestPullListChanges(t *testing.T) {
 
 	want := []*scm.Change{}
 	raw, _ := ioutil.ReadFile("testdata/pr_change.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -174,7 +174,7 @@ func TestPullCreate(t *testing.T) {
 
 	want := new(scm.PullRequest)
 	raw, _ := ioutil.ReadFile("testdata/pr.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -201,7 +201,32 @@ func TestPullCreateComment(t *testing.T) {
 
 	want := new(scm.Comment)
 	raw, _ := ioutil.ReadFile("testdata/pr_comment.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestPullListCommits(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("http://example.com:7990").
+		Get("rest/api/1.0/projects/PRJ/repos/my-repo/pull-requests/1/commits").
+		Reply(200).
+		Type("application/json").
+		File("testdata/commits.json")
+
+	client, _ := New("http://example.com:7990")
+	got, _, err := client.PullRequests.ListCommits(context.Background(), "PRJ/my-repo", 1, scm.ListOptions{Size: 30, Page: 1})
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := []*scm.Commit{}
+	raw, _ := ioutil.ReadFile("testdata/commits.json.golden")
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/stash/release.go b/scm/driver/stash/release.go
new file mode 100644
index 000000000..8376200bb
--- /dev/null
+++ b/scm/driver/stash/release.go
@@ -0,0 +1,43 @@
+package stash
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+type releaseService struct {
+	client *wrapper
+}
+
+func (s *releaseService) Find(ctx context.Context, repo string, id int) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) FindByTag(ctx context.Context, repo string, tag string) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) List(ctx context.Context, repo string, opts scm.ReleaseListOptions) ([]*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Create(ctx context.Context, repo string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Delete(ctx context.Context, repo string, id int) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) DeleteByTag(ctx context.Context, repo string, tag string) (*scm.Response, error) {
+	return nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) Update(ctx context.Context, repo string, id int, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
+func (s *releaseService) UpdateByTag(ctx context.Context, repo string, tag string, input *scm.ReleaseInput) (*scm.Release, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
diff --git a/scm/driver/stash/repo.go b/scm/driver/stash/repo.go
index 17e17362e..f8093a145 100644
--- a/scm/driver/stash/repo.go
+++ b/scm/driver/stash/repo.go
@@ -81,7 +81,7 @@ type hookInput struct {
 	URL    string   `json:"url"`
 	Active bool     `json:"active"`
 	Config struct {
-		Secret string `json:"secret"`
+		Secret string `json:"secret,omitempty"`
 	} `json:"configuration"`
 }
 
@@ -103,7 +103,19 @@ func (s *repositoryService) Find(ctx context.Context, repo string) (*scm.Reposit
 	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s", namespace, name)
 	out := new(repository)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
-	return convertRepository(out), res, err
+	outputRepo := convertRepository(out)
+
+	branch := new(branch)
+	pathBranch := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/branches/default", namespace, name)
+	_, errBranch := s.client.do(ctx, "GET", pathBranch, nil, branch)
+	if errBranch == nil {
+		outputRepo.Branch = branch.DisplayID
+	}
+	if err == nil {
+		err = errBranch
+	}
+
+	return outputRepo, res, err
 }
 
 // FindHook returns a repository hook.
@@ -137,10 +149,10 @@ func (s *repositoryService) FindPerms(ctx context.Context, repo string) (*scm.Pe
 		}, nil, nil
 	}
 	// HACK: test if the user has write access to the repository.
-	_, name := scm.Split(repo)
+	namespace, _ := scm.Split(repo)
 	repos, _, _ := s.listWrite(ctx, repo)
 	for _, repo := range repos {
-		if repo.Name == name {
+		if repo.Namespace == namespace {
 			return &scm.Perm{
 				Pull:  true,
 				Push:  true,
@@ -161,7 +173,31 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
 	path := fmt.Sprintf("rest/api/1.0/repos?%s", encodeListRoleOptions(opts))
 	out := new(repositories)
 	res, err := s.client.do(ctx, "GET", path, nil, &out)
-	if !out.pagination.LastPage.Bool {
+	if res != nil && !out.pagination.LastPage.Bool {
+		res.Page.First = 1
+		res.Page.Next = opts.Page + 1
+	}
+	return convertRepositoryList(out), res, err
+}
+
+// ListV2 returns the user repository list based on the searchTerm passed.
+func (s *repositoryService) ListV2(ctx context.Context, opts scm.RepoListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("rest/api/1.0/repos?%s", encodeRepoListOptions(opts))
+	out := new(repositories)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	if res != nil && !out.pagination.LastPage.Bool {
+		res.Page.First = 1
+		res.Page.Next = opts.ListOptions.Page + 1
+	}
+	return convertRepositoryList(out), res, err
+}
+
+// ListNamespace returns the user repository list based on searchterm and namespace.
+func (s *repositoryService) ListNamespace(ctx context.Context, namespace string, opts scm.ListOptions) ([]*scm.Repository, *scm.Response, error) {
+	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos?%s", namespace, encodeListRoleOptions(opts))
+	out := new(repositories)
+	res, err := s.client.do(ctx, "GET", path, nil, &out)
+	if res != nil && !out.pagination.LastPage.Bool {
 		res.Page.First = 1
 		res.Page.Next = opts.Page + 1
 	}
@@ -170,8 +206,8 @@ func (s *repositoryService) List(ctx context.Context, opts scm.ListOptions) ([]*
 
 // listWrite returns the user repository list.
 func (s *repositoryService) listWrite(ctx context.Context, repo string) ([]*scm.Repository, *scm.Response, error) {
-	namespace, name := scm.Split(repo)
-	path := fmt.Sprintf("rest/api/1.0/repos?size=1000&permission=REPO_WRITE&projectname=%s&name=%s", namespace, name)
+	_, name := scm.Split(repo)
+	path := fmt.Sprintf("rest/api/1.0/repos?size=1000&permission=REPO_WRITE&name=%s", name)
 	out := new(repositories)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
 	return convertRepositoryList(out), res, err
@@ -183,7 +219,7 @@ func (s *repositoryService) ListHooks(ctx context.Context, repo string, opts scm
 	path := fmt.Sprintf("rest/api/1.0/projects/%s/repos/%s/webhooks?%s", namespace, name, encodeListOptions(opts))
 	out := new(hooks)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
-	if !out.pagination.LastPage.Bool {
+	if res != nil && !out.pagination.LastPage.Bool {
 		res.Page.First = 1
 		res.Page.Next = opts.Page + 1
 	}
diff --git a/scm/driver/stash/repo_test.go b/scm/driver/stash/repo_test.go
index 57c6fdb1d..ff7138df1 100644
--- a/scm/driver/stash/repo_test.go
+++ b/scm/driver/stash/repo_test.go
@@ -25,6 +25,12 @@ func TestRepositoryFind(t *testing.T) {
 		Type("application/json").
 		File("testdata/repo.json")
 
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/branches/default").
+		Reply(200).
+		Type("application/json").
+		File("testdata/default_branch.json")
+
 	client, _ := New("http://example.com:7990")
 	got, _, err := client.Repositories.Find(context.Background(), "PRJ/my-repo")
 	if err != nil {
@@ -33,7 +39,7 @@ func TestRepositoryFind(t *testing.T) {
 
 	want := new(scm.Repository)
 	raw, _ := ioutil.ReadFile("testdata/repo.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -76,6 +82,12 @@ func TestRepositoryPerms(t *testing.T) {
 		Type("application/json").
 		File("testdata/webhooks.json")
 
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/branches/default").
+		Reply(200).
+		Type("application/json").
+		File("testdata/default_branch.json")
+
 	client, _ := New("http://example.com:7990")
 	got, _, err := client.Repositories.FindPerms(context.Background(), "PRJ/my-repo")
 	if err != nil {
@@ -117,6 +129,12 @@ func TestRepositoryPerms_ReadOnly(t *testing.T) {
 		Type("application/json").
 		File("testdata/repo.json")
 
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/branches/default").
+		Reply(200).
+		Type("application/json").
+		File("testdata/default_branch.json")
+
 	client, _ := New("http://example.com:7990")
 	got, _, err := client.Repositories.FindPerms(context.Background(), "PRJ/my-repo")
 	if err != nil {
@@ -157,12 +175,17 @@ func TestRepositoryPerms_Write(t *testing.T) {
 		Get("/rest/api/1.0/repos").
 		MatchParam("size", "1000").
 		MatchParam("permission", "REPO_WRITE").
-		MatchParam("projectname", "PRJ").
 		MatchParam("name", "my-repo").
 		Reply(200).
 		Type("application/json").
 		File("testdata/repos.json")
 
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/my-repo/branches/default").
+		Reply(200).
+		Type("application/json").
+		File("testdata/default_branch.json")
+
 	client, _ := New("http://example.com:7990")
 	got, _, err := client.Repositories.FindPerms(context.Background(), "PRJ/my-repo")
 	if err != nil {
@@ -185,6 +208,57 @@ func TestRepositoryPerms_Write(t *testing.T) {
 	}
 }
 
+func TestRepositoryPermsDifferentProjectName_Write(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/quux").
+		Reply(200).
+		Type("application/json").
+		File("testdata/repo.json")
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/quux/webhooks").
+		Reply(404).
+		Type("application/json")
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/repos").
+		MatchParam("size", "1000").
+		MatchParam("permission", "REPO_WRITE").
+		MatchParam("name", "quux").
+		Reply(200).
+		Type("application/json").
+		File("testdata/repos.json")
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/projects/PRJ/repos/quux/branches/default").
+		Reply(200).
+		Type("application/json").
+		File("testdata/default_branch.json")
+
+	client, _ := New("http://example.com:7990")
+	got, _, err := client.Repositories.FindPerms(context.Background(), "PRJ/quux")
+	if err != nil {
+		t.Error(err)
+	}
+
+	want := &scm.Perm{
+		Pull:  true,
+		Push:  true,
+		Admin: false,
+	}
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+
+	if gock.IsPending() {
+		t.Errorf("pending API requests")
+	}
+}
+
 func TestRepositoryPerms_Forbidden(t *testing.T) {
 	defer gock.Off()
 
@@ -237,7 +311,48 @@ func TestRepositoryList(t *testing.T) {
 
 	want := []*scm.Repository{}
 	raw, _ := ioutil.ReadFile("testdata/repos.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
+
+	if diff := cmp.Diff(got, want); diff != "" {
+		t.Errorf("Unexpected Results")
+		t.Log(diff)
+	}
+}
+
+func TestRepositoryListV2(t *testing.T) {
+	defer gock.Off()
+
+	gock.New("http://example.com:7990").
+		Get("/rest/api/1.0/repos").
+		MatchParam("name", "quux").
+		MatchParam("limit", "25").
+		MatchParam("start", "50").
+		MatchParam("permission", "REPO_READ").
+		Reply(200).
+		Type("application/json").
+		File("testdata/repos_filter.json")
+
+	client, _ := New("http://example.com:7990")
+	got, res, err := client.Repositories.ListV2(context.Background(), scm.RepoListOptions{
+		ListOptions: scm.ListOptions{Page: 3, Size: 25},
+		RepoSearchTerm: scm.RepoSearchTerm{
+			RepoName: "quux",
+		},
+	})
+	if err != nil {
+		t.Error(err)
+	}
+
+	if got, want := res.Page.First, 1; got != want {
+		t.Errorf("Want Page.First %d, got %d", want, got)
+	}
+	if got, want := res.Page.Next, 4; got != want {
+		t.Errorf("Want Page.Next %d, got %d", want, got)
+	}
+
+	want := []*scm.Repository{}
+	raw, _ := ioutil.ReadFile("testdata/repos_filter.json.golden")
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -293,7 +408,7 @@ func TestRepositoryHookFind(t *testing.T) {
 
 	want := new(scm.Hook)
 	raw, _ := ioutil.ReadFile("testdata/webhook.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -320,7 +435,7 @@ func TestRepositoryHookList(t *testing.T) {
 
 	want := []*scm.Hook{}
 	raw, _ := ioutil.ReadFile("testdata/webhooks.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -357,7 +472,7 @@ func TestRepositoryHookCreate(t *testing.T) {
 
 	want := new(scm.Hook)
 	raw, _ := ioutil.ReadFile("testdata/webhook.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -394,7 +509,7 @@ func TestRepositoryHookUpdate(t *testing.T) {
 
 	want := new(scm.Hook)
 	raw, _ := ioutil.ReadFile("testdata/webhook.json.golden")
-	json.Unmarshal(raw, want)
+	_ = json.Unmarshal(raw, want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/stash/stash.go b/scm/driver/stash/stash.go
index 3eada146e..62db1c7fd 100644
--- a/scm/driver/stash/stash.go
+++ b/scm/driver/stash/stash.go
@@ -9,7 +9,9 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
+	"fmt"
 	"io"
+	"mime/multipart"
 	"net/url"
 	"strings"
 
@@ -37,9 +39,11 @@ func New(uri string) (*scm.Client, error) {
 	client.Contents = &contentService{client}
 	client.Git = &gitService{client}
 	client.Issues = &issueService{client}
+	client.Milestones = &milestoneService{client}
 	client.Organizations = &organizationService{client}
 	client.PullRequests = &pullService{client}
 	client.Repositories = &repositoryService{client}
+	client.Releases = &releaseService{client}
 	client.Reviews = &reviewService{client}
 	client.Users = &userService{client}
 	client.Webhooks = &webhookService{client}
@@ -64,16 +68,42 @@ func (c *wrapper) do(ctx context.Context, method, path string, in, out interface
 	req := &scm.Request{
 		Method: method,
 		Path:   path,
+		Header: map[string][]string{
+			"Accept":            {"application/json"},
+			"x-atlassian-token": {"no-check"},
+		},
 	}
 	// if we are posting or putting data, we need to
 	// write it to the body of the request.
 	if in != nil {
-		buf := new(bytes.Buffer)
-		json.NewEncoder(buf).Encode(in)
-		req.Header = map[string][]string{
-			"Content-Type": {"application/json"},
+		switch content := in.(type) {
+		case *contentCreateUpdate:
+			// add the content to the multipart
+			var b bytes.Buffer
+			mw := &MultipartWriter{Writer: multipart.NewWriter(&b)}
+			// add the other fields
+			// The Write function doesn't write the string value to multipart if it is Empty
+			mw.Write("content", string(content.Content))
+			mw.Write("message", content.Message)
+			mw.Write("branch", content.Branch)
+			mw.Write("sourceCommitId", content.Sha)
+			if mw.Error != nil {
+				return nil, fmt.Errorf("error writing multipart-content. err: %s", mw.Error)
+			}
+			mw.Close()
+			// write the multipart response to the body
+			req.Body = &b
+			// write the content type that contains the length of the multipart
+			req.Header = map[string][]string{
+				"Content-Type":      {mw.FormDataContentType()},
+				"x-atlassian-token": {"no-check"},
+			}
+		default:
+			buf := new(bytes.Buffer)
+			json.NewEncoder(buf).Encode(in)
+			req.Header["Content-Type"] = []string{"application/json"}
+			req.Body = buf
 		}
-		req.Body = buf
 	}
 
 	// execute the http request
@@ -121,7 +151,9 @@ type pagination struct {
 
 // Error represents a Stash error.
 type Error struct {
-	Errors []struct {
+	Message string `json:"message"`
+	Status  int    `json:"status-code"`
+	Errors  []struct {
 		Message         string `json:"message"`
 		ExceptionName   string `json:"exceptionName"`
 		CurrentVersion  int    `json:"currentVersion"`
@@ -131,6 +163,9 @@ type Error struct {
 
 func (e *Error) Error() string {
 	if len(e.Errors) == 0 {
+		if len(e.Message) > 0 {
+			return fmt.Sprintf("bitbucket: status: %d message: %s", e.Status, e.Message)
+		}
 		return "bitbucket: undefined error"
 	}
 	return e.Errors[0].Message
diff --git a/scm/driver/stash/testdata/branch_create.json b/scm/driver/stash/testdata/branch_create.json
new file mode 100644
index 000000000..90f12b96a
--- /dev/null
+++ b/scm/driver/stash/testdata/branch_create.json
@@ -0,0 +1,10 @@
+{
+    "ref": "refs/heads/Hello",
+    "node_id": "MDM6UmVmMzQ0NDI5MDA5OnJlZnMvaGVhZHMvSGVsbG8=",
+    "url": "https://api.github.com/repos/tphoney/scm-test/git/refs/heads/Hello",
+    "object": {
+        "sha": "312797ba52425353dec56871a255e2a36fc96344",
+        "type": "commit",
+        "url": "https://api.github.com/repos/tphoney/scm-test/git/commits/312797ba52425353dec56871a255e2a36fc96344"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/branches_filter.json b/scm/driver/stash/testdata/branches_filter.json
new file mode 100644
index 000000000..f6b88ab8f
--- /dev/null
+++ b/scm/driver/stash/testdata/branches_filter.json
@@ -0,0 +1,24 @@
+{
+  "size": 1,
+  "limit": 25,
+  "isLastPage": true,
+  "values": [
+    {
+      "id": "refs/heads/master",
+      "displayId": "master",
+      "type": "BRANCH",
+      "latestCommit": "11ce869211917dd65610e70fcee454943b35ac6e",
+      "latestChangeset": "11ce869211917dd65610e70fcee454943b35ac6e",
+      "isDefault": true
+    },
+    {
+      "id": "refs/heads/master-patch",
+      "displayId": "master-patch",
+      "type": "BRANCH",
+      "latestCommit": "11ce869211917dd65610e70fcee454943b35ac6f",
+      "latestChangeset": "11ce869211917dd65610e70fcee454943b35ac6f",
+      "isDefault": true
+    }
+  ],
+  "start": 0
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/branches_filter.json.golden b/scm/driver/stash/testdata/branches_filter.json.golden
new file mode 100644
index 000000000..a158fbac2
--- /dev/null
+++ b/scm/driver/stash/testdata/branches_filter.json.golden
@@ -0,0 +1,12 @@
+[
+    {
+        "Name": "master",
+        "Path": "refs/heads/master",
+        "Sha": "11ce869211917dd65610e70fcee454943b35ac6e"
+    },
+   {
+        "Name": "master-patch",
+        "Path": "refs/heads/master-patch",
+        "Sha": "11ce869211917dd65610e70fcee454943b35ac6f"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/commits.json b/scm/driver/stash/testdata/commits.json
index 9e26dfeeb..ac9da334b 100644
--- a/scm/driver/stash/testdata/commits.json
+++ b/scm/driver/stash/testdata/commits.json
@@ -1 +1,72 @@
-{}
\ No newline at end of file
+{
+    "size": 1,
+    "limit": 25,
+    "isLastPage": true,
+    "values": [
+{
+    "id": "131cb13f4aed12e725177bc4b7c28db67839bf9f",
+    "displayId": "131cb13f4ae",
+    "author": {
+        "name": "jcitizen",
+        "emailAddress": "jane@example.com",
+        "id": 1,
+        "displayName": "Jane Citizen",
+        "active": true,
+        "slug": "jcitizen",
+        "type": "NORMAL",
+        "links": {
+            "self": [
+                {
+                    "href": "http://example.com:7990/users/jcitizen"
+                }
+            ]
+        }
+    },
+    "authorTimestamp": 1530720102000,
+    "committer": {
+        "name": "jcitizen",
+        "emailAddress": "jane@example.com",
+        "id": 1,
+        "displayName": "Jane Citizen",
+        "active": true,
+        "slug": "jcitizen",
+        "type": "NORMAL",
+        "links": {
+            "self": [
+                {
+                    "href": "http://example.com:7990/users/jcitizen"
+                }
+            ]
+        }
+    },
+    "committerTimestamp": 1530720102000,
+    "message": "update files",
+    "parents": [
+        {
+            "id": "4f4b0ef1714a5b6cafdaf2f53c7f5f5b38fb9348",
+            "displayId": "4f4b0ef1714",
+            "author": {
+                "name": "Jane Citizen",
+                "emailAddress": "jane@example.com"
+            },
+            "authorTimestamp": 1530719890000,
+            "committer": {
+                "name": "Jane Citizen",
+                "emailAddress": "jane@example.com"
+            },
+            "committerTimestamp": 1530719890000,
+            "message": "update files",
+            "parents": [
+                {
+                    "id": "f636fe22d302c852df1a68fff2d744039fe55b3d",
+                    "displayId": "f636fe22d30"
+                }
+            ]
+        }
+    ]
+}
+],
+"start": 0,
+"authorCount": 1,
+"totalCount": 1
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/commits.json.golden b/scm/driver/stash/testdata/commits.json.golden
index 0637a088a..33d1ffec4 100644
--- a/scm/driver/stash/testdata/commits.json.golden
+++ b/scm/driver/stash/testdata/commits.json.golden
@@ -1 +1,21 @@
-[]
\ No newline at end of file
+[
+    {
+        "Sha": "131cb13f4aed12e725177bc4b7c28db67839bf9f",
+        "Message": "update files",
+        "Author": {
+            "Name": "Jane Citizen",
+            "Email": "jane@example.com",
+            "Date": "2018-07-04T09:01:42-07:00",
+            "Login": "jcitizen",
+            "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+        },
+        "Committer": {
+            "Name": "Jane Citizen",
+            "Email": "jane@example.com",
+            "Date": "2018-07-04T09:01:42-07:00",
+            "Login": "jcitizen",
+            "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+        },
+        "Link": ""
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/content_create.json b/scm/driver/stash/testdata/content_create.json
new file mode 100644
index 000000000..7188f36c5
--- /dev/null
+++ b/scm/driver/stash/testdata/content_create.json
@@ -0,0 +1,21 @@
+{
+    "id": "abcdef0123abcdef4567abcdef8987abcdef6543",
+    "displayId": "abcdef0123a",
+    "author": {
+        "name": "charlie",
+        "emailAddress": "charlie@example.com"
+    },
+    "authorTimestamp": 1636089306104,
+    "committer": {
+        "name": "charlie",
+        "emailAddress": "charlie@example.com"
+    },
+    "committerTimestamp": 1636089306104,
+    "message": "WIP on feature 1",
+    "parents": [
+        {
+            "id": "abcdef0123abcdef4567abcdef8987abcdef6543",
+            "displayId": "abcdef0"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/content_list.json b/scm/driver/stash/testdata/content_list.json
index ec0447795..4d5f72873 100644
--- a/scm/driver/stash/testdata/content_list.json
+++ b/scm/driver/stash/testdata/content_list.json
@@ -27,7 +27,8 @@
     "testdata/branches.json.golden",
     "testdata/changes.json",
     "testdata/changes.json.golden",
-    "testdata/commit.json"
+    "testdata/commit.json",
+    "testdata/default_branch.json"
   ],
   "start": 0,
   "nextPageStart": 25
diff --git a/scm/driver/stash/testdata/content_list.json.golden b/scm/driver/stash/testdata/content_list.json.golden
index 72d6fcf30..f02920ac4 100644
--- a/scm/driver/stash/testdata/content_list.json.golden
+++ b/scm/driver/stash/testdata/content_list.json.golden
@@ -98,5 +98,9 @@
   {
     "path": "testdata/commit.json",
     "kind": "file"
+  },
+  {
+    "path": "testdata/default_branch.json",
+    "kind": "file"
   }
 ]
diff --git a/scm/driver/stash/testdata/content_update.json b/scm/driver/stash/testdata/content_update.json
new file mode 100644
index 000000000..7188f36c5
--- /dev/null
+++ b/scm/driver/stash/testdata/content_update.json
@@ -0,0 +1,21 @@
+{
+    "id": "abcdef0123abcdef4567abcdef8987abcdef6543",
+    "displayId": "abcdef0123a",
+    "author": {
+        "name": "charlie",
+        "emailAddress": "charlie@example.com"
+    },
+    "authorTimestamp": 1636089306104,
+    "committer": {
+        "name": "charlie",
+        "emailAddress": "charlie@example.com"
+    },
+    "committerTimestamp": 1636089306104,
+    "message": "WIP on feature 1",
+    "parents": [
+        {
+            "id": "abcdef0123abcdef4567abcdef8987abcdef6543",
+            "displayId": "abcdef0"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/default_branch.json b/scm/driver/stash/testdata/default_branch.json
new file mode 100644
index 000000000..ceeec302e
--- /dev/null
+++ b/scm/driver/stash/testdata/default_branch.json
@@ -0,0 +1,8 @@
+{
+    "id": "refs/heads/feature_branch",
+    "displayId": "feature_branch",
+    "type": "BRANCH",
+    "latestCommit": "c567b3f4a2980299e2a1148360f23ffb0f4c9764",
+    "latestChangeset": "c567b3f4a2980299e2a1148360f23ffb0f4c9764",
+    "isDefault": true
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/pr.json.golden b/scm/driver/stash/testdata/pr.json.golden
index 8ec0d5d2f..72e48a411 100644
--- a/scm/driver/stash/testdata/pr.json.golden
+++ b/scm/driver/stash/testdata/pr.json.golden
@@ -10,6 +10,16 @@
     "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/1",
     "Closed": false,
     "Merged": false,
+    "Base": {
+        "Name": "master",
+        "Path": "refs/heads/master",
+        "Sha": "5c64a07cd6c0f21b753bf261ef059c7e7633c50a"
+    },
+    "Head": {
+        "Name": "feature/x",
+        "Path": "refs/heads/feature/x",
+        "Sha": "131cb13f4aed12e725177bc4b7c28db67839bf9f"
+    },
     "Author": {
         "Login": "jcitizen",
         "Name": "Jane Citizen",
diff --git a/scm/driver/stash/testdata/prs.json.golden b/scm/driver/stash/testdata/prs.json.golden
index c9ede89d8..085a38788 100644
--- a/scm/driver/stash/testdata/prs.json.golden
+++ b/scm/driver/stash/testdata/prs.json.golden
@@ -11,6 +11,16 @@
         "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/1",
         "Closed": false,
         "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "5c64a07cd6c0f21b753bf261ef059c7e7633c50a"
+        },
+        "Head": {
+            "Name": "feature/x",
+            "Path": "refs/heads/feature/x",
+            "Sha": "131cb13f4aed12e725177bc4b7c28db67839bf9f"
+        },
         "Author": {
             "Login": "jcitizen",
             "Name": "Jane Citizen",
diff --git a/scm/driver/stash/testdata/repo.json.golden b/scm/driver/stash/testdata/repo.json.golden
index e7257362f..66414388a 100644
--- a/scm/driver/stash/testdata/repo.json.golden
+++ b/scm/driver/stash/testdata/repo.json.golden
@@ -3,7 +3,7 @@
     "Namespace": "PRJ",
     "Name": "my-repo",
     "Perm": null,
-    "Branch": "master",
+    "Branch": "feature_branch",
     "Private": true,
     "Clone": "http://example.com:7990/scm/prj/my-repo.git",
     "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git",
diff --git a/scm/driver/stash/testdata/repos.json b/scm/driver/stash/testdata/repos.json
index 46f119c9b..68bc40157 100644
--- a/scm/driver/stash/testdata/repos.json
+++ b/scm/driver/stash/testdata/repos.json
@@ -43,6 +43,47 @@
                     }
                 ]
             }
+        },
+        {
+            "slug": "quux",
+            "id": 2,
+            "name": "quux",
+            "scmId": "git",
+            "state": "AVAILABLE",
+            "statusMessage": "Available",
+            "forkable": true,
+            "project": {
+                "key": "PRJ",
+                "id": 2,
+                "name": "different_name",
+                "public": false,
+                "type": "NORMAL",
+                "links": {
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/projects/PRJ"
+                        }
+                    ]
+                }
+            },
+            "public": false,
+            "links": {
+                "clone": [
+                    {
+                        "href": "ssh://git@example.com:7999/prj/quux.git",
+                        "name": "ssh"
+                    },
+                    {
+                        "href": "http://jcitizen@example.com:7990/scm/prj/quux.git",
+                        "name": "http"
+                    }
+                ],
+                "self": [
+                    {
+                        "href": "http://example.com:7990/projects/PRJ/repos/quux/browse"
+                    }
+                ]
+            }
         }
     ],
     "start": 0
diff --git a/scm/driver/stash/testdata/repos.json.golden b/scm/driver/stash/testdata/repos.json.golden
index ef009866b..18e4d4018 100644
--- a/scm/driver/stash/testdata/repos.json.golden
+++ b/scm/driver/stash/testdata/repos.json.golden
@@ -11,5 +11,18 @@
         "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse",
         "Created": "0001-01-01T00:00:00Z",
         "Updated": "0001-01-01T00:00:00Z"
+    },
+   {
+        "ID": "2",
+        "Namespace": "PRJ",
+        "Name": "quux",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "http://example.com:7990/scm/prj/quux.git",
+        "CloneSSH": "ssh://git@example.com:7999/prj/quux.git",
+        "Link": "http://example.com:7990/projects/PRJ/repos/quux/browse",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
     }
 ]
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/repos_filter.json b/scm/driver/stash/testdata/repos_filter.json
new file mode 100644
index 000000000..80bb1c9a4
--- /dev/null
+++ b/scm/driver/stash/testdata/repos_filter.json
@@ -0,0 +1,49 @@
+{
+  "size": 25,
+  "limit": 25,
+  "isLastPage": false,
+  "values": [
+    {
+      "slug": "quux",
+      "id": 2,
+      "name": "quux",
+      "scmId": "git",
+      "state": "AVAILABLE",
+      "statusMessage": "Available",
+      "forkable": true,
+      "project": {
+        "key": "PRJ",
+        "id": 2,
+        "name": "different_name",
+        "public": false,
+        "type": "NORMAL",
+        "links": {
+          "self": [
+            {
+              "href": "http://example.com:7990/projects/PRJ"
+            }
+          ]
+        }
+      },
+      "public": false,
+      "links": {
+        "clone": [
+          {
+            "href": "ssh://git@example.com:7999/prj/quux.git",
+            "name": "ssh"
+          },
+          {
+            "href": "http://jcitizen@example.com:7990/scm/prj/quux.git",
+            "name": "http"
+          }
+        ],
+        "self": [
+          {
+            "href": "http://example.com:7990/projects/PRJ/repos/quux/browse"
+          }
+        ]
+      }
+    }
+  ],
+  "start": 0
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/repos_filter.json.golden b/scm/driver/stash/testdata/repos_filter.json.golden
new file mode 100644
index 000000000..34ff2307b
--- /dev/null
+++ b/scm/driver/stash/testdata/repos_filter.json.golden
@@ -0,0 +1,15 @@
+[
+   {
+        "ID": "2",
+        "Namespace": "PRJ",
+        "Name": "quux",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "http://example.com:7990/scm/prj/quux.git",
+        "CloneSSH": "ssh://git@example.com:7999/prj/quux.git",
+        "Link": "http://example.com:7990/projects/PRJ/repos/quux/browse",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    }
+]
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/webhooks/pr_declined.json.golden b/scm/driver/stash/testdata/webhooks/pr_declined.json.golden
index 828961b05..936b70c82 100644
--- a/scm/driver/stash/testdata/webhooks/pr_declined.json.golden
+++ b/scm/driver/stash/testdata/webhooks/pr_declined.json.golden
@@ -25,6 +25,16 @@
         "Link": "",
         "Closed": true,
         "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "823b2230a56056231c9425d63758fa87078a66b4"
+        },
+        "Head": {
+            "Name": "develop",
+            "Path": "refs/heads/develop",
+            "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56"
+        },
         "Author": {
             "Login": "jcitizen",
             "Name": "Jane Citizen",
diff --git a/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden b/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden
index 9e26dfeeb..7852bd44a 100644
--- a/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden
+++ b/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden
@@ -1 +1,53 @@
-{}
\ No newline at end of file
+{
+    "Action": "closed",
+    "Repo": {
+        "ID": "1",
+        "Namespace": "PRJ",
+        "Name": "my-repo",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "",
+        "CloneSSH": "",
+        "Link": "",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 2,
+        "Title": "added LICENSE",
+        "Body": "added BSD license text",
+        "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56",
+        "Ref": "refs/pull-requests/2/from",
+        "Source": "develop",
+        "Target": "master",
+        "Fork": "PRJ/my-repo",
+        "Link": "",
+        "Closed": true,
+        "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "823b2230a56056231c9425d63758fa87078a66b4"
+        },
+        "Head": {
+            "Name": "develop",
+            "Path": "refs/heads/develop",
+            "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56"
+        },
+        "Author": {
+            "Login": "jcitizen",
+            "Name": "Jane Citizen",
+            "Email": "jane@example.com",
+            "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+        },
+        "Created": "2018-07-05T12:21:30-07:00",
+        "Updated": "2018-07-05T12:30:48-07:00"
+    },
+    "Sender": {
+        "Login": "jcitizen",
+        "Name": "Jane Citizen",
+        "Email": "jane@example.com",
+        "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+    }
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json
new file mode 100644
index 000000000..aaf938392
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json
@@ -0,0 +1,159 @@
+{
+    "eventKey": "pr:from_ref_updated",
+    "date": "2021-11-23T10:02:52+0000",
+    "actor": {
+        "name": "jcitizen",
+        "emailAddress": "jane@example.com",
+        "id": 1,
+        "displayName": "Jane Citizen",
+        "active": true,
+        "slug": "jcitizen",
+        "type": "NORMAL",
+        "links": {
+            "self": [
+                {
+                    "href": "http://example.com:7990/users/jcitizen"
+                }
+            ]
+        }
+    },
+    "pullRequest": {
+        "id": 2,
+        "version": 0,
+        "title": "added LICENSE",
+        "description": "added BSD license text",
+        "state": "OPEN",
+        "open": true,
+        "closed": false,
+        "createdDate": 1637591800463,
+        "updatedDate": 1637661772258,
+        "fromRef": {
+            "id": "refs/heads/feature-2",
+            "displayId": "feature-2",
+            "latestCommit": "c00d0f269a9844dd168f72f6a9d78a9cff49a318",
+            "type": "BRANCH",
+            "repository": {
+                "slug": "my-repo",
+                "id": 1,
+                "name": "my-repo",
+                "hierarchyId": "5aa23c1a1280b0c0acce",
+                "scmId": "git",
+                "state": "AVAILABLE",
+                "statusMessage": "Available",
+                "forkable": true,
+                "project": {
+                    "key": "PRJ",
+                    "id": 2,
+                    "name": "PRJ",
+                    "public": false,
+                    "type": "NORMAL",
+                    "links": {
+                        "self": [
+                            {
+                                "href": "http://example.com:7990/projects/PRJ"
+                            }
+                        ]
+                    }
+                },
+                "public": false,
+                "links": {
+                    "clone": [
+                        {
+                            "href": "http://example.com:7990/scm/prj/my-repo.git",
+                            "name": "http"
+                        },
+                        {
+                            "href": "ssh://git@example.com:7999/prj/my-repo.git",
+                            "name": "ssh"
+                        }
+                    ],
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse"
+                        }
+                    ]
+                }
+            }
+        },
+        "toRef": {
+            "id": "refs/heads/master",
+            "displayId": "master",
+            "type": "BRANCH",
+            "latestCommit": "823b2230a56056231c9425d63758fa87078a66b4",
+            "repository": {
+                "slug": "my-repo",
+                "id": 1,
+                "name": "my-repo",
+                "hierarchyId": "5aa23c1a1280b0c0acce",
+                "scmId": "git",
+                "state": "AVAILABLE",
+                "statusMessage": "Available",
+                "forkable": true,
+                "project": {
+                    "key": "PRJ",
+                    "id": 2,
+                    "name": "PRJ",
+                    "public": false,
+                    "type": "NORMAL",
+                    "links": {
+                        "self": [
+                            {
+                                "href": "http://example.com:7990/projects/PRJ"
+                            }
+                        ]
+                    }
+                },
+                "public": false,
+                "links": {
+                    "clone": [
+                        {
+                            "href": "http://example.com:7990/scm/prj/my-repo.git",
+                            "name": "http"
+                        },
+                        {
+                            "href": "ssh://git@example.com:7999/prj/my-repo.git",
+                            "name": "ssh"
+                        }
+                    ],
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse"
+                        }
+                    ]
+                }
+            }
+        },
+        "locked": false,
+        "author": {
+            "user": {
+                "name": "jcitizen",
+                "emailAddress": "jane@example.com",
+                "id": 1,
+                "displayName": "Jane Citizen",
+                "active": true,
+                "slug": "jcitizen",
+                "type": "NORMAL",
+                "links": {
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/users/jcitizen"
+                        }
+                    ]
+                }
+            },
+            "role": "AUTHOR",
+            "approved": false,
+            "status": "UNAPPROVED"
+        },
+        "reviewers": [],
+        "participants": [],
+        "links": {
+            "self": [
+                {
+                    "href": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2"
+                }
+            ]
+        }
+    },
+    "previousFromHash": "caf52ccb18c039cd9b930ed1f7b3edfb34cb92dd"
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden
new file mode 100644
index 000000000..ce48bb948
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden
@@ -0,0 +1,53 @@
+{
+    "Action": "synchronized",
+    "Repo": {
+        "ID": "1",
+        "Namespace": "PRJ",
+        "Name": "my-repo",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "http://example.com:7990/scm/prj/my-repo.git",
+        "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git",
+        "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 2,
+        "Title": "added LICENSE",
+        "Body": "added BSD license text",
+        "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318",
+        "Ref": "refs/pull-requests/2/from",
+        "Source": "feature-2",
+        "Target": "master",
+        "Fork": "PRJ/my-repo",
+        "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2",
+        "Closed": false,
+        "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "823b2230a56056231c9425d63758fa87078a66b4"
+        },
+        "Head": {
+            "Name": "feature-2",
+            "Path": "refs/heads/feature-2",
+            "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318"
+        },
+        "Author": {
+            "Login": "jcitizen",
+            "Name": "Jane Citizen",
+            "Email": "jane@example.com",
+            "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+        },
+        "Created": "2021-11-22T14:36:40Z",
+        "Updated": "2021-11-23T10:02:52Z"
+    },
+    "Sender": {
+        "Login": "jcitizen",
+        "Name": "Jane Citizen",
+        "Email": "jane@example.com",
+        "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+    }
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_merged.json.golden b/scm/driver/stash/testdata/webhooks/pr_merged.json.golden
index 7f15cd419..2b3367535 100644
--- a/scm/driver/stash/testdata/webhooks/pr_merged.json.golden
+++ b/scm/driver/stash/testdata/webhooks/pr_merged.json.golden
@@ -18,6 +18,7 @@
         "Title": "Develop",
         "Body": "* added LICENSE\r\n* add COPYING file",
         "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56",
+        "Merge": "83f836ca538dc4f43d29fda46a5b85ea49f1b8e7",
         "Ref": "refs/pull-requests/3/from",
         "Source": "develop",
         "Target": "master",
@@ -25,6 +26,16 @@
         "Link": "",
         "Closed": true,
         "Merged": true,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "823b2230a56056231c9425d63758fa87078a66b4"
+        },
+        "Head": {
+            "Name": "develop",
+            "Path": "refs/heads/develop",
+            "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56"
+        },
         "Author": {
             "Login": "jcitizen",
             "Name": "Jane Citizen",
diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_meta.json b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json
new file mode 100644
index 000000000..a44e432b7
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json
@@ -0,0 +1,224 @@
+{
+  "eventKey": "pr:modified",
+  "date": "2021-11-24T14:43:48+0000",
+  "actor": {
+    "name": "jcitizen",
+    "emailAddress": "jane@example.com",
+    "id": 1,
+    "displayName": "Jane Citizen",
+    "active": true,
+    "slug": "jcitizen",
+    "type": "NORMAL",
+    "links": {
+      "self": [
+        {
+          "href": "http://example.com:7990/users/jcitizen"
+        }
+      ]
+    }
+  },
+  "pullRequest": {
+    "id": 6,
+    "version": 1,
+    "title": "Clean README.md",
+    "description": "Removed old information out of `REAME.nd`.",
+    "state": "OPEN",
+    "open": true,
+    "closed": false,
+    "createdDate": 1637764009216,
+    "updatedDate": 1637765028112,
+    "fromRef": {
+      "id": "refs/heads/cleanup",
+      "displayId": "cleanup",
+      "latestCommit": "a268fca7b65055e13f60ac1a2c9a527615f113ca",
+      "type": "BRANCH",
+      "repository": {
+        "slug": "my-repo",
+        "id": 12,
+        "name": "My Repo",
+        "hierarchyId": "5aa23c1a1280b0c0acce",
+        "scmId": "git",
+        "state": "AVAILABLE",
+        "statusMessage": "Available",
+        "forkable": true,
+        "origin": {
+          "slug": "my-repo",
+          "id": 1,
+          "name": "My Repo",
+          "hierarchyId": "5aa23c1a1280b0c0acce",
+          "scmId": "git",
+          "state": "AVAILABLE",
+          "statusMessage": "Available",
+          "forkable": true,
+          "project": {
+            "key": "PRJ",
+            "id": 2,
+            "name": "Project",
+            "public": false,
+            "type": "NORMAL",
+            "links": {
+              "self": [
+                {
+                  "href": "http://example.com:7990/projects/PRJ"
+                }
+              ]
+            }
+          },
+          "public": false,
+          "links": {
+            "clone": [
+              {
+                "href": "ssh://git@example.com:7999/prj/my-repo.git",
+                "name": "ssh"
+              },
+              {
+                "href": "http://example.com:7990/scm/prj/my-repo.git",
+                "name": "http"
+              }
+            ],
+            "self": [
+              {
+                "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse"
+              }
+            ]
+          }
+        },
+        "project": {
+          "key": "~jcitizen",
+          "id": 1,
+          "name": "Jane Citizen",
+          "type": "PERSONAL",
+          "owner": {
+            "name": "jcitizen",
+            "emailAddress": "jane@example.com",
+            "id": 2,
+            "displayName": "Jane Citizen",
+            "active": true,
+            "slug": "jcitizen",
+            "type": "NORMAL",
+            "links": {
+              "self": [
+                {
+                  "href": "http://example.com:7990/users/jcitizen"
+                }
+              ]
+            }
+          },
+          "links": {
+            "self": [
+              {
+                "href": "http://example.com:7990/users/jcitizen"
+              }
+            ]
+          }
+        },
+        "public": false,
+        "links": {
+          "clone": [
+            {
+              "href": "http://example.com:7990/scm/~jcitizen/my-repo.git",
+              "name": "http"
+            },
+            {
+              "href": "ssh://git@example.com:7999/~jcitizen/my-repo.git",
+              "name": "ssh"
+            }
+          ],
+          "self": [
+            {
+              "href": "http://example.com:7990/users/jcitizen/repos/my-repo/browse"
+            }
+          ]
+        }
+      }
+    },
+    "toRef": {
+      "id": "refs/heads/master",
+      "displayId": "master",
+      "latestCommit": "a4dae7792cf772d72149730091141ef48bba8697",
+      "type": "BRANCH",
+      "repository": {
+        "slug": "my-repo",
+        "id": 1,
+        "name": "My Repo",
+        "hierarchyId": "5aa23c1a1280b0c0acce",
+        "scmId": "git",
+        "state": "AVAILABLE",
+        "statusMessage": "Available",
+        "forkable": true,
+        "project": {
+          "key": "PRJ",
+          "id": 2,
+          "name": "Project",
+          "public": false,
+          "type": "NORMAL",
+          "links": {
+            "self": [
+              {
+                "href": "http://example.com:7990/projects/PRJ"
+              }
+            ]
+          }
+        },
+        "public": false,
+        "links": {
+          "clone": [
+            {
+              "href": "ssh://git@example.com:7999/prj/my-repo.git",
+              "name": "ssh"
+            },
+            {
+              "href": "http://example.com:7990/scm/prj/my-repo.git",
+              "name": "http"
+            }
+          ],
+          "self": [
+            {
+              "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse"
+            }
+          ]
+        }
+      }
+    },
+    "locked": false,
+    "author": {
+      "user": {
+        "name": "jcitizen",
+        "emailAddress": "jane@example.com",
+        "id": 1,
+        "displayName": "Jane Citizen",
+        "active": true,
+        "slug": "jcitizen",
+        "type": "NORMAL",
+        "links": {
+          "self": [
+            {
+              "href": "http://example.com:7990/users/jcitizen"
+            }
+          ]
+        }
+      },
+      "role": "AUTHOR",
+      "approved": false,
+      "status": "UNAPPROVED"
+    },
+    "reviewers": [],
+    "participants": [],
+    "links": {
+      "self": [
+        {
+          "href": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/6"
+        }
+      ]
+    }
+  },
+  "previousTitle": "Clean README.md",
+  "previousDescription": "Cleaned old info out of REAME.",
+  "previousTarget": {
+    "id": "refs/heads/master",
+    "displayId": "master",
+    "type": "BRANCH",
+    "latestCommit": "a4dae7792cf772d72149730091141ef48bba8697",
+    "latestChangeset": "a4dae7792cf772d72149730091141ef48bba8697"
+  }
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden
new file mode 100644
index 000000000..d901de8cc
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden
@@ -0,0 +1,53 @@
+{
+    "Action": "updated",
+    "Repo": {
+        "ID": "1",
+        "Namespace": "PRJ",
+        "Name": "my-repo",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "http://example.com:7990/scm/prj/my-repo.git",
+        "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git",
+        "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 6,
+        "Title": "Clean README.md",
+        "Body": "Removed old information out of `REAME.nd`.",
+        "Sha": "a268fca7b65055e13f60ac1a2c9a527615f113ca",
+        "Ref": "refs/pull-requests/6/from",
+        "Source": "cleanup",
+        "Target": "master",
+        "Fork": "~jcitizen/my-repo",
+        "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/6",
+        "Closed": false,
+        "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "a4dae7792cf772d72149730091141ef48bba8697"
+        },
+        "Head": {
+            "Name": "cleanup",
+            "Path": "refs/heads/cleanup",
+            "Sha": "a268fca7b65055e13f60ac1a2c9a527615f113ca"
+        },
+        "Author": {
+            "Login": "jcitizen",
+            "Name": "Jane Citizen",
+            "Email": "jane@example.com",
+            "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+        },
+        "Created": "2021-11-24T14:26:49Z",
+        "Updated": "2021-11-24T14:43:48Z"
+    },
+    "Sender": {
+        "Login": "jcitizen",
+        "Name": "Jane Citizen",
+        "Email": "jane@example.com",
+        "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+    }
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_toref.json b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json
new file mode 100644
index 000000000..ff33036c6
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json
@@ -0,0 +1,167 @@
+{
+    "eventKey": "pr:modified",
+    "date": "2021-11-23T13:16:20+0000",
+    "actor": {
+        "name": "jcitizen",
+        "emailAddress": "jane@example.com",
+        "id": 1,
+        "displayName": "Jane Citizen",
+        "active": true,
+        "slug": "jcitizen",
+        "type": "NORMAL",
+        "links": {
+            "self": [
+                {
+                    "href": "http://example.com:7990/users/jcitizen"
+                }
+            ]
+        }
+    },
+    "pullRequest": {
+        "id": 2,
+        "version": 0,
+        "title": "added LICENSE",
+        "description": "added BSD license text",
+        "state": "OPEN",
+        "open": true,
+        "closed": false,
+        "createdDate": 1637591800463,
+        "updatedDate": 1637673380977,
+        "fromRef": {
+            "id": "refs/heads/feature-2",
+            "displayId": "feature-2",
+            "latestCommit": "c00d0f269a9844dd168f72f6a9d78a9cff49a318",
+            "type": "BRANCH",
+            "repository": {
+                "slug": "my-repo",
+                "id": 1,
+                "name": "my-repo",
+                "hierarchyId": "5aa23c1a1280b0c0acce",
+                "scmId": "git",
+                "state": "AVAILABLE",
+                "statusMessage": "Available",
+                "forkable": true,
+                "project": {
+                    "key": "PRJ",
+                    "id": 2,
+                    "name": "PRJ",
+                    "public": false,
+                    "type": "NORMAL",
+                    "links": {
+                        "self": [
+                            {
+                                "href": "http://example.com:7990/projects/PRJ"
+                            }
+                        ]
+                    }
+                },
+                "public": false,
+                "links": {
+                    "clone": [
+                        {
+                            "href": "http://example.com:7990/scm/prj/my-repo.git",
+                            "name": "http"
+                        },
+                        {
+                            "href": "ssh://git@example.com:7999/prj/my-repo.git",
+                            "name": "ssh"
+                        }
+                    ],
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse"
+                        }
+                    ]
+                }
+            }
+        },
+        "toRef": {
+            "id": "refs/heads/master",
+            "displayId": "master",
+            "latestCommit": "0667def9e377a8fa76ce67b6d4e53e21b246f030",
+            "type": "BRANCH",
+            "repository": {
+                "slug": "my-repo",
+                "id": 1,
+                "name": "my-repo",
+                "hierarchyId": "5aa23c1a1280b0c0acce",
+                "scmId": "git",
+                "state": "AVAILABLE",
+                "statusMessage": "Available",
+                "forkable": true,
+                "project": {
+                    "key": "PRJ",
+                    "id": 2,
+                    "name": "PRJ",
+                    "public": false,
+                    "type": "NORMAL",
+                    "links": {
+                        "self": [
+                            {
+                                "href": "http://example.com:7990/projects/PRJ"
+                            }
+                        ]
+                    }
+                },
+                "public": false,
+                "links": {
+                    "clone": [
+                        {
+                            "href": "http://example.com:7990/scm/prj/my-repo.git",
+                            "name": "http"
+                        },
+                        {
+                            "href": "ssh://git@example.com:7999/prj/my-repo.git",
+                            "name": "ssh"
+                        }
+                    ],
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse"
+                        }
+                    ]
+                }
+            }
+        },
+        "locked": false,
+        "author": {
+            "user": {
+                "name": "jcitizen",
+                "emailAddress": "jane@example.com",
+                "id": 1,
+                "displayName": "Jane Citizen",
+                "active": true,
+                "slug": "jcitizen",
+                "type": "NORMAL",
+                "links": {
+                    "self": [
+                        {
+                            "href": "http://example.com:7990/users/jcitizen"
+                        }
+                    ]
+                }
+            },
+            "role": "AUTHOR",
+            "approved": false,
+            "status": "UNAPPROVED"
+        },
+        "reviewers": [],
+        "participants": [],
+        "links": {
+            "self": [
+                {
+                    "href": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2"
+                }
+            ]
+        }
+    },
+    "previousTitle": "old title",
+    "previousDescription": "old description",
+    "previousTarget": {
+        "id": "refs/heads/wrong",
+        "displayId": "wrong",
+        "type": "BRANCH",
+        "latestCommit": "2bbad782fd5453ded95fc5ce8b16839ff10615e0",
+        "latestChangeset": "2bbad782fd5453ded95fc5ce8b16839ff10615e0"
+    }
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden
new file mode 100644
index 000000000..c7d808d75
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden
@@ -0,0 +1,53 @@
+{
+    "Action": "synchronized",
+    "Repo": {
+        "ID": "1",
+        "Namespace": "PRJ",
+        "Name": "my-repo",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "http://example.com:7990/scm/prj/my-repo.git",
+        "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git",
+        "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "PullRequest": {
+        "Number": 2,
+        "Title": "added LICENSE",
+        "Body": "added BSD license text",
+        "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318",
+        "Ref": "refs/pull-requests/2/from",
+        "Source": "feature-2",
+        "Target": "master",
+        "Fork": "PRJ/my-repo",
+        "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2",
+        "Closed": false,
+        "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "0667def9e377a8fa76ce67b6d4e53e21b246f030"
+        },
+        "Head": {
+            "Name": "feature-2",
+            "Path": "refs/heads/feature-2",
+            "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318"
+        },
+        "Author": {
+            "Login": "jcitizen",
+            "Name": "Jane Citizen",
+            "Email": "jane@example.com",
+            "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+        },
+        "Created": "2021-11-22T14:36:40Z",
+        "Updated": "2021-11-23T13:16:20Z"
+    },
+    "Sender": {
+        "Login": "jcitizen",
+        "Name": "Jane Citizen",
+        "Email": "jane@example.com",
+        "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg"
+    }
+}
diff --git a/scm/driver/stash/testdata/webhooks/pr_open.json.golden b/scm/driver/stash/testdata/webhooks/pr_open.json.golden
index 5d29c8f49..cfed1a2ed 100644
--- a/scm/driver/stash/testdata/webhooks/pr_open.json.golden
+++ b/scm/driver/stash/testdata/webhooks/pr_open.json.golden
@@ -25,6 +25,16 @@
         "Link": "",
         "Closed": false,
         "Merged": false,
+        "Base": {
+            "Name": "master",
+            "Path": "refs/heads/master",
+            "Sha": "823b2230a56056231c9425d63758fa87078a66b4"
+        },
+        "Head": {
+            "Name": "develop",
+            "Path": "refs/heads/develop",
+            "Sha": "208b0a5c05eddadad01f2aed8802fe0c3b3eaf5e"
+        },
         "Author": {
             "Login": "jcitizen",
             "Name": "Jane Citizen",
diff --git a/scm/driver/stash/testdata/webhooks/push_v5.json b/scm/driver/stash/testdata/webhooks/push_v5.json
new file mode 100644
index 000000000..444d2ff44
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/push_v5.json
@@ -0,0 +1,44 @@
+{
+    "eventKey": "repo:refs_changed",
+    "date": "2023-05-30T15:40:47-0400",
+    "actor": {
+        "name": "trangineni",
+        "emailAddress": "tanuja.rangineni@transamerica.com",
+        "id": 39725,
+        "displayName": "Rangineni, Tanuja",
+        "active": true,
+        "slug": "trangineni",
+        "type": "NORMAL"
+    },
+    "repository": {
+        "slug": "transamerica.cloud.individual.tftest",
+        "id": 13027,
+        "name": "transamerica.cloud.individual.tftest",
+        "scmId": "git",
+        "state": "AVAILABLE",
+        "statusMessage": "Available",
+        "forkable": true,
+        "project": {
+            "key": "ISCLOUD",
+            "id": 1453,
+            "name": "Individual Cloud",
+            "description": "Project for Individual Solutions application terraform.",
+            "public": false,
+            "type": "NORMAL"
+        },
+        "public": false
+    },
+    "changes": [
+        {
+            "ref": {
+                "id": "refs/heads/trangineni/devtfvars-1685475548410",
+                "displayId": "trangineni/devtfvars-1685475548410",
+                "type": "BRANCH"
+            },
+            "refId": "refs/heads/trangineni/devtfvars-1685475548410",
+            "fromHash": "b2b710209761a3fab9fc867aad7b7725fd0fd028",
+            "toHash": "51a33664b6d7fb5aa15063064eb230362cd02e9e",
+            "type": "UPDATE"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/scm/driver/stash/testdata/webhooks/push_v5.json.golden b/scm/driver/stash/testdata/webhooks/push_v5.json.golden
new file mode 100644
index 000000000..ffe8c4c87
--- /dev/null
+++ b/scm/driver/stash/testdata/webhooks/push_v5.json.golden
@@ -0,0 +1,50 @@
+{
+    "Ref": "refs/heads/trangineni/devtfvars-1685475548410",
+    "After": "51a33664b6d7fb5aa15063064eb230362cd02e9e",
+    "Before": "b2b710209761a3fab9fc867aad7b7725fd0fd028",
+    "Repo": {
+        "ID": "13027",
+        "Namespace": "ISCLOUD",
+        "Name": "transamerica.cloud.individual.tftest",
+        "Perm": null,
+        "Branch": "master",
+        "Private": true,
+        "Clone": "",
+        "CloneSSH": "",
+        "Link": "",
+        "Created": "0001-01-01T00:00:00Z",
+        "Updated": "0001-01-01T00:00:00Z"
+    },
+    "Commit": {
+        "Sha": "51a33664b6d7fb5aa15063064eb230362cd02e9e",
+        "Message": "",
+        "Author": {
+            "Name": "Rangineni, Tanuja",
+            "Email": "tanuja.rangineni@transamerica.com",
+            "Date": "0001-01-01T00:00:00Z",
+            "Login": "trangineni",
+            "Avatar": "https://www.gravatar.com/avatar/1c0ba1559ee8ed291204dad48479149b.jpg"
+        },
+        "Committer": {
+            "Name": "Rangineni, Tanuja",
+            "Email": "tanuja.rangineni@transamerica.com",
+            "Date": "0001-01-01T00:00:00Z",
+            "Login": "trangineni",
+            "Avatar": "https://www.gravatar.com/avatar/1c0ba1559ee8ed291204dad48479149b.jpg"
+        },
+        "Link": ""
+    },
+    "Commits": [
+        {
+            "Sha": "51a33664b6d7fb5aa15063064eb230362cd02e9e",
+            "Message": "",
+            "Link": ""
+        }
+    ], 
+    "Sender": {
+        "Login": "trangineni",
+        "Name": "Rangineni, Tanuja",
+        "Email": "tanuja.rangineni@transamerica.com",
+        "Avatar": "https://www.gravatar.com/avatar/1c0ba1559ee8ed291204dad48479149b.jpg"
+    }
+}
\ No newline at end of file
diff --git a/scm/driver/stash/user.go b/scm/driver/stash/user.go
index 4b7fa5b8a..aeab588be 100644
--- a/scm/driver/stash/user.go
+++ b/scm/driver/stash/user.go
@@ -20,7 +20,7 @@ type userService struct {
 }
 
 func (s *userService) Find(ctx context.Context) (*scm.User, *scm.Response, error) {
-	path := fmt.Sprintf("plugins/servlet/applinks/whoami")
+	path := "plugins/servlet/applinks/whoami"
 	out := new(bytes.Buffer)
 	res, err := s.client.do(ctx, "GET", path, nil, out)
 	if err != nil {
@@ -78,6 +78,10 @@ func (s *userService) FindEmail(ctx context.Context) (string, *scm.Response, err
 	return email, res, err
 }
 
+func (s *userService) ListEmail(context.Context, scm.ListOptions) ([]*scm.Email, *scm.Response, error) {
+	return nil, nil, scm.ErrNotSupported
+}
+
 type user struct {
 	Name         string `json:"name"`
 	EmailAddress string `json:"emailAddress"`
diff --git a/scm/driver/stash/user_test.go b/scm/driver/stash/user_test.go
index 9e05e4d31..ac4df21ad 100644
--- a/scm/driver/stash/user_test.go
+++ b/scm/driver/stash/user_test.go
@@ -39,7 +39,7 @@ func TestUserFind(t *testing.T) {
 
 	want := new(scm.User)
 	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -82,7 +82,7 @@ func TestUserLoginFind(t *testing.T) {
 
 	want := new(scm.User)
 	raw, _ := ioutil.ReadFile("testdata/user.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
@@ -117,7 +117,7 @@ func TestUserLoginFind_ViaSearch(t *testing.T) {
 
 	want := new(scm.User)
 	raw, _ := ioutil.ReadFile("testdata/user_search.json.golden")
-	json.Unmarshal(raw, &want)
+	_ = json.Unmarshal(raw, &want)
 
 	if diff := cmp.Diff(got, want); diff != "" {
 		t.Errorf("Unexpected Results")
diff --git a/scm/driver/stash/util.go b/scm/driver/stash/util.go
index 64e982ba2..5f56e21ed 100644
--- a/scm/driver/stash/util.go
+++ b/scm/driver/stash/util.go
@@ -12,6 +12,10 @@ import (
 	"github.com/drone/go-scm/scm"
 )
 
+const (
+	defaultLimit = 25
+)
+
 func encodeListOptions(opts scm.ListOptions) string {
 	params := url.Values{}
 	if opts.Page > 1 {
@@ -25,6 +29,40 @@ func encodeListOptions(opts scm.ListOptions) string {
 	return params.Encode()
 }
 
+func encodeListOptionsV2(opts scm.ListOptions) string {
+	params := url.Values{}
+	limit := defaultLimit
+	if opts.Size != 0 {
+		limit = opts.Size
+	}
+	params.Set("limit", strconv.Itoa(limit))
+
+	if opts.Page > 0 {
+		params.Set("start", strconv.Itoa(
+			(opts.Page-1)*limit),
+		)
+	}
+	return params.Encode()
+}
+
+func encodeBranchListOptions(opts scm.BranchListOptions) string {
+	params := url.Values{}
+	if opts.SearchTerm != "" {
+		params.Set("filterText", opts.SearchTerm)
+	}
+	if opts.PageListOptions != (scm.ListOptions{}) {
+		if opts.PageListOptions.Page > 1 {
+			params.Set("start", strconv.Itoa(
+				(opts.PageListOptions.Page-1)*opts.PageListOptions.Size),
+			)
+		}
+		if opts.PageListOptions.Size != 0 {
+			params.Set("limit", strconv.Itoa(opts.PageListOptions.Size))
+		}
+	}
+	return params.Encode()
+}
+
 func encodeListRoleOptions(opts scm.ListOptions) string {
 	params := url.Values{}
 	if opts.Page > 1 {
@@ -39,6 +77,27 @@ func encodeListRoleOptions(opts scm.ListOptions) string {
 	return params.Encode()
 }
 
+func encodeRepoListOptions(opts scm.RepoListOptions) string {
+	params := url.Values{}
+	if opts.RepoSearchTerm != (scm.RepoSearchTerm{}) {
+		if opts.RepoSearchTerm.RepoName != "" {
+			params.Set("name", opts.RepoSearchTerm.RepoName)
+		}
+	}
+	if opts.ListOptions != (scm.ListOptions{}) {
+		if opts.ListOptions.Page > 1 {
+			params.Set("start", strconv.Itoa(
+				(opts.ListOptions.Page-1)*opts.ListOptions.Size),
+			)
+		}
+		if opts.ListOptions.Size != 0 {
+			params.Set("limit", strconv.Itoa(opts.ListOptions.Size))
+		}
+	}
+	params.Set("permission", "REPO_READ")
+	return params.Encode()
+}
+
 func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
 	params := url.Values{}
 	if opts.Page > 1 {
@@ -52,7 +111,7 @@ func encodePullRequestListOptions(opts scm.PullRequestListOptions) string {
 	if opts.Open && opts.Closed {
 		params.Set("state", "all")
 	} else if opts.Closed {
-		params.Set("state", "closed")
+		params.Set("state", "declined")
 	}
 	return params.Encode()
 }
diff --git a/scm/driver/stash/util_test.go b/scm/driver/stash/util_test.go
index 86e2e331d..d9e86101c 100644
--- a/scm/driver/stash/util_test.go
+++ b/scm/driver/stash/util_test.go
@@ -32,6 +32,28 @@ func Test_encodeListOptions(t *testing.T) {
 	}
 }
 
+func Test_encodeListOptionsV2(t *testing.T) {
+	tests := []struct {
+		page int
+		size int
+		text string
+	}{
+		{page: 0, size: 30, text: "limit=30"},
+		{page: 1, size: 30, text: "limit=30&start=0"},
+		{page: 5, size: 30, text: "limit=30&start=120"},
+		{page: 2, size: 5, text: "limit=5&start=5"},
+	}
+	for _, test := range tests {
+		opts := scm.ListOptions{
+			Page: test.page,
+			Size: test.size,
+		}
+		if got, want := encodeListOptionsV2(opts), test.text; got != want {
+			t.Errorf("Want encoded list options %q, got %q", want, got)
+		}
+	}
+}
+
 func Test_encodePullRequestListOptions(t *testing.T) {
 	t.Parallel()
 	opts := scm.PullRequestListOptions{
diff --git a/scm/driver/stash/webhook.go b/scm/driver/stash/webhook.go
index 84ef85cb1..31c130638 100644
--- a/scm/driver/stash/webhook.go
+++ b/scm/driver/stash/webhook.go
@@ -21,7 +21,6 @@ import (
 // TODO(bradrydzewski) push hook does not include repository git+http link
 // TODO(bradrydzewski) push hook does not include repository git+ssh link
 // TODO(bradrydzewski) push hook does not include repository html link
-// TODO(bradrydzewski) missing pull request synchrnoized webhook. See https://jira.atlassian.com/browse/BSERV-10279
 // TODO(bradrydzewski) pr hook does not include repository git+http link
 // TODO(bradrydzewski) pr hook does not include repository git+ssh link
 // TODO(bradrydzewski) pr hook does not include repository html link
@@ -42,7 +41,7 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
 	switch req.Header.Get("X-Event-Key") {
 	case "repo:refs_changed":
 		hook, err = s.parsePushHook(data)
-	case "pr:opened", "pr:declined", "pr:merged":
+	case "pr:opened", "pr:from_ref_updated", "pr:modified", "pr:declined", "pr:deleted", "pr:merged":
 		hook, err = s.parsePullRequest(data)
 	}
 	if err != nil {
@@ -100,8 +99,25 @@ func (s *webhookService) parsePullRequest(data []byte) (scm.Webhook, error) {
 	switch src.EventKey {
 	case "pr:opened":
 		dst.Action = scm.ActionOpen
+	case "pr:from_ref_updated":
+		// The request includes field "previousFromHash", which could be compared
+		// to the FromRef.Latestcommit to ensure there is actually a change,
+		// but there is unlikely need for that.
+		dst.Action = scm.ActionSync
+	case "pr:modified":
+		// BitBucket Server (Stash) sends "pr:modified" for any edits to the PR,
+		// including edits to the title or description. Thus, return the hook
+		// action only when the target reference has changed (name or hash).
+		if src.PullRequest.ToRef.DisplayID == src.PreviousTarget.DisplayID &&
+		src.PullRequest.ToRef.LatestCommit == src.PreviousTarget.LatestCommit {
+			dst.Action = scm.ActionUpdate
+		} else {
+			dst.Action = scm.ActionSync
+		}
 	case "pr:declined":
 		dst.Action = scm.ActionClose
+	case "pr:deleted":
+		dst.Action = scm.ActionClose
 	case "pr:merged":
 		dst.Action = scm.ActionMerge
 	default:
@@ -127,6 +143,16 @@ type pullRequestHook struct {
 	Date        string `json:"date"`
 	Actor       *user  `json:"actor"`
 	PullRequest *pr    `json:"pullRequest"`
+	// only in pr:from_ref_updated
+	PreviousFromHash string `json:"previousFromHash"`
+	// only in pr:modified
+	PreviousTarget struct {
+		ID              string `json:"id"`              // "refs/heads/master"
+		DisplayID       string `json:"displayId"`       // "master"
+		Type            string `json:"type"`            // "BRANCH"
+		LatestCommit    string `json:"latestCommit"`    // "860c4eb4ed0f969b47144234ba13c31c498cca69"
+		LatestChangeset string `json:"latestChangeset"` // "860c4eb4ed0f969b47144234ba13c31c498cca69"
+	} `json:"previousTarget"`
 }
 
 type change struct {
diff --git a/scm/driver/stash/webhook_test.go b/scm/driver/stash/webhook_test.go
index e7f162185..28e72a0ad 100644
--- a/scm/driver/stash/webhook_test.go
+++ b/scm/driver/stash/webhook_test.go
@@ -37,7 +37,14 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/push.json.golden",
 			obj:    new(scm.PushHook),
 		},
-
+		// v5 test
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "repo:refs_changed",
+			before: "testdata/webhooks/push_v5.json",
+			after:  "testdata/webhooks/push_v5.json.golden",
+			obj:    new(scm.PushHook),
+		},
 		//
 		// tag events
 		//
@@ -92,6 +99,30 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pr_open.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
+		// pull request source branch updated
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "pr:from_ref_updated",
+			before: "testdata/webhooks/pr_from_ref_updated.json",
+			after:  "testdata/webhooks/pr_from_ref_updated.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request modified, target branch updated
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "pr:modified",
+			before: "testdata/webhooks/pr_modified_toref.json",
+			after:  "testdata/webhooks/pr_modified_toref.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
+		// pull request modified, no change to toref
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "pr:modified",
+			before: "testdata/webhooks/pr_modified_meta.json",
+			after:  "testdata/webhooks/pr_modified_meta.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
 		// pull request fulfilled (merged)
 		{
 			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
@@ -108,6 +139,14 @@ func TestWebhooks(t *testing.T) {
 			after:  "testdata/webhooks/pr_declined.json.golden",
 			obj:    new(scm.PullRequestHook),
 		},
+		// pull request deleted
+		{
+			sig:    "71295b197fa25f4356d2fb9965df3f2379d903d7",
+			event:  "pr:deleted",
+			before: "testdata/webhooks/pr_deleted.json",
+			after:  "testdata/webhooks/pr_deleted.json.golden",
+			obj:    new(scm.PullRequestHook),
+		},
 	}
 
 	for _, test := range tests {
@@ -144,7 +183,7 @@ func TestWebhooks(t *testing.T) {
 			t.Log(diff)
 
 			// debug only. remove once implemented
-			json.NewEncoder(os.Stdout).Encode(o)
+			_ = json.NewEncoder(os.Stdout).Encode(o)
 		}
 
 		switch event := o.(type) {
diff --git a/scm/enrich/doc.go b/scm/enrich/doc.go
new file mode 100644
index 000000000..4792e6e14
--- /dev/null
+++ b/scm/enrich/doc.go
@@ -0,0 +1,3 @@
+// Package normalize provides facilities for enriching
+// data structures with missing information.
+package enrich
diff --git a/scm/enrich/webhook.go b/scm/enrich/webhook.go
new file mode 100644
index 000000000..2dde306d2
--- /dev/null
+++ b/scm/enrich/webhook.go
@@ -0,0 +1,17 @@
+// Copyright 2022 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package enrich
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+// Webhook enriches the webhook payload with missing
+// information not included in the webhook payload.
+func Webhook(ctx context.Context, client *scm.Client, webhook *scm.Webhook) error {
+	return nil // TODO
+}
diff --git a/scm/enrich/webhook_test.go b/scm/enrich/webhook_test.go
new file mode 100644
index 000000000..94fe1bca4
--- /dev/null
+++ b/scm/enrich/webhook_test.go
@@ -0,0 +1,5 @@
+// Copyright 2022 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package enrich
diff --git a/scm/example_test.go b/scm/example_test.go
index 7055616a2..f95aaff7d 100644
--- a/scm/example_test.go
+++ b/scm/example_test.go
@@ -91,7 +91,7 @@ func ExampleRepository_list() {
 	}
 }
 
-func ExampleBranch_find() {
+func ExampleGitService_FindBranch() {
 	client, err := github.New("https://api.github.com")
 	if err != nil {
 		log.Fatal(err)
@@ -105,7 +105,7 @@ func ExampleBranch_find() {
 	log.Println(branch.Name, branch.Sha)
 }
 
-func ExampleBranch_list() {
+func ExampleGitService_ListBranches() {
 	client, err := github.New("https://api.github.com")
 	if err != nil {
 		log.Fatal(err)
@@ -126,7 +126,7 @@ func ExampleBranch_list() {
 	}
 }
 
-func ExampleTag_find() {
+func ExampleGitService_FindTag() {
 	client, err := github.New("https://api.github.com")
 	if err != nil {
 		log.Fatal(err)
@@ -140,7 +140,7 @@ func ExampleTag_find() {
 	log.Println(tag.Name, tag.Sha)
 }
 
-func ExampleTag_list() {
+func ExampleGitService_ListTags() {
 	client, err := github.New("https://api.github.com")
 	if err != nil {
 		log.Fatal(err)
diff --git a/scm/git.go b/scm/git.go
index a5bda8e1c..afcc9e1e9 100644
--- a/scm/git.go
+++ b/scm/git.go
@@ -20,6 +20,12 @@ type (
 		Sha  string
 	}
 
+	// ReferenceInput provides a SHA for creating a reference.
+	ReferenceInput struct {
+		Name string
+		Sha  string
+	}
+
 	// Commit represents a repository commit.
 	Commit struct {
 		Sha       string
@@ -35,6 +41,7 @@ type (
 		Ref  string
 		Page int
 		Size int
+		Path string
 	}
 
 	// Signature identifies a git commit creator.
@@ -49,8 +56,20 @@ type (
 		Avatar string
 	}
 
+	// Pipeline Execution details
+	Execution struct {
+		Number  int
+		Status  ExecutionStatus
+		Created time.Time
+		Updated time.Time
+		URL     string
+	}
+
 	// GitService provides access to git resources.
 	GitService interface {
+		// CreateBranch creates a git branch by name given a sha.
+		CreateBranch(ctx context.Context, repo string, params *ReferenceInput) (*Response, error)
+
 		// FindBranch finds a git branch by name.
 		FindBranch(ctx context.Context, repo, name string) (*Reference, *Response, error)
 
@@ -63,6 +82,9 @@ type (
 		// ListBranches returns a list of git branches.
 		ListBranches(ctx context.Context, repo string, opts ListOptions) ([]*Reference, *Response, error)
 
+		// ListBranchesV2 returns a list of git branches based on the searchTerm passed.
+		ListBranchesV2(ctx context.Context, repo string, opts BranchListOptions) ([]*Reference, *Response, error)
+
 		// ListCommits returns a list of git commits.
 		ListCommits(ctx context.Context, repo string, opts CommitListOptions) ([]*Commit, *Response, error)
 
@@ -78,4 +100,8 @@ type (
 		// return a 2-way or 3-way diff changeset.
 		CompareChanges(ctx context.Context, repo, source, target string, opts ListOptions) ([]*Change, *Response, error)
 	}
+
+	// CreateBranch is a type alias for upstream projects
+	// that use the previous CreateBranch type name.
+	CreateBranch = ReferenceInput
 )
diff --git a/scm/milestone.go b/scm/milestone.go
new file mode 100644
index 000000000..a251c9dba
--- /dev/null
+++ b/scm/milestone.go
@@ -0,0 +1,42 @@
+package scm
+
+import (
+	"context"
+	"time"
+)
+
+type (
+	// MilestoneInput contains the information needed to create a milestone
+	MilestoneInput struct {
+		Title       string
+		Description string
+		State       string
+		DueDate     time.Time
+	}
+
+	// MilestoneListOptions provides options for querying a list of repository milestones.
+	MilestoneListOptions struct {
+		Page   int
+		Size   int
+		Open   bool
+		Closed bool
+	}
+
+	// MilestoneService provides access to creating, listing, updating, and deleting milestones
+	MilestoneService interface {
+		// Find returns the milestone for the given number in the given repository
+		Find(context.Context, string, int) (*Milestone, *Response, error)
+
+		// List returns a list of milestones in the given repository
+		List(context.Context, string, MilestoneListOptions) ([]*Milestone, *Response, error)
+
+		// Create creates a milestone in the given repository
+		Create(context.Context, string, *MilestoneInput) (*Milestone, *Response, error)
+
+		// Update updates a milestone in the given repository
+		Update(context.Context, string, int, *MilestoneInput) (*Milestone, *Response, error)
+
+		// Delete deletes a milestone in the given repository
+		Delete(context.Context, string, int) (*Response, error)
+	}
+)
diff --git a/scm/pr.go b/scm/pr.go
index 65b2d5683..1e9a74945 100644
--- a/scm/pr.go
+++ b/scm/pr.go
@@ -22,8 +22,10 @@ type (
 		Fork    string
 		Link    string
 		Diff    string
+		Draft   bool
 		Closed  bool
 		Merged  bool
+		Merge   string
 		Base    Reference
 		Head    Reference
 		Author  User
@@ -51,10 +53,13 @@ type (
 
 	// Change represents a changed file.
 	Change struct {
-		Path    string
-		Added   bool
-		Renamed bool
-		Deleted bool
+		Path         string
+		Added        bool
+		Renamed      bool
+		Deleted      bool
+		Sha          string
+		BlobID       string
+		PrevFilePath string
 	}
 
 	Label struct {
@@ -62,6 +67,17 @@ type (
 		Color string
 	}
 
+	// Milestone the milestone
+	Milestone struct {
+		Number      int
+		ID          int
+		Title       string
+		Description string
+		Link        string
+		State       string
+		DueDate     time.Time
+	}
+
 	// PullRequestService provides access to pull request resources.
 	PullRequestService interface {
 		// Find returns the repository pull request by number.
@@ -79,6 +95,9 @@ type (
 		// ListComments returns the pull request comment list.
 		ListComments(context.Context, string, int, ListOptions) ([]*Comment, *Response, error)
 
+		// ListCommits returns the pull request commit list.
+		ListCommits(context.Context, string, int, ListOptions) ([]*Commit, *Response, error)
+
 		// Merge merges the repository pull request.
 		Merge(context.Context, string, int) (*Response, error)
 
diff --git a/scm/release.go b/scm/release.go
new file mode 100644
index 000000000..68d6a058d
--- /dev/null
+++ b/scm/release.go
@@ -0,0 +1,67 @@
+package scm
+
+import (
+	"context"
+	"time"
+)
+
+type (
+	// Release the release
+	Release struct {
+		ID          int
+		Title       string
+		Description string
+		Link        string
+		Tag         string
+		Commitish   string
+		Draft       bool
+		Prerelease  bool
+		Created     time.Time
+		Published   time.Time
+	}
+
+	// ReleaseInput contains the information needed to create a release
+	ReleaseInput struct {
+		Title       string
+		Description string
+		Tag         string
+		Commitish   string
+		Draft       bool
+		Prerelease  bool
+	}
+
+	// ReleaseListOptions provides options for querying a list of repository releases.
+	ReleaseListOptions struct {
+		Page   int
+		Size   int
+		Open   bool
+		Closed bool
+	}
+
+	// ReleaseService provides access to creating, listing, updating, and deleting releases
+	ReleaseService interface {
+		// Find returns the release for the given number in the given repository
+		Find(context.Context, string, int) (*Release, *Response, error)
+
+		// FindByTag returns the release for the given tag in the given repository
+		FindByTag(context.Context, string, string) (*Release, *Response, error)
+
+		// List returns a list of releases in the given repository
+		List(context.Context, string, ReleaseListOptions) ([]*Release, *Response, error)
+
+		// Create creates a release in the given repository
+		Create(context.Context, string, *ReleaseInput) (*Release, *Response, error)
+
+		// Update updates a release in the given repository
+		Update(context.Context, string, int, *ReleaseInput) (*Release, *Response, error)
+
+		// UpdateByTag deletes a release in the given repository by tag
+		UpdateByTag(context.Context, string, string, *ReleaseInput) (*Release, *Response, error)
+
+		// Delete deletes a release in the given repository
+		Delete(context.Context, string, int) (*Response, error)
+
+		// DeleteByTag deletes a release in the given repository by tag
+		DeleteByTag(context.Context, string, string) (*Response, error)
+	}
+)
diff --git a/scm/repo.go b/scm/repo.go
index 8bcfa7231..5fe699427 100644
--- a/scm/repo.go
+++ b/scm/repo.go
@@ -17,6 +17,7 @@ type (
 		Name       string
 		Perm       *Perm
 		Branch     string
+		Archived   bool
 		Private    bool
 		Visibility Visibility
 		Clone      string
@@ -64,6 +65,7 @@ type (
 		Deployment         bool
 		Issue              bool
 		IssueComment       bool
+		Pipeline           bool
 		PullRequest        bool
 		PullRequestComment bool
 		Push               bool
@@ -119,6 +121,12 @@ type (
 		// List returns a list of repositories.
 		List(context.Context, ListOptions) ([]*Repository, *Response, error)
 
+		// ListV2 returns a list of repositories based on the searchTerm passed.
+		ListV2(context.Context, RepoListOptions) ([]*Repository, *Response, error)
+
+		// ListNamespace returns a list of repos in namespace
+		ListNamespace(context.Context, string, ListOptions) ([]*Repository, *Response, error)
+
 		// ListHooks returns a list or repository hooks.
 		ListHooks(context.Context, string, ListOptions) ([]*Hook, *Response, error)
 
diff --git a/scm/traverse/doc.go b/scm/traverse/doc.go
new file mode 100644
index 000000000..845da57c3
--- /dev/null
+++ b/scm/traverse/doc.go
@@ -0,0 +1,3 @@
+// Package traverse provides facilities for traversing
+// and combining the paginated results.
+package traverse
diff --git a/scm/traverse/repos.go b/scm/traverse/repos.go
new file mode 100644
index 000000000..c417d2704
--- /dev/null
+++ b/scm/traverse/repos.go
@@ -0,0 +1,36 @@
+// Copyright 2022 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package traverse
+
+import (
+	"context"
+
+	"github.com/drone/go-scm/scm"
+)
+
+// Repos returns the full repository list, traversing and
+// combining paginated responses if necessary.
+func Repos(ctx context.Context, client *scm.Client) ([]*scm.Repository, error) {
+	list := []*scm.Repository{}
+	opts := scm.ListOptions{Size: 100}
+	for {
+		result, meta, err := client.Repositories.List(ctx, opts)
+		if err != nil {
+			return nil, err
+		}
+		for _, src := range result {
+			if src != nil {
+				list = append(list, src)
+			}
+		}
+		opts.Page = meta.Page.Next
+		opts.URL = meta.Page.NextURL
+
+		if opts.Page == 0 && opts.URL == "" {
+			break
+		}
+	}
+	return list, nil
+}
diff --git a/scm/traverse/repos_test.go b/scm/traverse/repos_test.go
new file mode 100644
index 000000000..63cf67f35
--- /dev/null
+++ b/scm/traverse/repos_test.go
@@ -0,0 +1,5 @@
+// Copyright 2022 Drone.IO Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package traverse
diff --git a/scm/user.go b/scm/user.go
index 93c40c469..fd448770a 100644
--- a/scm/user.go
+++ b/scm/user.go
@@ -12,6 +12,7 @@ import (
 type (
 	// User represents a user account.
 	User struct {
+		ID      string
 		Login   string
 		Name    string
 		Email   string
@@ -20,6 +21,13 @@ type (
 		Updated time.Time
 	}
 
+	// Email represents a user email.
+	Email struct {
+		Value    string
+		Primary  bool
+		Verified bool
+	}
+
 	// UserService provides access to user account resources.
 	UserService interface {
 		// Find returns the authenticated user.
@@ -30,5 +38,8 @@ type (
 
 		// FindLogin returns the user account by username.
 		FindLogin(context.Context, string) (*User, *Response, error)
+
+		// ListEmail returns the user email list.
+		ListEmail(context.Context, ListOptions) ([]*Email, *Response, error)
 	}
 )
diff --git a/scm/util.go b/scm/util.go
index 6108feee8..4e72f524f 100644
--- a/scm/util.go
+++ b/scm/util.go
@@ -14,6 +14,13 @@ import (
 // from the git ref (e.g. refs/pulls/{d}/head)
 var re = regexp.MustCompile("\\d+")
 
+// regular expressions to test whether or not a string is
+// a sha1 or sha256 commit hash.
+var (
+	sha1   = regexp.MustCompile("^([a-f0-9]{40})$")
+	sha256 = regexp.MustCompile("^([a-f0-9]{64})$")
+)
+
 // Split splits the full repository name into segments.
 func Split(s string) (owner, name string) {
 	parts := strings.SplitN(s, "/", 2)
@@ -77,3 +84,47 @@ func IsPullRequest(ref string) bool {
 		strings.HasPrefix(ref, "refs/pull-request/") ||
 		strings.HasPrefix(ref, "refs/merge-requests/")
 }
+
+// IsHash returns true if the string is a commit hash.
+func IsHash(s string) bool {
+	return sha1.MatchString(s) || sha256.MatchString(s)
+}
+
+func ConvertVisibility(from string) Visibility {
+	switch from {
+	case "public":
+		return VisibilityPublic
+	case "private":
+		return VisibilityPrivate
+	case "internal":
+		return VisibilityInternal
+	default:
+		return VisibilityUndefined
+	}
+}
+
+func ConvertExecutionStatus(from string) ExecutionStatus {
+	switch from {
+	case "running", "in_progress", "INPROGRESS":
+		return StatusRunning
+	case "success", "completed", "SUCCESSFUL":
+		return StatusSuccess
+	case "Failed", "failure", "FAILED":
+		return StatusFailed
+	case "Canceled", "cancelled", "STOPPED":
+		return StatusCanceled
+	case "pending", "queued":
+		return StatusPending
+	default:
+		return StatusUnknown
+	}
+}
+
+func ConvertPrivate(from string) bool {
+	switch from {
+	case "public", "":
+		return false
+	default:
+		return true
+	}
+}
diff --git a/scm/util_test.go b/scm/util_test.go
index 44b0ba181..c2b87ae74 100644
--- a/scm/util_test.go
+++ b/scm/util_test.go
@@ -193,3 +193,88 @@ func TestExtractPullRequest(t *testing.T) {
 		}
 	}
 }
+
+func TestIsHash(t *testing.T) {
+	tests := []struct {
+		name string
+		tag  bool
+	}{
+		{
+			name: "aacad6eca956c3a340ae5cd5856aa9c4a3755408",
+			tag:  true,
+		},
+		{
+			name: "3da541559918a808c2402bba5012f6c60b27661c",
+			tag:  true,
+		},
+		{
+			name: "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b",
+			tag:  true,
+		},
+		// not a sha
+		{
+			name: "aacad6e",
+			tag:  false,
+		},
+		{
+			name: "master",
+			tag:  false,
+		},
+		{
+			name: "refs/heads/master",
+			tag:  false,
+		},
+		{
+			name: "issue/42",
+			tag:  false,
+		},
+		{
+			name: "feature/foo",
+			tag:  false,
+		},
+	}
+	for _, test := range tests {
+		if got, want := IsHash(test.name), test.tag; got != want {
+			t.Errorf("Got IsHash %v, want %v", got, want)
+		}
+	}
+}
+
+func TestConvertVisibility(t *testing.T) {
+	tests := []struct {
+		in  string
+		out Visibility
+	}{
+		{"public", 1},
+		{"", 0},
+		{"private", 3},
+		{"internal", 2},
+		{"invalid", 0},
+		{"unknown", 0},
+	}
+
+	for _, test := range tests {
+		if got, want := ConvertVisibility(test.in), test.out; got != want {
+			t.Errorf("Want %d for %v type, got %d", want, test.in, got)
+		}
+	}
+}
+
+func TestConvertPrivate(t *testing.T) {
+	tests := []struct {
+		in  string
+		out bool
+	}{
+		{"public", false},
+		{"", false},
+		{"private", true},
+		{"internal", true},
+		{"invalid", true},
+	}
+
+	for _, test := range tests {
+		if got, want := ConvertPrivate(test.in), test.out; got != want {
+			t.Errorf("Want private %v, got %v", want, got)
+		}
+	}
+}
diff --git a/scm/webhook.go b/scm/webhook.go
index b36c25feb..ffe4791cd 100644
--- a/scm/webhook.go
+++ b/scm/webhook.go
@@ -37,6 +37,15 @@ type (
 		Commits []Commit
 	}
 
+	// PipelineHook
+	PipelineHook struct {
+		Commit      Commit
+		Execution   Execution
+		PullRequest PullRequest
+		Repo        Repository
+		Sender      User
+	}
+
 	// BranchHook represents a branch or tag event,
 	// eg create and delete github event types.
 	BranchHook struct {
@@ -115,6 +124,15 @@ type (
 		Task      string
 	}
 
+	// ReleaseHook represents a release event. This is
+	// currently a GitHub-specific event type.
+	ReleaseHook struct {
+		Action  Action
+		Release Release
+		Repo    Repository
+		Sender  User
+	}
+
 	// SecretFunc provides the Webhook parser with the
 	// secret key used to validate webhook authenticity.
 	SecretFunc func(webhook Webhook) (string, error)
@@ -140,3 +158,5 @@ func (h *IssueCommentHook) Repository() Repository       { return h.Repo }
 func (h *PullRequestHook) Repository() Repository        { return h.Repo }
 func (h *PullRequestCommentHook) Repository() Repository { return h.Repo }
 func (h *ReviewCommentHook) Repository() Repository      { return h.Repo }
+func (h *ReleaseHook) Repository() Repository            { return h.Repo }
+func (h *PipelineHook) Repository() Repository           { return h.Repo }