diff --git a/cli.py b/cli.py index 1905f4d1..25a2eec6 100755 --- a/cli.py +++ b/cli.py @@ -9,6 +9,8 @@ """ import os +import sys +import traceback import click from mergin import ( @@ -63,6 +65,13 @@ def _init_client(): return MerginClient(url, auth_token='Bearer {}'.format(auth_token)) +def _print_unhandled_exception(): + """ Outputs details of an unhandled exception that is being handled right now """ + click.secho("Unhandled exception!", fg='red') + for line in traceback.format_exception(*sys.exc_info()): + click.echo(line) + + @click.group() def cli(): pass @@ -99,7 +108,7 @@ def init(project, directory, public): c.create_project(project, directory, is_public=public) click.echo('Done') except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() @cli.command() @@ -145,7 +154,7 @@ def download(project, directory): print("Cancelling...") download_project_cancel(job) except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() def num_version(name): @@ -201,7 +210,7 @@ def push(): print("Cancelling...") push_project_cancel(job) except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() @cli.command() @@ -236,7 +245,7 @@ def pull(): print("Cancelling...") pull_project_cancel(job) except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() @cli.command() @@ -281,7 +290,7 @@ def remove(project): shutil.rmtree(os.path.join(os.getcwd())) click.echo('Done') except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() if __name__ == '__main__': diff --git a/mergin/client_pull.py b/mergin/client_pull.py index 7b24cfe3..31a3c639 100644 --- a/mergin/client_pull.py +++ b/mergin/client_pull.py @@ -367,6 +367,9 @@ def pull_project_async(mc, directory): # The basefile does not exist for some reason. This should not happen normally (maybe user removed the file # or we removed it within previous pull because we failed to apply patch the older version for some reason). # But it's not a problem - we will download the newest version and we're sorted. + file_path = file['path'] + mp.log.info(f"missing base file for {file_path} -> going to download it (version {server_version})") + file['version'] = server_version items = _download_items(file, temp_dir, diff_only=False) dest_file_path = mp.fpath(file["path"], temp_dir) #dest_file_path = os.path.join(os.path.dirname(os.path.normpath(os.path.join(temp_dir, file['path']))), os.path.basename(file['path'])) diff --git a/mergin/test/test_client.py b/mergin/test/test_client.py index 442de4c1..a46a629d 100644 --- a/mergin/test/test_client.py +++ b/mergin/test/test_client.py @@ -391,3 +391,36 @@ def test_new_project_sync(mc): mp = MerginProject(project_dir) local_changes = mp.get_push_changes() assert not local_changes["added"] and not local_changes["removed"] and not local_changes["updated"] + + +def test_missing_basefile_pull(mc): + """ Test pull of a project where basefile of a .gpkg is missing for some reason + (it should gracefully handle it by downloading the missing basefile) + """ + + test_project = 'test_missing_basefile_pull' + project = API_USER + '/' + test_project + project_dir = os.path.join(TMP_DIR, test_project) # primary project dir for updates + project_dir_2 = os.path.join(TMP_DIR, test_project + '_2') # concurrent project dir + test_data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), test_project) + + cleanup(mc, project, [project_dir, project_dir_2]) + # create remote project + shutil.copytree(test_data_dir, project_dir) + mc.create_project_and_push(test_project, project_dir) + + # update our gpkg in a different directory + mc.download_project(project, project_dir_2) + shutil.copy(os.path.join(TEST_DATA_DIR, "inserted_1_A.gpkg"), os.path.join(project_dir_2, "base.gpkg")) + mc.pull_project(project_dir_2) + mc.push_project(project_dir_2) + + # make some other local change + shutil.copy(os.path.join(TEST_DATA_DIR, "inserted_1_B.gpkg"), os.path.join(project_dir, "base.gpkg")) + + # remove the basefile to simulate the issue + os.remove(os.path.join(project_dir, '.mergin', 'base.gpkg')) + + # try to sync again -- it should not crash + mc.pull_project(project_dir) + mc.push_project(project_dir) diff --git a/mergin/test/test_missing_basefile_pull/base.gpkg b/mergin/test/test_missing_basefile_pull/base.gpkg new file mode 100644 index 00000000..6aa18bca Binary files /dev/null and b/mergin/test/test_missing_basefile_pull/base.gpkg differ diff --git a/mergin/test/test_missing_basefile_pull/test.qgs b/mergin/test/test_missing_basefile_pull/test.qgs new file mode 100644 index 00000000..e69de29b