Added more phrases for the notices.

Theme resources can now be restricted to logged in users to avoid wasting bandwidth.
The template building step is now a flag to gosora.exe / Gosora.

Added build_templates.bat for test and development purposes.
Added some extra error checks to the batch files.
Fixed a compile error in the installer.
Fixed a big data race in the template transpiler and cleaned up a few loose ends.
Renamed CTemplateSet.log() to CTemplateSet.detail() to bring it in line with the /common/ logging
Renamed CTemplateSet.logf() to CTemplateSet.detailf() to bring it in line with the /common/ logging
You can now override templates in /templates/ without overwriting them by adding a modified version of a template with the same name in /templates/overrides/
Added Go Git as a dependency.
Removed config.go from the repository, you need to rely on the installer to generate this now, or make one yourself based on the implementation of it in the installer.
Travis now does tests for Go 1.10

Began work on the upgrader.
This commit is contained in:
Azareal 2018-03-21 05:56:33 +00:00
parent b20e295375
commit 2b86a17478
36 changed files with 549 additions and 158 deletions

View File

@ -1,11 +1,13 @@
language: go
go:
- 1.9
- 1.10
- master
before_install:
- cd $HOME
- git clone https://github.com/Azareal/Gosora
- cd Gosora
- mv config_default.noparse config.go
- chmod 755 ./update-deps-linux
- chmod 755 ./run-linux-tests
- ./update-deps-linux

View File

@ -50,7 +50,7 @@ It's entirely possible that your host might already have MySQL, so you might be
cd to the directory / folder the code is in. In other words, type cd followed by the location of the code and it should jump there.
Run ./install-gosora-linux
Run ./install-linux
Follow the instructions shown on the screen.
@ -65,7 +65,7 @@ Follow the instructions shown on the screen.
*Linux*
In the same directory you installed it, you simply have to type: ./run-gosora-linux
In the same directory you installed it, you simply have to type: ./run-linux
*Windows*
@ -73,13 +73,13 @@ Run run.bat
*Updating Dependencies*
You can update the dependencies which Gosora relies on by running update-deps.bat on Windows or ./update-deps-linux on Linux. These dependencies do not include Go or MySQL.
You can update the dependencies which Gosora relies on by running update-deps.bat on Windows or ./update-deps-linux on Linux. These dependencies do not include Go or MySQL, those have to be updated separately.
We're also looking into ways to distribute ready made executables for Windows. While this is not a complicated endeavour, the configuration settings currently get built with the rest of the program for speed, and we will likely have to change this.
With the introduction of the new settings system, we will begin moving some of the less critical settings out of the configuration file, and will likely have a config.xml or config.ini in the future to store the critical settings in.
You might have to run run.bat or ./run-gosora-linux twice after changing a template to make sure the templates are compiled properly. We'll be resolving this issue while rolling out the new compiled templates system to the rest of the routes.
You'll need to restart the server every-time you change a template, e.g. with `run.bat` or `./run-linux`
Several important features for saving memory in the templates system may have to be implemented before the new compiled template system is rolled out to every route. These features are coming fairly soon, but not before the higher priority items.
@ -90,6 +90,8 @@ An example of running the commands directly on Windows. We're looking into reduc
Linux is similar, however you might need to use cd and mv a bit more like in the shell files due to the differences in go build across platforms. Additionally, Linux doesn't require `StackExchange/wmi` or ``/x/sys/windows`
You also need to substitute the `gosora.exe` bits for `./Gosora` on Linux. For more info, you might want to take a gander inside the `./run-linux` and `./install-linux` shell files to see how they're implemented.
```bash
git clone https://github.com/Azareal/Gosora
@ -113,6 +115,8 @@ go get -u github.com/denisenkom/go-mssqldb
go get -u github.com/fsnotify/fsnotify
go get -u gopkg.in/src-d/go-git.v4/...
go generate
@ -130,6 +134,8 @@ go build ./install
install.exe
gosora.exe -build-templates
gosora.exe
```

2
build_templates.bat Normal file
View File

@ -0,0 +1,2 @@
gosora.exe -build-templates
pause

View File

