dotfiles

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit e7c90ef4648dcd2410188bb18fb702d9bfe75410 (tree)
parent 8a6b5646a4c63d26796c305ddb9566ea30bdc8ae
Author: Motiejus Jakštys <desired.mta@gmail.com>
Date:   Tue,  9 Jun 2020 09:30:29 +0300

move src/

Diffstat:
Dsrc/joplin2site/Makefile | 3---
Dsrc/joplin2site/go.mod | 10----------
Dsrc/joplin2site/go.sum | 21---------------------
Dsrc/joplin2site/internal/cli/cli.go | 33---------------------------------
Dsrc/joplin2site/internal/cli/cli_test.go | 44--------------------------------------------
Dsrc/joplin2site/internal/html/html.go | 41-----------------------------------------
Dsrc/joplin2site/internal/html/index.go | 19-------------------
Dsrc/joplin2site/internal/note/note.go | 196-------------------------------------------------------------------------------
Dsrc/joplin2site/internal/note/note_test.go | 73-------------------------------------------------------------------------
Dsrc/joplin2site/internal/page/index.go | 16----------------
Dsrc/joplin2site/internal/page/page.go | 140-------------------------------------------------------------------------------
Dsrc/joplin2site/internal/page/page_test.go | 72------------------------------------------------------------------------
Dsrc/joplin2site/internal/page/render.go | 46----------------------------------------------
Dsrc/joplin2site/main.go | 23-----------------------
14 files changed, 0 insertions(+), 737 deletions(-)

