Home

gitpecker @main - refs - log -
-
https://git.jolheiser.com/gitpecker.git
Woodpecker addon for generic git forge and OIDC
tree log patch
gitpeckerhook Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQCkjF6nGjPjJPW81w33yIrYhqM2j2n4BCVOzqXQyKHBXSJvr0wDRAtfQSNBMjWQuWS KDJNzHLPoddKcPhBUlOQk= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
4 hours ago
3 changed files, 163 additions(+), 1 deletions(-)
.gitignorecmd/gitpeckerhook/main.gocmd/gitpeckerhook/woodpecker.go
M .gitignore -> .gitignore
1
2
3
4
5
6
7
8
9
diff --git a/.gitignore b/.gitignore
index b5a13702634a0c7364585ec8ceb3245862cc2bb4..9bd517c63978452ae58a849aefa3d71e731db0fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-gitpecker*
+/gitpecker*
 .env
 nixos.qcow2
I cmd/gitpeckerhook/main.go
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
diff --git a/cmd/gitpeckerhook/main.go b/cmd/gitpeckerhook/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..f13effc123a49a8d3281b84a7d481e0616a6255d
--- /dev/null
+++ b/cmd/gitpeckerhook/main.go
@@ -0,0 +1,97 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+func maine() error {
+	fs := flag.NewFlagSet("gitpeckerhook", flag.ExitOnError)
+	domainFlag := fs.String("domain", "", "Woodpecker domain (e.g. https://ci.example.com)")
+	fs.StringVar(domainFlag, "d", *domainFlag, "--domain")
+	cacheFlag := fs.String("cache", ".cache.json", "Path to JSON cache file")
+	fs.StringVar(cacheFlag, "c", *cacheFlag, "--cache")
+	tokenFlag := fs.String("token", "", "Woodpecker API token")
+	fs.StringVar(tokenFlag, "t", *tokenFlag, "--token")
+	tokenFileFlag := fs.String("token-file", "", "Path to a file containing a Woodpecker API token")
+	fs.StringVar(tokenFileFlag, "T", *tokenFileFlag, "--token-file")
+	if err := fs.Parse(os.Args[1:]); err != nil {
+		return err
+	}
+
+	token := *tokenFlag
+	if *tokenFileFlag != "" {
+		data, err := os.ReadFile(*tokenFileFlag)
+		if err != nil {
+			return err
+		}
+		token = strings.TrimSpace(string(data))
+	}
+
+	pwd, err := os.Getwd()
+	if err != nil {
+		return err
+	}
+
+	repo := filepath.Base(pwd)
+	repo = strings.TrimSuffix(repo, ".git")
+
+	cache, err := ensureCache(*cacheFlag)
+	if err != nil {
+		return err
+	}
+
+	client := client{domain: *domainFlag, token: token}
+
+	if _, ok := cache[repo]; !ok {
+		id, err := client.lookupRepo(repo)
+		if err != nil {
+			return err
+		}
+		cache[repo] = id
+		fi, err := os.Open(*cacheFlag)
+		if err != nil {
+			return err
+		}
+		defer fi.Close()
+		if err := json.NewEncoder(fi).Encode(cache); err != nil {
+			return err
+		}
+	}
+
+	return client.triggerPipeline(cache[repo])
+}
+
+func ensureCache(cachePath string) (map[string]int, error) {
+	var c map[string]int
+
+	_, err := os.Stat(cachePath)
+	if err != nil {
+		if err := os.MkdirAll(filepath.Dir(cachePath), os.ModePerm); err != nil {
+			return c, err
+		}
+		fi, err := os.Create(cachePath)
+		if err != nil {
+			return c, err
+		}
+		fi.WriteString("{}")
+		fi.Close()
+	}
+
+	fi, err := os.Open(cachePath)
+	if err != nil {
+		return c, err
+	}
+	defer fi.Close()
+
+	return c, json.NewDecoder(fi).Decode(&c)
+}
+
+func main() {
+	if err := maine(); err != nil {
+		panic(err)
+	}
+}
I cmd/gitpeckerhook/woodpecker.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
diff --git a/cmd/gitpeckerhook/woodpecker.go b/cmd/gitpeckerhook/woodpecker.go
new file mode 100644
index 0000000000000000000000000000000000000000..b0889aacec0d99016a3840c8d1372d04571d8eef
--- /dev/null
+++ b/cmd/gitpeckerhook/woodpecker.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+)
+
+var httpClient = http.Client{Timeout: time.Second * 10}
+
+type client struct {
+	domain string
+	token  string
+}
+
+type lookupResp struct {
+	ID int `json:"id"`
+}
+
+func (c client) lookupRepo(repoName string) (int, error) {
+	u := strings.TrimSuffix(c.domain, "/") + "/api/repos/lookup/" + repoName
+
+	req, err := http.NewRequest(http.MethodGet, u, nil)
+	if err != nil {
+		return -1, err
+	}
+	req.Header.Add("Authorization", "Bearer "+c.token)
+
+	resp, err := httpClient.Do(req)
+	if err != nil {
+		return -1, err
+	}
+	defer resp.Body.Close()
+
+	var l lookupResp
+	if err := json.NewDecoder(resp.Body).Decode(&l); err != nil {
+		return -1, err
+	}
+
+	return l.ID, nil
+}
+
+func (c client) triggerPipeline(repoID int) error {
+	u := strings.TrimSuffix(c.domain, "/") + fmt.Sprintf("/api/repos/%d/pipelines", repoID)
+
+	req, err := http.NewRequest(http.MethodPost, u, nil)
+	if err != nil {
+		return err
+	}
+	req.Header.Add("Authorization", "Bearer "+c.token)
+
+	resp, err := httpClient.Do(req)
+	if err != nil {
+		return err
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		return errors.New("could not trigger pipeline")
+	}
+
+	return nil
+}