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

Add --immutable arg #164

Merged
merged 5 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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 bin/cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function runMaster() {
await install({
root,
cwd: `${root}/${data[0].dir}`,
frozenLockfile: true,
immutable: true,
conservative: true,
});

Expand Down
8 changes: 7 additions & 1 deletion commands/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,13 @@ const add /*: Add */ = async ({root, cwd, args, dev = false}) => {
dirs: projects.map(dir => `${root}/${dir}`),
target: resolve(root, cwd),
});
await generateBazelBuildRules({root, deps, projects, dependencySyncRule});
await generateBazelBuildRules({
root,
deps,
projects,
dependencySyncRule,
immutable: false,
});
}
};

Expand Down
2 changes: 1 addition & 1 deletion commands/ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type CiArgs = {
export type Ci = (CiArgs) => Promise<void>
*/
const ci /*: Ci */ = async ({root, cwd}) => {
await install({root, cwd, frozenLockfile: true, conservative: true});
await install({root, cwd, immutable: true, conservative: true});
};

module.exports = {ci};
3 changes: 2 additions & 1 deletion commands/focus.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,13 @@ const focus /*: Focus */ = async ({
await validateVersionPolicy({dirs: deps.map(dep => dep.dir), versionPolicy});

if (workspace === 'sandbox') {
await generateBazelignore({root});
await generateBazelignore({root, immutable: false});
await generateBazelBuildRules({
root,
deps,
projects,
dependencySyncRule,
immutable: false,
});
}

Expand Down
50 changes: 38 additions & 12 deletions commands/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const {node, yarn} = require('../utils/binary-paths.js');
export type InstallArgs = {
root: string,
cwd: string,
frozenLockfile?: boolean,
immutable?: boolean,
conservative?: boolean,
skipPreinstall?: boolean,
skipPostinstall?: boolean,
Expand All @@ -33,7 +33,7 @@ export type Install = (InstallArgs) => Promise<void>
const install /*: Install */ = async ({
root,
cwd,
frozenLockfile = false,
immutable = false,
conservative = true,
skipPreinstall = false,
skipPostinstall = false,
Expand Down Expand Up @@ -71,14 +71,27 @@ const install /*: Install */ = async ({
validateDeps({deps});
await validateVersionPolicy({dirs: deps.map(dep => dep.dir), versionPolicy});

if (workspace === 'sandbox' && frozenLockfile === false) {
await generateBazelignore({root});
await generateBazelBuildRules({
root,
deps: all,
projects,
dependencySyncRule,
});
if (workspace === 'sandbox') {
const changedGeneratedFiles = [
...(await generateBazelignore({
root,
immutable,
})),
...(await generateBazelBuildRules({
root,
deps: all,
projects,
dependencySyncRule,
immutable,
})),
];

if (immutable && changedGeneratedFiles.length > 0) {
throw new ImmutableInstallError(
`Generated files would have changed, but 'immutable' arg was passed`,
changedGeneratedFiles
);
}
}

if (hooks.bool_shouldinstall) {
Expand All @@ -97,7 +110,7 @@ const install /*: Install */ = async ({
const env = process.env;
const path = dirname(node) + ':' + String(process.env.PATH);
const spawnArgs = [yarn, 'install'];
if (frozenLockfile) {
if (immutable) {
spawnArgs.push('--immutable');
}

Expand Down Expand Up @@ -131,6 +144,19 @@ const install /*: Install */ = async ({
}
};

/**
* An error that's thrown when the `immutable` flag is
* `true` and generated files would have changed.
*/
class ImmutableInstallError extends Error {
/*:: changedFiles: Array<string>; */

constructor(message /*: string */, changedFiles /*: Array<string> */) {
super(message);
this.changedFiles = changedFiles;
}
}

const validateRegistration = ({root, cwd, projects}) => {
if (!projects.find(dir => resolve(`${root}/${dir}`) === cwd)) {
const registrationError = `Your cwd ${cwd} is not listed in manifest.json or package.json. If you are at the wrong directory, cd into your desired directory or use the --cwd flag. If you are in the desired directory, make sure it is listed in the projects field in manifest.json or workspaces field in package.json`;
Expand Down Expand Up @@ -173,4 +199,4 @@ const validateVersionPolicy = async ({dirs, versionPolicy}) => {
if (!result.valid) throw new Error(getErrorMessage(result, false));
};

module.exports = {install};
module.exports = {install, ImmutableInstallError};
8 changes: 7 additions & 1 deletion commands/remove.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ const remove /*: Remove */ = async ({root, cwd, args}) => {
dirs: projects.map(dir => `${root}/${dir}`),
target: resolve(root, cwd),
});
await generateBazelBuildRules({root, deps, projects, dependencySyncRule});
await generateBazelBuildRules({
root,
deps,
projects,
dependencySyncRule,
immutable: false,
});
}
};

Expand Down
74 changes: 60 additions & 14 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// @flow
const {getRootDir} = require('./utils/get-root-dir.js');
const {parse, normalize} = require('./utils/parse-argv.js');
const {cli} = require('./utils/cli.js');
const {cli, CliError} = require('./utils/cli.js');
const {version} = require('./commands/version.js');
const {init} = require('./commands/init.js');
const {scaffold} = require('./commands/scaffold.js');
const {install} = require('./commands/install.js');
const {install, ImmutableInstallError} = require('./commands/install.js');
const {ci} = require('./commands/ci.js');
const {focus} = require('./commands/focus.js');
const {add} = require('./commands/add.js');
Expand Down Expand Up @@ -86,26 +86,46 @@ const runCLI /*: RunCLI */ = async argv => {
--skipPreinstall Skip the preinstall hook
--skipPostinstall Skip the postinstall hook
--mode If set to skip-build, skips build scripts. If set to update-lockfile, skips link step
--immutable Fail if generated files need to be modified
--verbose`,
async ({cwd, skipPreinstall, skipPostinstall, mode, verbose}) =>
install({
root: await rootOf(args),
cwd,
skipPreinstall: Boolean(skipPreinstall),
skipPostinstall: Boolean(skipPostinstall),
mode,
verbose: Boolean(verbose),
}),
async ({
cwd,
skipPreinstall,
skipPostinstall,
mode,
immutable,
verbose,
}) => {
try {
await install({
root: await rootOf(args),
cwd,
skipPreinstall: Boolean(skipPreinstall),
skipPostinstall: Boolean(skipPostinstall),
mode,
immutable: Boolean(immutable),
verbose: Boolean(verbose),
});
} catch (error) {
handleInstallError(error);
}
},
],
ci: [
`Install all dependencies for all project without modifying source files
`Install all dependencies for all projects, failing if any generated files need to be modified

--cwd [cwd] Project directory to use`,
async ({cwd}) => ci({root: await rootOf(args), cwd}),
async ({cwd}) => {
try {
await ci({root: await rootOf(args), cwd});
} catch (error) {
handleInstallError(error);
}
},
],
focus: [
`Install all dependencies for one or more projects without installing the rest

--cwd [cwd] Project directory to use
--all Install all dependencies, like regular yarn install
--production Install only production dependencies, not devDependencies
Expand Down Expand Up @@ -366,6 +386,32 @@ const runCLI /*: RunCLI */ = async argv => {
);
};

const ansiRed = '\x1b[91m';
const ansiReset = '\x1b[0m';
/**
* Checks whether the provided error is a `ImmutableInstallError`,
* and if it is, throws a `CliError` with a formatted message.
*/
function handleInstallError(error /*: Error | ImmutableInstallError */) {
if (error instanceof ImmutableInstallError) {
const shouldUseColors = process.stdout.isTTY || process.env.FORCE_COLOR;
const errorMessage =
(shouldUseColors ? ansiRed : '') +
'ERROR: ' +
error.message +
'\n' +
error.changedFiles
.sort((a, b) => a.localeCompare(b))
.map(l => ' - ' + l)
.join('\n') +
(shouldUseColors ? ansiReset : '');

throw new CliError(errorMessage, 1);
} else {
throw error;
}
}

async function rootOf(args) {
return getRootDir({dir: args.cwd});
}
Expand Down
4 changes: 4 additions & 0 deletions tests/fixtures/immutable-install/a/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "a",
"version": "0.0.0"
}
9 changes: 9 additions & 0 deletions tests/fixtures/immutable-install/b/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package(default_visibility = ["//visibility:public"])
load("@jazelle//:build-rules.bzl", "web_library")

web_library(
name = "b",
deps = [
],
dist = ["dist"],
)
7 changes: 7 additions & 0 deletions tests/fixtures/immutable-install/b/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "b",
"version": "0.0.0",
"dependencies": {
"a": "0.0.0"
}
}
3 changes: 3 additions & 0 deletions tests/fixtures/immutable-install/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"workspace": "sandbox"
}
4 changes: 4 additions & 0 deletions tests/fixtures/immutable-install/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"private": true,
"workspaces": ["a", "b"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("@jazelle//:build-rules.bzl", "web_library")

package(default_visibility = ["//visibility:public"])

web_library(
name = "node_modules",
srcs = glob(["**/*"]),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @flow
module.exports.template = ({name, path, label, dependencies}) => `
#
# name: ${name}
# path: ${path}
# label: ${label}
# dependencies: ${dependencies.join('|')}
#`;
Empty file.
Loading
Loading