From fa0eddcd76d7a9262836011d4a12a7bb5d5848bb Mon Sep 17 00:00:00 2001 From: "W. Augusto Andreoli" Date: Sat, 28 Dec 2024 11:09:15 +0100 Subject: [PATCH] ci: add golangci linter rules --- .github/workflows/go.yaml | 23 ++++++++--- .golangci.yaml | 17 ++++++++ README.md | 17 ++++---- cmd/lsdg.go | 6 +-- cmd/tidy_up.go | 83 +++++++++++++++++++++------------------ cmd/tidy_up_test.go | 24 +++++++---- 6 files changed, 109 insertions(+), 61 deletions(-) create mode 100644 .golangci.yaml diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml index 2c0c4e6..0b16651 100644 --- a/.github/workflows/go.yaml +++ b/.github/workflows/go.yaml @@ -1,18 +1,31 @@ name: go on: [push, pull_request] jobs: - build: + lint: runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "stable" + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: "v1.60" + test: + strategy: + matrix: + go: [stable] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.21" - - name: Install richgo - run: | - go install github.com/kyoh86/richgo@latest + go-version: ${{ matrix.go }} - name: Run tests and generate coverage data run: | make test-go-coverage diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..ea5afd2 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,17 @@ +# https://golangci-lint.run/usage/configuration/ +linters: + enable-all: true + disable: + # keep-sorted start + - copyloopvar + - depguard + - exportloopref + - forbidigo + - gci + - gochecknoinits + - godox + - intrange + - ireturn + - paralleltest + - testpackage + # keep-sorted end diff --git a/README.md b/README.md index a095883..323a6b8 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,15 @@ Logseq Doctor: heal your flat old Markdown files before importing them. -**NOTE:** This project is still alpha, so it\'s very rough on the edges -(documentation and feature-wise). - -At the moment, it has a Python package shipped with a Rust module, plus -an external Go executable with recent additions. - -The long-term plan is to convert it to Go and slowly remove Rust and -Python. New features will be added to the Go executable only. +> [!NOTE] +> This project is still alpha, so it\'s very rough on the edges +> (documentation and feature-wise). +> +> At the moment, it has a Python package shipped with a Rust extension, plus +> an external Go executable with recent additions. +> +> The long-term plan is to convert it to Go and slowly remove Rust and +> Python. New features will be added to the Go executable only. ## Installation diff --git a/cmd/lsdg.go b/cmd/lsdg.go index 068ef5c..796b6ae 100644 --- a/cmd/lsdg.go +++ b/cmd/lsdg.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/cobra" ) -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ +// rootCmd represents the base command when called without any subcommands. +var rootCmd = &cobra.Command{ //nolint:exhaustruct,gochecknoglobals Use: "lsdg", Short: "Logseq Doctor (Go) heals your Markdown files for Logseq", Long: `Logseq Doctor (Go) heals your Markdown files for Logseq. @@ -40,5 +40,5 @@ func init() { // Cobra also supports local flags, which will only run // when this action is called directly. - //rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } diff --git a/cmd/tidy_up.go b/cmd/tidy_up.go index fbfa9e5..57f3807 100644 --- a/cmd/tidy_up.go +++ b/cmd/tidy_up.go @@ -3,17 +3,18 @@ package cmd import ( "context" "fmt" - "github.com/andreoliwa/logseq-go" - "github.com/andreoliwa/logseq-go/content" - "github.com/spf13/cobra" "log" "os" "sort" "strings" + + "github.com/andreoliwa/logseq-go" + "github.com/andreoliwa/logseq-go/content" + "github.com/spf13/cobra" ) -// tidyUpCmd represents the tidyUp command -var tidyUpCmd = &cobra.Command{ +// tidyUpCmd represents the tidyUp command. +var tidyUpCmd = &cobra.Command{ //nolint:exhaustruct,gochecknoglobals Use: "tidy-up", Short: "Tidy up your Markdown files.", Long: `Tidy up your Markdown files, checking for invalid content and fixing some of them automatically. @@ -22,7 +23,7 @@ var tidyUpCmd = &cobra.Command{ - Check for running tasks (DOING) - Check for double spaces`, Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, args []string) { dir := os.Getenv("LOGSEQ_GRAPH_PATH") if dir == "" { log.Fatalln("LOGSEQ_GRAPH_PATH environment variable is not set.") @@ -32,7 +33,6 @@ var tidyUpCmd = &cobra.Command{ graph, err := logseq.Open(ctx, dir) if err != nil { log.Fatalln("error opening graph: %w", err) - return } exitCode := 0 @@ -43,11 +43,9 @@ var tidyUpCmd = &cobra.Command{ page, err := graph.OpenViaPath(path) if err != nil { log.Fatalf("%s: error opening file via path: %s\n", path, err) - return } if page == nil { log.Fatalf("%s: error opening file via path: page is nil\n", path) - return } changes := make([]string, 0) @@ -73,16 +71,6 @@ var tidyUpCmd = &cobra.Command{ func init() { rootCmd.AddCommand(tidyUpCmd) - - // Here you will define your flags and configuration settings. - - // Cobra supports Persistent Flags which will work for this command - // and all subcommands, e.g.: - // tidyUpCmd.PersistentFlags().String("foo", "", "A help for foo") - - // Cobra supports local flags which will only run when this command - // is called directly, e.g.: - // tidyUpCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } // isValidMarkdownFile checks if a file is a Markdown file, by looking at its extension, not its content. @@ -106,34 +94,40 @@ func isValidMarkdownFile(filePath string) bool { // checkForbiddenReferences checks if a page has forbidden references to other pages or tags. func checkForbiddenReferences(page logseq.Page) string { all := make([]string, 0) + for _, block := range page.Blocks() { block.Children().FilterDeep(func(n content.Node) bool { - var to string + var reference string if pageLink, ok := n.(*content.PageLink); ok { - to = pageLink.To + reference = pageLink.To } else if tag, ok := n.(*content.Hashtag); ok { - to = tag.To + reference = tag.To } // TODO: these values should be read from a config file or env var forbidden := false - switch strings.ToLower(to) { + + switch strings.ToLower(reference) { case "quick capture": forbidden = true case "inbox": forbidden = true } + if forbidden { - all = append(all, to) + all = append(all, reference) } + return false }) } - count := len(all) - if count > 0 { + + if count := len(all); count > 0 { unique := sortAndRemoveDuplicates(all) + return fmt.Sprintf("remove %d forbidden references to pages/tags: %s", count, strings.Join(unique, ", ")) } + return "" } @@ -144,9 +138,11 @@ func sortAndRemoveDuplicates(elements []string) []string { for _, element := range elements { if !seen[element] { seen[element] = true + uniqueElements = append(uniqueElements, element) } } + sort.Strings(uniqueElements) return uniqueElements @@ -155,51 +151,62 @@ func sortAndRemoveDuplicates(elements []string) []string { // checkRunningTasks checks if a page has running tasks (DOING, etc.). func checkRunningTasks(page logseq.Page) string { all := make([]string, 0) + for _, block := range page.Blocks() { block.Children().FilterDeep(func(n content.Node) bool { if task, ok := n.(*content.TaskMarker); ok { - s := task.Status - // TODO: the conversion from a task status to the "DOING"/"IN-PROGRESS" strings should be done in logseq-go - if s == content.TaskStatusDoing { + status := task.Status + // TODO: convert to strings "DOING"/"IN-PROGRESS" in logseq-go + if status == content.TaskStatusDoing { all = append(all, "DOING") } - if s == content.TaskStatusInProgress { + + if status == content.TaskStatusInProgress { all = append(all, "IN-PROGRESS") } } + return false }) } - count := len(all) - if count > 0 { + + if count := len(all); count > 0 { unique := sortAndRemoveDuplicates(all) + return fmt.Sprintf("stop %d running task(s): %s", count, strings.Join(unique, ", ")) } + return "" } func checkDoubleSpaces(page logseq.Page) string { all := make([]string, 0) + for _, block := range page.Blocks() { - block.Children().FilterDeep(func(n content.Node) bool { + block.Children().FilterDeep(func(node content.Node) bool { var value string - if text, ok := n.(*content.Text); ok { + + if text, ok := node.(*content.Text); ok { value = text.Value - } else if pageLink, ok := n.(*content.PageLink); ok { + } else if pageLink, ok := node.(*content.PageLink); ok { value = pageLink.To - } else if tag, ok := n.(*content.Hashtag); ok { + } else if tag, ok := node.(*content.Hashtag); ok { value = tag.To } + if strings.Contains(value, " ") { all = append(all, fmt.Sprintf("'%s'", value)) } + return false }) } - count := len(all) - if count > 0 { + + if count := len(all); count > 0 { unique := sortAndRemoveDuplicates(all) + return fmt.Sprintf("%d double spaces: %s", count, strings.Join(unique, ", ")) } + return "" } diff --git a/cmd/tidy_up_test.go b/cmd/tidy_up_test.go index 5b404f5..d955582 100644 --- a/cmd/tidy_up_test.go +++ b/cmd/tidy_up_test.go @@ -2,12 +2,13 @@ package cmd import ( "context" - "github.com/andreoliwa/logseq-go" - "github.com/stretchr/testify/assert" "os" "path/filepath" "reflect" "testing" + + "github.com/andreoliwa/logseq-go" + "github.com/stretchr/testify/assert" ) func TestSortAndRemoveDuplicates(t *testing.T) { @@ -20,7 +21,11 @@ func TestSortAndRemoveDuplicates(t *testing.T) { {"one element", []string{"apple"}, []string{"apple"}}, {"duplicates", []string{"orange", "apple", "banana", "apple"}, []string{"apple", "banana", "orange"}}, {"sorted unique", []string{"orange", "banana", "apple"}, []string{"apple", "banana", "orange"}}, - {"unsorted with duplicates", []string{"orange", "banana", "apple", "apple", "orange"}, []string{"apple", "banana", "orange"}}, + { + "unsorted with duplicates", + []string{"orange", "banana", "apple", "apple", "orange"}, + []string{"apple", "banana", "orange"}, + }, } for _, test := range tests { @@ -51,8 +56,7 @@ func TestIsValidMarkdownFile(t *testing.T) { // Create a valid markdown file validFilePath := filepath.Join(dir, "valid_markdown_file.md") - err := os.WriteFile(validFilePath, []byte("# Test"), 0644) - if err != nil { + if err := os.WriteFile(validFilePath, []byte("# Test"), 0o600); err != nil { t.Fatalf("Failed to create test file: %v", err) } @@ -70,7 +74,10 @@ func TestIsValidMarkdownFile(t *testing.T) { } func setupPage(t *testing.T, name string) logseq.Page { + t.Helper() + ctx := context.Background() + graph, err := logseq.Open(ctx, filepath.Join("testdata", "graph")) if err != nil { t.Fatal(err) @@ -86,7 +93,8 @@ func setupPage(t *testing.T, name string) logseq.Page { func TestCheckForbiddenReferences(t *testing.T) { invalid := setupPage(t, "forbidden") - assert.Equal(t, "remove 4 forbidden references to pages/tags: Inbox, quick capture", checkForbiddenReferences(invalid)) + assert.Equal(t, "remove 4 forbidden references to pages/tags: Inbox, quick capture", + checkForbiddenReferences(invalid)) valid := setupPage(t, "valid") assert.Equal(t, "", checkForbiddenReferences(valid)) @@ -102,7 +110,9 @@ func TestCheckRunningTasks(t *testing.T) { func TestCheckDoubleSpaces(t *testing.T) { invalid := setupPage(t, "spaces") - assert.Equal(t, "3 double spaces: 'Link With Spaces ', 'Regular text with spaces', 'some tag with spaces'", checkDoubleSpaces(invalid)) + assert.Equal(t, + "3 double spaces: 'Link With Spaces ', 'Regular text with spaces', 'some tag with spaces'", + checkDoubleSpaces(invalid)) valid := setupPage(t, "valid") assert.Equal(t, "", checkDoubleSpaces(valid))