Skip to content

Commit

Permalink
Merge pull request #665 from Green-Software-Foundation/env-data-in-ou…
Browse files Browse the repository at this point in the history
…tput

Env data in output
  • Loading branch information
MariamKhalatova authored May 3, 2024
2 parents 1a19ae1 + 965bfae commit cd9ccc2
Show file tree
Hide file tree
Showing 14 changed files with 436 additions and 15 deletions.
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.

2 changes: 1 addition & 1 deletion src/__mocks__/fs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const mkdir = (dirPath: string) => dirPath;

export const writeFile = async (pathToFile: string, content: string) => {
if (pathToFile === 'reject') {
throw Error('Wrong file path');
throw new Error('Wrong file path');
}

const mockPathToFile = 'mock-pathToFile';
Expand Down
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
3 changes: 1 addition & 2 deletions src/__tests__/unit/util/json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ describe('util/json: ', () => {
expect.assertions(1);

try {
const response = await readAndParseJson<typeof path>(path);
console.log(response);
await readAndParseJson<typeof path>(path);
} catch (error) {
expect(error).toBeInstanceOf(Error);
}
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
85 changes: 85 additions & 0 deletions src/lib/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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 dateTime = `${getProcessStartingTimestamp()} (UTC)`;

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': dateTime,
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;
};
};
Loading

0 comments on commit cd9ccc2

Please sign in to comment.