You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							240 lines
						
					
					
						
							7.1 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							240 lines
						
					
					
						
							7.1 KiB
						
					
					
				
								#!/usr/bin/env python3
							 | 
						|
								'''
							 | 
						|
								Title: pyissuesyncd
							 | 
						|
								(c) 2021 Jake "Poikilos" Gustafson
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								Purpose:
							 | 
						|
								This python-based daemon synchronizes issues (one-way) from one
							 | 
						|
								repository to another.
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								License:
							 | 
						|
								See the license file in the included EnlivenMinetest directory or at
							 | 
						|
								[EnlivenMinetest](https://github.com/poikilos/EnlivenMinetest)
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								Outputs:
							 | 
						|
								data_directory: The data directory for this service daemon is
							 | 
						|
								os.path.join(profile, ".cache", "pyissuesyncd").
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								required arguments:
							 | 
						|
								--dst-repo (or set the DST_REPO environment variable)
							 | 
						|
								    Issues and dependent data will be overwritten at this API URL.
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								optional arguments:
							 | 
						|
								Environment variables get be set, but a CLI argument will override the
							 | 
						|
								corresponding variable noted below in all caps.
							 | 
						|
								
							 | 
						|
								The two _CACHE directories below are used as the single_cache option
							 | 
						|
								for the Repo (see enissue.py's Repo class for documentation).
							 | 
						|
								
							 | 
						|
								--src-cache: Set the directory to store a cached version of the source repo's data.
							 | 
						|
								* defaults to SRC_CACHE or os.path.join(data_directory, "source")
							 | 
						|
								
							 | 
						|
								--dst-cache: Set the directory to store a cached version of the destination repo's data.
							 | 
						|
								* defaults to DST_CACHE or os.path.join(data_directory, "destination")
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								Examples:
							 | 
						|
								DST_REPO=https://example.com/git/repo pyissuesyncd
							 | 
						|
								pyissuesyncd --dst-repo https://example.com/git/repo
							 | 
						|
								
							 | 
						|
								'''
							 | 
						|
								import os
							 | 
						|
								import sys
							 | 
						|
								import json
							 | 
						|
								# see <https://stackoverflow.com/questions/5574702/how-to-print-to-stderr-in-python>
							 | 
						|
								def error(*args, **kwargs):
							 | 
						|
								    print(*args, file=sys.stderr, **kwargs)
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								myFilePath = os.path.realpath(__file__)
							 | 
						|
								me = os.path.basename(__file__)
							 | 
						|
								myDir = os.path.dirname(myFilePath)
							 | 
						|
								
							 | 
						|
								try:
							 | 
						|
								    import enissue
							 | 
						|
								except ModuleNotFoundError as ex:
							 | 
						|
								    PATH = os.environ.get("PATH")
							 | 
						|
								    found_d = None
							 | 
						|
								    if PATH is not None:
							 | 
						|
								        more_paths = PATH.split(os.pathsep)
							 | 
						|
								        # ^ pathsep such as ':' (NOT dirsep such as '/'!)
							 | 
						|
								        more_paths
							 | 
						|
								        for this_d in more_paths:
							 | 
						|
								            tryF = os.path.join(this_d, "enissue.py")
							 | 
						|
								            if os.path.isfile(tryF):
							 | 
						|
								                found_d = this_d
							 | 
						|
								                break
							 | 
						|
								    if found_d is not None:
							 | 
						|
								        sys.path.append(found_d)
							 | 
						|
								        print("* detected enissue.py in {}".format(found_d))
							 | 
						|
								        # print("path: {}".format(sys.path))
							 | 
						|
								
							 | 
						|
								        try:
							 | 
						|
								            import eni
							 | 
						|
								        except ModuleNotFoundError as ex2:
							 | 
						|
								            error(ex2)
							 | 
						|
								            print("{} must be in the same directory as enissue.py or in"
							 | 
						|
								                  " PATH".format(me))
							 | 
						|
								            sys.exit(1)
							 | 
						|
								    else:
							 | 
						|
								        print("{} must be in the same directory as enissue.py or in"
							 | 
						|
								              " PATH".format(me))
							 | 
						|
								        raise ex
							 | 
						|
								
							 | 
						|
								from enissue import Repo
							 | 
						|
								
							 | 
						|
								data_directory = os.path.join(Repo.profile, ".cache", "pyissuesyncd")
							 | 
						|
								
							 | 
						|
								def get_issue(repo, options, issue_no):
							 | 
						|
								    results, err = repo.load_issues(
							 | 
						|
								        options,
							 | 
						|
								        issue_no=issue_no,
							 | 
						|
								    )
							 | 
						|
								    if results is None:
							 | 
						|
								        if err is not None:
							 | 
						|
								            if err.get('code') == 410:
							 | 
						|
								                # error("The issue was deleted")
							 | 
						|
								                pass
							 | 
						|
								            elif err.get('code') == 404:
							 | 
						|
								                # error("The issue doesn't exist")
							 | 
						|
								                pass
							 | 
						|
								            return None, err
							 | 
						|
								        else:
							 | 
						|
								            msg = ("Unknown error: Results should not be None unless"
							 | 
						|
								                   " there is an error (issue_no={})."
							 | 
						|
								                   "".format(issue_no))
							 | 
						|
								            return (
							 | 
						|
								                None,
							 | 
						|
								                {
							 | 
						|
								                    'reason': msg,
							 | 
						|
								                }
							 | 
						|
								            )
							 | 
						|
								    elif not isinstance(results, list):
							 | 
						|
								        raise RuntimeError("Results must be a list even if there is"
							 | 
						|
								                           " only one result.")
							 | 
						|
								    elif len(results) > 1:
							 | 
						|
								        raise RuntimeError("Results should have"
							 | 
						|
								                           " only one result.")
							 | 
						|
								    issue = results[0]
							 | 
						|
								    '''
							 | 
						|
								    match = repo.get_match(
							 | 
						|
								        mode,
							 | 
						|
								        issue_no=issue_no,
							 | 
						|
								        match_all_labels_lower=match_all_labels_lower,
							 | 
						|
								    )
							 | 
						|
								    matching_issue = match['issue']
							 | 
						|
								    if matching_issue is not None:
							 | 
						|
								        repo.show_issue(
							 | 
						|
								            matching_issue,
							 | 
						|
								            refresh=False,
							 | 
						|
								            never_expire=options.get('never_expire') is True,
							 | 
						|
								        )
							 | 
						|
								    '''
							 | 
						|
								    return issue, None
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								def start_issuesyncd(src_options, dst_options):
							 | 
						|
								    # src_never_expire = src_options.get('never_expire') is True
							 | 
						|
								    non_issue = 1
							 | 
						|
								    issue_no = non_issue - 1
							 | 
						|
								    # while True:
							 | 
						|
								    while issue_no < non_issue: # for debug only
							 | 
						|
								        issue_no += 1
							 | 
						|
								        src_repo = Repo(src_options)
							 | 
						|
								        src_issue, err = get_issue(src_repo, src_options, issue_no)
							 | 
						|
								        if err is not None:
							 | 
						|
								            error("Error accessing source issue {}: {}: {}"
							 | 
						|
								                  "".format(issue_no, err.get('code'),
							 | 
						|
								                            err.get('reason')))
							 | 
						|
								            continue
							 | 
						|
								
							 | 
						|
								        print("[pyissuesyncd] src_issue:")
							 | 
						|
								        print(json.dumps(src_issue, indent=2))
							 | 
						|
								
							 | 
						|
								        enissue.set_verbose(True)
							 | 
						|
								        dst_repo = Repo(dst_options)
							 | 
						|
								        dst_issue, err = get_issue(dst_repo, dst_options, issue_no)
							 | 
						|
								        print("[pyissuesyncd] dst_issue:")
							 | 
						|
								        print(json.dumps(dst_issue, indent=2))
							 | 
						|
								        if err is not None:
							 | 
						|
								            if err.get('code') == 404:
							 | 
						|
								                dst_repo.create_issue(src_issue, src_repo)
							 | 
						|
								                continue
							 | 
						|
								            error("Error accessing destination issue {}: {}: {}"
							 | 
						|
								                  "".format(issue_no, err.get('code'),
							 | 
						|
								                            err.get('reason')))
							 | 
						|
								            continue
							 | 
						|
								        dst_repo.update_issue(src_issue, src_repo)
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								def usage():
							 | 
						|
								    print(__doc__)
							 | 
						|
								
							 | 
						|
								if __name__ == "__main__":
							 | 
						|
								    src_options = {
							 | 
						|
								        'repo_url': "https://github.com/poikilos/EnlivenMinetest",
							 | 
						|
								        'never_expire': True,
							 | 
						|
								        'quiet': True,
							 | 
						|
								    }
							 | 
						|
								    dst_options = {
							 | 
						|
								        'never_expire': True,
							 | 
						|
								        'quiet': True,
							 | 
						|
								    }
							 | 
						|
								    DST_REPO = os.environ.get("DST_REPO")
							 | 
						|
								    if DST_REPO is not None:
							 | 
						|
								        dst_options['repo_url'] = DST_REPO
							 | 
						|
								        del DST_REPO
							 | 
						|
								    SRC_REPO = os.environ.get("SRC_REPO")
							 | 
						|
								    if DST_REPO is not None:
							 | 
						|
								        src_options['repo_url'] = SRC_REPO
							 | 
						|
								        del SRC_REPO
							 | 
						|
								
							 | 
						|
								    SRC_CACHE = os.environ.get("SRC_CACHE")
							 | 
						|
								    if SRC_CACHE is None:
							 | 
						|
								        SRC_CACHE = os.path.join(data_directory, "source")
							 | 
						|
								
							 | 
						|
								    DST_CACHE = os.environ.get("DST_CACHE")
							 | 
						|
								    if DST_CACHE is None:
							 | 
						|
								        DST_CACHE = os.path.join(data_directory, "destination")
							 | 
						|
								
							 | 
						|
								    prev_arg = None
							 | 
						|
								
							 | 
						|
								    manual_args = ['--dst-repo', '--src-repo', '--src-cache', '--dst-cache']
							 | 
						|
								
							 | 
						|
								    for arg in sys.argv[1:]:
							 | 
						|
								        if prev_arg == "--dst-repo":
							 | 
						|
								            dst_options['repo_url'] = arg
							 | 
						|
								        elif prev_arg == "--src-repo":
							 | 
						|
								            src_options['repo_url'] = arg
							 | 
						|
								        elif prev_arg == "--src_cache":
							 | 
						|
								            SRC_CACHE = arg
							 | 
						|
								        elif prev_arg == "--dst_cache":
							 | 
						|
								            DST_CACHE = arg
							 | 
						|
								        elif arg in manual_args:
							 | 
						|
								            pass
							 | 
						|
								        else:
							 | 
						|
								            usage()
							 | 
						|
								            error("Error: The argument is not valid: {}".format(arg))
							 | 
						|
								            sys.exit(1)
							 | 
						|
								        prev_arg = arg
							 | 
						|
								
							 | 
						|
								    src_options['single_cache'] = SRC_CACHE
							 | 
						|
								    dst_options['single_cache'] = DST_CACHE
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								    error("SRC_REPO (--src-repo) is {}"
							 | 
						|
								          "".format(src_options.get('repo_url')))
							 | 
						|
								    error("DST_REPO (--dst-repo) is {}"
							 | 
						|
								          "".format(dst_options.get('repo_url')))
							 | 
						|
								    if dst_options.get('repo_url') is None:
							 | 
						|
								        error("Error: You must set DST_REPO in the environment or specify a url after --dst-repo")
							 | 
						|
								        sys.exit(1)
							 | 
						|
								
							 | 
						|
								    start_issuesyncd(src_options, dst_options)
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								
							 |