@ -14,34 +14,45 @@ See the license file in the included EnlivenMinetest directory or at
[EnlivenMinetest](https://github.com/poikilos/EnlivenMinetest)
[EnlivenMinetest](https://github.com/poikilos/EnlivenMinetest)
Outputs:
Definitions:
data_directory: The data directory for this service daemon is
- data_directory: The data directory for this service daemon is
os.path.join(profile, ".cache", "pyissuesyncd").
os.path.join(profile, ".cache", "pyissuesyncd").
- repo: In this context, repo refers to a project management system with
a web API.
Examples:
DST_REPO=https://example.com/git/repo pyissuesyncd
pyissuesyncd --dst-repo https://example.com/git/repo
The two _CACHE directories below are used as the single_cache option
for the Repo (see enissue.py's Repo class for documentation).
'''
__old_doc__ = '''
required arguments:
required arguments:
--dst-repo (or set the DST_REPO environment variable)
--dst-repo (or set the DST_REPO environment variable)
Issues and dependent data will be overwritten at this API URL.
Issues and dependent data will be overwritten at this API URL.
optional arguments:
optional arguments:
Environment variables get be set, but a CLI argument will override the
Environment variables get be set, but a CLI argument will override the
corresponding variable noted below in all caps.
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.
--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")
* 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.
--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")
* defaults to DST_CACHE or os.path.join(data_directory, "destination")
--src-repo-token: Set the token you generated using the repo's web interface.
* defaults to SRC_REPO_TOKEN or None
Examples:
--src-min-issue: Set what issue number to check first.
DST_REPO=https://example.com/git/repo pyissuesyncd
* defaults to SRC_MIN_ISSUE or None
pyissuesyncd --dst-repo https://example.com/git/repo
--src-max-issue: Set what issue number to check last.
* defaults to SRC_MAX_ISSUE or None
'''
'''
import os
import os
import sys
import sys
@ -140,6 +151,23 @@ def get_issue(repo, options, issue_no):
def start_issuesyncd(src_options, dst_options):
def start_issuesyncd(src_options, dst_options):
src_s_c = src_options.get("single_cache")
dst_s_c = dst_options.get("single_cache")
if src_s_c is None:
usage()
raise ValueError("single_cache is None in src_options but"
" it should be set to a directory that will"
" contain the issues directory.")
if dst_s_c is None:
usage()
raise ValueError("single_cache is None in dst_options but"
" it should be set to a directory that will"
" contain the issues directory.")
if src_s_c == dst_s_c:
usage()
raise ValueError("single_cache for dst and src are both"
" {} but must be different."
"".format(src_s_c))
# src_never_expire = src_options.get('never_expire') is True
# src_never_expire = src_options.get('never_expire') is True
max_issue = src_options.get('max_issue')
max_issue = src_options.get('max_issue')
if max_issue is None:
if max_issue is None:
@ -149,7 +177,11 @@ def start_issuesyncd(src_options, dst_options):
else:
else:
max_issue = int(max_issue)
max_issue = int(max_issue)
issue_no = 0 # This is incremented to 1 before use.
min_issue = src_options.get("min_issue")
if min_issue is None:
min_issue = 1
issue_no = min_issue - 1 # This is incremented to 1 before use.
# issue_no = max_issue - 1 # debug only
# issue_no = max_issue - 1 # debug only
src_res_code = 0
src_res_code = 0
end_codes = [404, 403]
end_codes = [404, 403]
@ -216,12 +248,25 @@ def start_issuesyncd(src_options, dst_options):
continue
continue
else:
else:
error("However, an issue was returned.")
error("However, an issue was returned.")
error("Got issue {}".format(issue_no))
got_fmt = "#{}: got (source not recorded)"
if src_repo.last_src is not None:
if src_repo.last_src.startswith("http:"):
got_fmt = "#{} downloaded"
elif os.path.isfile(src_repo.last_src):
got_fmt = "#{} loaded from cache"
error(got_fmt.format(issue_no))
# Example: ~/.cache/pyissuesyncd/source/issues/1.json
# Example: ~/.cache/pyissuesyncd/source/issues/1.json
src_dt_parser = src_repo.options['default_dt_parser']
src_dt_parser = src_repo.options['default_dt_parser']
src_created_dt_s = src_repo.getKnown(src_issue, 'created_at')
src_created_dt_s = src_repo.getKnown(src_issue, 'created_at')
src_updated_dt_s = src_repo.getKnown(src_issue, 'updated_at')
src_updated_dt_s = src_repo.getKnown(src_issue, 'updated_at')
try:
src_updated_dt = src_dt_parser(src_updated_dt_s)
src_updated_dt = src_dt_parser(src_updated_dt_s)
except ValueError as ex:
error("Error in {}".format(src_repo.last_src))
error(ex)
error("If you changed repos and used the same cache dir,"
" manually delete the cache file or directory above.")
sys.exit(1)
src_updated_ts = int(src_updated_dt.strftime("%s"))
src_updated_ts = int(src_updated_dt.strftime("%s"))
# ^ See <https://stackoverflow.com/questions/19801727/convert-
# ^ See <https://stackoverflow.com/questions/19801727/convert-
# datetime-to-unix-timestamp-and-convert-it-back-in-python>
# datetime-to-unix-timestamp-and-convert-it-back-in-python>
@ -294,70 +339,179 @@ def start_issuesyncd(src_options, dst_options):
pass
pass
# dst_repo.update_issue(src_issue, src_repo)
# dst_repo.update_issue(src_issue, src_repo)
'''
manual_args = ['--dst-repo', '--src-repo', '--src-cache',
'--dst-cache', '--src-max-issue', '--src-repo-token']
'''
prev_arg = None
collect_src_keys = {
'SRC_REPO': "repo_url",
'SRC_CACHE': "single_cache",
'SRC_MAX_ISSUE': "max_issue",
'SRC_REPO_TOKEN': "token",
'SRC_MIN_ISSUE': "min_issue",
}
collect_dst_keys = {
'DST_REPO': "repo_url",
'DST_CACHE': "token",
}
src_args = {}
for envVarName, option in collect_src_keys.items():
_arg = "--{}".format(envVarName).replace("_", "-").lower()
src_args[_arg] = option
dst_args = {}
for envVarName, option in collect_dst_keys.items():
_arg = "--{}".format(envVarName).replace("_", "-").lower()
dst_args[_arg] = option
env_help = {
'DST_REPO': "Issues and dependent data will be overwritten at this API URL.",
'SRC_CACHE': "Set the directory to store a cached version of the source repo's data.",
'DST_CACHE': "Set the directory to store a cached version of the destination repo's data.",
'SRC_REPO_TOKEN': "Set the token you generated using the repo's web interface.",
'SRC_MIN_ISSUE': "Set what issue number to check first.",
'SRC_MAX_ISSUE': "Set what issue number to check last.",
}
src_option_defaults = {
'repo_url': 'https://github.com/poikilos/EnlivenMinetest',
'api_id': 'GitHub',
}
env_default_help = {
'SRC_CACHE': '(default = os.path.join(data_directory, "source"))',
'DST_CACHE': 'os.path.join(data_directory, "destination")',
'SRC_REPO_TOKEN': 'None',
'SRC_MIN_ISSUE': 'None',
'SRC_MAX_ISSUE': 'None',
'SRC_REPO': src_option_defaults['repo_url'],
}
required_env_names = ['DST_REPO']
def usage():
def usage():
print(__doc__)
print(__doc__)
print("All options:")
arg_w = 17
env_w = 27
line_fmt = "{:"+str(arg_w)+"} {:"+str(env_w)+"} {}"
p = " " # option help prefix
for _arg, option in src_args.items():
help_msg = "Set src_options['{}']".format(option)
envVarName = None
envVarMsg = None
for k, v in collect_src_keys.items():
if v == option:
envVarName = k
envVarMsg = "(or env var {})".format(envVarName)
break
# arg_msg = _arg
# if envVarName in required_env_names:
# arg_msg += " (required)"
print(line_fmt.format(_arg, envVarMsg, help_msg))
if envVarName is not None:
env_help_msg = env_help.get(envVarName)
if env_help_msg is not None:
print(p+env_help_msg)
env_default_help_msg = env_default_help.get(envVarName)
if env_default_help_msg is not None:
print(p+"(default: {})".format(env_default_help_msg))
if envVarName in required_env_names:
print(p+"(required)")
for _arg, option in dst_args.items():
help_msg = "Set dst_options['{}']".format(option)
envVarName = None
envVarMsg = None
for k, v in collect_dst_keys.items():
if v == option:
envVarName = k
envVarMsg = "(or env var {})".format(envVarName)
break
print(line_fmt.format(_arg, envVarMsg, help_msg))
if envVarName is not None:
env_help_msg = env_help.get(envVarName)
if env_help_msg is not None:
print(p+env_help_msg)
env_default_help_msg = env_default_help.get(envVarName)
if env_default_help_msg is not None:
print(p+"(default: {})".format(env_default_help_msg))
if envVarName in required_env_names:
print(p+"(required)")
print("")
if __name__ == "__main__":
if __name__ == "__main__":
src_options = {
src_options = {
'repo_url': "https://github.com/poikilos/EnlivenMinetest",
'repo_url': src_option_defaults['repo_url'] ,
'never_expire': True,
'never_expire': True,
'quiet': True,
'quiet': True,
'api_id': "GitHub",
'api_id': src_option_defaults['api_id'] ,
}
}
dst_options = {
dst_options = {
'never_expire': True,
'never_expire': True,
'quiet': True,
'quiet': True,
'api_id': "Gitea",
'api_id': "Gitea",
}
}
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")
SRC_MAX_ISSUE = os.environ.get('SRC_MAX_ISSUE')
prev_arg = None
manual_args = ['--dst-repo', '--src-repo', '--src-cache',
'--dst-cache', '--src-max-issue']
for envVarName, option in collect_src_keys.items():
_VAL = os.environ.get(envVarName)
if _VAL is not None:
src_args[option] = _VAL
error("* environment set src_options['{}'] to {} via {}"
"".format(option, _VAL, envVarName))
for envVarName, option in collect_dst_keys.items():
_VAL = os.environ.get(envVarName)
if _VAL is not None:
dst_args[option] = _VAL
error("* environment set dst_options['{}'] to {} via {}"
"".format(option, _VAL, envVarName))
set_obj_name = None
set_key = None
for arg in sys.argv[1:]:
for arg in sys.argv[1:]:
if prev_arg == "--dst-repo":
if set_key is not None:
dst_options['repo_url'] = arg
if set_obj_name == "dst":
elif prev_arg == "--src-repo":
dst_options[set_key] = arg
src_options['repo_url'] = arg
error("* set dst_options['{}'] to {}"
elif prev_arg == "--src_cache":
"".format(set_key, arg))
SRC_CACHE = arg
elif set_obj_name == "src":
elif prev_arg == "--dst_cache":
src_options[set_key] = arg
DST_CACHE = arg
error("* set src_options['{}'] to {}"
elif prev_arg == "--src-max-issue":
"".format(set_key, arg))
SRC_MAX_ISSUE = int(arg)
else:
elif arg in manual_args:
raise RuntimeError("set_obj_name must be dst or src.")
set_key = None
set_obj_name = None
elif arg in ['-h', '--help']:
usage()
sys.exit(0)
elif arg in src_args.keys():
set_key = src_args[arg]
set_obj_name = "src"
pass
elif arg in dst_args.keys():
set_key = dst_args[arg]
set_obj_name = "dst"
pass
pass
else:
else:
usage()
usage()
error("Error: The argument is not valid: {}".format(arg))
error("Error: The argument is not valid: {}".format(arg))
sys.exit(1)
sys.exit(1)
prev_arg = arg
prev_arg = arg
if set_key is not None:
usage()
error("Error: You must provide a value after {}"
"".format(set_key))
sys.exit(1)
src_options['single_cache'] = SRC_CACHE
# INFO: start_issuesyncd warns if SRC_MAX_ISSUE is None.
src_options['max_issue'] = SRC_MAX_ISSUE
if src_options.get('single_cache') is None:
# ^ INFO: start_issuesyncd warns if SRC_MAX_ISSUE is None.
src_options['single_cache'] = os.path.join(data_directory, "source")
dst_options['single_cache'] = DST_CACHE
if dst_options.get('single_cache') is None:
dst_options['single_cache'] = os.path.join(data_directory, "destination")
error("SRC_REPO (--src-repo) is {}"
error("SRC_REPO (--src-repo) is {}"
"".format(src_options.get('repo_url')))
"".format(src_options.get('repo_url')))