diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index abc50b24075ff92f18832462441b969f54908035..dee8fbf0798b04a1c754d6a3adb8b5899ffccdac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,11 @@ install_py37: - eossr-codemeta2zenodo --help - eossr-codemeta2zenodo --input_codemeta_file ./codemeta.json - cat .zenodo.json + - echo "testing all entry points and bash scripts" + - eossr-upload_new_deposit -h + - eossr-upload_new_version_deposit -h + - eossr-check_connection_zenodo -h + - which parse_last_release_git.sh only: - branches diff --git a/eossr/api/apizenodo.py b/eossr/api/apizenodo.py index 7eea1f13bca82099fd5521a3e58e010f45f39a3e..36b0ef88592c5391094119715afd0f6fc567cad5 100644 --- a/eossr/api/apizenodo.py +++ b/eossr/api/apizenodo.py @@ -313,7 +313,7 @@ class ZenodoAPI: else: pass - def test_upload_to_zenodo(self): + def check_upload_to_zenodo(self): """ `Tests` the different stages of the GitLab-Zenodo connection and that the status_code returned by every stage is the correct one. diff --git a/eossr/scripts/check_connection_zenodo.py b/eossr/scripts/check_connection_zenodo.py new file mode 100644 index 0000000000000000000000000000000000000000..b9ef240f8d689459a839887e40c69ba003827ffa --- /dev/null +++ b/eossr/scripts/check_connection_zenodo.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +import argparse +from distutils.util import strtobool +from eossr.api.apizenodo import ZenodoAPI + + +def main(): + # Required arguments + parser = argparse.ArgumentParser(description="Test the connection to zenodo and all the stages of a new upload.") + + parser.add_argument('--token', '-t', type=str, + dest='zenodo_token', + help='Personal access token to (sandbox)Zenodo', + required=True) + + parser.add_argument('--sandbox', '-s', action='store', + type=lambda x: bool(strtobool(x)), + dest='sandbox_flag', + help='Set the Zenodo environment.' + 'If True connects with Zenodo. If False with Sandbox Zenodo', + default=False) + + parser.add_argument('--project_dir', '-p', action='store', + dest='project_dir', + help='Path to the root directory of the directory to be uploaded. ' + 'DEFAULT; assumed to be on it, i.e., "./"', + default='./' + ) + + args = parser.parse_args() + + zenodo = ZenodoAPI(access_token=args.zenodo_token, + sandbox=args.sandbox_flag, + proj_root_dir=args.project_dir + ) + + zenodo.test_upload_to_zenodo() + + +if __name__ == '__main__': + main() diff --git a/eossr/scripts/eossr_upload_new_deposit.py b/eossr/scripts/eossr_upload_new_deposit.py new file mode 100644 index 0000000000000000000000000000000000000000..a36385d2585c5b731ae490b400222aeccbf7d6c5 --- /dev/null +++ b/eossr/scripts/eossr_upload_new_deposit.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python + +import os +import json +import argparse +from pathlib import Path +from distutils.util import strtobool +from eossr.api.apizenodo import ZenodoAPI +from eossr.metadata.codemeta2zenodo import parse_codemeta_and_write_zenodo_metadata_file + + +def create_zenodo_metadata(metadata_filename, repo_root_dir='./'): + """ + Checks for a zenodo metadata file, otherwise it looks for a codemeta.json file to create a the .zenodo.json file + + :param metadata_filename: str + path and name to the zenodo metadata json file + NOT TO BE CHANGED. The file must be named `.zenodo.json` and be stored in the root directory of the library. + + :param repo_root_dir: str + Path to the project root directory to be uploaded + """ + # root_dir = find_root_directory() + root_dir = Path(repo_root_dir) + print(f'working dir : {root_dir}') # DEBUG + + files_json = [file for file in os.listdir(root_dir) if file.endswith('.json')] + print(f'JSON files found: \n{files_json}') + + zenodo_metadata_filename = metadata_filename + codemeta_file = 'codemeta.json' + + if codemeta_file in files_json and zenodo_metadata_filename not in files_json: + print(f"\nCreating {zenodo_metadata_filename} automatically on the CI pipeline.\n") + parse_codemeta_and_write_zenodo_metadata_file(codemeta_file, zenodo_metadata_filename) + + elif os.path.isfile(zenodo_metadata_filename): + print(f"\n{zenodo_metadata_filename} metadata file found in the root directory of the library ! \n") + pass + + else: + print(f"\n{codemeta_file} not found, thus any zenodo_metadata file `{zenodo_metadata_filename}` was" + f" created during the CI pipeline." + f"Please provide one so that the CI can run correctly (examples in the 'codemeta_utils' directory)") + exit(-1) + + +def main(): + parser = argparse.ArgumentParser(description="Upload new deposit entry to Zenodo") + + parser.add_argument('--token', '-t', type=str, + dest='zenodo_token', + help='Personal access token to (sandbox)Zenodo', + required=True) + + parser.add_argument('--sandbox', '-s', action='store', + type=lambda x: bool(strtobool(x)), + dest='sandbox_flag', + help='Set the Zenodo environment.' + 'If True connects with Zenodo. If False with Sanbox Zenodo', + default=False) + + parser.add_argument('--input-dir', '-i', type=str, + dest='input_directory', + help='Path to the directory containing the files to upload.' + 'ALL files will be uploaded.', + required=True) + + args = parser.parse_args() + + zenodo = ZenodoAPI( + access_token=args.zenodo_token, + sandbox=args.sandbox_flag # True for sandbox.zenodo.org !! False for zenodo.org + ) + + # 1 - create empty deposit + new_entry = zenodo.create_new_entry() + + if new_entry.status_code < 399: + deposition_id = new_entry.json()['id'] + doi = new_entry.json()['metadata']['prereserve_doi']['doi'] + print(f" * Status {new_entry.status_code}. New entry to Zenodo created ! Deposition id {deposition_id}") + else: + print(f" ! ERROR; the NEW entry COULD NOT be created.") + print(new_entry.json()) + + # 2 - upload files + for file in os.listdir(args.input_directory): + full_path_file = args.input_directory + '/' + file + + new_upload = zenodo.upload_file_entry( + deposition_id, + name_file=file, + path_file=full_path_file + ) + + print(f" * File {file} correctly uploaded !\n", new_upload) + + # 3 - Create the zenodo metadata file from a codemeta.json file + zenodo_metadata_filename = '.zenodo.json' + create_zenodo_metadata(zenodo_metadata_filename) + + # And upload the repository metadata + with open(zenodo_metadata_filename) as json_file: + entry_metadata = json.load(json_file) + + # entry_info['metadata']['doi'] = doi # In the new version of the API the doi is updated automatically. + update_entry = zenodo.update_metadata_entry( + deposition_id, + json_metadata=entry_metadata + ) + + if update_entry.status_code < 399: + print(f" * Status {update_entry.status_code}. Repository information correctly uploaded !") + else: + print(f" ! Repository information NOT correctly uploaded ! Status {update_entry.status_code}\n", + update_entry.json()) + + # 4 - publish entry + publish = zenodo.publish_entry(deposition_id) + + if publish.status_code == 204: + print(" * New deposit correctly published !\n") + print(f" * The new doi should look like 10.5281/{deposition_id}. However please") + print(f" ** Check the upload at {zenodo.zenodo_api_url[:-4]}/deposit/{deposition_id} **") + else: + print(f" ! New deposit NOT correctly published ! Status {publish.status_code}\n", + publish.json()) + + +if __name__ == '__main__': + main() diff --git a/eossr/scripts/eossr_upload_new_version_deposit.py b/eossr/scripts/eossr_upload_new_version_deposit.py new file mode 100644 index 0000000000000000000000000000000000000000..a696ab696b27bfdd7213d74514271234ad01aff6 --- /dev/null +++ b/eossr/scripts/eossr_upload_new_version_deposit.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + + +import os +import json +import argparse +from pathlib import Path +from distutils.util import strtobool +from eossr.api.apizenodo import ZenodoAPI +from eossr.metadata.codemeta2zenodo import parse_codemeta_and_write_zenodo_metadata_file + + +def create_zenodo_metadata(metadata_filename, repo_root_dir='./'): + """ + Checks for a zenodo metadata file, otherwise it looks for a codemeta.json file to create a the .zenodo.json file + + param metadata_filename: str + path and name to the zenodo metada json file + NOT TO BE CHANGED. The file must be named `.zenodo.json` and be stored in the root directory of the library. + """ + # root_dir = find_root_directory() + root_dir = Path(repo_root_dir) + + files_json = [file for file in os.listdir(root_dir) if file.endswith('.json')] + print(f'JSON files : {files_json}') + + zenodo_metadata_filename = metadata_filename + codemeta_file = 'codemeta.json' + + if codemeta_file in files_json and zenodo_metadata_filename not in files_json: + print(f"\nCreating {zenodo_metadata_filename} automatically at the CI pipeline.\n") + parse_codemeta_and_write_zenodo_metadata_file(codemeta_file, zenodo_metadata_filename) + + elif os.path.isfile(zenodo_metadata_filename): + print(f"\n{zenodo_metadata_filename} metadata file found in the root directory of the library ! \n") + pass + + else: + print(f"\n{codemeta_file} not found, thus any zenodo_metadata file `{zenodo_metadata_filename}` was" + f" created during the CI pipeline." + f"Please provide one so that the CI can run correctly (examples in the 'codemeta_utils' directory)") + exit(-1) + + +def main(): + parser = argparse.ArgumentParser(description="Upload a new version of an existing deposit to Zenodo") + + parser.add_argument('--token', '-t', type=str, + dest='zenodo_token', + help='Personal access token to (sandbox)Zenodo', + required=True) + + parser.add_argument('--sandbox', '-s', action='store', + type=lambda x: bool(strtobool(x)), + dest='sandbox_flag', + help='Set the Zenodo environment.' + 'If True connects with Zenodo. If False with Sandbox Zenodo', + default=False) + + parser.add_argument('--input-dir', '-i', type=str, + dest='input_directory', + help='Path to the directory containing the files to upload.' + 'ALL files will be uploaded.', + required=True) + + parser.add_argument('--deposit_id', '-id', type=str, + dest='deposit_id', + help='deposit_id of the deposit that is going to be updated by a new version', + required=True) + + args = parser.parse_args() + + zenodo = ZenodoAPI( + access_token=args.zenodo_token, + sandbox=args.sandbox_flag # True for sandbox.zenodo.org !! False for zenodo.org + ) + + # 1 - request a new version of an existing deposit + new_version = zenodo.new_version_entry(args.deposit_id) + + if new_version.status_code < 399: + print(f" * Status {new_version.status_code}. New version of the {args.deposit_id} entry correctly created !") + else: + print(f" ! ERROR; new version of the {args.deposit_id} entry COULD NOT be created.") + print(new_version.json()) + + new_deposition_id = new_version.json()['links']['latest_draft'].rsplit('/')[-1] + + # PRE-2 - If you DO NOT want to erase the old files, comment the following lines + old_files_ids = [file['id'] for file in new_version.json()['files']] + for file_id in old_files_ids: + zenodo.erase_file_entry( + new_deposition_id, + file_id + ) + + # 2 - Upload new version of file(s) + for file in os.listdir(args.input_directory): + full_path_file = args.input_directory + '/' + file + + new_upload = zenodo.upload_file_entry( + new_deposition_id, + name_file=file, + path_file=full_path_file + ) + + print(f" * File {file} correctly uploaded !\n", new_upload) + + # 3 - Look for a zenodo metadata file, otherwise try to create one + zenodo_metadata_filename = '.zenodo.json' + create_zenodo_metadata(zenodo_metadata_filename) + + with open(zenodo_metadata_filename) as json_file: + update_entry_metadata = json.load(json_file) + + # update_entry_info['metadata']['doi'] = doi # In the new version of the API the doi is updated automatically. + update_entry = zenodo.update_metadata_entry( + new_deposition_id, + json_metadata=update_entry_metadata + ) + + if update_entry.status_code < 399: + print(f" * Status {update_entry.status_code}. Repository information correctly uploaded !\n") + else: + print(f" ! Repository information NOT correctly uploaded ! Status {update_entry.status_code}\n", + update_entry.json()) + + # 4 - publish entry - to publish the entry, uncomment the two lone below + publish = zenodo.publish_entry(new_deposition_id) + + if publish.status_code == 204: + print(" * New version of the old deposition correctly published !\n") + print(f" * Old deposition id {args.deposit_id}, new deposition id {new_deposition_id}") + print(f" * The new doi should look like 10.5281/{new_deposition_id}. However please") + print(f" ** Check the upload at {zenodo.zenodo_api_url[:-4]}/deposit/{new_deposition_id} **") + else: + print(f" ! New deposit NOT correctly published ! Status {publish.status_code}\n", + publish.json()) + + +if __name__ == '__main__': + main() diff --git a/eossr/scripts/parse_last_release_git.sh b/eossr/scripts/parse_last_release_git.sh new file mode 100755 index 0000000000000000000000000000000000000000..cf70750df0a454e0a90ac3b56582d3e3b906105c --- /dev/null +++ b/eossr/scripts/parse_last_release_git.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +REPOSITORY_NAME="$1" +REPOSITORY_BASE_URL="$2" + +LAST_RELEASE=`git ls-remote --tags --refs --sort="v:refname" $REPOSITORY_BASE_URL.git | tail -n1 | sed 's/.*\///'` + +if [ -z "$LAST_RELEASE" ]; then + echo "No tag / new release found ! - Or error when parsing. Downloading last commit to the repository (master branch) ;" + wget -O $REPOSITORY_NAME-master.zip "$REPOSITORY_BASE_URL"/-/archive/master/"$REPOSITORY_NAME"-master.zip + mv $REPOSITORY_NAME-master.zip ./build +else + echo "$LAST_RELEASE tag / release found !" + wget -O $REPOSITORY_NAME-$LAST_RELEASE.zip "$REPOSITORY_BASE_URL"/-/archive/"$LAST_RELEASE"/"$REPOSITORY_NAME"-"$LAST_RELEASE".zip + mv $REPOSITORY_NAME-$LAST_RELEASE.zip ./build +fi diff --git a/eossr/scripts/tests/test_eossr_codemeta2zenodo.py b/eossr/scripts/tests/test_scripts.py similarity index 100% rename from eossr/scripts/tests/test_eossr_codemeta2zenodo.py rename to eossr/scripts/tests/test_scripts.py diff --git a/setup.py b/setup.py index 1a5f5f5cdc94cdb32dff4c42e07199ef74057fc3..69f6bcf68551760d53287c7792b38a57e4e2b353 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,10 @@ import re from setuptools import setup, find_packages entry_points = {'console_scripts': [ - 'eossr-codemeta2zenodo = eossr.scripts.eossr_codemeta2zenodo:main' + 'eossr-codemeta2zenodo = eossr.scripts.eossr_codemeta2zenodo:main', + 'eossr-upload_new_deposit = eossr.scripts.eossr_upload_new_deposit:main', + 'eossr-upload_new_version_deposit = eossr.scripts.eossr_upload_new_version_deposit:main', + 'eossr-check_connection_zenodo = eossr.scripts.check_connection_zenodo:main' ] } @@ -23,7 +26,7 @@ setup( "requests" ], packages=find_packages(), - # scripts=[], + scripts=['eossr/scripts/parse_last_release_git.sh'], tests_require=['pytest'], author='Thomas Vuillaume & Enrique Garcia', author_email='vuillaume<at>lapp.in2p3.fr',