Skip to content

Commit

Permalink
Add basic ability to clone repo and run ledger command
Browse files Browse the repository at this point in the history
  • Loading branch information
mput committed Feb 10, 2024
1 parent b260f75 commit 7d0cbeb
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 21 deletions.
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env sh

export TELEGRAM_TOKEN="YOUR_TELEGRAM_TOKEN"
export URL="YOUR_URL"
export GIT_URL="YOUR_GIT_URL"
## Fine-grained personal access tokens for repo with RW Contents scope
export GIT_ACCESS_TOKEN="YOUR_GIT"
35 changes: 35 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install ledger
run:
sudo apt-get update
sudo apt-get install -y ledger

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...
env:
GIT_URL: ${{ secrets.GIT_URL }}
GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/dev/dev-env-setup.sh
.env
/tmp/
/.env.test
51 changes: 51 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
run:
timeout: 5m
output:
format: tab
skip-dirs:
- vendor

linters-settings:
govet:
check-shadowing: true
goconst:
min-len: 2
min-occurrences: 2
misspell:
locale: US
lll:
line-length: 140
gocritic:
enabled-tags:
- performance
- style
- experimental
- opinionated
- diagnostic
- security

linters:
enable:
- sloglint
- bodyclose
- megacheck
- revive
- govet
- unconvert
- gas
- gocyclo
- dupl
- misspell
- unparam
- unused
- typecheck
- ineffassign
- stylecheck
- gochecknoinits
- exportloopref
- gocritic
- nakedret
- gosimple
- prealloc
- goconst
fast: false
4 changes: 2 additions & 2 deletions app/bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Opts struct {
Token string `long:"token" env:"TOKEN" required:"true" description:"telegram bot token"`
} `group:"telegram" namespace:"telegram" env-namespace:"TELEGRAM"`

Url string `long:"url" env:"URL" required:"true" description:"bot url"`
URL string `long:"url" env:"URL" required:"true" description:"bot url"`
Debug bool `long:"debug" env:"DEBUG" description:"debug mode"`
}

