Skip to content

Commit

Permalink
Merge pull request #218 from ZenVoich/cli/global-cache-install-resolve
Browse files Browse the repository at this point in the history
install/resolve via global cache
  • Loading branch information
ZenVoich authored Apr 3, 2024
2 parents 2195738 + 58fe4a7 commit bde2a20
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 70 deletions.
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Refactor `mops install` command
- Reduce install threads to 12 (was 16)
- Reduce install threads to 6 when install called from `mops sources`
- Install dependencies directly to global cache, copy to local cache only final resolved dependencies

## 0.41.1
- Fix bin path for npm
Expand Down
32 changes: 25 additions & 7 deletions cli/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,33 @@ import path from 'node:path';
import ncp from 'ncp';
import getFolderSize from 'get-folder-size';

import {globalCacheDir} from './mops.js';
import {getDependencyType, globalCacheDir, parseGithubURL} from './mops.js';

export let isCached = (pkgId : string) => {
let dir = path.join(globalCacheDir, 'packages', pkgId);
export let getDepCacheDir = (cacheName : string) => {
return path.join(globalCacheDir, 'packages', cacheName);
};

export let isDepCached = (cacheName : string) => {
let dir = getDepCacheDir(cacheName);
return fs.existsSync(dir);
};

export let addCache = (pkgId : string, source : string) => {
let dest = path.join(globalCacheDir, 'packages', pkgId);
export function getDepCacheName(name : string, version : string) {
let depType = getDependencyType(version);
return depType === 'mops' ? getMopsDepCacheName(name, version) : getGithubDepCacheName(name, version);
}

export function getMopsDepCacheName(name : string, version : string) {
return `${name}@${version}`;
}

export function getGithubDepCacheName(name : string, repo : string) {
const {branch, commitHash} = parseGithubURL(repo);
return `_github/${name}#${branch}` + (commitHash ? `@${commitHash}` : '');
}

export let addCache = (cacheName : string, source : string) => {
let dest = path.join(globalCacheDir, 'packages', cacheName);
fs.mkdirSync(dest, {recursive: true});

return new Promise<void>((resolve, reject) => {
Expand All @@ -24,8 +42,8 @@ export let addCache = (pkgId : string, source : string) => {
});
};

export let copyCache = (pkgId : string, dest : string) => {
let source = path.join(globalCacheDir, 'packages', pkgId);
export let copyCache = (cacheName : string, dest : string) => {
let source = path.join(globalCacheDir, 'packages', cacheName);
fs.mkdirSync(dest, {recursive: true});

return new Promise<void>((resolve, reject) => {
Expand Down
11 changes: 6 additions & 5 deletions cli/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {checkConfigFile, getGithubCommit, parseGithubURL, readConfig, writeConfi
import {getHighestVersion} from '../api/getHighestVersion.js';
import {installMopsDep} from './install/install-mops-dep.js';
import {installFromGithub} from '../vessel.js';
import {notifyInstalls} from '../notify-installs.js';
import {checkIntegrity} from '../integrity.js';
import {checkRequirements} from '../check-requirements.js';
import {syncLocalCache} from './install/sync-local-cache.js';
import {notifyInstalls} from '../notify-installs.js';

type AddOptions = {
verbose ?: boolean;
Expand Down Expand Up @@ -87,8 +88,6 @@ export async function add(name : string, {verbose = false, dev = false, lock} :
};
}

let installedPackages = {};

if (pkgDetails.repo) {
await installFromGithub(pkgDetails.name, pkgDetails.repo, {verbose: verbose});
}
Expand All @@ -97,7 +96,6 @@ export async function add(name : string, {verbose = false, dev = false, lock} :
if (res === false) {
return;
}
installedPackages = {...installedPackages, ...res};
}

const depsProp = dev ? 'dev-dependencies' : 'dependencies';
Expand All @@ -116,8 +114,11 @@ export async function add(name : string, {verbose = false, dev = false, lock} :
if (lock !== 'ignore') {
logUpdate('Checking integrity...');
}

let installedPackages = await syncLocalCache();

await Promise.all([
notifyInstalls(Object.keys(installedPackages)),
notifyInstalls(installedPackages),
checkIntegrity(lock),
]);

Expand Down
8 changes: 5 additions & 3 deletions cli/commands/install/install-all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import process from 'node:process';
import chalk from 'chalk';
import {createLogUpdate} from 'log-update';
import {checkConfigFile, readConfig} from '../../mops.js';
import {notifyInstalls} from '../../notify-installs.js';
import {checkIntegrity} from '../../integrity.js';
import {installDeps} from './install-deps.js';
import {checkRequirements} from '../../check-requirements.js';
import {syncLocalCache} from './sync-local-cache.js';
import {notifyInstalls} from '../../notify-installs.js';

type InstallAllOptions = {
verbose ?: boolean;
Expand All @@ -28,16 +29,17 @@ export async function installAll({verbose = false, silent = false, threads, lock
if (!res) {
return;
}
let installedDeps = res;

let logUpdate = createLogUpdate(process.stdout, {showCursor: true});

if (!silent && lock !== 'ignore') {
logUpdate('Checking integrity...');
}

let installedPackages = await syncLocalCache();

await Promise.all([
notifyInstalls(Object.keys(installedDeps)),
notifyInstalls(installedPackages),
checkIntegrity(lock),
]);

Expand Down
2 changes: 2 additions & 0 deletions cli/commands/install/install-dep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {installFromGithub} from '../../vessel.js';
import {installMopsDep} from './install-mops-dep.js';
import {Dependency} from '../../types.js';
import {installLocalDep} from './install-local-dep.js';
import {getRootDir} from '../../mops.js';

type InstallDepOptions = {
verbose ?: boolean;
Expand All @@ -19,6 +20,7 @@ export async function installDep(dep : Dependency, {verbose, silent, threads} :
}
else if (dep.path) {
let depPath = dep.path;
parentPkgPath = parentPkgPath || getRootDir();
if (parentPkgPath) {
depPath = path.resolve(parentPkgPath, dep.path);
}
Expand Down
28 changes: 17 additions & 11 deletions cli/commands/install/install-mops-dep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import path from 'node:path';
import {Buffer} from 'node:buffer';
import {createLogUpdate} from 'log-update';
import chalk from 'chalk';
import {deleteSync} from 'del';
import {checkConfigFile, formatDir, progressBar, readConfig} from '../../mops.js';
import {getHighestVersion} from '../../api/getHighestVersion.js';
import {storageActor} from '../../api/actors.js';
import {parallel} from '../../parallel.js';
import {addCache, copyCache, isCached} from '../../cache.js';
import {getDepCacheDir, getMopsDepCacheName, isDepCached} from '../../cache.js';
import {downloadFile, getPackageFilesInfo} from '../../api/downloadPackageFiles.js';
import {installDeps} from './install-deps.js';

Expand Down Expand Up @@ -46,6 +47,8 @@ export async function installMopsDep(pkg : string, version = '', {verbose, silen
}

let dir = formatDir(pkg, version);
let cacheName = getMopsDepCacheName(pkg, version);
let cacheDir = getDepCacheDir(cacheName);
let alreadyInstalled = false;

// already installed
Expand All @@ -54,8 +57,7 @@ export async function installMopsDep(pkg : string, version = '', {verbose, silen
alreadyInstalled = true;
}
// copy from cache
else if (isCached(`${pkg}@${version}`)) {
await copyCache(`${pkg}@${version}`, dir);
else if (isDepCached(cacheName)) {
silent || logUpdate(`${dep ? 'Dependency' : 'Installing'} ${pkg}@${version} (global cache)`);
}
// download
Expand All @@ -79,20 +81,24 @@ export async function installMopsDep(pkg : string, version = '', {verbose, silen
progress();
});

// write files to disk
for (let [filePath, data] of filesData.entries()) {
fs.mkdirSync(path.join(dir, path.dirname(filePath)), {recursive: true});
fs.writeFileSync(path.join(dir, filePath), Buffer.from(data));
// write files to global cache
try {
for (let [filePath, data] of filesData.entries()) {
fs.mkdirSync(path.join(cacheDir, path.dirname(filePath)), {recursive: true});
fs.writeFileSync(path.join(cacheDir, filePath), Buffer.from(data));
}
}
catch (err) {
console.error(chalk.red('Error: ') + err);
deleteSync([cacheDir], {force: true});
return false;
}
}
catch (err) {
console.error(chalk.red('Error: ') + err);
return false;
}

// add to cache
await addCache(`${pkg}@${version}`, dir);

progress();
}

Expand All @@ -104,7 +110,7 @@ export async function installMopsDep(pkg : string, version = '', {verbose, silen
}

// install dependencies
let config = readConfig(path.join(dir, 'mops.toml'));
let config = readConfig(path.join(cacheDir, 'mops.toml'));
let res = await installDeps(Object.values(config.dependencies || {}), {silent, verbose});

if (!res) {
Expand Down
34 changes: 34 additions & 0 deletions cli/commands/install/sync-local-cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import fs from 'node:fs';
import path from 'node:path';
import {copyCache, getDepCacheName} from '../../cache.js';
import {getDependencyType, getRootDir} from '../../mops.js';
import {resolvePackages} from '../../resolve-packages.js';

export async function syncLocalCache({verbose = false} = {}) : Promise<Record<string, string>> {
let resolvedPackages = await resolvePackages();
let rootDir = getRootDir();

verbose && console.log('Syncing local cache...');

let installedDeps : Record<string, string> = {};

await Promise.all(Object.entries(resolvedPackages).map(([name, value]) => {
let depType = getDependencyType(value);

if (depType === 'mops' || depType === 'github') {
let cacheName = getDepCacheName(name, value);
let dest = path.join(rootDir, '.mops', cacheName);

if (!fs.existsSync(dest)) {
if (depType === 'mops') {
installedDeps[name] = value;
}
return copyCache(cacheName, path.join(rootDir, '.mops', cacheName));
}
}

return Promise.resolve();
}));

return installedDeps;
}
33 changes: 15 additions & 18 deletions cli/commands/remove.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import fs from 'node:fs';
import {deleteSync} from 'del';
import chalk from 'chalk';
import {formatDir, formatGithubDir, checkConfigFile, readConfig, writeConfig} from '../mops.js';
import {checkConfigFile, getRootDir, readConfig, writeConfig} from '../mops.js';
import {Config, Dependency} from '../types.js';
import {checkIntegrity} from '../integrity.js';
import {getDepCacheDir, getDepCacheName} from '../cache.js';
import path from 'node:path';
import {syncLocalCache} from './install/sync-local-cache.js';

type RemoveOptions = {
verbose ?: boolean;
Expand All @@ -12,7 +15,6 @@ type RemoveOptions = {
lock ?: 'update' | 'ignore';
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function remove(name : string, {dev = false, verbose = false, dryRun = false, lock} : RemoveOptions = {}) {
if (!checkConfigFile()) {
return;
Expand All @@ -31,13 +33,12 @@ export async function remove(name : string, {dev = false, verbose = false, dryRu
}

function getTransitiveDependenciesOf(name : string, version : string | undefined, repo ?: string) {
let pkgDir = '';
if (repo) {
pkgDir = formatGithubDir(name, repo);
}
else if (version) {
pkgDir = formatDir(name, version);
let value = version || repo;
if (!value) {
return [];
}
let cacheName = getDepCacheName(name, value);
let pkgDir = getDepCacheDir(cacheName);
let configFile = pkgDir + '/mops.toml';
if (!fs.existsSync(configFile)) {
verbose && console.log('no config', configFile);
Expand Down Expand Up @@ -78,16 +79,11 @@ export async function remove(name : string, {dev = false, verbose = false, dryRu
verbose && console.log(`Ignored transitive dependency ${depId} (other deps depend on it)`);
continue;
}
let pkgDir;
if (dep.repo) {
pkgDir = formatGithubDir(dep.name, dep.repo);
}
else if (dep.version) {
pkgDir = formatDir(dep.name, dep.version);
}
if (pkgDir && fs.existsSync(pkgDir)) {
dryRun || deleteSync([`${pkgDir}`], {force: true});
verbose && console.log(`Removed local cache ${pkgDir}`);
let cacheName = getDepCacheName(dep.name, dep.version || dep.repo || '');
let localCacheDir = path.join(getRootDir(), '.mops', cacheName);
if (localCacheDir && fs.existsSync(localCacheDir)) {
dryRun || deleteSync([localCacheDir], {force: true});
verbose && console.log(`Removed local cache ${localCacheDir}`);
}
}

Expand All @@ -100,6 +96,7 @@ export async function remove(name : string, {dev = false, verbose = false, dryRu
}
dryRun || writeConfig(config);

await syncLocalCache();
await checkIntegrity(lock);

console.log(chalk.green('Package removed ') + `${name} = "${version}"`);
Expand Down
14 changes: 9 additions & 5 deletions cli/notify-installs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import {getDependencyType} from './mops.js';
import {mainActor} from './api/actors.js';
import {resolvePackages} from './resolve-packages.js';

export async function notifyInstalls(names : string[]) {
let resolvedPackages = await resolvePackages();
let packages : [string, string][] = names.map(name => [name, resolvedPackages[name] as string]);
export async function notifyInstalls(installedDeps : Record<string, string>) {
let packages = Object.entries(installedDeps).filter(([_, version]) => getDependencyType(version) === 'mops');
if (packages.length) {
let actor = await mainActor();
await actor.notifyInstalls(packages.filter(([_, version]) => getDependencyType(version) === 'mops'));

try {
await actor.notifyInstalls(packages);
}
catch (err) {
// verbose && console.error('Failed to notify installs:', err);
}
}
}
19 changes: 10 additions & 9 deletions cli/resolve-packages.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import process from 'node:process';
import path from 'node:path';
import chalk from 'chalk';
import {checkConfigFile, formatDir, formatGithubDir, getRootDir, parseGithubURL, readConfig} from './mops.js';
import {checkConfigFile, getRootDir, parseGithubURL, readConfig} from './mops.js';
import {VesselConfig, readVesselConfig} from './vessel.js';
import {Config, Dependency} from './types.js';
import {getDepCacheDir, getDepCacheName} from './cache.js';

export async function resolvePackages({verbose = false} = {}) : Promise<Record<string, string>> {
if (!checkConfigFile()) {
Expand Down Expand Up @@ -76,25 +77,25 @@ export async function resolvePackages({verbose = false} = {}) : Promise<Record<s
}

let nestedConfig;
let nestedDir = '';
let localNestedDir = '';

// read nested config
if (repo) {
nestedDir = formatGithubDir(name, repo);
nestedConfig = await readVesselConfig(nestedDir, {silent: true}) || {};
let cacheDir = getDepCacheName(name, repo);
nestedConfig = await readVesselConfig(getDepCacheDir(cacheDir), {silent: true}) || {};
}
else if (pkgDetails.path) {
nestedDir = path.resolve(configDir, pkgDetails.path);
nestedConfig = readConfig(nestedDir + '/mops.toml');
localNestedDir = path.resolve(configDir, pkgDetails.path);
nestedConfig = readConfig(localNestedDir + '/mops.toml');
}
else if (version) {
nestedDir = formatDir(name, version);
nestedConfig = readConfig(nestedDir + '/mops.toml');
let cacheDir = getDepCacheName(name, version);
nestedConfig = readConfig(getDepCacheDir(cacheDir) + '/mops.toml');
}

// collect nested deps
if (nestedConfig) {
await collectDeps(nestedConfig, nestedDir);
await collectDeps(nestedConfig, localNestedDir);
}

if (!versions[name]) {
Expand Down
Loading

0 comments on commit bde2a20

Please sign in to comment.