@mondaydotcomorg/z2h-cli 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,7 +26,7 @@ function defaultS3Remote(app) {
26
26
  async function initRepo(dir, remote) {
27
27
  await ensureGitignore(dir);
28
28
  await execGit(['init', '-b', 'master'], dir);
29
- await overrideGlobalHooks(dir);
29
+ await disableLocalHooks(dir);
30
30
  await execGit(['add', '.'], dir);
31
31
  await execGit(['commit', '-m', 'Initial commit'], dir);
32
32
  await execGit(['remote', 'add', 'origin', remote], dir);
@@ -49,7 +49,7 @@ async function guardCleanTree(dir) {
49
49
  }
50
50
  async function cloneRepo(remote, dir, env = {}) {
51
51
  await execa('git', ['clone', remote, dir], { env: { ...process.env, ...env } });
52
- await overrideGlobalHooks(dir);
52
+ await disableLocalHooks(dir);
53
53
  }
54
54
  async function safeBackupIfDirty(dir) {
55
55
  let status;
@@ -136,25 +136,17 @@ async function gatherConflictInfo(dir, gitEnv) {
136
136
  return { localCommits, remoteCommits, conflictingFiles };
137
137
  }
138
138
  /**
139
- * If the user has a global core.hooksPath (e.g. ~/.global-git-hooks with a
140
- * prevent-push-to-master script), override it locally so Z2H's non-interactive
141
- * pushes aren't blocked. Other repos on the machine stay protected.
139
+ * Z2H repos are agent-driven and non-interactive — git hooks (whether from
140
+ * global core.hooksPath, init.templateDir, or manually placed .git/hooks/*)
141
+ * serve no purpose and break non-interactive pushes. Point core.hooksPath to an
142
+ * empty local directory so all hook sources are bypassed for this repo only.
142
143
  */
143
- async function overrideGlobalHooks(dir) {
144
+ async function disableLocalHooks(dir) {
144
145
  if (await fs.pathExists(path.join(dir, LOCAL_HOOKS_DIR))) {
145
146
  return;
146
147
  }
147
- const globalHooksPath = await execGit(['config', '--global', 'core.hooksPath'], dir)
148
- .then(r => r.stdout.trim())
149
- .catch(() => '');
150
- if (!globalHooksPath) {
151
- return;
152
- }
153
148
  await fs.ensureDir(path.join(dir, LOCAL_HOOKS_DIR));
154
149
  await execGit(['config', '--local', 'core.hooksPath', LOCAL_HOOKS_DIR], dir);
155
- // eslint-disable-next-line no-console
156
- console.warn(`⚠️ Detected global git hooks at ${globalHooksPath}\n` +
157
- ` Overriding locally for this Z2H project (your other repos are unaffected).`);
158
150
  }
159
151
  async function ensureGitignore(dir) {
160
152
  const gitignorePath = path.join(dir, '.gitignore');
@@ -33,7 +33,7 @@ function defaultS3Remote(app) {
33
33
  async function initRepo(dir, remote) {
34
34
  await ensureGitignore(dir);
35
35
  await execGit(['init', '-b', 'master'], dir);
36
- await overrideGlobalHooks(dir);
36
+ await disableLocalHooks(dir);
37
37
  await execGit(['add', '.'], dir);
38
38
  await execGit(['commit', '-m', 'Initial commit'], dir);
39
39
  await execGit(['remote', 'add', 'origin', remote], dir);
@@ -56,7 +56,7 @@ async function guardCleanTree(dir) {
56
56
  }
57
57
  async function cloneRepo(remote, dir, env = {}) {
58
58
  await execa.execa('git', ['clone', remote, dir], { env: { ...process.env, ...env } });
59
- await overrideGlobalHooks(dir);
59
+ await disableLocalHooks(dir);
60
60
  }
61
61
  async function safeBackupIfDirty(dir) {
62
62
  let status;
@@ -143,25 +143,17 @@ async function gatherConflictInfo(dir, gitEnv) {
143
143
  return { localCommits, remoteCommits, conflictingFiles };
144
144
  }
145
145
  /**
146
- * If the user has a global core.hooksPath (e.g. ~/.global-git-hooks with a
147
- * prevent-push-to-master script), override it locally so Z2H's non-interactive
148
- * pushes aren't blocked. Other repos on the machine stay protected.
146
+ * Z2H repos are agent-driven and non-interactive — git hooks (whether from
147
+ * global core.hooksPath, init.templateDir, or manually placed .git/hooks/*)
148
+ * serve no purpose and break non-interactive pushes. Point core.hooksPath to an
149
+ * empty local directory so all hook sources are bypassed for this repo only.
149
150
  */
150
- async function overrideGlobalHooks(dir) {
151
+ async function disableLocalHooks(dir) {
151
152
  if (await fs__default.default.pathExists(path__default.default.join(dir, LOCAL_HOOKS_DIR))) {
152
153
  return;
153
154
  }
154
- const globalHooksPath = await execGit(['config', '--global', 'core.hooksPath'], dir)
155
- .then(r => r.stdout.trim())
156
- .catch(() => '');
157
- if (!globalHooksPath) {
158
- return;
159
- }
160
155
  await fs__default.default.ensureDir(path__default.default.join(dir, LOCAL_HOOKS_DIR));
161
156
  await execGit(['config', '--local', 'core.hooksPath', LOCAL_HOOKS_DIR], dir);
162
- // eslint-disable-next-line no-console
163
- console.warn(`⚠️ Detected global git hooks at ${globalHooksPath}\n` +
164
- ` Overriding locally for this Z2H project (your other repos are unaffected).`);
165
157
  }
166
158
  async function ensureGitignore(dir) {
167
159
  const gitignorePath = path__default.default.join(dir, '.gitignore');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/z2h-cli",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "bin": "./bin/z2h-cli.js",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -53,7 +53,7 @@ describe('defaultS3Remote', () => {
53
53
  describe('cloneRepo', () => {
54
54
  it('calls git clone with correct args', async () => {
55
55
  mockExeca.mockResolvedValueOnce(execaOk()); // git clone
56
- mockExeca.mockRejectedValueOnce(new Error('no global hooksPath')); // git config --global (no override needed)
56
+ mockExeca.mockResolvedValueOnce(execaOk()); // git config --local core.hooksPath
57
57
  await cloneRepo('s3://bucket/my-app/git', DIR);
58
58
  expect(mockExeca).toHaveBeenCalledWith(
59
59
  'git',
@@ -64,15 +64,14 @@ describe('cloneRepo', () => {
64
64
 
65
65
  it('merges supplied env into process.env for git subprocess', async () => {
66
66
  mockExeca.mockResolvedValueOnce(execaOk()); // git clone
67
- mockExeca.mockRejectedValueOnce(new Error('no global hooksPath')); // git config --global
67
+ mockExeca.mockResolvedValueOnce(execaOk()); // git config --local core.hooksPath
68
68
  await cloneRepo('s3://bucket/my-app/git', DIR, { AWS_ACCESS_KEY_ID: 'KEY' });
69
69
  const opts = (mockExeca.mock.calls[0] as unknown as unknown[])[2] as { env: Record<string, string> };
70
70
  expect(opts.env['AWS_ACCESS_KEY_ID']).toBe('KEY');
71
71
  });
72
72
 
73
- it('overrides global hooks locally when core.hooksPath is set', async () => {
73
+ it('disables hooks locally after clone', async () => {
74
74
  mockExeca.mockResolvedValueOnce(execaOk()); // git clone
75
- mockExeca.mockResolvedValueOnce(execaOk('/Users/dev/.global-git-hooks')); // git config --global core.hooksPath
76
75
  mockExeca.mockResolvedValueOnce(execaOk()); // git config --local core.hooksPath
77
76
  await cloneRepo('s3://bucket/my-app/git', DIR);
78
77
  expect(mockExeca).toHaveBeenCalledWith(
@@ -81,6 +80,13 @@ describe('cloneRepo', () => {
81
80
  expect.objectContaining({})
82
81
  );
83
82
  });
83
+
84
+ it('skips disabling hooks when .zth/hooks already exists', async () => {
85
+ mockExeca.mockResolvedValueOnce(execaOk()); // git clone
86
+ mockFs.pathExists.mockResolvedValueOnce(true as never); // .zth/hooks exists
87
+ await cloneRepo('s3://bucket/my-app/git', DIR);
88
+ expect(mockExeca).toHaveBeenCalledTimes(1); // only the clone call
89
+ });
84
90
  });
85
91
 
86
92
  // ── guardCleanTree ───────────────────────────────────────────────────────────
@@ -39,7 +39,7 @@ export function defaultS3Remote(app: string): string {
39
39
  export async function initRepo(dir: string, remote: string): Promise<void> {
40
40
  await ensureGitignore(dir);
41
41
  await execGit(['init', '-b', 'master'], dir);
42
- await overrideGlobalHooks(dir);
42
+ await disableLocalHooks(dir);
43
43
  await execGit(['add', '.'], dir);
44
44
  await execGit(['commit', '-m', 'Initial commit'], dir);
45
45
  await execGit(['remote', 'add', 'origin', remote], dir);
@@ -65,7 +65,7 @@ export async function guardCleanTree(dir: string): Promise<void> {
65
65
 
66
66
  export async function cloneRepo(remote: string, dir: string, env: Record<string, string> = {}): Promise<void> {
67
67
  await execa('git', ['clone', remote, dir], { env: { ...process.env, ...env } });
68
- await overrideGlobalHooks(dir);
68
+ await disableLocalHooks(dir);
69
69
  }
70
70
 
71
71
  export async function safeBackupIfDirty(dir: string): Promise<void> {
@@ -158,31 +158,18 @@ async function gatherConflictInfo(dir: string, gitEnv?: GitEnv): Promise<Conflic
158
158
  }
159
159
 
160
160
  /**
161
- * If the user has a global core.hooksPath (e.g. ~/.global-git-hooks with a
162
- * prevent-push-to-master script), override it locally so Z2H's non-interactive
163
- * pushes aren't blocked. Other repos on the machine stay protected.
161
+ * Z2H repos are agent-driven and non-interactive — git hooks (whether from
162
+ * global core.hooksPath, init.templateDir, or manually placed .git/hooks/*)
163
+ * serve no purpose and break non-interactive pushes. Point core.hooksPath to an
164
+ * empty local directory so all hook sources are bypassed for this repo only.
164
165
  */
165
- async function overrideGlobalHooks(dir: string): Promise<void> {
166
+ async function disableLocalHooks(dir: string): Promise<void> {
166
167
  if (await fs.pathExists(path.join(dir, LOCAL_HOOKS_DIR))) {
167
168
  return;
168
169
  }
169
170
 
170
- const globalHooksPath = await execGit(['config', '--global', 'core.hooksPath'], dir)
171
- .then(r => r.stdout.trim())
172
- .catch(() => '');
173
-
174
- if (!globalHooksPath) {
175
- return;
176
- }
177
-
178
171
  await fs.ensureDir(path.join(dir, LOCAL_HOOKS_DIR));
179
172
  await execGit(['config', '--local', 'core.hooksPath', LOCAL_HOOKS_DIR], dir);
180
-
181
- // eslint-disable-next-line no-console
182
- console.warn(
183
- `⚠️ Detected global git hooks at ${globalHooksPath}\n` +
184
- ` Overriding locally for this Z2H project (your other repos are unaffected).`
185
- );
186
173
  }
187
174
 
188
175
  async function ensureGitignore(dir: string): Promise<void> {