Rename migration script and add more migrators

This commit is contained in:
Dan Buch 2016-05-30 09:51:12 -04:00
parent 459201f506
commit 80190b203c
No known key found for this signature in database
GPG Key ID: FAEF12936DD3E3EC
5 changed files with 261 additions and 83 deletions

View File

@ -2,6 +2,8 @@ language: go
sudo: false sudo: false
cache: pip
go: go:
- 1.2.2 - 1.2.2
- 1.3.3 - 1.3.3
@ -25,8 +27,11 @@ matrix:
before_script: before_script:
- go get github.com/urfave/gfmxr/... - go get github.com/urfave/gfmxr/...
- pip install flake8
script: script:
- flake8 runtests cli-v1-to-v2
- ./runtests vet - ./runtests vet
- ./runtests test - ./runtests test
- ./runtests gfmxr - ./runtests gfmxr
- ./cli-v1-to-v2 --selftest

View File

@ -18,8 +18,10 @@ install:
- go env - go env
- go get github.com/urfave/gfmxr/... - go get github.com/urfave/gfmxr/...
- go get -v -t ./... - go get -v -t ./...
- pip install flake8
build_script: build_script:
- flake8 runtests cli-v1-to-v2
- python runtests vet - python runtests vet
- python runtests test - python runtests test
- python runtests gfmxr - python runtests gfmxr

View File

@ -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\\.(?P<type>IntSlice|StringSlice){(?P<args>[^}]*)}',
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())

235
cli-v1-to-v2 Executable file
View File

@ -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\\.(?P<type>IntSlice|StringSlice){(?P<args>[^}]*)}',
flags=re.DOTALL
)
_FLAG_LITERAL_RE = re.compile(
'(?P<prefix>[^&]?)cli\\.(?P<type>\\w+)Flag{',
)
_COMMAND_SLICE_RE = re.compile(
'(?P<prefix>\\[\\])cli\\.Command{',
)
_COMMAND_LITERAL_RE = re.compile(
'(?P<prefix>\\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())

View File

@ -9,18 +9,13 @@ import tempfile
from subprocess import check_call, check_output from subprocess import check_call, check_output
PACKAGE_NAME = os.environ.get( _PACKAGE_NAME = os.environ.get(
'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 'CLI_PACKAGE_NAME', 'github.com/urfave/cli'
) )
_TARGETS = {}
def main(sysargs=sys.argv[:]): def main(sysargs=sys.argv[:]):
targets = {
'vet': _vet,
'test': _test,
'gfmxr': _gfmxr
}
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
'target', nargs='?', choices=tuple(targets.keys()), default='test' 'target', nargs='?', choices=tuple(targets.keys()), default='test'
@ -31,6 +26,12 @@ def main(sysargs=sys.argv[:]):
return 0 return 0
def _target(func):
_TARGETS[func.__name__.strip('_')] = func
return func
@_target
def _test(): def _test():
if check_output('go version'.split()).split()[2] < 'go1.2': if check_output('go version'.split()).split()[2] < 'go1.2':
_run('go test -v .'.split()) _run('go test -v .'.split())
@ -46,7 +47,7 @@ def _test():
_run('go test -v'.split() + [ _run('go test -v'.split() + [
'-coverprofile={}'.format(coverprofile), '-coverprofile={}'.format(coverprofile),
('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') ('{}/{}'.format(_PACKAGE_NAME, subpackage)).rstrip('/')
]) ])
combined_name = _combine_coverprofiles(coverprofiles) combined_name = _combine_coverprofiles(coverprofiles)
@ -54,14 +55,24 @@ def _test():
os.remove(combined_name) os.remove(combined_name)
@_target
def _gfmxr(): def _gfmxr():
_run(['gfmxr', '-c', str(_gfmxr_count()), '-s', 'README.md']) _run(['gfmxr', '-c', str(_gfmxr_count()), '-s', 'README.md'])
@_target
def _vet(): def _vet():
_run('go vet ./...'.split()) _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): def _run(command):
print('runtests: {}'.format(' '.join(command)), file=sys.stderr) print('runtests: {}'.format(' '.join(command)), file=sys.stderr)
check_call(command) check_call(command)