Skip to content

Commit 6f71925

Browse files
committed
autorun support
1 parent 045d8e3 commit 6f71925

File tree

4 files changed

+98
-0
lines changed

4 files changed

+98
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ information.
2222
pip install nbgitpuller
2323
```
2424

25+
### Configuration
26+
27+
Copy `jupyter_git_pull_config.py` to one of your Jupyter configuration paths (as determined from `jupyter --paths`) and edit it to meet your needs.
28+
2529
## Example
2630

2731
This example shows how to use the [nbgitpuller link generator]

jupyter_git_pull_config.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# May be set to a list of URLs described as Python regular expressions (using re.fullmatch())
2+
# where it is permitted to autorun scripts from the pulled project as a pre-initialisation
3+
# step.
4+
#
5+
# WARNING: Enable this only if you understand and accept the risks of AUTORUN.INF.
6+
# ----
7+
# c.NbGitPuller.autorun_allow = [
8+
# r'https://github\.com/org/name\.git',
9+
# r'https://github\.com/org-two/name-two\.git'
10+
# ]
11+
# ----
12+
#
13+
# To allow all sources (*not* recommended) use:
14+
# ----
15+
# c.NbGitPuller.autorun_allow = True
16+
# ----
17+
#
18+
# The default is 'False' which means the autorun functionality is completely disabled
19+
#c.NbGitPuller.autorun_allow = False
20+
21+
# List of scripts to search for when attempting to autorun. The first match will
22+
# be run with a single argument of 'init' or 'update' depending on what nbgitpuller
23+
# is doing.
24+
# ----
25+
# c.NbGitPuller.autorun_script = [
26+
# '.nbgitpuller.script',
27+
# '.different.script'
28+
# ]
29+
# ----
30+
#
31+
# The 'script' must be executable and when checked out on a 'exec' (ie. not a 'noexec') mountpoint
32+
#
33+
# The default is the empty list.
34+
#c.NbGitPuller.autorun_script = []

nbgitpuller/application.py

+29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from .version import __version__ # noqa
22
from .pull import GitPuller # noqa
33
from jupyter_server.extension.application import ExtensionApp
4+
from traitlets import Bool, CRegExp, List, Unicode, Union
5+
from traitlets.config import Configurable
46
import os
57

68

@@ -12,6 +14,33 @@ class NbGitPuller(ExtensionApp):
1214
os.path.join(os.path.dirname(__file__), 'static')
1315
]
1416

17+
autorun_allow = Union(
18+
[Bool(), List(CRegExp())],
19+
default_value=False,
20+
config=True,
21+
help="""
22+
List of URLs described as Python regular expressions (using re.fullmatch()) where
23+
it is permitted to autorun scripts from the pulled project as a pre-initialisation
24+
step. Enable this only if you understand and accept the risks of AUTORUN.INF.
25+
26+
When set to boolean True, all URLs are allowed, whilst False (default) autorun
27+
is disabled completely.
28+
"""
29+
)
30+
31+
autorun_script = List(
32+
Unicode(),
33+
default_value=[],
34+
config=True,
35+
help="""
36+
List of scripts to search for when attempting to autorun. The first match will
37+
be run with a single argument of 'init' or 'update' depending on what nbgitpuller
38+
is doing.
39+
40+
Enable this only if you understand and accept the risks of AUTORUN.INF.
41+
"""
42+
)
43+
1544
def initialize_handlers(self):
1645
from .handlers import (
1746
SyncHandler,

nbgitpuller/pull.py

+31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import re
23
import subprocess
34
import logging
45
import time
@@ -81,6 +82,9 @@ def __init__(self, git_url, repo_dir, branch, **kwargs):
8182
elif not self.branch_exists(self.branch_name):
8283
raise ValueError(f"Branch: {self.branch_name} -- not found in repo: {self.git_url}")
8384

85+
self.autorun_allow = kwargs.pop('autorun_allow', False)
86+
self.autorun_script = kwargs.pop('autorun_script', [])
87+
8488
newargs = {k: v for k, v in kwargs.items() if v is not None}
8589
super(GitPuller, self).__init__(**newargs)
8690

@@ -143,6 +147,30 @@ def pull(self):
143147
else:
144148
yield from self.update()
145149

150+
def autorun(self, operation="method"):
151+
"""
152+
Search for and execute the autorun script.
153+
"""
154+
155+
if not self.autorun_allow:
156+
return
157+
if not any(( re.fullmatch(pattern, self.git_url) for pattern in self.autorun_allow )):
158+
logging.info('autorun skipped, URL does not match any rules')
159+
return
160+
161+
script = next(( s for s in self.autorun_script if os.access(os.path.join(self.repo_dir, s), os.X_OK)), None)
162+
if not script:
163+
logging.info('autorun skipped, no matching (executable) script')
164+
return
165+
166+
try:
167+
for line in execute_cmd([ os.path.join(self.repo_dir, script), operation ], cwd=self.repo_dir, close_fds=True):
168+
yield line
169+
except subprocess.CalledProcessError:
170+
m = f"Problem autorunning {script}"
171+
logging.exception(m)
172+
raise ValueError(m)
173+
146174
def initialize_repo(self):
147175
"""
148176
Clones repository
@@ -154,6 +182,7 @@ def initialize_repo(self):
154182
clone_args.extend(['--branch', self.branch_name])
155183
clone_args.extend(["--", self.git_url, self.repo_dir])
156184
yield from execute_cmd(clone_args)
185+
yield from self.autorun('init')
157186
logging.info('Repo {} initialized'.format(self.repo_dir))
158187

159188
def reset_deleted_files(self):
@@ -343,6 +372,8 @@ def update(self):
343372
yield from self.ensure_lock()
344373
yield from self.merge()
345374

375+
yield from self.autorun('update')
376+
346377

347378
def main():
348379
"""

0 commit comments

Comments
 (0)