Merge ~chad.smith/cloud-init:tools/migrate-script into cloud-init:master

Proposed by Chad Smith
Status: Merged
Approved by: Chad Smith
Approved revision: 54cece7e182681987ee739849f5eb9aea7e5e35c
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~chad.smith/cloud-init:tools/migrate-script
Merge into: cloud-init:master
Diff against target: 242 lines (+230/-0)
2 files modified
tools/.lp-to-git-user (+1/-0)
tools/migrate-lp-user-to-github (+229/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Ryan Harper Needs Fixing
Review via email: mp+375163@code.launchpad.net

Commit message

tools: add migrate-lp-user-to-github script to link LP to github

To link a launchpad account name to your github account for licensing
accountability each LP user should publish a merge proposal in launchpad
with their LP account and a matching merge proposal in github using
their github user.

Cloud-init will track these usename maps in ./tools/.lp-to-git-user as
JSON.

Run ./tools/migrate-lp-user-to-github <LP_USERNAME> <GITHUB_USERNAME>
to automatically create merge proposals in launchpad and your github
account.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:505ff34c3f0d23249b2298b75caf760e61792701
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1250/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1250//rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:8b499f40bdc9d134d379dda5cc0c4fa29b6968e1
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1252/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1252//rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:752e4d74a96bd340d913f74a1d36b38af4cd8921
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1253/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1253//rebuild

review: Approve (continuous-integration)
Revision history for this message
Ryan Harper (raharper) wrote :

This works quite well.

Can we add a check that the local master/upstream is in sync with github cloud-init master? I've not fetched from master, so the github pull request I got included my two usernames *and* the 19.3 release bump since my master local branch was behind remote master.

review: Needs Fixing
Revision history for this message
Ryan Harper (raharper) wrote :

I re-ran, and the error message isn't clear about which remote (github or launchpad) has the existing branches.

So we should prefix each error with the host.

Also, it appears to be changing into a new branch under the covers, the migrate-lp-to-github; so we should emit that we're creating a local branch and the name when we do it, and pushing to github, etc.

And I think we should emit the git commands used to reset things:

git push <remote> --delete migrate-lp-to-github

or have the script do this for users.

Revision history for this message
Ryan Harper (raharper) wrote :

Also, I wish we'd store the Launchpad oath token so repeated invocations don't force a new browser open.

Revision history for this message
Ryan Harper (raharper) wrote :

I can't get this to work after removing both branches.

Creating a migration branch: migrate-lp-to-github adding your users
The authorization page:
 (https://launchpad.net/+authorize-token?oauth_token=Tn4qDRnf4r4NvPBTKBQ3&allow_permission=DESKTOP_INTEGRATION)
should be opening in your browser. Use your browser to authorize
this program to access Launchpad on your behalf.
Waiting to hear from Launchpad about your decision...
[30816:30816:1105/175558.038112:ERROR:buffer_manager.cc(488)] [.DisplayCompositor]GL ERROR :GL_INVALID_OPERATION : glBufferData: <- error from previous GL command
[30777:30777:1105/175558.055909:ERROR:account_tracker.cc(241)] AccessTokenFetched error: Invalid credentials (credentials rejected by client).
Fontconfig error: Cannot load default config file
Traceback (most recent call last):
  File "./tools/migrate-lp-user-to-github", line 203, in <module>
  File "./tools/migrate-lp-user-to-github", line 193, in main
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/resource.py", line 609, in __call__
    extra_headers=extra_headers)
  File "/usr/lib/python3/dist-packages/lazr/restfulclient/_browser.py", line 426, in _request
    raise error
lazr.restfulclient.errors.BadRequest: HTTP Error 400: Bad Request
Response headers:
---
-content-encoding: gzip
connection: close
content-length: 155
content-security-policy: frame-ancestors 'self';
content-type: text/plain
date: Tue, 05 Nov 2019 23:56:18 GMT
server: zope.server.http (HTTP)
status: 400
strict-transport-security: max-age=15552000
vary: Accept,Accept-Encoding
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-launchpad-revision: 469f241f4e73cc0bdffa4e30654052a2af068e06
x-lazr-notifications: []
x-powered-by: Zope (www.zope.org), Python (www.python.org)
x-xss-protection: 1; mode=block
---
Response body:
---
b'There is already a branch merge proposal registered for branch ~raharper/cloud-init:migrate-lp-to-github to land on cloud-init:master that is still active.'
---

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:bc9fa0fe5fb5859a4e10ff92dcb0726d89ad898c
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1261/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1261//rebuild

review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:7f6b69529fb9c2942f7084d7ffa444af57204488
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1263/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1263//rebuild

review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:54cece7e182681987ee739849f5eb9aea7e5e35c
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1273/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1273//rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/tools/.lp-to-git-user b/tools/.lp-to-git-user
2new file mode 100644
3index 0000000..0967ef4
4--- /dev/null
5+++ b/tools/.lp-to-git-user
6@@ -0,0 +1 @@
7+{}
8diff --git a/tools/migrate-lp-user-to-github b/tools/migrate-lp-user-to-github
9new file mode 100755
10index 0000000..6b095a1
11--- /dev/null
12+++ b/tools/migrate-lp-user-to-github
13@@ -0,0 +1,229 @@
14+#!/usr/bin/python3
15+"""Link your Launchpad user to github, proposing branches to LP and Github"""
16+
17+from argparse import ArgumentParser
18+from subprocess import Popen, PIPE
19+import os
20+import sys
21+
22+try:
23+ from launchpadlib.launchpad import Launchpad
24+except ImportError:
25+ print("Missing python launchpadlib dependency to create branches for you."
26+ "Install with: sudo apt-get install python3-launchpadlib" )
27+ sys.exit(1)
28+
29+if "avoid-pep8-E402-import-not-top-of-file":
30+ _tdir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
31+ sys.path.insert(0, _tdir)
32+ from cloudinit import util
33+
34+
35+DRYRUN = False
36+LP_TO_GIT_USER_FILE='.lp-to-git-user'
37+MIGRATE_BRANCH_NAME='migrate-lp-to-github'
38+GITHUB_PULL_URL='https://github.com/canonical/cloud-init/compare/master...{github_user}:{branch}'
39+GH_UPSTREAM_URL='https://github.com/canonical/cloud-init'
40+
41+
42+def error(message):
43+ if isinstance(message, bytes):
44+ message = message.decode('utf-8')
45+ log('ERROR: {error}'.format(error=message))
46+ sys.exit(1)
47+
48+
49+def log(message):
50+ print(message)
51+
52+
53+def subp(cmd, skip=False):
54+ prefix = 'SKIPPED: ' if skip else '$ '
55+ log('{prefix}{command}'.format(prefix=prefix, command=' '.join(cmd)))
56+ if skip:
57+ return
58+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
59+ out, err = proc.communicate()
60+ if proc.returncode:
61+ error(err if err else out)
62+ return out.decode('utf-8')
63+
64+
65+LP_GIT_PATH_TMPL = 'git+ssh://{launchpad_user}@git.launchpad.net/'
66+LP_UPSTREAM_PATH_TMPL = LP_GIT_PATH_TMPL + 'cloud-init'
67+LP_REMOTE_PATH_TMPL = LP_GIT_PATH_TMPL + '~{launchpad_user}/cloud-init'
68+GITHUB_REMOTE_PATH_TMPL = 'git@github.com:{github_user}/cloud-init.git'
69+
70+
71+# Comment templates
72+COMMIT_MSG_TMPL = '''
73+lp-to-git-users: adding {gh_username}
74+
75+Mapped from {lp_username}
76+'''
77+PUBLISH_DIR='/tmp/cloud-init-lp-to-github-migration'
78+
79+def get_parser():
80+ parser = ArgumentParser(description=__doc__)
81+ parser.add_argument(
82+ '--dryrun', required=False, default=False, action='store_true',
83+ help=('Run commands and review operation in dryrun mode, '
84+ 'making not changes.'))
85+ parser.add_argument('launchpad_user', help='Your launchpad username.')
86+ parser.add_argument('github_user', help='Your github username.')
87+ parser.add_argument(
88+ '--local-repo-dir', required=False, dest='repo_dir',
89+ help=('The name of the local directory into which we clone.'
90+ ' Default: {}'.format(PUBLISH_DIR)))
91+ parser.add_argument(
92+ '--upstream-branch', required=False, dest='upstream',
93+ default='origin/master',
94+ help=('The name of remote branch target into which we will merge.'
95+ ' Default: origin/master'))
96+ parser.add_argument(
97+ '-v', '--verbose', required=False, default=False, action='store_true',
98+ help=('Print all actions.'))
99+ parser.add_argument(
100+ '--push-remote', required=False, dest='pushremote',
101+ help=('QA-only provide remote name into which you want to push'))
102+ return parser
103+
104+
105+def create_publish_branch(upstream, publish_branch):
106+ '''Create clean publish branch target in the current git repo.'''
107+ branches = subp(['git', 'branch'])
108+ upstream_remote, upstream_branch = upstream.split('/', 1)
109+ subp(['git', 'checkout', upstream_branch])
110+ subp(['git', 'pull'])
111+ if publish_branch in branches:
112+ subp(['git', 'branch', '-D', publish_branch])
113+ subp(['git', 'checkout', upstream, '-b', publish_branch])
114+
115+
116+def add_lp_and_github_remotes(lp_user, gh_user):
117+ """Add lp and github remotes if not present.
118+
119+ @return Tuple with (lp_remote_name, gh_remote_name)
120+ """
121+ lp_remote = LP_REMOTE_PATH_TMPL.format(launchpad_user=lp_user)
122+ gh_remote = GITHUB_REMOTE_PATH_TMPL.format(github_user=gh_user)
123+ remotes = subp(['git', 'remote', '-v'])
124+ lp_remote_name = gh_remote_name = None
125+ for remote in remotes.splitlines():
126+ if not remote:
127+ continue
128+ remote_name, remote_url, _operation = remote.split()
129+ if lp_remote == remote_url:
130+ lp_remote_name = remote_name
131+ elif gh_remote == remote_url:
132+ gh_remote_name = remote_name
133+ if not lp_remote_name:
134+ log("launchpad: Creating git remote launchpad-{} to point at your"
135+ " LP repo".format(lp_user))
136+ lp_remote_name = 'launchpad-{}'.format(lp_user)
137+ subp(['git', 'remote', 'add', lp_remote_name, lp_remote])
138+ subp(['git', 'fetch', lp_remote_name])
139+ if not gh_remote_name:
140+ log("github: Creating git remote github-{} to point at your"
141+ " GH repo".format(gh_user))
142+ gh_remote_name = 'github-{}'.format(gh_user)
143+ subp(['git', 'remote', 'add', gh_remote_name, gh_remote])
144+ try:
145+ subp(['git', 'fetch', gh_remote_name])
146+ except:
147+ log("ERROR: [github] Could not fetch remote '{remote}'."
148+ "Please create a fork for your github user by clicking 'Fork'"
149+ " from {gh_upstream}".format(
150+ remote=gh_remote, gh_upstream=GH_UPSTREAM_URL))
151+ sys.exit(1)
152+ return (lp_remote_name, gh_remote_name)
153+
154+
155+def create_migration_branch(
156+ branch_name, upstream, lp_user, gh_user, commit_msg):
157+ """Create an LP to Github migration branch and add lp_user->gh_user."""
158+ log("Creating a migration branch: {} adding your users".format(
159+ MIGRATE_BRANCH_NAME))
160+ create_publish_branch(upstream, MIGRATE_BRANCH_NAME)
161+ lp_to_git_map = {}
162+ lp_to_git_file = os.path.join(os.getcwd(), LP_TO_GIT_USER_FILE)
163+ if os.path.exists(lp_to_git_file):
164+ with open(lp_to_git_file) as stream:
165+ lp_to_git_map = util.load_json(stream.read())
166+ if gh_user in lp_to_git_map.values():
167+ raise RuntimeError(
168+ "github user '{}' already in {}".format(gh_user, lp_to_git_file))
169+ if lp_user in lp_to_git_map:
170+ raise RuntimeError(
171+ "launchpad user '{}' already in {}".format(
172+ lp_user, lp_to_git_file))
173+ lp_to_git_map[lp_user] = gh_user
174+ with open(lp_to_git_file, 'w') as stream:
175+ stream.write(util.json_dumps(lp_to_git_map))
176+ subp(['git', 'add', lp_to_git_file])
177+ commit_file = os.path.join(os.path.dirname(os.getcwd()), 'commit.msg')
178+ with open(commit_file, 'wb') as stream:
179+ stream.write(commit_msg.encode('utf-8'))
180+ subp(['git', 'commit', '--all', '-F', commit_file])
181+
182+
183+def main():
184+ global DRYRUN
185+ global VERBOSITY
186+ parser = get_parser()
187+ args = parser.parse_args()
188+ DRYRUN = args.dryrun
189+ VERBOSITY = 1 if args.verbose else 0
190+ repo_dir = args.repo_dir or PUBLISH_DIR
191+ if not os.path.exists(repo_dir):
192+ subp(['git', 'clone',
193+ LP_UPSTREAM_PATH_TMPL.format(launchpad_user=args.launchpad_user),
194+ repo_dir])
195+ os.chdir(repo_dir)
196+ log("Sycing master branch with upstream")
197+ subp(['git', 'checkout', 'master'])
198+ subp(['git', 'pull'])
199+ lp_remote_name, gh_remote_name = add_lp_and_github_remotes(
200+ args.launchpad_user, args.github_user)
201+ commit_msg = COMMIT_MSG_TMPL.format(
202+ gh_username=args.github_user, lp_username=args.launchpad_user)
203+ create_migration_branch(
204+ MIGRATE_BRANCH_NAME, args.upstream, args.launchpad_user,
205+ args.github_user, commit_msg)
206+
207+ for push_remote in (lp_remote_name, gh_remote_name):
208+ subp(['git', 'push', push_remote, MIGRATE_BRANCH_NAME, '--force'])
209+
210+ # Make merge request on LP
211+ log("[launchpad] Automatically creating merge proposal using launchpadlib")
212+ lp = Launchpad.login_with(
213+ "server-team github-migration tool", 'production', version='devel')
214+ master = lp.git_repositories.getByPath(
215+ path='cloud-init').getRefByPath(path='master')
216+ LP_BRANCH_PATH='~{launchpad_user}/cloud-init/+git/cloud-init'
217+ lp_git_repo = lp.git_repositories.getByPath(
218+ path=LP_BRANCH_PATH.format(launchpad_user=args.launchpad_user))
219+ lp_user_migrate_branch = lp_git_repo.getRefByPath(
220+ path='refs/heads/migrate-lp-to-github')
221+ lp_merge_url = (
222+ 'https://code.launchpad.net/' +
223+ LP_BRANCH_PATH.format(launchpad_user=args.launchpad_user) +
224+ '/+ref/' + MIGRATE_BRANCH_NAME)
225+ try:
226+ lp_user_migrate_branch.createMergeProposal(
227+ commit_message=commit_msg, merge_target=master, needs_review=True)
228+ except Exception as e:
229+ log('[launchpad] active merge proposal already exists at:\n'
230+ '{url}\n'.format(url=lp_merge_url))
231+ else:
232+ log("[launchpad] Merge proposal created at:\n{url}.\n".format(
233+ url=lp_merge_url))
234+ log("To link your account to github open your browser and"
235+ " click 'Create pull request' at the following URL:\n"
236+ "{url}".format(url=GITHUB_PULL_URL.format(
237+ github_user=args.github_user, branch=MIGRATE_BRANCH_NAME)))
238+ return 0
239+
240+
241+if __name__ == '__main__':
242+ sys.exit(main())

Subscribers

People subscribed via source and target branches