Skip to content

Commit

Permalink
PLAT-13620: Adding new environment dependency validation (#1115)
Browse files Browse the repository at this point in the history
* PLAT-13620: Adding new environment dependency validation

* PLAT-13620: fixing up linting errors

* PLAT-13620: Adding cached environment variable

* PLAT-13620: Exporting enum

* Migrating environment check to new function

* Updating error printout message
  • Loading branch information
taylor-cb committed Mar 12, 2024
1 parent 15bdfd0 commit 67adf29
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 29 deletions.
26 changes: 22 additions & 4 deletions index.js
Expand Up @@ -42,7 +42,10 @@ import { sendFeedback } from "./scripts/feedback.js";
import { logout } from "./scripts/logout.js";
import { modulesArchive, modulesGet, modulesList } from "./scripts/modules.js";
import { publish } from "./scripts/publish.js";
import { preExecuteChecks } from "./scripts/utils/environment.js";
import {
validateEnvironmentDependencies,
EnvironmentDependency
} from "./scripts/utils/environment.js";
import { analytics } from "./scripts/analytics/wrapper.js";
import { HAS_ASKED_OPT_IN_NAME } from "./scripts/analytics/config.js";
import { EVENT } from "./scripts/analytics/constants.js";
Expand Down Expand Up @@ -109,7 +112,10 @@ async function dispatcher() {

const commands = {
demo: () => {
preExecuteChecks();
validateEnvironmentDependencies([
EnvironmentDependency.Python,
EnvironmentDependency.PipEnv
]);
createDemo(
path.join(gitRoot(), "demo"),
path.join(sourceDir, "cookiecutter.yaml")
Expand Down Expand Up @@ -139,7 +145,11 @@ const commands = {
}
},
add: () => {
preExecuteChecks();
validateEnvironmentDependencies([
EnvironmentDependency.Python,
EnvironmentDependency.Yarn
]);

const args = arg({
"--source": String,
"--project": String
Expand All @@ -151,6 +161,8 @@ const commands = {
addModules(modules, args["--source"], args["--project"], gitRoot());
},
remove: () => {
validateEnvironmentDependencies([EnvironmentDependency.Yarn]);

const args = arg({
"--source": String,
"--project": String
Expand All @@ -162,7 +174,11 @@ const commands = {
removeModules(modules, args["--source"], args["--project"], gitRoot());
},
create: () => {
preExecuteChecks(true);
validateEnvironmentDependencies([
EnvironmentDependency.Python,
EnvironmentDependency.CookieCutter
]);

const args = arg({
"--name": String,
"--type": String,
Expand Down Expand Up @@ -199,6 +215,8 @@ const commands = {
commitModules(modules, args["--source"], gitRoot());
},
init: () => {
validateEnvironmentDependencies([EnvironmentDependency.Git]);

const args = arg({
"--name": String
});
Expand Down
3 changes: 3 additions & 0 deletions scripts/info.js
@@ -1,7 +1,10 @@
import { invalid, section } from "../utils.js";
import { apiClient } from "./utils/apiClient.js";
import { validateEnvironmentDependencies } from "./utils/environment.js";

export const info = async () => {
validateEnvironmentDependencies(undefined, true);

const response = await apiClient.get({
path: "/v2/user/"
});
Expand Down
183 changes: 158 additions & 25 deletions scripts/utils/environment.js
@@ -1,10 +1,22 @@
import { execSync, spawnSync } from "node:child_process";
import { invalid, section } from "../../utils.js";
import { invalid, section, valid } from "../../utils.js";
import { configFile } from "./configFile.js";

const ENVIRONMENT_VERSIONS_CONFIG_NAME = "environment-versions";
const PYTHON_VERSION_REGEX = /Python (3\.[0-9]*)/;

const userdir = process.cwd();

export const execOptions = { encoding: "utf8", stdio: "inherit" };

export const EnvironmentDependency = {
Yarn: "yarn",
Git: "git",
Python: "python",
PipEnv: "pipenv",
CookieCutter: "cookiecutter"
};

/**
* Setup Python environment to desired version
* @param {*} options: exec options
Expand All @@ -13,37 +25,158 @@ export function configurePython(options = execOptions) {
execSync("pipenv --python 3.8.17", options);
}

export function preExecuteChecks(cookiecutterCheck = false) {
// Check if Python 3.8.x is installed
function formatStdout(stdout) {
return stdout.replace(/\n/g, "");
}

export function getEnvironmentVersions(dependencies) {
const environmentVersions = {};

if (dependencies.includes(EnvironmentDependency.Yarn)) {
const yarn = spawnSync("yarn", ["--version"], {
cwd: userdir,
shell: true,
encoding: "utf8"
});

if (!yarn.error && !yarn.stderr) {
environmentVersions.yarn = formatStdout(yarn.stdout);
}
}

if (dependencies.includes(EnvironmentDependency.Git)) {
const git = spawnSync("git", ["--version"], {
cwd: userdir,
shell: true,
encoding: "utf8"
});

if (!git.error && !git.stderr) {
environmentVersions.git = formatStdout(git.stdout);
}
}

if (dependencies.includes(EnvironmentDependency.Python)) {
const python = spawnSync("python", ["--version"], {
cwd: userdir,
shell: true,
encoding: "utf8"
});

try {
section("Checking Python version");
if (python.stdout && !python.error && !python.stderr) {
const versionMatch = python.stdout.match(PYTHON_VERSION_REGEX);

const pythonVersionRegex = /Python 3\.[0-9]*/;
if (versionMatch && versionMatch[1]) {
environmentVersions.python = versionMatch[1];
}
}
}

const pythonCheck = spawnSync("python", ["--version"], {
if (dependencies.includes(EnvironmentDependency.PipEnv)) {
const pipenv = spawnSync("pipenv", ["--version"], {
cwd: userdir,
shell: true,
encoding: "utf8"
});
const userPythonVersion = pythonCheck.stdout || pythonCheck.stderr;
if (!pythonVersionRegex.test(userPythonVersion)) {
invalid(`Found Python version: ${userPythonVersion}. Please install 3.x and try again.`);
}
} catch (error) {
invalid("Error detecting python version, please check install and try again.");
}

if (cookiecutterCheck) {
// Check if Cookiecutter is installed
try {
execSync("cookiecutter --version", {
cwd: userdir,
shell: true,
encoding: "utf8"
});
} catch (error) {
invalid("Cookiecutter is not installed. Please install Cookiecutter before running this command.");

if (!pipenv.stderr && !pipenv.error) {
environmentVersions.pipenv = formatStdout(pipenv.stdout);
}
}

if (EnvironmentDependency.CookieCutter) {
const cookiecutter = spawnSync("cookiecutter --version", {
cwd: userdir,
shell: true,
encoding: "utf8"
});

if (!cookiecutter.stderr && !cookiecutter.error) {
environmentVersions.cookiecutter = cookiecutter.stdout;
}
}

return environmentVersions;
}

export function validateEnvironmentDependencies(
dependencies = [
EnvironmentDependency.Yarn,
EnvironmentDependency.Git,
EnvironmentDependency.Python,
EnvironmentDependency.PipEnv,
EnvironmentDependency.CookieCutter
],
force = false
) {
section("Checking environment compatibility");

const configValues = configFile.get(ENVIRONMENT_VERSIONS_CONFIG_NAME);

const cachedEnvironmentVersions = !force && configValues ? configValues : {};

let missingEnvironmentVersions = dependencies;

if (!force) {
missingEnvironmentVersions = dependencies.filter(
(dependency) => !cachedEnvironmentVersions[dependency]
);
}

const currentEnvironmentVersions = getEnvironmentVersions(
missingEnvironmentVersions
);

const environmentVersions = {
...cachedEnvironmentVersions,
...currentEnvironmentVersions
};

configFile.set(ENVIRONMENT_VERSIONS_CONFIG_NAME, environmentVersions);
configFile.save();

const printInvalidMessage = (message) =>
invalid(
`${message}\n\nVisit the following page for environment requirements https://github.com/crowdbotics/modules?tab=readme-ov-file#requirements-for-contributing`
);

if (dependencies.includes(EnvironmentDependency.Yarn)) {
if (!environmentVersions.yarn) {
printInvalidMessage("yarn is not available in your system");
} else {
valid("yarn version", environmentVersions.yarn);
}
}

if (dependencies.includes(EnvironmentDependency.Git)) {
if (!environmentVersions.git) {
printInvalidMessage("git is not available in your system");
} else {
valid(environmentVersions.git);
}
}

if (dependencies.includes(EnvironmentDependency.Python)) {
if (!environmentVersions.python) {
printInvalidMessage("Python 3.x is not available in your system");
} else {
valid(environmentVersions.python);
}
}

if (dependencies.includes(EnvironmentDependency.PipEnv)) {
if (!environmentVersions.pipenv) {
printInvalidMessage("pipenv is not available in your system");
} else {
valid(environmentVersions.pipenv);
}
}

if (dependencies.includes(EnvironmentDependency.CookieCutter)) {
if (!environmentVersions.cookiecutter) {
printInvalidMessage("cookiecutter is not available in your system");
} else {
valid(environmentVersions.cookiecutter);
}
}
}

0 comments on commit 67adf29

Please sign in to comment.