Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 337576fbb9 | |||
| 5cb4007629 | |||
| 07fa29649e | |||
| 7bb2962bb0 | |||
| 3904c64796 | |||
| 48ad5e5251 | |||
| 317c4fcd63 | |||
| bf97052855 | |||
| dd8930fd74 | |||
| 2449e5cea1 | |||
| dba3cf5d96 | |||
| 804f83828f | |||
| cf2fd6332f | |||
| 51649f27f8 | |||
| 74db91e276 | |||
| a1d81c730f | |||
| 18d1bd9734 | |||
| fc6a1d3e0c | |||
| 6df34a78ce | |||
| aee5c2b919 | |||
| 8cdfe810db | |||
| 17a680df41 | |||
| 11d3fc25ee | |||
| d2783ed733 | |||
| 6d0f74e38b | |||
| 2ecbeafacb | |||
| a4d3fb1a3e | |||
| 83cac82450 | |||
| fc2bacfcdc | |||
| 7d8a7c11a6 | |||
| e37d7620d6 | |||
| 6e8cbc3e8c | |||
| d0916938ce | |||
| 488879804f | |||
| 736dd1a66c | |||
| d58deaf097 | |||
| f4a32c43cf | |||
| 5aed7594cf | |||
| 81094dc942 | |||
| f29fffce7e | |||
| 8fe871750e | |||
| 709d0d73c6 | |||
| febac16edd | |||
| 1ffef93f4c | |||
| 41b8fa9231 | |||
| 053accfafc | |||
| fe25433a45 | |||
| 0ee008991d | |||
| cf4dce7f71 | |||
| 4b479b0296 | |||
| 53f5a4ce69 | |||
| d08fed3cf5 | |||
| d5ef2f1539 | |||
| 1727735bd4 |
+1
-1
@@ -1 +1 @@
|
||||
* @actions/actions-experience
|
||||
* @actions/actions-workflow-development-reviewers
|
||||
|
||||
+10
-2
@@ -8,6 +8,8 @@ Hi there! We're thrilled that you'd like to contribute to this project. Your hel
|
||||
|
||||
We accept pull requests for bug fixes and features where we've discussed the approach in an issue and given the go-ahead for a community member to work on it. We'd also love to hear about ideas for new features as issues.
|
||||
|
||||
We track issues on our project board [here](https://github.com/orgs/github/projects/9557/views/1).
|
||||
|
||||
Please do:
|
||||
|
||||
* Check existing issues to verify that the [bug][bug issues] or [feature request][feature request issues] has not already been submitted.
|
||||
@@ -21,7 +23,7 @@ Please avoid:
|
||||
|
||||
* Opening pull requests for issues marked `needs-design`, `needs-investigation`, or `blocked`.
|
||||
|
||||
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE.md).
|
||||
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
|
||||
|
||||
@@ -60,4 +62,10 @@ Please also look at the `README.md` files for each package for additional notes
|
||||
|
||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||
- [GitHub Help](https://help.github.com)
|
||||
- [GitHub Help](https://help.github.com)
|
||||
|
||||
|
||||
[bug issues]: https://github.com/actions/languageservices/labels/bug
|
||||
[feature request issues]: https://github.com/actions/languageservices/labels/enhancement
|
||||
[hw]: https://github.com/actions/languageservices/labels/help%20wanted
|
||||
[gfi]: https://github.com/actions/languageservices/labels/good%20first%20issue
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# actions/expressions
|
||||
|
||||
blah
|
||||
|
||||
`@actions/expressions` is a library to parse and evaluate GitHub Actions [expressions](https://docs.github.com/actions/learn-github-actions/expressions).
|
||||
|
||||
## Installation
|
||||
@@ -92,4 +94,4 @@ npm run format-check
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the terms of the MIT open source license. Please refer to [MIT](../LICENSE) for the full terms.
|
||||
This project is licensed under the terms of the MIT open source license. Please refer to [MIT](../LICENSE) for the full terms.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
|
||||
@@ -32,7 +32,7 @@ export class Evaluator implements ExprVisitor<data.ExpressionData> {
|
||||
return this.eval(this.n);
|
||||
}
|
||||
|
||||
private eval(n: Expr): data.ExpressionData {
|
||||
protected eval(n: Expr): data.ExpressionData {
|
||||
return n.accept(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"description": "Language server for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -43,8 +43,8 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.1",
|
||||
"@actions/workflow-parser": "^0.3.1",
|
||||
"@actions/languageservice": "^0.3.4",
|
||||
"@actions/workflow-parser": "^0.3.4",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
|
||||
@@ -2,8 +2,8 @@ import {File} from "@actions/workflow-parser/workflows/file";
|
||||
import {FileProvider} from "@actions/workflow-parser/workflows/file-provider";
|
||||
import {fileIdentifier} from "@actions/workflow-parser/workflows/file-reference";
|
||||
import {Octokit} from "@octokit/rest";
|
||||
import path from "path";
|
||||
import {TTLCache} from "./utils/cache";
|
||||
import vscodeURI from "vscode-uri/lib/umd";
|
||||
|
||||
export function getFileProvider(
|
||||
client: Octokit | undefined,
|
||||
@@ -31,7 +31,10 @@ export function getFileProvider(
|
||||
throw new Error("Local file references are not supported with this configuration");
|
||||
}
|
||||
|
||||
const file = await readFile(path.join(workspace, ref.path));
|
||||
const workspaceURI = vscodeURI.URI.parse(workspace);
|
||||
const refURI = vscodeURI.Utils.joinPath(workspaceURI, ref.path);
|
||||
const file = await readFile(refURI.toString());
|
||||
|
||||
if (!file) {
|
||||
throw new Error(`File not found: ${ref.path}`);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"description": "Language service for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -44,8 +44,8 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.1",
|
||||
"@actions/workflow-parser": "^0.3.1",
|
||||
"@actions/expressions": "^0.3.4",
|
||||
"@actions/workflow-parser": "^0.3.4",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.7",
|
||||
|
||||
@@ -474,4 +474,15 @@ jobs:
|
||||
expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("adds a new line and indentation for mapping keys", async () => {
|
||||
const input = "concurrency: |";
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
expect(result.filter(x => x.label === "cancel-in-progress").map(x => x.textEdit?.newText)).toEqual([
|
||||
"\n cancel-in-progress: "
|
||||
]);
|
||||
expect(result.filter(x => x.label === "group").map(x => x.textEdit?.newText)).toEqual(["\n group: "]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import {Evaluator, ExpressionEvaluationError, data} from "@actions/expressions";
|
||||
import {Expr, Logical} from "@actions/expressions/ast";
|
||||
import {ExpressionData} from "@actions/expressions/data/expressiondata";
|
||||
import {TokenType} from "@actions/expressions/lexer";
|
||||
import {falsy, truthy} from "@actions/expressions/result";
|
||||
import {AccessError} from "./error-dictionary";
|
||||
|
||||
export type ValidationError = {
|
||||
message: string;
|
||||
severity: "error" | "warning";
|
||||
};
|
||||
|
||||
export class ValidationEvaluator extends Evaluator {
|
||||
public readonly errors: ValidationError[] = [];
|
||||
|
||||
public validate() {
|
||||
super.evaluate();
|
||||
}
|
||||
|
||||
protected override eval(n: Expr): ExpressionData {
|
||||
try {
|
||||
return super.eval(n);
|
||||
} catch (e) {
|
||||
// Record error
|
||||
if (e instanceof AccessError) {
|
||||
this.errors.push({
|
||||
message: `Context access might be invalid: ${e.keyName}`,
|
||||
severity: "warning"
|
||||
});
|
||||
} else if (e instanceof ExpressionEvaluationError) {
|
||||
this.errors.push({
|
||||
message: `Expression might be invalid: ${e.message}`,
|
||||
severity: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Return null but continue with the validation
|
||||
return new data.Null();
|
||||
}
|
||||
|
||||
override visitLogical(logical: Logical): ExpressionData {
|
||||
let result: data.ExpressionData | undefined;
|
||||
|
||||
for (const arg of logical.args) {
|
||||
const r = this.eval(arg);
|
||||
|
||||
// Simulate short-circuit behavior but continue to evalute all arguments for validation purposes
|
||||
if (
|
||||
!result &&
|
||||
((logical.operator.type === TokenType.AND && falsy(r)) || (logical.operator.type === TokenType.OR && truthy(r)))
|
||||
) {
|
||||
result = r;
|
||||
}
|
||||
}
|
||||
|
||||
// result is always assigned before we return here
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return result!;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,52 @@ jobs:
|
||||
]);
|
||||
});
|
||||
|
||||
it("access invalid context field in short-circuited expression", async () => {
|
||||
const result = await validate(
|
||||
createDocument(
|
||||
"wf.yaml",
|
||||
`on: push
|
||||
run-name: name-\${{ github.does-not-exist || github.does-not-exist2 }}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo`
|
||||
)
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
message: "Context access might be invalid: does-not-exist",
|
||||
range: {
|
||||
end: {
|
||||
character: 69,
|
||||
line: 1
|
||||
},
|
||||
start: {
|
||||
character: 15,
|
||||
line: 1
|
||||
}
|
||||
},
|
||||
severity: DiagnosticSeverity.Warning
|
||||
},
|
||||
{
|
||||
message: "Context access might be invalid: does-not-exist2",
|
||||
range: {
|
||||
end: {
|
||||
character: 69,
|
||||
line: 1
|
||||
},
|
||||
start: {
|
||||
character: 15,
|
||||
line: 1
|
||||
}
|
||||
},
|
||||
severity: DiagnosticSeverity.Warning
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it("partial skip access invalid context on incomplete", async () => {
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: (context: string) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Evaluator, ExpressionEvaluationError, Lexer, Parser} from "@actions/expressions";
|
||||
import {Lexer, Parser} from "@actions/expressions";
|
||||
import {Expr} from "@actions/expressions/ast";
|
||||
import {isBasicExpression, isString, ParseWorkflowResult, WorkflowTemplate} from "@actions/workflow-parser";
|
||||
import {ParseWorkflowResult, WorkflowTemplate, isBasicExpression, isString} from "@actions/workflow-parser";
|
||||
import {ErrorPolicy} from "@actions/workflow-parser/model/convert";
|
||||
import {splitAllowedContext} from "@actions/workflow-parser/templates/allowed-context";
|
||||
import {BasicExpressionToken} from "@actions/workflow-parser/templates/tokens/basic-expression-token";
|
||||
@@ -13,9 +13,10 @@ import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {Diagnostic, DiagnosticSeverity, URI} from "vscode-languageserver-types";
|
||||
import {ActionMetadata, ActionReference} from "./action";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {getContext, Mode} from "./context-providers/default";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
||||
import {AccessError, wrapDictionary} from "./expression-validation/error-dictionary";
|
||||
import {Mode, getContext} from "./context-providers/default";
|
||||
import {WorkflowContext, getWorkflowContext} from "./context/workflow-context";
|
||||
import {wrapDictionary} from "./expression-validation/error-dictionary";
|
||||
import {ValidationEvaluator} from "./expression-validation/evaluator";
|
||||
import {validatorFunctions} from "./expression-validation/functions";
|
||||
import {error} from "./log";
|
||||
import {findToken} from "./utils/find-token";
|
||||
@@ -202,28 +203,17 @@ async function validateExpression(
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const context = await getContext(namedContexts, contextProviderConfig, workflowContext, Mode.Validation);
|
||||
const context = await getContext(namedContexts, contextProviderConfig, workflowContext, Mode.Validation);
|
||||
|
||||
const e = new Evaluator(expr, wrapDictionary(context), validatorFunctions);
|
||||
e.evaluate();
|
||||
const e = new ValidationEvaluator(expr, wrapDictionary(context), validatorFunctions);
|
||||
e.validate();
|
||||
|
||||
// Any invalid context access would've thrown an error via the `ErrorDictionary`, for now we don't have to check the actual
|
||||
// result of the evaluation.
|
||||
} catch (e) {
|
||||
if (e instanceof AccessError) {
|
||||
diagnostics.push({
|
||||
message: `Context access might be invalid: ${e.keyName}`,
|
||||
severity: DiagnosticSeverity.Warning,
|
||||
range: mapRange(expression.range)
|
||||
});
|
||||
} else if (e instanceof ExpressionEvaluationError) {
|
||||
diagnostics.push({
|
||||
message: `Expression might be invalid: ${e.message}`,
|
||||
severity: DiagnosticSeverity.Error,
|
||||
range: mapRange(expression.range)
|
||||
});
|
||||
}
|
||||
}
|
||||
diagnostics.push(
|
||||
...e.errors.map(e => ({
|
||||
message: e.message,
|
||||
range: mapRange(expression.range),
|
||||
severity: e.severity === "error" ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,10 @@ function mappingValues(
|
||||
// No special insertText in this case
|
||||
break;
|
||||
|
||||
case DefinitionType.String:
|
||||
case DefinitionType.Boolean:
|
||||
insertText = `\n${indentation}${key}: `;
|
||||
break;
|
||||
default:
|
||||
insertText = `${key}: `;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.3.1"
|
||||
"version": "0.3.4"
|
||||
}
|
||||
|
||||
Generated
+13
-12
@@ -135,7 +135,7 @@
|
||||
},
|
||||
"expressions": {
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.0.3",
|
||||
@@ -395,11 +395,11 @@
|
||||
},
|
||||
"languageserver": {
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.1",
|
||||
"@actions/workflow-parser": "^0.3.1",
|
||||
"@actions/languageservice": "^0.3.4",
|
||||
"@actions/workflow-parser": "^0.3.4",
|
||||
"@octokit/rest": "^19.0.7",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
@@ -678,11 +678,11 @@
|
||||
},
|
||||
"languageservice": {
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.1",
|
||||
"@actions/workflow-parser": "^0.3.1",
|
||||
"@actions/expressions": "^0.3.4",
|
||||
"@actions/workflow-parser": "^0.3.4",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.7",
|
||||
@@ -6652,9 +6652,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
@@ -11719,10 +11720,10 @@
|
||||
},
|
||||
"workflow-parser": {
|
||||
"name": "@actions/workflow-parser",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.1",
|
||||
"@actions/expressions": "^0.3.4",
|
||||
"cronstrue": "^2.21.0",
|
||||
"yaml": "^2.0.0-8"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/workflow-parser",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.4",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
@@ -43,7 +43,7 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.1",
|
||||
"@actions/expressions": "^0.3.4",
|
||||
"cronstrue": "^2.21.0",
|
||||
"yaml": "^2.0.0-8"
|
||||
},
|
||||
|
||||
@@ -59,17 +59,24 @@ export function convertOn(context: TemplateContext, token: TemplateToken): Event
|
||||
|
||||
// All other events are defined as mappings. During schema validation we already ensure that events
|
||||
// receive only known keys, so here we can focus on the values and whether they are valid.
|
||||
|
||||
const eventToken = item.value.assertMapping(`event ${eventName}`);
|
||||
if (eventName === "workflow_call") {
|
||||
result.workflow_call = convertEventWorkflowCall(context, eventToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eventName === "workflow_dispatch") {
|
||||
result.workflow_dispatch = convertEventWorkflowDispatchInputs(context, eventToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
result[eventName] = {
|
||||
...convertPatternFilter("branches", eventToken),
|
||||
...convertPatternFilter("tags", eventToken),
|
||||
...convertPatternFilter("paths", eventToken),
|
||||
...convertFilter("types", eventToken),
|
||||
...convertFilter("workflows", eventToken),
|
||||
// workflow_call and workflow_dispatch share input parsing
|
||||
...convertEventWorkflowDispatchInputs(context, eventToken),
|
||||
...convertEventWorkflowCall(context, eventToken)
|
||||
...convertFilter("workflows", eventToken)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ function convertStep(context: TemplateContext, idBuilder: IdBuilder, step: Templ
|
||||
let id: StringToken | undefined;
|
||||
let name: ScalarToken | undefined;
|
||||
let uses: StringToken | undefined;
|
||||
let continueOnError: boolean | undefined;
|
||||
let continueOnError: boolean | ScalarToken | undefined;
|
||||
let env: MappingToken | undefined;
|
||||
const ifCondition = new BasicExpressionToken(undefined, undefined, "success()", undefined, undefined, undefined);
|
||||
for (const item of mapping) {
|
||||
@@ -78,7 +78,11 @@ function convertStep(context: TemplateContext, idBuilder: IdBuilder, step: Templ
|
||||
env = item.value.assertMapping("step env");
|
||||
break;
|
||||
case "continue-on-error":
|
||||
continueOnError = item.value.assertBoolean("steps item continue-on-error").value;
|
||||
if (!item.value.isExpression) {
|
||||
continueOnError = item.value.assertBoolean("steps item continue-on-error").value;
|
||||
} else {
|
||||
continueOnError = item.value.assertScalar("steps item continue-on-error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {TemplateContext} from "../../templates/template-context";
|
||||
import {MappingToken, TemplateToken} from "../../templates/tokens";
|
||||
import {isMapping} from "../../templates/tokens/type-guards";
|
||||
import {SecretConfig, WorkflowCallConfig} from "../workflow-template";
|
||||
import {SecretConfig, WorkflowCallConfig, InputConfig, InputType} from "../workflow-template";
|
||||
import {convertStringList} from "./string-list";
|
||||
import {ScalarToken} from "../../templates/tokens/scalar-token";
|
||||
|
||||
export function convertEventWorkflowCall(context: TemplateContext, token: MappingToken): WorkflowCallConfig {
|
||||
const result: WorkflowCallConfig = {};
|
||||
@@ -11,7 +13,7 @@ export function convertEventWorkflowCall(context: TemplateContext, token: Mappin
|
||||
|
||||
switch (key.value) {
|
||||
case "inputs":
|
||||
// Ignore, these are handled by convertEventWorkflowDispatchInputs
|
||||
result.inputs = convertWorkflowInputs(context, item.value.assertMapping("workflow dispatch inputs"));
|
||||
break;
|
||||
|
||||
case "secrets":
|
||||
@@ -27,6 +29,94 @@ export function convertEventWorkflowCall(context: TemplateContext, token: Mappin
|
||||
return result;
|
||||
}
|
||||
|
||||
export function convertWorkflowInputs(
|
||||
context: TemplateContext,
|
||||
token: MappingToken
|
||||
): {
|
||||
[inputName: string]: InputConfig;
|
||||
} {
|
||||
const result: {[inputName: string]: InputConfig} = {};
|
||||
|
||||
for (const item of token) {
|
||||
const inputName = item.key.assertString("input name");
|
||||
const inputMapping = item.value.assertMapping("input configuration");
|
||||
|
||||
result[inputName.value] = convertWorkflowInput(context, inputMapping);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function convertWorkflowInput(context: TemplateContext, token: MappingToken): InputConfig {
|
||||
const result: InputConfig = {
|
||||
type: InputType.string // Default to string
|
||||
};
|
||||
|
||||
let defaultValue: undefined | ScalarToken;
|
||||
|
||||
for (const item of token) {
|
||||
const key = item.key.assertString("workflow dispatch input key");
|
||||
|
||||
switch (key.value) {
|
||||
case "description":
|
||||
result.description = item.value.assertString("input description").value;
|
||||
break;
|
||||
|
||||
case "required":
|
||||
result.required = item.value.assertBoolean("input required").value;
|
||||
break;
|
||||
|
||||
case "default":
|
||||
defaultValue = item.value.assertScalar("input default");
|
||||
break;
|
||||
|
||||
case "type":
|
||||
result.type = InputType[item.value.assertString("input type").value as keyof typeof InputType];
|
||||
break;
|
||||
|
||||
case "options":
|
||||
result.options = convertStringList("input options", item.value.assertSequence("input options"));
|
||||
break;
|
||||
|
||||
default:
|
||||
context.error(item.key, `Invalid key '${key.value}'`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate default value
|
||||
if (defaultValue !== undefined && !defaultValue.isExpression) {
|
||||
try {
|
||||
switch (result.type) {
|
||||
case InputType.boolean:
|
||||
result.default = defaultValue.assertBoolean("input default").value;
|
||||
|
||||
break;
|
||||
|
||||
case InputType.string:
|
||||
case InputType.choice:
|
||||
case InputType.environment:
|
||||
result.default = defaultValue.assertString("input default").value;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
context.error(defaultValue, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate `options` for `choice` type
|
||||
if (result.type === InputType.choice) {
|
||||
if (result.options === undefined || result.options.length === 0) {
|
||||
context.error(token, "Missing 'options' for choice input");
|
||||
}
|
||||
} else {
|
||||
if (result.options !== undefined) {
|
||||
context.error(token, "Input type is not 'choice', but 'options' is defined");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function convertWorkflowCallSecrets(
|
||||
context: TemplateContext,
|
||||
token: MappingToken
|
||||
|
||||
@@ -86,7 +86,7 @@ type BaseStep = {
|
||||
id: string;
|
||||
name?: ScalarToken;
|
||||
if: BasicExpressionToken;
|
||||
"continue-on-error"?: boolean;
|
||||
"continue-on-error"?: boolean | ScalarToken;
|
||||
env?: MappingToken;
|
||||
};
|
||||
|
||||
@@ -158,7 +158,7 @@ export type WorkflowDispatchConfig = {
|
||||
};
|
||||
|
||||
export type WorkflowCallConfig = {
|
||||
inputs?: {[inputName: string]: InputConfig};
|
||||
inputs?: {[inputName: string]: InputConfig & {default?: string | boolean | number | ScalarToken}};
|
||||
secrets?: {[secretName: string]: SecretConfig};
|
||||
// TODO - these are supported in C# and Go but not in TS yet
|
||||
// outputs: { [outputName: string]: OutputConfig }
|
||||
|
||||
@@ -576,7 +576,9 @@
|
||||
"merge-group-mapping": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"types": "merge-group-activity"
|
||||
"types": "merge-group-activity",
|
||||
"branches": "event-branches",
|
||||
"branches-ignore": "event-branches-ignore"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+14
-1
@@ -72,7 +72,13 @@ on:
|
||||
- edited
|
||||
- deleted
|
||||
merge_group:
|
||||
types: checks_requested
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
branches-ignore:
|
||||
- develop
|
||||
types:
|
||||
- checks_requested
|
||||
milestone:
|
||||
types:
|
||||
- created
|
||||
@@ -313,6 +319,13 @@ jobs:
|
||||
]
|
||||
},
|
||||
"merge_group": {
|
||||
"branches": [
|
||||
"master",
|
||||
"main"
|
||||
],
|
||||
"branches-ignore": [
|
||||
"develop"
|
||||
],
|
||||
"types": [
|
||||
"checks_requested"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user