article-cli 1.1.0__tar.gz → 1.2.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: article-cli
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: CLI tool for managing LaTeX articles with git integration and Zotero bibliography
5
5
  Project-URL: Homepage, https://github.com/feelpp/article.cli
6
6
  Project-URL: Documentation, https://github.com/feelpp/article.cli#readme
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "article-cli"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  authors = [
9
9
  {name = "Christophe Prud'homme", email = "prudhomm@cemosis.fr"},
10
10
  ]
@@ -5,10 +5,10 @@ This package provides tools for:
5
5
  - Git release management with gitinfo2 support
6
6
  - Zotero bibliography synchronization
7
7
  - LaTeX build file cleanup
8
- - Git hooks setup
8
+ - Git hooks setup
9
9
  """
10
10
 
11
- __version__ = "1.1.0"
11
+ __version__ = "1.2.0"
12
12
  __author__ = "Christophe Prud'homme"
13
13
  __email__ = "prudhomm@cemosis.fr"
14
14
 
@@ -19,4 +19,11 @@ from .git_manager import GitManager
19
19
  from .repository_setup import RepositorySetup
20
20
  from .latex_compiler import LaTeXCompiler
21
21
 
22
- __all__ = ["main", "Config", "ZoteroBibTexUpdater", "GitManager", "RepositorySetup", "LaTeXCompiler"]
22
+ __all__ = [
23
+ "main",
24
+ "Config",
25
+ "ZoteroBibTexUpdater",
26
+ "GitManager",
27
+ "RepositorySetup",
28
+ "LaTeXCompiler",
29
+ ]
@@ -56,9 +56,7 @@ Environment variables:
56
56
  init_parser = subparsers.add_parser(
57
57
  "init", help="Initialize repository with workflows and configuration"
58
58
  )
59
- init_parser.add_argument(
60
- "--title", required=True, help="Article title"
61
- )
59
+ init_parser.add_argument("--title", required=True, help="Article title")
62
60
  init_parser.add_argument(
63
61
  "--authors",
64
62
  required=True,
@@ -86,41 +84,40 @@ Environment variables:
86
84
  subparsers.add_parser("clean", help="Clean LaTeX build files")
87
85
 
88
86
  # Compile command
89
- compile_parser = subparsers.add_parser("compile", help="Compile LaTeX document using latexmk")
87
+ compile_parser = subparsers.add_parser(
88
+ "compile", help="Compile LaTeX document using latexmk"
89
+ )
90
90
  compile_parser.add_argument(
91
- "tex_file",
92
- nargs="?",
93
- help="LaTeX file to compile (auto-detected if not specified)"
91
+ "tex_file",
92
+ nargs="?",
93
+ help="LaTeX file to compile (auto-detected if not specified)",
94
94
  )
95
95
  compile_parser.add_argument(
96
- "--engine",
97
- choices=["latexmk", "pdflatex"],
96
+ "--engine",
97
+ choices=["latexmk", "pdflatex"],
98
98
  default="latexmk",
99
- help="LaTeX engine to use (default: latexmk)"
99
+ help="LaTeX engine to use (default: latexmk)",
100
100
  )
101
101
  compile_parser.add_argument(
102
- "--shell-escape",
103
- action="store_true",
104
- help="Enable shell escape (for code highlighting, etc.)"
102
+ "--shell-escape",
103
+ action="store_true",
104
+ help="Enable shell escape (for code highlighting, etc.)",
105
105
  )
106
106
  compile_parser.add_argument(
107
- "--clean-first",
108
- action="store_true",
109
- help="Clean build files before compilation"
107
+ "--clean-first",
108
+ action="store_true",
109
+ help="Clean build files before compilation",
110
110
  )
111
111
  compile_parser.add_argument(
112
- "--clean-after",
113
- action="store_true",
114
- help="Clean build files after compilation"
112
+ "--clean-after", action="store_true", help="Clean build files after compilation"
115
113
  )
116
114
  compile_parser.add_argument(
117
- "--watch",
118
- action="store_true",
119
- help="Watch for changes and recompile automatically"
115
+ "--watch",
116
+ action="store_true",
117
+ help="Watch for changes and recompile automatically",
120
118
  )
121
119
  compile_parser.add_argument(
122
- "--output-dir",
123
- help="Output directory for compiled files"
120
+ "--output-dir", help="Output directory for compiled files"
124
121
  )
125
122
 
126
123
  # Create command
@@ -223,48 +220,50 @@ def handle_compile_command(args: argparse.Namespace, config: Config) -> int:
223
220
  """Handle the compile command"""
224
221
  try:
225
222
  from .latex_compiler import LaTeXCompiler
226
-
223
+
227
224
  # Auto-detect tex file if not provided
228
225
  tex_file = args.tex_file
229
226
  if not tex_file:
230
227
  tex_file = _auto_detect_tex_file()
231
228
  if not tex_file:
232
- print_error("No .tex file specified and none found in current directory")
229
+ print_error(
230
+ "No .tex file specified and none found in current directory"
231
+ )
233
232
  return 1
234
-
233
+
235
234
  # Validate tex file exists
236
235
  tex_path = Path(tex_file)
237
236
  if not tex_path.exists():
238
237
  print_error(f"LaTeX file not found: {tex_file}")
239
238
  return 1
240
-
239
+
241
240
  compiler = LaTeXCompiler(config)
242
-
241
+
243
242
  # Clean before compilation if requested
244
243
  if args.clean_first:
245
244
  print_info("Cleaning build files before compilation...")
246
245
  git_manager = GitManager()
247
246
  latex_config = config.get_latex_config()
248
247
  git_manager.clean_latex_files(latex_config["clean_extensions"])
249
-
248
+
250
249
  # Compile the document
251
250
  success = compiler.compile(
252
251
  tex_file=tex_file,
253
252
  engine=args.engine,
254
253
  shell_escape=args.shell_escape,
255
254
  output_dir=args.output_dir,
256
- watch=args.watch
255
+ watch=args.watch,
257
256
  )
258
-
257
+
259
258
  # Clean after compilation if requested
260
259
  if args.clean_after and success:
261
260
  print_info("Cleaning build files after compilation...")
262
261
  git_manager = GitManager()
263
262
  latex_config = config.get_latex_config()
264
263
  git_manager.clean_latex_files(latex_config["clean_extensions"])
265
-
264
+
266
265
  return 0 if success else 1
267
-
266
+
268
267
  except Exception as e:
269
268
  print_error(f"Compilation failed: {e}")
270
269
  return 1
@@ -274,18 +273,18 @@ def _auto_detect_tex_file() -> Optional[str]:
274
273
  """Auto-detect main .tex file in current directory"""
275
274
  current_dir = Path.cwd()
276
275
  tex_files = list(current_dir.glob("*.tex"))
277
-
276
+
278
277
  if not tex_files:
279
278
  return None
280
-
279
+
281
280
  if len(tex_files) == 1:
282
281
  return tex_files[0].name
283
-
282
+
284
283
  # Multiple .tex files - prefer common patterns
285
284
  for pattern in ["main.tex", "article.tex", f"{current_dir.name}.tex"]:
286
285
  if (current_dir / pattern).exists():
287
286
  return pattern
288
-
287
+
289
288
  # Return first .tex file found
290
289
  print_info(f"Multiple .tex files found, using: {tex_files[0].name}")
291
290
  return tex_files[0].name
@@ -5,9 +5,7 @@ Provides compilation functionality that mimics LaTeX Workshop configuration
5
5
  with support for latexmk and pdflatex engines.
6
6
  """
