Skip to content

Commit 39ca5c8

Browse files
committed
[#7] Take type info from action-typing.yml
1 parent 365bd57 commit 39ca5c8

File tree

8 files changed

+53
-39
lines changed

8 files changed

+53
-39
lines changed

README.md

+15-7
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,49 @@
66

77
Bring type-safety to your GitHub actions' API!
88

9-
This is a GitHub action that validates your action's manifest (action.y(a)ml) and ensures the inputs and outputs have
10-
types set according to a certain specification.
9+
This is a GitHub action that validates your action's type specs (`action-types.y(a)ml`) and ensures the inputs and
10+
outputs have types set according to a certain specification.
1111

1212
# Example
1313

14-
Let's say your action has such manifest:
14+
Let's say your action has such manifest (`action.yml`):
1515

1616
```yaml
1717
name: My cool action
1818
description: Just to showcase GitHub Actions typing
19-
typingSpec: krzema12/[email protected]
2019
inputs:
2120
verbose:
2221
description: 'Set to true to display debug information helpful when troubleshooting issues with this action.'
2322
required: false
2423
default: 'false'
25-
type: boolean
2624
log-level:
2725
description: 'Specify the level of details for logging.'
2826
required: true
2927
permissions:
3028
description: 'Who should have access.'
31-
type: inttteger
3229
runs:
3330
using: 'node16'
3431
image: 'dist/main.js'
3532
```
3633
34+
and such `action-types.yml` next to it:
35+
36+
```yaml
37+
typingSpec: krzema12/[email protected]
38+
inputs:
39+
verbose:
40+
type: boolean
41+
permissions:
42+
type: inttteger
43+
```
44+
3745
This action, once used within a workflow, will fail the workflow run and produce such output:
3846

3947
![Example output](docs/ExampleOutput.png)
4048

4149
# Usage
4250

43-
In your action's `action.yml`:
51+
Create a new file in your action repo's root directory: `action-types.yml`, then:
4452

4553
- add top-level attribute: `typingSpec: krzema12/[email protected]`. Thanks to this, you as the actions' author
4654
state which kind of typings your actions adheres to. At the time of writing this, no standard has emerged yet. This

action-types.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
typingSpec: krzema12/[email protected]
2+
inputs:
3+
verbose:
4+
type: boolean

action.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
name: GitHub Actions Typing
22
description: Bring type-safety to your GitHub actions' API!
33
author: Piotr Krzemiński
4-
typingSpec: krzema12/[email protected]
54
inputs:
65
verbose:
76
description: 'Set to true to display debug information helpful when troubleshooting issues with this action.'
87
required: false
98
default: 'false'
10-
type: boolean
119
runs:
1210
using: 'docker'
1311
image: 'Dockerfile'

src/main/kotlin/it/krzeminski/githubactionstyping/Main.kt

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package it.krzeminski.githubactionstyping
22

33
import it.krzeminski.githubactionstyping.github.getBooleanInput
4-
import it.krzeminski.githubactionstyping.parsing.parseManifest
5-
import it.krzeminski.githubactionstyping.parsing.readActionManifest
4+
import it.krzeminski.githubactionstyping.parsing.parseTypesManifest
5+
import it.krzeminski.githubactionstyping.parsing.readActionTypesManifest
66
import it.krzeminski.githubactionstyping.reporting.toPlaintextReport
77
import it.krzeminski.githubactionstyping.validation.ItemValidationResult
88
import it.krzeminski.githubactionstyping.validation.validate
99
import kotlin.system.exitProcess
1010

1111
fun main() {
12-
val manifest = readActionManifest() ?: return
13-
val parsedManifest = parseManifest(manifest)
12+
val manifest = readActionTypesManifest() ?: run {
13+
println("No types manifest (action-types.yml or action-types.yaml) found!")
14+
exitProcess(1)
15+
throw IllegalStateException()
16+
}
17+
val parsedManifest = parseTypesManifest(manifest)
1418

1519
if (getBooleanInput("verbose")) {
1620
println("Action's manifest:")

src/main/kotlin/it/krzeminski/githubactionstyping/parsing/ManifestParsing.kt renamed to src/main/kotlin/it/krzeminski/githubactionstyping/parsing/TypesManifestParsing.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable
55
import kotlinx.serialization.decodeFromString
66

77
@Serializable
8-
data class Manifest(
8+
data class TypesManifest(
99
val typingSpec: String? = null,
1010
val inputs: Map<String, ApiItem> = emptyMap(),
1111
val outputs: Map<String, ApiItem> = emptyMap(),
@@ -26,5 +26,5 @@ private val myYaml = Yaml(
2626
)
2727
)
2828

29-
fun parseManifest(manifestString: String): Manifest =
29+
fun parseTypesManifest(manifestString: String): TypesManifest =
3030
myYaml.decodeFromString(manifestString)

src/main/kotlin/it/krzeminski/githubactionstyping/parsing/ManifestReading.kt renamed to src/main/kotlin/it/krzeminski/githubactionstyping/parsing/TypesManifestReading.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package it.krzeminski.githubactionstyping.parsing
22

33
import java.io.File
44

5-
fun readActionManifest(): String? =
5+
fun readActionTypesManifest(): String? =
66
listOf("yaml", "yml")
7-
.map { "action.$it" }
7+
.map { "action-types.$it" }
88
.firstOrNull { File(it).exists() }
99
?.let { File(it).readText() }

src/main/kotlin/it/krzeminski/githubactionstyping/validation/ManifestValidation.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package it.krzeminski.githubactionstyping.validation
22

33
import it.krzeminski.githubactionstyping.parsing.ApiItem
4-
import it.krzeminski.githubactionstyping.parsing.Manifest
4+
import it.krzeminski.githubactionstyping.parsing.TypesManifest
55
import it.krzeminski.githubactionstyping.validation.types.validateBoolean
66
import it.krzeminski.githubactionstyping.validation.types.validateEnum
77
import it.krzeminski.githubactionstyping.validation.types.validateFloat
@@ -11,7 +11,7 @@ import it.krzeminski.githubactionstyping.validation.types.validateString
1111

1212
const val expectedTypingSpec = "krzema12/[email protected]"
1313

14-
fun Manifest.validate(): ActionValidationResult {
14+
fun TypesManifest.validate(): ActionValidationResult {
1515
if (this.typingSpec == null || this.typingSpec != expectedTypingSpec) {
1616
return ActionValidationResult(
1717
overallResult = ItemValidationResult.Invalid(

src/test/kotlin/it/krzeminski/githubactionstyping/validation/ManifestValidationTest.kt

+20-20
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ package it.krzeminski.githubactionstyping.validation
33
import io.kotest.core.spec.style.FunSpec
44
import io.kotest.matchers.shouldBe
55
import it.krzeminski.githubactionstyping.parsing.ApiItem
6-
import it.krzeminski.githubactionstyping.parsing.Manifest
6+
import it.krzeminski.githubactionstyping.parsing.TypesManifest
77

88
class ManifestValidationTest : FunSpec({
99
context("success cases") {
1010
test("primitive types") {
1111
// given
12-
val manifest = Manifest(
12+
val manifest = TypesManifest(
1313
typingSpec = expectedTypingSpec,
1414
inputs = mapOf(
1515
"string-input" to ApiItem(type = "string"),
@@ -38,7 +38,7 @@ class ManifestValidationTest : FunSpec({
3838

3939
test("enum type") {
4040
// given
41-
val manifest = Manifest(
41+
val manifest = TypesManifest(
4242
typingSpec = expectedTypingSpec,
4343
inputs = mapOf(
4444
"enum-input" to ApiItem(type = "enum", allowedValues = listOf("foo", "bar", "baz")),
@@ -59,7 +59,7 @@ class ManifestValidationTest : FunSpec({
5959

6060
test("list type") {
6161
// given
62-
val manifest = Manifest(
62+
val manifest = TypesManifest(
6363
typingSpec = expectedTypingSpec,
6464
inputs = mapOf(
6565
"list-of-strings-input" to ApiItem(
@@ -113,7 +113,7 @@ class ManifestValidationTest : FunSpec({
113113
context("failure cases") {
114114
test("no typing spec set") {
115115
// given
116-
val manifest = Manifest(
116+
val manifest = TypesManifest(
117117
typingSpec = null,
118118
inputs = emptyMap(),
119119
outputs = emptyMap(),
@@ -134,7 +134,7 @@ class ManifestValidationTest : FunSpec({
134134

135135
test("incorrect typing spec set") {
136136
// given
137-
val manifest = Manifest(
137+
val manifest = TypesManifest(
138138
typingSpec = "incorrect-typing-spec",
139139
inputs = emptyMap(),
140140
outputs = emptyMap(),
@@ -156,7 +156,7 @@ class ManifestValidationTest : FunSpec({
156156

157157
test("input and output without type") {
158158
// given
159-
val manifest = Manifest(
159+
val manifest = TypesManifest(
160160
typingSpec = expectedTypingSpec,
161161
inputs = mapOf(
162162
"some-input" to ApiItem(type = null),
@@ -187,7 +187,7 @@ class ManifestValidationTest : FunSpec({
187187

188188
test("unknown type") {
189189
// given
190-
val manifest = Manifest(
190+
val manifest = TypesManifest(
191191
typingSpec = expectedTypingSpec,
192192
inputs = mapOf(
193193
"some-input" to ApiItem(type = "for-sure-unknown-type"),
@@ -210,7 +210,7 @@ class ManifestValidationTest : FunSpec({
210210

211211
test("primitive types with 'allowedValues' attribute") {
212212
// given
213-
val manifest = Manifest(
213+
val manifest = TypesManifest(
214214
typingSpec = expectedTypingSpec,
215215
inputs = mapOf(
216216
"string-input" to ApiItem(type = "string", allowedValues = listOf("foo", "bar")),
@@ -237,7 +237,7 @@ class ManifestValidationTest : FunSpec({
237237

238238
test("primitive types with 'separator' attribute") {
239239
// given
240-
val manifest = Manifest(
240+
val manifest = TypesManifest(
241241
typingSpec = expectedTypingSpec,
242242
inputs = mapOf(
243243
"string-input" to ApiItem(type = "string", separator = ","),
@@ -264,7 +264,7 @@ class ManifestValidationTest : FunSpec({
264264

265265
test("non-list types with 'listItem' attribute") {
266266
// given
267-
val manifest = Manifest(
267+
val manifest = TypesManifest(
268268
typingSpec = expectedTypingSpec,
269269
inputs = mapOf(
270270
"string-input" to ApiItem(type = "string", listItem = ApiItem(type = "string")),
@@ -297,7 +297,7 @@ class ManifestValidationTest : FunSpec({
297297

298298
test("enum type with 'separator' attribute") {
299299
// given
300-
val manifest = Manifest(
300+
val manifest = TypesManifest(
301301
typingSpec = expectedTypingSpec,
302302
inputs = mapOf(
303303
"enum-input" to ApiItem(type = "enum", allowedValues = listOf("foo", "bar", "baz"), separator = ","),
@@ -318,7 +318,7 @@ class ManifestValidationTest : FunSpec({
318318

319319
test("enum type without 'allowedValues' attribute") {
320320
// given
321-
val manifest = Manifest(
321+
val manifest = TypesManifest(
322322
typingSpec = expectedTypingSpec,
323323
inputs = mapOf(
324324
"enum-input" to ApiItem(type = "enum", allowedValues = null),
@@ -339,7 +339,7 @@ class ManifestValidationTest : FunSpec({
339339

340340
test("enum type with just one allowed value") {
341341
// given
342-
val manifest = Manifest(
342+
val manifest = TypesManifest(
343343
typingSpec = expectedTypingSpec,
344344
inputs = mapOf(
345345
"enum-input" to ApiItem(type = "enum", allowedValues = listOf("foo")),
@@ -360,7 +360,7 @@ class ManifestValidationTest : FunSpec({
360360

361361
test("list type without 'listItem' attribute") {
362362
// given
363-
val manifest = Manifest(
363+
val manifest = TypesManifest(
364364
typingSpec = expectedTypingSpec,
365365
inputs = mapOf(
366366
"list-input" to ApiItem(type = "list", separator = ","),
@@ -381,7 +381,7 @@ class ManifestValidationTest : FunSpec({
381381

382382
test("list type without 'separator' attribute") {
383383
// given
384-
val manifest = Manifest(
384+
val manifest = TypesManifest(
385385
typingSpec = expectedTypingSpec,
386386
inputs = mapOf(
387387
"list-input" to ApiItem(type = "list", listItem = ApiItem(type = "string")),
@@ -402,7 +402,7 @@ class ManifestValidationTest : FunSpec({
402402

403403
test("list type with 'allowedValues' attribute") {
404404
// given
405-
val manifest = Manifest(
405+
val manifest = TypesManifest(
406406
typingSpec = expectedTypingSpec,
407407
inputs = mapOf(
408408
"list-input" to ApiItem(
@@ -428,7 +428,7 @@ class ManifestValidationTest : FunSpec({
428428

429429
test("list type with forbidden list item type") {
430430
// given
431-
val manifest = Manifest(
431+
val manifest = TypesManifest(
432432
typingSpec = expectedTypingSpec,
433433
inputs = mapOf(
434434
"list-of-lists-input" to ApiItem(
@@ -459,7 +459,7 @@ class ManifestValidationTest : FunSpec({
459459

460460
test("list type with list item type with incorrect attributes") {
461461
// given
462-
val manifest = Manifest(
462+
val manifest = TypesManifest(
463463
typingSpec = expectedTypingSpec,
464464
inputs = mapOf(
465465
"list-of-enums-without-allowed-values-input" to ApiItem(
@@ -502,7 +502,7 @@ class ManifestValidationTest : FunSpec({
502502

503503
test("non-integer types with named values") {
504504
// given
505-
val manifest = Manifest(
505+
val manifest = TypesManifest(
506506
typingSpec = expectedTypingSpec,
507507
inputs = mapOf(
508508
"string-input" to ApiItem(type = "string", namedValues = mapOf("foo" to 1)),

0 commit comments

Comments
 (0)