<commit_before>/*
 * This derives the configuration and capabilities of the current project.
 * It is used in the UI to only show/run those elements, which should work.
 * The corresponding file in the webapp is smc-webapp/project_configuration.ts
 */

import * as which from "which";
import * as process from "process";
import { access as fs_access, constants as fs_constaints } from "fs";
import { exec as child_process_exec } from "child_process";
import { promisify } from "util";
const exec = promisify(child_process_exec);
import { APPS } from "../smc-webapp/frame-editors/x11-editor/apps";
import { ConfigurationAspect } from "../smc-webapp/project_configuration";
import {
  Configuration,
  Capabilities,
  MainCapabilities,
  LIBRARY_INDEX_FILE
} from "../smc-webapp/project_configuration";
import { parser2tool, Tool as FormatTool } from "../smc-util/code-formatter";

// we prefix the environment PATH by default bin paths pointing into it in order to pick up locally installed binaries.
// they can't be set as defaults for projects since this could break it from starting up
function construct_path(): string {
  const env = process.env;
  // we can safely assume that PATH is defined
  const entries = env.PATH!.split(":");
  const home = env.HOME ?? "/home/user";
  entries.unshift(`${home}/.local/bin`);
  entries.unshift(`${home}/bin`);
  return entries.join(":");
}

const PATH = construct_path();

// test if the given utiltiy "name" exists (executable in the PATH)
async function have(name: string): Promise<boolean> {
  return new Promise<boolean>(resolve => {
    which(name, { path: PATH }, function(error, path) {
      resolve(error == null && path != null);
    });
  });
}

// we cache this as long as the project runs
const conf: { [key in ConfigurationAspect]?: Configuration } = {};

// check for all X11 apps.
// UI will only show buttons for existing executables.
async function x11_apps(): Promise<Capabilities> {
  const status: Promise<boolean>[] = [];
  const KEYS = Object.keys(APPS);
  for (const key of KEYS) {
    const app = APPS[key];
    status.push(have(app.command != null ? app.command : key));
  }
  const results = await Promise.all(status);
  const ret: { [key: string]: boolean } = {};
  KEYS.map((name, idx) => (ret[name] = results[idx]));
  return ret;
}

// determines if X11 support exists at all
async function x11(): Promise<boolean> {
  return await have("xpra");
}

// do we have "sage"? which version?
async function sage_info(): Promise<{
  exists: boolean;
  version: number[] | undefined;
}> {
  // TODO probably also check if smc_sagews is working? or the sage server?
  // without sage, sagews files are disabled
  const exists = await have("sage");
  let version: number[] | undefined = undefined;
  if (exists) {
    // We need the version of sage (--version runs quickly)
    try {
      const info = (
        await exec("sage --version", { env: { PATH } })
      ).stdout.trim();
      const m = info.match(/version ([\d+.]+[\d+])/);
      if (m != null) {
        const v = m[1];
        if (v != null && v.length > 1) {
          version = v.split(".").map(x => parseInt(x));
          //console.log(`Sage version info: ${info} ->  ${version}`);
        }
      }
    } catch (err) {
      console.log("Problem fetching sage version info -- ignoring", err);
    }<commit_msg>sage version: keep the environment<commit_after>/*
 * This derives the configuration and capabilities of the current project.
 * It is used in the UI to only show/run those elements, which should work.
 * The corresponding file in the webapp is smc-webapp/project_configuration.ts
 */

import * as which from "which";
import * as process from "process";
import { access as fs_access, constants as fs_constaints } from "fs";
import { exec as child_process_exec } from "child_process";
import { promisify } from "util";
const exec = promisify(child_process_exec);
import { APPS } from "../smc-webapp/frame-editors/x11-editor/apps";
import { ConfigurationAspect } from "../smc-webapp/project_configuration";
import {
  Configuration,
  Capabilities,
  MainCapabilities,
  LIBRARY_INDEX_FILE
} from "../smc-webapp/project_configuration";
import { parser2tool, Tool as FormatTool } from "../smc-util/code-formatter";
import { copy } from "../smc-util/misc2"

// we prefix the environment PATH by default bin paths pointing into it in order to pick up locally installed binaries.
// they can't be set as defaults for projects since this could break it from starting up
function construct_path(): string {
  const env = process.env;
  // we can safely assume that PATH is defined
  const entries = env.PATH!.split(":");
  const home = env.HOME ?? "/home/user";
  entries.unshift(`${home}/.local/bin`);
  entries.unshift(`${home}/bin`);
  return entries.join(":");
}

const PATH = construct_path();

// test if the given utiltiy "name" exists (executable in the PATH)
async function have(name: string): Promise<boolean> {
  return new Promise<boolean>(resolve => {
    which(name, { path: PATH }, function(error, path) {
      resolve(error == null && path != null);
    });
  });
}

// we cache this as long as the project runs
const conf: { [key in ConfigurationAspect]?: Configuration } = {};

// check for all X11 apps.
// UI will only show buttons for existing executables.
async function x11_apps(): Promise<Capabilities> {
  const status: Promise<boolean>[] = [];
  const KEYS = Object.keys(APPS);
  for (const key of KEYS) {
    const app = APPS[key];
    status.push(have(app.command != null ? app.command : key));
  }
  const results = await Promise.all(status);
  const ret: { [key: string]: boolean } = {};
  KEYS.map((name, idx) => (ret[name] = results[idx]));
  return ret;
}

// determines if X11 support exists at all
async function x11(): Promise<boolean> {
  return await have("xpra");
}

// do we have "sage"? which version?
async function sage_info(): Promise<{
  exists: boolean;
  version: number[] | undefined;
}> {
  // TODO probably also check if smc_sagews is working? or the sage server?
  // without sage, sagews files are disabled
  const exists = await have("sage");
  let version: number[] | undefined = undefined;
  if (exists) {
    // We need the version of sage (--version runs quickly)
    try {
      const env = copy(process.env);
      env.PATH = PATH;
      const info = (await exec("sage --version", { env })).stdout.trim();
      const m = info.match(/version ([\d+.]+[\d+])/);
      if (m != null) {
        const v = m[1];
        if (v != null && v.length > 1) {
          version = v.split(".").map(x => parseInt(x));
          // console.log(`Sage version info: ${info} ->  ${version}`, env);
        }
      }
    } catch (err) {
      // TODO: do something better than silently ignoring errors.  This console.log
      // isn't going to be seen by the user.
      console.log("Problem fetching sage version info -- ignoring", err);
    }
{'fraction_non_alphanumeric': 0.13906137184115525, 'fraction_numerical': 0.0038989169675090253, 'mean_word_length': 3.400254129606099, 'pattern_counts': {'":': 4, '<': 15, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '342669', 'n_tokens_mistral': 2082, 'n_tokens_neox': 2047, 'n_words': 735}