Skip to content

Commit 2e5b081

Browse files
authored
Merge pull request #80 from winebarrel/add_cronskd
Add cronskd command
2 parents fc52e0f + e8fa029 commit 2e5b081

File tree

8 files changed

+271
-1
lines changed

8 files changed

+271
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66
/cronviz.exe
77
/crongrep
88
/crongrep.exe
9+
/cronskd
10+
/cronskd.exe
911
/dist/

.goreleaser.yml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ builds:
3131
- -X main.version={{.Version}}
3232
env:
3333
- CGO_ENABLED=0
34+
- id: cronskd
35+
binary: cronskd
36+
dir: ./cmd/cronskd
37+
ldflags:
38+
- -X main.version={{.Version}}
39+
env:
40+
- CGO_ENABLED=0
3441
checksum:
3542
name_template: "checksums.txt"
3643
archives:
@@ -46,6 +53,9 @@ archives:
4653
- id: crongrep
4754
builds: [crongrep]
4855
name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
56+
- id: cronskd
57+
builds: [cronskd]
58+
name_template: "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
4959
brews:
5060
- name: cronplan
5161
ids: [cronplan]
@@ -87,6 +97,16 @@ brews:
8797
license: MIT
8898
install: |
8999
bin.install 'crongrep'
100+
- name: cronskd
101+
ids: [cronskd]
102+
repository:
103+
owner: winebarrel
104+
name: homebrew-cronplan
105+
homepage: https://github.com/winebarrel/cronplan
106+
description: cronskd is a tool to show a schedule of cron expressions.
107+
license: MIT
108+
install: |
109+
bin.install 'cronskd'
90110
nfpms:
91111
- id: cronplan-nfpms
92112
builds: [cronplan]
@@ -126,7 +146,18 @@ nfpms:
126146
file_name_template: "{{ .Binary }}_{{ .Version }}_{{ .Arch }}"
127147
homepage: https://github.com/winebarrel/cronplan
128148
maintainer: Genki Sugawara <sugawara@winebarrel.jp>
129-
description: crongrep is a tool to visualize cron schedule.
149+
description: crongrep is a tool to grep with cron expression.
150+
license: MIT
151+
formats:
152+
- deb
153+
- rpm
154+
bindir: /usr/bin
155+
- id: cronskd-nfpms
156+
builds: [cronskd]
157+
file_name_template: "{{ .Binary }}_{{ .Version }}_{{ .Arch }}"
158+
homepage: https://github.com/winebarrel/cronplan
159+
maintainer: Genki Sugawara <sugawara@winebarrel.jp>
160+
description: cronskd is a tool to show a schedule of cron expressions.
130161
license: MIT
131162
formats:
132163
- deb

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ build:
77
cd ./cmd/cronmatch && go build -o ../../cronmatch
88
cd ./cmd/cronviz && go build -o ../../cronviz
99
cd ./cmd/crongrep && go build -o ../../crongrep
10+
cd ./cmd/cronskd && go build -o ../../cronskd
1011

