Jaypore CI

> Jaypore CI: Minimal, Offline, Local CI system.
Log | Files | Refs | README

cron_parser.go (1799B)


      1 package jci
      2 
      3 import (
      4 	"bufio"
      5 	"errors"
      6 	"fmt"
      7 	"io"
      8 	"os"
      9 	"path/filepath"
     10 	"strings"
     11 )
     12 
     13 // CronEntry represents a single line from .jci/crontab
     14 // It contains the parsed schedule as well as the command portion.
     15 type CronEntry struct {
     16 	Line     int
     17 	Schedule string
     18 	Command  string
     19 	Raw      string
     20 }
     21 
     22 // LoadCronEntries opens .jci/crontab (if it exists) and parses all entries.
     23 func LoadCronEntries(repoRoot string) ([]CronEntry, error) {
     24 	cronPath := filepath.Join(repoRoot, ".jci", "crontab")
     25 	f, err := os.Open(cronPath)
     26 	if err != nil {
     27 		if os.IsNotExist(err) {
     28 			return nil, nil
     29 		}
     30 		return nil, err
     31 	}
     32 	defer f.Close()
     33 
     34 	return parseCronEntries(f)
     35 }
     36 
     37 func parseCronEntries(r io.Reader) ([]CronEntry, error) {
     38 	scanner := bufio.NewScanner(r)
     39 	var entries []CronEntry
     40 	lineNum := 0
     41 
     42 	for scanner.Scan() {
     43 		lineNum++
     44 		raw := strings.TrimSpace(scanner.Text())
     45 		if raw == "" || strings.HasPrefix(raw, "#") {
     46 			continue
     47 		}
     48 
     49 		schedule, command, err := splitCronLine(raw)
     50 		if err != nil {
     51 			return nil, fmt.Errorf("line %d: %w", lineNum, err)
     52 		}
     53 
     54 		entries = append(entries, CronEntry{
     55 			Line:     lineNum,
     56 			Schedule: schedule,
     57 			Command:  command,
     58 			Raw:      raw,
     59 		})
     60 	}
     61 
     62 	if err := scanner.Err(); err != nil {
     63 		return nil, err
     64 	}
     65 
     66 	return entries, nil
     67 }
     68 
     69 func splitCronLine(raw string) (string, string, error) {
     70 	if strings.HasPrefix(raw, "@") {
     71 		parts := strings.Fields(raw)
     72 		if len(parts) < 2 {
     73 			return "", "", errors.New("missing command for cron entry")
     74 		}
     75 		return parts[0], strings.Join(parts[1:], " "), nil
     76 	}
     77 
     78 	parts := strings.Fields(raw)
     79 	if len(parts) < 6 {
     80 		return "", "", errors.New("cron entry must have 5 time fields and a command")
     81 	}
     82 
     83 	schedule := strings.Join(parts[:5], " ")
     84 	command := strings.Join(parts[5:], " ")
     85 	return schedule, command, nil
     86 }