7
7
 
8
- import os
9
8
  import subprocess
10
- import sys
11
9
  import time
12
10
  from pathlib import Path
13
11
  from typing import Dict, List, Optional
@@ -103,6 +101,8 @@ class LaTeXCompiler:
103
101
  # Stream output in real time
104
102
  try:
105
103
  while True:
104
+ if process.stdout is None:
105
+ break
106
106
  output = process.stdout.readline()
107
107
  if output == "" and process.poll() is not None:
108
108
  break
@@ -178,7 +178,7 @@ class LaTeXCompiler:
178
178
  try:
179
179
  # Run multiple passes for cross-references, bibliography, etc.
180
180
  passes = ["First pass", "Second pass", "Third pass"]
181
-
181
+
182
182
  for i, pass_name in enumerate(passes):
183
183
  print_info(f"{pass_name}...")
184
184
  result = subprocess.run(
@@ -237,12 +237,14 @@ class LaTeXCompiler:
237
237
  # Core options (from LaTeX Workshop)
238
238
  if shell_escape:
239
239
  cmd.append("--shell-escape")
240
-
241
- cmd.extend([
242
- "-pdf",
243
- "-interaction=nonstopmode",
244
- "-synctex=1",
245
- ])
240
+
241
+ cmd.extend(
242
+ [
243
+ "-pdf",
244
+ "-interaction=nonstopmode",
245
+ "-synctex=1",
246
+ ]
247
+ )
246
248
 
247
249
  if output_dir:
248
250
  cmd.extend(["-outdir", output_dir])
@@ -264,11 +266,13 @@ class LaTeXCompiler:
264
266
  if shell_escape:
265
267
  cmd.append("--shell-escape")
266
268
 
267
- cmd.extend([
268
- "-synctex=1",
269
- "-interaction=nonstopmode",
270
- "-file-line-error",
271
- ])
269
+ cmd.extend(
270
+ [
271
+ "-synctex=1",
272
+ "-interaction=nonstopmode",
273
+ "-file-line-error",
274
+ ]
275
+ )
272
276
 
273
277
  if output_dir:
274
278
  cmd.extend(["-output-directory", output_dir])
@@ -282,7 +286,10 @@ class LaTeXCompiler:
282
286
  base_name = tex_path.stem
283
287
 
284
288
  # Check if bibliography is needed
285
- if "citation undefined" in latex_output.lower() or "rerun" in latex_output.lower():
289
+ if (
290
+ "citation undefined" in latex_output.lower()
291
+ or "rerun" in latex_output.lower()
292
+ ):
286
293
  # Check for .aux file to determine if we need bibtex
287
294
  aux_file = tex_path.with_suffix(".aux")
288
295
  if aux_file.exists():
@@ -306,12 +313,12 @@ class LaTeXCompiler:
306
313
  size = pdf_path.stat().st_size
307
314
  size_mb = size / (1024 * 1024)
308
315
  print_info(f"PDF size: {size_mb:.2f} MB")
309
-
316
+
310
317
  # Show modification time
311
318
  mtime = pdf_path.stat().st_mtime
312
319
  mtime_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime))
