问题描述
我正在使用 Dhall 为 github 操作生成 Yaml 文件。在 GH Action 中,您可以指定一个矩阵来生成多个案例(例如 Scala 版本和项目名称的组合)。但您也可以指定要排除的组合。
例如:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest,windows-latest,ubuntu-18.04]
node: [8,10,12,14]
exclude:
# excludes node 8 on macOS
- os: macos-latest
- node: 8
另一个有效的例子:
runs-on: ${{ matrix.os }}
strategy:
matrix:
jvm: [8,11]
scala: [2.12.12,2.13.5,3.0.0]
exclude:
- jvm: 8
- scala: 2.12.12
最后一个:
runs-on: ${{ matrix.os }}
strategy:
matrix:
ruby: [2.7.3,3.0.0]
特别是对于 matrix
,我们有多个字段是 List Text
,但我们也有 exclude
,我认为它是 List
的 Record
。
如果我只是从头开始编写代码,而不定义类型,我可以在 Dhall 中编写此代码(而且它似乎使用我拥有的特定键生成 Record
。
但是,如果我想构建一个带有类型的库,或者更具体地说是扩展这个库,我无法弄清楚如何编写正确的类型。通常,请参阅此定义:
{ matrix : List { mapKey : Text,mapValue : List Text },fail-fast : Optional Bool,max-parallel : Optional Natural
}
https://github.com/regadas/github-actions-dhall/blob/master/types/Strategy.dhall
matrix
被定义为 List { mapKey : Text,mapValue : List Text }
,但 exclude
不是 List Text
所以它不起作用。而且我不知道如何更改此定义以接受 exclude
。
请注意,在示例中它是 os
和 node
,但它可以是任意键和任意数量的键。
那么知道如何定义该类型吗?
编辑:添加更多示例
解决方法
看起来这需要更改上游 github-actions-dhall 存储库。具体来说,matrix
可能应该是 Optional
字段的记录,而不是 Map
。根据您的示例,它可能至少具有以下字段:
{ os : Optional (List Text),node : Optional (List Natural),exclude : { os : Optional Text,node : Optional Natural }
}
有关更多指导,请参阅 Dhall 手册的这些章节,其中讨论了如何在绑定到配置格式时对 Dhall 类型进行建模:
编辑:将示例 YAML 配置转换为 Dhall 值或相应的 Dhall 类型的一种简单方法是使用 yaml-to-dhall
。例如,如果你把你给我的三个例子放在一个 YAML 数组中(每个例子一个元素),像这样:
- runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest,windows-latest,ubuntu-18.04]
node: [8,10,12,14]
exclude:
# excludes node 8 on macOS
- os: macos-latest
- node: 8
- runs-on: ${{ matrix.os }}
strategy:
matrix:
jvm: [8,11]
scala: [2.12.12,2.13.5,3.0.0]
exclude:
- jvm: 8
- scala: 2.12.12
- runs-on: ${{ matrix.os }}
strategy:
matrix:
ruby: [2.7.3,3.0.0]
然后您可以询问 yaml-to-dhall
您可以使用哪种类型来统一这三个示例,如下所示:
$ yaml-to-dhall type < examples.yaml
List
{ runs-on : Text,strategy :
{ matrix :
{ exclude :
Optional
( List
{ jvm : Optional Natural,node : Optional Natural,os : Optional Text,scala : Optional Text
}
),jvm : Optional (List Natural),os : Optional (List Text),ruby : Optional (List Text),scala : Optional (List Text)
}
}
}
然后,如果您只是从推断类型中删除 List
,那么对于您的配置来说,这是一个可以处理这三个示例的有效类型:
{ runs-on : Text,scala : Optional (List Text)
}
}
}
... 和 yaml-to-dhall
还可以显示该类型对应的 Dhall 值是什么:
$ ✓ yaml-to-dhall < examples.yaml
[ { runs-on = "\${{ matrix.os }}",strategy.matrix
=
{ exclude = Some
[ { jvm = None Natural,node = None Natural,os = Some "macos-latest",scala = None Text
},{ jvm = None Natural,node = Some 8,os = None Text,scala = None Text }
],jvm = None (List Natural),node = Some [ 8,14 ],os = Some [ "macos-latest","windows-latest","ubuntu-18.04" ],ruby = None (List Text),scala = None (List Text)
}
},{ runs-on = "\${{ matrix.os }}",strategy.matrix
=
{ exclude = Some
[ { jvm = Some 8,scala = None Text },scala = Some "2.12.12"
}
],jvm = Some [ 8,11 ],node = None (List Natural),os = None (List Text),scala = Some [ "2.12.12","2.13.5","3.0.0" ]
}
},strategy.matrix
=
{ exclude =
None
( List
{ jvm : Optional Natural,scala : Optional Text
}
),ruby = Some [ "2.7.3","3.0.0" ],scala = None (List Text)
}
}
]
...虽然您可能希望使用 Dhall 对模式的支持来缩短它。上面有关默认处理的 Dhall 手册的链接有更多详细信息,但简短的总结是您可以将以下三个模式保存到名为 ./schemas.dhall
的文件中:
-- ./schemas.dhall
let Exclusion =
{ Type =
{ jvm : Optional Natural,scala : Optional Text
},default =
{ jvm = None Natural,os = None Natural,scala = None Text
}
}
let Matrix =
{ Type =
{ exclude : Optional (List Exclusion.Type),scala : Optional (List Text)
},default =
{ exclude = None (List Exclusion.Type),scala = None (List Text)
}
}
let Config =
{ Type = { runs-on : Text,strategy : { matrix : Matrix.Type } },default = {=}
}
in { Exclusion,Matrix,Config }
...然后重写结果以使用这些模式,如下所示:
$ yaml-to-dhall < examples.yaml | dhall rewrite-with-schemas --schemas ./schemas.dhall
let schemas = ./schemas.dhall
in [ schemas.Config::{,runs-on = "\${{ matrix.os }}",strategy.matrix
= schemas.Matrix::{,exclude = Some
[ schemas.Exclusion::{ os = Some "macos-latest" },schemas.Exclusion::{ node = Some 8,os = None Text }
],"ubuntu-18.04" ]
}
},schemas.Config::{,exclude = Some
[ schemas.Exclusion::{ jvm = Some 8,os = None Text },schemas.Exclusion::{ os = None Text,scala = Some "2.12.12" }
],"3.0.0" ]
}
},strategy.matrix = schemas.Matrix::{ ruby = Some [ "2.7.3","3.0.0" ] }
}
]