Compare commits

..

24 Commits

Author SHA1 Message Date
Jonathan Tamsut 6513b0d15d Merge pull request #55 from actions/release/0.3.7
Release version 0.3.7
2023-08-04 12:55:35 -07:00
GitHub Actions 207cfa12c0 Release extension version 0.3.7 2023-08-04 19:54:21 +00:00
Jonathan Tamsut 28ab3928fd Merge pull request #52 from actions/fix-indentation-completion
Correctly indent completion options
2023-08-04 12:20:50 -07:00
Jonathan Tamsut afbe42bffe fix test failures 2023-08-04 12:13:21 -07:00
Christopher Schleiden a324b8b9dc small tweaks 2023-08-04 11:32:10 -07:00
Christopher Schleiden 4f7d03ed0c Correctly indent completion options 2023-08-04 11:19:42 -07:00
Crystal Tenn 4ddbbc9db7 Merge pull request #28 from actions/dependabot/npm_and_yarn/yaml-2.2.2
Bump yaml from 2.1.3 to 2.2.2
2023-07-12 17:23:42 -04:00
Crystal Tenn 3ea2cf1829 Merge branch 'main' into dependabot/npm_and_yarn/yaml-2.2.2 2023-07-12 16:48:53 -04:00
Crystal Tenn 2c30f2f45f Merge pull request #47 from muzimuzhi/typo
Fix typos in workflow schema
2023-07-12 16:48:25 -04:00
Yukai Chou cf2d9cd0b9 Fix typos in workflow schema 2023-07-07 16:25:08 +08:00
Felipe Suero 8f2f59092e Merge pull request #46 from actions/release/0.3.6
Release version 0.3.6
2023-06-14 14:57:51 -04:00
GitHub Actions 5de89b0f8e Release extension version 0.3.6 2023-06-14 17:21:25 +00:00
Felipe Suero f4afa48ea4 Merge pull request #45 from actions/lerna-config-p2
Lerna config p2
2023-06-14 13:13:13 -04:00
Felipe Suero af5dd4b91e update gitignore 2023-06-14 12:43:13 -04:00
Felipe Suero 12d28370dc Explicitly state packages to upgrade 2023-06-14 12:42:49 -04:00
Felipe Suero 833b6fcac5 Explicitly state packages to upgrade 2023-06-14 12:42:36 -04:00
Felipe Suero cd7fabeb7f Merge pull request #44 from actions/felipesu19-patch-1
Update lerna.json
2023-06-14 12:03:35 -04:00
Felipe Suero 26da52bdf8 Update lerna.json
The current version of lerna doesn't use useWorkspaces anymore, defaulting to using the workspace config if one exists: 

```
ECONFIGWORKSPACES The "useWorkspaces" option has been removed. By default lerna will resolve your packages using your package manager's workspaces configuration. Alternatively, you can manually provide a list of package globs to be used instead via the "packages" option in lerna.json.
```
2023-06-14 11:28:36 -04:00
Felipe Suero 31aa95fb10 Merge pull request #43 from Olfi01/main
[languageserver] Enable support for GitHub Enterprise Server
2023-06-13 10:16:54 -04:00
Olfi01 b912482163 Apply suggestions from code review
Changed parameter naming to match general pattern