313
320
  print_info(f"Generated: {mtime_str}")
314
-
321
+
315
322
  except Exception:
316
323
  pass # Silently ignore errors getting file info
317
324
 
@@ -346,14 +353,16 @@ class LaTeXCompiler:
346
353
  def print_dependency_status(self) -> None:
347
354
  """Print status of LaTeX dependencies"""
348
355
  print_info("Checking LaTeX dependencies...")
349
-
356
+
350
357
  deps = self.check_dependencies()
351
-
358
+
352
359
  for tool, available in deps.items():
353
360
  status = "✅ Available" if available else "❌ Not found"
354
361
  print(f" {tool}: {status}")
355
-
362
+
356
363
  if not all(deps.values()):
357
- print_info("\nSome LaTeX tools are missing. Install a LaTeX distribution like TeX Live.")
364
+ print_info(
365
+ "\nSome LaTeX tools are missing. Install a LaTeX distribution like TeX Live."
366
+ )
358
367
  print_info("On macOS: brew install --cask mactex")
359
- print_info("On Ubuntu: sudo apt-get install texlive-full")
368
+ print_info("On Ubuntu: sudo apt-get install texlive-full")
@@ -8,10 +8,8 @@ Provides functionality to initialize LaTeX article repositories with:
8
8
  - Git hooks and configuration
9
9
  """
10
10
 
11
- import os
12
- import shutil
13
11
  from pathlib import Path
14
- from typing import Dict, List, Optional
12
+ from typing import List, Optional
15
13
 
16
14
  from .zotero import print_error, print_info, print_success
17
15
 
@@ -47,23 +45,24 @@ class RepositorySetup:
47
45
  - .vscode/settings.json with LaTeX Workshop configuration
48
46
  - LTeX dictionary files for spell checking
49
47
  - hooks/post-commit for gitinfo2 integration
48
+ - main.tex if no .tex file exists
50
49
 
51
50
  Args:
52
51
  title: Article title
53
52
  authors: List of author names
54
53
  group_id: Zotero group ID
55
54
  force: Overwrite existing files if True
56
- main_tex_file: Main .tex filename (auto-detected if None)
55
+ main_tex_file: Main .tex filename (auto-detected if None, created if missing)
57
56
 
58
57
  Returns:
59
58
  True if successful, False otherwise
60
59
  """