Expand All @@ -30,7 +30,7 @@ func (opts *Opts) Execute() error {

dispatcher := ext.NewDispatcher(&ext.DispatcherOpts{
// If an error is returned by a handler, log it and continue going.
Error: func(b *gotgbot.Bot, ctx *ext.Context, err error) ext.DispatcherAction {
Error: func(_ *gotgbot.Bot, _ *ext.Context, err error) ext.DispatcherAction {
log.Println("an error occurred while handling update:", err.Error())
return ext.DispatcherActionNoop
},
Expand Down
85 changes: 85 additions & 0 deletions app/bot/repo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package bot

import (
"fmt"
"log"
"os/exec"
"strings"

"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/transport/http"
)

func cloneRepo(url, token, workdir string) error {
log.Printf("[INFO] cloning %s..\n", url)

_, err := git.PlainClone(workdir, false, &git.CloneOptions{
URL: url,
Auth: &http.BasicAuth{
Username: "username",
Password: token,
},
Depth: 2})

if err != nil {
return fmt.Errorf("unable to clone %s into %s: %v", url, workdir, err)
}

return nil
}

func FetchOrCloneRepo(url, token, workdir string) error {
r, err := git.PlainOpen(workdir)

if err == git.ErrRepositoryNotExists {
return cloneRepo(url, token, workdir)
}
if err != nil {
return fmt.Errorf("unable to ensure repo %s: %v", workdir, err)
}

log.Printf("[INFO] fetching repo %s..\n", url)
err = r.Fetch(&git.FetchOptions{
Auth: &http.BasicAuth{
Username: "username",
Password: token,
},
Depth: 2})

if err == git.NoErrAlreadyUpToDate {
return nil
}

if err != nil {
return fmt.Errorf("unable to fetch %s: %v", workdir, err)
}

return nil
}

const ledgerBinary = "ledger"

func ExecLedgerCmd(url, token, workdir, file string, arg ...string) (string, error) {
log.Printf("[INFO] executing ledger command: %s on file: %s\n", arg, file)

err := FetchOrCloneRepo(url, token, workdir)

if err != nil {
return "", fmt.Errorf("unable to ensure repo: %v", err)
}

arg = append([]string{"-f", file, "--pedantic"}, arg...)

cmd := exec.Command(ledgerBinary, arg...)
cmd.Dir = workdir
var out strings.Builder
var errOut strings.Builder
cmd.Stdout = &out
cmd.Stderr = &errOut

err = cmd.Run()
if err != nil {
return "", fmt.Errorf("unable to run ledger command: %v", errOut.String())
}
return out.String(), nil
}
122 changes: 122 additions & 0 deletions app/bot/repo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package bot

import (
"os"
"strings"
"testing"

"github.com/joho/godotenv"
)



func TestCloneRepo(t *testing.T) {
godotenv.Load("../../.env")
tmpDir := t.TempDir()

gitURL := os.Getenv("GIT_URL")
if gitURL == "" {
t.Errorf("GIT_URL is not set")
return
}

gitAccessToken := os.Getenv("GIT_ACCESS_TOKEN")
if gitAccessToken == "" {
t.Errorf("GIT_ACCESS_TOKEN is not set")
return
}

t.Run("happy path", func(t *testing.T) {
err := cloneRepo(gitURL, gitAccessToken, tmpDir)
if err != nil {
t.Errorf("Error: %v", err)
return
}
_, err = os.Stat(tmpDir + "/main.ledger")
if err != nil {
t.Errorf("Can't access main ledger file: %v", err)
return
}
})
}

func TestExecLedgerCmd(t *testing.T) {
godotenv.Load("../../.env.test")
tmpDir := t.TempDir()

gitURL := os.Getenv("GIT_URL")
if gitURL == "" {
t.Errorf("GIT_URL is not set")
return
}

gitAccessToken := os.Getenv("GIT_ACCESS_TOKEN")
if gitAccessToken == "" {
t.Errorf("GIT_ACCESS_TOKEN is not set")
return
}

t.Run("happy path", func(t *testing.T) {

res, err := ExecLedgerCmd(gitURL, gitAccessToken, tmpDir, "main.ledger", "bal")
if err != nil {
t.Errorf("Command execution error: %v", err)
return
}

expected := `
1049,30 EUR Assets
949,30 EUR Cards:Bank
100,00 EUR Cash:Main
-1100,00 EUR Equity:Opening-Balance
50,70 EUR Expenses
4,00 EUR Addictions:Cigarettes
40,15 EUR Food
11,80 EUR Coffee
28,35 EUR Eat-Out
6,55 EUR Personal:Subscriptions
--------------------
0`

if res == "" {
t.Errorf("Command executed with empty result")
return
}

if strings.TrimSpace(res) != strings.TrimSpace(expected) {
t.Errorf("Command executed with unexpected result: %s", res)
return
}

})

t.Run("wrong file", func(t *testing.T) {
_, err := ExecLedgerCmd(gitURL, gitAccessToken, tmpDir, "not-exists.ledger", "bal")

if err == nil {
t.Errorf("Error excpected")
return
}

if !strings.Contains(err.Error(), "Cannot read journal file") {
t.Errorf("Unexpected error: %v", err)
return
}
})


t.Run("wrong command", func(t *testing.T) {
_, err := ExecLedgerCmd(gitURL, gitAccessToken, tmpDir, "main.ledger", "unknown-command")

if err == nil {
t.Errorf("Error excpected")
return
}

if !strings.Contains(err.Error(), "Unrecognized command 'unknown-command'") {
t.Errorf("Unexpected error: %v", err)
return
}
})

}
12 changes: 0 additions & 12 deletions app/cmd/server.go

This file was deleted.

2 changes: 0 additions & 2 deletions dev/dev-env-setup.sh.example

This file was deleted.

34 changes: 31 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
module github.com/mput/teledger

go 1.21.6
go 1.21.7

require github.com/jessevdk/go-flags v1.5.0
require github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.24
require golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 // indirect

require (
github.com/PaulSonOfLars/gotgbot/v2 v2.0.0-rc.24
github.com/go-git/go-git/v5 v5.11.0
github.com/joho/godotenv v1.5.1
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
Loading

0 comments on commit 7d0cbeb

Please sign in to comment.