Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cron parser #19

Merged
merged 20 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@
"-c",
"config.local.yaml"
]
}
},
{
"name": "Parse",
"type": "go",
"request": "launch",
"mode": "auto",
"program": ".",
"env": {},
"args": [
"parse",
"crontab.log",
"-u"
]
},
]
}
8 changes: 8 additions & 0 deletions cmd/parser/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package parser

type parserConfig struct {
cronFile string
output string
cronMatcher string
hasUser bool
}
36 changes: 36 additions & 0 deletions cmd/parser/cron_line.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package parser

import (
"log"
"regexp"
"strings"
)

type cronLine struct {
string
}

func (l cronLine) exportEnv() map[string]string {
match := envRegex.FindStringSubmatch(l.string)
answer := make(map[string]string)
switch len(match) {
case 0:
case 3:
answer[match[1]] = match[2]
default:
log.Panicf("found multiple(%d) env vars in single line\n please attach your crontab file too\n affected line: %s\n parser result: %#v\n", len(match), l.string, match)

Check warning on line 21 in cmd/parser/cron_line.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_line.go#L13-L21

Added lines #L13 - L21 were not covered by tests
}
return answer

Check warning on line 23 in cmd/parser/cron_line.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_line.go#L23

Added line #L23 was not covered by tests
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test coverage and improve error handling for exportEnv.

The method exportEnv uses regex to parse environment variables from a cron line. It's crucial to handle different cases robustly and to add tests to ensure its reliability, especially since it uses panic for error handling which is not recommended.

-	log.Panicf("found multiple(%d) env vars in single line\n please attach your crontab file too\n affected line: %s\n parser result: %#v\n", len(match), l.string, match)
+	if len(match) > 3 {
+		log.Errorf("Multiple environment variables found in a single line; please check the syntax or attach your crontab file for further investigation. Affected line: %s", l.string)
+		return nil
+	}

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 13-21: cmd/parser/cron_line.go#L13-L21
Added lines #L13 - L21 were not covered by tests


[warning] 23-23: cmd/parser/cron_line.go#L23
Added line #L23 was not covered by tests


func (l cronLine) exportSpec(regex *regexp.Regexp, env map[string]string, parser cronSpecParser) *cronSpec {
match := regex.FindStringSubmatch(l.string)
if len(match) < 1 {
if len(strings.Trim(l.string, " \n\t")) == 0 {
return nil
} else {
log.Panicf("cannot parse this non-empty line as cron specification: %s", l.string)
}

Check warning on line 33 in cmd/parser/cron_line.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_line.go#L26-L33

Added lines #L26 - L33 were not covered by tests
}
return parser(match, env)

Check warning on line 35 in cmd/parser/cron_line.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_line.go#L35

Added line #L35 was not covered by tests
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure robust parsing in exportSpec and add test coverage.

The method exportSpec parses cron specifications from a line, but lacks comprehensive error handling and test coverage. Consider refactoring to return an error instead of using panic, and add corresponding unit tests.

-	log.Panicf("cannot parse this non-empty line as cron specification: %s", l.string)
+	if len(strings.Trim(l.string, " \n\t")) != 0 {
+		log.Errorf("Failed to parse line as cron specification: %s", l.string)
+		return nil, fmt.Errorf("invalid cron specification: %s", l.string)
+	}

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 26-32: cmd/parser/cron_line.go#L26-L32
Added lines #L26 - L32 were not covered by tests


[warning] 35-35: cmd/parser/cron_line.go#L35
Added line #L35 was not covered by tests

13 changes: 13 additions & 0 deletions cmd/parser/cron_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package parser

import (
"github.com/FMotalleb/crontab-go/config"
)

func (cfg parserConfig) parse() config.Config {
cron := NewCronFromFile(cfg.cronFile)
return *cron.ParseConfig(
cfg.cronMatcher,
cfg.hasUser,
)

Check warning on line 12 in cmd/parser/cron_parser.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_parser.go#L7-L12

Added lines #L7 - L12 were not covered by tests
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test coverage for the parse method.

The method parse() is critical as it transforms the cron file content into a structured configuration. Currently, there is no test coverage for this newly added method, which is risky given its core functionality.

Tools
GitHub Check: codecov/patch

[warning] 7-12: cmd/parser/cron_parser.go#L7-L12
Added lines #L7 - L12 were not covered by tests


Ensure proper error handling in parse method.

The method lacks explicit error handling. Consider modifying NewCronFromFile and ParseConfig to return an error alongside the current return type, which can then be handled appropriately here.

-	cron := NewCronFromFile(cfg.cronFile)
-	return *cron.ParseConfig(cfg.cronMatcher, cfg.hasUser)
+	cron, err := NewCronFromFile(cfg.cronFile)
+	if err != nil {
+		return nil, err
+	}
+	config, err := cron.ParseConfig(cfg.cronMatcher, cfg.hasUser)
+	if err != nil {
+		return nil, err
+	}
+	return config, nil

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 7-12: cmd/parser/cron_parser.go#L7-L12
Added lines #L7 - L12 were not covered by tests

}
50 changes: 50 additions & 0 deletions cmd/parser/cron_spec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package parser

import (
"log"
"regexp"
)

type (
cronSpecParser = func([]string, map[string]string) *cronSpec
cronSpec struct {
timing string
user string
command string
environ map[string]string
}
)

func normalParser(regex *regexp.Regexp) cronSpecParser {
cronIndex := regex.SubexpIndex("cron")
// userIndex := regex.SubexpIndex("user")
cmdIndex := regex.SubexpIndex("cmd")
if cronIndex < 0 || cmdIndex < 0 {
log.Panicf("cannot find groups (cron,cmd) in regexp: `%s", regex)
}
return func(match []string, env map[string]string) *cronSpec {
return &cronSpec{
timing: match[cronIndex],
user: "",
command: match[cmdIndex],
environ: env,
}
}

Check warning on line 32 in cmd/parser/cron_spec.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_spec.go#L18-L32

Added lines #L18 - L32 were not covered by tests
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor error handling in normalParser and add tests.

The function normalParser uses panic for error handling, which is not ideal for library functions. Refactor to return an error and ensure that all paths are covered by unit tests.

-	log.Panicf("cannot find groups (cron,cmd) in regexp: `%s", regex)
+	if cronIndex < 0 || cmdIndex < 0 {
+		return nil, fmt.Errorf("required groups not found in regex: %s", regex.String())
+	}

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 18-19: cmd/parser/cron_spec.go#L18-L19
Added lines #L18 - L19 were not covered by tests


[warning] 21-23: cmd/parser/cron_spec.go#L21-L23
Added lines #L21 - L23 were not covered by tests


[warning] 25-30: cmd/parser/cron_spec.go#L25-L30
Added lines #L25 - L30 were not covered by tests


func withUserParser(regex *regexp.Regexp) cronSpecParser {
cronIndex := regex.SubexpIndex("cron")
userIndex := regex.SubexpIndex("user")
cmdIndex := regex.SubexpIndex("cmd")
if cronIndex < 0 || cmdIndex < 0 || userIndex < 0 {
log.Panicf("cannot find groups (cron,user,cmd) in regexp: `%s", regex)
}
return func(match []string, env map[string]string) *cronSpec {
return &cronSpec{
timing: match[cronIndex],
user: match[userIndex],
command: match[cmdIndex],
environ: env,
}
}

Check warning on line 49 in cmd/parser/cron_spec.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_spec.go#L35-L49

Added lines #L35 - L49 were not covered by tests
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve error handling in withUserParser and ensure test coverage.

Similar to normalParser, withUserParser uses panic for error handling. Refactor this to return an error and add comprehensive tests to cover all scenarios.

-	log.Panicf("cannot find groups (cron,user,cmd) in regexp: `%s", regex)
+	if cronIndex < 0 || cmdIndex < 0 || userIndex < 0 {
+		return nil, fmt.Errorf("required groups not found in regex: %s", regex.String())
+	}

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 35-40: cmd/parser/cron_spec.go#L35-L40
Added lines #L35 - L40 were not covered by tests


[warning] 42-47: cmd/parser/cron_spec.go#L42-L47
Added lines #L42 - L47 were not covered by tests

170 changes: 170 additions & 0 deletions cmd/parser/cron_string.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package parser

import (
"fmt"
"log"
"os"
"regexp"
"strings"

"github.com/FMotalleb/crontab-go/config"
)

var envRegex = regexp.MustCompile(`^(?<key>[\w\d_]+)=(?<value>.*)$`)

type CronString struct {
string
}

func NewCronString(cron string) CronString {
return CronString{cron}

Check warning on line 20 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L19-L20

Added lines #L19 - L20 were not covered by tests
}
FMotalleb marked this conversation as resolved.
Show resolved Hide resolved

func NewCronFromFile(filePath string) CronString {
file, err := os.OpenFile(filePath, os.O_RDONLY, 0o644)
if err != nil {
log.Panicf("can't open cron file: %v", err)
}
stat, err := file.Stat()
if err != nil {
log.Panicf("can't stat cron file: %v", err)
}
content := make([]byte, stat.Size())
_, err = file.Read(content)
if err != nil {
log.Panicf("can't open cron file: %v", err)
}
return CronString{string(content)}

Check warning on line 37 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L23-L37

Added lines #L23 - L37 were not covered by tests
}

func (s CronString) replaceAll(regex string, repl string) CronString {
reg := regexp.MustCompile(regex)
out := reg.ReplaceAllString(s.string, repl)
return CronString{out}

Check warning on line 43 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L40-L43

Added lines #L40 - L43 were not covered by tests
Comment on lines +21 to +24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit tests for replaceAll.

The function replaceAll is not covered by unit tests. It's crucial to add comprehensive unit tests for this function to ensure its reliability and correctness.

func TestReplaceAll(t *testing.T) {
	cron := NewCronString("0 0 * * *")
	result := cron.replaceAll(`\d`, "1")
	expected := "1 1 * * *"
	if result.string != expected {
		t.Errorf("Expected %s, got %s", expected, result.string)
	}
}
Tools
GitHub Check: codecov/patch

[warning] 21-24: cmd/parser/cron_string.go#L21-L24
Added lines #L21 - L24 were not covered by tests

}

func (s CronString) sanitizeLineBreaker() CronString {
return s.replaceAll(
`\s*\\\s*\n\s*([\n|\n\s])*`,
" ",
)

Check warning on line 50 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L46-L50

Added lines #L46 - L50 were not covered by tests
Comment on lines +27 to +31
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit tests for sanitizeLineBreaker.

The function sanitizeLineBreaker is not covered by unit tests. It's crucial to add comprehensive unit tests for this function to ensure its reliability and correctness.

func TestSanitizeLineBreaker(t *testing.T) {
	cron := NewCronString("0 0 * * * \\\n0 1 * * *")
	result := cron.sanitizeLineBreaker()
	expected := "0 0 * * * 0 1 * * *"
	if result.string != expected {
		t.Errorf("Expected %s, got %s", expected, result.string)
	}
}
Tools
GitHub Check: codecov/patch

[warning] 27-31: cmd/parser/cron_string.go#L27-L31
Added lines #L27 - L31 were not covered by tests

}

func (s CronString) sanitizeEmptyLine() CronString {
return s.replaceAll(
`\n\s*\n`,
"\n",
)

Check warning on line 57 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L53-L57

Added lines #L53 - L57 were not covered by tests
Comment on lines +34 to +38
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit tests for sanitizeEmptyLine.

The function sanitizeEmptyLine is not covered by unit tests. It's crucial to add comprehensive unit tests for this function to ensure its reliability and correctness.

func TestSanitizeEmptyLine(t *testing.T) {
	cron := NewCronString("0 0 * * *\n\n0 1 * * *")
	result := cron.sanitizeEmptyLine()
	expected := "0 0 * * *\n0 1 * * *"
	if result.string != expected {
		t.Errorf("Expected %s, got %s", expected, result.string)
	}
}
Tools
GitHub Check: codecov/patch

[warning] 34-38: cmd/parser/cron_string.go#L34-L38
Added lines #L34 - L38 were not covered by tests

}

func (s CronString) sanitizeComments() CronString {
return s.replaceAll(
`\s*#.*`,
"",
)

Check warning on line 64 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L60-L64

Added lines #L60 - L64 were not covered by tests
Comment on lines +41 to +45
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit tests for sanitizeComments.

The function sanitizeComments is not covered by unit tests. It's crucial to add comprehensive unit tests for this function to ensure its reliability and correctness.

func TestSanitizeComments(t *testing.T) {
	cron := NewCronString("0 0 * * * # comment")
	result := cron.sanitizeComments()
	expected := "0 0 * * *"
	if result.string != expected {
		t.Errorf("Expected %s, got %s", expected, result.string)
	}
}
Tools
GitHub Check: codecov/patch

[warning] 41-45: cmd/parser/cron_string.go#L41-L45
Added lines #L41 - L45 were not covered by tests

}

func (s CronString) sanitize() CronString {
return s.
replaceAll("\r\n", "\n").
sanitizeComments().
sanitizeLineBreaker().
sanitizeEmptyLine()

Check warning on line 72 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L67-L72

Added lines #L67 - L72 were not covered by tests
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit tests for sanitize.

The function sanitize is not covered by unit tests. It's crucial to add comprehensive unit tests for this function to ensure its reliability and correctness.

func TestSanitize(t *testing.T) {
	cron := NewCronString("0 0 * * * \\\n# comment\n\n0 1 * * *")
	result := cron.sanitize()
	expected := "0 0 * * * 0 1 * * *"
	if result.string != expected {
		t.Errorf("Expected %s, got %s", expected, result.string)
	}
}
Tools
GitHub Check: codecov/patch

[warning] 48-53: cmd/parser/cron_string.go#L48-L53
Added lines #L48 - L53 were not covered by tests

}

func (s CronString) lines() []string {
return strings.Split(s.string, "\n")

Check warning on line 76 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L75-L76

Added lines #L75 - L76 were not covered by tests
Comment on lines +65 to +66
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit tests for lines.

The function lines is not covered by unit tests. It's crucial to add comprehensive unit tests for this function to ensure its reliability and correctness.

func TestLines(t *testing.T) {
	cron := NewCronString("0 0 * * *\n0 1 * * *")
	result := cron.lines()
	expected := []string{"0 0 * * *", "0 1 * * *"}
	for i, line := range result {
		if line != expected[i] {
			t.Errorf("Expected %s, got %s", expected[i], line)
		}
	}
}
Tools
GitHub Check: codecov/patch

[warning] 65-66: cmd/parser/cron_string.go#L65-L66
Added lines #L65 - L66 were not covered by tests

}

func (s *CronString) parseAsSpec(
pattern string,
hasUser bool,
) []cronSpec {
envTable := make(map[string]string)
specs := make([]cronSpec, 0)
lines := s.sanitize().lines()
matcher, parser := buildMapper(hasUser, pattern)

for _, line := range lines {
l := cronLine{line}
if env := l.exportEnv(); len(env) > 0 {
for key, val := range l.exportEnv() {
if old, ok := envTable[key]; ok {
log.Printf("env var of key `%s`, value `%s`, is going to be replaced by `%s`\n", key, old, val)
}
envTable[key] = val

Check warning on line 95 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L82-L95

Added lines #L82 - L95 were not covered by tests

}
} else {
if spec := l.exportSpec(matcher, envTable, parser); spec != nil {
specs = append(specs, *spec)

Check warning on line 100 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L97-L100

Added lines #L97 - L100 were not covered by tests
}
}
}

Check warning on line 103 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L103

Added line #L103 was not covered by tests
return specs
}

func (s *CronString) ParseConfig(
pattern string,
hasUser bool,
) *config.Config {
specs := s.parseAsSpec(pattern, hasUser)
cfg := &config.Config{}
for _, spec := range specs {
addSpec(cfg, spec)
}

Check warning on line 115 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L109-L115

Added lines #L109 - L115 were not covered by tests
return cfg
}

func buildMapper(hasUser bool, pattern string) (*regexp.Regexp, func([]string, map[string]string) *cronSpec) {
lineParser := "(?<cmd>.*)"
if hasUser {
lineParser = fmt.Sprintf(`(?<user>\w[\w\d]*)\s+%s`, lineParser)
}
cronLineMatcher := fmt.Sprintf(`^(?<cron>%s)\s+%s$`, pattern, lineParser)

matcher, err := regexp.Compile(cronLineMatcher)
if err != nil {
log.Panicf("cannot parse cron `%s`", matcher)
}
var parser cronSpecParser
if hasUser {
parser = withUserParser(matcher)
} else {
parser = normalParser(matcher)
}

Check warning on line 135 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L118-L135

Added lines #L118 - L135 were not covered by tests
return matcher, parser
}

func addSpec(cfg *config.Config, spec cronSpec) {
jobName := fmt.Sprintf("FromCron: %s", spec.timing)
for _, job := range cfg.Jobs {
if job.Name == jobName {
task := config.Task{
Command: spec.command,
UserName: spec.user,
Env: spec.environ,
}
job.Tasks = append(
job.Tasks,
task,
)
return

Check warning on line 152 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L138-L152

Added lines #L138 - L152 were not covered by tests
}
}
initJob(jobName, spec.timing, cfg)

Check warning on line 155 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L154-L155

Added lines #L154 - L155 were not covered by tests
addSpec(cfg, spec)
}

func initJob(jobName string, timing string, cfg *config.Config) {
job := &config.JobConfig{}
job.Name = jobName
job.Description = "Imported from cron file"
job.Disabled = false
job.Events = []config.JobEvent{
{
Cron: timing,
},
}

Check warning on line 168 in cmd/parser/cron_string.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/cron_string.go#L158-L168

Added lines #L158 - L168 were not covered by tests
cfg.Jobs = append(cfg.Jobs, job)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Lack of unit tests for critical functions.

The functions NewCronString, NewCronFromFile, and replaceAll in cmd/parser/cron_string.go are not covered by unit tests. It's crucial to add comprehensive unit tests for these functions to ensure their reliability and robustness.

  • Add unit tests for NewCronString to verify its behavior with various cron strings.
  • Add unit tests for NewCronFromFile to test file reading and error handling.
  • Add unit tests for replaceAll to ensure the correctness of regex replacements.
Analysis chain

Robust functions for handling and parsing cron strings.

The new functions introduced for handling cron strings are crucial for the parser's functionality. Consider replacing panics with more graceful error handling to make the module more robust and easier to integrate into other parts of the application. Additionally, ensure comprehensive testing, particularly for the regex operations and file handling aspects, to cover all edge cases and cron formats.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that the cron string handling functions are covered by unit tests.

# Test: Search for tests covering the new functions in `cron_string.go`. Expect: Comprehensive coverage.
rg --type go 'NewCronString' -A 5 -B 5
rg --type go 'NewCronFromFile' -A 5 -B 5
rg --type go 'replaceAll' -A 5 -B 5

Length of output: 3454



Script:

#!/bin/bash
# Description: Verify that the cron string handling functions are covered by unit tests.

# Test: Search for unit tests covering the new functions in `cron_string.go`. Expect: Comprehensive coverage.
rg --type go 'func Test' -l | xargs grep -E 'NewCronString|NewCronFromFile|replaceAll'

Length of output: 88

Tools
golangci-lint

97-97: unnecessary trailing newline

(whitespace)

GitHub Check: codecov/patch

[warning] 19-20: cmd/parser/cron_string.go#L19-L20
Added lines #L19 - L20 were not covered by tests


[warning] 23-26: cmd/parser/cron_string.go#L23-L26
Added lines #L23 - L26 were not covered by tests


[warning] 28-30: cmd/parser/cron_string.go#L28-L30
Added lines #L28 - L30 were not covered by tests


[warning] 32-35: cmd/parser/cron_string.go#L32-L35
Added lines #L32 - L35 were not covered by tests


[warning] 37-37: cmd/parser/cron_string.go#L37
Added line #L37 was not covered by tests


[warning] 40-43: cmd/parser/cron_string.go#L40-L43
Added lines #L40 - L43 were not covered by tests


[warning] 46-50: cmd/parser/cron_string.go#L46-L50
Added lines #L46 - L50 were not covered by tests


[warning] 53-57: cmd/parser/cron_string.go#L53-L57
Added lines #L53 - L57 were not covered by tests


[warning] 60-64: cmd/parser/cron_string.go#L60-L64
Added lines #L60 - L64 were not covered by tests


[warning] 67-72: cmd/parser/cron_string.go#L67-L72
Added lines #L67 - L72 were not covered by tests


[warning] 75-76: cmd/parser/cron_string.go#L75-L76
Added lines #L75 - L76 were not covered by tests


[warning] 82-86: cmd/parser/cron_string.go#L82-L86
Added lines #L82 - L86 were not covered by tests


[warning] 88-93: cmd/parser/cron_string.go#L88-L93
Added lines #L88 - L93 were not covered by tests


[warning] 95-95: cmd/parser/cron_string.go#L95
Added line #L95 was not covered by tests


[warning] 97-99: cmd/parser/cron_string.go#L97-L99
Added lines #L97 - L99 were not covered by tests


[warning] 103-103: cmd/parser/cron_string.go#L103
Added line #L103 was not covered by tests


[warning] 109-113: cmd/parser/cron_string.go#L109-L113
Added lines #L109 - L113 were not covered by tests


[warning] 115-115: cmd/parser/cron_string.go#L115
Added line #L115 was not covered by tests


[warning] 118-121: cmd/parser/cron_string.go#L118-L121
Added lines #L118 - L121 were not covered by tests


[warning] 123-123: cmd/parser/cron_string.go#L123
Added line #L123 was not covered by tests

65 changes: 65 additions & 0 deletions cmd/parser/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Package parser manages holds the logic behind the sub command `parse`
// this package is responsible for parsing a crontab file into valid config yaml file
package parser

import (
"bytes"
"encoding/json"
"errors"
"io"
"log"
"os"

"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
)

var (
cfg = &parserConfig{}
ParserCmd = &cobra.Command{
Use: "parse <crontab file path>",
ValidArgs: []string{"crontab file path"},
Short: "Parse crontab syntax and converts it into yaml syntax for crontab-go",
Run: func(cmd *cobra.Command, args []string) {
cfg.cronFile = cmd.Flags().Arg(0)
log.SetPrefix("[Cron Parser]")
if cfg.cronFile == "" {
log.Panicln(errors.New("no crontab file specified, see usage using --help flag"))
}
finalConfig := cfg.parse()
str, err := json.Marshal(finalConfig)
if err != nil {
log.Panicf("failed to marshal final config: %v", err)
}
hashMap := make(map[string]any)
if err := json.Unmarshal(str, &hashMap); err != nil {
log.Panicf("failed to unmarshal final config: %v", err)
}
ans, err := yaml.Marshal(hashMap)
if err != nil {
log.Panicf("failed to marshal final config: %v", err)
}
result := string(ans)
log.Printf("output:\n%s", result)
if cfg.output != "" {
outputFile, err := os.OpenFile(cfg.output, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
log.Panicf("failed to open output file: %v", err)
}
buf := bytes.NewBufferString(result)
_, err = io.Copy(outputFile, buf)
if err != nil {
log.Panicf("failed to write output file: %v", err)
}

Check warning on line 53 in cmd/parser/parser.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/parser.go#L23-L53

Added lines #L23 - L53 were not covered by tests
}
log.Println("Done writing output")
os.Exit(0)

Check warning on line 56 in cmd/parser/parser.go

View check run for this annotation

Codecov / codecov/patch

cmd/parser/parser.go#L55-L56

Added lines #L55 - L56 were not covered by tests
},
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Unit tests for ParserCmd command are missing.

The ParserCmd command provides robust functionality for parsing crontab files into YAML configuration. However, there are no specific unit tests found for this command. Ensure that this new command is covered by unit tests, especially given its complexity and the multiple stages of data handling involved.

  • Add unit tests to cover the command's functionalities, including error handling, file operations, and data conversions.
Analysis chain

Comprehensive ParserCmd command added for crontab parsing.

The ParserCmd provides robust functionality for parsing crontab files into YAML configuration. Consider using standard error returns instead of panics for better error handling in production environments. Additionally, ensure that this new command is covered by unit tests, especially given its complexity and the multiple stages of data handling involved.


Unit tests for ParserCmd command are missing or not found.

The ParserCmd command provides robust functionality for parsing crontab files into YAML configuration. However, there are no specific unit tests found for this command. Ensure that this new command is covered by unit tests, especially given its complexity and the multiple stages of data handling involved.

  • Verify if there are any test files covering ParserCmd directly or indirectly.
  • If no tests are found, add unit tests to cover the command's functionalities.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that the `ParserCmd` and its functionalities are covered by unit tests.

# Test: Search for tests covering the `ParserCmd`. Expect: At least one test case for each major functionality.
rg --type go 'ParserCmd' -A 10 -B 10

Length of output: 2659



Script:

#!/bin/bash
# Description: Verify if there are any test files covering `ParserCmd` directly or indirectly.

# Search for test files that might cover `ParserCmd`.
fd --type f --name '*_test.go' | xargs rg 'ParserCmd'

Length of output: 955



Script:

#!/bin/bash
# Description: Verify if there are any test files covering `ParserCmd` directly or indirectly.

# Search for test files that might cover `ParserCmd`.
fd -e go -t f '_test' | xargs rg 'ParserCmd'

Length of output: 42

Tools
GitHub Check: codecov/patch

[warning] 23-53: cmd/parser/parser.go#L23-L53
Added lines #L23 - L53 were not covered by tests


[warning] 55-56: cmd/parser/parser.go#L55-L56
Added lines #L55 - L56 were not covered by tests

)

func init() {
ParserCmd.PersistentFlags().StringVarP(&cfg.output, "output", "o", "", "output file to write configuration to")
ParserCmd.PersistentFlags().BoolVarP(&cfg.hasUser, "with-user", "u", false, "indicates that whether the given cron file has user field")
ParserCmd.PersistentFlags().StringVar(&cfg.cronMatcher, "matcher", `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*|(\*\/\d)) ?){5,7})`, "matcher for cron")
}
Loading