Merge branch 'main' into joshmgross/workflow_call-validation

This commit is contained in:
Josh Gross
2023-02-01 10:44:24 -05:00
38 changed files with 510 additions and 344 deletions
+3 -2
View File
@@ -12,14 +12,15 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Use Node.js 16.x - name: Use Node.js 16.15
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16.x node-version: 16.15
cache: 'npm' cache: 'npm'
registry-url: 'https://npm.pkg.github.com' registry-url: 'https://npm.pkg.github.com'
- run: npm ci - run: npm ci
env: env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: npm run format-check -ws
- run: npm run build -ws - run: npm run build -ws
- run: npm test -ws - run: npm test -ws
+1
View File
@@ -3,3 +3,4 @@ dist
*.md *.md
*.js *.js
*.json *.json
*.d.ts
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@github/actions-expressions", "name": "@github/actions-expressions",
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"source": "./src/index.ts", "source": "./src/index.ts",
@@ -37,7 +37,7 @@
"watch": "tsc --build tsconfig.build.json --watch" "watch": "tsc --build tsconfig.build.json --watch"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
}, },
"files": [ "files": [
"dist/**/*" "dist/**/*"
@@ -45,7 +45,7 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.7.4" "typescript": "^4.7.4"
+6 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "@github/actions-languageserver", "name": "@github/actions-languageserver",
"version": "0.1.100", "version": "0.1.109",
"description": "Language server for GitHub Actions", "description": "Language server for GitHub Actions",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
@@ -38,15 +38,15 @@
"watch": "tsc --build tsconfig.build.json --watch" "watch": "tsc --build tsconfig.build.json --watch"
}, },
"dependencies": { "dependencies": {
"@github/actions-languageservice": "^0.1.100", "@github/actions-languageservice": "^0.1.109",
"@github/actions-workflow-parser": "^0.1.100", "@github/actions-workflow-parser": "^0.1.109",
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.7",
"vscode-languageserver": "^8.0.2", "vscode-languageserver": "^8.0.2",
"vscode-languageserver-textdocument": "^1.0.7", "vscode-languageserver-textdocument": "^1.0.7",
"yaml": "^2.1.3" "yaml": "^2.1.3"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
}, },
"files": [ "files": [
"dist/**/*" "dist/**/*"
@@ -55,7 +55,7 @@
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"fetch-mock": "^9.11.0", "fetch-mock": "^9.11.0",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
+8
View File
@@ -0,0 +1,8 @@
import {Octokit} from "@octokit/rest";
export function getClient(token: string, userAgent?: string): Octokit {
return new Octokit({
auth: token,
userAgent: userAgent || `GitHub Actions Language Server`
});
}
+16 -13
View File
@@ -16,19 +16,20 @@ import {
TextDocumentSyncKind TextDocumentSyncKind
} from "vscode-languageserver"; } from "vscode-languageserver";
import {TextDocument} from "vscode-languageserver-textdocument"; import {TextDocument} from "vscode-languageserver-textdocument";
import {getClient} from "./client";
import {Commands} from "./commands";
import {contextProviders} from "./context-providers"; import {contextProviders} from "./context-providers";
import {descriptionProvider} from "./description-provider";
import {InitializationOptions, RepositoryContext} from "./initializationOptions"; import {InitializationOptions, RepositoryContext} from "./initializationOptions";
import {onCompletion} from "./on-completion"; import {onCompletion} from "./on-completion";
import {TTLCache} from "./utils/cache"; import {TTLCache} from "./utils/cache";
import {valueProviders} from "./value-providers"; import {valueProviders} from "./value-providers";
import {getActionInputs} from "./value-providers/action-inputs"; import {getActionInputs} from "./value-providers/action-inputs";
import {Commands} from "./commands";
import {descriptionProvider} from "./description-provider";
export function initConnection(connection: Connection) { export function initConnection(connection: Connection) {
const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument); const documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
let sessionToken: string | undefined; let client: Octokit | undefined;
let repos: RepositoryContext[] = []; let repos: RepositoryContext[] = [];
const cache = new TTLCache(); const cache = new TTLCache();
@@ -49,7 +50,11 @@ export function initConnection(connection: Connection) {
); );
const options: InitializationOptions = params.initializationOptions; const options: InitializationOptions = params.initializationOptions;
sessionToken = options.sessionToken;
if (options.sessionToken) {
client = getClient(options.sessionToken, options.userAgent);
}
if (options.repos) { if (options.repos) {
repos = options.repos; repos = options.repos;
} }
@@ -93,15 +98,13 @@ export function initConnection(connection: Connection) {
const repoContext = repos.find(repo => textDocument.uri.startsWith(repo.workspaceUri)); const repoContext = repos.find(repo => textDocument.uri.startsWith(repo.workspaceUri));
const config: ValidationConfig = { const config: ValidationConfig = {
valueProviderConfig: valueProviders(sessionToken, repoContext, cache), valueProviderConfig: valueProviders(client, repoContext, cache),
contextProviderConfig: contextProviders(sessionToken, repoContext, cache), contextProviderConfig: contextProviders(client, repoContext, cache),
getActionInputs: async action => { getActionInputs: async action => {
if (sessionToken) { if (client) {
const octokit = new Octokit({ return await getActionInputs(client, cache, action);
auth: sessionToken
});
return await getActionInputs(octokit, cache, action);
} }
return undefined; return undefined;
} }
}; };
@@ -114,7 +117,7 @@ export function initConnection(connection: Connection) {
return await onCompletion( return await onCompletion(
position, position,
documents.get(textDocument.uri)!, documents.get(textDocument.uri)!,
sessionToken, client,
repos.find(repo => textDocument.uri.startsWith(repo.workspaceUri)), repos.find(repo => textDocument.uri.startsWith(repo.workspaceUri)),
cache cache
); );
@@ -122,7 +125,7 @@ export function initConnection(connection: Connection) {
connection.onHover(async ({position, textDocument}: HoverParams): Promise<Hover | null> => { connection.onHover(async ({position, textDocument}: HoverParams): Promise<Hover | null> => {
return hover(documents.get(textDocument.uri)!, position, { return hover(documents.get(textDocument.uri)!, position, {
descriptionProvider: descriptionProvider(sessionToken, cache) descriptionProvider: descriptionProvider(client, cache)
}); });
}); });
@@ -9,18 +9,14 @@ import {RepositoryContext} from "./initializationOptions";
import {TTLCache} from "./utils/cache"; import {TTLCache} from "./utils/cache";
export function contextProviders( export function contextProviders(
sessionToken: string | undefined, client: Octokit | undefined,
repo: RepositoryContext | undefined, repo: RepositoryContext | undefined,
cache: TTLCache cache: TTLCache
): ContextProviderConfig { ): ContextProviderConfig {
if (!repo || !sessionToken) { if (!repo || !client) {
return {getContext: (_: string) => Promise.resolve(undefined)}; return {getContext: (_: string) => Promise.resolve(undefined)};
} }
const octokit = new Octokit({
auth: sessionToken
});
const getContext = async ( const getContext = async (
name: string, name: string,
defaultContext: DescriptionDictionary | undefined, defaultContext: DescriptionDictionary | undefined,
@@ -28,11 +24,11 @@ export function contextProviders(
) => { ) => {
switch (name) { switch (name) {
case "secrets": case "secrets":
return await getSecrets(workflowContext, octokit, cache, repo, defaultContext); return await getSecrets(workflowContext, client, cache, repo, defaultContext);
case "vars": case "vars":
return await getVariables(workflowContext, octokit, cache, repo, defaultContext); return await getVariables(workflowContext, client, cache, repo, defaultContext);
case "steps": case "steps":
return await getStepsContext(octokit, cache, defaultContext, workflowContext); return await getStepsContext(client, cache, defaultContext, workflowContext);
} }
}; };
@@ -41,6 +41,15 @@ export async function getSecrets(
} }
>(); >();
secrets.orgSecrets.forEach(secret =>
secretsMap.set(secret.value.toLowerCase(), {
key: secret.value,
value: new data.StringData("***"),
description: "Organization secret"
})
);
// Override org secrets with repo secrets
secrets.repoSecrets.forEach(secret => secrets.repoSecrets.forEach(secret =>
secretsMap.set(secret.value.toLowerCase(), { secretsMap.set(secret.value.toLowerCase(), {
key: secret.value, key: secret.value,
@@ -76,6 +85,7 @@ async function getRemoteSecrets(
): Promise<{ ): Promise<{
repoSecrets: StringData[]; repoSecrets: StringData[];
environmentSecrets: StringData[]; environmentSecrets: StringData[];
orgSecrets: StringData[];
}> { }> {
return { return {
repoSecrets: await cache.get(`${repo.owner}/${repo.name}/secrets`, undefined, () => repoSecrets: await cache.get(`${repo.owner}/${repo.name}/secrets`, undefined, () =>
@@ -86,18 +96,22 @@ async function getRemoteSecrets(
(await cache.get(`${repo.owner}/${repo.name}/secrets/environment/${environmentName}`, undefined, () => (await cache.get(`${repo.owner}/${repo.name}/secrets/environment/${environmentName}`, undefined, () =>
fetchEnvironmentSecrets(octokit, repo.id, environmentName) fetchEnvironmentSecrets(octokit, repo.id, environmentName)
))) || ))) ||
[] [],
orgSecrets: await cache.get(`${repo.owner}/secrets`, undefined, () => fetchOrganizationSecrets(octokit, repo))
}; };
} }
async function fetchSecrets(octokit: Octokit, owner: string, name: string): Promise<StringData[]> { async function fetchSecrets(octokit: Octokit, owner: string, name: string): Promise<StringData[]> {
try { try {
const response = await octokit.actions.listRepoSecrets({ return await octokit.paginate(
owner, octokit.actions.listRepoSecrets,
repo: name {
}); owner,
repo: name,
return response.data.secrets.map(secret => new StringData(secret.name)); per_page: 100
},
response => response.data.map(secret => new StringData(secret.name))
);
} catch (e) { } catch (e) {
console.log("Failure to retrieve secrets: ", e); console.log("Failure to retrieve secrets: ", e);
} }
@@ -111,15 +125,37 @@ async function fetchEnvironmentSecrets(
environmentName: string environmentName: string
): Promise<StringData[]> { ): Promise<StringData[]> {
try { try {
const response = await octokit.actions.listEnvironmentSecrets({ return await octokit.paginate(
repository_id: repositoryId, octokit.actions.listEnvironmentSecrets,
environment_name: environmentName {
}); repository_id: repositoryId,
environment_name: environmentName,
return response.data.secrets.map(secret => new StringData(secret.name)); per_page: 100
},
response => response.data.map(secret => new StringData(secret.name))
);
} catch (e) { } catch (e) {
console.log("Failure to retrieve environment secrets: ", e); console.log("Failure to retrieve environment secrets: ", e);
} }
return []; return [];
} }
async function fetchOrganizationSecrets(octokit: Octokit, repo: RepositoryContext): Promise<StringData[]> {
if (!repo.organizationOwned) {
return [];
}
try {
const secrets: {name: string}[] = await octokit.paginate("GET /repos/{owner}/{repo}/actions/organization-secrets", {
owner: repo.owner,
repo: repo.name,
per_page: 100
});
return secrets.map(secret => new StringData(secret.name));
} catch (e) {
console.log("Failure to retrieve organization secrets: ", e);
}
return [];
}
@@ -42,6 +42,15 @@ export async function getVariables(
} }
>(); >();
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 => variables.repoVariables.forEach(variable =>
variablesMap.set(variable.key.toLowerCase(), { variablesMap.set(variable.key.toLowerCase(), {
key: variable.key, key: variable.key,
@@ -77,6 +86,7 @@ export async function getRemoteVariables(
): Promise<{ ): Promise<{
repoVariables: Pair[]; repoVariables: Pair[];
environmentVariables: Pair[]; environmentVariables: Pair[];
organizationVariables: Pair[];
}> { }> {
// Repo variables // Repo variables
return { return {
@@ -88,25 +98,27 @@ export async function getRemoteVariables(
(await cache.get(`${repo.owner}/${repo.name}/vars/environment/${environmentName}`, undefined, () => (await cache.get(`${repo.owner}/${repo.name}/vars/environment/${environmentName}`, undefined, () =>
fetchEnvironmentVariables(octokit, repo.id, environmentName) fetchEnvironmentVariables(octokit, repo.id, environmentName)
))) || ))) ||
[] [],
organizationVariables: await cache.get(`${repo.owner}/vars`, undefined, () =>
fetchOrganizationVariables(octokit, repo)
)
}; };
} }
async function fetchVariables(octokit: Octokit, owner: string, name: string): Promise<Pair[]> { async function fetchVariables(octokit: Octokit, owner: string, name: string): Promise<Pair[]> {
try { try {
const response = (await octokit.paginate("GET /repos/{owner}/{repo}/actions/variables{?per_page}", { return await octokit.paginate(
owner: owner, octokit.actions.listRepoVariables,
repo: name {
})) as { owner: owner,
name: string; repo: name,
value: string; per_page: 100
created_at: string; },
updated_at: string; response =>
}[]; response.data.map(variable => {
return {key: variable.name, value: new StringData(variable.value)};
return response.map(variable => { })
return {key: variable.name, value: new StringData(variable.value)}; );
});
} catch (e) { } catch (e) {
console.log("Failure to retrieve variables: ", e); console.log("Failure to retrieve variables: ", e);
} }
@@ -120,25 +132,45 @@ async function fetchEnvironmentVariables(
environmentName: string environmentName: string
): Promise<Pair[]> { ): Promise<Pair[]> {
try { try {
const response = (await octokit.paginate( return await octokit.paginate(
"GET /repositories/{repository_id}/environments/{environment_name}/variables{?per_page}", octokit.actions.listEnvironmentVariables,
{ {
repository_id: repositoryId, repository_id: repositoryId,
environment_name: environmentName environment_name: environmentName,
} per_page: 100
)) as { },
name: string; response =>
value: string; response.data.map(variable => {
created_at: string; return {key: variable.name, value: new StringData(variable.value)};
updated_at: string; })
}[]; );
return response.map(variable => {
return {key: variable.name, value: new StringData(variable.value)};
});
} catch (e) { } catch (e) {
console.log("Failure to retrieve environment variables: ", e); console.log("Failure to retrieve environment variables: ", e);
} }
return []; return [];
} }
async function fetchOrganizationVariables(octokit: Octokit, repo: RepositoryContext): Promise<Pair[]> {
if (!repo.organizationOwned) {
return [];
}
try {
const variables: {name: string; value: string}[] = await octokit.paginate(
"GET /repos/{owner}/{repo}/actions/organization-variables",
{
owner: repo.owner,
repo: repo.name,
per_page: 100
}
);
return variables.map(variable => {
return {key: variable.name, value: new StringData(variable.value)};
});
} catch (e) {
console.log("Failure to retrieve organization variables: ", e);
}
return [];
}
@@ -1,22 +1,17 @@
import {DescriptionProvider} from "@github/actions-languageservice/hover"; import {DescriptionProvider} from "@github/actions-languageservice/hover";
import {Octokit} from "@octokit/rest"; import {Octokit} from "@octokit/rest";
import {TTLCache} from "./utils/cache";
import {getActionInputDescription} from "./description-providers/action-input"; import {getActionInputDescription} from "./description-providers/action-input";
import {TTLCache} from "./utils/cache";
export function descriptionProvider(sessionToken: string | undefined, cache: TTLCache): DescriptionProvider { export function descriptionProvider(client: Octokit | undefined, cache: TTLCache): DescriptionProvider {
const octokit =
sessionToken &&
new Octokit({
auth: sessionToken
});
const getDescription: DescriptionProvider["getDescription"] = async (context, token, path) => { const getDescription: DescriptionProvider["getDescription"] = async (context, token, path) => {
if (!octokit) { if (!client) {
return undefined; return undefined;
} }
const parent = path[path.length - 1]; const parent = path[path.length - 1];
if (context.step && parent.definition?.key === "step-with") { if (context.step && parent.definition?.key === "step-with") {
return await getActionInputDescription(octokit, cache, context.step, token); return await getActionInputDescription(client, cache, context.step, token);
} }
}; };
@@ -9,6 +9,11 @@ export interface InitializationOptions {
*/ */
sessionToken?: string; sessionToken?: string;
/**
* Optional user agent to use when making calls to github.com
*/
userAgent?: string;
/** /**
* List of repositories that the language server should be aware of * List of repositories that the language server should be aware of
*/ */
@@ -31,6 +36,11 @@ export interface RepositoryContext {
*/ */
owner: string; owner: string;
/**
* Indicates if the repository is owned by an organization
*/
organizationOwned: boolean;
/** /**
* Repository name * Repository name
*/ */
+4 -3
View File
@@ -1,4 +1,5 @@
import {complete} from "@github/actions-languageservice/complete"; import {complete} from "@github/actions-languageservice/complete";
import {Octokit} from "@octokit/rest";
import {CompletionItem, Position} from "vscode-languageserver"; import {CompletionItem, Position} from "vscode-languageserver";
import {TextDocument} from "vscode-languageserver-textdocument"; import {TextDocument} from "vscode-languageserver-textdocument";
import {contextProviders} from "./context-providers"; import {contextProviders} from "./context-providers";
@@ -9,14 +10,14 @@ import {valueProviders} from "./value-providers";
export async function onCompletion( export async function onCompletion(
position: Position, position: Position,
document: TextDocument, document: TextDocument,
sessionToken: string | undefined, client: Octokit | undefined,
repoContext: RepositoryContext | undefined, repoContext: RepositoryContext | undefined,
cache: TTLCache cache: TTLCache
): Promise<CompletionItem[]> { ): Promise<CompletionItem[]> {
return await complete( return await complete(
document, document,
position, position,
repoContext && valueProviders(sessionToken, repoContext, cache), repoContext && valueProviders(client, repoContext, cache),
repoContext && contextProviders(sessionToken, repoContext, cache) repoContext && contextProviders(client, repoContext, cache)
); );
} }
+6 -10
View File
@@ -9,34 +9,30 @@ import {getEnvironments} from "./value-providers/job-environment";
import {getRunnerLabels} from "./value-providers/runs-on"; import {getRunnerLabels} from "./value-providers/runs-on";
export function valueProviders( export function valueProviders(
sessionToken: string | undefined, client: Octokit | undefined,
repo: RepositoryContext | undefined, repo: RepositoryContext | undefined,
cache: TTLCache cache: TTLCache
): ValueProviderConfig { ): ValueProviderConfig {
if (!repo || !sessionToken) { if (!repo || !client) {
return {}; return {};
} }
const octokit = new Octokit({
auth: sessionToken
});
return { return {
"job-environment": { "job-environment": {
kind: ValueProviderKind.AllowedValues, kind: ValueProviderKind.AllowedValues,
get: (_: WorkflowContext) => getEnvironments(octokit, cache, repo.owner, repo.name) get: (_: WorkflowContext) => getEnvironments(client, cache, repo.owner, repo.name)
}, },
"job-environment-name": { "job-environment-name": {
kind: ValueProviderKind.AllowedValues, kind: ValueProviderKind.AllowedValues,
get: (_: WorkflowContext) => getEnvironments(octokit, cache, repo.owner, repo.name) get: (_: WorkflowContext) => getEnvironments(client, cache, repo.owner, repo.name)
}, },
"runs-on": { "runs-on": {
kind: ValueProviderKind.SuggestedValues, kind: ValueProviderKind.SuggestedValues,
get: (_: WorkflowContext) => getRunnerLabels(octokit, cache, repo.owner, repo.name) get: (_: WorkflowContext) => getRunnerLabels(client, cache, repo.owner, repo.name)
}, },
"step-with": { "step-with": {
kind: ValueProviderKind.AllowedValues, kind: ValueProviderKind.AllowedValues,
get: (context: WorkflowContext) => getActionInputValues(octokit, cache, context) get: (context: WorkflowContext) => getActionInputValues(client, cache, context)
} }
}; };
} }
@@ -2,6 +2,8 @@ import {Value} from "@github/actions-languageservice/value-providers/config";
import {Octokit} from "@octokit/rest"; import {Octokit} from "@octokit/rest";
import {TTLCache} from "../utils/cache"; import {TTLCache} from "../utils/cache";
// Limitation: getRunnerLabels returns default hosted labels and labels for repository self-hosted runners.
// It doesn't return labels for organization runners visible to the repository.
export async function getRunnerLabels(client: Octokit, cache: TTLCache, owner: string, name: string): Promise<Value[]> { export async function getRunnerLabels(client: Octokit, cache: TTLCache, owner: string, name: string): Promise<Value[]> {
const defaultLabels = [ const defaultLabels = [
"ubuntu-latest", "ubuntu-latest",
@@ -33,14 +35,17 @@ export async function getRunnerLabels(client: Octokit, cache: TTLCache, owner: s
async function fetchRunnerLabels(client: Octokit, owner: string, name: string): Promise<Set<string>> { async function fetchRunnerLabels(client: Octokit, owner: string, name: string): Promise<Set<string>> {
const labels = new Set<string>(); const labels = new Set<string>();
try { try {
const response = await client.actions.listSelfHostedRunnersForRepo({ const itor = client.paginate.iterator(client.actions.listSelfHostedRunnersForRepo, {
owner, owner,
repo: name repo: name,
per_page: 100
}); });
for (const runner of response.data.runners) { for await (const response of itor) {
for (const label of runner.labels) { for (const runner of response.data) {
labels.add(label.name); for (const label of runner.labels) {
labels.add(label.name);
}
} }
} }
} catch (e) { } catch (e) {
+2 -1
View File
@@ -8,7 +8,8 @@
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true,
"moduleResolution": "node", "moduleResolution": "node",
"lib": ["es6", "webworker"] "lib": ["es6", "webworker"],
"resolveJsonModule": true
}, },
"watchOptions": { "watchOptions": {
"watchFile": "useFsEvents", "watchFile": "useFsEvents",
+5 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "@github/actions-languageservice", "name": "@github/actions-languageservice",
"version": "0.1.100", "version": "0.1.109",
"description": "Language service for GitHub Actions", "description": "Language service for GitHub Actions",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
@@ -38,14 +38,14 @@
"watch": "tsc --build tsconfig.build.json --watch" "watch": "tsc --build tsconfig.build.json --watch"
}, },
"dependencies": { "dependencies": {
"@github/actions-expressions": "^0.1.100", "@github/actions-expressions": "^0.1.109",
"@github/actions-workflow-parser": "^0.1.100", "@github/actions-workflow-parser": "^0.1.109",
"vscode-languageserver-textdocument": "^1.0.7", "vscode-languageserver-textdocument": "^1.0.7",
"vscode-languageserver-types": "^3.17.2", "vscode-languageserver-types": "^3.17.2",
"yaml": "^2.1.1" "yaml": "^2.1.1"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
}, },
"files": [ "files": [
"dist/**/*" "dist/**/*"
@@ -54,7 +54,7 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
@@ -255,6 +255,13 @@ jobs:
expect(result.map(x => x.label)).toEqual(["event"]); expect(result.map(x => x.label)).toEqual(["event"]);
}); });
it("auto-complete partial", async () => {
const input = "run-name: ${{ github.ev| }}";
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig);
expect(result.map(x => x.label)).toEqual(["event"]);
});
it("using default context provider", async () => { it("using default context provider", async () => {
const input = const input =
"on: push\njobs:\n build:\n runs-on: ubuntu-latest\n environment:\n url: ${{ runner.| }}\n steps:\n - run: echo"; "on: push\njobs:\n build:\n runs-on: ubuntu-latest\n environment:\n url: ${{ runner.| }}\n steps:\n - run: echo";
+62 -29
View File
@@ -1,6 +1,12 @@
import {complete as completeExpression} from "@github/actions-expressions"; import {DescriptionDictionary, complete as completeExpression} from "@github/actions-expressions";
import {CompletionItem as ExpressionCompletionItem} from "@github/actions-expressions/completion"; import {CompletionItem as ExpressionCompletionItem} from "@github/actions-expressions/completion";
import {convertWorkflowTemplate, isSequence, isString, parseWorkflow} from "@github/actions-workflow-parser"; import {
convertWorkflowTemplate,
isBasicExpression,
isSequence,
isString,
parseWorkflow
} from "@github/actions-workflow-parser";
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert"; import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
import {DefinitionType} from "@github/actions-workflow-parser/templates/schema/definition-type"; import {DefinitionType} from "@github/actions-workflow-parser/templates/schema/definition-type";
import {StringDefinition} from "@github/actions-workflow-parser/templates/schema/string-definition"; import {StringDefinition} from "@github/actions-workflow-parser/templates/schema/string-definition";
@@ -24,6 +30,7 @@ import {transform} from "./utils/transform";
import {Value, ValueProviderConfig} from "./value-providers/config"; import {Value, ValueProviderConfig} from "./value-providers/config";
import {defaultValueProviders} from "./value-providers/default"; import {defaultValueProviders} from "./value-providers/default";
import {definitionValues} from "./value-providers/definition"; import {definitionValues} from "./value-providers/definition";
import {TokenRange} from "@github/actions-workflow-parser/templates/tokens/token-range";
export function getExpressionInput(input: string, pos: number): string { export function getExpressionInput(input: string, pos: number): string {
// Find start marker around the cursor position // Find start marker around the cursor position
@@ -71,37 +78,14 @@ export async function complete(
// If we are inside an expression, take a different code-path. The workflow parser does not correctly create // If we are inside an expression, take a different code-path. The workflow parser does not correctly create
// expression nodes for invalid expressions and during editing expressions are invalid most of the time. // expression nodes for invalid expressions and during editing expressions are invalid most of the time.
if (token) { if (token) {
const isExpression = const isStringExpressionToken = isStringExpression(token);
token.definition?.definitionType === DefinitionType.String && (token.definition as StringDefinition).isExpression; const isBasicExpressionToken = isBasicExpression(token) && token.isExpression;
const containsExpression = isString(token) && token.value.indexOf(OPEN_EXPRESSION) >= 0;
if (isString(token) && (isExpression || containsExpression)) {
const currentInput = token.source || token.value;
// Transform the overall position into a node relative position
let relCharPos: number = 0;
const range = mapRange(token.range!);
if (range.start.line !== range.end.line) {
const lines = currentInput.split("\n");
const lineDiff = newPos.line - range.start.line - 1;
const linesBeforeCusor = lines.slice(0, lineDiff);
relCharPos = linesBeforeCusor.join("\n").length + newPos.character + 1;
} else {
relCharPos = newPos.character - range.start.character;
}
const expressionInput = (getExpressionInput(currentInput, relCharPos) || "").trim();
if (isStringExpressionToken || isBasicExpressionToken) {
const allowedContext = token.definitionInfo?.allowedContext || []; const allowedContext = token.definitionInfo?.allowedContext || [];
const context = await getContext(allowedContext, contextProviderConfig, workflowContext, Mode.Completion); const context = await getContext(allowedContext, contextProviderConfig, workflowContext, Mode.Completion);
try { return getExpressionCompletionItems(token, context, newPos);
return completeExpression(expressionInput, context, [], validatorFunctions).map(item =>
mapExpressionCompletionItem(item, currentInput[relCharPos])
);
} catch (e: any) {
error(`Error while completing expression: '${e?.message || "<no details>"}'`);
return [];
}
} }
} }
@@ -216,6 +200,35 @@ export function getExistingValues(token: TemplateToken | null, parent: TemplateT
} }
} }
function getExpressionCompletionItems(
token: TemplateToken,
context: DescriptionDictionary,
pos: Position
): CompletionItem[] {
let expressionInput = "";
let currentInput = "";
let relCharPos: number = 0;
if (isBasicExpression(token)) {
expressionInput = currentInput = token.expression;
relCharPos = getRelCharPos(token.range!, expressionInput, pos);
} else {
const stringToken = token.assertString("Expected string token for expression completion");
currentInput = stringToken.source || stringToken.value;
relCharPos = getRelCharPos(stringToken.range!, currentInput, pos);
expressionInput = (getExpressionInput(currentInput, relCharPos) || "").trim();
}
try {
return completeExpression(expressionInput, context, [], validatorFunctions).map(item =>
mapExpressionCompletionItem(item, currentInput[relCharPos])
);
} catch (e: any) {
error(`Error while completing expression: '${e?.message || "<no details>"}'`);
return [];
}
}
function filterAndSortCompletionOptions(options: Value[], existingValues?: Set<string>) { function filterAndSortCompletionOptions(options: Value[], existingValues?: Set<string>) {
options = options.filter(x => !existingValues?.has(x.label)); options = options.filter(x => !existingValues?.has(x.label));
options.sort((a, b) => a.label.localeCompare(b.label)); options.sort((a, b) => a.label.localeCompare(b.label));
@@ -239,3 +252,23 @@ function mapExpressionCompletionItem(item: ExpressionCompletionItem, charAfterPo
kind: item.function ? CompletionItemKind.Function : CompletionItemKind.Variable kind: item.function ? CompletionItemKind.Function : CompletionItemKind.Variable
}; };
} }
function getRelCharPos(tokenRange: TokenRange, currentInput: string, pos: Position): number {
// Transform the overall position into a node relative position
const range = mapRange(tokenRange);
if (range.start.line !== range.end.line) {
const lines = currentInput.split("\n");
const lineDiff = pos.line - range.start.line - 1;
const linesBeforeCusor = lines.slice(0, lineDiff);
return linesBeforeCusor.join("\n").length + pos.character + 1;
} else {
return pos.character - range.start.character;
}
}
function isStringExpression(token: TemplateToken): boolean {
const isExpression =
token.definition?.definitionType === DefinitionType.String && (token.definition as StringDefinition).isExpression;
const containsExpression = isString(token) && token.value.indexOf(OPEN_EXPRESSION) >= 0;
return isExpression || containsExpression;
}
@@ -1,4 +1,4 @@
import descriptions from "./descriptions.json"; import descriptions from "./descriptions.json" assert {type: "json"};
export const RootContext = "root"; export const RootContext = "root";
@@ -1,55 +1,55 @@
import check_run from "./check_run.json"; import check_run from "./check_run.json" assert {type: "json"};
import check_suite from "./check_suite.json"; import check_suite from "./check_suite.json" assert {type: "json"};
import commit_comment from "./commit_comment.json"; import commit_comment from "./commit_comment.json" assert {type: "json"};
import content_reference from "./content_reference.json"; import content_reference from "./content_reference.json" assert {type: "json"};
import create from "./create.json"; import create from "./create.json" assert {type: "json"};
import deletePayload from "./delete.json"; import deletePayload from "./delete.json" assert {type: "json"};
import deploy_key from "./deploy_key.json"; import deploy_key from "./deploy_key.json" assert {type: "json"};
import deployment from "./deployment.json"; import deployment from "./deployment.json" assert {type: "json"};
import deployment_status from "./deployment_status.json"; import deployment_status from "./deployment_status.json" assert {type: "json"};
import fork from "./fork.json"; import fork from "./fork.json" assert {type: "json"};
import github_app_authorization from "./github_app_authorization.json"; import github_app_authorization from "./github_app_authorization.json" assert {type: "json"};
import gollum from "./gollum.json"; import gollum from "./gollum.json" assert {type: "json"};
import installation from "./installation.json"; import installation from "./installation.json" assert {type: "json"};
import installation_repositories from "./installation_repositories.json"; import installation_repositories from "./installation_repositories.json" assert {type: "json"};
import issue_comment from "./issue_comment.json"; import issue_comment from "./issue_comment.json" assert {type: "json"};
import issues from "./issues.json"; import issues from "./issues.json" assert {type: "json"};
import label from "./label.json"; import label from "./label.json" assert {type: "json"};
import marketplace_purchase from "./marketplace_purchase.json"; import marketplace_purchase from "./marketplace_purchase.json" assert {type: "json"};
import member from "./member.json"; import member from "./member.json" assert {type: "json"};
import membership from "./membership.json"; import membership from "./membership.json" assert {type: "json"};
import merge_group from "./merge_group.json"; import merge_group from "./merge_group.json" assert {type: "json"};
import meta from "./meta.json"; import meta from "./meta.json" assert {type: "json"};
import milestone from "./milestone.json"; import milestone from "./milestone.json" assert {type: "json"};
import org_block from "./org_block.json"; import org_block from "./org_block.json" assert {type: "json"};
import organization from "./organization.json"; import organization from "./organization.json" assert {type: "json"};
import packagePayload from "./package_payload.json"; import packagePayload from "./package_payload.json" assert {type: "json"};
import page_build from "./page_build.json"; import page_build from "./page_build.json" assert {type: "json"};
import ping from "./ping.json"; import ping from "./ping.json" assert {type: "json"};
import project from "./project.json"; import project from "./project.json" assert {type: "json"};
import project_card from "./project_card.json"; import project_card from "./project_card.json" assert {type: "json"};
import project_column from "./project_column.json"; import project_column from "./project_column.json" assert {type: "json"};
import publicPayload from "./public.json"; import publicPayload from "./public.json" assert {type: "json"};
import pull_request from "./pull_request.json"; import pull_request from "./pull_request.json" assert {type: "json"};
import pull_request_review from "./pull_request_review.json"; import pull_request_review from "./pull_request_review.json" assert {type: "json"};
import pull_request_review_comment from "./pull_request_review_comment.json"; import pull_request_review_comment from "./pull_request_review_comment.json" assert {type: "json"};
import push from "./push.json"; import push from "./push.json" assert {type: "json"};
import release from "./release.json"; import release from "./release.json" assert {type: "json"};
import repository from "./repository.json"; import repository from "./repository.json" assert {type: "json"};
import repository_dispatch from "./repository_dispatch.json"; import repository_dispatch from "./repository_dispatch.json" assert {type: "json"};
import repository_import from "./repository_import.json"; import repository_import from "./repository_import.json" assert {type: "json"};
import repository_vulnerability_alert from "./repository_vulnerability_alert.json"; import repository_vulnerability_alert from "./repository_vulnerability_alert.json" assert {type: "json"};
import schedule from "./schedule.json"; import schedule from "./schedule.json" assert {type: "json"};
import security_advisory from "./security_advisory.json"; import security_advisory from "./security_advisory.json" assert {type: "json"};
import sponsorship from "./sponsorship.json"; import sponsorship from "./sponsorship.json" assert {type: "json"};
import star from "./star.json"; import star from "./star.json" assert {type: "json"};
import status from "./status.json"; import status from "./status.json" assert {type: "json"};
import team from "./team.json"; import team from "./team.json" assert {type: "json"};
import team_add from "./team_add.json"; import team_add from "./team_add.json" assert {type: "json"};
import watch from "./watch.json"; import watch from "./watch.json" assert {type: "json"};
import workflow_call from "./workflow_call.json"; import workflow_call from "./workflow_call.json" assert {type: "json"};
import workflow_dispatch from "./workflow_dispatch.json"; import workflow_dispatch from "./workflow_dispatch.json" assert {type: "json"};
import workflow_run from "./workflow_run.json"; import workflow_run from "./workflow_run.json" assert {type: "json"};
export const eventPayloads: {[key: string]: Object} = { export const eventPayloads: {[key: string]: Object} = {
check_run, check_run,
+13 -6
View File
@@ -17,7 +17,7 @@ function testHoverConfig(tokenValue: string, tokenKey: string, description?: str
return description; return description;
} }
} satisfies DescriptionProvider } satisfies DescriptionProvider
} satisfies HoverConfig } satisfies HoverConfig;
} }
describe("hover", () => { describe("hover", () => {
@@ -146,7 +146,8 @@ jobs:
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
// The `ref` is a `string` definition and inherits the context from `step-with` // The `ref` is a `string` definition and inherits the context from `step-with`
const expected = "**Context:** github, inputs, vars, needs, strategy, matrix, secrets, steps, job, runner, env, hashFiles(1,255)" const expected =
"**Context:** github, inputs, vars, needs, strategy, matrix, secrets, steps, job, runner, env, hashFiles(1,255)";
expect(result?.contents).toEqual(expected); expect(result?.contents).toEqual(expected);
}); });
}); });
@@ -164,11 +165,15 @@ jobs:
ref|: main ref|: main
`; `;
const result = await hover(...getPositionFromCursor(input), testHoverConfig("ref", "string", "The branch, tag or SHA to checkout.")); const result = await hover(
...getPositionFromCursor(input),
testHoverConfig("ref", "string", "The branch, tag or SHA to checkout.")
);
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
const expected = "The branch, tag or SHA to checkout.\n\n" + const expected =
"**Context:** github, inputs, vars, needs, strategy, matrix, secrets, steps, job, runner, env, hashFiles(1,255)" "The branch, tag or SHA to checkout.\n\n" +
"**Context:** github, inputs, vars, needs, strategy, matrix, secrets, steps, job, runner, env, hashFiles(1,255)";
expect(result?.contents).toEqual(expected); expect(result?.contents).toEqual(expected);
}); });
@@ -184,6 +189,8 @@ jobs:
const result = await hover(...getPositionFromCursor(input), testHoverConfig("uses", "non-empty-string", undefined)); const result = await hover(...getPositionFromCursor(input), testHoverConfig("uses", "non-empty-string", undefined));
expect(result).not.toBeUndefined(); expect(result).not.toBeUndefined();
expect(result?.contents).toEqual("Selects an action to run as part of a step in your job. An action is a reusable unit of code. You can use an action defined in the same repository as the workflow, a public repository, or in a published Docker container image."); expect(result?.contents).toEqual(
"Selects an action to run as part of a step in your job. An action is a reusable unit of code. You can use an action defined in the same repository as the workflow, a public repository, or in a published Docker container image."
);
}); });
}); });
+4 -2
View File
@@ -39,7 +39,7 @@ export async function hover(document: TextDocument, position: Position, config?:
info(`Calculating hover for token with definition ${token.definition.key}`); info(`Calculating hover for token with definition ${token.definition.key}`);
if (tokenResult.parent && isCronMappingValue(tokenResult)) { if (tokenResult.parent && isCronMappingValue(tokenResult)) {
const tokenValue = (token as StringToken).value const tokenValue = (token as StringToken).value;
let description = getCronDescription(tokenValue); let description = getCronDescription(tokenValue);
if (description) { if (description) {
return { return {
@@ -82,7 +82,9 @@ async function getDescription(
} }
function isCronMappingValue(tokenResult: TokenResult): boolean { function isCronMappingValue(tokenResult: TokenResult): boolean {
return tokenResult.parent?.definition?.key === "cron-mapping" && return (
tokenResult.parent?.definition?.key === "cron-mapping" &&
isString(tokenResult.token!) && isString(tokenResult.token!) &&
tokenResult.token.value !== "cron" tokenResult.token.value !== "cron"
);
} }
@@ -141,13 +141,16 @@ function posInToken(pos: Position, token: TemplateToken): boolean {
const tokenChar = pos.character + 1; const tokenChar = pos.character + 1;
// Check lines // Check lines
if (r.start[0] > tokenLine || tokenLine > r.end[0]) { if (r.start.line > tokenLine || tokenLine > r.end.line) {
return false; return false;
} }
// Position is within the token lines. Check character/column if pos line matches // Position is within the token lines. Check character/column if pos line matches
// start or end // start or end
if ((r.start[0] === tokenLine && tokenChar < r.start[1]) || (r.end[0] === tokenLine && tokenChar > r.end[1])) { if (
(r.start.line === tokenLine && tokenChar < r.start.column) ||
(r.end.line === tokenLine && tokenChar > r.end.column)
) {
return false; return false;
} }
@@ -163,14 +166,14 @@ function onSameLine(pos: Position, key: TemplateToken, value: TemplateToken): bo
return false; return false;
} }
if (value.range.start[0] !== value.range.end[0]) { if (value.range.start.line !== value.range.end.line) {
// Token occupies multiple lines, can't be an empty node // Token occupies multiple lines, can't be an empty node
return false; return false;
} }
// TokenRange is one-based, Position is zero-based // TokenRange is one-based, Position is zero-based
const posLine = pos.line + 1; const posLine = pos.line + 1;
if (posLine != value.range.start[0]) { if (posLine != value.range.start.line) {
return false; return false;
} }
+2 -2
View File
@@ -23,7 +23,7 @@ export function mapRange(range: TokenRange | undefined): Range {
export function mapPosition(position: TokenPosition): Position { export function mapPosition(position: TokenPosition): Position {
return { return {
line: position[0] - 1, line: position.line - 1,
character: position[1] - 1 character: position.column - 1
}; };
} }
+2 -30
View File
@@ -73,21 +73,7 @@ jobs:
{valueProviderConfig: defaultValueProviders} {valueProviderConfig: defaultValueProviders}
); );
expect(result.length).toBe(1); expect(result.length).toBe(0);
expect(result[0]).toEqual({
message: "Value 'does-not-exist' might not be valid",
severity: DiagnosticSeverity.Warning,
range: {
end: {
character: 27,
line: 3
},
start: {
character: 13,
line: 3
}
}
} as Diagnostic);
}); });
it("value in sequence not returned by value provider", async () => { it("value in sequence not returned by value provider", async () => {
@@ -106,21 +92,7 @@ jobs:
{valueProviderConfig: defaultValueProviders} {valueProviderConfig: defaultValueProviders}
); );
expect(result.length).toBe(1); expect(result.length).toBe(0);
expect(result[0]).toEqual({
message: "Value 'does-not-exist' might not be valid",
severity: DiagnosticSeverity.Warning,
range: {
end: {
character: 20,
line: 5
},
start: {
character: 6,
line: 5
}
}
} as Diagnostic);
}); });
it("single value not returned by allowed value provider", async () => { it("single value not returned by allowed value provider", async () => {
+3 -9
View File
@@ -150,13 +150,7 @@ function invalidValue(diagnostics: Diagnostic[], token: StringToken, kind: Value
}); });
break; break;
case ValueProviderKind.SuggestedValues: // no messages for SuggestedValues
diagnostics.push({
message: `Value '${token.value}' might not be valid`,
severity: DiagnosticSeverity.Warning,
range: mapRange(token.range)
});
break;
} }
} }
@@ -168,8 +162,8 @@ function getProviderContext(
): WorkflowContext { ): WorkflowContext {
const {parent, path} = findToken( const {parent, path} = findToken(
{ {
line: token.range!.start[0] - 1, line: token.range!.start.line - 1,
character: token.range!.start[1] - 1 character: token.range!.start.column - 1
}, },
root root
); );
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@github/actions-workflow-parser", "name": "@github/actions-workflow-parser",
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"source": "./src/index.ts", "source": "./src/index.ts",
@@ -40,12 +40,12 @@
"watch": "tsc --build tsconfig.build.json --watch" "watch": "tsc --build tsconfig.build.json --watch"
}, },
"dependencies": { "dependencies": {
"@github/actions-expressions": "^0.1.100", "@github/actions-expressions": "^0.1.109",
"cronstrue": "^2.21.0", "cronstrue": "^2.21.0",
"yaml": "^2.0.0-8" "yaml": "^2.0.0-8"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
}, },
"files": [ "files": [
"dist/**/*" "dist/**/*"
@@ -56,7 +56,7 @@
"@typescript-eslint/parser": "^5.40.0", "@typescript-eslint/parser": "^5.40.0",
"eslint": "7.32.0", "eslint": "7.32.0",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
@@ -85,15 +85,15 @@ jobs:
[ [
"${{ github.event_name }}", "${{ github.event_name }}",
{ {
start: [7, 16], start: {line: 7, column: 16},
end: [7, 40] end: {line: 7, column: 40}
} }
], ],
[ [
"${{ github.ref }}", "${{ github.ref }}",
{ {
start: [8, 24], start: {line: 8, column: 24},
end: [8, 41] end: {line: 8, column: 41}
} }
] ]
]); ]);
@@ -160,16 +160,16 @@ jobs:
{ {
prefix: "test.yaml (Line: 6, Col: 14)", prefix: "test.yaml (Line: 6, Col: 14)",
range: { range: {
start: [7, 16], start: {line: 7, column: 16},
end: [7, 40] end: {line: 7, column: 40}
}, },
rawMessage: "Unrecognized function: 'fromJSON2'" rawMessage: "Unrecognized function: 'fromJSON2'"
}, },
{ {
prefix: "test.yaml (Line: 6, Col: 14)", prefix: "test.yaml (Line: 6, Col: 14)",
range: { range: {
start: [8, 24], start: {line: 8, column: 24},
end: [8, 51] end: {line: 8, column: 51}
}, },
rawMessage: "Unrecognized function: 'toJSON2'" rawMessage: "Unrecognized function: 'toJSON2'"
} }
+4 -4
View File
@@ -32,8 +32,8 @@ describe("parseWorkflow", () => {
expect(result.context.errors.getErrors()).toEqual([ expect(result.context.errors.getErrors()).toEqual([
new TemplateValidationError("Required property is missing: runs-on", "test.yaml (Line: 4, Col: 5)", undefined, { new TemplateValidationError("Required property is missing: runs-on", "test.yaml (Line: 4, Col: 5)", undefined, {
start: [4, 5], start: {line: 4, column: 5},
end: [5, 24] end: {line: 5, column: 24}
}) })
]); ]);
}); });
@@ -62,8 +62,8 @@ jobs:
"test.yaml (Line: 6, Col: 13)", "test.yaml (Line: 6, Col: 13)",
undefined, undefined,
{ {
start: [6, 13], start: {line: 6, column: 13},
end: [6, 37] end: {line: 6, column: 37}
} }
) )
]); ]);
@@ -455,7 +455,7 @@ class TemplateReader {
// Doesn't contain "${{" // Doesn't contain "${{"
// Check if value should still be evaluated as an expression // Check if value should still be evaluated as an expression
if (definitionInfo.definition instanceof StringDefinition && definitionInfo.definition.isExpression) { if (definitionInfo.definition instanceof StringDefinition && definitionInfo.definition.isExpression) {
const expression = this.parseIntoExpressionToken(token.range!, raw, allowedContext, token); const expression = this.parseIntoExpressionToken(token.range!, raw, allowedContext, token, definitionInfo);
if (expression) { if (expression) {
return expression; return expression;
} }
@@ -500,11 +500,11 @@ class TemplateReader {
); );
let tr = token.range!; let tr = token.range!;
if (tr.start[0] === tr.end[0]) { if (tr.start.line === tr.end.line) {
// If it's a single line expression, adjust the range to only cover the sub-expression // If it's a single line expression, adjust the range to only cover the sub-expression
tr = { tr = {
start: [tr.start[0], tr.start[1] + startExpression], start: {line: tr.start.line, column: tr.start.column + startExpression},
end: [tr.end[0], tr.start[1] + endExpression + 1] end: {line: tr.end.line, column: tr.start.column + endExpression + 1}
}; };
} else { } else {
// Adjust the range to only cover the expression for multi-line strings // Adjust the range to only cover the expression for multi-line strings
@@ -515,12 +515,12 @@ class TemplateReader {
const adjustedEnd = endExpression - beginningOfLine + 1; const adjustedEnd = endExpression - beginningOfLine + 1;
tr = { tr = {
start: [tr.start[0] + adjustedStartLine, adjustedStart], start: {line: tr.start.line + adjustedStartLine, column: adjustedStart},
end: [tr.start[0] + adjustedStartLine, adjustedEnd] end: {line: tr.start.line + adjustedStartLine, column: adjustedEnd}
}; };
} }
const expression = this.parseIntoExpressionToken(tr, rawExpression, allowedContext, token); const expression = this.parseIntoExpressionToken(tr, rawExpression, allowedContext, token, definitionInfo);
if (!expression) { if (!expression) {
// Record that we've hit an error but continue to validate any other expressions // Record that we've hit an error but continue to validate any other expressions
@@ -615,9 +615,10 @@ class TemplateReader {
tr: TokenRange, tr: TokenRange,
rawExpression: string, rawExpression: string,
allowedContext: string[], allowedContext: string[],
token: TemplateToken token: TemplateToken,
definitionInfo: DefinitionInfo | undefined
): ExpressionToken | undefined { ): ExpressionToken | undefined {
const parseExpressionResult = this.parseExpression(tr, rawExpression, allowedContext, token.definitionInfo); const parseExpressionResult = this.parseExpression(tr, rawExpression, allowedContext, definitionInfo);
// Check for error // Check for error
if (parseExpressionResult.error) { if (parseExpressionResult.error) {
@@ -42,11 +42,11 @@ export abstract class TemplateToken {
} }
public get line(): number | undefined { public get line(): number | undefined {
return this.range?.start[0]; return this.range?.start.line;
} }
public get col(): number | undefined { public get col(): number | undefined {
return this.range?.start[1]; return this.range?.start.column;
} }
public get definition(): Definition | undefined { public get definition(): Definition | undefined {
@@ -1,4 +1,10 @@
export type Position = [line: number, character: number]; /** Represents the position within a template object */
export type Position = {
/** The one-based line value */
line: number;
/** The one-based column value */
column: number;
};
export type TokenRange = { export type TokenRange = {
start: Position; start: Position;
@@ -1,6 +1,6 @@
import {JSONObjectReader} from "../templates/json-object-reader"; import {JSONObjectReader} from "../templates/json-object-reader";
import {TemplateSchema} from "../templates/schema"; import {TemplateSchema} from "../templates/schema";
import WorkflowSchema from "../workflow-v1.0.json"; import WorkflowSchema from "../workflow-v1.0.json" assert {type: "json"};
let schema: TemplateSchema; let schema: TemplateSchema;
@@ -74,8 +74,8 @@ it("YAML errors include range information", () => {
const error = context.errors.getErrors()[0]; const error = context.errors.getErrors()[0];
expect(error.range).toEqual({ expect(error.range).toEqual({
start: [7, 38], start: {line: 7, column: 38},
end: [7, 63] end: {line: 7, column: 63}
}); });
}); });
@@ -89,8 +89,8 @@ export class YamlObjectReader implements ObjectReader {
const elp = this.lineCounter.linePos(endPos); const elp = this.lineCounter.linePos(endPos);
return { return {
start: [slp.line, slp.col], start: {line: slp.line, column: slp.col},
end: [elp.line, elp.col] end: {line: elp.line, column: elp.col}
}; };
} }
@@ -216,7 +216,7 @@ function rangeFromLinePos(linePos: [LinePos] | [LinePos, LinePos] | undefined):
return; return;
} }
// TokenRange and linePos are both 1-based // TokenRange and linePos are both 1-based
const start: Position = [linePos[0].line, linePos[0].col]; const start: Position = {line: linePos[0].line, column: linePos[0].col};
const end: Position = linePos.length == 2 ? [linePos[1].line, linePos[1].col] : start; const end: Position = linePos.length == 2 ? {line: linePos[1].line, column: linePos[1].col} : start;
return {start, end}; return {start, end};
} }
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "browser-playground", "name": "browser-playground",
"version": "0.1.100", "version": "0.1.109",
"description": "", "description": "",
"private": true, "private": true,
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@github/actions-languageserver": "^0.1.100", "@github/actions-languageserver": "^0.1.109",
"monaco-editor-webpack-plugin": "^7.0.1", "monaco-editor-webpack-plugin": "^7.0.1",
"monaco-editor-workers": "^0.34.2", "monaco-editor-workers": "^0.34.2",
"monaco-languageclient": "^4.0.3", "monaco-languageclient": "^4.0.3",
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"$schema": "node_modules/lerna/schemas/lerna-schema.json", "$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useWorkspaces": true, "useWorkspaces": true,
"version": "0.1.100" "version": "0.1.109"
} }
+119 -63
View File
@@ -18,28 +18,28 @@
}, },
"actions-expressions": { "actions-expressions": {
"name": "@github/actions-expressions", "name": "@github/actions-expressions",
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
} }
}, },
"actions-languageserver": { "actions-languageserver": {
"name": "@github/actions-languageserver", "name": "@github/actions-languageserver",
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@github/actions-languageservice": "^0.1.100", "@github/actions-languageservice": "^0.1.109",
"@github/actions-workflow-parser": "^0.1.100", "@github/actions-workflow-parser": "^0.1.109",
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.7",
"vscode-languageserver": "^8.0.2", "vscode-languageserver": "^8.0.2",
"vscode-languageserver-textdocument": "^1.0.7", "vscode-languageserver-textdocument": "^1.0.7",
"yaml": "^2.1.3" "yaml": "^2.1.3"
@@ -48,22 +48,22 @@
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"fetch-mock": "^9.11.0", "fetch-mock": "^9.11.0",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
} }
}, },
"actions-languageservice": { "actions-languageservice": {
"name": "@github/actions-languageservice", "name": "@github/actions-languageservice",
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@github/actions-expressions": "^0.1.100", "@github/actions-expressions": "^0.1.109",
"@github/actions-workflow-parser": "^0.1.100", "@github/actions-workflow-parser": "^0.1.109",
"vscode-languageserver-textdocument": "^1.0.7", "vscode-languageserver-textdocument": "^1.0.7",
"vscode-languageserver-types": "^3.17.2", "vscode-languageserver-types": "^3.17.2",
"yaml": "^2.1.1" "yaml": "^2.1.1"
@@ -71,21 +71,21 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
} }
}, },
"actions-workflow-parser": { "actions-workflow-parser": {
"name": "@github/actions-workflow-parser", "name": "@github/actions-workflow-parser",
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@github/actions-expressions": "^0.1.100", "@github/actions-expressions": "^0.1.109",
"cronstrue": "^2.21.0", "cronstrue": "^2.21.0",
"yaml": "^2.0.0-8" "yaml": "^2.0.0-8"
}, },
@@ -95,20 +95,20 @@
"@typescript-eslint/parser": "^5.40.0", "@typescript-eslint/parser": "^5.40.0",
"eslint": "7.32.0", "eslint": "7.32.0",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4" "typescript": "^4.8.4"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16.15"
} }
}, },
"browser-playground": { "browser-playground": {
"version": "0.1.100", "version": "0.1.109",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@github/actions-languageserver": "^0.1.100", "@github/actions-languageserver": "^0.1.109",
"monaco-editor-webpack-plugin": "^7.0.1", "monaco-editor-webpack-plugin": "^7.0.1",
"monaco-editor-workers": "^0.34.2", "monaco-editor-workers": "^0.34.2",
"monaco-languageclient": "^4.0.3", "monaco-languageclient": "^4.0.3",
@@ -2904,11 +2904,11 @@
"dev": true "dev": true
}, },
"node_modules/@octokit/plugin-paginate-rest": { "node_modules/@octokit/plugin-paginate-rest": {
"version": "5.0.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz",
"integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==",
"dependencies": { "dependencies": {
"@octokit/types": "^8.0.0" "@octokit/types": "^9.0.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
@@ -2917,6 +2917,19 @@
"@octokit/core": ">=4" "@octokit/core": ">=4"
} }
}, },
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
"integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
"integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
"dependencies": {
"@octokit/openapi-types": "^16.0.0"
}
},
"node_modules/@octokit/plugin-request-log": { "node_modules/@octokit/plugin-request-log": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
@@ -2926,11 +2939,11 @@
} }
}, },
"node_modules/@octokit/plugin-rest-endpoint-methods": { "node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "6.7.0", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz",
"integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==", "integrity": "sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==",
"dependencies": { "dependencies": {
"@octokit/types": "^8.0.0", "@octokit/types": "^9.0.0",
"deprecation": "^2.3.1" "deprecation": "^2.3.1"
}, },
"engines": { "engines": {
@@ -2940,6 +2953,19 @@
"@octokit/core": ">=3" "@octokit/core": ">=3"
} }
}, },
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
"integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
},
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
"integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
"dependencies": {
"@octokit/openapi-types": "^16.0.0"
}
},
"node_modules/@octokit/request": { "node_modules/@octokit/request": {
"version": "6.2.2", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.2.tgz",
@@ -2970,14 +2996,14 @@
} }
}, },
"node_modules/@octokit/rest": { "node_modules/@octokit/rest": {
"version": "19.0.5", "version": "19.0.7",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.7.tgz",
"integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", "integrity": "sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==",
"dependencies": { "dependencies": {
"@octokit/core": "^4.1.0", "@octokit/core": "^4.1.0",
"@octokit/plugin-paginate-rest": "^5.0.0", "@octokit/plugin-paginate-rest": "^6.0.0",
"@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-request-log": "^1.0.4",
"@octokit/plugin-rest-endpoint-methods": "^6.7.0" "@octokit/plugin-rest-endpoint-methods": "^7.0.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
@@ -10998,9 +11024,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "2.7.1", "version": "2.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
"dev": true, "dev": true,
"bin": { "bin": {
"prettier": "bin-prettier.js" "prettier": "bin-prettier.js"
@@ -14535,7 +14561,7 @@
"requires": { "requires": {
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.7.4" "typescript": "^4.7.4"
@@ -14544,13 +14570,13 @@
"@github/actions-languageserver": { "@github/actions-languageserver": {
"version": "file:actions-languageserver", "version": "file:actions-languageserver",
"requires": { "requires": {
"@github/actions-languageservice": "^0.1.100", "@github/actions-languageservice": "^0.1.109",
"@github/actions-workflow-parser": "^0.1.100", "@github/actions-workflow-parser": "^0.1.109",
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.7",
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"fetch-mock": "^9.11.0", "fetch-mock": "^9.11.0",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4", "typescript": "^4.8.4",
@@ -14562,11 +14588,11 @@
"@github/actions-languageservice": { "@github/actions-languageservice": {
"version": "file:actions-languageservice", "version": "file:actions-languageservice",
"requires": { "requires": {
"@github/actions-expressions": "^0.1.100", "@github/actions-expressions": "^0.1.109",
"@github/actions-workflow-parser": "^0.1.100", "@github/actions-workflow-parser": "^0.1.109",
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4", "typescript": "^4.8.4",
@@ -14578,14 +14604,14 @@
"@github/actions-workflow-parser": { "@github/actions-workflow-parser": {
"version": "file:actions-workflow-parser", "version": "file:actions-workflow-parser",
"requires": { "requires": {
"@github/actions-expressions": "^0.1.100", "@github/actions-expressions": "^0.1.109",
"@types/jest": "^29.0.3", "@types/jest": "^29.0.3",
"@typescript-eslint/eslint-plugin": "^5.40.0", "@typescript-eslint/eslint-plugin": "^5.40.0",
"@typescript-eslint/parser": "^5.40.0", "@typescript-eslint/parser": "^5.40.0",
"cronstrue": "^2.21.0", "cronstrue": "^2.21.0",
"eslint": "7.32.0", "eslint": "7.32.0",
"jest": "^29.0.3", "jest": "^29.0.3",
"prettier": "^2.7.1", "prettier": "^2.8.3",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"typescript": "^4.8.4", "typescript": "^4.8.4",
@@ -16280,11 +16306,26 @@
"dev": true "dev": true
}, },
"@octokit/plugin-paginate-rest": { "@octokit/plugin-paginate-rest": {
"version": "5.0.1", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-5.0.1.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz",
"integrity": "sha512-7A+rEkS70pH36Z6JivSlR7Zqepz3KVucEFVDnSrgHXzG7WLAzYwcHZbKdfTXHwuTHbkT1vKvz7dHl1+HNf6Qyw==", "integrity": "sha512-Sq5VU1PfT6/JyuXPyt04KZNVsFOSBaYOAq2QRZUwzVlI10KFvcbUo8lR258AAQL1Et60b0WuVik+zOWKLuDZxw==",
"requires": { "requires": {
"@octokit/types": "^8.0.0" "@octokit/types": "^9.0.0"
},
"dependencies": {
"@octokit/openapi-types": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
"integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
},
"@octokit/types": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
"integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
"requires": {
"@octokit/openapi-types": "^16.0.0"
}
}
} }
}, },
"@octokit/plugin-request-log": { "@octokit/plugin-request-log": {
@@ -16294,12 +16335,27 @@
"requires": {} "requires": {}
}, },
"@octokit/plugin-rest-endpoint-methods": { "@octokit/plugin-rest-endpoint-methods": {
"version": "6.7.0", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.7.0.tgz", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.0.1.tgz",
"integrity": "sha512-orxQ0fAHA7IpYhG2flD2AygztPlGYNAdlzYz8yrD8NDgelPfOYoRPROfEyIe035PlxvbYrgkfUZIhSBKju/Cvw==", "integrity": "sha512-pnCaLwZBudK5xCdrR823xHGNgqOzRnJ/mpC/76YPpNP7DybdsJtP7mdOwh+wYZxK5jqeQuhu59ogMI4NRlBUvA==",
"requires": { "requires": {
"@octokit/types": "^8.0.0", "@octokit/types": "^9.0.0",
"deprecation": "^2.3.1" "deprecation": "^2.3.1"
},
"dependencies": {
"@octokit/openapi-types": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
"integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
},
"@octokit/types": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.0.0.tgz",
"integrity": "sha512-LUewfj94xCMH2rbD5YJ+6AQ4AVjFYTgpp6rboWM5T7N3IsIF65SBEOVcYMGAEzO/kKNiNaW4LoWtoThOhH06gw==",
"requires": {
"@octokit/openapi-types": "^16.0.0"
}
}
} }
}, },
"@octokit/request": { "@octokit/request": {
@@ -16326,14 +16382,14 @@
} }
}, },
"@octokit/rest": { "@octokit/rest": {
"version": "19.0.5", "version": "19.0.7",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.5.tgz", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.7.tgz",
"integrity": "sha512-+4qdrUFq2lk7Va+Qff3ofREQWGBeoTKNqlJO+FGjFP35ZahP+nBenhZiGdu8USSgmq4Ky3IJ/i4u0xbLqHaeow==", "integrity": "sha512-HRtSfjrWmWVNp2uAkEpQnuGMJsu/+dBr47dRc5QVgsCbnIc1+GFEaoKBWkYG+zjrsHpSqcAElMio+n10c0b5JA==",
"requires": { "requires": {
"@octokit/core": "^4.1.0", "@octokit/core": "^4.1.0",
"@octokit/plugin-paginate-rest": "^5.0.0", "@octokit/plugin-paginate-rest": "^6.0.0",
"@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-request-log": "^1.0.4",
"@octokit/plugin-rest-endpoint-methods": "^6.7.0" "@octokit/plugin-rest-endpoint-methods": "^7.0.0"
} }
}, },
"@octokit/types": { "@octokit/types": {
@@ -17506,7 +17562,7 @@
"browser-playground": { "browser-playground": {
"version": "file:browser-playground", "version": "file:browser-playground",
"requires": { "requires": {
"@github/actions-languageserver": "^0.1.100", "@github/actions-languageserver": "^0.1.109",
"css-loader": "^6.7.2", "css-loader": "^6.7.2",
"monaco-editor-webpack-plugin": "^7.0.1", "monaco-editor-webpack-plugin": "^7.0.1",
"monaco-editor-workers": "^0.34.2", "monaco-editor-workers": "^0.34.2",
@@ -22508,9 +22564,9 @@
"dev": true "dev": true
}, },
"prettier": { "prettier": {
"version": "2.7.1", "version": "2.8.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
"dev": true "dev": true
}, },
"pretty-format": { "pretty-format": {