From 80190b203cb0d63ae8f39b97634369fccbc48147 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 09:51:12 -0400 Subject: [PATCH 01/24] Rename migration script and add more migrators --- .travis.yml | 5 + appveyor.yml | 2 + cli-migrate-slice-types | 75 ------------- cli-v1-to-v2 | 235 ++++++++++++++++++++++++++++++++++++++++ runtests | 27 +++-- 5 files changed, 261 insertions(+), 83 deletions(-) delete mode 100755 cli-migrate-slice-types create mode 100755 cli-v1-to-v2 diff --git a/.travis.yml b/.travis.yml index 657e96a..66643e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,8 @@ language: go sudo: false +cache: pip + go: - 1.2.2 - 1.3.3 @@ -25,8 +27,11 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... +- pip install flake8 script: +- flake8 runtests cli-v1-to-v2 - ./runtests vet - ./runtests test - ./runtests gfmxr +- ./cli-v1-to-v2 --selftest diff --git a/appveyor.yml b/appveyor.yml index 173086e..5714f8b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,8 +18,10 @@ install: - go env - go get github.com/urfave/gfmxr/... - go get -v -t ./... +- pip install flake8 build_script: +- flake8 runtests cli-v1-to-v2 - python runtests vet - python runtests test - python runtests gfmxr diff --git a/cli-migrate-slice-types b/cli-migrate-slice-types deleted file mode 100755 index 5c6cb1f..0000000 --- a/cli-migrate-slice-types +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function, unicode_literals - -import argparse -import os -import re -import sys - - -DESCRIPTION = """\ -Migrate arbitrary `.go` sources from the pre-v2.0.0 API for StringSlice and -IntSlice types, optionally writing the changes back to file. -""" -SLICE_TYPE_RE = re.compile( - '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', - flags=re.DOTALL -) - - -def main(sysargs=sys.argv[:]): - parser = argparse.ArgumentParser( - description=DESCRIPTION, - formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('basedir', nargs='?', metavar='BASEDIR', - type=os.path.abspath, default=os.getcwd()) - parser.add_argument('-w', '--write', help='write changes back to file', - action='store_true', default=False) - - args = parser.parse_args(sysargs[1:]) - - for filepath in _find_candidate_files(args.basedir): - updated_source = _update_source(filepath) - if args.write: - print('Updating {!r}'.format(filepath)) - - with open(filepath, 'w') as outfile: - outfile.write(updated_source) - else: - print('// -> Updated {!r}'.format(filepath)) - print(updated_source) - - return 0 - - -def _update_source(filepath): - with open(filepath) as infile: - source = infile.read() - return SLICE_TYPE_RE.sub(_slice_type_repl, source) - - -def _slice_type_repl(match): - return 'cli.New{}({})'.format( - match.groupdict()['type'], match.groupdict()['args'] - ) - - -def _find_candidate_files(basedir): - for curdir, dirs, files in os.walk(basedir): - for i, dirname in enumerate(dirs[:]): - if dirname.startswith('.'): - dirs.pop(i) - - for filename in files: - if not filename.endswith('.go'): - continue - - filepath = os.path.join(curdir, filename) - if not os.access(filepath, os.R_OK | os.W_OK): - continue - - yield filepath - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 new file mode 100755 index 0000000..8743245 --- /dev/null +++ b/cli-v1-to-v2 @@ -0,0 +1,235 @@ +#!/usr/bin/env python +from __future__ import print_function, unicode_literals + +import argparse +import logging +import os +import re +import sys + + +_DESCRIPTION = """\ +Migrate arbitrary `.go` sources (mostly) from the v1 to v2 API. +""" +_V2_IMPORT = '"gopkg.in/urfave/cli.v2"' +_V1_IMPORT_RE = re.compile( + '"(?:github\\.com|gopkg\\.in)/(?:codegangsta|urfave)/cli(?:\\.v1|)"', +) +_SLICE_TYPE_RE = re.compile( + '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', + flags=re.DOTALL +) +_FLAG_LITERAL_RE = re.compile( + '(?P[^&]?)cli\\.(?P\\w+)Flag{', +) +_COMMAND_SLICE_RE = re.compile( + '(?P\\[\\])cli\\.Command{', +) +_COMMAND_LITERAL_RE = re.compile( + '(?P\\s+)cli\\.Command{', +) + +_MIGRATORS = {} + + +def main(sysargs=sys.argv[:]): + parser = argparse.ArgumentParser( + description=_DESCRIPTION, + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('basedir', nargs='?', metavar='BASEDIR', + type=os.path.abspath, default=os.getcwd()) + parser.add_argument('-w', '--write', help='write changes back to file', + action='store_true', default=False) + parser.add_argument('-q', '--quiet', help='quiet down the logging', + action='store_true', default=False) + parser.add_argument('--selftest', help='run internal tests', + action='store_true', default=False) + + args = parser.parse_args(sysargs[1:]) + + if args.selftest: + logging.basicConfig( + level=logging.WARN, + format='selftest: %(message)s' + ) + test_migrators() + return 0 + + logging.basicConfig( + level=(logging.FATAL if args.quiet else logging.INFO), + format='%(message)s' + ) + + for filepath in _find_candidate_files(args.basedir): + updated_source = _update_filepath(filepath) + if args.write: + logging.info('Updating %s', filepath) + + with open(filepath, 'w') as outfile: + outfile.write(updated_source) + else: + logging.info('// Updated %s:', filepath) + print(updated_source) + + return 0 + + +def _find_candidate_files(basedir): + for curdir, dirs, files in os.walk(basedir): + for i, dirname in enumerate(dirs[:]): + if dirname.startswith('.'): + dirs.pop(i) + + for filename in files: + if not filename.endswith('.go'): + continue + + filepath = os.path.join(curdir, filename) + if not os.access(filepath, os.R_OK | os.W_OK): + continue + + yield filepath + + +def _update_filepath(filepath): + with open(filepath) as infile: + return _update_source(infile.read()) + + +def _update_source(source): + for migrator, func in _MIGRATORS.items(): + logging.info('Running %s migrator', migrator) + source = func(source) + return source + + +def _migrator(func): + _MIGRATORS[func.__name__.replace('_migrate_', '')] = func + return func + + +@_migrator +def _migrate_command_slice(source): + return _COMMAND_SLICE_RE.sub(_command_slice_repl, source) + + +def _command_slice_repl(match): + return '{}*cli.Command{{'.format(match.groupdict()['prefix']) + + +@_migrator +def _migrate_command_literal(source): + return _COMMAND_LITERAL_RE.sub(_command_literal_repl, source) + + +def _command_literal_repl(match): + return '{}&cli.Command{{'.format(match.groupdict()['prefix']) + + +@_migrator +def _migrate_slice_types(source): + return _SLICE_TYPE_RE.sub(_slice_type_repl, source) + + +def _slice_type_repl(match): + return 'cli.New{}({})'.format( + match.groupdict()['type'], match.groupdict()['args'] + ) + + +@_migrator +def _migrate_flag_literals(source): + return _FLAG_LITERAL_RE.sub(_flag_literal_repl, source) + + +def _flag_literal_repl(match): + return '{}&cli.{}Flag{{'.format( + match.groupdict()['prefix'], match.groupdict()['type'] + ) + + +@_migrator +def _migrate_v1_imports(source): + return _V1_IMPORT_RE.sub(_V2_IMPORT, source) + + +def test_migrators(): + import difflib + + for i, (source, expected) in enumerate(_MIGRATOR_TESTS): + actual = _update_source(source) + if expected != actual: + udiff = difflib.unified_diff( + expected.splitlines(), actual.splitlines(), + fromfile='a/source.go', tofile='b/source.go', lineterm='' + ) + for line in udiff: + print(line) + raise AssertionError('migrated source does not match expected') + logging.warn('Test case %d/%d OK', i+1, len(_MIGRATOR_TESTS)) + + +_MIGRATOR_TESTS = ( + (""" +\t\t\t&cli.StringSlice{"a", "b", "c"}, +""", """ +\t\t\tcli.NewStringSlice("a", "b", "c"), +"""), + (""" +\t\tcli.IntFlag{ +\t\t\tName: "yep", +\t\t\tValue: 3, +\t\t} +""", """ +\t\t&cli.IntFlag{ +\t\t\tName: "yep", +\t\t\tValue: 3, +\t\t} +"""), + (""" +\t\tapp.Commands = []cli.Command{ +\t\t\t{ +\t\t\t\tName: "whatebbs", +\t\t\t}, +\t\t} +""", """ +\t\tapp.Commands = []*cli.Command{ +\t\t\t{ +\t\t\t\tName: "whatebbs", +\t\t\t}, +\t\t} +"""), + (""" +\t\tapp.Commands = []cli.Command{ +\t\t\tcli.Command{ +\t\t\t\tName: "whatebbs", +\t\t\t}, +\t\t} +""", """ +\t\tapp.Commands = []*cli.Command{ +\t\t\t&cli.Command{ +\t\t\t\tName: "whatebbs", +\t\t\t}, +\t\t} +"""), + (""" +\t"github.com/codegangsta/cli" +\t"github.com/urfave/cli" +\t"gopkg.in/codegangsta/cli" +\t"gopkg.in/codegangsta/cli.v1" +\t"gopkg.in/urfave/cli" +\t"gopkg.in/urfave/cli.v1" +""", """ +\t"gopkg.in/urfave/cli.v2" +\t"gopkg.in/urfave/cli.v2" +\t"gopkg.in/urfave/cli.v2" +\t"gopkg.in/urfave/cli.v2" +\t"gopkg.in/urfave/cli.v2" +\t"gopkg.in/urfave/cli.v2" +""" + ) +) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/runtests b/runtests index 72c1f0d..d077055 100755 --- a/runtests +++ b/runtests @@ -9,18 +9,13 @@ import tempfile from subprocess import check_call, check_output -PACKAGE_NAME = os.environ.get( +_PACKAGE_NAME = os.environ.get( 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' ) +_TARGETS = {} def main(sysargs=sys.argv[:]): - targets = { - 'vet': _vet, - 'test': _test, - 'gfmxr': _gfmxr - } - parser = argparse.ArgumentParser() parser.add_argument( 'target', nargs='?', choices=tuple(targets.keys()), default='test' @@ -31,6 +26,12 @@ def main(sysargs=sys.argv[:]): return 0 +def _target(func): + _TARGETS[func.__name__.strip('_')] = func + return func + + +@_target def _test(): if check_output('go version'.split()).split()[2] < 'go1.2': _run('go test -v .'.split()) @@ -46,7 +47,7 @@ def _test(): _run('go test -v'.split() + [ '-coverprofile={}'.format(coverprofile), - ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') + ('{}/{}'.format(_PACKAGE_NAME, subpackage)).rstrip('/') ]) combined_name = _combine_coverprofiles(coverprofiles) @@ -54,14 +55,24 @@ def _test(): os.remove(combined_name) +@_target def _gfmxr(): _run(['gfmxr', '-c', str(_gfmxr_count()), '-s', 'README.md']) +@_target def _vet(): _run('go vet ./...'.split()) +@_target +def _migrator(): + exe = os.path.join( + os.path.abspath(os.path.dirname(__file__)), 'cli-v1-to-v2' + ) + run(['py.test', exe]) + + def _run(command): print('runtests: {}'.format(' '.join(command)), file=sys.stderr) check_call(command) From eadd3918f10b686858154a3e3a659b412a9cb45a Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 09:53:47 -0400 Subject: [PATCH 02/24] Correct some flake8 bits --- cli-v1-to-v2 | 3 +-- runtests | 12 ++---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 8743245..74eac50 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -226,8 +226,7 @@ _MIGRATOR_TESTS = ( \t"gopkg.in/urfave/cli.v2" \t"gopkg.in/urfave/cli.v2" \t"gopkg.in/urfave/cli.v2" -""" - ) +""") ) diff --git a/runtests b/runtests index d077055..c9cbf5e 100755 --- a/runtests +++ b/runtests @@ -18,11 +18,11 @@ _TARGETS = {} def main(sysargs=sys.argv[:]): parser = argparse.ArgumentParser() parser.add_argument( - 'target', nargs='?', choices=tuple(targets.keys()), default='test' + 'target', nargs='?', choices=tuple(_TARGETS.keys()), default='test' ) args = parser.parse_args(sysargs[1:]) - targets[args.target]() + _TARGETS[args.target]() return 0 @@ -65,14 +65,6 @@ def _vet(): _run('go vet ./...'.split()) -@_target -def _migrator(): - exe = os.path.join( - os.path.abspath(os.path.dirname(__file__)), 'cli-v1-to-v2' - ) - run(['py.test', exe]) - - def _run(command): print('runtests: {}'.format(' '.join(command)), file=sys.stderr) check_call(command) From b47e1523955b3ddf5949e97b784ac72e3943d527 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 10:21:13 -0400 Subject: [PATCH 03/24] Handle migration of both files and dirs --- cli-v1-to-v2 | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 74eac50..1ff4892 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -36,7 +36,7 @@ def main(sysargs=sys.argv[:]): parser = argparse.ArgumentParser( description=_DESCRIPTION, formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('basedir', nargs='?', metavar='BASEDIR', + parser.add_argument('path', nargs='+', type=os.path.abspath, default=os.getcwd()) parser.add_argument('-w', '--write', help='write changes back to file', action='store_true', default=False) @@ -60,7 +60,7 @@ def main(sysargs=sys.argv[:]): format='%(message)s' ) - for filepath in _find_candidate_files(args.basedir): + for filepath in _find_candidate_files(args.path): updated_source = _update_filepath(filepath) if args.write: logging.info('Updating %s', filepath) @@ -74,21 +74,26 @@ def main(sysargs=sys.argv[:]): return 0 -def _find_candidate_files(basedir): - for curdir, dirs, files in os.walk(basedir): - for i, dirname in enumerate(dirs[:]): - if dirname.startswith('.'): - dirs.pop(i) +def _find_candidate_files(paths): + for path in paths: + if not os.path.isdir(path): + yield path + continue - for filename in files: - if not filename.endswith('.go'): - continue + for curdir, dirs, files in os.walk(path): + for i, dirname in enumerate(dirs[:]): + if dirname.startswith('.'): + dirs.pop(i) - filepath = os.path.join(curdir, filename) - if not os.access(filepath, os.R_OK | os.W_OK): - continue + for filename in files: + if not filename.endswith('.go'): + continue - yield filepath + filepath = os.path.join(curdir, filename) + if not os.access(filepath, os.R_OK | os.W_OK): + continue + + yield filepath def _update_filepath(filepath): From e9027a59efbacbba3ad6eab638aa2995478f6b10 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 10:23:21 -0400 Subject: [PATCH 04/24] Do a user-level install with pip --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 66643e7..744ef8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... -- pip install flake8 +- pip install --user flake8 script: - flake8 runtests cli-v1-to-v2 diff --git a/appveyor.yml b/appveyor.yml index 5714f8b..7ed2b65 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ install: - go env - go get github.com/urfave/gfmxr/... - go get -v -t ./... -- pip install flake8 +- pip install --user flake8 build_script: - flake8 runtests cli-v1-to-v2 From 6d59b40b685583b878111df4254aac3eb45c896f Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 10:27:44 -0400 Subject: [PATCH 05/24] Skip flake8 bits on Windows since pip isn't already there, installing it is a bit of a pain, and the results *should* be the same on all platforms --- appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7ed2b65..173086e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,10 +18,8 @@ install: - go env - go get github.com/urfave/gfmxr/... - go get -v -t ./... -- pip install --user flake8 build_script: -- flake8 runtests cli-v1-to-v2 - python runtests vet - python runtests test - python runtests gfmxr From 462217f9fca36771bd1da9c65db8b2888ba71a5e Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 10:46:00 -0400 Subject: [PATCH 06/24] Allow zero+ paths in migration script and run the self-tests on Windows, too. --- appveyor.yml | 1 + cli-v1-to-v2 | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 173086e..e93734d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,3 +23,4 @@ build_script: - python runtests vet - python runtests test - python runtests gfmxr +- python cli-v1-to-v2 --selftest diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 1ff4892..175afa7 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -36,7 +36,7 @@ def main(sysargs=sys.argv[:]): parser = argparse.ArgumentParser( description=_DESCRIPTION, formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('path', nargs='+', + parser.add_argument('path', nargs='*', type=os.path.abspath, default=os.getcwd()) parser.add_argument('-w', '--write', help='write changes back to file', action='store_true', default=False) @@ -60,7 +60,11 @@ def main(sysargs=sys.argv[:]): format='%(message)s' ) - for filepath in _find_candidate_files(args.path): + paths = args.path + if len(paths) == 0: + paths = ['.'] + + for filepath in _find_candidate_files(paths): updated_source = _update_filepath(filepath) if args.write: logging.info('Updating %s', filepath) @@ -86,7 +90,7 @@ def _find_candidate_files(paths): dirs.pop(i) for filename in files: - if not filename.endswith('.go'): + if not filename.decode('utf-8').endswith('.go'): continue filepath = os.path.join(curdir, filename) From 3914ca9d5f43b15dfa8f9f644bcde8cd803ae5d1 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 10:54:06 -0400 Subject: [PATCH 07/24] Update docs around migration script and ensure ~/.local/bin is in PATH on OS X --- .gitignore | 1 + .travis.yml | 1 + CHANGELOG.md | 4 ++-- README.md | 3 +++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7823778..2a7262d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.coverprofile +.extracted-examples diff --git a/.travis.yml b/.travis.yml index 744ef8d..2667e57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ matrix: include: - go: 1.6.2 os: osx + env: PATH=$HOME/.local/bin:$PATH - go: 1.1.2 install: go get -v . before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION diff --git a/CHANGELOG.md b/CHANGELOG.md index 248181e..c9e2c07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ ### Removed - the ability to specify `&StringSlice{...string}` or `&IntSlice{...int}`. - To migrate to the new API, you may choose to run [this helper - (python) script](./cli-migrate-slice-types). + To migrate to the new API, you may choose to run [the migrator + (python) script](./cli-v1-to-v2). - The optimistic reordering of arguments and flags introduced by https://github.com/codegangsta/cli/pull/36. This behavior only worked when all arguments appeared before all flags, but caused [weird issues with boolean diff --git a/README.md b/README.md index 11a3ea4..4cfb562 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,9 @@ import ( ... ``` +**NOTE**: There is a [migrator (python) script](./cli-v1-to-v2) available to aid +with the transition from the v1 to v2 API. + ### Pinning to the `v1` branch Similarly to the section above describing use of the `v2` branch, if one wants From 5565a037054f9030be63bfa2b47f6adb63facaa4 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 11:02:49 -0400 Subject: [PATCH 08/24] Migrate and build extracted v1 examples --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 2667e57..b014f0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,10 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... - pip install --user flake8 +- mkdir -p .extracted-examples +- curl -sSL -o .extracted-examples/README.md + 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' +- cd .extracted-examples && gfmxr extract -o . script: - flake8 runtests cli-v1-to-v2 @@ -36,3 +40,9 @@ script: - ./runtests test - ./runtests gfmxr - ./cli-v1-to-v2 --selftest +- for f in .extracted-examples/*.go ; do + ./cli-v1-to-v2 -w $f ; + go build -o .extracted-examples/example $f ; + ./cli-v1-to-v2 -w $f ; + go build -o .extracted-examples/example $f ; + done From e5ba2f41c7886b1d53d5cb438ed761bf26851226 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 11:09:37 -0400 Subject: [PATCH 09/24] Ensure we're in the right dir before continuing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b014f0f..d7f72ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ before_script: - mkdir -p .extracted-examples - curl -sSL -o .extracted-examples/README.md 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' -- cd .extracted-examples && gfmxr extract -o . +- pushd .extracted-examples && gfmxr extract -o . ; popd script: - flake8 runtests cli-v1-to-v2 From 0ecae535a85d072ab1ae6ac05c6bff51ae0d43ad Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 12:24:05 -0400 Subject: [PATCH 10/24] Where is flake8 on OS X? --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d7f72ee..60d8eb2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... - pip install --user flake8 +- find ~/.local -name flake8 - mkdir -p .extracted-examples - curl -sSL -o .extracted-examples/README.md 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' From 27f4b751e42b4ece1835c47126e2029d84cc20a0 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 12:29:35 -0400 Subject: [PATCH 11/24] Try a system pip install on OS X --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 60d8eb2..4f143a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,13 +12,17 @@ go: - 1.6.2 - master +env: + global: + - PIP_FLAGS="--user" + matrix: allow_failures: - go: master include: - go: 1.6.2 os: osx - env: PATH=$HOME/.local/bin:$PATH + env: PIP_FLAGS="" - go: 1.1.2 install: go get -v . before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION @@ -28,8 +32,7 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... -- pip install --user flake8 -- find ~/.local -name flake8 +- pip install $PIP_FLAGS flake8 - mkdir -p .extracted-examples - curl -sSL -o .extracted-examples/README.md 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' From b4db7c2b27cb8b5284cbbfdc41e2f1c3e9e68d69 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 12:34:33 -0400 Subject: [PATCH 12/24] Do sudo-tastic system install os OS X --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4f143a7..8f43203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,9 +12,7 @@ go: - 1.6.2 - master -env: - global: - - PIP_FLAGS="--user" +env: PIP_INSTALL="pip install --user" matrix: allow_failures: @@ -22,7 +20,7 @@ matrix: include: - go: 1.6.2 os: osx - env: PIP_FLAGS="" + env: PIP_INSTALL="sudo pip install" - go: 1.1.2 install: go get -v . before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION @@ -32,7 +30,7 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... -- pip install $PIP_FLAGS flake8 +- $PIP_INSTALL flake8 - mkdir -p .extracted-examples - curl -sSL -o .extracted-examples/README.md 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' From 9158c1189d3ccca28e8266050bbbe7f9476c40cd Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 12:40:13 -0400 Subject: [PATCH 13/24] Add dir with flake8 to path better-er --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f43203..a2d3b83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,15 +12,12 @@ go: - 1.6.2 - master -env: PIP_INSTALL="pip install --user" - matrix: allow_failures: - go: master include: - go: 1.6.2 os: osx - env: PIP_INSTALL="sudo pip install" - go: 1.1.2 install: go get -v . before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION @@ -30,7 +27,10 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... -- $PIP_INSTALL flake8 +- pip install --user flake8 +- if [ -d ~/Library/Python ] ; then + export PATH=$PATH:$(dirname $(find ~/Library/Python -name flake8)) ; + fi - mkdir -p .extracted-examples - curl -sSL -o .extracted-examples/README.md 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' From 586b49ded8a3ea52b841732b6c91f0f5b2ef4f4e Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 12:44:41 -0400 Subject: [PATCH 14/24] Give up and use sudo on OS X again --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index a2d3b83..b6a805e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,15 @@ go: - 1.6.2 - master +env: pip_install="pip install --user" + matrix: allow_failures: - go: master include: - go: 1.6.2 os: osx + env: pip_install="sudo pip install" - go: 1.1.2 install: go get -v . before_script: echo skipping gfmxr on $TRAVIS_GO_VERSION @@ -27,10 +30,7 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... -- pip install --user flake8 -- if [ -d ~/Library/Python ] ; then - export PATH=$PATH:$(dirname $(find ~/Library/Python -name flake8)) ; - fi +- $pip_install flake8 - mkdir -p .extracted-examples - curl -sSL -o .extracted-examples/README.md 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' From 2cfb957033dacc14d07867af9fb92f194ecf9441 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 13:59:45 -0400 Subject: [PATCH 15/24] Run migration tests via `./runtests` --- .travis.yml | 11 +---------- appveyor.yml | 1 + runtests | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index b6a805e..550bccf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,10 +31,6 @@ matrix: before_script: - go get github.com/urfave/gfmxr/... - $pip_install flake8 -- mkdir -p .extracted-examples -- curl -sSL -o .extracted-examples/README.md - 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' -- pushd .extracted-examples && gfmxr extract -o . ; popd script: - flake8 runtests cli-v1-to-v2 @@ -42,9 +38,4 @@ script: - ./runtests test - ./runtests gfmxr - ./cli-v1-to-v2 --selftest -- for f in .extracted-examples/*.go ; do - ./cli-v1-to-v2 -w $f ; - go build -o .extracted-examples/example $f ; - ./cli-v1-to-v2 -w $f ; - go build -o .extracted-examples/example $f ; - done +- ./runtests migrations diff --git a/appveyor.yml b/appveyor.yml index e93734d..ae80b2f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,3 +24,4 @@ build_script: - python runtests test - python runtests gfmxr - python cli-v1-to-v2 --selftest +- python runtests migrations diff --git a/runtests b/runtests index c9cbf5e..5dffd7d 100755 --- a/runtests +++ b/runtests @@ -2,7 +2,9 @@ from __future__ import print_function import argparse +import glob import os +import shutil import sys import tempfile @@ -65,6 +67,30 @@ def _vet(): _run('go vet ./...'.split()) +@_target +def _migrations(): + migration_script = os.path.abspath( + os.environ.get('V1TOV2', './cli-v1-to-v2') + ) + v1_readme_url = os.environ.get( + 'V1README', + 'https://raw.githubusercontent.com/urfave/cli/v1/README.md' + ) + + tmpdir = tempfile.mkdtemp() + try: + os.chdir(tmpdir) + _run('curl -sSL -o README.md {}'.format(v1_readme_url).split()) + _run('gfmxr extract -o .'.split()) + + for gofile in glob.glob('*.go'): + for i in (0, 1): + _run(['python', migration_script, '-w', gofile]) + _run('go build -o tmp.out {}'.format(gofile).split()) + finally: + shutil.rmtree(tmpdir, ignore_errors=True) + + def _run(command): print('runtests: {}'.format(' '.join(command)), file=sys.stderr) check_call(command) From 659e1c1e0bf53b6d1d55ade8393eaabe9cd637e9 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 15:15:49 -0400 Subject: [PATCH 16/24] Fill in migrations for cli.Exit and BoolFlag with Value --- cli-v1-to-v2 | 46 +++++++++++++++++++++++++++++++++++++++++++++- runtests | 3 ++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 175afa7..60a19b6 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -20,7 +20,7 @@ _SLICE_TYPE_RE = re.compile( flags=re.DOTALL ) _FLAG_LITERAL_RE = re.compile( - '(?P[^&]?)cli\\.(?P\\w+)Flag{', + '(?P\\s+)cli\\.(?P\\w+)Flag{', ) _COMMAND_SLICE_RE = re.compile( '(?P\\[\\])cli\\.Command{', @@ -28,6 +28,11 @@ _COMMAND_SLICE_RE = re.compile( _COMMAND_LITERAL_RE = re.compile( '(?P\\s+)cli\\.Command{', ) +_EXIT_ERROR_RE = re.compile('cli\\.NewExitError') +_BOOL_T_FLAG_RE = re.compile( + 'cli\\.BoolTFlag{(?P[^}]*)}', + flags=re.DOTALL +) _MIGRATORS = {} @@ -162,6 +167,21 @@ def _migrate_v1_imports(source): return _V1_IMPORT_RE.sub(_V2_IMPORT, source) +@_migrator +def _migrate_new_exit_error(source): + return _EXIT_ERROR_RE.sub('cli.Exit', source) + +@_migrator +def _migrate_bool_t_flag(source): + return _BOOL_T_FLAG_RE.sub(_bool_t_flag_repl, source) + + +def _bool_t_flag_repl(match): + return 'cli.BoolFlag{{Value: true, {}}}'.format( + match.groupdict()['args'] + ) + + def test_migrators(): import difflib @@ -235,6 +255,30 @@ _MIGRATOR_TESTS = ( \t"gopkg.in/urfave/cli.v2" \t"gopkg.in/urfave/cli.v2" \t"gopkg.in/urfave/cli.v2" +"""), + (""" +\t\t\t\treturn cli.NewExitError("foo whatebber", 9) +""", """ +\t\t\t\treturn cli.Exit("foo whatebber", 9) +"""), + (""" +\t\t\tapp.Flags = []cli.Flag{ +\t\t\t\tcli.StringFlag{ +\t\t\t\t\tName: "aha", +\t\t\t\t}, +\t\t\t\tcli.BoolTFlag{ +\t\t\t\t\tName: "blurp", +\t\t\t\t}, +\t\t\t} +""", """ +\t\t\tapp.Flags = []cli.Flag{ +\t\t\t\t&cli.StringFlag{ +\t\t\t\t\tName: "aha", +\t\t\t\t}, +\t\t\t\t&cli.BoolFlag{Value: true, +\t\t\t\t\tName: "blurp", +\t\t\t\t}, +\t\t\t} """) ) diff --git a/runtests b/runtests index 5dffd7d..4975f96 100755 --- a/runtests +++ b/runtests @@ -88,7 +88,8 @@ def _migrations(): _run(['python', migration_script, '-w', gofile]) _run('go build -o tmp.out {}'.format(gofile).split()) finally: - shutil.rmtree(tmpdir, ignore_errors=True) + if os.environ.get('NOCLEAN', '') == '': + shutil.rmtree(tmpdir, ignore_errors=True) def _run(command): From e3a099f7c54fbeaecdf2dc4c774665ac6e33bd0d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 30 May 2016 15:24:16 -0400 Subject: [PATCH 17/24] Cleanups per flake8 and code review --- .gitignore | 1 - .travis.yml | 2 -- cli-v1-to-v2 | 5 +++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 2a7262d..7823778 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ *.coverprofile -.extracted-examples diff --git a/.travis.yml b/.travis.yml index 550bccf..e7475b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ language: go sudo: false -cache: pip - go: - 1.2.2 - 1.3.3 diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 60a19b6..90f127a 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -171,13 +171,14 @@ def _migrate_v1_imports(source): def _migrate_new_exit_error(source): return _EXIT_ERROR_RE.sub('cli.Exit', source) + @_migrator def _migrate_bool_t_flag(source): return _BOOL_T_FLAG_RE.sub(_bool_t_flag_repl, source) def _bool_t_flag_repl(match): - return 'cli.BoolFlag{{Value: true, {}}}'.format( + return 'cli.BoolFlag{{Value: true,{}}}'.format( match.groupdict()['args'] ) @@ -275,7 +276,7 @@ _MIGRATOR_TESTS = ( \t\t\t\t&cli.StringFlag{ \t\t\t\t\tName: "aha", \t\t\t\t}, -\t\t\t\t&cli.BoolFlag{Value: true, +\t\t\t\t&cli.BoolFlag{Value: true, \t\t\t\t\tName: "blurp", \t\t\t\t}, \t\t\t} From c9011d835167b3e148a585661c596ee2891d56ba Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Tue, 31 May 2016 16:39:56 -0400 Subject: [PATCH 18/24] Add migrators for context.Args() indexing and EnvVars --- cli-v1-to-v2 | 68 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 90f127a..1d64e72 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -19,16 +19,12 @@ _SLICE_TYPE_RE = re.compile( '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', flags=re.DOTALL ) -_FLAG_LITERAL_RE = re.compile( - '(?P\\s+)cli\\.(?P\\w+)Flag{', -) -_COMMAND_SLICE_RE = re.compile( - '(?P\\[\\])cli\\.Command{', -) -_COMMAND_LITERAL_RE = re.compile( - '(?P\\s+)cli\\.Command{', -) +_FLAG_LITERAL_RE = re.compile('(?P\\s+)cli\\.(?P\\w+)Flag{') +_COMMAND_SLICE_RE = re.compile('(?P\\[\\])cli\\.Command{') +_COMMAND_LITERAL_RE = re.compile('(?P\\s+)cli\\.Command{') _EXIT_ERROR_RE = re.compile('cli\\.NewExitError') +_CONTEXT_ARGS_INDEX_RE = re.compile('\\.Args\\(\\)\\[(?P\\d+)\\]') +_ENVVAR_STRING_RE = re.compile('EnvVar:(?P\\s+)"(?P[^"]+)"') _BOOL_T_FLAG_RE = re.compile( 'cli\\.BoolTFlag{(?P[^}]*)}', flags=re.DOTALL @@ -183,6 +179,30 @@ def _bool_t_flag_repl(match): ) +@_migrator +def _migrate_context_args_index(source): + return _CONTEXT_ARGS_INDEX_RE.sub(_context_args_index_repl, source) + + +def _context_args_index_repl(match): + return '.Args().Get({})'.format(match.groupdict()['index']) + + +@_migrator +def _migrate_envvar_string(source): + return _ENVVAR_STRING_RE.sub(_envvar_string_repl, source) + + +def _envvar_string_repl(match): + return 'EnvVars:{}[]string{{{}}}'.format( + match.groupdict()['ws'], + ', '.join([ + '"{}"'.format(s) for s in + re.split('\\s*,\\s*', match.groupdict()['string']) + ]) + ) + + def test_migrators(): import difflib @@ -280,6 +300,36 @@ _MIGRATOR_TESTS = ( \t\t\t\t\tName: "blurp", \t\t\t\t}, \t\t\t} +"""), + (""" +\t\t\tAction = func(c *cli.Context) error { +\t\t\t\tif c.Args()[4] == "meep" { +\t\t\t\t\treturn nil +\t\t\t\t} +\t\t\t\treturn errors.New("mope") +\t\t\t} +""", """ +\t\t\tAction = func(c *cli.Context) error { +\t\t\t\tif c.Args().Get(4) == "meep" { +\t\t\t\t\treturn nil +\t\t\t\t} +\t\t\t\treturn errors.New("mope") +\t\t\t} +"""), + (""" +\t\tapp.Flags = []cli.Flag{ +\t\t\tcli.StringFlag{ +\t\t\t\tName: "toots", +\t\t\t\tEnvVar: "TOOTS,TOOTERS", +\t\t\t}, +\t\t} +""", """ +\t\tapp.Flags = []cli.Flag{ +\t\t\t&cli.StringFlag{ +\t\t\t\tName: "toots", +\t\t\t\tEnvVars: []string{"TOOTS", "TOOTERS"}, +\t\t\t}, +\t\t} """) ) From cbd0e8231babff69e6cb2149503ee6005d0ab44f Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Tue, 31 May 2016 20:58:12 -0400 Subject: [PATCH 19/24] Add migrator for stringly values of flag "Name" and reorganize things a bit for less jumpy editing --- cli-v1-to-v2 | 130 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 43 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 1d64e72..ce7ad3c 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -12,24 +12,6 @@ _DESCRIPTION = """\ Migrate arbitrary `.go` sources (mostly) from the v1 to v2 API. """ _V2_IMPORT = '"gopkg.in/urfave/cli.v2"' -_V1_IMPORT_RE = re.compile( - '"(?:github\\.com|gopkg\\.in)/(?:codegangsta|urfave)/cli(?:\\.v1|)"', -) -_SLICE_TYPE_RE = re.compile( - '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', - flags=re.DOTALL -) -_FLAG_LITERAL_RE = re.compile('(?P\\s+)cli\\.(?P\\w+)Flag{') -_COMMAND_SLICE_RE = re.compile('(?P\\[\\])cli\\.Command{') -_COMMAND_LITERAL_RE = re.compile('(?P\\s+)cli\\.Command{') -_EXIT_ERROR_RE = re.compile('cli\\.NewExitError') -_CONTEXT_ARGS_INDEX_RE = re.compile('\\.Args\\(\\)\\[(?P\\d+)\\]') -_ENVVAR_STRING_RE = re.compile('EnvVar:(?P\\s+)"(?P[^"]+)"') -_BOOL_T_FLAG_RE = re.compile( - 'cli\\.BoolTFlag{(?P[^}]*)}', - flags=re.DOTALL -) - _MIGRATORS = {} @@ -120,86 +102,135 @@ def _migrator(func): @_migrator def _migrate_command_slice(source): - return _COMMAND_SLICE_RE.sub(_command_slice_repl, source) + return re.sub( + '(?P\\[\\])cli\\.Command{', + _command_slice_repl, source + ) def _command_slice_repl(match): - return '{}*cli.Command{{'.format(match.groupdict()['prefix']) + return '{prefix}*cli.Command{{'.format(**match.groupdict()) @_migrator def _migrate_command_literal(source): - return _COMMAND_LITERAL_RE.sub(_command_literal_repl, source) + return re.sub( + '(?P\\s+)cli\\.Command{', + _command_literal_repl, source + ) def _command_literal_repl(match): - return '{}&cli.Command{{'.format(match.groupdict()['prefix']) + return '{prefix}&cli.Command{{'.format(**match.groupdict()) @_migrator def _migrate_slice_types(source): - return _SLICE_TYPE_RE.sub(_slice_type_repl, source) + return re.sub( + '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', + _slice_type_repl, source, flags=re.DOTALL + ) def _slice_type_repl(match): - return 'cli.New{}({})'.format( - match.groupdict()['type'], match.groupdict()['args'] - ) + return 'cli.New{type}({args})'.format(**match.groupdict()) @_migrator def _migrate_flag_literals(source): - return _FLAG_LITERAL_RE.sub(_flag_literal_repl, source) + return re.sub( + '(?P\\s+)cli\\.(?P\\w+)Flag{', + _flag_literal_repl, source + ) def _flag_literal_repl(match): - return '{}&cli.{}Flag{{'.format( - match.groupdict()['prefix'], match.groupdict()['type'] - ) + return '{prefix}&cli.{type}Flag{{'.format(**match.groupdict()) @_migrator def _migrate_v1_imports(source): - return _V1_IMPORT_RE.sub(_V2_IMPORT, source) + return re.sub( + '"(?:github\\.com|gopkg\\.in)/(?:codegangsta|urfave)/cli(?:\\.v1|)"', + _V2_IMPORT, source + ) @_migrator def _migrate_new_exit_error(source): - return _EXIT_ERROR_RE.sub('cli.Exit', source) + return re.sub('cli\\.NewExitError', 'cli.Exit', source) @_migrator def _migrate_bool_t_flag(source): - return _BOOL_T_FLAG_RE.sub(_bool_t_flag_repl, source) + return re.sub( + 'cli\\.BoolTFlag{(?P[^}]*)}', + _bool_t_flag_repl, source, flags=re.DOTALL + ) def _bool_t_flag_repl(match): - return 'cli.BoolFlag{{Value: true,{}}}'.format( - match.groupdict()['args'] - ) + return 'cli.BoolFlag{{Value: true,{args}}}'.format(**match.groupdict()) @_migrator def _migrate_context_args_index(source): - return _CONTEXT_ARGS_INDEX_RE.sub(_context_args_index_repl, source) + return re.sub( + '\\.Args\\(\\)\\[(?P\\d+)\\]', + _context_args_index_repl, source + ) def _context_args_index_repl(match): - return '.Args().Get({})'.format(match.groupdict()['index']) + return '.Args().Get({index})'.format(**match.groupdict()) @_migrator def _migrate_envvar_string(source): - return _ENVVAR_STRING_RE.sub(_envvar_string_repl, source) + return re.sub('EnvVar:(?P\\s+)"(?P[^"]+)"', _envvar_string_repl, source) def _envvar_string_repl(match): - return 'EnvVars:{}[]string{{{}}}'.format( - match.groupdict()['ws'], - ', '.join([ + return 'EnvVars:{ws}[]string{{{value}}}'.format( + value=', '.join([ '"{}"'.format(s) for s in re.split('\\s*,\\s*', match.groupdict()['string']) - ]) + ]), + **match.groupdict() + ) + + +@_migrator +def _migrate_flag_name_stringly(source): + return re.sub( + '(?P\\s+)Name:(?P\\s+)"(?P[^"]+)"', + _flag_name_stringly_repl, source + ) + + +def _flag_name_stringly_repl(match): + revars = dict(match.groupdict()) + + string = revars['string'] + parts = list( + reversed( + sorted( + filter(lambda s: len(s.strip()) > 0, [ + part.strip() for part in string.split(',') + ]), key=len + ) + ) + ) + + if len(parts) == 1: + return '{prefix}Name:{ws}"{string}"'.format(**revars) + + return ( + '{prefix}Name:{ws}"{name}", Aliases: []string{{{aliases}}}' + ).format( + name=parts[0], + aliases=', '.join(['"{}"'.format(s) for s in parts[1:]]), + **revars ) @@ -330,6 +361,19 @@ _MIGRATOR_TESTS = ( \t\t\t\tEnvVars: []string{"TOOTS", "TOOTERS"}, \t\t\t}, \t\t} +"""), + (""" +\t\tapp.Flags = []cli.Flag{ +\t\t\tcli.StringFlag{ +\t\t\t\tName: "t, tootles, toots", +\t\t\t}, +\t\t} +""", """ +\t\tapp.Flags = []cli.Flag{ +\t\t\t&cli.StringFlag{ +\t\t\t\tName: "tootles", Aliases: []string{"toots", "t"}, +\t\t\t}, +\t\t} """) ) From 733431d5c24b958f1184db84c3d172ed6931609e Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Tue, 31 May 2016 20:59:09 -0400 Subject: [PATCH 20/24] Wrap a long line --- cli-v1-to-v2 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index ce7ad3c..08435f7 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -187,7 +187,10 @@ def _context_args_index_repl(match): @_migrator def _migrate_envvar_string(source): - return re.sub('EnvVar:(?P\\s+)"(?P[^"]+)"', _envvar_string_repl, source) + return re.sub( + 'EnvVar:(?P\\s+)"(?P[^"]+)"', + _envvar_string_repl, source + ) def _envvar_string_repl(match): From 92b70c22ac79283a22b2ce5b47fe0cca214444c2 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Wed, 1 Jun 2016 19:11:05 -0400 Subject: [PATCH 21/24] Add more v1 -> v2 migrators --- cli-v1-to-v2 | 144 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 114 insertions(+), 30 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 08435f7..e6b0e9a 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -2,6 +2,7 @@ from __future__ import print_function, unicode_literals import argparse +import io import logging import os import re @@ -52,7 +53,7 @@ def main(sysargs=sys.argv[:]): if args.write: logging.info('Updating %s', filepath) - with open(filepath, 'w') as outfile: + with io.open(filepath, 'w', encoding='utf-8') as outfile: outfile.write(updated_source) else: logging.info('// Updated %s:', filepath) @@ -84,7 +85,7 @@ def _find_candidate_files(paths): def _update_filepath(filepath): - with open(filepath) as infile: + with io.open(filepath, encoding='utf-8') as infile: return _update_source(infile.read()) @@ -96,39 +97,39 @@ def _update_source(source): def _migrator(func): - _MIGRATORS[func.__name__.replace('_migrate_', '')] = func + _MIGRATORS[func.__name__.strip('_')] = func return func @_migrator -def _migrate_command_slice(source): +def _slice_pointer_types(source): return re.sub( - '(?P\\[\\])cli\\.Command{', - _command_slice_repl, source + '(?P\\[\\])cli\\.(?PCommand|Author){', + _slice_pointer_types_repl, source, flags=re.UNICODE ) -def _command_slice_repl(match): - return '{prefix}*cli.Command{{'.format(**match.groupdict()) +def _slice_pointer_types_repl(match): + return '{prefix}*cli.{type}{{'.format(**match.groupdict()) @_migrator -def _migrate_command_literal(source): +def _pointer_type_literal(source): return re.sub( - '(?P\\s+)cli\\.Command{', - _command_literal_repl, source + '(?P\\s+)cli\\.(?PCommand|Author){', + _pointer_type_literal_repl, source, flags=re.UNICODE ) -def _command_literal_repl(match): - return '{prefix}&cli.Command{{'.format(**match.groupdict()) +def _pointer_type_literal_repl(match): + return '{prefix}&cli.{type}{{'.format(**match.groupdict()) @_migrator -def _migrate_slice_types(source): +def _slice_types(source): return re.sub( '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', - _slice_type_repl, source, flags=re.DOTALL + _slice_type_repl, source, flags=re.DOTALL|re.UNICODE ) @@ -137,10 +138,10 @@ def _slice_type_repl(match): @_migrator -def _migrate_flag_literals(source): +def _flag_literals(source): return re.sub( '(?P\\s+)cli\\.(?P\\w+)Flag{', - _flag_literal_repl, source + _flag_literal_repl, source, flags=re.UNICODE ) @@ -149,23 +150,23 @@ def _flag_literal_repl(match): @_migrator -def _migrate_v1_imports(source): +def _v1_imports(source): return re.sub( '"(?:github\\.com|gopkg\\.in)/(?:codegangsta|urfave)/cli(?:\\.v1|)"', - _V2_IMPORT, source + _V2_IMPORT, source, flags=re.UNICODE ) @_migrator -def _migrate_new_exit_error(source): - return re.sub('cli\\.NewExitError', 'cli.Exit', source) +def _new_exit_error(source): + return re.sub('cli\\.NewExitError', 'cli.Exit', source, flags=re.UNICODE) @_migrator -def _migrate_bool_t_flag(source): +def _bool_t_flag(source): return re.sub( 'cli\\.BoolTFlag{(?P[^}]*)}', - _bool_t_flag_repl, source, flags=re.DOTALL + _bool_t_flag_repl, source, flags=re.DOTALL|re.UNICODE ) @@ -174,10 +175,10 @@ def _bool_t_flag_repl(match): @_migrator -def _migrate_context_args_index(source): +def _context_args_index(source): return re.sub( '\\.Args\\(\\)\\[(?P\\d+)\\]', - _context_args_index_repl, source + _context_args_index_repl, source, flags=re.UNICODE ) @@ -186,10 +187,10 @@ def _context_args_index_repl(match): @_migrator -def _migrate_envvar_string(source): +def _envvar_string(source): return re.sub( 'EnvVar:(?P\\s+)"(?P[^"]+)"', - _envvar_string_repl, source + _envvar_string_repl, source, flags=re.UNICODE ) @@ -197,17 +198,20 @@ def _envvar_string_repl(match): return 'EnvVars:{ws}[]string{{{value}}}'.format( value=', '.join([ '"{}"'.format(s) for s in - re.split('\\s*,\\s*', match.groupdict()['string']) + re.split( + '\\s*,\\s*', match.groupdict()['string'], + flags=re.UNICODE + ) ]), **match.groupdict() ) @_migrator -def _migrate_flag_name_stringly(source): +def _flag_name_stringly(source): return re.sub( '(?P\\s+)Name:(?P\\s+)"(?P[^"]+)"', - _flag_name_stringly_repl, source + _flag_name_stringly_repl, source, flags=re.UNICODE ) @@ -237,6 +241,86 @@ def _flag_name_stringly_repl(match): ) +@_migrator +def _commands_opaque_type(source): + return re.sub('cli\\.Commands', '[]*cli.Command', source, flags=re.UNICODE) + + +@_migrator +def _flag_names(source): + return re.sub('\\.GetName\\(\\)', '.Names()[0]', source, flags=re.UNICODE) + + +@_migrator +def _app_categories(source): + return re.sub( + '\\.App\\.Categories\\(\\)', '.App.Categories', + source, flags=re.UNICODE + ) + + +@_migrator +def _range_categories(source): + # XXX: brittle + return re.sub( + '(?P:=\\s+range\\s+)categories(?P[^\\.])', + _range_categories_repl, source, flags=re.UNICODE|re.IGNORECASE + ) + + +def _range_categories_repl(match): + return '{prefix}categories.Categories(){suffix}'.format( + **match.groupdict() + ) + + +@_migrator +def _command_category_commands(source): + # XXX: brittle + return re.sub( + '(?P\\s+category\\.)Commands(?P[^(])', + _command_category_commands_repl, source, flags=re.UNICODE + ) + + +def _command_category_commands_repl(match): + return '{prefix}VisibleCommands(){suffix}'.format(**match.groupdict()) + + +@_migrator +def _context_bool_t(source): + # XXX: probably brittle + return re.sub( + '(?P\\S+)(?:Global|)BoolT\\(', + _context_bool_t_repl, source, flags=re.UNICODE + ) + + +def _context_bool_t_repl(match): + return '!{prefix}Bool('.format(**match.groupdict()) + + +@_migrator +def _context_global_methods(source): + return re.sub( + '\\.Global(?P' \ + 'Bool|Duration|Float64|Generic|Int|IntSlice|String|StringSlice|' \ + 'FlagNames|IsSet|Set' \ + ')\\(', + _context_global_methods_repl, source, flags=re.UNICODE + ) + + +def _context_global_methods_repl(match): + return '.{method}('.format(**match.groupdict()) + + +@_migrator +def _context_parent(source): + # XXX: brittle + return re.sub('\\.Parent\\(\\)', '.Lineage()[1]', source, flags=re.UNICODE) + + def test_migrators(): import difflib From 73acde8e690f035024688f293a7400359dc77eb1 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 2 Jun 2016 04:37:36 -0400 Subject: [PATCH 22/24] Make migrators ordered, clean up some duplication --- cli-v1-to-v2 | 112 ++++++++++++++++++--------------------------------- 1 file changed, 40 insertions(+), 72 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index e6b0e9a..b3fb7ca 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -12,8 +12,7 @@ import sys _DESCRIPTION = """\ Migrate arbitrary `.go` sources (mostly) from the v1 to v2 API. """ -_V2_IMPORT = '"gopkg.in/urfave/cli.v2"' -_MIGRATORS = {} +_MIGRATORS = [] def main(sysargs=sys.argv[:]): @@ -90,70 +89,60 @@ def _update_filepath(filepath): def _update_source(source): - for migrator, func in _MIGRATORS.items(): + for migrator, func in _MIGRATORS: logging.info('Running %s migrator', migrator) source = func(source) return source +def _subfmt(pattern, replfmt, source, flags=re.UNICODE): + def repl(match): + return replfmt.format(**match.groupdict()) + return re.sub(pattern, repl, source, flags=flags) + + def _migrator(func): - _MIGRATORS[func.__name__.strip('_')] = func + _MIGRATORS.append((func.__name__.strip('_'), func)) return func @_migrator def _slice_pointer_types(source): - return re.sub( + return _subfmt( '(?P\\[\\])cli\\.(?PCommand|Author){', - _slice_pointer_types_repl, source, flags=re.UNICODE + '{prefix}*cli.{type}{{', source ) -def _slice_pointer_types_repl(match): - return '{prefix}*cli.{type}{{'.format(**match.groupdict()) - - @_migrator def _pointer_type_literal(source): - return re.sub( + return _subfmt( '(?P\\s+)cli\\.(?PCommand|Author){', - _pointer_type_literal_repl, source, flags=re.UNICODE + '{prefix}&cli.{type}{{', source ) -def _pointer_type_literal_repl(match): - return '{prefix}&cli.{type}{{'.format(**match.groupdict()) - - @_migrator def _slice_types(source): - return re.sub( + return _subfmt( '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', - _slice_type_repl, source, flags=re.DOTALL|re.UNICODE + 'cli.New{type}({args})', source, flags=re.DOTALL|re.UNICODE ) -def _slice_type_repl(match): - return 'cli.New{type}({args})'.format(**match.groupdict()) - - @_migrator def _flag_literals(source): - return re.sub( + return _subfmt( '(?P\\s+)cli\\.(?P\\w+)Flag{', - _flag_literal_repl, source, flags=re.UNICODE + '{prefix}&cli.{type}Flag{{', source ) -def _flag_literal_repl(match): - return '{prefix}&cli.{type}Flag{{'.format(**match.groupdict()) - - @_migrator def _v1_imports(source): return re.sub( '"(?:github\\.com|gopkg\\.in)/(?:codegangsta|urfave)/cli(?:\\.v1|)"', - _V2_IMPORT, source, flags=re.UNICODE + '"gopkg.in/urfave/cli.v2"', source, flags=re.UNICODE ) @@ -164,28 +153,29 @@ def _new_exit_error(source): @_migrator def _bool_t_flag(source): - return re.sub( + return _subfmt( 'cli\\.BoolTFlag{(?P[^}]*)}', - _bool_t_flag_repl, source, flags=re.DOTALL|re.UNICODE + 'cli.BoolFlag{{Value: true,{args}}}', + source, flags=re.DOTALL|re.UNICODE ) -def _bool_t_flag_repl(match): - return 'cli.BoolFlag{{Value: true,{args}}}'.format(**match.groupdict()) +@_migrator +def _context_args_len(source): + return _subfmt( + 'len\\((?P\\S+)\\.Args\\(\\)\\)', + '{prefix}.Args().Len()', source + ) @_migrator def _context_args_index(source): - return re.sub( + return _subfmt( '\\.Args\\(\\)\\[(?P\\d+)\\]', - _context_args_index_repl, source, flags=re.UNICODE + '.Args().Get({index})', source ) -def _context_args_index_repl(match): - return '.Args().Get({index})'.format(**match.groupdict()) - - @_migrator def _envvar_string(source): return re.sub( @@ -253,68 +243,46 @@ def _flag_names(source): @_migrator def _app_categories(source): - return re.sub( - '\\.App\\.Categories\\(\\)', '.App.Categories', - source, flags=re.UNICODE + source = _subfmt( + '(?Prange\\s+\\S+)\\.App\\.Categories\\(\\)', + '{prefix}.App.Categories.Categories()', source ) - -@_migrator -def _range_categories(source): - # XXX: brittle return re.sub( - '(?P:=\\s+range\\s+)categories(?P[^\\.])', - _range_categories_repl, source, flags=re.UNICODE|re.IGNORECASE - ) - - -def _range_categories_repl(match): - return '{prefix}categories.Categories(){suffix}'.format( - **match.groupdict() + '\\.App\\.Categories\\(\\)', '.App.Categories', + source, flags=re.UNICODE ) @_migrator def _command_category_commands(source): # XXX: brittle - return re.sub( + return _subfmt( '(?P\\s+category\\.)Commands(?P[^(])', - _command_category_commands_repl, source, flags=re.UNICODE + '{prefix}VisibleCommands(){suffix}', source ) -def _command_category_commands_repl(match): - return '{prefix}VisibleCommands(){suffix}'.format(**match.groupdict()) - - @_migrator def _context_bool_t(source): # XXX: probably brittle - return re.sub( + return _subfmt( '(?P\\S+)(?:Global|)BoolT\\(', - _context_bool_t_repl, source, flags=re.UNICODE + '!{prefix}Bool(', source ) -def _context_bool_t_repl(match): - return '!{prefix}Bool('.format(**match.groupdict()) - - @_migrator def _context_global_methods(source): - return re.sub( + return _subfmt( '\\.Global(?P' \ 'Bool|Duration|Float64|Generic|Int|IntSlice|String|StringSlice|' \ 'FlagNames|IsSet|Set' \ ')\\(', - _context_global_methods_repl, source, flags=re.UNICODE + '.{method}(', source ) -def _context_global_methods_repl(match): - return '.{method}('.format(**match.groupdict()) - - @_migrator def _context_parent(source): # XXX: brittle From 6dcb4fdfa4a19c33769e1f2ccb04c5e615b05b4d Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 2 Jun 2016 04:43:31 -0400 Subject: [PATCH 23/24] Migrator script output tweaks, debug logging --- cli-v1-to-v2 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index b3fb7ca..91f01d5 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -25,6 +25,9 @@ def main(sysargs=sys.argv[:]): action='store_true', default=False) parser.add_argument('-q', '--quiet', help='quiet down the logging', action='store_true', default=False) + parser.add_argument('-D', '--debug', help='debug up the logging', + action='store_true', + default=(os.environ.get('DEBUG') != '')) parser.add_argument('--selftest', help='run internal tests', action='store_true', default=False) @@ -38,10 +41,10 @@ def main(sysargs=sys.argv[:]): test_migrators() return 0 - logging.basicConfig( - level=(logging.FATAL if args.quiet else logging.INFO), - format='%(message)s' - ) + level = logging.FATAL if args.quiet else logging.INFO + level = logging.DEBUG if args.debug else level + + logging.basicConfig(level=level, format='%(message)s') paths = args.path if len(paths) == 0: @@ -90,7 +93,7 @@ def _update_filepath(filepath): def _update_source(source): for migrator, func in _MIGRATORS: - logging.info('Running %s migrator', migrator) + logging.debug('Running %s migrator', migrator) source = func(source) return source From c413767d2598dc02bd4f69d1080e6aa40e882268 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Thu, 2 Jun 2016 04:45:13 -0400 Subject: [PATCH 24/24] Fixing some flake8 gripes --- cli-v1-to-v2 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli-v1-to-v2 b/cli-v1-to-v2 index 91f01d5..904d50e 100755 --- a/cli-v1-to-v2 +++ b/cli-v1-to-v2 @@ -129,7 +129,7 @@ def _pointer_type_literal(source): def _slice_types(source): return _subfmt( '&cli\\.(?PIntSlice|StringSlice){(?P[^}]*)}', - 'cli.New{type}({args})', source, flags=re.DOTALL|re.UNICODE + 'cli.New{type}({args})', source, flags=re.DOTALL | re.UNICODE ) @@ -159,7 +159,7 @@ def _bool_t_flag(source): return _subfmt( 'cli\\.BoolTFlag{(?P[^}]*)}', 'cli.BoolFlag{{Value: true,{args}}}', - source, flags=re.DOTALL|re.UNICODE + source, flags=re.DOTALL | re.UNICODE ) @@ -278,9 +278,9 @@ def _context_bool_t(source): @_migrator def _context_global_methods(source): return _subfmt( - '\\.Global(?P' \ - 'Bool|Duration|Float64|Generic|Int|IntSlice|String|StringSlice|' \ - 'FlagNames|IsSet|Set' \ + '\\.Global(?P' + 'Bool|Duration|Float64|Generic|Int|IntSlice|String|StringSlice|' + 'FlagNames|IsSet|Set' ')\\(', '.{method}(', source )