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

--sync-workspace-lock issue #877

Closed
5 tasks done
achille1789 opened this issue Jun 18, 2024 · 6 comments · Fixed by #881
Closed
5 tasks done

--sync-workspace-lock issue #877

achille1789 opened this issue Jun 18, 2024 · 6 comments · Fixed by #881
Labels
enhancement New feature or request

Comments

@achille1789
Copy link

Describe the bug

A short overview first:
I have a"postinstall" script that runs "husky install" (Husky is npm package).
Using Husky, I run a script on the git "pre-commit" hook that checks the name of the branch and fails the commit in case the branch name is not formatted as expected (branch "main" fails the script).
When I merge a branch on "main", the pipeline:

  1. sets this env vars: HUSKY=0
  2. installs the dependencies but because of the above env var, Husky is not initialised
  3. runs "lerna version"
  4. lerna bumps the version, updates the package-lock.json, commits and pushes the tag

Everything works as expected as long as I don't set syncWorkspaceLock: true in lerna.json (I use npm).
Setting the flag to true, Lerna fails to commit because the "pre-commit" hook is triggered and the script throws an error because the branch name is "main".

Expectation

As suggested by the docs, I should use syncWorkspaceLock: true and Lerna should be able to commit.

Reproduction

Should be possible to reproduce the issue following my bug description.

Lerna config and logs

lerna.json

{
  "$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json",
  "version": "independent",
  "command": {
    "version": {
      "allowBranch": [
        "main"
      ],
      "message": "chore(release): [ci skip] version bump",
      "conventionalCommits": true,
      "private": false
    }
  },
  "yes": true,
  "changelogPreset": "./changelog-preset.config.js",
  "ignoreChanges": [
    "**/Tests/**"
  ],
  "useWorkspaces": true
}

lerna-debug.log

<!-- If you have a `lerna-debug.log` available, please paste it here -->
<!-- Otherwise, feel free to delete this <details> block -->

Environment Info

System:
OS: macOS 14.5
CPU: (10) arm64 Apple M1 Pro
Memory: 258.52 MB / 32.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.12.2 - ~/.nvm/versions/node/v20.12.2/bin/node
npm: 10.5.0 - ~/.nvm/versions/node/v20.12.2/bin/npm
Browsers:
Chrome: 125.0.6422.176
Safari: 17.5
npmPackages:
@lerna-lite/cli: ^3.5.1 => 3.5.1
@lerna-lite/run: ^3.5.1 => 3.5.1
@lerna-lite/version: ^3.5.2 => 3.5.2

Used Package Manager

npm

Validations

@ghiscoding
Copy link
Member

ghiscoding commented Jun 18, 2024

I don't use Husky and don't have much knowledge about it, all I know is that below is what the npm lock file update runs. This flag was created to sync the lock file and that's all it should do. I added this flag to replace the previous and often failing implementation that tried to modify the lock file directly, the new sync flag is simply calling whichever package manager is installed to do the synching of the lock file (we should always rely on the package manager instead of trying to modify the lock file ourselves which is a bad idea). It does accept extra npm arguments if that helps.

case 'npm':
default:
inputLockfileName = 'package-lock.json';
if (await validateFileExists(join(cwd, inputLockfileName))) {
const localNpmVersion = execPackageManagerSync('npm', ['--version']);
log.silly(`npm`, `current local npm version is "${localNpmVersion}"`);
// with npm version >=8.5.0, we can simply call "npm install --package-lock-only"
if (semver.gte(localNpmVersion, '8.5.0')) {
log.verbose('lock', `updating lock file via "npm install --package-lock-only"`);
await execPackageManager('npm', ['install', '--package-lock-only', ...npmClientArgs], { cwd });
} else {
log.error(
'lock',
'your npm version is lower than 8.5.0 which is the minimum requirement to use `--sync-workspace-lock`'
);
}
outputLockfileName = inputLockfileName;
}
break;

Looking at the Version command, the sync seems to happen before the version and postversion lifecycles.

} else if (this.options.syncWorkspaceLock) {
// update lock file, with npm client defined when `--sync-workspace-lock` is enabled
chain = chain.then(() =>
runInstallLockFileOnly(npmClient, this.project.manifest.location, this.options.npmClientArgs || []).then(
(lockfilePath) => {
if (lockfilePath) {
changedFiles.add(lockfilePath);
}
}
)
);
}

Then the version lifecycle is called later at line 838 and the postversion lifecycle is at line 860. The order of the lifecycle for the lock file is exactly the same as the original Lerna when it tried to update the lock file directly. From what I can remember, the lock file must be updated before being added to the list of git changes and so before the version lifecycle