@ -11,20 +11,13 @@ var EnableWebsockets = false // Put this in caps for consistency with the other
var wsHub WSHub
var errWsNouser = errors.New("This user isn't connected via WebSockets")
type WSHub struct {
}
type WSHub struct{}
func (_ *WSHub) guestCount() int {
return 0
}
func (_ *WSHub) guestCount() int { return 0 }
func (_ *WSHub) userCount() int {
return 0
}
func (_ *WSHub) userCount() int { return 0 }
func (hub *WSHub) broadcastMessage(_ string) error {
return nil
}
func (hub *WSHub) broadcastMessage(_ string) error { return nil }
func (hub *WSHub) pushMessage(_ int, _ string) error {
return errWsNouser
@ -38,5 +31,4 @@ func (hub *WSHub) pushAlerts(_ []int, _ int, _ string, _ string, _ int, _ int, _
return errWsNouser
}
func RouteWebsockets(_ http.ResponseWriter, _ *http.Request, _ User) {
}
func RouteWebsockets(_ http.ResponseWriter, _ *http.Request, _ User) {}

View File

@ -224,6 +224,9 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *
if len(theme.Resources) > 0 {
rlist := theme.Resources
for _, resource := range rlist {
if resource.Loggedin && !user.Loggedin {
continue
}
if resource.Location == "global" || resource.Location == "frontend" {
extarr := strings.Split(resource.Name, ".")
ext := extarr[len(extarr)-1]

View File

@ -4,7 +4,10 @@ import (
"html/template"
"log"
"net/http"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"./templates"
@ -119,7 +122,7 @@ var Template_ip_search_handle = func(pi IPSearchPage, w http.ResponseWriter) err
}
// ? - Add template hooks?
func compileTemplates() error {
func CompileTemplates() error {
var config tmpl.CTemplateConfig
config.Minify = Config.MinifyTemplates
config.SuperDebug = Dev.TemplateDebug
@ -235,6 +238,23 @@ func compileTemplates() error {
return err
}
var wg sync.WaitGroup
var writeTemplate = func(name string, content string) {
log.Print("Writing template '" + name + "'")
if content == "" {
log.Fatal("No content body")
}
wg.Add(1)
go func() {
err := writeFile("./template_"+name+".go", content)
if err != nil {
log.Fatal(err)
}
wg.Done()
}()
}
// Let plugins register their own templates
DebugLog("Registering the templates for the plugins")
config = c.GetConfig()
@ -247,20 +267,22 @@ func compileTemplates() error {
if err != nil {
return err
}
go writeTemplate(tmplItem.Name, compiledTmpl)
writeTemplate(tmplItem.Name, compiledTmpl)
}
log.Print("Writing the templates")
go writeTemplate("topic", topicIDTmpl)
go writeTemplate("topic_alt", topicIDAltTmpl)
go writeTemplate("profile", profileTmpl)
go writeTemplate("forums", forumsTmpl)
go writeTemplate("topics", topicsTmpl)
go writeTemplate("forum", forumTmpl)
go writeTemplate("login", loginTmpl)
go writeTemplate("register", registerTmpl)
go writeTemplate("ip_search", ipSearchTmpl)
go writeTemplate("error", errorTmpl)
writeTemplate("topic", topicIDTmpl)
writeTemplate("topic_alt", topicIDAltTmpl)
writeTemplate("profile", profileTmpl)
writeTemplate("forums", forumsTmpl)
writeTemplate("topics", topicsTmpl)
writeTemplate("forum", forumTmpl)
writeTemplate("login", loginTmpl)
writeTemplate("register", registerTmpl)
writeTemplate("ip_search", ipSearchTmpl)
writeTemplate("error", errorTmpl)
wg.Add(1)
go func() {
out := "package main\n\n"
for templateName, count := range c.TemplateFragmentCount {
@ -271,26 +293,15 @@ func compileTemplates() error {
if err != nil {
log.Fatal(err)
}
wg.Done()
}()
wg.Wait()
return nil
}
func writeTemplate(name string, content string) {
err := writeFile("./template_"+name+".go", content)
if err != nil {
log.Fatal(err)
}
}
func InitTemplates() error {
if Dev.DebugMode {
log.Print("Initialising the template system")
}
err := compileTemplates()
if err != nil {
return err
}
DebugLog("Initialising the template system")
// TODO: Add support for 64-bit integers
// TODO: Add support for floats
@ -365,7 +376,34 @@ func InitTemplates() error {
// The interpreted templates...
DebugLog("Loading the template files...")
Templates.Funcs(fmap)
template.Must(Templates.ParseGlob("templates/*"))
templateFiles, err := filepath.Glob("templates/*.html")
if err != nil {
return err
}
var templateFileMap = make(map[string]int)
for index, path := range templateFiles {
path = strings.Replace(path, "\\", "/", -1)
log.Print("templateFile: ", path)
templateFileMap[path] = index
}
overrideFiles, err := filepath.Glob("templates/overrides/*.html")
if err != nil {
return err
}
for _, path := range overrideFiles {
path = strings.Replace(path, "\\", "/", -1)
log.Print("overrideFile: ", path)
index, ok := templateFileMap["templates/"+strings.TrimPrefix(path, "templates/overrides/")]
if !ok {
log.Print("not ok: templates/" + strings.TrimPrefix(path, "templates/overrides/"))
templateFiles = append(templateFiles, path)
continue
}
templateFiles[index] = path
}
template.Must(Templates.ParseFiles(templateFiles...))
template.Must(Templates.ParseGlob("pages/*"))
return nil

View File

@ -109,9 +109,14 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
c.expectsInt = expectsInt
holdreflect := reflect.ValueOf(expectsInt)
res, err := ioutil.ReadFile(fileDir + name)
res, err := ioutil.ReadFile(fileDir + "overrides/" + name)
if err != nil {
return "", err
c.detail("override path: ", fileDir+"overrides/"+name)
c.detail("override err: ", err)
res, err = ioutil.ReadFile(fileDir + name)
if err != nil {
return "", err
}
}
content := string(res)
@ -125,14 +130,14 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
if err != nil {
return "", err
}
c.log(name)
c.detail(name)
out = ""
fname := strings.TrimSuffix(name, filepath.Ext(name))
c.templateList = map[string]*parse.Tree{fname: tree}
varholder := "tmpl_" + fname + "_vars"
c.log(c.templateList)
c.detail(c.templateList)
c.localVars = make(map[string]map[string]VarItemReflect)
c.localVars[fname] = make(map[string]VarItemReflect)
c.localVars[fname]["."] = VarItemReflect{".", varholder, holdreflect}
@ -203,16 +208,16 @@ w.Write([]byte(`, " + ", -1)
}
fmt.Println(" ")
}
c.log("Output!")
c.log(fout)
c.detail("Output!")
c.detail(fout)
return fout, nil
}
func (c *CTemplateSet) rootIterate(tree *parse.Tree, varholder string, holdreflect reflect.Value, fname string) (out string) {
c.log(tree.Root)
c.detail(tree.Root)
treeLength := len(tree.Root.Nodes)
for index, node := range tree.Root.Nodes {
c.log("Node:", node.String())
c.detail("Node:", node.String())
c.previousNode = c.currentNode
c.currentNode = node.Type()
if treeLength != (index + 1) {
@ -224,10 +229,10 @@ func (c *CTemplateSet) rootIterate(tree *parse.Tree, varholder string, holdrefle
}
func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value, templateName string, node parse.Node) (out string) {
c.log("in compileSwitch")
c.detail("in compileSwitch")
switch node := node.(type) {
case *parse.ActionNode:
c.log("Action Node")
c.detail("Action Node")
if node.Pipe == nil {
break
}
@ -235,30 +240,30 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value
out += c.compileSubswitch(varholder, holdreflect, templateName, cmd)
}
case *parse.IfNode:
c.log("If Node:")
c.log("node.Pipe", node.Pipe)
c.detail("If Node:")
c.detail("node.Pipe", node.Pipe)
var expr string
for _, cmd := range node.Pipe.Cmds {
c.log("If Node Bit:", cmd)
c.log("Bit Type:", reflect.ValueOf(cmd).Type().Name())
c.detail("If Node Bit:", cmd)
c.detail("Bit Type:", reflect.ValueOf(cmd).Type().Name())
expr += c.compileVarswitch(varholder, holdreflect, templateName, cmd)
c.log("Expression Step:", c.compileVarswitch(varholder, holdreflect, templateName, cmd))
c.detail("Expression Step:", c.compileVarswitch(varholder, holdreflect, templateName, cmd))
}
c.log("Expression:", expr)
c.detail("Expression:", expr)
c.previousNode = c.currentNode
c.currentNode = parse.NodeList
c.nextNode = -1
out = "if " + expr + " {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.List) + "}"
if node.ElseList == nil {
c.log("Selected Branch 1")
c.detail("Selected Branch 1")
return out + "\n"
}
c.log("Selected Branch 2")
c.detail("Selected Branch 2")
return out + " else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}\n"
case *parse.ListNode:
c.log("List Node")
c.detail("List Node")
for _, subnode := range node.Nodes {
out += c.compileSwitch(varholder, holdreflect, templateName, subnode)
}
@ -291,15 +296,15 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value
}
func (c *CTemplateSet) compileRangeNode(varholder string, holdreflect reflect.Value, templateName string, node *parse.RangeNode) (out string) {
c.log("Range Node!")
c.log(node.Pipe)
c.detail("Range Node!")
c.detail(node.Pipe)
var outVal reflect.Value
for _, cmd := range node.Pipe.Cmds {
c.log("Range Bit:", cmd)
c.detail("Range Bit:", cmd)
out, outVal = c.compileReflectSwitch(varholder, holdreflect, templateName, cmd)
}
c.log("Returned:", out)
c.log("Range Kind Switch!")
c.detail("Returned:", out)
c.detail("Range Kind Switch!")
switch outVal.Kind() {
case reflect.Map:
@ -308,7 +313,7 @@ func (c *CTemplateSet) compileRangeNode(varholder string, holdreflect reflect.Va
item = outVal.MapIndex(key)
}
c.log("Range item:", item)
c.detail("Range item:", item)
if !item.IsValid() {
panic("item" + "^\n" + "Invalid map. Maybe, it doesn't have any entries for the template engine to analyse?")
}
@ -334,11 +339,11 @@ func (c *CTemplateSet) compileRangeNode(varholder string, holdreflect reflect.Va
}
func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string) {
c.log("in compileSubswitch")
c.detail("in compileSubswitch")
firstWord := node.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
c.log("Field Node:", n.Ident)
c.detail("Field Node:", n.Ident)
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Variable declarations are coming soon! */
cur := holdreflect
@ -351,12 +356,12 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
// ! Might not work so well for non-struct pointers
skipPointers := func(cur reflect.Value, id string) reflect.Value {
if cur.Kind() == reflect.Ptr {
c.log("Looping over pointer")
c.detail("Looping over pointer")
for cur.Kind() == reflect.Ptr {
cur = cur.Elem()
}
c.log("Data Kind:", cur.Kind().String())
c.log("Field Bit:", id)
c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", id)
}
return cur
}
@ -364,8 +369,8 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
var assLines string
var multiline = false
for _, id := range n.Ident {
c.log("Data Kind:", cur.Kind().String())
c.log("Field Bit:", id)
c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", id)
cur = skipPointers(cur, id)
if !cur.IsValid() {
@ -382,7 +387,7 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
panic(varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
c.log("in-loop varbit: " + varbit)
c.detail("in-loop varbit: " + varbit)
if cur.Kind() == reflect.Map {
cur = cur.MapIndex(reflect.ValueOf(id))
varbit += "[\"" + id + "\"]"
@ -427,7 +432,7 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
}
varbit += cur.Type().Name() + ")"
}
c.log("End Cycle: ", varbit)
c.detail("End Cycle: ", varbit)
}
if multiline {
@ -446,20 +451,20 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
}
return out
case *parse.DotNode:
c.log("Dot Node:", node.String())
c.detail("Dot Node:", node.String())
return c.compileVarsub(varholder, holdreflect, "")
case *parse.NilNode:
panic("Nil is not a command x.x")
case *parse.VariableNode:
c.log("Variable Node:", n.String())
c.log(n.Ident)
c.detail("Variable Node:", n.String())
c.detail(n.Ident)
varname, reflectVal := c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
return c.compileVarsub(varname, reflectVal, "")
case *parse.StringNode:
return n.Quoted
case *parse.IdentifierNode:
c.log("Identifier Node:", node)
c.log("Identifier Node Args:", node.Args)
c.detail("Identifier Node:", node)
c.detail("Identifier Node Args:", node.Args)
out, outval, lit := c.compileIdentSwitch(varholder, holdreflect, templateName, node)
if lit {
return out
@ -471,7 +476,7 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
}
func (c *CTemplateSet) compileVarswitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string) {
c.log("in compileVarswitch")
c.detail("in compileVarswitch")
firstWord := node.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
@ -485,26 +490,26 @@ func (c *CTemplateSet) compileVarswitch(varholder string, holdreflect reflect.Va
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
return c.compileBoolsub(n.String(), varholder, templateName, holdreflect)
case *parse.ChainNode:
c.log("Chain Node:", n.Node)
c.log("Node Args:", node.Args)
c.detail("Chain Node:", n.Node)
c.detail("Node Args:", node.Args)
case *parse.IdentifierNode:
c.log("Identifier Node:", node)
c.log("Node Args:", node.Args)
c.detail("Identifier Node:", node)
c.detail("Node Args:", node.Args)
return c.compileIdentSwitchN(varholder, holdreflect, templateName, node)
case *parse.DotNode:
return varholder
case *parse.VariableNode:
c.log("Variable Node:", n.String())
c.log("Node Identifier:", n.Ident)
c.detail("Variable Node:", n.String())
c.detail("Node Identifier:", n.Ident)
out, _ = c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
case *parse.NilNode:
panic("Nil is not a command x.x")
case *parse.PipeNode:
c.log("Pipe Node!")
c.log(n)
c.log("Node Args:", node.Args)
c.detail("Pipe Node!")
c.detail(n)
c.detail("Node Args:", node.Args)
out += c.compileIdentSwitchN(varholder, holdreflect, templateName, node)
c.log("Out:", out)
c.detail("Out:", out)
default:
return c.unknownNode(firstWord)
}
@ -518,15 +523,15 @@ func (c *CTemplateSet) unknownNode(node parse.Node) (out string) {
}
func (c *CTemplateSet) compileIdentSwitchN(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string) {
c.log("in compileIdentSwitchN")
c.detail("in compileIdentSwitchN")
out, _, _ = c.compileIdentSwitch(varholder, holdreflect, templateName, node)
return out
}
func (c *CTemplateSet) dumpSymbol(pos int, node *parse.CommandNode, symbol string) {
c.log("symbol: ", symbol)
c.log("node.Args[pos + 1]", node.Args[pos+1])
c.log("node.Args[pos + 2]", node.Args[pos+2])
c.detail("symbol: ", symbol)
c.detail("node.Args[pos + 1]", node.Args[pos+1])
c.detail("node.Args[pos + 2]", node.Args[pos+2])
}
func (c *CTemplateSet) compareFunc(varholder string, holdreflect reflect.Value, templateName string, pos int, node *parse.CommandNode, compare string) (out string) {
@ -552,7 +557,7 @@ func (c *CTemplateSet) simpleMath(varholder string, holdreflect reflect.Value, t
}
func (c *CTemplateSet) compareJoin(varholder string, holdreflect reflect.Value, templateName string, pos int, node *parse.CommandNode, symbol string) (pos2 int, out string) {
c.logf("Building %s function", symbol)
c.detailf("Building %s function", symbol)
if pos == 0 {
fmt.Println("pos:", pos)
panic(symbol + " is missing a left operand")
@ -572,24 +577,24 @@ func (c *CTemplateSet) compareJoin(varholder string, holdreflect reflect.Value,
}
out = left + " " + symbol + " " + right
c.log("Left operand:", node.Args[pos-1])
c.log("Right operand:", node.Args[pos+1])
c.detail("Left operand:", node.Args[pos-1])
c.detail("Right operand:", node.Args[pos+1])
if !funcExists {
pos++
}
c.log("pos:", pos)
c.log("len(node.Args):", len(node.Args))
c.detail("pos:", pos)
c.detail("len(node.Args):", len(node.Args))
return pos, out
}
func (c *CTemplateSet) compileIdentSwitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string, val reflect.Value, literal bool) {
c.log("in compileIdentSwitch")
c.detail("in compileIdentSwitch")
ArgLoop:
for pos := 0; pos < len(node.Args); pos++ {
id := node.Args[pos]
c.log("pos:", pos)
c.log("ID:", id)
c.detail("pos:", pos)
c.detail("ID:", id)
switch id.String() {
case "not":
out += "!"
@ -683,7 +688,7 @@ ArgLoop:
literal = true
break ArgLoop
default:
c.log("Variable!")
c.detail("Variable!")
if len(node.Args) > (pos + 1) {
nextNode := node.Args[pos+1].String()
if nextNode == "or" || nextNode == "and" {
@ -697,7 +702,7 @@ ArgLoop:
}
func (c *CTemplateSet) compileReflectSwitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string, outVal reflect.Value) {
c.log("in compileReflectSwitch")
c.detail("in compileReflectSwitch")
firstWord := node.Args[0]
switch n := firstWord.(type) {
case *parse.FieldNode:
@ -710,8 +715,8 @@ func (c *CTemplateSet) compileReflectSwitch(varholder string, holdreflect reflec
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
return c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
case *parse.ChainNode:
c.log("Chain Node:", n.Node)
c.log("node.Args:", node.Args)
c.detail("Chain Node:", n.Node)
c.detail("node.Args:", node.Args)
case *parse.DotNode:
return varholder, holdreflect
case *parse.NilNode:
@ -723,13 +728,13 @@ func (c *CTemplateSet) compileReflectSwitch(varholder string, holdreflect reflec
}
func (c *CTemplateSet) compileIfVarsubN(varname string, varholder string, templateName string, cur reflect.Value) (out string) {
c.log("in compileIfVarsubN")
c.detail("in compileIfVarsubN")
out, _ = c.compileIfVarsub(varname, varholder, templateName, cur)
return out
}
func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templateName string, cur reflect.Value) (out string, val reflect.Value) {
c.log("in compileIfVarsub")
c.detail("in compileIfVarsub")
if varname[0] != '.' && varname[0] != '$' {
return varname, cur
}
@ -757,22 +762,22 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat
}
bits[0] = strings.TrimPrefix(bits[0], "$")
c.log("Cur Kind:", cur.Kind())
c.log("Cur Type:", cur.Type().Name())
c.detail("Cur Kind:", cur.Kind())
c.detail("Cur Type:", cur.Type().Name())
for _, bit := range bits {
c.log("Variable Field:", bit)
c.detail("Variable Field:", bit)
if bit == "" {
continue
}
// TODO: Fix this up so that it works for regular pointers and not just struct pointers. Ditto for the other cur.Kind() == reflect.Ptr we have in this file
if cur.Kind() == reflect.Ptr {
c.log("Looping over pointer")
c.detail("Looping over pointer")
for cur.Kind() == reflect.Ptr {
cur = cur.Elem()
}
c.log("Data Kind:", cur.Kind().String())
c.log("Field Bit:", bit)
c.detail("Data Kind:", cur.Kind().String())
c.detail("Field Bit:", bit)
}
cur = cur.FieldByName(bit)
@ -784,13 +789,13 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat
if !cur.IsValid() {
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
}
c.log("Data Kind:", cur.Kind())
c.log("Data Type:", cur.Type().Name())
c.detail("Data Kind:", cur.Kind())
c.detail("Data Type:", cur.Type().Name())
}
c.log("Out Value:", out)
c.log("Out Kind:", cur.Kind())
c.log("Out Type:", cur.Type().Name())
c.detail("Out Value:", out)
c.detail("Out Kind:", cur.Kind())
c.detail("Out Type:", cur.Type().Name())
for _, varItem := range c.varList {
if strings.HasPrefix(out, varItem.Destination) {
@ -798,9 +803,9 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat
}
}
c.log("Out Value:", out)
c.log("Out Kind:", cur.Kind())
c.log("Out Type:", cur.Type().Name())
c.detail("Out Value:", out)
c.detail("Out Kind:", cur.Kind())
c.detail("Out Type:", cur.Type().Name())
_, ok := c.stats[out]
if ok {
@ -813,7 +818,7 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat
}
func (c *CTemplateSet) compileBoolsub(varname string, varholder string, templateName string, val reflect.Value) string {
c.log("in compileBoolsub")
c.detail("in compileBoolsub")
out, val := c.compileIfVarsub(varname, varholder, templateName, val)
// TODO: What if it's a pointer or an interface? I *think* we've got pointers handled somewhere, but not interfaces which we don't know the types of at compile time
switch val.Kind() {
@ -834,7 +839,7 @@ func (c *CTemplateSet) compileBoolsub(varname string, varholder string, template
}
func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value, assLines string) (out string) {
c.log("in compileVarsub")
c.detail("in compileVarsub")
// Is this a literal string?
if len(varname) != 0 && varname[0] == '"' {
@ -858,8 +863,8 @@ func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value, assLines
val = val.Elem()
}
c.log("varname: ", varname)
c.log("assLines: ", assLines)
c.detail("varname: ", varname)
c.detail("assLines: ", assLines)
switch val.Kind() {
case reflect.Int:
c.importMap["strconv"] = "strconv"
@ -883,13 +888,13 @@ func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value, assLines
fmt.Println("Unknown Type:", val.Type().Name())
panic("-- I don't know what this variable's type is o.o\n")
}
c.log("out: ", out)
c.detail("out: ", out)
return assLines + out
}
func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) {
c.log("in compileSubtemplate")
c.log("Template Node: ", node.Name)
c.detail("in compileSubtemplate")
c.detail("Template Node: ", node.Name)
fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name))
varholder := "tmpl_" + fname + "_vars"
@ -904,16 +909,21 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
case *parse.NilNode:
panic("Nil is not a command x.x")
default:
c.log("Unknown Node: ", firstWord)
c.detail("Unknown Node: ", firstWord)
panic("")
}
}
}
// TODO: Cascade errors back up the tree to the caller?
res, err := ioutil.ReadFile(c.fileDir + node.Name)
res, err := ioutil.ReadFile(c.fileDir + "overrides/" + node.Name)
if err != nil {
log.Fatal(err)
c.detail("override path: ", c.fileDir+"overrides/"+node.Name)
c.detail("override err: ", err)
res, err = ioutil.ReadFile(c.fileDir + node.Name)
if err != nil {
log.Fatal(err)
}
}
content := string(res)
@ -930,7 +940,7 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
c.templateList[fname] = tree
subtree := c.templateList[fname]
c.log("subtree.Root", subtree.Root)
c.detail("subtree.Root", subtree.Root)
c.localVars[fname] = make(map[string]VarItemReflect)
c.localVars[fname]["."] = VarItemReflect{".", varholder, holdreflect}
@ -943,13 +953,13 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
// TODO: Should we rethink the way the log methods work or their names?
func (c *CTemplateSet) log(args ...interface{}) {
func (c *CTemplateSet) detail(args ...interface{}) {
if c.config.SuperDebug {
fmt.Println(args...)
}
}
func (c *CTemplateSet) logf(left string, args ...interface{}) {
func (c *CTemplateSet) detailf(left string, args ...interface{}) {
if c.config.SuperDebug {
fmt.Printf(left, args...)
}

View File

@ -71,6 +71,7 @@ type TemplateMapping struct {
type ThemeResource struct {
Name string
Location string
Loggedin bool // Only serve this resource to logged in users
}
type ThemeStmts struct {

View File

@ -0,0 +1,15 @@
package adventure
// We're experimenting with struct tags here atm
type Adventure struct {
ID int `schema:"name=aid;primary;auto"`
Name string `schema:"name=name;type=short_text"`
Desc string `schema:"name=desc;type=text"`
CreatedBy int `schema:"name=createdBy"`
//CreatedBy int `schema:"name=createdBy;relatesTo=users.uid"`
}
// TODO: Should we add a table interface?
func (adventure *Adventure) GetTable() string {
return "adventure"
}

View File

@ -0,0 +1,8 @@
package adventure
type AdventureStore interface {
Create() (int, error)
}
type DefaultAdventureStore struct {
}

View File

@ -0,0 +1,7 @@
{
"UName":"adventure",
"Name":"Adventure",
"Author":"Azareal",
"URL":"https://github.com/Azareal/Gosora",
"Skip":true
}

View File

@ -0,0 +1 @@
This file is here so that Git will include this folder in the repository.

View File

@ -0,0 +1 @@
This file is here so that Git will include this folder in the repository.

View File

@ -28,6 +28,9 @@ go get -u github.com/bamiaux/rez
echo "Installing fsnotify"
go get -u github.com/fsnotify/fsnotify
echo "Installing Go Git"
go get -u gopkg.in/src-d/go-git.v4/...
echo "Building the installer"
cd ./install

View File

@ -85,6 +85,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel%
)
echo Installing Go Git
go get -u gopkg.in/src-d/go-git.v4/...
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the installer
go generate

View File

@ -198,7 +198,8 @@ func handleDatabaseDetails() (adap install.InstallAdapter, ok bool) {
var dbUsername string
var dbPassword string
var dbName string
var dbPort string
// TODO: Let the admin set the database port?
//var dbPort string
for {
fmt.Println("Which database adapter do you wish to use? mysql, mssql, or mysql? Default: mysql")

View File

@ -231,7 +231,18 @@
"NoticePhrases": {
"account_banned":"Your account has been suspended. Some of your permissions may have been revoked.",
"account_inactive":"Your account hasn't been activated yet. Some features may remain unavailable until it is."
"account_inactive":"Your account hasn't been activated yet. Some features may remain unavailable until it is.",
"account_password_updated":"Your password was successfully updated",
"account_avatar_updated":"Your avatar was successfully updated",
"account_username_updated":"Your username was successfully updated",
"account_mail_disabled":"The mail system is currently disabled.",
"account_mail_verify_success":"Your email was successfully verified",
"panel_forum_created":"The forum was successfully created",
"panel_forum_deleted":"The forum was successfully deleted",
"panel_forum_updated":"The forum was successfully updated",
"panel_forum_perms_updated":"The forum permissions were successfully updated",
"panel_user_updated":"The user was successfully updated"
},
"TmplPhrases": {

11
main.go
View File

@ -7,6 +7,7 @@
package main
import (
"flag"
"fmt"
"io"
"log"
@ -217,6 +218,16 @@ func main() {
}
defer db.Close()
buildTemplates := flag.Bool("build-templates", false, "build the templates")
flag.Parse()
if *buildTemplates {
err = common.CompileTemplates()
if err != nil {
log.Fatal(err)
}
return
}
err = afterDBInit()
if err != nil {
log.Fatal(err)

View File

@ -129,12 +129,12 @@ func routeProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user
if err != nil {
return common.LocalError("Invalid UID", w, r, user)
}
profileOwner, err := common.Users.Get(uid)
if err == ErrNoRows {
return common.LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
} else if err != nil {
return common.InternalError(err,w,r)
return common.InternalError(err, w, r)
}
content := common.PreparseMessage(r.PostFormValue("reply-content"))
@ -278,7 +278,7 @@ func routeAccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user
// Log the user out as a safety precaution
common.Auth.ForceLogout(user.ID)
headerVars.NoticeList = append(headerVars.NoticeList, "Your password was successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_password_updated"))
pi := common.Page{"Edit Password", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_critical", w, r, &user, &pi) {
return nil
@ -367,7 +367,7 @@ func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c
return common.InternalError(err, w, r)
}
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
headerVars.NoticeList = append(headerVars.NoticeList, "Your avatar was successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_avatar_updated"))
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
@ -410,7 +410,7 @@ func routeAccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user
}
user.Name = newUsername
headerVars.NoticeList = append(headerVars.NoticeList, "Your username was successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_username_updated"))
pi := common.Page{"Edit Username", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
return nil
@ -461,7 +461,7 @@ func routeAccountEditEmail(w http.ResponseWriter, r *http.Request, user common.U
emailList = append(emailList, email)
}
if !common.Site.EnableEmails {
headerVars.NoticeList = append(headerVars.NoticeList, "The mail system is currently disabled.")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_disabled"))
}
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
@ -531,9 +531,9 @@ func routeAccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, us
}
if !common.Site.EnableEmails {
headerVars.NoticeList = append(headerVars.NoticeList, "The mail system is currently disabled.")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_disabled"))
}
headerVars.NoticeList = append(headerVars.NoticeList, "Your email was successfully verified")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_mail_verify_success"))
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_email", w, r, &user, &pi) {
return nil

View File

@ -198,11 +198,11 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User)
}
if r.FormValue("created") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, "The forum was successfully created")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_created"))
} else if r.FormValue("deleted") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, "The forum was successfully deleted")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_deleted"))
} else if r.FormValue("updated") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, "The forum was successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_updated"))
}
pi := common.PanelPage{common.GetTitlePhrase("panel_forums"), user, headerVars, stats, "forums", forumList, nil}
@ -335,7 +335,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
}
if r.FormValue("updated") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, "The forum was successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forum_updated"))
}
pi := common.PanelEditForumPage{common.GetTitlePhrase("panel_edit_forum"), user, headerVars, stats, "forums", forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist}
@ -501,7 +501,7 @@ func routePanelForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, us
addNameLangToggle("MoveTopic", forumPerms.MoveTopic)
if r.FormValue("updated") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, "The forum permissions were successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_forums_perms_updated"))
}
pi := common.PanelEditForumGroupPage{common.GetTitlePhrase("panel_edit_forum"), user, headerVars, stats, "forums", forum.ID, gid, forum.Name, forum.Desc, forum.Active, forum.Preset, formattedPermList}
@ -1794,7 +1794,7 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use
}
if r.FormValue("updated") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, "The user was successfully updated")
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("panel_user_updated"))
}
pi := common.PanelPage{common.GetTitlePhrase("panel_edit_user"), user, headerVars, stats, "users", groupList, targetUser}

20
plugin_adventure.go Normal file
View File

@ -0,0 +1,20 @@
// WIP - Experimental adventure plugin, this might find a new home soon, but it's here to stress test Gosora's extensibility for now
package main
import "./common"
func init() {
common.Plugins["adventure"] = common.NewPlugin("adventure", "WIP", "Azareal", "http://github.com/Azareal", "", "", "", initAdventure, nil, deactivateAdventure, installAdventure, nil)
}
func initAdventure() error {
return nil
}
// TODO: Change the signature to return an error?
func deactivateAdventure() {
}
func installAdventure() error {
return nil
}

View File

@ -2,5 +2,7 @@ echo "Generating the dynamic code"
go generate
echo "Building Gosora"
go build -o Gosora
echo "Building the templates"
./Gosora -build-templates
echo "Running Gosora"
./Gosora

View File

@ -2,5 +2,7 @@ echo "Generating the dynamic code"
go generate
echo "Building Gosora"
go build -o Gosora -tags no_ws
echo "Building the templates"
./Gosora -build-templates
echo "Running Gosora"
./Gosora

View File

@ -14,6 +14,10 @@ if %errorlevel% neq 0 (
)
echo Running the router generator
router_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build ./query_gen
@ -23,6 +27,10 @@ if %errorlevel% neq 0 (
)
echo Running the query generator
query_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the executable
go build -o gosora.exe -tags no_ws
@ -31,6 +39,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel%
)
echo Building the templates
gosora.exe -build-templates
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Running Gosora
gosora.exe
pause

15
run.bat
View File

@ -14,6 +14,10 @@ if %errorlevel% neq 0 (
)
echo Running the router generator
router_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build ./query_gen
@ -23,6 +27,10 @@ if %errorlevel% neq 0 (
)
echo Running the query generator
query_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the executable
go build -o gosora.exe
@ -31,6 +39,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel%
)
echo Building the templates
gosora.exe -build-templates
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Running Gosora
gosora.exe
rem Or you could redirect the output to a file

View File

@ -14,6 +14,10 @@ if %errorlevel% neq 0 (
)
echo Running the router generator
router_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build ./query_gen
@ -23,6 +27,10 @@ if %errorlevel% neq 0 (
)
echo Running the query generator
query_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the executable
go build -o gosora.exe -tags mssql
@ -31,6 +39,13 @@ if %errorlevel% neq 0 (
exit /b %errorlevel%
)
echo Building the templates
gosora.exe -build-templates
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Running Gosora
gosora.exe
pause

View File

@ -14,6 +14,10 @@ if %errorlevel% neq 0 (
)
echo Running the router generator
router_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build ./query_gen
@ -23,6 +27,10 @@ if %errorlevel% neq 0 (
)
echo Running the query generator
query_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the executable
go test

View File

@ -14,6 +14,10 @@ if %errorlevel% neq 0 (
)
echo Running the router generator
router_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the query generator
go build ./query_gen
@ -23,6 +27,10 @@ if %errorlevel% neq 0 (
)
echo Running the query generator
query_gen.exe
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the executable
go test -tags mssql

3
schema/schema.json Normal file
View File

@ -0,0 +1,3 @@
{
"Version":"0"
}

View File

@ -0,0 +1 @@
This file is here so that Git will include this folder in the repository.

View File

@ -19,11 +19,13 @@
},
{
"Name":"trumbowyg/trumbowyg.min.js",
"Location":"global"
"Location":"global",
"Loggedin": true
},
{
"Name":"trumbowyg/ui/trumbowyg.custom.css",
"Location":"global"
"Location":"global",
"Loggedin":true
},
{
"Name":"cosora/misc.js",

View File

@ -26,4 +26,7 @@ echo "Updating the Rez Image Resizer"
go get -u github.com/bamiaux/rez
echo "Updating fsnotify"
go get -u github.com/fsnotify/fsnotify
go get -u github.com/fsnotify/fsnotify
echo "Updating Go Git"
go get -u gopkg.in/src-d/go-git.v4/...

View File

@ -82,5 +82,12 @@ if %errorlevel% neq 0 (
exit /b %errorlevel%
)
echo Updating Go Git
go get -u gopkg.in/src-d/go-git.v4/...
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo The dependencies were successfully updated
pause

103
update.bat Normal file
View File

@ -0,0 +1,103 @@
@echo off
echo Updating the dependencies
echo Updating the MySQL Driver
go get -u github.com/go-sql-driver/mysql
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating the PostgreSQL Driver
go get -u github.com/lib/pq
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating the MSSQL Driver
go get -u github.com/denisenkom/go-mssqldb
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating the bcrypt library
go get -u golang.org/x/crypto/bcrypt
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating /x/sys/windows (dependency for gopsutil)
go get -u golang.org/x/sys/windows
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating wmi (dependency for gopsutil)
go get -u github.com/StackExchange/wmi
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating the gopsutil library
go get -u github.com/Azareal/gopsutil
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating the WebSockets library
go get -u github.com/gorilla/websocket
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating Sourcemap (dependency for OttoJS)
go get -u gopkg.in/sourcemap.v1
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating OttoJS
go get -u github.com/robertkrimen/otto
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating the Rez Image Resizer
go get -u github.com/bamiaux/rez
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating fsnotify
go get -u github.com/fsnotify/fsnotify
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Updating Go Git
go get -u gopkg.in/src-d/go-git.v4/...
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
echo Building the updater
go generate
go build ./updater
if %errorlevel% neq 0 (
pause
exit /b %errorlevel%
)
updater.exe

68
updater/main.go Normal file
View File

@ -0,0 +1,68 @@
package main
import (
"bufio"
"fmt"
"os"
"runtime/debug"
"gopkg.in/src-d/go-git.v4"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Capture panics instead of closing the window at a superhuman speed before the user can read the message on Windows
defer func() {
r := recover()
if r != nil {
fmt.Println(r)
debug.PrintStack()
pressAnyKey(scanner)
return
}
}()
err := updater(scanner)
if err != nil {
fmt.Println(err)
}
}
func pressAnyKey(scanner *bufio.Scanner) {
fmt.Println("Please press enter to exit...")
for scanner.Scan() {
_ = scanner.Text()
return
}
}
func updater(scanner *bufio.Scanner) error {
fmt.Println("Welcome to Gosora's Upgrader")
fmt.Print("We're going to check for new updates, please wait patiently")
repo, err := git.PlainOpen("./.git")
if err != nil {
return err
}
workTree, err := repo.Worktree()
if err != nil {
return err
}
err = workTree.Pull(&git.PullOptions{RemoteName: "origin"})
if err != nil {
return err
}
fmt.Println("Updated to the latest commit")
headRef, err := repo.Head()
if err != nil {
return err
}
fmt.Println("Commit details:")
commit, err := repo.CommitObject(headRef.Hash())
return err
}