61
60
  print_info(f"Initializing repository at: {self.repo_path}")
62
61
 
63
- # Detect or validate main .tex file
64
- tex_file = self._detect_tex_file(main_tex_file)
62
+ # Detect or validate main .tex file, create if missing
63
+ tex_file = self._detect_or_create_tex_file(main_tex_file, title, authors, force)
65
64
  if not tex_file:
66
- print_error("No main .tex file found or specified")
65
+ print_error("Failed to detect or create main .tex file")
67
66
  return False
68
67
 
69
68
  project_name = self.repo_path.name
@@ -113,27 +112,39 @@ class RepositorySetup:
113
112
  print_error(f"Failed to initialize repository: {e}")
114
113
  return False
115
114
 
116
- def _detect_tex_file(self, specified: Optional[str]) -> Optional[str]:
115
+ def _detect_or_create_tex_file(
116
+ self, specified: Optional[str], title: str, authors: List[str], force: bool
117
+ ) -> Optional[str]:
117
118
  """
118
- Detect main .tex file in repository
119
+ Detect main .tex file in repository or create one if missing
119
120
 
120
121
  Args:
121
122
  specified: User-specified filename (takes priority)
123
+ title: Article title (for creating new .tex file)
124
+ authors: List of author names (for creating new .tex file)
125
+ force: Overwrite existing file if True
122
126
 
123
127
  Returns:
124
- Main .tex filename or None if not found
128
+ Main .tex filename or None on failure
125
129
  """
126
130
  if specified:
127
131
  tex_path = self.repo_path / specified
128
132
  if tex_path.exists():
129
133
  return specified
130
- print_error(f"Specified .tex file not found: {specified}")
134
+ # Specified file doesn't exist - create it
135
+ if self._create_tex_file(specified, title, authors, force):
136
+ return specified
131
137
  return None
132
138
 
133
139
  # Auto-detect .tex files
134
140
  tex_files = list(self.repo_path.glob("*.tex"))
135
141
 
136
142
  if not tex_files:
143
+ # No .tex files found - create main.tex
144
+ default_name = "main.tex"
145
+ print_info(f"No .tex file found, creating {default_name}")
146
+ if self._create_tex_file(default_name, title, authors, force):
147
+ return default_name
137
148
  return None
138
149
 
139
150
  if len(tex_files) == 1:
@@ -151,6 +162,94 @@ class RepositorySetup:
151
162
  )
152
163
  return tex_files[0].name
153
164
 
