From 50eb8cfdf74ee506652dc62dc13d7645d17bd204 Mon Sep 17 00:00:00 2001 From: Corentin Date: Thu, 12 Nov 2020 20:05:27 +0000 Subject: [PATCH 01/15] Fix URL in the Installation documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3031daa..dd0da2e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ _Hell is the .bats_ ## Installation ````bash -pip install git+https://github.com/Arubinu/jeanpaulstart.git +pip install git+https://github.com/cube-creative/jeanpaulstart.git ```` ## Usage as a CLI From 47446a3182421cf8b53b7c9cd368c8a5a774e30a Mon Sep 17 00:00:00 2001 From: Corentin Date: Thu, 12 Nov 2020 20:16:31 +0000 Subject: [PATCH 02/15] Add directory support to copy task (#8) --- README.fr-FR.md | 16 +++++++++++---- README.md | 16 +++++++++++---- jeanpaulstart/file_io.py | 40 +++++++++++++++++++++++++++++++----- jeanpaulstart/tasks/copy_.py | 5 +++-- 4 files changed, 62 insertions(+), 15 deletions(-) diff --git a/README.fr-FR.md b/README.fr-FR.md index ce8ba3b..1e6e831 100644 --- a/README.fr-FR.md +++ b/README.fr-FR.md @@ -118,16 +118,24 @@ Ces modules sont listés au démarrage de Jean-Paul Start, s'ils répondent aux ### Copy -Permet de copier un fichier +Permet de copier un fichier ou un dossier -Si la destination existe, et `force: no`, aucune action n'est effectuée +Si le chemin est un dossier, il est copié recursivement. Dans ce cas, si le chemin se termine par "/", seul le contenu de ce dossier sera copié vers la destination. + +Si la source est un dossier, la destination doit etre un dossier aussi. + +Si la destination existe, et `force: no`, aucune action n'est effectuée. + +- `force` est facultatif, sa valeur par défaut est `yes` +- `replace` est facultatif, sa valeur par défaut est `no` ````yaml - name: Name of task copy: - src: /path/to/source.ext - dest: /path/to/destination.ext + src: /path/to/source + dest: /path/to/destination force: [yes|no] + replace: [yes|no] ```` ### File diff --git a/README.md b/README.md index dd0da2e..5a4a3fb 100644 --- a/README.md +++ b/README.md @@ -131,16 +131,24 @@ Tasks are written as plugins, in the package `jeanpaulstart.tasks`, they must co ### Copy -Copies a file +Copies a file or a directory -If destination exists and `force: no`, nothing will happen +If path is a directory, it is copied recursively. In this case, if path ends with "/", only inside contents of that directory are copied to destination. + +If the source is a directory, the destination must be a directory too. + +If destination exists and `force: no`, nothing will happen. + +- `force` is not mandatory, defaults to `yes` +- `replace` is not mandatory, defaults to `no` ````yaml - name: Name of task copy: - src: /path/to/source.ext - dest: /path/to/destination.ext + src: /path/to/source + dest: /path/to/destination force: [yes|no] + replace: [yes|no] ```` ### File diff --git a/jeanpaulstart/file_io.py b/jeanpaulstart/file_io.py index aadcdff..199ce01 100644 --- a/jeanpaulstart/file_io.py +++ b/jeanpaulstart/file_io.py @@ -1,6 +1,7 @@ import os import io import shutil +import distutils.dir_util def read_file_utf16(filepath): @@ -12,14 +13,43 @@ def read_file_utf16(filepath): return f.read() -def copy(source, destination, force=True): - if not force and os.path.isfile(destination): +def copy(source, destination, force=True, replace=False): + if not force and os.path.exists(destination): return - dirname = os.path.dirname(destination) - if not os.path.isdir(dirname): os.makedirs(dirname) + # If source is a directory, this must be a directory too and + # if the destination exists, is a file, an exception is raised. + if os.path.isdir(source) and os.path.isfile(destination): + raise ValueError('The destination must be a directory') - shutil.copy(source, destination) + # If destination is a non-existent path and if either destination ends + # with "/" or source is a directory, destination is created. + if not os.path.exists(destination): + if destination.endswith('/') or os.path.isdir(source): + os.makedirs(destination) + + # If the source is a file and the destination dirname is a non-existent + # path, dirname is created. + if os.path.isfile(source): + dirname = os.path.dirname(destination) + if not os.path.exists(dirname): + os.makedirs(dirname) + + if os.path.isdir(source) and os.path.isdir(destination): + if not destination.endswith('/'): + name = os.path.split(source)[-1] + destination = os.path.join(destination, name) + + # Clear + if replace: + shutil.rmtree(destination) + + distutils.dir_util.copy_tree(source, destination) + # shutil.copytree(source, destination, dirs_exist_ok=True) + # dirs_exist_ok only introduced in 3.8 + + else: + shutil.copy(source, destination) def mkdir(path): diff --git a/jeanpaulstart/tasks/copy_.py b/jeanpaulstart/tasks/copy_.py index d727db8..1b9d0fa 100644 --- a/jeanpaulstart/tasks/copy_.py +++ b/jeanpaulstart/tasks/copy_.py @@ -11,10 +11,11 @@ def validate(user_data): def normalize_after_split(splitted): splitted['arguments']['force'] = splitted['arguments'].get('force', True) + splitted['arguments']['replace'] = splitted['arguments'].get('replace', False) return splitted -def apply_(src, dest, force): - file_io.copy(src, dest, force) +def apply_(src, dest, force, replace): + file_io.copy(src, dest, force, replace) return OK From d69026b706b073ed04b81317a279355a7c530912 Mon Sep 17 00:00:00 2001 From: Corentin Date: Sun, 24 Jan 2021 19:43:25 +0000 Subject: [PATCH 03/15] =?UTF-8?q?=E2=9C=A8=20Implement=20Modo=20Launcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/line_in_file.py | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 jeanpaulstart/tasks/line_in_file.py diff --git a/jeanpaulstart/tasks/line_in_file.py b/jeanpaulstart/tasks/line_in_file.py new file mode 100644 index 0000000..6b88321 --- /dev/null +++ b/jeanpaulstart/tasks/line_in_file.py @@ -0,0 +1,42 @@ +from jeanpaulstart.constants import * + + +TASK_COMMAND = 'line_in_file' + + +def validate(user_data): + return OK, "" + + +def normalize_after_split(splitted): + splitted['arguments']['replace'] = splitted['arguments'].get('replace', False) + return splitted + + +def apply_(filepath, value, insert_after, replace): + print(filepath, value, insert_after, replace) + insert_line(filepath, value, insert_after, replace) + + return OK + + +def insert_line(filepath, value, insert_after, replace): + value += "\n" + with open(filepath) as f: + content = f.readlines() + + row = 0 + if insert_after: + for index, line in enumerate(content): + if line.strip() == insert_after: + row = index + 1 + break + + if content[row] == value and not replace: + return + + content.insert(row, value) + + with open(filepath, 'w') as f: + for line in content: + f.write(line) From b21dd5601ab2069b64e55c52b5035f4ea7764492 Mon Sep 17 00:00:00 2001 From: Corentin Date: Sun, 24 Jan 2021 19:56:03 +0000 Subject: [PATCH 04/15] =?UTF-8?q?=F0=9F=94=A5=20Remove=20debug=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/line_in_file.py | 1 - 1 file changed, 1 deletion(-) diff --git a/jeanpaulstart/tasks/line_in_file.py b/jeanpaulstart/tasks/line_in_file.py index 6b88321..89d3d57 100644 --- a/jeanpaulstart/tasks/line_in_file.py +++ b/jeanpaulstart/tasks/line_in_file.py @@ -14,7 +14,6 @@ def normalize_after_split(splitted): def apply_(filepath, value, insert_after, replace): - print(filepath, value, insert_after, replace) insert_line(filepath, value, insert_after, replace) return OK From 6e050b512a44546267846644697b8f4aa5aeb056 Mon Sep 17 00:00:00 2001 From: Corentin Date: Mon, 25 Jan 2021 11:22:02 +0000 Subject: [PATCH 05/15] =?UTF-8?q?=F0=9F=94=A5=20Switch=20to=20shutil.copyt?= =?UTF-8?q?ree=20for=20distutils.dir=5Futil.copy=5Ftree=20raises=20strange?= =?UTF-8?q?=20exceptions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/file_io.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/jeanpaulstart/file_io.py b/jeanpaulstart/file_io.py index 199ce01..07dd2a0 100644 --- a/jeanpaulstart/file_io.py +++ b/jeanpaulstart/file_io.py @@ -17,7 +17,7 @@ def copy(source, destination, force=True, replace=False): if not force and os.path.exists(destination): return - # If source is a directory, this must be a directory too and + # If source is a directory, the destination must be a directory too and # if the destination exists, is a file, an exception is raised. if os.path.isdir(source) and os.path.isfile(destination): raise ValueError('The destination must be a directory') @@ -44,10 +44,7 @@ def copy(source, destination, force=True, replace=False): if replace: shutil.rmtree(destination) - distutils.dir_util.copy_tree(source, destination) - # shutil.copytree(source, destination, dirs_exist_ok=True) - # dirs_exist_ok only introduced in 3.8 - + shutil.copytree(source, destination, dirs_exist_ok=True) else: shutil.copy(source, destination) From 470141f70c68f647e09a04b136a0a29b80c3ec73 Mon Sep 17 00:00:00 2001 From: Corentin Date: Mon, 25 Jan 2021 13:13:26 +0000 Subject: [PATCH 06/15] =?UTF-8?q?=F0=9F=9A=A7=20Add=20skeleton=20for=20git?= =?UTF-8?q?=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/git.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 jeanpaulstart/tasks/git.py diff --git a/jeanpaulstart/tasks/git.py b/jeanpaulstart/tasks/git.py new file mode 100644 index 0000000..5c53899 --- /dev/null +++ b/jeanpaulstart/tasks/git.py @@ -0,0 +1,23 @@ +from jeanpaulstart.constants import * + + +TASK_COMMAND = 'git' + + +def validate(user_data): + return OK, "" + + +def normalize_after_split(splitted): + splitted['arguments']['sub_directory'] = splitted['arguments'].get('sub_directory', None) + return splitted + + +def apply_(url, destination, sub_directory): + checkout_remote(url, destination, sub_directory) + + return OK + + +def checkout_remote(url, destination, sub_directory): + pass From d55330a0fb4288f7b99a6ddeb20d6efa8e77a443 Mon Sep 17 00:00:00 2001 From: Corentin Date: Mon, 25 Jan 2021 13:14:33 +0000 Subject: [PATCH 07/15] =?UTF-8?q?=F0=9F=9A=A7=20Rename=20git=20module=20to?= =?UTF-8?q?=20git=5F=20to=20avoid=20conflict=20with=20third-party=20git=20?= =?UTF-8?q?package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/{git.py => git_.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename jeanpaulstart/tasks/{git.py => git_.py} (100%) diff --git a/jeanpaulstart/tasks/git.py b/jeanpaulstart/tasks/git_.py similarity index 100% rename from jeanpaulstart/tasks/git.py rename to jeanpaulstart/tasks/git_.py From 599da40e696ccf01f4c91639aa3b79139b9410e3 Mon Sep 17 00:00:00 2001 From: Corentin Date: Mon, 25 Jan 2021 16:18:25 +0000 Subject: [PATCH 08/15] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Make=20line=5Fin=5Ff?= =?UTF-8?q?ile=20safer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/line_in_file.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/jeanpaulstart/tasks/line_in_file.py b/jeanpaulstart/tasks/line_in_file.py index 89d3d57..9afe8dd 100644 --- a/jeanpaulstart/tasks/line_in_file.py +++ b/jeanpaulstart/tasks/line_in_file.py @@ -24,17 +24,20 @@ def insert_line(filepath, value, insert_after, replace): with open(filepath) as f: content = f.readlines() - row = 0 if insert_after: + row = None for index, line in enumerate(content): - if line.strip() == insert_after: + if line == insert_after + '\n': row = index + 1 break + else: + row = 0 if content[row] == value and not replace: return - content.insert(row, value) + if row is not None: + content.insert(row, value) with open(filepath, 'w') as f: for line in content: From c704ff9453e1f6b41280f0d580529dfb74a72b0c Mon Sep 17 00:00:00 2001 From: Corentin Date: Mon, 25 Jan 2021 18:48:48 +0000 Subject: [PATCH 09/15] =?UTF-8?q?=E2=9C=A8=20New=20Git=20JPS=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/git_.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/jeanpaulstart/tasks/git_.py b/jeanpaulstart/tasks/git_.py index 5c53899..281d272 100644 --- a/jeanpaulstart/tasks/git_.py +++ b/jeanpaulstart/tasks/git_.py @@ -1,23 +1,32 @@ +import git +import os from jeanpaulstart.constants import * - TASK_COMMAND = 'git' - +DEFAULT_BRANCH = 'master' def validate(user_data): return OK, "" def normalize_after_split(splitted): - splitted['arguments']['sub_directory'] = splitted['arguments'].get('sub_directory', None) + splitted['arguments']['branch'] = splitted['arguments'].get('branch', DEFAULT_BRANCH) return splitted -def apply_(url, destination, sub_directory): - checkout_remote(url, destination, sub_directory) +def apply_(url, dest, branch): + checkout_remote(url, dest, branch) return OK -def checkout_remote(url, destination, sub_directory): - pass +def checkout_remote(url, dest, branch): + if not os.path.exists(dest): + repo = git.Repo.clone_from(url, dest) + else: + repo = git.Repo(dest) + + repo.git.reset("--hard") + repo.git.clean("-df") + repo.git.pull("origin", branch) + repo.git.checkout(branch) From 3505c8ca25845fb5c8c9f50d83c837d1b17ba321 Mon Sep 17 00:00:00 2001 From: Corentin Date: Thu, 5 Aug 2021 20:17:48 +0100 Subject: [PATCH 10/15] =?UTF-8?q?=E2=9C=A8=20git=20task=20now=20removes=20?= =?UTF-8?q?untracked=20files=20when=20cleaning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/git_.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jeanpaulstart/tasks/git_.py b/jeanpaulstart/tasks/git_.py index 281d272..7b66340 100644 --- a/jeanpaulstart/tasks/git_.py +++ b/jeanpaulstart/tasks/git_.py @@ -5,6 +5,7 @@ TASK_COMMAND = 'git' DEFAULT_BRANCH = 'master' + def validate(user_data): return OK, "" @@ -27,6 +28,6 @@ def checkout_remote(url, dest, branch): repo = git.Repo(dest) repo.git.reset("--hard") - repo.git.clean("-df") + repo.git.clean("-dfx") repo.git.pull("origin", branch) repo.git.checkout(branch) From 1901a0d8db6bc94bab2dae9590bea692316b45fd Mon Sep 17 00:00:00 2001 From: Corentin Date: Fri, 6 Aug 2021 13:25:40 +0100 Subject: [PATCH 11/15] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20Fix=20branch=20sw?= =?UTF-8?q?itcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/git_.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jeanpaulstart/tasks/git_.py b/jeanpaulstart/tasks/git_.py index 7b66340..f7203d3 100644 --- a/jeanpaulstart/tasks/git_.py +++ b/jeanpaulstart/tasks/git_.py @@ -27,7 +27,8 @@ def checkout_remote(url, dest, branch): else: repo = git.Repo(dest) - repo.git.reset("--hard") + repo.git.reset("--hard", "origin/{}".format(branch)) repo.git.clean("-dfx") - repo.git.pull("origin", branch) - repo.git.checkout(branch) + repo.git.fetch("--all") + repo.git.pull("origin", branch, "--tags", "--no-edit") + repo.git.checkout("-B", branch, "origin/{}".format(branch)) From 2e33236ffeaf1ac92bf4df6ff643ec4ad0ae14fd Mon Sep 17 00:00:00 2001 From: Corentin Date: Fri, 1 Oct 2021 15:07:41 +0100 Subject: [PATCH 12/15] Git task now creates the destination parent directory if it does not exists --- jeanpaulstart/tasks/git_.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jeanpaulstart/tasks/git_.py b/jeanpaulstart/tasks/git_.py index f7203d3..b2c7afe 100644 --- a/jeanpaulstart/tasks/git_.py +++ b/jeanpaulstart/tasks/git_.py @@ -22,6 +22,10 @@ def apply_(url, dest, branch): def checkout_remote(url, dest, branch): + parent = os.path.abspath(os.path.join(dest, os.pardir)) + if not os.path.exists(parent): + os.makedirs(parent) + if not os.path.exists(dest): repo = git.Repo.clone_from(url, dest) else: From 683ff2c833a5515951566b744aa635182bf0422e Mon Sep 17 00:00:00 2001 From: Corentin Date: Fri, 1 Oct 2021 19:46:14 +0100 Subject: [PATCH 13/15] =?UTF-8?q?=F0=9F=90=9B=20Fetch=20before=20reset=20i?= =?UTF-8?q?n=20case=20of=20new=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/git_.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jeanpaulstart/tasks/git_.py b/jeanpaulstart/tasks/git_.py index b2c7afe..dd63ab2 100644 --- a/jeanpaulstart/tasks/git_.py +++ b/jeanpaulstart/tasks/git_.py @@ -31,6 +31,7 @@ def checkout_remote(url, dest, branch): else: repo = git.Repo(dest) + repo.git.fetch("--all") repo.git.reset("--hard", "origin/{}".format(branch)) repo.git.clean("-dfx") repo.git.fetch("--all") From 666751828098943b42eaf67f9de2b88328cb194a Mon Sep 17 00:00:00 2001 From: Corentin Date: Fri, 1 Oct 2021 19:46:33 +0100 Subject: [PATCH 14/15] =?UTF-8?q?=F0=9F=90=9B=20Expand=20path=20with=20env?= =?UTF-8?q?ironment=20variables=20if=20any?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/git_.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jeanpaulstart/tasks/git_.py b/jeanpaulstart/tasks/git_.py index dd63ab2..a7fc57a 100644 --- a/jeanpaulstart/tasks/git_.py +++ b/jeanpaulstart/tasks/git_.py @@ -1,5 +1,6 @@ import git import os +from string import Template from jeanpaulstart.constants import * TASK_COMMAND = 'git' @@ -22,6 +23,7 @@ def apply_(url, dest, branch): def checkout_remote(url, dest, branch): + dest = Template(dest).substitute(os.environ) parent = os.path.abspath(os.path.join(dest, os.pardir)) if not os.path.exists(parent): os.makedirs(parent) From 9809167fdccbfae165427790cb8703296f987e1f Mon Sep 17 00:00:00 2001 From: Corentin Date: Mon, 4 Oct 2021 15:07:47 +0100 Subject: [PATCH 15/15] =?UTF-8?q?=E2=9C=A8=20New=20Delete=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jeanpaulstart/tasks/delete_.py | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 jeanpaulstart/tasks/delete_.py diff --git a/jeanpaulstart/tasks/delete_.py b/jeanpaulstart/tasks/delete_.py new file mode 100644 index 0000000..127d999 --- /dev/null +++ b/jeanpaulstart/tasks/delete_.py @@ -0,0 +1,37 @@ +import os +import shutil +from jeanpaulstart.constants import * + + +TASK_COMMAND = 'delete' + + +def validate(user_data): + return OK, "" + + +def normalize_after_split(splitted): + return splitted + + +def apply_(path): + if not os.path.exists(path): + return OK + + if os.path.isdir(path): + if path[-1] in ("/", "\\"): # Delete content + for filename in os.listdir(path): + file_path = os.path.join(path, filename) + + if os.path.isfile(file_path) or os.path.islink(file_path): + os.unlink(file_path) + elif os.path.isdir(file_path): + os.system('rmdir /S /Q "{}"'.format(file_path)) + # shutil.rmtree(file_path) + else: # Delete whole directory + os.system('rmdir /S /Q "{}"'.format(path)) + # shutil.rmtree(path) + else: + os.remove(path) + + return OK