Skip to content

Commit

Permalink
Merge branch 'map-object'
Browse files Browse the repository at this point in the history
  • Loading branch information
rhysd committed Sep 20, 2021
2 parents 444f866 + 4b1f983 commit c72195b
Show file tree
Hide file tree
Showing 13 changed files with 936 additions and 890 deletions.
7 changes: 4 additions & 3 deletions docs/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ checker.
| Array | Array of specific type elements | `array<T>` |
| Loose object | Object which can contain any properties | `object` |
| Strict object | Object whose properties are strictly typed | `{prop1: T1, prop2: T2}` |
| Map object | Object who has specific type values like `env` context | `{string => T}` |

Type check by actionlint is more strict than GitHub Actions runtime.

Expand Down Expand Up @@ -278,7 +279,7 @@ test.yaml:11:24: receiver of object dereference "owner" must be type of object b
|
11 | - run: echo '${{ github.repository.owner }}'
| ^~~~~~~~~~~~~~~~~~~~~~~
test.yaml:13:20: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type object [expression]
test.yaml:13:20: object, array, and null values should not be evaluated in template with ${{ }} but evaluating the value of type {string => string} [expression]
|
13 | - run: echo '${{ env }}'
| ^~~
Expand Down Expand Up @@ -502,7 +503,7 @@ test.yaml:8:23: property "cache" is not defined in object type {} [expression]
|
8 | - run: echo ${{ steps.cache.outputs.cache-hit }}
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.yaml:18:23: property "cache_hit" is not defined in object type {cache-hit: any} [expression]
test.yaml:18:23: property "cache_hit" is not defined in object type {cache-hit: string} [expression]
|
18 | - run: echo ${{ steps.cache.outputs.cache_hit }}
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -559,7 +560,7 @@ test.yaml:8:23: property "my_action" is not defined in object type {} [expressio
|
8 | - run: echo ${{ steps.my_action.outputs.some_value }}
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.yaml:15:23: property "some-value" is not defined in object type {some_value: any} [expression]
test.yaml:15:23: property "some-value" is not defined in object type {some_value: string} [expression]
|
15 | - run: echo ${{ steps.my_action.outputs.some-value }}
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
153 changes: 78 additions & 75 deletions expr_sema.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,84 +193,76 @@ var BuiltinFuncSignatures = map[string][]*FuncSignature{
// documented at https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#contexts
var BuiltinGlobalVariableTypes = map[string]ExprType{
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context
"github": &ObjectType{
Props: map[string]ExprType{
"action": StringType{},
"action_path": StringType{},
"actor": StringType{},
"base_ref": StringType{},
"event": NewObjectType(), // Note: Stricter type check for this payload would be possible
"event_name": StringType{},
"event_path": StringType{},
"head_ref": StringType{},
"job": StringType{},
"ref": StringType{},
"repository": StringType{},
"repository_owner": StringType{},
"run_id": StringType{},
"run_number": StringType{},
"sha": StringType{},
"token": StringType{},
"workflow": StringType{},
"workspace": StringType{},
// These are not documented but actually exist
"action_ref": StringType{},
"action_repository": StringType{},
"api_url": StringType{},
"env": StringType{},
"graphql_url": StringType{},
"path": StringType{},
"repositoryurl": StringType{}, // repositoryUrl
"retention_days": NumberType{},
"server_url": StringType{},
},
StrictProps: true,
},
"github": NewStrictObjectType(map[string]ExprType{
"action": StringType{},
"action_path": StringType{},
"actor": StringType{},
"base_ref": StringType{},
"event": NewEmptyObjectType(), // Note: Stricter type check for this payload would be possible
"event_name": StringType{},
"event_path": StringType{},
"head_ref": StringType{},
"job": StringType{},
"ref": StringType{},
"repository": StringType{},
"repository_owner": StringType{},
"run_id": StringType{},
"run_number": StringType{},
"sha": StringType{},
"token": StringType{},
"workflow": StringType{},
"workspace": StringType{},
// Below props are not documented but actually exist
"action_ref": StringType{},
"action_repository": StringType{},
"api_url": StringType{},
"env": StringType{},
"graphql_url": StringType{},
"path": StringType{},
"repositoryurl": StringType{}, // repositoryUrl
"retention_days": NumberType{},
"server_url": StringType{},
}),
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#env-context
"env": NewObjectType(),
"env": NewMapObjectType(StringType{}), // env.<env_name>
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#job-context
"job": &ObjectType{
Props: map[string]ExprType{
"container": &ObjectType{
Props: map[string]ExprType{
"id": StringType{},
"network": StringType{},
},
StrictProps: true,
},
"services": NewObjectType(),
"status": StringType{},
},
StrictProps: true,
},
"job": NewStrictObjectType(map[string]ExprType{
"container": NewStrictObjectType(map[string]ExprType{
"id": StringType{},
"network": StringType{},
}),
"services": NewMapObjectType(
NewStrictObjectType(map[string]ExprType{
"id": StringType{}, // job.services.<service id>.id
"network": StringType{},
"ports": NewEmptyObjectType(),
}),
),
"status": StringType{},
}),
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#steps-context
"steps": NewStrictObjectType(),
"steps": NewEmptyStrictObjectType(), // This value will be updated contextually
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#runner-context
"runner": &ObjectType{
Props: map[string]ExprType{
"os": StringType{},
"temp": StringType{},
"tool_cache": StringType{},
// These are not documented but actually exist
"workspace": StringType{},
},
StrictProps: true,
},
"runner": NewStrictObjectType(map[string]ExprType{
"os": StringType{},
"temp": StringType{},
"tool_cache": StringType{},
// These are not documented but actually exist
"workspace": StringType{},
}),
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#contexts
"secrets": NewObjectType(),
"secrets": NewEmptyObjectType(),
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#contexts
"strategy": &ObjectType{
Props: map[string]ExprType{
"fail-fast": BoolType{},
"job-index": NumberType{},
"job-total": NumberType{},
"max-parallel": NumberType{},
},
},
"strategy": NewObjectType(map[string]ExprType{
"fail-fast": BoolType{},
"job-index": NumberType{},
"job-total": NumberType{},
"max-parallel": NumberType{},
}),
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#contexts
"matrix": NewStrictObjectType(),
"matrix": NewEmptyStrictObjectType(), // This value will be updated contextually
// https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#needs-context
"needs": NewStrictObjectType(),
"needs": NewEmptyStrictObjectType(), // This value will be updated contextually
}

// Semantics checker
Expand Down Expand Up @@ -380,7 +372,10 @@ func (sema *ExprSemanticsChecker) checkObjectDeref(n *ObjectDerefNode) ExprType
if t, ok := ty.Props[n.Property]; ok {
return t
}
if ty.StrictProps {
if ty.Mapped != nil {
return ty.Mapped
}
if ty.IsStrict() {
sema.errorf(n, "property %q is not defined in object type %s", n.Property, ty.String())
}
return AnyType{}
Expand All @@ -398,7 +393,9 @@ func (sema *ExprSemanticsChecker) checkObjectDeref(n *ObjectDerefNode) ExprType
var elem ExprType = AnyType{}
if t, ok := et.Props[n.Property]; ok {
elem = t
} else if et.StrictProps {
} else if et.Mapped != nil {
elem = et.Mapped
} else if et.IsStrict() {
sema.errorf(n, "property %q is not defined in object type %s as element of filtered array", n.Property, et.String())
}
return &ArrayType{elem, true}
Expand Down Expand Up @@ -458,11 +455,17 @@ func (sema *ExprSemanticsChecker) checkIndexAccess(n *IndexAccessNode) ExprType
if prop, ok := ty.Props[lit.Value]; ok {
return prop
}
if ty.StrictProps {
if ty.Mapped != nil {
return ty.Mapped
}
if ty.IsStrict() {
sema.errorf(n, "property %q is not defined in object type %s", lit.Value, ty.String())
}
}
return AnyType{}
if ty.Mapped != nil {
return ty.Mapped
}
return AnyType{} // Fallback
default:
sema.errorf(n.Index, "property access of object must be type of string but got %q", idx.String())
return AnyType{}
Expand Down Expand Up @@ -613,7 +616,7 @@ func (sema *ExprSemanticsChecker) checkCompareOp(n *CompareOpNode) ExprType {
func (sema *ExprSemanticsChecker) checkLogicalOp(n *LogicalOpNode) ExprType {
lty := sema.check(n.Left)
rty := sema.check(n.Right)
return lty.Fuse(rty)
return lty.Merge(rty)
}

func (sema *ExprSemanticsChecker) check(expr ExprNode) ExprType {
Expand Down
Loading

0 comments on commit c72195b

Please sign in to comment.