Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure interpreter to use current directory as workdir #599

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions apps/interpreter/src/current-dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg
//
// SPDX-License-Identifier: AGPL-3.0-only

/**
* Gets the current directory path.
* We use this method for emulating tests in other directory to configure the correct working directory.
*/
export function getCurrentDir(): string {
return process.cwd();
}
36 changes: 21 additions & 15 deletions apps/interpreter/src/examples-smoke-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@
// SPDX-License-Identifier: AGPL-3.0-only

import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { processExitMockImplementation } from '@jvalue/jayvee-execution/test';
import {
PostgresLoaderExecutorMock,
SQLiteLoaderExecutorMock,
} from '@jvalue/jayvee-extensions/rdbms/test';
import { HttpExtractorExecutorMock } from '@jvalue/jayvee-extensions/std/test';
import {
createJayveeServices,
initializeWorkspace,
} from '@jvalue/jayvee-language-server';
import { NodeFileSystem } from 'langium/node';
import nock from 'nock';
import { type MockInstance, vi } from 'vitest';

Expand Down Expand Up @@ -44,9 +40,22 @@ vi.mock('sqlite3', () => {
};
});

describe('jv example smoke tests', () => {
const baseDir = path.resolve(__dirname, '../../../example/');
// simulate as if we were starting the jv cli in the example dir
vi.mock('./current-dir', () => {
const currentDirMock = () =>
path.join(
path.dirname(fileURLToPath(import.meta.url)), // relative to this test file
'..',
'..',
'..',
'example',
);
return {
getCurrentDir: currentDirMock,
};
});

describe('jv example smoke tests', () => {
const defaultOptions: RunOptions = {
pipeline: '.*',
env: new Map<string, string>(),
Expand All @@ -61,10 +70,7 @@ describe('jv example smoke tests', () => {
let postgresLoaderMock: PostgresLoaderExecutorMock;
let sqliteLoaderMock: SQLiteLoaderExecutorMock;

beforeAll(async () => {
const services = createJayveeServices(NodeFileSystem).Jayvee;
await initializeWorkspace(services);

beforeAll(() => {
exitSpy = vi
.spyOn(process, 'exit')
.mockImplementation(processExitMockImplementation);
Expand Down Expand Up @@ -99,7 +105,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'cars.jv'), {
await runAction('cars.jv', {
...defaultOptions,
});

Expand Down Expand Up @@ -131,7 +137,7 @@ describe('jv example smoke tests', () => {
postgresLoaderMock.setup();
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'electric-vehicles.jv'), {
await runAction('electric-vehicles.jv', {
...defaultOptions,
env: new Map<string, string>([
['DB_HOST', 'mock'],
Expand Down Expand Up @@ -194,7 +200,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'gtfs-rt.jv'), {
await runAction('gtfs-rt.jv', {
...defaultOptions,
});

Expand Down Expand Up @@ -222,7 +228,7 @@ describe('jv example smoke tests', () => {
});
sqliteLoaderMock.setup();

await runAction(path.resolve(baseDir, 'gtfs-static.jv'), {
await runAction('gtfs-static.jv', {
...defaultOptions,
});

Expand Down
36 changes: 29 additions & 7 deletions apps/interpreter/src/parse-only.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';

import { type JayveeInterpreter } from '@jvalue/jayvee-interpreter-lib';

Expand All @@ -20,11 +21,27 @@ const interpreterMock: JayveeInterpreter = {

vi.stubGlobal('DefaultJayveeInterpreter', interpreterMock);

const dirPathOfThisTest = path.dirname(fileURLToPath(import.meta.url));
const pathExamplesRelativeToThisTest = path.join('..', '..', '..', 'example');

// simulate as if we were starting the jv cli in the example dir
vi.mock('./current-dir', () => {
const currentDirMock = () =>
path.join(dirPathOfThisTest, pathExamplesRelativeToThisTest);
return {
getCurrentDir: currentDirMock,
};
});

describe('Parse Only', () => {
const pathToValidModel = path.resolve(__dirname, '../../../example/cars.jv');
const pathToInvalidModel = path.resolve(
__dirname,
'../test/assets/broken-model.jv',
const pathToValidModelFromExamplesDir = 'cars.jv';
const pathToInvalidModelFromExamplesDir = path.join(
'..',
'apps',
'interpreter',
'test',
'assets',
'broken-model.jv',
);

const defaultOptions: RunOptions = {
Expand All @@ -51,7 +68,7 @@ describe('Parse Only', () => {

it('should exit with 0 on a valid option', async () => {
await expect(
runAction(pathToValidModel, {
runAction(pathToValidModelFromExamplesDir, {
...defaultOptions,
parseOnly: true,
}),
Expand All @@ -62,10 +79,15 @@ describe('Parse Only', () => {
});

it('should exit with 1 on error', async () => {
expect(fs.existsSync(pathToInvalidModel)).toBe(true);
const modelPathRelativeToThisTest = path.join(
dirPathOfThisTest,
pathExamplesRelativeToThisTest,
pathToInvalidModelFromExamplesDir,
);
expect(fs.existsSync(modelPathRelativeToThisTest)).toBe(true);

await expect(
runAction(pathToInvalidModel, {
runAction(pathToInvalidModelFromExamplesDir, {
...defaultOptions,
parseOnly: true,
}),
Expand Down
12 changes: 9 additions & 3 deletions apps/interpreter/src/run-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0-only

import path from 'node:path';
import process from 'node:process';

import {
Expand All @@ -16,6 +17,7 @@ import {
type JayveeServices,
} from '@jvalue/jayvee-language-server';

import { getCurrentDir } from './current-dir';
import { parsePipelineMatcherRegExp, parseRunOptions } from './run-options';

export async function runAction(
Expand All @@ -33,20 +35,24 @@ export async function runAction(
return process.exit(ExitCode.FAILURE);
}

const currentDir = getCurrentDir();
const workingDir = currentDir;
const absoluteFilePath = path.join(currentDir, filePath);

const interpreter = new DefaultJayveeInterpreter({
pipelineMatcher: (pipelineDefinition) =>
pipelineRegExp.test(pipelineDefinition.name),
env: options.env,
debug: options.debug,
debugGranularity: options.debugGranularity,
debugTarget: options.debugTarget,
});
}).addWorkspace(workingDir);

if (options.parseOnly === true) {
return await runParseOnly(filePath, interpreter);
return await runParseOnly(absoluteFilePath, interpreter);
}

const exitCode = await interpreter.interpretFile(filePath);
const exitCode = await interpreter.interpretFile(absoluteFilePath);
process.exit(exitCode);
}

Expand Down
31 changes: 30 additions & 1 deletion libs/interpreter-lib/src/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

// eslint-disable-next-line unicorn/prefer-node-protocol
import { strict as assert } from 'assert';
import path from 'node:path';

import {
type DebugGranularity,
Expand All @@ -28,9 +29,11 @@ import {
type RuntimeParameterProvider,
type WrapperFactoryProvider,
createJayveeServices,
initializeWorkspace,
internalValueToString,
} from '@jvalue/jayvee-language-server';
import chalk from 'chalk';
import { type WorkspaceFolder } from 'langium';
import { NodeFileSystem } from 'langium/node';

import { LoggerFactory } from './logging';
Expand Down Expand Up @@ -80,7 +83,7 @@ export interface JayveeInterpreter {
* Parses a model without executing it.
* Also sets up the environment so that the model can be properly executed.
*
* @param extractAstNodeFn method that extracts the AST node; should also initialize the workspace correctly.
* @param extractAstNodeFn method that extracts the AST node
* @returns the parsed Jayvee model, or undefined on failure.
*/
parseModel(
Expand All @@ -94,6 +97,8 @@ export interface JayveeInterpreter {
export class DefaultJayveeInterpreter implements JayveeInterpreter {
private readonly services: JayveeServices;
private readonly loggerFactory: LoggerFactory;
private readonly workspaces: WorkspaceFolder[] = [];
private isWorkspaceInitialized = false;

constructor(private readonly options: InterpreterOptions) {
this.services = createJayveeServices(NodeFileSystem).Jayvee;
Expand All @@ -102,7 +107,18 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
this.loggerFactory = new LoggerFactory(options.debug);
}

addWorkspace(uri: string): DefaultJayveeInterpreter {
this.isWorkspaceInitialized = false;
this.workspaces.push({
name: 'projectRoot',
uri: path.resolve(uri),
});
return this;
}

async interpretModel(model: JayveeModel): Promise<ExitCode> {
await this.prepareInterpretation();

const interpretationExitCode = await this.interpretJayveeModel(
model,
new StdExecExtension(),
Expand All @@ -112,6 +128,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
}

async interpretFile(filePath: string): Promise<ExitCode> {
await this.prepareInterpretation();

const extractAstNodeFn = async (
services: JayveeServices,
loggerFactory: LoggerFactory,
Expand All @@ -131,6 +149,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
}

async interpretString(modelString: string): Promise<ExitCode> {
await this.prepareInterpretation();

const extractAstNodeFn = async (
services: JayveeServices,
loggerFactory: LoggerFactory,
Expand All @@ -155,6 +175,8 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
loggerFactory: LoggerFactory,
) => Promise<JayveeModel>,
): Promise<JayveeModel | undefined> {
await this.prepareInterpretation();

try {
const model = await extractAstNodeFn(this.services, this.loggerFactory);
return model;
Expand Down Expand Up @@ -274,6 +296,13 @@ export class DefaultJayveeInterpreter implements JayveeInterpreter {
logExecutionDuration(startTime, executionContext.logger);
return ExitCode.SUCCESS;
}

private async prepareInterpretation(): Promise<void> {
if (!this.isWorkspaceInitialized) {
await initializeWorkspace(this.services, this.workspaces);
this.isWorkspaceInitialized = true;
}
}
}

export function logPipelineOverview(
Expand Down
9 changes: 0 additions & 9 deletions libs/interpreter-lib/src/parsing-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,6 @@ export async function extractDocumentFromFile(
return Promise.reject(ExitCode.FAILURE);
}

const workingDirPath = path.dirname(filePath);

await initializeWorkspace(services, [
{
name: 'projectRoot',
uri: path.resolve(workingDirPath),
},
]);

const document = services.shared.workspace.LangiumDocuments.getDocument(
URI.file(path.resolve(filePath)),
);
Expand Down
Loading