Co-authored-by: Christopher Schleiden <cschleiden@live.de>
2023-05-25 00:57:30 +02:00
flmeyer 41436c6570 Use correct RequestError class 2023-05-19 16:34:58 +02:00
flmeyer 468b68840b Add try-catch to avoid failing requests
On GHES servers below version 3.8, the variables context is unavailable, resulting in 404 errors when calling the corresponding endpoint.
2023-05-12 22:42:09 +02:00
flmeyer 57a77551b0 Enable support for GitHub Enterprise Server 2023-05-08 17:00:05 +02:00
dependabot[bot] a34a500176 Bump yaml from 2.1.3 to 2.2.2
Bumps [yaml](https://github.com/eemeli/yaml) from 2.1.3 to 2.2.2.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.1.3...v2.2.2)

---
updated-dependencies:
- dependency-name: yaml
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-05 17:48:24 +00:00
15 changed files with 173 additions and 86 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
*/node_modules
*/dist
lerna-debug.log
node_modules
.DS_Store
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/expressions",
"version": "0.3.5",
"version": "0.3.7",
"license": "MIT",
"type": "module",
"source": "./src/index.ts",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/languageserver",
"version": "0.3.5",
"version": "0.3.7",
"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.5",
"@actions/workflow-parser": "^0.3.5",
"@actions/languageservice": "^0.3.7",
"@actions/workflow-parser": "^0.3.7",
"@octokit/rest": "^19.0.7",
"@octokit/types": "^9.0.0",
"vscode-languageserver": "^8.0.2",
+3 -2
View File
@@ -1,8 +1,9 @@
import {Octokit} from "@octokit/rest";
export function getClient(token: string, userAgent?: string): Octokit {
export function getClient(token: string, userAgent?: string, apiUrl?: string): Octokit {
return new Octokit({
auth: token,
userAgent: userAgent || `GitHub Actions Language Server`
userAgent: userAgent || `GitHub Actions Language Server`,
baseUrl: apiUrl
});
}
+1 -1
View File
@@ -51,7 +51,7 @@ export function initConnection(connection: Connection) {
const options = params.initializationOptions as InitializationOptions;
if (options.sessionToken) {
client = getClient(options.sessionToken, options.userAgent);
client = getClient(options.sessionToken, options.userAgent, options.gitHubApiUrl);
}
if (options.repos) {
@@ -2,9 +2,10 @@ import {data, DescriptionDictionary} from "@actions/expressions";
import {Pair} from "@actions/expressions/data/expressiondata";
import {StringData} from "@actions/expressions/data/index";
import {WorkflowContext} from "@actions/languageservice/context/workflow-context";
import {warn} from "@actions/languageservice/log";
import {log, warn} from "@actions/languageservice/log";
import {isMapping, isString} from "@actions/workflow-parser";
import {Octokit} from "@octokit/rest";
import {RequestError} from "@octokit/request-error";
import {RepositoryContext} from "../initializationOptions";
import {TTLCache} from "../utils/cache";
@@ -42,50 +43,58 @@ export async function getVariables(
}
const variablesContext = defaultContext || new DescriptionDictionary();
const variables = await getRemoteVariables(octokit, cache, repo, environmentName);
try {
const variables = await getRemoteVariables(octokit, cache, repo, environmentName);
// Build combined map of variables
const variablesMap = new Map<
string,
{
key: string;
value: data.StringData;
description?: string;
}
>();
// Build combined map of variables
const variablesMap = new Map<
string,
{
key: string;
value: data.StringData;
description?: string;
}
>();
variables.organizationVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), {
key: variable.key,
value: new data.StringData(variable.value.coerceString()),
description: `${variable.value.coerceString()} - Organization variable`
})
);
variables.organizationVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), {
key: variable.key,
value: new data.StringData(variable.value.coerceString()),
description: `${variable.value.coerceString()} - Organization variable`
})
);
// Override org variables with repo variables
variables.repoVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), {
key: variable.key,
value: new data.StringData(variable.value.coerceString()),
description: `${variable.value.coerceString()} - Repository variable`
})
);
// Override org variables with repo variables
variables.repoVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), {
key: variable.key,
value: new data.StringData(variable.value.coerceString()),
description: `${variable.value.coerceString()} - Repository variable`
})
);
// Override repo variables with environment veriables (if defined)
variables.environmentVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), {
key: variable.key,
value: new data.StringData(variable.value.coerceString()),
description: `${variable.value.coerceString()} - Variable for environment \`${environmentName || ""}\``
})
);
// Override repo variables with environment veriables (if defined)
variables.environmentVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), {
key: variable.key,
value: new data.StringData(variable.value.coerceString()),
description: `${variable.value.coerceString()} - Variable for environment \`${environmentName || ""}\``
})
);
// Sort variables by key and add to context
Array.from(variablesMap.values())
.sort((a, b) => a.key.localeCompare(b.key))
.forEach(variable => variablesContext?.add(variable.key, variable.value, variable.description));
// Sort variables by key and add to context
Array.from(variablesMap.values())
.sort((a, b) => a.key.localeCompare(b.key))
.forEach(variable => variablesContext?.add(variable.key, variable.value, variable.description));
return variablesContext;
return variablesContext;
} catch (e) {
if (!(e instanceof RequestError)) throw e;
if (e.name == "HttpError" && e.status == 404) {
log("Failure to request variables. Ignore if you're using GitHub Enterprise Server below version 3.8");
return variablesContext;
} else throw e;
}
}
export async function getRemoteVariables(
@@ -23,6 +23,11 @@ export interface InitializationOptions {
* Desired log level
*/
logLevel?: LogLevel;
/**
* If a GitHub Enterprise Server should be used, the URL of the API endpoint, eg "https://ghe.my-company.com/api/v3"
*/
gitHubApiUrl?: string;
}
export interface RepositoryContext {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/languageservice",
"version": "0.3.5",
"version": "0.3.7",
"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.5",
"@actions/workflow-parser": "^0.3.5",
"@actions/expressions": "^0.3.7",
"@actions/workflow-parser": "^0.3.7",
"vscode-languageserver-textdocument": "^1.0.7",
"vscode-languageserver-types": "^3.17.2",
"vscode-uri": "^3.0.7",
+38 -6
View File
@@ -4,8 +4,8 @@ import {complete} from "./complete";
import {registerLogger} from "./log";
import {getPositionFromCursor} from "./test-utils/cursor-position";
import {TestLogger} from "./test-utils/logger";
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
import {clearCache} from "./utils/workflow-cache";
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
registerLogger(new TestLogger());
@@ -406,7 +406,7 @@ jobs:
expect(result.map(e => e.label)).toContain("runs-on");
const textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit;
expect(textEdit.newText).toEqual("runs-on");
expect(textEdit.newText).toEqual("runs-on: ");
expect(textEdit.range).toEqual({
start: {line: 3, character: 4},
end: {line: 3, character: 10}
@@ -421,7 +421,7 @@ jobs:
expect(result.map(e => e.label)).toContain("runs-on");
const textEdit = result.filter(e => e.label === "runs-on")[0].textEdit as TextEdit;
expect(textEdit.newText).toEqual("runs-on");
expect(textEdit.newText).toEqual("runs-on: ");
expect(textEdit.range).toEqual({
start: {line: 3, character: 4},
end: {line: 3, character: 4}
@@ -448,7 +448,7 @@ jobs:
]);
// One-of
expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency"]);
expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency: "]);
});
it("custom indentation", async () => {
@@ -471,11 +471,11 @@ jobs:
]);
// One-of
expect(result.filter(x => x.label === "concurrency").map(x => x.textEdit?.newText)).toEqual(["concurrency"]);
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 () => {
it("adds a new line and indentation for mapping keys when the key is given", async () => {
const input = "concurrency: |";
const result = await complete(...getPositionFromCursor(input));
@@ -485,4 +485,36 @@ jobs:
]);
expect(result.filter(x => x.label === "group").map(x => x.textEdit?.newText)).toEqual(["\n group: "]);
});
it("does not add new line if no key in line", async () => {
const input = "run-n|";
const result = await complete(...getPositionFromCursor(input));
expect(result.filter(x => x.label === "run-name").map(x => x.textEdit?.newText)).toEqual(["run-name: "]);
});
it("adds new line for nested mapping", async () => {
const input = "on:\n workflow_dispatch: in|";
const result = await complete(...getPositionFromCursor(input));
expect(result.filter(x => x.label === "inputs").map(x => x.textEdit?.newText)).toEqual(["\n inputs:\n "]);
});
it("adds : for one-of", async () => {
const input = "on:\n check_run:\n ty|";
const result = await complete(...getPositionFromCursor(input));
expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["types: "]);
});
it("does not add : for one-of in key mode", async () => {
const input = "on:\n check_run: ty|";
const result = await complete(...getPositionFromCursor(input));
expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["types"]);
});
});
+2 -2
View File
@@ -24,7 +24,7 @@ import {isPlaceholder, transform} from "./utils/transform";
import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache";
import {Value, ValueProviderConfig} from "./value-providers/config";
import {defaultValueProviders} from "./value-providers/default";
import {definitionValues} from "./value-providers/definition";
import {DefinitionValueMode, definitionValues} from "./value-providers/definition";
export function getExpressionInput(input: string, pos: number): string {
// Find start marker around the cursor position
@@ -180,7 +180,7 @@ async function getValues(
return [];
}
const values = definitionValues(def, indentation);
const values = definitionValues(def, indentation, keyToken ? DefinitionValueMode.Key : DefinitionValueMode.Parent);
return filterAndSortCompletionOptions(values, existingValues);
}
@@ -9,15 +9,30 @@ import {getWorkflowSchema} from "@actions/workflow-parser/workflows/workflow-sch
import {Value} from "./config";
import {stringsToValues} from "./strings-to-values";
export function definitionValues(def: Definition, indentation: string): Value[] {
export enum DefinitionValueMode {
/**
* We're getting completion options for a parent token
* foo:
* ba|
*/
Parent,
/**
* We're getting completion options for a key token. For example:
* foo: |
*/
Key
}
export function definitionValues(def: Definition, indentation: string, mode: DefinitionValueMode): Value[] {
const schema = getWorkflowSchema();
if (def instanceof MappingDefinition) {
return mappingValues(def, schema.definitions, indentation);
return mappingValues(def, schema.definitions, indentation, mode);
}
if (def instanceof OneOfDefinition) {
return oneOfValues(def, schema.definitions, indentation);
return oneOfValues(def, schema.definitions, indentation, mode);
}
if (def instanceof BooleanDefinition) {
@@ -36,7 +51,7 @@ export function definitionValues(def: Definition, indentation: string): Value[]
if (def instanceof SequenceDefinition) {
const itemDef = schema.getDefinition(def.itemType);
if (itemDef) {
return definitionValues(itemDef, indentation);
return definitionValues(itemDef, indentation, mode);
}
}
@@ -46,7 +61,8 @@ export function definitionValues(def: Definition, indentation: string): Value[]
function mappingValues(
mappingDefinition: MappingDefinition,
definitions: {[key: string]: Definition},
indentation: string
indentation: string,
mode: DefinitionValueMode
): Value[] {
const properties: Value[] = [];
for (const [key, value] of Object.entries(mappingDefinition.properties)) {
@@ -60,21 +76,38 @@ function mappingValues(
if (typeDef) {
switch (typeDef.definitionType) {
case DefinitionType.Sequence:
insertText = `${key}:\n${indentation}- `;
if (mode == DefinitionValueMode.Key) {
insertText = `\n${indentation}${key}:\n${indentation}${indentation}- `;
} else {
insertText = `${key}:\n${indentation}- `;
}
break;
case DefinitionType.Mapping:
insertText = `${key}:\n${indentation}`;
if (mode == DefinitionValueMode.Key) {
insertText = `\n${indentation}${key}:\n${indentation}${indentation}`;
} else {
insertText = `${key}:\n${indentation}`;
}
break;
case DefinitionType.OneOf:
// No special insertText in this case
if (mode == DefinitionValueMode.Parent) {
insertText = `${key}: `;
} else {
// No special insertText in this case
}
break;
case DefinitionType.String:
case DefinitionType.Boolean:
insertText = `\n${indentation}${key}: `;
if (mode == DefinitionValueMode.Key) {
insertText = `\n${indentation}${key}: `;
} else {
insertText = `${key}: `;
}
break;
default:
insertText = `${key}: `;
}
@@ -93,11 +126,12 @@ function mappingValues(
function oneOfValues(
oneOfDefinition: OneOfDefinition,
definitions: {[key: string]: Definition},
indentation: string
indentation: string,
mode: DefinitionValueMode
): Value[] {
const values: Value[] = [];
for (const key of oneOfDefinition.oneOf) {
values.push(...definitionValues(definitions[key], indentation));
values.push(...definitionValues(definitions[key], indentation, mode));
}
return distinctValues(values);
}
+7 -2
View File
@@ -1,5 +1,10 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useWorkspaces": true,
"version": "0.3.5"
"packages": [
"expressions",
"workflow-parser",
"languageservice",
"languageserver"
],
"version": "0.3.7"
}
+12 -11
View File
@@ -135,7 +135,7 @@
},
"expressions": {
"name": "@actions/expressions",
"version": "0.3.5",
"version": "0.3.7",
"license": "MIT",
"devDependencies": {
"@types/jest": "^29.0.3",
@@ -395,11 +395,11 @@
},
"languageserver": {
"name": "@actions/languageserver",
"version": "0.3.5",
"version": "0.3.7",
"license": "MIT",
"dependencies": {
"@actions/languageservice": "^0.3.5",
"@actions/workflow-parser": "^0.3.5",
"@actions/languageservice": "^0.3.7",
"@actions/workflow-parser": "^0.3.7",
"@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.5",
"version": "0.3.7",
"license": "MIT",
"dependencies": {
"@actions/expressions": "^0.3.5",
"@actions/workflow-parser": "^0.3.5",
"@actions/expressions": "^0.3.7",
"@actions/workflow-parser": "^0.3.7",
"vscode-languageserver-textdocument": "^1.0.7",
"vscode-languageserver-types": "^3.17.2",
"vscode-uri": "^3.0.7",
@@ -11668,8 +11668,9 @@
"license": "ISC"
},
"node_modules/yaml": {
"version": "2.1.3",
"license": "ISC",
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"engines": {
"node": ">= 14"
}
@@ -11720,10 +11721,10 @@
},
"workflow-parser": {
"name": "@actions/workflow-parser",
"version": "0.3.5",
"version": "0.3.7",
"license": "MIT",
"dependencies": {
"@actions/expressions": "^0.3.5",
"@actions/expressions": "^0.3.7",
"cronstrue": "^2.21.0",
"yaml": "^2.0.0-8"
},
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/workflow-parser",
"version": "0.3.5",
"version": "0.3.7",
"license": "MIT",
"type": "module",
"source": "./src/index.ts",
@@ -43,7 +43,7 @@
"watch": "tsc --build tsconfig.build.json --watch"
},
"dependencies": {
"@actions/expressions": "^0.3.5",
"@actions/expressions": "^0.3.7",
"cronstrue": "^2.21.0",
"yaml": "^2.0.0-8"
},
+2 -2
View File
@@ -1183,7 +1183,7 @@
]
},
"workflow-run-activity": {
"description": "The types of workflow run activity that trigger the workflow. Suupported activity types: `completed`, `requested`, `in_progress`.",
"description": "The types of workflow run activity that trigger the workflow. Supported activity types: `completed`, `requested`, `in_progress`.",
"one-of": [
"workflow-run-activity-type",
"workflow-run-activity-types"
@@ -2489,7 +2489,7 @@
"string": {
"require-non-empty": true
},
"description": "Use `shell` to override the default shell settings in the runner's operating system. You can use built-in shell keywords, or you can define a custom set of shell options. The shell command that is run internally executes a temporary file that contains the comands specified in `run`."
"description": "Use `shell` to override the default shell settings in the runner's operating system. You can use built-in shell keywords, or you can define a custom set of shell options. The shell command that is run internally executes a temporary file that contains the commands specified in `run`."
},
"working-directory": {
"string": {