diff --git a/src/joplin2site/Makefile b/src/joplin2site/Makefile @@ -1,3 +0,0 @@ -.PHONY: test -test: - go test -cover ./... diff --git a/src/joplin2site/go.mod b/src/joplin2site/go.mod @@ -1,10 +0,0 @@ -module github.com/motiejus/dotfiles/joplin2site - -go 1.13 - -require ( - github.com/gomarkdown/markdown v0.0.0-20200513213024-62c5e2c608cc - github.com/jessevdk/go-flags v1.4.0 - github.com/stretchr/testify v1.6.0 - gopkg.in/yaml.v2 v2.3.0 -) diff --git a/src/joplin2site/go.sum b/src/joplin2site/go.sum @@ -1,21 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gomarkdown/markdown v0.0.0-20200513213024-62c5e2c608cc h1:T+Fwk3llJdUIQeBI8fC/ARqRD5mWy3AE5I6ZU3VkIw8= -github.com/gomarkdown/markdown v0.0.0-20200513213024-62c5e2c608cc/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= -github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/motiejus/dotfiles v0.0.0-20200605063426-19a173d27a2c h1:Sd1o/rS6JlslzP0IpjfzzdYVletzRc2mdMO4VWiybMs= -github.com/motiejus/dotfiles v0.0.0-20200607081742-b7277bd7edc7 h1:aiSSPWEYINcmwBfaXwiO+Cssj8TbF7b5X6tPhWr1kaM= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= -github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/joplin2site/internal/cli/cli.go b/src/joplin2site/internal/cli/cli.go @@ -1,33 +0,0 @@ -package cli - -import ( - "fmt" - "io" - - goflags "github.com/jessevdk/go-flags" -) - -type flags struct { - PositionalArgs struct { - Dir goflags.Filename `long:"dir" description:"Directory with Joplin notes"` - } `positional-args:"yes" required:"yes"` -} - -type App struct { - Args []string - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer -} - -func (a *App) Run() error { - var opts flags - args, err := goflags.ParseArgs(&opts, a.Args) - if err != nil { - return err - } - if len(args) != 0 { - return fmt.Errorf("Got unexpected arguments: %q", args) - } - return nil -} diff --git a/src/joplin2site/internal/cli/cli_test.go b/src/joplin2site/internal/cli/cli_test.go @@ -1,44 +0,0 @@ -package cli - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestFlags(t *testing.T) { - tests := []struct { - name string - args []string - wantErr string - }{ - { - name: "ok", - args: []string{"dir1"}, - }, - { - name: "missing arg1", - args: []string{}, - wantErr: "the required argument `Dir` was not provided", - }, - { - name: "unexpected arguments", - args: []string{"dir1", "bar1"}, - wantErr: `Got unexpected arguments: ["bar1"]`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - a := &App{ - Args: tt.args, - } - err := a.Run() - if tt.wantErr != "" { - assert.EqualError(t, err, tt.wantErr) - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/src/joplin2site/internal/html/html.go b/src/joplin2site/internal/html/html.go @@ -1,41 +0,0 @@ -package html - -import ( - "fmt" - - inote "github.com/motiejus/dotfiles/joplin2site/internal/note" - ipage "github.com/motiejus/dotfiles/joplin2site/internal/page" -) - -type ( - // Webpage is the resulting URL -> []byte map. - Webpage map[string][]byte - - // pageContext contains everything that's necessary to render a page. - pageContext struct { - ipage.Page - - Prev *ipage.Page - Next *ipage.Page - } -) - -// Render accepts a TLD and returns URL -> []bytes mapping. -func Render(dir, tld string) (Webpage, error) { - notes, err := inote.ListNotes(dir) - if err != nil { - return nil, fmt.Errorf("failed to get notes: %w", err) - } - tree := inote.BuildTree(notes) - if err := notes.Shake(tree, tld); err != nil { - return nil, err - } - - // Convert all publish-able notes to pages. - pages, err := ipage.ToPublishablePages(notes) - if err != nil { - return nil, err - } - - return nil, nil -} diff --git a/src/joplin2site/internal/html/index.go b/src/joplin2site/internal/html/index.go @@ -1,19 +0,0 @@ -package html - -const _pageTemplate = ` -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <title>{{ .Title }}</title> - <meta name="viewport" content="width=device-width,initial-scale=1"> - <style> - .pd{width:4em;flex-shrink:0;padding-bottom:.9em} - .par div{display:flex} - </style> -</head> -<body> -{{ block "body" }} -</body> -</html> -` diff --git a/src/joplin2site/internal/note/note.go b/src/joplin2site/internal/note/note.go @@ -1,196 +0,0 @@ -package note - -import ( - "fmt" - "io/ioutil" - "strings" - - "time" - - "gopkg.in/yaml.v2" -) - -// Note is how Joplin understands the note. -type Note struct { - ID string `yaml:"id"` - ParentID string `yaml:"parent_id"` - Title string `yaml:"-"` - Body string `yaml:"-"` - CreatedTime time.Time `yaml:"created_time"` - UpdatedTime time.Time `yaml:"updated_time"` - IsConflict int `yaml:"is_conflict"` - Latitude float64 `yaml:"latitude"` - Longitude float64 `yaml:"longitude"` - Altitude float64 `yaml:"altitude"` - Author string `yaml:"author"` - SourceUrl string `yaml:"source_url"` - IsTODO int `yaml:"is_todo"` - TODODue int `yaml:"todo_due"` - TODOCompleted int `yaml:"todo_completed"` - Source string `yaml:"source"` - SourceApplication string `yaml:"source_application"` - ApplicationData string `yaml:"application_data"` - Order int `yaml:"order"` - UserCreatedTime time.Time `yaml:"user_created_time"` - UserUpdatedTime time.Time `yaml:"user_updated_time"` - EncryptionCipherText string `yaml:"encryption_cipher_text"` - EncryptionApplied int `yaml:"encryption_applied"` - MarkupLanguage int `yaml:"markup_language"` - IsShared int `yaml:"is_shared"` - BodyHTML string `yaml:"body_html"` - // BaseURL is if `body_html` is provided and contains relative URLs, provide the `base_url` parameter too so that all the URLs can be converted to absolute ones. The base URL is basically where the HTML was fetched from, minus the query (everything after the '?'). For example if the original page was `https://stackoverflow.com/search?q=%5Bjava%5D+test`, the base URL is `https://stackoverflow.com/search`. - BaseUrl string `yaml:"base_url"` - // ImageDataUrl contains an image to attach to the note, in [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format. - ImageDataUrl string `yaml:"image_data_url"` - // CropRect is, if an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }` - CropRect string `yaml:"crop_rect"` - Type ItemType `yaml:"type_"` -} - -// ItemType is a type of the note. Copied from readme/api.md -type ItemType int - -const ( - ItemTypeNote ItemType = 1 - ItemTypeFolder = 2 - ItemTypeSetting = 3 - ItemTypeResource = 4 - ItemTypeTag = 5 - ItemTypeNoteTag = 6 - ItemTypeSearch = 7 - ItemTypeAlarm = 8 - ItemTypeMasterKey = 9 - ItemTypeItemChange = 10 - ItemTypeNoteResource = 11 - ItemTypeResourceLocalState = 12 - ItemTypeRevision = 13 - ItemTypeMigration = 14 - ItemTypeSmartFilter = 15 -) - -func Parse(in string) (Note, error) { - titleIdx := strings.Index(in, "\n\n") - paramsIdx := strings.LastIndex(in, "\n\n") - title, body, params := - in[:titleIdx], - in[min(titleIdx+2, paramsIdx):paramsIdx], - in[paramsIdx:] - - var note Note - if err := yaml.Unmarshal([]byte(params), &note); err != nil { - return Note{}, err - } - - note.Title = title - note.Body = body - - return note, nil -} - -type ( - // Notes is a map from ID to *Note - Notes map[string]Note - - // NoteTree is a note hierarchy by ID. - NoteTree map[string]NoteChildren - - // NoteChildren is a string set of node's children. - NoteChildren map[string]struct{} -) - -func ListNotes(dir string) (Notes, error) { - files, err := ioutil.ReadDir(dir) - if err != nil { - return nil, err - } - notes := make(Notes, len(files)) - for _, file := range files { - body, err := ioutil.ReadFile(file.Name()) - if err != nil { - return nil, fmt.Errorf("failed to read %q: %w", file.Name(), err) - } - note, err := Parse(string(body)) - if err != nil { - return nil, fmt.Errorf("failed to parse %q: %w", file.Name(), err) - } - - notes[note.ID] = note - } - - return notes, nil -} - -// Shake returns a sub-set of notes which eventually parent to "tld" -func (notes Notes) Shake(tree NoteTree, tld string) error { - topID := notes.GetFolderID(tld) - if topID == "" { - return fmt.Errorf("tld %q not found", tld) - } - - children := make(NoteChildren) - tree.flatten(topID, children) - - ret := make(Notes, len(children)) - for noteID := range children { - ret[noteID] = notes[noteID] - } - notes = ret - - return nil -} - -// GetFolderID returns a folder ID for a particular title -func (notes Notes) GetFolderID(title string) string { - var parentID string - for _, note := range notes { - if note.Type != ItemTypeFolder { - continue - } - if note.Title == title { - parentID = note.ID - } - } - - return parentID -} - -// BuildTree builds a note tree. -func BuildTree(notes Notes) NoteTree { - ret := make(NoteTree) - for _, note := range notes { - if _, ok := ret[note.ParentID]; !ok { - ret[note.ParentID] = make(NoteChildren) - } - ret[note.ParentID][note.ID] = struct{}{} - } - return ret -} - -// Subtree takes a sub-tree under a given tld. -func (t NoteTree) Subtree(id string) { - var ret NoteTree - subtree(id, ret) - t = ret -} - -func (t NoteTree) subtree(id string, acc NoteTree) { - for cid := range t[id] { - acc[cid] = make(NoteChildren, len(t[cid])) - t.subtree(cid, acc) - } -} - -// flatten returns a flat list of all `id`'s descendants. -func (t NoteTree) flatten(id string, acc NoteChildren) { - acc[id] = struct{}{} - for child := range t { - t.flatten(child, acc) - } -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} diff --git a/src/joplin2site/internal/note/note_test.go b/src/joplin2site/internal/note/note_test.go @@ -1,73 +0,0 @@ -package note - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestParse(t *testing.T) { - tests := []struct { - name string - note string - wantErr string - wantNote Note - }{ - { - name: "ok, has body", - note: `Meta - -Fonts: https://news.ycombinator.com/item?id=23381513 - -id: 4c7dd536ce1641afa4df349d87d9d29f -parent_id: 9e651b478a5a43c196c31719300fee6e -type_: 1 -`, - wantNote: Note{ - ID: "4c7dd536ce1641afa4df349d87d9d29f", - Title: "Meta", - Body: "Fonts: https://news.ycombinator.com/item?id=23381513", - ParentID: "9e651b478a5a43c196c31719300fee6e", - Type: ItemTypeNote, - }, - }, - { - name: "ok folder", - note: `blog - -id: 9e651b478a5a43c196c31719300fee6e -updated_time: 2020-06-04T16:07:19.930Z -encryption_applied: 0 -type_: 2 -`, - wantNote: Note{ - ID: "9e651b478a5a43c196c31719300fee6e", - Title: "blog", - Body: "", - UpdatedTime: time.Date(2020, 6, 4, 16, 07, 19, 930000000, time.UTC), - Type: ItemTypeFolder, - }, - }, - { - name: "bad yaml", - note: `blog - -bad yaml`, - wantErr: "yaml: unmarshal errors:\n line 3: cannot unmarshal !!str `bad yaml` into note.Note", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - note, err := Parse(tt.note) - if tt.wantErr != "" { - assert.EqualError(t, err, tt.wantErr) - return - } - require.NoError(t, err) - assert.Equal(t, tt.wantNote, note) - }) - } -} diff --git a/src/joplin2site/internal/page/index.go b/src/joplin2site/internal/page/index.go @@ -1,16 +0,0 @@ -package page - -import "html/template" - -const _indexForTemplate = ` - <div class="par"> - {{ range . }} - <div> - <div class="pd">{{ .PublishedAt }}</div> - <a href="{{ .URL }}">{{ .Title | html }}</a> - </div> - {{ end }} - </div> -` - -var _indexFor = template.Must(template.New("indexFor").Parse(_indexForTemplate)) diff --git a/src/joplin2site/internal/page/page.go b/src/joplin2site/internal/page/page.go @@ -1,140 +0,0 @@ -package page - -import ( - "errors" - "fmt" - "sort" - "strings" - "time" - - inote "github.com/motiejus/dotfiles/joplin2site/internal/note" - "gopkg.in/yaml.v2" -) - -type ( - // Page is a stand-alone converted note. - Page struct { - ID string - Title string - URL string - Body string - CreatedAt time.Time - PublishedAt time.Time - } - - // Pages is a slice of pages ordered by publish date. - Pages []Page -) - -type userMeta struct { - URL string `yaml:"url"` - PublishedAt time.Time `yaml:"published_at"` -} - -var ( - ErrMetaStart = errors.New("missing metadata opening tag") - ErrMetaEnd = errors.New("missing metadata closing tag") - ErrUnpublished = errors.New("not yet published") - ErrBadType = errors.New("invalid note type") -) - -const ( - _metaPrefix = "<!--\n" - _metaSuffix = "\n-->\n" -) - -// FromNote converts a Joplin Note to a Page -func FromNote(note inote.Note) (Page, error) { - if note.Type != inote.ItemTypeNote { - return Page{}, fmt.Errorf("got %d, expected %d: %w", - note.Type, inote.ItemTypeNote, ErrBadType) - } - - if !strings.HasPrefix(note.Body, _metaPrefix) { - return Page{}, ErrMetaStart - } - endIdx := strings.Index(note.Body, _metaSuffix) - if endIdx == -1 { - return Page{}, ErrMetaEnd - } - metaS := note.Body[len(_metaPrefix):endIdx] - body := note.Body[endIdx+len(_metaSuffix):] - - var meta userMeta - if err := yaml.UnmarshalStrict([]byte(metaS), &meta); err != nil { - return Page{}, fmt.Errorf("bad user's metadata: %w", err) - } - - if page.PublishedAt.After(time.Now()) { - return Page{}, ErrUnpublished - } - - return Page{ - ID: note.ID, - Title: note.Title, - URL: meta.URL, - Body: body, - CreatedAt: note.CreatedTime, - PublishedAt: meta.PublishedAt, - }, nil -} - -// SubPages returns immediate Pages of a tree -func SubPages(notes inote.Notes, title string) (Pages, error) { - // Find the parent note that will be the parent of the sub-notebook. - parentID := notes.GetFolderID(title) - if parentID == "" { - return nil, fmt.Errorf("sub-page %q not found", title) - } - - var pages Pages - for _, note := range notes { - if note.ParentID != parentID { - continue - } - page, err := FromNote(note) - if err != nil && (errors.Is(err, ErrBadType) || errors.Is(err, ErrUnpublished)) { - continue - } else if err != nil { - return nil, fmt.Errorf("failed to convert note to page: %w", err) - } - pages = append(pages, page) - } - sort.Slice(pages, func(i, j int) bool { - return pages[i].PublishedAt.Before(pages[j].PublishedAt) - }) - return pages, nil -} - -// ToPublishablePages returns notes that can be published. -func ToPublishablePages(notes inote.Notes) (Pages, error) { - pages := make([]Page, len(notes)) - for id, note := range notes { - page, err := ipage.FromNote(note) - if err != nil && (errors.Is(err, ErrBadType) || errors.Is(err, ErrUnpublished)) { - continue - } else if err != nil { - return nil, fmt.Errorf("failed to convert note to page: %w", err) - } - pages = append(pages, page) - } - return pages, nil -} - -type PrevNext struct { - Prev *page.Page - Next *page.Page -} - -// Sequenced returns sequenced pages within the same folder. -func (pages Pages) Sequenced(notes inote.Notes, tree inote.NoteTree) map[string]PrevNext { - // folders maps folder ID -> []noteID - var folders map[string][]string - for _, page := range pages { - parentID := notes[page.ID].ParentID - if _, ok := folders[parentID]; !ok { - folders[parentID] = []string{} - } - folders[parentID] = append(folders[parentID], page.ID) - } -} diff --git a/src/joplin2site/internal/page/page_test.go b/src/joplin2site/internal/page/page_test.go @@ -1,72 +0,0 @@ -package page - -import ( - "testing" - "time" - - "github.com/motiejus/dotfiles/joplin2site/internal/note" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestFromNote(t *testing.T) { - tests := []struct { - name string - note note.Note - wantPage Page - wantErr string - }{ - { - name: "ok minimal", - note: note.Note{ - Body: `<!-- -url: /about/ -published_at: 2020-06-05T12:42:00Z ---> -# Heading 3`, - }, - wantPage: Page{ - URL: "/about/", - Body: "# Heading 3", - PublishedAt: time.Date(2020, 6, 5, 12, 42, 00, 0, time.UTC), - }, - }, - - { - name: "missing metadata opening tag", - note: note.Note{}, - wantErr: `missing metadata opening tag`, - }, - { - name: "missing metadata closing tag", - note: note.Note{ - Body: "<!--\n foo bar noend\n\n", - }, - wantErr: `missing metadata closing tag`, - }, - { - name: "bad user's metadata", - note: note.Note{ - Body: `<!-- -foo: /non-existing-key/ ---> -`, - }, - wantErr: "bad user's metadata: yaml: unmarshal errors:\n line 1: field foo not found in type page.userMeta", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - page, err := FromNote(tt.note) - if tt.wantErr != "" { - assert.EqualError(t, err, tt.wantErr) - return - } - - require.NoError(t, err) - assert.Equal(t, tt.wantPage, page) - }) - } - -} diff --git a/src/joplin2site/internal/page/render.go b/src/joplin2site/internal/page/render.go @@ -1,46 +0,0 @@ -package page - -import ( - "bytes" - "fmt" - "html/template" - - "github.com/gomarkdown/markdown" - "github.com/motiejus/dotfiles/joplin2site/internal/note" -) - -// Render() renders the concrete page -func (p *Page) Render(notes note.Notes) ([]byte, error) { - tpl, err := template.New(p.ID).Parse(p.Body) - if err != nil { - return nil, fmt.Errorf("failed to parse user's page: %w", err) - } - tctx := templateContext{notes: notes} - funcs := template.FuncMap{ - "indexFor": tctx.indexFor, - } - var buf bytes.Buffer - if err := tpl.Funcs(funcs).Execute(&buf, nil); err != nil { - return nil, fmt.Errorf("failed to execute template: %w", err) - } - html := markdown.ToHTML(buf.Bytes(), nil, nil) - - return html, nil -} - -type templateContext struct { - notes note.Notes -} - -func (t *templateContext) indexFor(title string) ([]byte, error) { - pages, err := SubPages(t.notes, title) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := _indexFor.Execute(&buf, pages); err != nil { - return nil, fmt.Errorf("failed to generate index: %w", err) - } - return buf.Bytes(), nil -} diff --git a/src/joplin2site/main.go b/src/joplin2site/main.go @@ -1,23 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/motiejus/dotfiles/joplin2site/internal/cli" -) - -func main() { - a := &cli.App{ - Args: os.Args[1:], - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, - } - - if err := a.Run(); err != nil { - fmt.Printf("ERROR: %v\n", err.Error()) - os.Exit(1) - } - os.Exit(0) -}