165
+ def _create_tex_file(
166
+ self, filename: str, title: str, authors: List[str], force: bool
167
+ ) -> bool:
168
+ """
169
+ Create a basic LaTeX article file
170
+
171
+ Args:
172
+ filename: Name of the .tex file to create
173
+ title: Article title
174
+ authors: List of author names
175
+ force: Overwrite if exists
176
+
177
+ Returns:
178
+ True if successful
179
+ """
180
+ tex_path = self.repo_path / filename
181
+
182
+ if tex_path.exists() and not force:
183
+ print_info(f"{filename} already exists (use --force to overwrite)")
184
+ return True
185
+
186
+ # Format authors for LaTeX
187
+ authors_latex = " \\and ".join(authors)
188
+
189
+ tex_content = f"""\\documentclass[a4paper,11pt]{{article}}
190
+
191
+ % Essential packages
192
+ \\usepackage[utf8]{{inputenc}}
193
+ \\usepackage[T1]{{fontenc}}
194
+ \\usepackage{{lmodern}}
195
+ \\usepackage{{amsmath,amssymb,amsthm}}
196
+ \\usepackage{{graphicx}}
197
+ \\usepackage{{hyperref}}
198
+ \\usepackage[margin=1in]{{geometry}}
199
+
200
+ % Bibliography
201
+ \\usepackage[style=numeric,sorting=none]{{biblatex}}
202
+ \\addbibresource{{references.bib}}
203
+
204
+ % Git version information
205
+ \\usepackage{{gitinfo2}}
206
+
207
+ % Title and authors
208
+ \\title{{{title}}}
209
+ \\author{{{authors_latex}}}
210
+ \\date{{\\today}}
211
+
212
+ \\begin{{document}}
213
+
214
+ \\maketitle
215
+
216
+ \\begin{{abstract}}
217
+ Your abstract goes here.
218
+ \\end{{abstract}}
219
+
220
+ \\section{{Introduction}}
221
+
222
+ Your introduction goes here.
223
+
224
+ \\section{{Methodology}}
225
+
226
+ Your methodology goes here.
227
+
228
+ \\section{{Results}}
229
+
230
+ Your results go here.
231
+
232
+ \\section{{Conclusion}}
233
+
234
+ Your conclusion goes here.
235
+
236
+ % Print bibliography
237
+ \\printbibliography
238
+
239
+ % Git information (optional - appears in footer)
240
+ \\vfill
241
+ \\hrule
242
+ \\small
243
+ \\noindent Git version: \\gitAbbrevHash{{}} (\\gitAuthorIsoDate) \\\\
244
+ Branch: \\gitBranch
245
+
246
+ \\end{{document}}
247
+ """
248
+
249
+ tex_path.write_text(tex_content)
250
+ print_success(f"Created: {tex_path.relative_to(self.repo_path)}")
251
+ return True
252
+
154
253
  def _create_directories(self) -> None:
155
254
  """Create necessary directory structure"""
