Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
689dca8802 | ||
|
|
10171d84a7 |
+146
-57
@@ -95,7 +95,6 @@ import { Expr } from "./ast.js"; // Points to .ts file, but use .js extension
|
||||
- Works with TypeScript 4.7+ (with node16 moduleResolution)
|
||||
- Well-established pattern
|
||||
- No post-processing needed
|
||||
- Works with ts-jest without extra configuration
|
||||
|
||||
**Cons:**
|
||||
- Confusing - `.js` files don't exist at write time
|
||||
@@ -103,7 +102,7 @@ import { Expr } from "./ast.js"; // Points to .ts file, but use .js extension
|
||||
|
||||
### Recommendation
|
||||
|
||||
**Use Option B** (manual `.js` extensions). Option A with `rewriteRelativeImportExtensions` has compatibility issues with ts-jest and requires additional workarounds.
|
||||
**Use Option A** with workarounds for known issues (see below).
|
||||
|
||||
---
|
||||
|
||||
@@ -155,95 +154,122 @@ at Object.<anonymous> (node_modules/ts-jest/dist/compiler/ts-compiler.js:43:123)
|
||||
|
||||
**Upstream Issue:** https://github.com/microsoft/TypeScript/issues/61037 (marked "Help Wanted", in Backlog, NOT FIXED as of Dec 2025)
|
||||
|
||||
**Workaround:** Post-process `.d.ts` files with a script. See `script/fix-dts-extensions.cjs`.
|
||||
**Workaround:** Post-process `.d.ts` files with a script. Create `script/fix-dts-extensions.cjs`:
|
||||
|
||||
**Note:** Since we use Option B (manual `.js` extensions), this bug does not affect our migration.
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Post-build script to fix TypeScript's rewriteRelativeImportExtensions bug
|
||||
* where .d.ts files get .ts extensions instead of .js extensions.
|
||||
* See: https://github.com/microsoft/TypeScript/issues/61037
|
||||
*/
|
||||
|
||||
### 4. yaml Package Internal Types Not Exported
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
**Problem:** The `yaml` package does not export internal types like `LinePos` and `NodeBase` that are used in `workflow-parser/src/workflows/yaml-object-reader.ts`.
|
||||
function fixDtsFile(filePath) {
|
||||
let content = fs.readFileSync(filePath, 'utf8');
|
||||
const original = content;
|
||||
|
||||
// Replace .ts extensions in import/export statements with .js
|
||||
content = content.replace(/(from\s+["'])([^"']+)\.ts(["'])/g, '$1$2.js$3');
|
||||
content = content.replace(/(import\s*\(\s*["'])([^"']+)\.ts(["']\s*\))/g, '$1$2.js$3');
|
||||
content = content.replace(/(export\s+\*\s+from\s+["'])([^"']+)\.ts(["'])/g, '$1$2.js$3');
|
||||
|
||||
if (content !== original) {
|
||||
fs.writeFileSync(filePath, content, 'utf8');
|
||||
console.log(`Fixed: ${filePath}`);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
**Error:**
|
||||
```
|
||||
error TS2305: Module '"yaml"' has no exported member 'LinePos'.
|
||||
error TS2305: Module '"yaml"' has no exported member 'NodeBase'.
|
||||
function walkDir(dir, callback) {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.isDirectory()) {
|
||||
walkDir(filePath, callback);
|
||||
} else if (file.endsWith('.d.ts')) {
|
||||
callback(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length === 0) {
|
||||
console.error('Usage: fix-dts-extensions.cjs <dist-dir> [<dist-dir2> ...]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let fixedCount = 0;
|
||||
for (const dir of args) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
console.warn(`Directory not found: ${dir}`);
|
||||
continue;
|
||||
}
|
||||
walkDir(dir, (filePath) => {
|
||||
if (fixDtsFile(filePath)) {
|
||||
fixedCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log(`Fixed ${fixedCount} .d.ts file(s)`);
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
**Solution:** Define local type aliases in the file that uses them:
|
||||
```typescript
|
||||
// Local type definitions to replace yaml internal imports
|
||||
type LinePos = { line: number; col: number };
|
||||
type NodeBase = { range?: [number, number, number] };
|
||||
```
|
||||
**Note:** Use `.cjs` extension since root `package.json` has `"type": "module"`.
|
||||
|
||||
### 5. languageserver Blocked by vscode-languageserver Dependency
|
||||
|
||||
**Problem:** The `vscode-languageserver` package (v8.0.2) does not have proper ESM exports. When using `moduleResolution: "node16"`, TypeScript requires packages to have an `exports` field in `package.json` for subpath imports to work.
|
||||
|
||||
**Error:**
|
||||
```
|
||||
src/index.ts(6,8): error TS2307: Cannot find module 'vscode-languageserver/browser' or its corresponding type declarations.
|
||||
src/connection.ts(1,43): error TS2307: Cannot find module 'vscode-languageserver/node' or its corresponding type declarations.
|
||||
```
|
||||
|
||||
**Root Cause:** The `vscode-languageserver` package.json only has `main` and `browser` fields, but no `exports` field:
|
||||
**Usage in package.json build scripts:**
|
||||
```json
|
||||
{
|
||||
"main": "./lib/node/main.js",
|
||||
"browser": {
|
||||
"./lib/node/main.js": "./lib/browser/main.js"
|
||||
"scripts": {
|
||||
"build": "tsc --build tsconfig.build.json && node ../script/fix-dts-extensions.cjs dist"
|
||||
}
|
||||
// No "exports" field!
|
||||
}
|
||||
```
|
||||
|
||||
With `moduleResolution: "node16"`, TypeScript follows Node.js ESM resolution rules which require explicit `exports` for subpath imports like `vscode-languageserver/browser` and `vscode-languageserver/node`.
|
||||
### 4. languageserver Tests Hang
|
||||
|
||||
**Status:** Verified December 2025. Version 9.0.1 is available but ESM export support is not confirmed.
|
||||
**Problem:** The languageserver tests hang indefinitely when running with the ESM configuration.
|
||||
|
||||
**Current Decision:** The languageserver package is **deferred** from this migration until the upstream `vscode-languageserver` package adds proper ESM exports. It will continue using the old `moduleResolution: "node"` configuration.
|
||||
**Status:** Not fully diagnosed. Tests pass on main branch but hang on ESM branch.
|
||||
|
||||
**Options to resolve:**
|
||||
- Wait for vscode-languageserver to add ESM exports
|
||||
- Try upgrading to vscode-languageserver v9.x to see if exports were added
|
||||
- Use a bundler to work around the module resolution
|
||||
- Fork or patch the dependency
|
||||
**Possible causes:**
|
||||
- Jest ESM module resolution issues
|
||||
- Cross-package source mappings in jest.config.js
|
||||
- vscode-languageserver ESM compatibility issues
|
||||
- Specific test file causing hang (needs isolation testing)
|
||||
|
||||
---
|
||||
|
||||
## Migration Status
|
||||
|
||||
| Package | Tests | ESM Status |
|
||||
|---------|-------|------------|
|
||||
| expressions | 1068 | ✅ Migrated |
|
||||
| workflow-parser | 292 | ✅ Migrated |
|
||||
| languageservice | 452 | ✅ Migrated |
|
||||
| languageserver | 6 files | ⏸️ Deferred (vscode-languageserver lacks ESM exports) |
|
||||
**Investigation needed:**
|
||||
- Run individual test files to isolate the hanging test
|
||||
- Check if vscode-languageserver has ESM compatibility issues
|
||||
- Review jest configuration for problematic mappings
|
||||
- Try running with `--detectOpenHandles` flag
|
||||
|
||||
---
|
||||
|
||||
## Required Configuration Changes
|
||||
|
||||
### tsconfig.build.json (each migrated package)
|
||||
|
||||
**Note:** We use **Option B** (manual `.js` extensions in source files) rather than `rewriteRelativeImportExtensions` because Option A caused ts-jest compatibility issues (tests would hang indefinitely).
|
||||
### tsconfig.json (each package)
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"skipLibCheck": true,
|
||||
"rewriteRelativeImportExtensions": true,
|
||||
"lib": ["ES2022"],
|
||||
"target": "ES2022"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `skipLibCheck: true` is needed to work around @types/node compatibility issues with TypeScript 5.x (TS2386 overload signature errors).
|
||||
```
|
||||
|
||||
### jest.config.js (each migrated package)
|
||||
### jest.config.js (each package)
|
||||
|
||||
```javascript
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
@@ -289,6 +315,69 @@ export default {
|
||||
|
||||
---
|
||||
|
||||
## Test Results (December 2025 Attempt)
|
||||
|
||||
| Package | Tests | Status |
|
||||
|---------|-------|--------|
|
||||
| expressions | 1068 | ✅ Pass |
|
||||
| workflow-parser | 292 | ✅ Pass |
|
||||
| languageservice | 452 | ✅ Pass |
|
||||
| languageserver | 6 files | ❌ Hangs (needs investigation) |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Prerequisites
|
||||
- [ ] Merge all pending PRs to avoid conflicts
|
||||
- [ ] Ensure clean main branch state
|
||||
|
||||
### Phase 2: TypeScript & Dependencies
|
||||
- [ ] Add `"overrides": { "typescript": "5.8.3" }` to root package.json
|
||||
- [ ] Update all workspace packages to TypeScript ^5.8.3
|
||||
- [ ] Downgrade ts-jest to ^29.0.3 in all packages
|
||||
- [ ] Run `npm install` to apply changes
|
||||
|
||||
### Phase 3: tsconfig Updates
|
||||
- [ ] Update tsconfig.json in each package with node16 settings
|
||||
- [ ] Add `rewriteRelativeImportExtensions: true`
|
||||
|
||||
### Phase 4: Add .ts Extensions to Imports
|
||||
- [ ] Update all relative imports in source files to use `.ts` extensions
|
||||
- [ ] This was already done in PR #243
|
||||
|
||||
### Phase 5: Create Fix Script
|
||||
- [ ] Add `script/fix-dts-extensions.cjs` to repository
|
||||
- [ ] Update build scripts to run fix after tsc
|
||||
|
||||
### Phase 6: Fix languageserver Tests
|
||||
- [ ] Investigate why tests hang
|
||||
- [ ] Isolate problematic test file
|
||||
- [ ] Apply fix or workaround
|
||||
|
||||
### Phase 7: Verification
|
||||
- [ ] Run `npm run build` in all packages
|
||||
- [ ] Run `npm test` in all packages
|
||||
- [ ] Test importing published packages in Node.js ESM mode
|
||||
- [ ] Verify browser-playground still works
|
||||
|
||||
### Phase 8: CI Updates
|
||||
- [ ] Update GitHub Actions workflows if needed
|
||||
- [ ] Ensure fix-dts-extensions.cjs runs in CI
|
||||
|
||||
---
|
||||
|
||||
## Summary of Required Changes
|
||||
|
||||
1. **Root package.json**: Add TypeScript override
|
||||
2. **All packages**: TypeScript 5.8.3, ts-jest 29.0.3
|
||||
3. **All tsconfigs**: node16 moduleResolution, rewriteRelativeImportExtensions
|
||||
4. **All imports**: Add .ts extensions (already done)
|
||||
5. **Build scripts**: Add post-build .d.ts fix
|
||||
6. **languageserver**: Debug test hang issue
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [TypeScript moduleResolution reference](https://www.typescriptlang.org/docs/handbook/modules/reference.html)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.27",
|
||||
"version": "0.3.25",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
@@ -60,6 +60,6 @@
|
||||
"prettier": "^2.8.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^29.0.3",
|
||||
"typescript": "^5.8.3"
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ExpressionData} from "./data/index.js";
|
||||
import {Token} from "./lexer.js";
|
||||
import {ExpressionData} from "./data";
|
||||
import {Token} from "./lexer";
|
||||
|
||||
export interface ExprVisitor<R> {
|
||||
visitLiteral(literal: Literal): R;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {complete, CompletionItem, trimTokenVector} from "./completion.js";
|
||||
import {DescriptionDictionary} from "./completion/descriptionDictionary.js";
|
||||
import {BooleanData} from "./data/boolean.js";
|
||||
import {Dictionary} from "./data/dictionary.js";
|
||||
import {StringData} from "./data/string.js";
|
||||
import {wellKnownFunctions} from "./funcs.js";
|
||||
import {FunctionDefinition, FunctionInfo} from "./funcs/info.js";
|
||||
import {Lexer, TokenType} from "./lexer.js";
|
||||
import {complete, CompletionItem, trimTokenVector} from "./completion";
|
||||
import {DescriptionDictionary} from "./completion/descriptionDictionary";
|
||||
import {BooleanData} from "./data/boolean";
|
||||
import {Dictionary} from "./data/dictionary";
|
||||
import {StringData} from "./data/string";
|
||||
import {wellKnownFunctions} from "./funcs";
|
||||
import {FunctionDefinition, FunctionInfo} from "./funcs/info";
|
||||
import {Lexer, TokenType} from "./lexer";
|
||||
|
||||
const testContext = new Dictionary(
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {DescriptionPair} from "./completion/descriptionDictionary.js";
|
||||
import {Dictionary, isDictionary} from "./data/dictionary.js";
|
||||
import {ExpressionData} from "./data/expressiondata.js";
|
||||
import {Evaluator} from "./evaluator.js";
|
||||
import {wellKnownFunctions} from "./funcs.js";
|
||||
import {FunctionDefinition, FunctionInfo} from "./funcs/info.js";
|
||||
import {Lexer, Token, TokenType} from "./lexer.js";
|
||||
import {Parser} from "./parser.js";
|
||||
import {DescriptionPair} from "./completion/descriptionDictionary";
|
||||
import {Dictionary, isDictionary} from "./data/dictionary";
|
||||
import {ExpressionData} from "./data/expressiondata";
|
||||
import {Evaluator} from "./evaluator";
|
||||
import {wellKnownFunctions} from "./funcs";
|
||||
import {FunctionDefinition, FunctionInfo} from "./funcs/info";
|
||||
import {Lexer, Token, TokenType} from "./lexer";
|
||||
import {Parser} from "./parser";
|
||||
|
||||
export type CompletionItem = {
|
||||
label: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {StringData} from "../data/index.js";
|
||||
import {DescriptionDictionary} from "./descriptionDictionary.js";
|
||||
import {StringData} from "../data";
|
||||
import {DescriptionDictionary} from "./descriptionDictionary";
|
||||
|
||||
describe("description dictionary", () => {
|
||||
it("pairs contains all values", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Dictionary} from "../data/dictionary.js";
|
||||
import {ExpressionData, Kind, Pair} from "../data/expressiondata.js";
|
||||
import {Dictionary} from "../data/dictionary";
|
||||
import {ExpressionData, Kind, Pair} from "../data/expressiondata";
|
||||
|
||||
export type DescriptionPair = Pair & {description?: string};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionData, ExpressionDataInterface, Kind, kindStr} from "./expressiondata.js";
|
||||
import {ExpressionData, ExpressionDataInterface, Kind, kindStr} from "./expressiondata";
|
||||
|
||||
export class Array implements ExpressionDataInterface {
|
||||
private v: ExpressionData[] = [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata.js";
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata";
|
||||
|
||||
export class BooleanData implements ExpressionDataInterface {
|
||||
constructor(public readonly value: boolean) {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Dictionary} from "./dictionary.js";
|
||||
import {StringData} from "./string.js";
|
||||
import {Dictionary} from "./dictionary";
|
||||
import {StringData} from "./string";
|
||||
|
||||
describe("dictionary", () => {
|
||||
it("pairs contains all values", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionData, ExpressionDataInterface, Kind, kindStr, Pair} from "./expressiondata.js";
|
||||
import {ExpressionData, ExpressionDataInterface, Kind, kindStr, Pair} from "./expressiondata";
|
||||
|
||||
export class Dictionary implements ExpressionDataInterface {
|
||||
private keys: string[] = [];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {Dictionary} from "./dictionary.js";
|
||||
import {Null} from "./null.js";
|
||||
import {Array} from "./array.js";
|
||||
import {StringData} from "./string.js";
|
||||
import {NumberData} from "./number.js";
|
||||
import {BooleanData} from "./boolean.js";
|
||||
import {Dictionary} from "./dictionary";
|
||||
import {Null} from "./null";
|
||||
import {Array} from "./array";
|
||||
import {StringData} from "./string";
|
||||
import {NumberData} from "./number";
|
||||
import {BooleanData} from "./boolean";
|
||||
|
||||
export enum Kind {
|
||||
String = 0,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export {Array} from "./array.js";
|
||||
export {BooleanData} from "./boolean.js";
|
||||
export {Dictionary} from "./dictionary.js";
|
||||
export {ExpressionData, Kind} from "./expressiondata.js";
|
||||
export {Null} from "./null.js";
|
||||
export {NumberData} from "./number.js";
|
||||
export {replacer} from "./replacer.js";
|
||||
export {reviver} from "./reviver.js";
|
||||
export {StringData} from "./string.js";
|
||||
export {Array} from "./array";
|
||||
export {BooleanData} from "./boolean";
|
||||
export {Dictionary} from "./dictionary";
|
||||
export {ExpressionData, Kind} from "./expressiondata";
|
||||
export {Null} from "./null";
|
||||
export {NumberData} from "./number";
|
||||
export {replacer} from "./replacer";
|
||||
export {reviver} from "./reviver";
|
||||
export {StringData} from "./string";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata.js";
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata";
|
||||
|
||||
export class Null implements ExpressionDataInterface {
|
||||
public readonly kind = Kind.Null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {NumberData} from "./number.js";
|
||||
import {NumberData} from "./number";
|
||||
|
||||
describe("number", () => {
|
||||
it("coerces to string", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata.js";
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata";
|
||||
|
||||
export class NumberData implements ExpressionDataInterface {
|
||||
constructor(public readonly value: number) {}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {Array} from "./array.js";
|
||||
import {Dictionary} from "./dictionary.js";
|
||||
import {Null} from "./null.js";
|
||||
import {NumberData} from "./number.js";
|
||||
import {replacer} from "./replacer.js";
|
||||
import {StringData} from "./string.js";
|
||||
import {Array} from "./array";
|
||||
import {Dictionary} from "./dictionary";
|
||||
import {Null} from "./null";
|
||||
import {NumberData} from "./number";
|
||||
import {replacer} from "./replacer";
|
||||
import {StringData} from "./string";
|
||||
|
||||
describe("replacer", () => {
|
||||
it("null", () => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {Array} from "./array.js";
|
||||
import {BooleanData} from "./boolean.js";
|
||||
import {Dictionary} from "./dictionary.js";
|
||||
import {Null} from "./null.js";
|
||||
import {NumberData} from "./number.js";
|
||||
import {StringData} from "./string.js";
|
||||
import {Array} from "./array";
|
||||
import {BooleanData} from "./boolean";
|
||||
import {Dictionary} from "./dictionary";
|
||||
import {Null} from "./null";
|
||||
import {NumberData} from "./number";
|
||||
import {StringData} from "./string";
|
||||
|
||||
/**
|
||||
* Replacer can be passed to JSON.stringify to convert an ExpressionData object into plain JSON
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Array} from "./array.js";
|
||||
import {BooleanData} from "./boolean.js";
|
||||
import {Dictionary} from "./dictionary.js";
|
||||
import {ExpressionData} from "./expressiondata.js";
|
||||
import {Null} from "./null.js";
|
||||
import {NumberData} from "./number.js";
|
||||
import {reviver} from "./reviver.js";
|
||||
import {StringData} from "./string.js";
|
||||
import {Array} from "./array";
|
||||
import {BooleanData} from "./boolean";
|
||||
import {Dictionary} from "./dictionary";
|
||||
import {ExpressionData} from "./expressiondata";
|
||||
import {Null} from "./null";
|
||||
import {NumberData} from "./number";
|
||||
import {reviver} from "./reviver";
|
||||
import {StringData} from "./string";
|
||||
|
||||
describe("reviver", () => {
|
||||
const tests: {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Array as dArray} from "./array.js";
|
||||
import {BooleanData} from "./boolean.js";
|
||||
import {Dictionary} from "./dictionary.js";
|
||||
import {ExpressionData} from "./expressiondata.js";
|
||||
import {Null} from "./null.js";
|
||||
import {NumberData} from "./number.js";
|
||||
import {StringData} from "./string.js";
|
||||
import {Array as dArray} from "./array";
|
||||
import {BooleanData} from "./boolean";
|
||||
import {Dictionary} from "./dictionary";
|
||||
import {ExpressionData} from "./expressiondata";
|
||||
import {Null} from "./null";
|
||||
import {NumberData} from "./number";
|
||||
import {StringData} from "./string";
|
||||
|
||||
/**
|
||||
* Reviver can be passed to `JSON.parse` to convert plain JSON into an `ExpressionData` object.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata.js";
|
||||
import {ExpressionDataInterface, Kind} from "./expressiondata";
|
||||
|
||||
export class StringData implements ExpressionDataInterface {
|
||||
constructor(public readonly value: string) {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Pos, Token, tokenString} from "./lexer.js";
|
||||
import {Pos, Token, tokenString} from "./lexer";
|
||||
|
||||
export const MAX_PARSER_DEPTH = 50;
|
||||
export const MAX_EXPRESSION_LENGTH = 21000;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as data from "./data/index.js";
|
||||
import {ExpressionEvaluationError} from "./errors.js";
|
||||
import {Evaluator} from "./evaluator.js";
|
||||
import {Lexer} from "./lexer.js";
|
||||
import {Parser} from "./parser.js";
|
||||
import * as data from "./data";
|
||||
import {ExpressionEvaluationError} from "./errors";
|
||||
import {Evaluator} from "./evaluator";
|
||||
import {Lexer} from "./lexer";
|
||||
import {Parser} from "./parser";
|
||||
|
||||
describe("evaluator", () => {
|
||||
const lexAndParse = (input: string) => {
|
||||
|
||||
@@ -10,14 +10,14 @@ import {
|
||||
Logical,
|
||||
Star,
|
||||
Unary
|
||||
} from "./ast.js";
|
||||
import * as data from "./data/index.js";
|
||||
import {FilteredArray} from "./filtered_array.js";
|
||||
import {wellKnownFunctions} from "./funcs.js";
|
||||
import {FunctionDefinition} from "./funcs/info.js";
|
||||
import {idxHelper} from "./idxHelper.js";
|
||||
import {TokenType} from "./lexer.js";
|
||||
import {equals, falsy, greaterThan, lessThan, truthy} from "./result.js";
|
||||
} from "./ast";
|
||||
import * as data from "./data";
|
||||
import {FilteredArray} from "./filtered_array";
|
||||
import {wellKnownFunctions} from "./funcs";
|
||||
import {FunctionDefinition} from "./funcs/info";
|
||||
import {idxHelper} from "./idxHelper";
|
||||
import {TokenType} from "./lexer";
|
||||
import {equals, falsy, greaterThan, lessThan, truthy} from "./result";
|
||||
|
||||
export class Evaluator implements ExprVisitor<data.ExpressionData> {
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import * as data from "./data/index.js";
|
||||
import * as data from "./data";
|
||||
|
||||
export class FilteredArray extends data.Array {}
|
||||
|
||||
+10
-10
@@ -1,13 +1,13 @@
|
||||
import {ErrorType, ExpressionError} from "./errors.js";
|
||||
import {contains} from "./funcs/contains.js";
|
||||
import {endswith} from "./funcs/endswith.js";
|
||||
import {format} from "./funcs/format.js";
|
||||
import {fromjson} from "./funcs/fromjson.js";
|
||||
import {FunctionDefinition, FunctionInfo} from "./funcs/info.js";
|
||||
import {join} from "./funcs/join.js";
|
||||
import {startswith} from "./funcs/startswith.js";
|
||||
import {tojson} from "./funcs/tojson.js";
|
||||
import {Token} from "./lexer.js";
|
||||
import {ErrorType, ExpressionError} from "./errors";
|
||||
import {contains} from "./funcs/contains";
|
||||
import {endswith} from "./funcs/endswith";
|
||||
import {format} from "./funcs/format";
|
||||
import {fromjson} from "./funcs/fromjson";
|
||||
import {FunctionDefinition, FunctionInfo} from "./funcs/info";
|
||||
import {join} from "./funcs/join";
|
||||
import {startswith} from "./funcs/startswith";
|
||||
import {tojson} from "./funcs/tojson";
|
||||
import {Token} from "./lexer";
|
||||
|
||||
export type ParseContext = {
|
||||
allowUnknownKeywords: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {BooleanData, ExpressionData, Kind} from "../data/index.js";
|
||||
import {equals} from "../result.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {BooleanData, ExpressionData, Kind} from "../data";
|
||||
import {equals} from "../result";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const contains: FunctionDefinition = {
|
||||
name: "contains",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {BooleanData, ExpressionData} from "../data/index.js";
|
||||
import {toUpperSpecial} from "../result.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {BooleanData, ExpressionData} from "../data";
|
||||
import {toUpperSpecial} from "../result";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const endswith: FunctionDefinition = {
|
||||
name: "endsWith",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Null, NumberData, StringData} from "../data/index.js";
|
||||
import {format} from "./format.js";
|
||||
import {Null, NumberData, StringData} from "../data";
|
||||
import {format} from "./format";
|
||||
|
||||
describe("format", () => {
|
||||
it("null", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ExpressionData, StringData} from "../data/index.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {ExpressionData, StringData} from "../data";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const format: FunctionDefinition = {
|
||||
name: "format",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {ExpressionData} from "../data/index.js";
|
||||
import {reviver} from "../data/reviver.js";
|
||||
import {ExpressionEvaluationError} from "../errors.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {ExpressionData} from "../data";
|
||||
import {reviver} from "../data/reviver";
|
||||
import {ExpressionEvaluationError} from "../errors";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const fromjson: FunctionDefinition = {
|
||||
name: "fromJson",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionData} from "../data/index.js";
|
||||
import {ExpressionData} from "../data";
|
||||
|
||||
export interface FunctionInfo {
|
||||
name: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ExpressionData, Kind, StringData} from "../data/index.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {ExpressionData, Kind, StringData} from "../data";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const join: FunctionDefinition = {
|
||||
name: "join",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {BooleanData, ExpressionData} from "../data/index.js";
|
||||
import {toUpperSpecial} from "../result.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {BooleanData, ExpressionData} from "../data";
|
||||
import {toUpperSpecial} from "../result";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const startswith: FunctionDefinition = {
|
||||
name: "startsWith",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {ExpressionData, StringData} from "../data/index.js";
|
||||
import {replacer} from "../data/replacer.js";
|
||||
import {FunctionDefinition} from "./info.js";
|
||||
import {ExpressionData, StringData} from "../data";
|
||||
import {replacer} from "../data/replacer";
|
||||
import {FunctionDefinition} from "./info";
|
||||
|
||||
export const tojson: FunctionDefinition = {
|
||||
name: "toJson",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ExpressionData} from "./data/index.js";
|
||||
import {ExpressionData} from "./data";
|
||||
|
||||
export class idxHelper {
|
||||
public readonly str: string | undefined;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export {Expr} from "./ast.js";
|
||||
export {complete, CompletionItem} from "./completion.js";
|
||||
export {DescriptionDictionary, DescriptionPair, isDescriptionDictionary} from "./completion/descriptionDictionary.js";
|
||||
export * as data from "./data/index.js";
|
||||
export {ExpressionError, ExpressionEvaluationError} from "./errors.js";
|
||||
export {Evaluator} from "./evaluator.js";
|
||||
export {wellKnownFunctions} from "./funcs.js";
|
||||
export {Lexer, Result} from "./lexer.js";
|
||||
export {Parser} from "./parser.js";
|
||||
export {Expr} from "./ast";
|
||||
export {complete, CompletionItem} from "./completion";
|
||||
export {DescriptionDictionary, DescriptionPair, isDescriptionDictionary} from "./completion/descriptionDictionary";
|
||||
export * as data from "./data";
|
||||
export {ExpressionError, ExpressionEvaluationError} from "./errors";
|
||||
export {Evaluator} from "./evaluator";
|
||||
export {wellKnownFunctions} from "./funcs";
|
||||
export {Lexer, Result} from "./lexer";
|
||||
export {Parser} from "./parser";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Lexer, Token, TokenType} from "./lexer.js";
|
||||
import {Lexer, Token, TokenType} from "./lexer";
|
||||
|
||||
describe("lexer", () => {
|
||||
const tests: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {StringData} from "./data/index.js";
|
||||
import {MAX_EXPRESSION_LENGTH} from "./errors.js";
|
||||
import {StringData} from "./data";
|
||||
import {MAX_EXPRESSION_LENGTH} from "./errors";
|
||||
|
||||
export enum TokenType {
|
||||
UNKNOWN,
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import {
|
||||
Binary,
|
||||
ContextAccess,
|
||||
Expr,
|
||||
FunctionCall,
|
||||
Grouping,
|
||||
IndexAccess,
|
||||
Literal,
|
||||
Logical,
|
||||
Star,
|
||||
Unary
|
||||
} from "./ast.js";
|
||||
import * as data from "./data/index.js";
|
||||
import {ErrorType, ExpressionError, MAX_PARSER_DEPTH} from "./errors.js";
|
||||
import {ParseContext, validateFunction} from "./funcs.js";
|
||||
import {FunctionInfo} from "./funcs/info.js";
|
||||
import {Token, TokenType} from "./lexer.js";
|
||||
import {Binary, ContextAccess, Expr, FunctionCall, Grouping, IndexAccess, Literal, Logical, Star, Unary} from "./ast";
|
||||
import * as data from "./data";
|
||||
import {ErrorType, ExpressionError, MAX_PARSER_DEPTH} from "./errors";
|
||||
import {ParseContext, validateFunction} from "./funcs";
|
||||
import {FunctionInfo} from "./funcs/info";
|
||||
import {Token, TokenType} from "./lexer";
|
||||
|
||||
export class Parser {
|
||||
private extContexts: Map<string, boolean>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {BooleanData, ExpressionData, NumberData, StringData} from "./data/index.js";
|
||||
import {coerceTypes, toUpperSpecial} from "./result.js";
|
||||
import {BooleanData, ExpressionData, NumberData, StringData} from "./data";
|
||||
import {coerceTypes, toUpperSpecial} from "./result";
|
||||
|
||||
describe("coerceTypes", () => {
|
||||
const tests: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as data from "./data/index.js";
|
||||
import * as data from "./data";
|
||||
|
||||
export function falsy(d: data.ExpressionData): boolean {
|
||||
switch (d.kind) {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import {Expr} from "./ast.js";
|
||||
import * as data from "./data/index.js";
|
||||
import {kindStr} from "./data/expressiondata.js";
|
||||
import {replacer} from "./data/replacer.js";
|
||||
import {reviver} from "./data/reviver.js";
|
||||
import {ExpressionError} from "./errors.js";
|
||||
import {Evaluator} from "./evaluator.js";
|
||||
import {Lexer, Result} from "./lexer.js";
|
||||
import {Parser} from "./parser.js";
|
||||
import {Expr} from "./ast";
|
||||
import * as data from "./data";
|
||||
import {kindStr} from "./data/expressiondata";
|
||||
import {replacer} from "./data/replacer";
|
||||
import {reviver} from "./data/reviver";
|
||||
import {ExpressionError} from "./errors";
|
||||
import {Evaluator} from "./evaluator";
|
||||
import {Lexer, Result} from "./lexer";
|
||||
import {Parser} from "./parser";
|
||||
|
||||
interface TestResult {
|
||||
value: data.ExpressionData;
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"noEmit": false,
|
||||
"outDir": "./dist",
|
||||
"skipLibCheck": true
|
||||
"outDir": "./dist"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,6 @@ The [package](https://www.npmjs.com/package/@actions/languageserver) contains Ty
|
||||
npm install @actions/languageserver
|
||||
```
|
||||
|
||||
To install the language server as a standalone CLI:
|
||||
|
||||
```bash
|
||||
npm install -g @actions/languageserver
|
||||
```
|
||||
|
||||
This makes the `actions-languageserver` command available globally.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage using `vscode-languageserver-node`
|
||||
@@ -100,150 +92,6 @@ const clientOptions: LanguageClientOptions = {
|
||||
const client = new LanguageClient("actions-language", "GitHub Actions Language Server", serverOptions, clientOptions);
|
||||
```
|
||||
|
||||
### Standalone CLI
|
||||
|
||||
After installing globally, you can run the language server directly:
|
||||
|
||||
```bash
|
||||
actions-languageserver --stdio
|
||||
```
|
||||
|
||||
This starts the language server using stdio transport, which is the standard way for editors to communicate with language servers.
|
||||
|
||||
### In Neovim
|
||||
|
||||
#### 1. Install the language server
|
||||
|
||||
```bash
|
||||
npm install -g @actions/languageserver
|
||||
```
|
||||
|
||||
#### 2. Set up filetype detection
|
||||
|
||||
Add this to your `init.lua` to detect GitHub Actions workflow files:
|
||||
|
||||
```lua
|
||||
vim.filetype.add({
|
||||
pattern = {
|
||||
[".*/%.github/workflows/.*%.ya?ml"] = "yaml.ghactions",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This sets the filetype to `yaml.ghactions` for YAML files in `.github/workflows/`, allowing you to keep separate YAML LSP configurations if needed.
|
||||
|
||||
#### 3. Create the LSP configuration
|
||||
|
||||
As of Neovim 0.11+ you can add this configuration in `~/.config/nvim/lsp/actionsls.lua`:
|
||||
|
||||
```lua
|
||||
local function get_github_token()
|
||||
local handle = io.popen("gh auth token 2>/dev/null")
|
||||
if not handle then return nil end
|
||||
local token = handle: read("*a"):gsub("%s+", "")
|
||||
handle:close()
|
||||
return token ~= "" and token or nil
|
||||
end
|
||||
|
||||
local function parse_github_remote(url)
|
||||
if not url or url == "" then return nil end
|
||||
|
||||
-- SSH format: [email protected]:owner/repo.git
|
||||
local owner, repo = url:match("git@github%.com:([^/]+)/([^/%.]+)")
|
||||
if owner and repo then
|
||||
return owner, repo: gsub("%.git$", "")
|
||||
end
|
||||
|
||||
-- HTTPS format: https://github.com/owner/repo.git
|
||||
owner, repo = url:match("github%.com/([^/]+)/([^/%.]+)")
|
||||
if owner and repo then
|
||||
return owner, repo:gsub("%.git$", "")
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function get_repo_info(owner, repo)
|
||||
local cmd = string.format(
|
||||
"gh repo view %s/%s --json id,owner --template '{{.id}}\t{{.owner.type}}' 2>/dev/null",
|
||||
owner,
|
||||
repo
|
||||
)
|
||||
local handle = io.popen(cmd)
|
||||
if not handle then return nil end
|
||||
local result = handle: read("*a"):gsub("%s+$", "")
|
||||
handle:close()
|
||||
|
||||
local id, owner_type = result:match("^(%d+)\t(.+)$")
|
||||
if id then
|
||||
return {
|
||||
id = tonumber(id),
|
||||
organizationOwned = owner_type == "Organization",
|
||||
}
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function get_repos_config()
|
||||
local handle = io.popen("git rev-parse --show-toplevel 2>/dev/null")
|
||||
if not handle then return nil end
|
||||
local git_root = handle: read("*a"):gsub("%s+", "")
|
||||
handle:close()
|
||||
|
||||
if git_root == "" then return nil end
|
||||
|
||||
handle = io.popen("git remote get-url origin 2>/dev/null")
|
||||
if not handle then return nil end
|
||||
local remote_url = handle:read("*a"):gsub("%s+", "")
|
||||
handle:close()
|
||||
|
||||
local owner, name = parse_github_remote(remote_url)
|
||||
if not owner or not name then return nil end
|
||||
|
||||
local info = get_repo_info(owner, name)
|
||||
|
||||
return {
|
||||
{
|
||||
id = info and info.id or 0,
|
||||
owner = owner,
|
||||
name = name,
|
||||
organizationOwned = info and info.organizationOwned or false,
|
||||
workspaceUri = "file://" .. git_root,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
cmd = { "actions-languageserver", "--stdio" },
|
||||
filetypes = { "yaml.ghactions" },
|
||||
root_markers = { ".git" },
|
||||
init_options = {
|
||||
-- Optional: provide a GitHub token and repo context for added functionality
|
||||
-- (e.g., repository-specific completions)
|
||||
sessionToken = get_github_token(),
|
||||
repos = get_repos_config(),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. Enable the LSP
|
||||
|
||||
Add to your `init.lua`:
|
||||
|
||||
```lua
|
||||
vim.lsp.enable('actionsls')
|
||||
```
|
||||
|
||||
#### 5. Verify it's working
|
||||
|
||||
Open any `.github/workflows/*.yml` file and run:
|
||||
|
||||
```vim
|
||||
:checkhealth vim.lsp
|
||||
```
|
||||
|
||||
You should see `actionsls` in the list of attached clients.
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](../CONTRIBUTING.md) at the root of the repository for general guidelines and recommendations.
|
||||
@@ -262,27 +110,6 @@ or to watch for changes
|
||||
npm run watch
|
||||
```
|
||||
|
||||
### Running the language server locally
|
||||
|
||||
After running
|
||||
|
||||
```bash
|
||||
npm run build:cli
|
||||
npm link
|
||||
```
|
||||
|
||||
`actions-languageserver` will be available globally. You can start it with:
|
||||
|
||||
```bash
|
||||
actions-languageserver --stdio
|
||||
```
|
||||
|
||||
Once linked you can also watch for changes and rebuild automatically:
|
||||
|
||||
```bash
|
||||
npm run watch:cli
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import "../dist/cli.bundle.cjs";
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.27",
|
||||
"version": "0.3.25",
|
||||
"description": "Language server for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -31,8 +31,7 @@
|
||||
"url": "https://github.com/actions/languageservices"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --build tsconfig.build.json && npm run build:cli",
|
||||
"build:cli": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/cli.bundle.cjs",
|
||||
"build": "tsc --build tsconfig.build.json",
|
||||
"clean": "rimraf dist",
|
||||
"format": "prettier --write '**/*.ts'",
|
||||
"format-check": "prettier --check '**/*.ts'",
|
||||
@@ -41,15 +40,11 @@
|
||||
"prepublishOnly": "npm run build && npm run test",
|
||||
"test": "NODE_OPTIONS=\"--experimental-vm-modules\" jest",
|
||||
"test-watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch",
|
||||
"watch": "tsc --build tsconfig.build.json --watch",
|
||||
"watch:cli": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/cli.bundle.cjs --watch"
|
||||
},
|
||||
"bin": {
|
||||
"actions-languageserver": "./bin/actions-languageserver"
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.27",
|
||||
"@actions/workflow-parser": "^0.3.27",
|
||||
"@actions/languageservice": "^0.3.25",
|
||||
"@actions/workflow-parser": "^0.3.25",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
@@ -60,14 +55,12 @@
|
||||
"node": ">= 18"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"bin/**/*"
|
||||
"dist/**/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.56.0",
|
||||
"@typescript-eslint/parser": "^5.56.0",
|
||||
"esbuild": "^0.27.1",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.27",
|
||||
"version": "0.3.25",
|
||||
"description": "Language service for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -47,8 +47,8 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.27",
|
||||
"@actions/workflow-parser": "^0.3.27",
|
||||
"@actions/expressions": "^0.3.25",
|
||||
"@actions/workflow-parser": "^0.3.25",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.8",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {actionIdentifier, parseActionReference as parse} from "./action.js";
|
||||
import {actionIdentifier, parseActionReference as parse} from "./action";
|
||||
|
||||
describe("parseActionReference", () => {
|
||||
it("basic action", () => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {CompletionItem, CompletionItemKind} from "vscode-languageserver-types";
|
||||
import {complete, getExpressionInput} from "./complete.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {complete, getExpressionInput} from "./complete";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {registerLogger} from "./log";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: (context: string) => {
|
||||
@@ -1110,7 +1110,7 @@ jobs:
|
||||
`;
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
|
||||
expect(result.map(x => x.label)).toEqual(["check_run_id", "container", "services", "status"]);
|
||||
expect(result.map(x => x.label)).toEqual(["container", "services", "status"]);
|
||||
});
|
||||
|
||||
it("job context is suggested within a job output", async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {complete} from "./complete.js";
|
||||
import {complete} from "./complete";
|
||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {CompletionItem, MarkupContent} from "vscode-languageserver-types";
|
||||
import {complete} from "./complete.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {complete} from "./complete";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
function mapResult(result: CompletionItem[]) {
|
||||
return result.map(x => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {MarkupContent, TextEdit} from "vscode-languageserver-types";
|
||||
import {complete} from "./complete.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config.js";
|
||||
import {complete} from "./complete";
|
||||
import {registerLogger} from "./log";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.length).toEqual(13);
|
||||
expect(result.length).toEqual(9);
|
||||
expect(result[0].label).toEqual("concurrency");
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.length).toEqual(30);
|
||||
expect(result.length).toEqual(21);
|
||||
});
|
||||
|
||||
it("string definition completion in sequence", async () => {
|
||||
@@ -243,7 +243,7 @@ jobs:
|
||||
runs-|`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result).toHaveLength(30);
|
||||
expect(result).toHaveLength(21);
|
||||
});
|
||||
|
||||
it("job key with comment afterwards", async () => {
|
||||
@@ -254,7 +254,7 @@ jobs:
|
||||
#`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result).toHaveLength(30);
|
||||
expect(result).toHaveLength(21);
|
||||
});
|
||||
|
||||
it("job key with other values afterwards", async () => {
|
||||
@@ -266,7 +266,7 @@ jobs:
|
||||
concurrency: 'group-name'`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result).toHaveLength(29);
|
||||
expect(result).toHaveLength(20);
|
||||
});
|
||||
|
||||
it("step key without space after colon", async () => {
|
||||
@@ -335,7 +335,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).toHaveLength(25);
|
||||
expect(result).toHaveLength(17);
|
||||
});
|
||||
|
||||
it("complete from behind a colon will replace it", async () => {
|
||||
@@ -348,7 +348,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
expect(result).toHaveLength(25);
|
||||
expect(result).toHaveLength(17);
|
||||
const textEdit = result[0].textEdit as TextEdit;
|
||||
expect(textEdit.range).toEqual({
|
||||
start: {line: 5, character: 4},
|
||||
@@ -510,104 +510,11 @@ jobs:
|
||||
expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["types: "]);
|
||||
});
|
||||
|
||||
it("adds newline and indentation for one-of in key mode", async () => {
|
||||
it("does not add : for one-of in key mode", async () => {
|
||||
const input = "on:\n check_run: ty|";
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// When completing a one-of property in key mode (after colon on same line),
|
||||
// insert newline + indentation + key + colon to create valid YAML structure
|
||||
expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["\n types: "]);
|
||||
});
|
||||
|
||||
it("handles mixed string and mapping completions for one-of in key mode", async () => {
|
||||
const input = "on: push\npermissions: |";
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// String values (read-all, write-all) should insert directly without newline
|
||||
expect(result.filter(x => x.label === "read-all").map(x => x.textEdit?.newText)).toEqual(["read-all"]);
|
||||
expect(result.filter(x => x.label === "write-all").map(x => x.textEdit?.newText)).toEqual(["write-all"]);
|
||||
|
||||
// Mapping keys with one-of types should insert with newline and indentation
|
||||
expect(result.filter(x => x.label === "actions").map(x => x.textEdit?.newText)).toEqual(["\n actions: "]);
|
||||
expect(result.filter(x => x.label === "contents").map(x => x.textEdit?.newText)).toEqual(["\n contents: "]);
|
||||
});
|
||||
|
||||
it("shows both simple and full syntax for null+mapping one-of", async () => {
|
||||
// check_run is a one-of: [null, mapping]. Show both:
|
||||
// - check_run (simple, just the key with colon)
|
||||
// - check_run (full syntax) (ready to add mapping keys)
|
||||
const input = "on:\n |";
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// Should have both check_run and check_run (full syntax)
|
||||
expect(result.some(x => x.label === "check_run")).toBe(true);
|
||||
expect(result.some(x => x.label === "check_run (full syntax)")).toBe(true);
|
||||
});
|
||||
|
||||
it("shows all three variants for scalar+sequence+mapping one-of", async () => {
|
||||
// runs-on is a one-of: [string, sequence, mapping]
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
|`;
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// Should have runs-on, runs-on (list), and runs-on (full syntax)
|
||||
expect(result.some(x => x.label === "runs-on")).toBe(true);
|
||||
expect(result.some(x => x.label === "runs-on (list)")).toBe(true);
|
||||
expect(result.some(x => x.label === "runs-on (full syntax)")).toBe(true);
|
||||
});
|
||||
|
||||
it("generates correct insertText for one-of variants in parent mode", async () => {
|
||||
// runs-on is a one-of: [string, sequence, mapping]
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
|`;
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// Scalar: just key with colon and space
|
||||
expect(result.find(x => x.label === "runs-on")?.textEdit?.newText).toEqual("runs-on: ");
|
||||
|
||||
// Sequence: key with colon, newline, and list item
|
||||
expect(result.find(x => x.label === "runs-on (list)")?.textEdit?.newText).toEqual("runs-on:\n - ");
|
||||
|
||||
// Mapping: key with colon, newline, and indentation for nested keys
|
||||
expect(result.find(x => x.label === "runs-on (full syntax)")?.textEdit?.newText).toEqual("runs-on:\n ");
|
||||
});
|
||||
|
||||
it("generates correct insertText for one-of variants in key mode", async () => {
|
||||
// concurrency is a one-of: [string, mapping] - testing key mode (after colon on same line)
|
||||
const input = "concurrency: |";
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// Scalar in key mode: newline + indented key + colon + space
|
||||
expect(result.find(x => x.label === "group")?.textEdit?.newText).toEqual("\n group: ");
|
||||
|
||||
// Boolean in key mode (cancel-in-progress): newline + indented key + colon + space
|
||||
expect(result.find(x => x.label === "cancel-in-progress")?.textEdit?.newText).toEqual("\n cancel-in-progress: ");
|
||||
});
|
||||
|
||||
it("uses base key as filterText for qualified one-of variants", async () => {
|
||||
// runs-on has multiple structural types, so variants get qualifiers
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
|`;
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
// Scalar: no qualifier, so no filterText needed
|
||||
expect(result.find(x => x.label === "runs-on")?.filterText).toBeUndefined();
|
||||
|
||||
// Sequence and mapping: qualified labels should filter on base key
|
||||
expect(result.find(x => x.label === "runs-on (list)")?.filterText).toEqual("runs-on");
|
||||
expect(result.find(x => x.label === "runs-on (full syntax)")?.filterText).toEqual("runs-on");
|
||||
expect(result.filter(x => x.label === "types").map(x => x.textEdit?.newText)).toEqual(["types"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,20 +11,20 @@ import {File} from "@actions/workflow-parser/workflows/file";
|
||||
import {FileProvider} from "@actions/workflow-parser/workflows/file-provider";
|
||||
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {CompletionItem, CompletionItemKind, CompletionItemTag, Range, TextEdit} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {getContext, Mode} from "./context-providers/default.js";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context.js";
|
||||
import {validatorFunctions} from "./expression-validation/functions.js";
|
||||
import {error} from "./log.js";
|
||||
import {isPotentiallyExpression} from "./utils/expression-detection.js";
|
||||
import {findToken} from "./utils/find-token.js";
|
||||
import {guessIndentation} from "./utils/indentation-guesser.js";
|
||||
import {mapRange} from "./utils/range.js";
|
||||
import {isPlaceholder, transform} from "./utils/transform.js";
|
||||
import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache.js";
|
||||
import {Value, ValueProviderConfig} from "./value-providers/config.js";
|
||||
import {defaultValueProviders} from "./value-providers/default.js";
|
||||
import {DefinitionValueMode, definitionValues} from "./value-providers/definition.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {getContext, Mode} from "./context-providers/default";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
||||
import {validatorFunctions} from "./expression-validation/functions";
|
||||
import {error} from "./log";
|
||||
import {isPotentiallyExpression} from "./utils/expression-detection";
|
||||
import {findToken} from "./utils/find-token";
|
||||
import {guessIndentation} from "./utils/indentation-guesser";
|
||||
import {mapRange} from "./utils/range";
|
||||
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 {DefinitionValueMode, definitionValues} from "./value-providers/definition";
|
||||
|
||||
export function getExpressionInput(input: string, pos: number): string {
|
||||
// Find start marker around the cursor position
|
||||
@@ -129,8 +129,6 @@ export async function complete(
|
||||
|
||||
const item: CompletionItem = {
|
||||
label: value.label,
|
||||
filterText: value.filterText,
|
||||
sortText: value.sortText,
|
||||
documentation: value.description && {
|
||||
kind: "markdown",
|
||||
value: value.description
|
||||
@@ -255,7 +253,7 @@ function getExpressionCompletionItems(
|
||||
|
||||
function filterAndSortCompletionOptions(options: Value[], existingValues?: Set<string>) {
|
||||
options = options.filter(x => !existingValues?.has(x.label));
|
||||
options.sort((a, b) => (a.sortText ?? a.label).localeCompare(b.sortText ?? b.label));
|
||||
options.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {DescriptionDictionary} from "@actions/expressions";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {Mode} from "./default.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {Mode} from "./default";
|
||||
|
||||
export type ContextProviderConfig = {
|
||||
getContext: (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {DescriptionDictionary} from "@actions/expressions";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {getContext, Mode} from "./default.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getContext, Mode} from "./default";
|
||||
|
||||
describe("getContext", () => {
|
||||
const emptyWorkflowContext: WorkflowContext = {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {Kind} from "@actions/expressions/data/expressiondata";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {ContextProviderConfig} from "./config.js";
|
||||
import {getDescription, RootContext} from "./descriptions.js";
|
||||
import {getEnvContext} from "./env.js";
|
||||
import {getGithubContext} from "./github.js";
|
||||
import {getInputsContext} from "./inputs.js";
|
||||
import {getJobContext} from "./job.js";
|
||||
import {getJobsContext} from "./jobs.js";
|
||||
import {getMatrixContext} from "./matrix.js";
|
||||
import {getNeedsContext} from "./needs.js";
|
||||
import {getSecretsContext} from "./secrets.js";
|
||||
import {getStepsContext} from "./steps.js";
|
||||
import {getStrategyContext} from "./strategy.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {ContextProviderConfig} from "./config";
|
||||
import {getDescription, RootContext} from "./descriptions";
|
||||
import {getEnvContext} from "./env";
|
||||
import {getGithubContext} from "./github";
|
||||
import {getInputsContext} from "./inputs";
|
||||
import {getJobContext} from "./job";
|
||||
import {getJobsContext} from "./jobs";
|
||||
import {getMatrixContext} from "./matrix";
|
||||
import {getNeedsContext} from "./needs";
|
||||
import {getSecretsContext} from "./secrets";
|
||||
import {getStepsContext} from "./steps";
|
||||
import {getStrategyContext} from "./strategy";
|
||||
|
||||
// ContextValue is the type of the value returned by a context provider
|
||||
// Null indicates that the context provider doesn't have any value to provide
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {isScalar, isString} from "@actions/workflow-parser";
|
||||
import {MappingToken} from "@actions/workflow-parser/templates/tokens/mapping-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getEnvContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {DescriptionDictionary} from "@actions/expressions";
|
||||
import {getEventPayload, getSupportedEventTypes} from "./eventPayloads.js";
|
||||
import {getEventPayload, getSupportedEventTypes} from "./eventPayloads";
|
||||
|
||||
describe("eventPayloads", () => {
|
||||
describe("getSupportedEventTypes", () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {DescriptionDictionary, isDescriptionDictionary} from "@actions/expressions";
|
||||
import {testGetWorkflowContext} from "../test-utils/test-workflow-context.js";
|
||||
import {Mode} from "./default.js";
|
||||
import {getGithubContext} from "./github.js";
|
||||
import {DescriptionDictionary, isDescriptionDictionary} from "@actions/expressions/.";
|
||||
import {testGetWorkflowContext} from "../test-utils/test-workflow-context";
|
||||
import {Mode} from "./default";
|
||||
import {getGithubContext} from "./github";
|
||||
|
||||
describe("github context", () => {
|
||||
it("single event", async () => {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {data, DescriptionDictionary, isDescriptionDictionary} from "@actions/expressions";
|
||||
import {ExpressionData} from "@actions/expressions/data/expressiondata";
|
||||
import {TypesFilterConfig} from "@actions/workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {Mode} from "./default.js";
|
||||
import {getDescription} from "./descriptions.js";
|
||||
import {getEventPayload, getSupportedEventTypes} from "./events/eventPayloads.js";
|
||||
import {getInputsContext} from "./inputs.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {Mode} from "./default";
|
||||
import {getDescription} from "./descriptions";
|
||||
import {getEventPayload, getSupportedEventTypes} from "./events/eventPayloads";
|
||||
import {getInputsContext} from "./inputs";
|
||||
|
||||
export function getGithubContext(workflowContext: WorkflowContext, mode: Mode): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {InputConfig} from "@actions/workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getInputsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {isMapping, isSequence} from "@actions/workflow-parser";
|
||||
import {MappingToken} from "@actions/workflow-parser/templates/tokens/mapping-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getJobContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#job-context
|
||||
@@ -35,9 +35,6 @@ export function getJobContext(workflowContext: WorkflowContext): DescriptionDict
|
||||
// Status
|
||||
jobContext.add("status", new data.Null());
|
||||
|
||||
// Check run ID
|
||||
jobContext.add("check_run_id", new data.Null());
|
||||
|
||||
return jobContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {StringData} from "@actions/expressions/data/string";
|
||||
import {MappingToken} from "@actions/workflow-parser/templates/tokens/mapping-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {getDescription} from "./descriptions.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getDescription} from "./descriptions";
|
||||
|
||||
export function getJobsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#jobs-context
|
||||
|
||||
@@ -6,9 +6,9 @@ import {MappingToken} from "@actions/workflow-parser/templates/tokens/mapping-to
|
||||
import {SequenceToken} from "@actions/workflow-parser/templates/tokens/sequence-token";
|
||||
import {StringToken} from "@actions/workflow-parser/templates/tokens/string-token";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {Mode} from "./default.js";
|
||||
import {getMatrixContext} from "./matrix.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {Mode} from "./default";
|
||||
import {getMatrixContext} from "./matrix";
|
||||
|
||||
type MatrixMap = {
|
||||
[key: string]: Array<string> | Array<{[key: string]: string}>;
|
||||
|
||||
@@ -3,8 +3,8 @@ import {isBasicExpression, isMapping, isSequence, isString} from "@actions/workf
|
||||
import {KeyValuePair} from "@actions/workflow-parser/templates/tokens/key-value-pair";
|
||||
import {MappingToken} from "@actions/workflow-parser/templates/tokens/mapping-token";
|
||||
import {SequenceToken} from "@actions/workflow-parser/templates/tokens/sequence-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {ContextValue, Mode} from "./default.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {ContextValue, Mode} from "./default";
|
||||
|
||||
export function getMatrixContext(workflowContext: WorkflowContext, mode: Mode): ContextValue {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#matrix-context
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {DescriptionDictionary} from "@actions/expressions";
|
||||
import {StringData} from "@actions/expressions/data/string";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {testGetWorkflowContext} from "../test-utils/test-workflow-context.js";
|
||||
import {getNeedsContext} from "./needs.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {testGetWorkflowContext} from "../test-utils/test-workflow-context";
|
||||
import {getNeedsContext} from "./needs";
|
||||
|
||||
describe("needs context", () => {
|
||||
describe("invalid workflow context", () => {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {isMapping, isScalar, isString} from "@actions/workflow-parser";
|
||||
import {isJob} from "@actions/workflow-parser/model/type-guards";
|
||||
import {WorkflowJob} from "@actions/workflow-parser/model/workflow-template";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getNeedsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {StringData} from "@actions/expressions/data/string";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {Mode} from "./default.js";
|
||||
import {getDescription} from "./descriptions.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {Mode} from "./default";
|
||||
import {getDescription} from "./descriptions";
|
||||
|
||||
export function getSecretsContext(workflowContext: WorkflowContext, mode: Mode): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import {DescriptionDictionary, isDescriptionDictionary} from "@actions/expressions";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {getStepsContext} from "./steps.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getStepsContext} from "./steps";
|
||||
|
||||
function createWorkflowContext(stepIds: string[], currentStepId?: string): WorkflowContext {
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {Step} from "@actions/workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {getDescription} from "./descriptions.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getDescription} from "./descriptions";
|
||||
|
||||
export function getStepsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
@@ -5,8 +5,8 @@ import {MappingToken} from "@actions/workflow-parser/templates/tokens/mapping-to
|
||||
import {NumberToken} from "@actions/workflow-parser/templates/tokens/number-token";
|
||||
import {StringToken} from "@actions/workflow-parser/templates/tokens/string-token";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {getStrategyContext} from "./strategy.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getStrategyContext} from "./strategy";
|
||||
|
||||
function stringToToken(value: string) {
|
||||
return new StringToken(undefined, undefined, value, undefined);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {isMapping, isScalar, isString} from "@actions/workflow-parser";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {scalarToData} from "../utils/scalar-to-data.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {scalarToData} from "../utils/scalar-to-data";
|
||||
|
||||
// Default strategy values when no strategy block is defined
|
||||
const DEFAULT_STRATEGY = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ActionStep, RunStep} from "@actions/workflow-parser/model/workflow-template";
|
||||
import {testGetWorkflowContext} from "../test-utils/test-workflow-context.js";
|
||||
import {testGetWorkflowContext} from "../test-utils/test-workflow-context";
|
||||
|
||||
describe("getWorkflowContext", () => {
|
||||
it("context for workflow", async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {isMapping, isString} from "@actions/workflow-parser";
|
||||
import {DESCRIPTION} from "@actions/workflow-parser/templates/template-constants";
|
||||
import {WorkflowContext} from "../context/workflow-context.js";
|
||||
import {TokenResult} from "../utils/find-token.js";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {TokenResult} from "../utils/find-token";
|
||||
|
||||
export function isReusableWorkflowJobInput(tokenResult: TokenResult): boolean {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {documentLinks} from "./document-links.js";
|
||||
import {createDocument} from "./test-utils/document.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {documentLinks} from "./document-links";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
|
||||
@@ -5,9 +5,9 @@ import {parseFileReference} from "@actions/workflow-parser/workflows/file-refere
|
||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {DocumentLink} from "vscode-languageserver-types";
|
||||
import * as vscodeURI from "vscode-uri";
|
||||
import {actionUrl, parseActionReference} from "./action.js";
|
||||
import {mapRange} from "./utils/range.js";
|
||||
import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache.js";
|
||||
import {actionUrl, parseActionReference} from "./action";
|
||||
import {mapRange} from "./utils/range";
|
||||
import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache";
|
||||
|
||||
export async function documentLinks(document: TextDocument, workspace: string | undefined): Promise<DocumentLink[]> {
|
||||
const file: File = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {complete} from "./complete.js";
|
||||
import {hover} from "./hover.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {complete} from "./complete";
|
||||
import {hover} from "./hover";
|
||||
import {registerLogger} from "./log";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
@@ -21,21 +21,17 @@ describe("end-to-end", () => {
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result.length).toEqual(13);
|
||||
expect(result.length).toEqual(9);
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toEqual([
|
||||
"concurrency",
|
||||
"concurrency (full syntax)",
|
||||
"defaults",
|
||||
"description",
|
||||
"env",
|
||||
"jobs",
|
||||
"name",
|
||||
"on",
|
||||
"on (list)",
|
||||
"on (full syntax)",
|
||||
"permissions",
|
||||
"permissions (full syntax)",
|
||||
"run-name"
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {parseWorkflow} from "@actions/workflow-parser";
|
||||
import {File} from "@actions/workflow-parser/workflows/file";
|
||||
import {nullTrace} from "../nulltrace.js";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position.js";
|
||||
import {findToken} from "../utils/find-token.js";
|
||||
import {ExpressionPos, mapToExpressionPos} from "./expression-pos.js";
|
||||
import {nullTrace} from "../nulltrace";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position";
|
||||
import {findToken} from "../utils/find-token";
|
||||
import {ExpressionPos, mapToExpressionPos} from "./expression-pos";
|
||||
|
||||
describe("mapToExpressionPos", () => {
|
||||
it("simple expression", () => {
|
||||
|
||||
@@ -3,8 +3,8 @@ import {ensureStatusFunction} from "@actions/workflow-parser/model/converter/if-
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {isBasicExpression, isString} from "@actions/workflow-parser/templates/tokens/type-guards";
|
||||
import {Position, Range as LSPRange} from "vscode-languageserver-textdocument";
|
||||
import {mapRange} from "../utils/range.js";
|
||||
import {posWithinRange} from "./pos-range.js";
|
||||
import {mapRange} from "../utils/range";
|
||||
import {posWithinRange} from "./pos-range";
|
||||
|
||||
export type ExpressionPos = {
|
||||
/** The expression that includes the position */
|
||||
|
||||
@@ -2,13 +2,13 @@ import {data, DescriptionDictionary, Lexer, Parser} from "@actions/expressions";
|
||||
import {convertWorkflowTemplate, parseWorkflow} from "@actions/workflow-parser";
|
||||
import {ErrorPolicy} from "@actions/workflow-parser/model/convert";
|
||||
import {File} from "@actions/workflow-parser/workflows/file";
|
||||
import {ContextProviderConfig} from "../context-providers/config.js";
|
||||
import {getContext, Mode} from "../context-providers/default.js";
|
||||
import {getWorkflowContext} from "../context/workflow-context.js";
|
||||
import {validatorFunctions} from "../expression-validation/functions.js";
|
||||
import {nullTrace} from "../nulltrace.js";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position.js";
|
||||
import {HoverVisitor} from "./visitor.js";
|
||||
import {ContextProviderConfig} from "../context-providers/config";
|
||||
import {getContext, Mode} from "../context-providers/default";
|
||||
import {getWorkflowContext} from "../context/workflow-context";
|
||||
import {validatorFunctions} from "../expression-validation/functions";
|
||||
import {nullTrace} from "../nulltrace";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position";
|
||||
import {HoverVisitor} from "./visitor";
|
||||
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: (context: string) => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "@actions/expressions/ast";
|
||||
import {FunctionDefinition} from "@actions/expressions/funcs/info";
|
||||
import {Pos, Range} from "@actions/expressions/lexer";
|
||||
import {posWithinRange} from "./pos-range.js";
|
||||
import {posWithinRange} from "./pos-range";
|
||||
|
||||
export type HoverResult =
|
||||
| undefined
|
||||
|
||||
@@ -3,7 +3,7 @@ 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.js";
|
||||
import {AccessError} from "./error-dictionary";
|
||||
|
||||
export type ValidationError = {
|
||||
message: string;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {data, DescriptionDictionary} from "@actions/expressions";
|
||||
import {format} from "@actions/expressions/funcs/format";
|
||||
import {Hover} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {hover} from "./hover.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {hover} from "./hover";
|
||||
import {registerLogger} from "./log";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: (context: string) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {hover} from "./hover.js";
|
||||
import {testHoverConfig} from "./hover.test.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {hover} from "./hover";
|
||||
import {testHoverConfig} from "./hover.test";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {isString} from "@actions/workflow-parser";
|
||||
import {DescriptionProvider, hover, HoverConfig} from "./hover.js";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position.js";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {DescriptionProvider, hover, HoverConfig} from "./hover";
|
||||
import {getPositionFromCursor} from "./test-utils/cursor-position";
|
||||
import {testFileProvider} from "./test-utils/test-file-provider";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
export function testHoverConfig(tokenValue: string, tokenKey: string, description?: string) {
|
||||
return {
|
||||
|
||||
@@ -9,21 +9,21 @@ import {File} from "@actions/workflow-parser/workflows/file";
|
||||
import {FileProvider} from "@actions/workflow-parser/workflows/file-provider";
|
||||
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {Hover} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {getContext, Mode} from "./context-providers/default.js";
|
||||
import {getFunctionDescription} from "./context-providers/descriptions.js";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {getContext, Mode} from "./context-providers/default";
|
||||
import {getFunctionDescription} from "./context-providers/descriptions";
|
||||
import {getWorkflowContext, WorkflowContext} from "./context/workflow-context";
|
||||
import {
|
||||
getReusableWorkflowInputDescription,
|
||||
isReusableWorkflowJobInput
|
||||
} from "./description-providers/reusable-job-inputs.js";
|
||||
import {ExpressionPos, mapToExpressionPos} from "./expression-hover/expression-pos.js";
|
||||
import {HoverVisitor} from "./expression-hover/visitor.js";
|
||||
import {info} from "./log.js";
|
||||
import {isPotentiallyExpression} from "./utils/expression-detection.js";
|
||||
import {findToken} from "./utils/find-token.js";
|
||||
import {mapRange} from "./utils/range.js";
|
||||
import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache.js";
|
||||
} from "./description-providers/reusable-job-inputs";
|
||||
import {ExpressionPos, mapToExpressionPos} from "./expression-hover/expression-pos";
|
||||
import {HoverVisitor} from "./expression-hover/visitor";
|
||||
import {info} from "./log";
|
||||
import {isPotentiallyExpression} from "./utils/expression-detection";
|
||||
import {findToken} from "./utils/find-token";
|
||||
import {mapRange} from "./utils/range";
|
||||
import {fetchOrConvertWorkflowTemplate, fetchOrParseWorkflow} from "./utils/workflow-cache";
|
||||
|
||||
export type HoverConfig = {
|
||||
descriptionProvider?: DescriptionProvider;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export {complete} from "./complete.js";
|
||||
export {ContextProviderConfig} from "./context-providers/config.js";
|
||||
export {documentLinks} from "./document-links.js";
|
||||
export {hover} from "./hover.js";
|
||||
export {Logger, LogLevel, registerLogger, setLogLevel} from "./log.js";
|
||||
export {validate, ValidationConfig, ActionsMetadataProvider} from "./validate.js";
|
||||
export {ValueProviderConfig, ValueProviderKind} from "./value-providers/config.js";
|
||||
export {complete} from "./complete";
|
||||
export {ContextProviderConfig} from "./context-providers/config";
|
||||
export {documentLinks} from "./document-links";
|
||||
export {hover} from "./hover";
|
||||
export {Logger, LogLevel, registerLogger, setLogLevel} from "./log";
|
||||
export {validate, ValidationConfig, ActionsMetadataProvider} from "./validate";
|
||||
export {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {clearCache} from "../utils/workflow-cache.js";
|
||||
import {getPositionFromCursor} from "./cursor-position.js";
|
||||
import {clearCache} from "../utils/workflow-cache";
|
||||
import {getPositionFromCursor} from "./cursor-position";
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Position, TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {createDocument} from "./document.js";
|
||||
import {createDocument} from "./document";
|
||||
|
||||
/**
|
||||
* Calculates the position of the cursor and the document without that cursor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Logger} from "../log.js";
|
||||
import {Logger} from "../log";
|
||||
|
||||
export class TestLogger implements Logger {
|
||||
error(message: string): void {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {convertWorkflowTemplate, parseWorkflow, WorkflowTemplate} from "@actions/workflow-parser";
|
||||
import {getWorkflowContext, WorkflowContext} from "../context/workflow-context.js";
|
||||
import {nullTrace} from "../nulltrace.js";
|
||||
import {findToken} from "../utils/find-token.js";
|
||||
import {getPositionFromCursor} from "./cursor-position.js";
|
||||
import {testFileProvider} from "./test-file-provider.js";
|
||||
import {getWorkflowContext, WorkflowContext} from "../context/workflow-context";
|
||||
import {nullTrace} from "../nulltrace";
|
||||
import {findToken} from "../utils/find-token";
|
||||
import {getPositionFromCursor} from "./cursor-position";
|
||||
import {testFileProvider} from "./test-file-provider";
|
||||
|
||||
export async function testGetWorkflowContext(input: string): Promise<WorkflowContext> {
|
||||
const [textDocument, pos] = getPositionFromCursor(input);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {isScalar, parseWorkflow} from "@actions/workflow-parser";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {TokenType} from "@actions/workflow-parser/templates/tokens/types";
|
||||
import {nullTrace} from "../nulltrace.js";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position.js";
|
||||
import {findToken} from "./find-token.js";
|
||||
import {nullTrace} from "../nulltrace";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position";
|
||||
import {findToken} from "./find-token";
|
||||
|
||||
type testTokenInfo = [definitionKey: string | null, tokenType: TokenType, literalValue?: string];
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position.js";
|
||||
import {transform} from "./transform.js";
|
||||
import {getPositionFromCursor} from "../test-utils/cursor-position";
|
||||
import {transform} from "./transform";
|
||||
|
||||
describe("transform", () => {
|
||||
it("adds : at end of line", () => {
|
||||
|
||||
@@ -4,8 +4,8 @@ import {TemplateContext} from "@actions/workflow-parser/templates/template-conte
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {File} from "@actions/workflow-parser/workflows/file";
|
||||
|
||||
import {CompletionConfig} from "../complete.js";
|
||||
import {nullTrace} from "../nulltrace.js";
|
||||
import {CompletionConfig} from "../complete";
|
||||
import {nullTrace} from "../nulltrace";
|
||||
|
||||
const parsedWorkflowCache = new Map<string, ParseWorkflowResult>();
|
||||
const workflowTemplateCache = new Map<string, WorkflowTemplate>();
|
||||
|
||||
@@ -4,9 +4,9 @@ import {Step} from "@actions/workflow-parser/model/workflow-template";
|
||||
import {ScalarToken} from "@actions/workflow-parser/templates/tokens/scalar-token";
|
||||
import {TemplateToken} from "@actions/workflow-parser/templates/tokens/template-token";
|
||||
import {Diagnostic, DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {parseActionReference} from "./action.js";
|
||||
import {mapRange} from "./utils/range.js";
|
||||
import {ValidationConfig} from "./validate.js";
|
||||
import {parseActionReference} from "./action";
|
||||
import {mapRange} from "./utils/range";
|
||||
import {ValidationConfig} from "./validate";
|
||||
|
||||
export async function validateAction(
|
||||
diagnostics: Diagnostic[],
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {ActionMetadata, ActionReference} from "./action.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {createDocument} from "./test-utils/document.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {validate, ValidationConfig} from "./validate.js";
|
||||
import {ValueProviderKind} from "./value-providers/config.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {ActionMetadata, ActionReference} from "./action";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {validate, ValidationConfig} from "./validate";
|
||||
import {ValueProviderKind} from "./value-providers/config";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {validate} from "./validate.js";
|
||||
import {createDocument} from "./test-utils/document.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {validate} from "./validate";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {createDocument} from "./test-utils/document.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {validate} from "./validate.js";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {DescriptionDictionary} from "@actions/expressions";
|
||||
import {DescriptionDictionary} from "@actions/expressions/.";
|
||||
import {DiagnosticSeverity} from "vscode-languageserver-types";
|
||||
import {ContextProviderConfig} from "./context-providers/config.js";
|
||||
import {registerLogger} from "./log.js";
|
||||
import {createDocument} from "./test-utils/document.js";
|
||||
import {TestLogger} from "./test-utils/logger.js";
|
||||
import {clearCache} from "./utils/workflow-cache.js";
|
||||
import {validate, ValidationConfig} from "./validate.js";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {registerLogger} from "./log";
|
||||
import {createDocument} from "./test-utils/document";
|
||||
import {TestLogger} from "./test-utils/logger";
|
||||
import {clearCache} from "./utils/workflow-cache";
|
||||
import {validate, ValidationConfig} from "./validate";
|
||||
|
||||
registerLogger(new TestLogger());
|
||||
|
||||
@@ -417,21 +417,6 @@ jobs:
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("job.check_run_id", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo \${{ job.check_run_id }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("job.services.<service_id>", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user