if (!this.hasRootedLeaf) {
// exec version lifecycle in root (after all updates)
chain = chain.then(() => this.runRootLifecycle('version'));
}
if (this.commitAndTag) {
chain = chain.then(() => gitAdd(Array.from(changedFiles), this.gitOpts, this.execOpts, this.options.dryRun));
}
return chain;
}
async commitAndTagUpdates() {
if (this.project.isIndependent()) {
this.tags = await this.gitCommitAndTagVersionForUpdates();
} else {
this.tags = await this.gitCommitAndTagVersion();
}
// run the postversion script for each update
await pMap(this.packagesToVersion, (pkg) => this.runPackageLifecycle(pkg, 'postversion'));
if (!this.hasRootedLeaf) {
// run postversion, if set, in the root directory
await this.runRootLifecycle('postversion');
}
}

So I don't know what your problem is since I don't use that kind of scripts on my side and I can't provide much more info. The only suggestion I could give is to look at the available version Lifecycle Scripts and maybe use a different one if that helps, otherwise I have no idea. Another suggestion might be to change the logs to verbose and see what commands it runs, and also maybe try it via the --dry-run mode which might help to see what runs.

in my case I don't need to use this sync flag because I use the workspace: protocol which technically speaking never changes the lock file neither git changes because workspace: are only replaced at publish time. However that is not true for npm and that is why this sync flag was created. I know the Jest project use this flag with Lerna-Lite but they also switched to workspace: not long after so they probably don't need the flag either.

EDIT

Actually someone did install Husky for conventional commits checks in one of my project and I also had to set HUSKY: 0 because it failed without it but that's all I did and it all worked correctly after. Note that I use dynamic Lerna version command because I have a checkbox in my release to sometime test it in dry-run mode and I build the Lerna query dynamically

(ref release.yml)

- name: Lerna Version 🏷️
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
          NPM_CONFIG_PROVENANCE: true
          HUSKY: 0
        run: |
          git config --global user.name "${{ github.actor }}"
          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
          pnpm whoami
          pnpm exec ${{ env.LERNA_VERSION_QUERY }}

@achille1789
Copy link
Author

Hi @ghiscoding many thanks for your quick and detailed response!

I investigated a little bit and basically the problem is that npm install --package-lock-only runs all the npm hooks, so setting "syncWorkspaceLock: true" was causing husky to be initialised, because HUSKY env var was already out of scope when this command run.
Leaving "syncWorkspaceLock: false" doesn't run npm install --package-lock-only so there is no problem.

I bypassed the issue moving HUSKY env var in the parent scope and now even setting "syncWorkspaceLock: true", when Lerna runs npm install --package-lock-only the HUSKY var is set and Husky is not initialised.

Anyway, looking at the original Lerna code:
https://github.com/lerna/lerna/blob/7bd983510110d00d9c3db94fb99f74773ab75d40/libs/commands/version/src/index.ts?#L811-L827

it is running npm install --package-lock-only --ignore-scripts that prevents to run the NPM hooks. So I don't know if it worths to do the same, just to avoid any issue in future when people migrate from Lerna to Lerna-lite

@ghiscoding
Copy link
Member

Anyway, looking at the original Lerna code: https://github.com/lerna/lerna/blob/7bd983510110d00d9c3db94fb99f74773ab75d40/libs/commands/version/src/index.ts?#L811-L827

it is running npm install --package-lock-only --ignore-scripts that prevents to run the NPM hooks. So I don't know if it worths to do the same, just to avoid any issue in future when people migrate from Lerna to Lerna-lite

hmm there is actually a CLI option runScriptsOnLockfileUpdate to pass for the command to ignore script, I guess we could add the same option in here too

@ghiscoding
Copy link
Member

ghiscoding commented Jun 19, 2024

Side note and as mentioned above you could also add the ignore script yourself via the npmClientArgs in the Lerna version command, so I guess something like this should work (untested), you can refer to the docs --npm-client-args

{
  "command": {
    "version": {
      "npmClientArgs": ["--ignore-scripts"]
    }
}

that is until I have time to add the other skip script run option. or you could also contribute the missing option 😉

@ghiscoding ghiscoding added enhancement New feature or request and removed need more info need help labels Jun 20, 2024
@achille1789
Copy link
Author

Side note and as mentioned above you could also add the ignore script yourself via the npmClientArgs in the Lerna version command, so I guess something like this should work (untested), you can refer to the docs --npm-client-args

{
  "command": {
    "version": {
      "npmClientArgs": ["--ignore-scripts"]
    }
}

that is until I have time to add the other skip script run option. or you could also contribute the missing option 😉

Hi @ghiscoding, as I said, I already solved my problem but I will keep in mind this as future reference!
Thanks

@ghiscoding
Copy link
Member

ghiscoding commented Jun 27, 2024

latest release v3.6.0 will no longer run scripts when npm install runs, the behavior is now the same as Lerna and if you want to run the scripts then the new option runScriptsOnLockfileUpdate will let you do that (which is also the same option as Lerna)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants