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

Env data in output #665

Merged
merged 22 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a169f74
feat(types): implement ContextWithExec in manifest
narekhovhannisyan Apr 24, 2024
229bd4c
feat(types): init environment lib types
narekhovhannisyan Apr 24, 2024
2898716
fix(lib): export raw content from load
narekhovhannisyan Apr 24, 2024
f10f9df
feat(lib): implement environment data collector
narekhovhannisyan Apr 24, 2024
e2f9ca9
feat(src): implement env data injection in index
narekhovhannisyan Apr 24, 2024
f1fb636
test(lib): update load test for raw content
narekhovhannisyan Apr 24, 2024
dbd99f5
test(lib): implement envirnment units
narekhovhannisyan Apr 24, 2024
83b1ba9
feat(util): implement custom os checker
narekhovhannisyan Apr 30, 2024
a6b714d
feat(util): add exec promise as helper
narekhovhannisyan Apr 30, 2024
fec7c4f
fix(types): make context with exec os string
narekhovhannisyan Apr 30, 2024
1b8948c
feat(lib): use updated strategy for env
narekhovhannisyan Apr 30, 2024
bdf8c72
test(util): implement cases for os checker
narekhovhannisyan Apr 30, 2024
bdd8e10
fix(package): update package lock file
narekhovhannisyan Apr 30, 2024
8ba7554
fix(types): add if version to env
narekhovhannisyan Apr 30, 2024
b669645
fix(lib): add if version to env
narekhovhannisyan Apr 30, 2024
daee335
fix(src): remove if version from index
narekhovhannisyan Apr 30, 2024
d6696a2
feat(lib): handle github dependencies in environment
narekhovhannisyan May 1, 2024
849c9ff
revert(util): drop redundant console.log
narekhovhannisyan May 1, 2024
1fa8416
test(util): drop redundant console.log from json
narekhovhannisyan May 1, 2024
679fbad
refactor(util): remove unused errors
narekhovhannisyan May 3, 2024
fcf4aeb
test(mocks): fix writefile
narekhovhannisyan May 3, 2024
965bfae
feat(lib): add UTC string to date time
narekhovhannisyan May 3, 2024
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
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions src/__tests__/unit/lib/environment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import {injectEnvironment} from '../../../lib/environment';

describe('lib/envirnoment: ', () => {
describe('injectEnvironment(): ', () => {
const context = {};

it('checks response to have `execution` property.', async () => {
// @ts-ignore
const response = await injectEnvironment(context);
expect(response).toHaveProperty('execution');
});

it('checks `execution` to have `command` and `environment` props.', async () => {
// @ts-ignore
const response = await injectEnvironment(context);
const {execution} = response;

expect(execution).toHaveProperty('command');
expect(execution).toHaveProperty('environment');
});

it('checks environment response type.', async () => {
// @ts-ignore
const response = await injectEnvironment(context);
const {environment} = response.execution;

expect(typeof environment['date-time']).toEqual('string');
expect(Array.isArray(environment.dependencies)).toBeTruthy();
expect(typeof environment['node-version']).toEqual('string');
expect(typeof environment.os).toEqual('string');
expect(typeof environment['os-version']).toEqual('string');
});
});
});
4 changes: 2 additions & 2 deletions src/__tests__/unit/lib/load.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('lib/load: ', () => {
},
},
},
context: {
rawContext: {
name: 'gsf-demo',
description: 'Hello',
tags: {
Expand Down Expand Up @@ -120,7 +120,7 @@ describe('lib/load: ', () => {
},
},
},
context: {
rawContext: {
name: 'gsf-demo',
description: 'Hello',
tags: {
Expand Down
151 changes: 151 additions & 0 deletions src/__tests__/unit/util/os-checker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* eslint-disable no-restricted-properties */
jest.mock('os', () => ({
platform: () => {
if (process.env.KIND === 'darwin') return 'darwin';
if (process.env.KIND === 'linux') return 'linux';
if (process.env.KIND === 'win32') return 'win32';

return 'sunos';
},
release: () => 'm.m.m',
}));
jest.mock('../../../util/helpers', () => ({
execPromise: async () => {
if (process.env.KIND === 'darwin' && process.env.REJECT === 'true')
return {
stdout: '',
};
if (process.env.KIND === 'linux' && process.env.REJECT === 'true') {
return {
stdout: '',
};
}
if (process.env.KIND === 'win32' && process.env.REJECT === 'true')
return {
stdout: '',
};
if (process.env.KIND === 'darwin') {
return {
stdout: `
ProductName: macOS
ProductVersion: 14.3.1
BuildVersion: 23D60
`,
};
}

if (process.env.KIND === 'linux') {
return {
stdout: `
Distributor ID: Ubuntu
Description: Ubuntu 22.04.4 LTS
Release: 22.04
Codename: jammy
`,
};
}

if (process.env.KIND === 'win32') {
return {
stdout: `
OS Name: Microsoft Windows 11 Enterprise
OS Version: 10.0.22631 N/A Build 22631
`,
};
}

return '';
},
}));

import {osInfo} from '../../../util/os-checker';

describe('util/os-checker: ', () => {
describe('osInfo(): ', () => {
it('returns object with `os` and `os-version` properties.', async () => {
const response = await osInfo();
expect.assertions(2);

expect(response).toHaveProperty('os');
expect(response).toHaveProperty('os-version');
});

it('returns mac os information.', async () => {
process.env.KIND = 'darwin';
expect.assertions(1);

const expectedResponse = {
os: 'macOS',
'os-version': '14.3.1',
};
const response = await osInfo();
expect(response).toEqual(expectedResponse);
});

it('returns windows information.', async () => {
process.env.KIND = 'win32';
expect.assertions(1);

const expectedResponse = {
os: 'Microsoft Windows 11 Enterprise',
'os-version': '10.0.22631 N/A Build 22631',
};
const response = await osInfo();
expect(response).toEqual(expectedResponse);
});

it('returns linux information.', async () => {
process.env.KIND = 'linux';
expect.assertions(1);

const expectedResponse = {
os: 'Ubuntu',
'os-version': '22.04.4 LTS',
};
const response = await osInfo();
expect(response).toEqual(expectedResponse);
});

it('returns default information.', async () => {
process.env.KIND = 'other';
expect.assertions(2);

const response = await osInfo();
expect(typeof response.os).toEqual('string');
expect(typeof response['os-version']).toEqual('string');
});

it('returns info from node os on linux.', async () => {
process.env.KIND = 'linux';
process.env.REJECT = 'true';

const response = await osInfo();
const expectedOS = 'linux';
const expectedOSVersion = 'm.m.m';
expect(response.os).toEqual(expectedOS);
expect(response['os-version']).toEqual(expectedOSVersion);
});

it('returns info from node os on darwin.', async () => {
process.env.KIND = 'darwin';
process.env.REJECT = 'true';

const response = await osInfo();
const expectedOS = 'darwin';
const expectedOSVersion = 'm.m.m';
expect(response.os).toEqual(expectedOS);
expect(response['os-version']).toEqual(expectedOSVersion);
});

it('returns info from node os on win32.', async () => {
process.env.KIND = 'win32';
process.env.REJECT = 'true';

const response = await osInfo();
const expectedOS = 'win32';
const expectedOSVersion = 'm.m.m';
expect(response.os).toEqual(expectedOS);
expect(response['os-version']).toEqual(expectedOSVersion);
});
});
});
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node
import {aggregate} from './lib/aggregate';
import {compute} from './lib/compute';
import {injectEnvironment} from './lib/environment';
import {exhaust} from './lib/exhaust';
import {initalize} from './lib/initialize';
import {load} from './lib/load';
Expand All @@ -9,9 +10,8 @@ import {parameterize} from './lib/parameterize';
import {parseArgs} from './util/args';
import {andHandle} from './util/helpers';
import {logger} from './util/logger';
import {STRINGS} from './config';

const packageJson = require('../package.json');
import {STRINGS} from './config';

const {DISCLAIMER_MESSAGE} = STRINGS;

Expand All @@ -25,12 +25,12 @@ const impactEngine = async () => {
logger.info(DISCLAIMER_MESSAGE);
const {inputPath, paramPath, outputOptions} = options;

const {tree, context, parameters} = await load(inputPath!, paramPath);
const {tree, rawContext, parameters} = await load(inputPath!, paramPath);
const context = await injectEnvironment(rawContext);
parameterize.combine(context.params, parameters);
const pluginStorage = await initalize(context.initialize.plugins);
const computedTree = await compute(tree, {context, pluginStorage});
const aggregatedTree = aggregate(computedTree, context.aggregation);
context['if-version'] = packageJson.version;
exhaust(aggregatedTree, context, outputOptions);

return;
Expand Down
84 changes: 84 additions & 0 deletions src/lib/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {DateTime} from 'luxon';

import {execPromise} from '../util/helpers';

import {Context, ContextWithExec} from '../types/manifest';
import {NpmListResponse, PackageDependency} from '../types/environment';
import {osInfo} from '../util/os-checker';

const packageJson = require('../../package.json');

/**
* 1. Gets the high-resolution real time when the application starts.
* 2. Converts the high-resolution time to milliseconds.
* 3. Gets the current DateTime.
* 4. Subtracts the milliseconds from the current DateTime.
*/
const getProcessStartingTimestamp = () => {
const startTime = process.hrtime();

const [seconds, nanoseconds] = process.hrtime(startTime);
const milliseconds = seconds * 1000 + nanoseconds / 1e6;

const currentDateTime = DateTime.local();

const applicationStartDateTime = currentDateTime.minus({
milliseconds: milliseconds,
});

return applicationStartDateTime.toUTC().toString();
};

/**
* Goes through the dependencies, converts them into oneliner.
*/
const flattenDependencies = (dependencies: [string, PackageDependency][]) =>
dependencies.map(dependency => {
const [packageName, versionInfo] = dependency;
const {version, extraneous, resolved} = versionInfo;
const ifExtraneous = extraneous ? ` extraneous -> ${resolved}` : '';
const ifFromGithub =
resolved && resolved.startsWith('git') ? ` (${resolved})` : '';
const formattedString = `${packageName}@${version}${
ifExtraneous || ifFromGithub
}`;

return formattedString;
});

/**
* 1. Runs `npm list --json`.
* 2. Parses json data and converts to list.
*/
const listDependencies = async () => {
const {stdout} = await execPromise('npm list --json');
const npmListResponse: NpmListResponse = JSON.parse(stdout);
const dependencies = Object.entries(npmListResponse.dependencies);

return flattenDependencies(dependencies);
};

/**
* Injects execution information (command, environment) to existing context.
*/
export const injectEnvironment = async (context: Context) => {
const dependencies = await listDependencies();
const info = await osInfo();

const contextWithExec: ContextWithExec = {
...context,
execution: {
command: process.argv.join(' '),
environment: {
'if-version': packageJson.version,
os: info.os,
'os-version': info['os-version'],
'node-version': process.versions.node,
'date-time': getProcessStartingTimestamp(),
dependencies,
},
},
};

return contextWithExec;
};
4 changes: 2 additions & 2 deletions src/lib/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {Parameters} from '../types/parameters';
*/
export const load = async (inputPath: string, paramPath?: string) => {
const rawManifest = await openYamlFileAsObject<any>(inputPath);
const {tree, ...context} = validateManifest(rawManifest);
const {tree, ...rawContext} = validateManifest(rawManifest);
const parametersFromCli =
paramPath &&
(await readAndParseJson<Parameters>(paramPath)); /** @todo validate json */
Expand All @@ -21,7 +21,7 @@ export const load = async (inputPath: string, paramPath?: string) => {

return {
tree,
context,
rawContext,
parameters,
};
};
19 changes: 19 additions & 0 deletions src/types/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type PackageDependency = {
version: string;
resolved?: string;
overridden: boolean;
extraneous?: boolean;
};

type PackageProblem = {
extraneous: string;
};

export type NpmListResponse = {
version: string;
name: string;
problems?: PackageProblem[];
dependencies: {
[key: string]: PackageDependency;
};
};
13 changes: 13 additions & 0 deletions src/types/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,18 @@ export type AggregationParams = Manifest['aggregation'];
export type AggregationParamsSure = Extract<Manifest['aggregation'], {}>;

export type Context = Omit<Manifest, 'tree'>;
export type ContextWithExec = Omit<Manifest, 'tree'> & {
execution: {
command: string;
environment: {
'if-version': string;
os: string;
'os-version': string;
'node-version': string;
'date-time': string;
dependencies: string[];
};
};
};

export type ManifestParameter = Extract<Manifest['params'], {}>[number];
Loading
Loading