summaryrefslogtreecommitdiff
path: root/main.go
blob: 4902f8b814fdc461fdb3e17f4bf185c0c0c93432 (plain)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
)

var logger *log.Logger
var logf *os.File

func main() {
	var err error
	logf, err = os.OpenFile("loggy.boi",
		os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		log.Fatal(err)
	}

	defer logf.Close()

	logger = log.New(logf, "", log.LstdFlags)
	sshOriginalCommand := os.Getenv("SSH_ORIGINAL_COMMAND")
	if sshOriginalCommand == "" {
		log.Fatal("SSH_ORIGINAL_COMMAND is not set")
	}

	if err := Run(sshOriginalCommand); err != nil {
		logger.Fatal(err)
	}
}

type Mkdir func([]string) error

func mkdir(path string) error {
	return os.MkdirAll(path, 0755)
}

func Run(sshCmd string) error {
	parts := strings.Split(sshCmd, " ")
	if len(parts) != 2 {
		return fmt.Errorf("expected 2 args: %s", sshCmd)
	}

	command := parts[0]
	repo := parts[1]

	remmappedRepo := RemapRepoPath(repo, "repos")
	// creates repos if they don't exist
	if _, err := os.Stat(remmappedRepo); os.IsNotExist(err) {
		if err := setupRepo(remmappedRepo); err != nil {
			return fmt.Errorf("failed to setup repo: %v", err)
		}
	}

	bin, err := exec.LookPath(command)
	if err != nil {
		return fmt.Errorf("failed to lookup %s:%v", command, err)
	}

	// Execute the git-shell command
	return syscall.Exec(bin, []string{command, remmappedRepo}, os.Environ())
}

func setupRepo(repo string) error {
	// Create the directory (and any necessary parent directories)
	if err := mkdir(repo); err != nil {
		return fmt.Errorf("failed to create directory: %v", err)
	}

	cmd := exec.Command("git", "init", "--bare", "--initial-branch=main", repo)
	cmd.Stdout = logf
	cmd.Stderr = logf
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to run initialize git repository: %v", err)
	}

	if err := writeDescriptionHook(repo); err != nil {
		logger.Printf("failed to create description hook: %v\n", err)
	}

	return nil
}

// 'a73x/cgit.git' => repos/a73x/cgit.git
func RemapRepoPath(repo string, path string) string {
	return filepath.Join(path, strings.Trim(repo, "'"))
}

var descriptionHook = `#!/bin/bash

BRANCH="main"
FILE=README
while read oldrev newrev ref
do
        # only checking out the master (or whatever branch you would like to deploy)
        if [ "$ref" = "refs/heads/$BRANCH" ];
        then
				changed=$(git ls-tree $REV $FILE)
				if [ -n $changed ]; then
                	echo "Ref $ref received. updating description"
                	git show HEAD:README | head -n1 > description
				fi
        else
                echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."
        fi
done`

func writeDescriptionHook(path string) error {
	hooksDir := filepath.Join(path, "hooks")
	scriptPath := filepath.Join(hooksDir, "post-receive")
	err := os.MkdirAll(hooksDir, 0755)
	if err != nil {
		return fmt.Errorf("error creating hooks directory: %v", err)
	}

	file, err := os.Create(scriptPath)
	if err != nil {
		return fmt.Errorf("error creating post-receive file: %v", err)
	}

	defer file.Close()

	// Write the script to the file
	_, err = file.WriteString(descriptionHook)
	if err != nil {
		return fmt.Errorf("error writing script to file: %v", err)
	}

	// Set the file permissions to be executable
	err = os.Chmod(scriptPath, 0755)
	if err != nil {
		return fmt.Errorf("error setting file permissions: %v", err)
	}

	return nil
}