Compare commits
No commits in common. "857eee205ce3f7598a036a54ae8729fcc1727b9e" and "46b2bb796f88823019fd3d0091178565aed83a54" have entirely different histories.
857eee205c
...
46b2bb796f
11 changed files with 159 additions and 249 deletions
|
|
@ -5,11 +5,11 @@
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
tags:
|
||||||
- master
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
basic:
|
docker:
|
||||||
runs-on: any
|
runs-on: any
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
|
@ -45,15 +45,6 @@ jobs:
|
||||||
push: true
|
push: true
|
||||||
pull: true
|
pull: true
|
||||||
tags: git.ronmi.tw/ronmi/forgejo-pages:arm64,ronmi/forgejo-pages:arm64
|
tags: git.ronmi.tw/ronmi/forgejo-pages:arm64,ronmi/forgejo-pages:arm64
|
||||||
- name: Build arm64 image (git)
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: git.dockerfile
|
|
||||||
platforms: linux/arm64
|
|
||||||
push: true
|
|
||||||
pull: true
|
|
||||||
tags: git.ronmi.tw/ronmi/forgejo-pages:git-arm64,ronmi/forgejo-pages:git-arm64
|
|
||||||
- name: Build amd64 binary
|
- name: Build amd64 binary
|
||||||
run: GOARCH=amd64 go build
|
run: GOARCH=amd64 go build
|
||||||
- name: Build amd64 image
|
- name: Build amd64 image
|
||||||
|
|
@ -64,24 +55,14 @@ jobs:
|
||||||
push: true
|
push: true
|
||||||
pull: true
|
pull: true
|
||||||
tags: git.ronmi.tw/ronmi/forgejo-pages:amd64,ronmi/forgejo-pages:amd64
|
tags: git.ronmi.tw/ronmi/forgejo-pages:amd64,ronmi/forgejo-pages:amd64
|
||||||
- name: Build amd64 image (git)
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: git.dockerfile
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: true
|
|
||||||
pull: true
|
|
||||||
tags: git.ronmi.tw/ronmi/forgejo-pages:git-amd64,ronmi/forgejo-pages:git-amd64
|
|
||||||
- name: Create multiarch image
|
- name: Create multiarch image
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create -t git.ronmi.tw/ronmi/forgejo-pages git.ronmi.tw/ronmi/forgejo-pages:arm64 git.ronmi.tw/ronmi/forgejo-pages:amd64
|
docker buildx imagetools create -t git.ronmi.tw/ronmi/forgejo-pages git.ronmi.tw/ronmi/forgejo-pages:arm64 git.ronmi.tw/ronmi/forgejo-pages:amd64
|
||||||
docker buildx imagetools create -t ronmi/forgejo-pages ronmi/forgejo-pages:arm64 ronmi/forgejo-pages:amd64
|
docker buildx imagetools create -t ronmi/forgejo-pages ronmi/forgejo-pages:arm64 ronmi/forgejo-pages:amd64
|
||||||
docker buildx imagetools create -t git.ronmi.tw/ronmi/forgejo-pages:git git.ronmi.tw/ronmi/forgejo-pages:git-arm64 git.ronmi.tw/ronmi/forgejo-pages:git-amd64
|
|
||||||
docker buildx imagetools create -t ronmi/forgejo-pages:git ronmi/forgejo-pages:git-arm64 ronmi/forgejo-pages:git-amd64
|
|
||||||
- name: Update readme to docker hub
|
- name: Update readme to docker hub
|
||||||
uses: https://github.com/peter-evans/dockerhub-description@v4
|
uses: https://github.com/peter-evans/dockerhub-description@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USER }}
|
username: ${{ secrets.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
repository: ronmi/forgejo-pages
|
repository: ronmi/forgejo-pages
|
||||||
|
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
forgejo-pages
|
|
||||||
|
|
@ -14,7 +14,7 @@ This mode is good for simple setup, like, you have small number of viewers, or y
|
||||||
|
|
||||||
### Webhook mode
|
### Webhook mode
|
||||||
|
|
||||||
Webhook mode is a tool which helps you to download latest content via git. You'll have to setup a webhook in forgejo server in order to notify it when to download new content. You have to have `git` binary in your path.
|
Webhook mode is a tool which helps you to download latest content via git. You'll have to setup a webhook in forgejo server in order to notify it when to download new content.
|
||||||
|
|
||||||
To serve downloaded pages, you'll have to use a web server like Nginx.
|
To serve downloaded pages, you'll have to use a web server like Nginx.
|
||||||
|
|
||||||
|
|
@ -41,13 +41,9 @@ Take care about permissions of the API token. For serve mode, repositories the k
|
||||||
```
|
```
|
||||||
docker run -p 8080:8080 --user 1000:1000 ronmi/forgejo-pages serve -s https://git.example.com -k my-secret-token
|
docker run -p 8080:8080 --user 1000:1000 ronmi/forgejo-pages serve -s https://git.example.com -k my-secret-token
|
||||||
|
|
||||||
docker run -p 8080:8080 --user 1000:1000 -v `pwd`/data:/data ronmi/forgejo-pages:git listen -u myuser -k my-secret-token -s https://git.example.com -a :8080 -b static-pages -d /data
|
docker run -p 8080:8080 --user 1000:1000 -v `pwd`/data:/data ronmi/forgejo-pages listen -u myuser -k my-secret-token -s https://git.example.com -a :8080 -b static-pages -d /data
|
||||||
```
|
```
|
||||||
|
|
||||||
Serve mode could use `latest` tag, a minimal image contains only libc, CA certificates, timezone data and page server binary.
|
|
||||||
|
|
||||||
Webhook mode should use `git` tag.
|
|
||||||
|
|
||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
### Can I use user.example.com/repo/path format?
|
### Can I use user.example.com/repo/path format?
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.ronmi.tw/ronmi/forgejo-pages/lib"
|
"git.ronmi.tw/ronmi/forgejo-pages/lib"
|
||||||
"github.com/raohwork/task"
|
|
||||||
"github.com/raohwork/task/httptask"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
@ -70,7 +68,7 @@ is up to you, eg. Nginx, Apache, or even a simple Go server.
|
||||||
GitPass: token,
|
GitPass: token,
|
||||||
}
|
}
|
||||||
|
|
||||||
s, r, err := lib.UseWebhook(bind, cfg)
|
s, err := lib.UseWebhook(bind, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot create server: ", err)
|
fmt.Println("cannot create server: ", err)
|
||||||
return
|
return
|
||||||
|
|
@ -85,11 +83,14 @@ is up to you, eg. Nginx, Apache, or even a simple Go server.
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
fmt.Println("starting server")
|
fmt.Println("starting server")
|
||||||
task.Wait(
|
go func() {
|
||||||
httptask.Server(s, task.Timeout(10*time.Second)),
|
<-ctx.Done()
|
||||||
r.Run,
|
stop()
|
||||||
).Run(ctx)
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
s.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
s.ListenAndServe()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
cmd/serve.go
11
cmd/serve.go
|
|
@ -14,8 +14,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.ronmi.tw/ronmi/forgejo-pages/lib"
|
"git.ronmi.tw/ronmi/forgejo-pages/lib"
|
||||||
"github.com/raohwork/task"
|
|
||||||
"github.com/raohwork/task/httptask"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
@ -72,7 +70,14 @@ cache/protection layer like Cloudflare in front of the server.
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
fmt.Println("starting server")
|
fmt.Println("starting server")
|
||||||
httptask.Server(s, task.Timeout(10*time.Second)).Run(ctx)
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
stop()
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
s.Shutdown(ctx)
|
||||||
|
}()
|
||||||
|
s.ListenAndServe()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
FROM debian:stable-slim
|
|
||||||
|
|
||||||
# Install git
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
ca-certificates git tzdata \
|
|
||||||
&& apt-get clean -y && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
ADD forgejo-pages /usr/bin/forgejo-pages
|
|
||||||
ENTRYPOINT ["/usr/bin/forgejo-pages"]
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -3,7 +3,6 @@ module git.ronmi.tw/ronmi/forgejo-pages
|
||||||
go 1.21.6
|
go 1.21.6
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/raohwork/task v0.3.0
|
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -26,8 +26,6 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/raohwork/task v0.3.0 h1:4j0jT1a+f5O+g6q22o42sFEdSlYGYtgUHLbg3cF/7VE=
|
|
||||||
github.com/raohwork/task v0.3.0/go.mod h1:QkVxY/Q/w6bW5Xjhcp8vn5qLgoS+70jUTyGueI0nzMo=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
|
|
||||||
159
lib/git.go
159
lib/git.go
|
|
@ -1,159 +0,0 @@
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type repoSpec struct {
|
|
||||||
user string
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type GitRunner struct {
|
|
||||||
ch <-chan repoSpec
|
|
||||||
cfg *WebhookCFG
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GitRunner) Run(ctx context.Context) error {
|
|
||||||
// graceful: exit only if all tasks are done
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case repo := <-g.ch:
|
|
||||||
err := g.cfg.download(repo.user, repo.name)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to download repo: ", err)
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) pageDir(user, repo string) string {
|
|
||||||
return filepath.Join(c.PageDir, user, repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) gitDir(user, repo string) string {
|
|
||||||
return filepath.Join(c.GitDir, user, repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) checkout(user, repo string) (err error) {
|
|
||||||
pageDir := c.pageDir(user, repo)
|
|
||||||
gitDir := c.gitDir(user, repo)
|
|
||||||
|
|
||||||
git := exec.Command(
|
|
||||||
"git",
|
|
||||||
"--git-dir", gitDir,
|
|
||||||
"--work-tree", pageDir,
|
|
||||||
"checkout", "origin/"+c.Branch,
|
|
||||||
)
|
|
||||||
output, err := git.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("git checkout failed: ", err)
|
|
||||||
fmt.Println("=========== Dump output: ============")
|
|
||||||
fmt.Println(string(output))
|
|
||||||
fmt.Println("=====================================")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) fetch(user, repo string) (err error) {
|
|
||||||
gitDir := c.gitDir(user, repo)
|
|
||||||
git := exec.Command(
|
|
||||||
"git",
|
|
||||||
"--git-dir", gitDir,
|
|
||||||
"fetch", "origin", c.Branch,
|
|
||||||
)
|
|
||||||
output, err := git.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("git fetch failed: ", err)
|
|
||||||
fmt.Println("=========== Dump output: ============")
|
|
||||||
fmt.Println(string(output))
|
|
||||||
fmt.Println("=====================================")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) clone(user, repo string) (err error) {
|
|
||||||
pageDir := c.pageDir(user, repo)
|
|
||||||
err = os.MkdirAll(filepath.Dir(pageDir), 0755)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gitDir := c.gitDir(user, repo)
|
|
||||||
err = os.MkdirAll(filepath.Dir(gitDir), 0700)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
uri := c.Server
|
|
||||||
uri.Path = "/" + path.Join(user, repo) + ".git"
|
|
||||||
uri.User = url.UserPassword(c.GitUser, c.GitPass)
|
|
||||||
|
|
||||||
git := exec.Command(
|
|
||||||
"git",
|
|
||||||
"clone",
|
|
||||||
"-b", c.Branch,
|
|
||||||
"--single-branch",
|
|
||||||
"--no-tags",
|
|
||||||
"--separate-git-dir", gitDir,
|
|
||||||
uri.String(),
|
|
||||||
pageDir,
|
|
||||||
)
|
|
||||||
output, err := git.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("git clone failed: ", err)
|
|
||||||
fmt.Println("=========== Dump output: ============")
|
|
||||||
fmt.Println(string(output))
|
|
||||||
fmt.Println("=====================================")
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Remove(filepath.Join(pageDir, ".git"))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var repoLock = &sync.Map{}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) lock(user, repo string) func() {
|
|
||||||
key := user + "/" + repo
|
|
||||||
|
|
||||||
lock, _ := repoLock.LoadOrStore(key, &sync.Mutex{})
|
|
||||||
m := lock.(*sync.Mutex)
|
|
||||||
m.Lock()
|
|
||||||
return m.Unlock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WebhookCFG) download(user, repo string) (err error) {
|
|
||||||
unlock := c.lock(user, repo)
|
|
||||||
defer unlock()
|
|
||||||
fmt.Println("Pulling ", user, repo)
|
|
||||||
|
|
||||||
gitDir := c.gitDir(user, repo)
|
|
||||||
if _, err = os.Stat(gitDir); os.IsNotExist(err) {
|
|
||||||
err = c.clone(user, repo)
|
|
||||||
} else {
|
|
||||||
err = c.fetch(user, repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.checkout(user, repo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
138
lib/hook.go
138
lib/hook.go
|
|
@ -10,7 +10,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WebhookCFG struct {
|
type WebhookCFG struct {
|
||||||
|
|
@ -19,7 +24,123 @@ type WebhookCFG struct {
|
||||||
GitDir string
|
GitDir string
|
||||||
GitUser string
|
GitUser string
|
||||||
GitPass string
|
GitPass string
|
||||||
ch chan<- repoSpec
|
}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) pageDir(user, repo string) string {
|
||||||
|
return filepath.Join(c.PageDir, user, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) gitDir(user, repo string) string {
|
||||||
|
return filepath.Join(c.GitDir, user, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) checkout(user, repo string) (err error) {
|
||||||
|
pageDir := c.pageDir(user, repo)
|
||||||
|
gitDir := c.gitDir(user, repo)
|
||||||
|
|
||||||
|
git := exec.Command(
|
||||||
|
"git",
|
||||||
|
"--git-dir", gitDir,
|
||||||
|
"--work-tree", pageDir,
|
||||||
|
"checkout", "origin/"+c.Branch,
|
||||||
|
)
|
||||||
|
output, err := git.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("git checkout failed: ", err)
|
||||||
|
fmt.Println("=========== Dump output: ============")
|
||||||
|
fmt.Println(string(output))
|
||||||
|
fmt.Println("=====================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) fetch(user, repo string) (err error) {
|
||||||
|
gitDir := c.gitDir(user, repo)
|
||||||
|
git := exec.Command(
|
||||||
|
"git",
|
||||||
|
"--git-dir", gitDir,
|
||||||
|
"fetch", "origin", c.Branch,
|
||||||
|
)
|
||||||
|
output, err := git.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("git fetch failed: ", err)
|
||||||
|
fmt.Println("=========== Dump output: ============")
|
||||||
|
fmt.Println(string(output))
|
||||||
|
fmt.Println("=====================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) clone(user, repo string) (err error) {
|
||||||
|
pageDir := c.pageDir(user, repo)
|
||||||
|
err = os.MkdirAll(filepath.Dir(pageDir), 0755)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gitDir := c.gitDir(user, repo)
|
||||||
|
err = os.MkdirAll(filepath.Dir(gitDir), 0700)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := c.Server
|
||||||
|
uri.Path = "/" + path.Join(user, repo) + ".git"
|
||||||
|
uri.User = url.UserPassword(c.GitUser, c.GitPass)
|
||||||
|
|
||||||
|
git := exec.Command(
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"-b", c.Branch,
|
||||||
|
"--single-branch",
|
||||||
|
"--no-tags",
|
||||||
|
"--separate-git-dir", gitDir,
|
||||||
|
uri.String(),
|
||||||
|
pageDir,
|
||||||
|
)
|
||||||
|
output, err := git.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("git clone failed: ", err)
|
||||||
|
fmt.Println("=========== Dump output: ============")
|
||||||
|
fmt.Println(string(output))
|
||||||
|
fmt.Println("=====================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(filepath.Join(pageDir, ".git"))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var repoLock = &sync.Map{}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) lock(user, repo string) func() {
|
||||||
|
key := user + "/" + repo
|
||||||
|
|
||||||
|
lock, _ := repoLock.LoadOrStore(key, &sync.Mutex{})
|
||||||
|
m := lock.(*sync.Mutex)
|
||||||
|
m.Lock()
|
||||||
|
return m.Unlock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebhookCFG) download(user, repo string) (err error) {
|
||||||
|
unlock := c.lock(user, repo)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
gitDir := c.gitDir(user, repo)
|
||||||
|
if _, err = os.Stat(gitDir); os.IsNotExist(err) {
|
||||||
|
err = c.clone(user, repo)
|
||||||
|
} else {
|
||||||
|
err = c.fetch(user, repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.checkout(user, repo)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type webhookPayload struct {
|
type webhookPayload struct {
|
||||||
|
|
@ -64,19 +185,22 @@ func (c *WebhookCFG) handle(w http.ResponseWriter, r *http.Request) {
|
||||||
user, repo := path.Split(payload.Repository.FullName)
|
user, repo := path.Split(payload.Repository.FullName)
|
||||||
user = user[:len(user)-1]
|
user = user[:len(user)-1]
|
||||||
go func() {
|
go func() {
|
||||||
c.ch <- repoSpec{user: user, name: repo}
|
fmt.Println("Pulling ", user, repo)
|
||||||
|
err = c.download(user, repo)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to download repository: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func UseWebhook(bind string, cfg *WebhookCFG) (*http.Server, *GitRunner, error) {
|
func UseWebhook(bind string, cfg *WebhookCFG) (*http.Server, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, nil, errors.New("webhook config is nil")
|
return nil, errors.New("webhook config is nil")
|
||||||
}
|
}
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: bind,
|
Addr: bind,
|
||||||
Handler: http.HandlerFunc(cfg.handle),
|
Handler: http.HandlerFunc(cfg.handle),
|
||||||
}
|
}
|
||||||
ch := make(chan repoSpec, 5)
|
return s, nil
|
||||||
cfg.ch = ch
|
|
||||||
return s, &GitRunner{ch: ch, cfg: cfg}, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
lib/web.go
31
lib/web.go
|
|
@ -12,8 +12,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"mime"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Forgejo struct {
|
type Forgejo struct {
|
||||||
|
|
@ -36,7 +34,7 @@ func (f *Forgejo) GetFile(ctx context.Context, headers map[string]string, user,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
continue
|
continue
|
||||||
|
|
@ -52,7 +50,7 @@ func (f *Forgejo) GetFile(ctx context.Context, headers map[string]string, user,
|
||||||
err = ErrNotFound
|
err = ErrNotFound
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,43 +102,24 @@ func (f *Forgejo) handle(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
file = strings.Join(arr[2:], "/")
|
file = strings.Join(arr[2:], "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
headers := map[string]string{}
|
headers := map[string]string{}
|
||||||
headers["If-None-Match"] = r.Header.Get("If-None-Match")
|
headers["If-None-Match"] = r.Header.Get("If-None-Match")
|
||||||
headers["If-Modified-Since"] = r.Header.Get("If-Modified-Since")
|
headers["If-Modified-Since"] = r.Header.Get("If-Modified-Since")
|
||||||
headers["If-Range"] = r.Header.Get("If-Range")
|
headers["If-Range"] = r.Header.Get("If-Range")
|
||||||
headers["Range"] = r.Header.Get("Range")
|
headers["Range"] = r.Header.Get("Range")
|
||||||
headers["X-Forwarded-For"] = r.Header.Get("X-Forwarded-For")
|
|
||||||
headers["X-Forwarded-Host"] = r.Header.Get("X-Forwarded-Host")
|
|
||||||
headers["X-Forwarded-Proto"] = r.Header.Get("X-Forwarded-Proto")
|
|
||||||
headers["X-Real-IP"] = r.Header.Get("X-Real-IP")
|
|
||||||
headers["X-Host"] = r.Header.Get("X-Host")
|
|
||||||
headers["CF-Connecting-IP"] = r.Header.Get("CF-Connecting-IP")
|
|
||||||
headers["CF-IPCountry"] = r.Header.Get("CF-IPCountry")
|
|
||||||
headers["CF-Visitor"] = r.Header.Get("CF-Visitor")
|
|
||||||
headers["CF-Request-ID"] = r.Header.Get("CF-Request-ID")
|
|
||||||
headers["CF-Ray"] = r.Header.Get("CF-Ray")
|
|
||||||
resp, err := f.GetFile(r.Context(), headers, user, repo, f.Branch, file)
|
resp, err := f.GetFile(r.Context(), headers, user, repo, f.Branch, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
trySet(w.Header(), "Etag", resp.Header)
|
trySet(w.Header(), "Etag", resp.Header)
|
||||||
trySet(w.Header(), "Last-Modified", resp.Header)
|
trySet(w.Header(), "Last-Modified", resp.Header)
|
||||||
trySet(w.Header(), "Content-Length", resp.Header)
|
trySet(w.Header(), "Content-Length", resp.Header)
|
||||||
trySet(w.Header(), "Content-Range", resp.Header)
|
trySet(w.Header(), "Content-Range", resp.Header)
|
||||||
contentType := resp.Header.Get("Content-Type")
|
|
||||||
if contentType == "" || strings.HasPrefix(contentType, "text/plain") {
|
|
||||||
if ct := mime.TypeByExtension(filepath.Ext(file)); ct != "" {
|
|
||||||
contentType = ct
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if contentType != "" {
|
|
||||||
w.Header().Set("Content-Type", contentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(resp.StatusCode)
|
w.WriteHeader(resp.StatusCode)
|
||||||
io.Copy(w, resp.Body)
|
io.Copy(w, resp.Body)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue