Repository: typesafegithub/github-workflows-kt (kotlin)

Issue:
[Bug] Cron does not accept the valid symbolic names for dayWeek
# Action

```kts
Cron(
    minute = "0",
    hour = "0",
    dayWeek = "FRI"
)
```

# Expected

Valid symbolic names for `dayWeek` to be accepted.

# Actual

```plain
java.lang.IllegalStateException: Your Cron syntax [0 0 * * FRI] contains errors:
- Field 'dayWeek' with value [FRI] contains invalid characters
	at it.krzeminski.githubactions.domain.triggers.ScheduleKt.Cron(Schedule.kt:32)
	at it.krzeminski.githubactions.domain.triggers.ScheduleKt.Cron$default(Schedule.kt:16)
	at Check_dependency_versions_main.<init>(check-dependency-versions.main.kts:34)
```

# Workaround, if exists

Using numeric values instead

# Library version

v0.35.0

Pull request description:
Closes #642.

Patch:
diff --git a/library/src/main/kotlin/it/krzeminski/githubactions/domain/triggers/Schedule.kt b/library/src/main/kotlin/it/krzeminski/githubactions/domain/triggers/Schedule.kt
index c557ef7ab8..211d18c269 100644
--- a/library/src/main/kotlin/it/krzeminski/githubactions/domain/triggers/Schedule.kt
+++ b/library/src/main/kotlin/it/krzeminski/githubactions/domain/triggers/Schedule.kt
@@ -8,11 +8,19 @@ public data class Schedule(
     override val _customArguments: Map<String, @Contextual Any> = mapOf(),
 ) : Trigger()
 
-/** See https://crontab.guru **/
+/**
+ * Builds a cron expression and performs no validation.
+ *
+ * @see https://crontab.guru
+ **/
 @Serializable
 public data class Cron(val expression: String)
 
-/** See https://crontab.guru **/
+/**
+ * Builds a cron expression given individual components. Performs basic validation.
+ *
+ * @see https://crontab.guru
+ **/
 public fun Cron(
     minute: String = "*",
     hour: String = "*",
@@ -25,8 +33,18 @@ public fun Cron(
         checkCronSyntax(minute, "minute", 0..59),
         checkCronSyntax(hour, "hour", 0..23),
         checkCronSyntax(dayMonth, "dayMonth", 0..59),
-        checkCronSyntax(month, "month", 1..31),
-        checkCronSyntax(dayWeek, "dayWeek", 0..6),
+        checkCronSyntax(
+            month,
+            "month",
+            1..31,
+            listOf("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"),
+        ),
+        checkCronSyntax(
+            dayWeek,
+            "dayWeek",
+            0..6,
+            listOf("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"),
+        ),
     ).flatten()
     val cron = "$minute $hour $dayMonth $month $dayWeek"
     check(errors.isEmpty()) {
@@ -36,16 +54,31 @@ public fun Cron(
     return Cron(cron)
 }
 
-private fun checkCronSyntax(value: String, field: String, range: IntRange): List<String> {
+private fun checkCronSyntax(
+    value: String,
+    field: String,
+    range: IntRange,
+    namedValues: List<String>? = null,
+): List<String> {
     val errors = mutableListOf<String>()
     fun addErrorIf(message: String, predicate: () -> Boolean) {
         if (predicate()) errors += "Field '$field' with value [$value] $message"
     }
 
     addErrorIf("contains invalid characters") {
-        val allowedCharacters = ('0'..'9') + listOf('-', '*', ',', '/')
+        val allowedCharacters = ('0'..'9') + listOf('-', '*', ',', '/') + ('A'..'Z')
         value.any { it !in allowedCharacters }
     }
+    addErrorIf("contains value(s) not included in $namedValues") {
+        namedValues?.let {
+            val replace = value.replace(Regex("[-*,/]"), " ")
+            val values = replace
+                .split(" ")
+                .filter { it.isNotBlank() }
+                .filter { it.toIntOrNull() == null }
+            values.any { it !in namedValues }
+        } ?: false
+    }
     addErrorIf("contains number(s) outside of range $range") {
         val replace = value.replace(Regex("[-*,/]"), " ")
         val numbers = replace


Test patch:
diff --git a/library/src/test/kotlin/it/krzeminski/githubactions/domain/triggers/ScheduleTest.kt b/library/src/test/kotlin/it/krzeminski/githubactions/domain/triggers/ScheduleTest.kt
index 0671944578..1f0b8eab31 100644
--- a/library/src/test/kotlin/it/krzeminski/githubactions/domain/triggers/ScheduleTest.kt
+++ b/library/src/test/kotlin/it/krzeminski/githubactions/domain/triggers/ScheduleTest.kt
@@ -15,6 +15,10 @@ class ScheduleTest : FunSpec({
                 to "1 2 3 4 5",
             Cron(minute = "1,4,5", hour = "4-6", dayMonth = "10-20/2", month = "6,8", dayWeek = "1/3")
                 to "1,4,5 4-6 10-20/2 6,8 1/3",
+            Cron(dayWeek = "MON")
+                to "* * * * MON",
+            Cron(month = "JAN")
+                to "* * * JAN *",
         )
 
         test("valid expressions") {
@@ -64,5 +68,27 @@ class ScheduleTest : FunSpec({
                 """.trimIndent(),
             )
         }
+
+        test("with invalid dayWeek name") {
+            shouldThrowAny {
+                Cron(dayWeek = "XYZ")
+            }.shouldHaveMessage(
+                """
+                Your Cron syntax [* * * * XYZ] contains errors:
+                - Field 'dayWeek' with value [XYZ] contains value(s) not included in [MON, TUE, WED, THU, FRI, SAT, SUN]
+                """.trimIndent(),
+            )
+        }
+
+        test("with invalid month name") {
+            shouldThrowAny {
+                Cron(month = "XYZ")
+            }.shouldHaveMessage(
+                """
+                Your Cron syntax [* * * XYZ *] contains errors:
+                - Field 'month' with value [XYZ] contains value(s) not included in [JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC]
+                """.trimIndent(),
+            )
+        }
     }
 })