1112
.PHONY: vet
1213
vet:
@@ -26,3 +27,4 @@ clean:
2627
rm -f cronmatch cronmatch.exe
2728
rm -f cronviz cronviz.exe
2829
rm -f crongrep crongrep.exe
30+
rm -f cronskd cronskd.exe

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,45 @@ Wed, 11 Oct 2023 12:10:00
247247
Thu, 26 Oct 2023 12:10:00
248248
```
249249

250+
# cronskd CLI
251+
252+
CLI to show a schedule of cron expressions.
253+
254+
## Installation
255+
256+
```
257+
brew install winebarrel/cronplan/cronskd
258+
```
259+
260+
## Usage
261+
262+
```
263+
Usage: cronskd [OPTION] [FILE]
264+
-e string
265+
end date (default: end of day)
266+
-s string
267+
start date (default: beginning of day)
268+
-version
269+
print version and exit
270+
```
271+
272+
```
273+
$ cat exprs.txt
274+
0 10 * * ? *
275+
15 12 * * ? *
276+
0 18 ? * MON-FRI *
277+
0 8 1 * ? *
278+
5 8-10 ? * MON-FRI *
279+
280+
$ cronskd -s '2024-11-11' exprs.txt
281+
Mon, 11 Nov 2024 08:05:00 5 8-10 ? * MON-FRI *
282+
Mon, 11 Nov 2024 09:05:00 5 8-10 ? * MON-FRI *
283+
Mon, 11 Nov 2024 10:00:00 0 10 * * ? *
284+
Mon, 11 Nov 2024 10:05:00 5 8-10 ? * MON-FRI *
285+
Mon, 11 Nov 2024 12:15:00 15 12 * * ? *
286+
Mon, 11 Nov 2024 18:00:00 0 18 ? * MON-FRI *
287+
```
288+
250289
## Related Links
251290

252291
* [Cron expressions reference - Amazon EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cron-expressions.html)

cmd/cronskd/flags.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
var (
12+
version string
13+
)
14+
15+
type flags struct {
16+
file string
17+
start string
18+
end string
19+
}
20+
21+
func init() {
22+
cmdLine := flag.NewFlagSet(filepath.Base(os.Args[0]), flag.ExitOnError)
23+
24+
cmdLine.Usage = func() {
25+
fmt.Fprintf(cmdLine.Output(), "Usage: %s [OPTION] [FILE]\n", cmdLine.Name())
26+
cmdLine.PrintDefaults()
27+
}
28+
29+
flag.CommandLine = cmdLine
30+
}
31+
32+
func parseFlags() *flags {
33+
flags := &flags{}
34+
flag.StringVar(&flags.start, "s", "", "start date (default: beginning of day)")
35+
flag.StringVar(&flags.end, "e", "", "end date (default: end of day)")
36+
showVersion := flag.Bool("version", false, "print version and exit")
37+
flag.Parse()
38+
39+
if *showVersion {
40+
printVersionAndExit()
41+
}
42+
43+
args := flag.Args()
44+
45+
if len(args) > 1 {
46+
printUsageAndExit()
47+
} else if len(args) == 0 {
48+
flags.file = "-"
49+
} else {
50+
flags.file = strings.TrimSpace(args[0])
51+
}
52+
53+
return flags
54+
}
55+
56+
func printVersionAndExit() {
57+
v := version
58+
59+
if v == "" {
60+
v = "<nil>"
61+
}
62+
63+
fmt.Fprintln(flag.CommandLine.Output(), v)
64+
os.Exit(0)
65+
}
66+
67+
func printUsageAndExit() {
68+
flag.CommandLine.Usage()
69+
os.Exit(0)
70+
}

cmd/cronskd/go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module github.com/winebarrel/cronplan/cmd/cronskd
2+
3+
go 1.21
4+
5+
toolchain go1.23.3
6+
7+
replace github.com/winebarrel/cronplan => ../..
8+
9+
require (
10+
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
11+
github.com/winebarrel/cronplan v0.0.0-00010101000000-000000000000
12+
)
13+
14+
require github.com/alecthomas/participle/v2 v2.1.1 // indirect

cmd/cronskd/go.sum

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0=
2+
github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
3+
github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8=
4+
github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c=
5+
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
6+
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
7+
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
8+
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
9+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
10+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11+
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
12+
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
13+
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
14+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
15+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16+
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
17+
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
18+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
19+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
20+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
21+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
22+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
23+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

cmd/cronskd/main.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"log"
7+
"os"
8+
"sort"
9+
"strings"
10+
"time"
11+
12+
"github.com/araddon/dateparse"
13+
"github.com/winebarrel/cronplan"
14+
)
15+
16+
func init() {
17+
log.SetFlags(0)
18+
}
19+
20+
func main() {
21+
flags := parseFlags()
22+
23+
var scanner *bufio.Scanner
24+
25+
if flags.file == "-" {
26+
scanner = bufio.NewScanner(os.Stdin)
27+
} else {
28+
file, err := os.Open(flags.file)
29+
30+
if err != nil {
31+
log.Fatal(err)
32+
}
33+
34+
defer file.Close()
35+
36+
scanner = bufio.NewScanner(file)
37+
}
38+
39+
type exprNext struct {
40+
expr string
41+
next time.Time
42+
}
43+
44+
schedule := []exprNext{}
45+
46+
for scanner.Scan() {
47+
expr := scanner.Text()
48+
expr = strings.TrimSpace(expr)
49+
expr = strings.TrimPrefix(expr, "cron(")
50+
expr = strings.TrimSuffix(expr, ")")
51+
52+
cron, err := cronplan.Parse(expr)
53+
54+
if err != nil {
55+
log.Fatal(err)
56+
}
57+
58+
var start, end time.Time
59+
60+
if flags.start == "" {
61+
now := time.Now()
62+
start = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
63+
} else {
64+
start, err = dateparse.ParseAny(flags.start)
65+
66+
if err != nil {
67+
log.Fatal(err)
68+
}
69+
}
70+
71+
if flags.end == "" {
72+
end = time.Date(start.Year(), start.Month(), start.Day(), 23, 59, 50, 0, start.Location())
73+
}
74+
75+
nexts := cron.Between(start, end)
76+
77+
for _, n := range nexts {
78+
schedule = append(schedule, exprNext{expr: expr, next: n})
79+
}
80+
}
81+
82+
sort.Slice(schedule, func(i, j int) bool {
83+
return schedule[i].next.Before(schedule[j].next)
84+
})
85+
86+
for _, ln := range schedule {
87+
fmt.Printf("%s\t%s\n", ln.next.Format("Mon, 02 Jan 2006 15:04:05"), ln.expr)
88+
}
89+
}

0 commit comments

Comments
 (0)