156
255
  directories = [
@@ -163,9 +262,7 @@ class RepositorySetup:
163
262
  directory.mkdir(parents=True, exist_ok=True)
164
263
  print_info(f"Created directory: {directory.relative_to(self.repo_path)}")
165
264
 
166
- def _create_workflow(
167
- self, project_name: str, tex_file: str, force: bool
168
- ) -> bool:
265
+ def _create_workflow(self, project_name: str, tex_file: str, force: bool) -> bool:
169
266
  """
170
267
  Create GitHub Actions workflow file
171
268
 
@@ -180,7 +277,9 @@ class RepositorySetup:
180
277
  workflow_path = self.repo_path / ".github" / "workflows" / "latex.yml"
181
278
 
182
279
  if workflow_path.exists() and not force:
183
- print_info(f"Workflow already exists: {workflow_path.name} (use --force to overwrite)")
280
+ print_info(
281
+ f"Workflow already exists: {workflow_path.name} (use --force to overwrite)"
282
+ )
184
283
  return True
185
284
 
186
285
  # Extract base name (without .tex extension)
@@ -203,7 +302,7 @@ class RepositorySetup:
203
302
  Returns:
204
303
  Workflow YAML content
205
304
  """
206
- return f'''name: Compile LaTeX and Release PDF
305
+ return f"""name: Compile LaTeX and Release PDF
207
306
 
208
307
  # This workflow uses article-cli for:
209
308
  # - Git hooks setup (article-cli setup)
@@ -639,7 +738,7 @@ jobs:
639
738
  echo "" >> $GITHUB_STEP_SUMMARY
640
739
  echo "Please check the workflow logs for details." >> $GITHUB_STEP_SUMMARY
641
740
  fi
642
- '''
741
+ """
643
742
 
644
743
  def _create_pyproject(
645
744
  self,
@@ -665,15 +764,13 @@ jobs:
665
764
  pyproject_path = self.repo_path / "pyproject.toml"
666
765
 
667
766
  if pyproject_path.exists() and not force:
668
- print_info(
669
- f"pyproject.toml already exists (use --force to overwrite)"
670
- )
767
+ print_info(f"pyproject.toml already exists (use --force to overwrite)")
671
768
  return True
672
769
 
673
770
  # Format authors for TOML
674
771
  authors_toml = ",\n ".join([f'{{name = "{author}"}}' for author in authors])
675
772
 
676
- pyproject_content = f'''# Article Repository Dependency Management
773
+ pyproject_content = f"""# Article Repository Dependency Management
677
774
  # This file manages dependencies for the LaTeX article project
678
775
 
679
776
  [project]
@@ -709,7 +806,7 @@ clean_extensions = [
709
806
  ".fls", ".synctex.gz", ".toc", ".fdb_latexmk",
710
807
  ".idx", ".ilg", ".ind", ".lof", ".lot"
711
808
  ]
712
- '''
809
+ """
713
810
 
714
811
  pyproject_path.write_text(pyproject_content)
715
812
  print_success(f"Created: {pyproject_path.relative_to(self.repo_path)}")
@@ -744,7 +841,7 @@ clean_extensions = [
744
841
 
745
842
  authors_list = "\n".join([f"- {author}" for author in authors])
746
843
 
747
- readme_content = f'''# {title}
844
+ readme_content = f"""# {title}
748
845
 
749
846
  ## Authors
750
847
 
@@ -867,7 +964,7 @@ article-cli config show
867
964
  url = {{https://github.com/feelpp/{project_name}}}
868
965
  }}
869
966
  ```
870
- '''
967
+ """
871
968
 
872
969
  readme_path.write_text(readme_content)
873
970
  print_success(f"Created: {readme_path.relative_to(self.repo_path)}")
@@ -958,10 +1055,12 @@ references.bib.backup
958
1055
  vscode_settings_path = self.repo_path / ".vscode" / "settings.json"
959
1056
 
960
1057
  if vscode_settings_path.exists() and not force:
961
- print_info(".vscode/settings.json already exists (use --force to overwrite)")
1058
+ print_info(
1059
+ ".vscode/settings.json already exists (use --force to overwrite)"
1060
+ )
962
1061
  return True
963
1062
 
964
- settings_content = '''{
1063
+ settings_content = """{
965
1064
  "latex-workshop.latex.recipes": [
966
1065
  {
967
1066
  "name": "latexmk-pdf",
@@ -1023,7 +1122,7 @@ references.bib.backup
1023
1122
  "ltex.enabled": true,
1024
1123
  "ltex.language": "en-US"
1025
1124
  }
1026
- '''
1125
+ """
1027
1126
 
1028
1127
  vscode_settings_path.write_text(settings_content)
1029
1128
  print_success(f"Created: {vscode_settings_path.relative_to(self.repo_path)}")
@@ -1059,7 +1158,9 @@ parametrical
1059
1158
  print_info(f"Created: {dictionary_path.relative_to(self.repo_path)}")
1060
1159
 
1061
1160
  # Hidden false positives file (empty initially)
1062
- false_positives_path = self.repo_path / ".vscode" / "ltex.hiddenFalsePositives.en-US.txt"
1161
+ false_positives_path = (
1162
+ self.repo_path / ".vscode" / "ltex.hiddenFalsePositives.en-US.txt"
1163
+ )
1063
1164
  if not false_positives_path.exists() or force:
1064
1165
  false_positives_path.write_text("")
1065
1166
  print_info(f"Created: {false_positives_path.relative_to(self.repo_path)}")
@@ -1083,7 +1184,7 @@ parametrical
1083
1184
  return True
1084
1185
 
1085
1186
  # gitinfo2 post-commit hook
1086
- post_commit_content = '''#!/bin/sh
1187
+ post_commit_content = """#!/bin/sh
1087
1188
  # Copyright 2015 Brent Longborough
1088
1189
  # Part of gitinfo2 package Version 2
1089
1190
  # Release 2.0.7 2015-11-22
@@ -1114,15 +1215,18 @@ git --no-pager log -1 --date=short --decorate=short \\
1114
1215
  firsttagdescribe={$FIRSTTAG},
1115
1216
  reltag={$RELTAG}
1116
1217
  ]{gitexinfo}" HEAD > .git/gitHeadInfo.gin
1117
- '''
1218
+ """
1118
1219
 
1119
1220
  post_commit_path.write_text(post_commit_content)
1120
-
1221
+
1121
1222
  # Make the hook executable
1122
1223
  import stat
1123
- post_commit_path.chmod(post_commit_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
1124
-
1224
+
1225
+ post_commit_path.chmod(
1226
+ post_commit_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1227
+ )
1228
+
1125
1229
  print_success(f"Created: {post_commit_path.relative_to(self.repo_path)}")
1126
1230
  print_info("Made post-commit hook executable")
1127
-
1231
+
1128
1232
  return True
File without changes
File without changes
File without changes