def check_travis_and_circleci_tests(current_branch_name): """Checks if all travis and circleci tests are passing on release branch. Args: current_branch_name: str. The name of current branch. Raises: Exception: The latest commit on release branch locally does not match the latest commit on local fork or upstream. Exception: The travis or circleci tests are failing. """ local_sha = subprocess.check_output( ['git', 'rev-parse', current_branch_name]) origin_sha = subprocess.check_output( ['git', 'rev-parse', 'origin/%s' % current_branch_name]) upstream_sha = subprocess.check_output([ 'git', 'rev-parse', '%s/%s' % (common.get_remote_alias( release_constants.REMOTE_URL), current_branch_name) ]) if local_sha != origin_sha: raise Exception('The latest commit on release branch locally does ' 'not match the latest commit on your local fork.') if local_sha != upstream_sha: raise Exception('The latest commit on release branch locally does ' 'not match the latest commit on Oppia repo.') python_utils.PRINT('\nEnter your GitHub username.\n') github_username = python_utils.INPUT().lower() travis_url = 'https://travis-ci.org/%s/oppia/branches' % github_username circleci_url = 'https://circleci.com/gh/%s/workflows/oppia' % ( github_username) try: python_utils.url_open(travis_url) except Exception: travis_url = 'https://travis-ci.org/oppia/oppia/branches' try: python_utils.url_open(circleci_url) except Exception: circleci_url = 'https://circleci.com/gh/oppia/workflows/oppia' common.open_new_tab_in_browser_if_possible(travis_url) python_utils.PRINT('Are all travis tests passing on branch %s?\n' % current_branch_name) travis_tests_passing = python_utils.INPUT().lower() if travis_tests_passing not in release_constants.AFFIRMATIVE_CONFIRMATIONS: raise Exception('Please fix the travis tests before deploying.') common.open_new_tab_in_browser_if_possible(circleci_url) python_utils.PRINT('Are all circleci tests passing on branch %s?\n' % current_branch_name) circleci_tests_passing = python_utils.INPUT().lower() if circleci_tests_passing not in ( release_constants.AFFIRMATIVE_CONFIRMATIONS): raise Exception('Please fix the circleci tests before deploying.')
def main(): """Performs task to initiate the release.""" common.require_cwd_to_be_oppia() common.verify_current_branch_name('develop') common.open_new_tab_in_browser_if_possible( release_constants.RELEASE_NOTES_URL) common.ask_user_to_confirm( 'Please check if anything extra is required for the release. ' 'If so, keep track of this and do it at the appropriate point.') common.open_new_tab_in_browser_if_possible( release_constants.RELEASE_DRIVE_URL) python_utils.PRINT( 'Has the QA lead created a document for the current release?\n' 'Confirm by entering y/ye/yes.\n') doc_for_release_created = python_utils.INPUT().lower() if doc_for_release_created not in ( release_constants.AFFIRMATIVE_CONFIRMATIONS): raise Exception('Please ensure a new doc is created for the ' 'release before starting with the release process.') remote_alias = common.get_remote_alias(release_constants.REMOTE_URL) python_utils.PRINT('Enter version of previous release.') previous_release_version = python_utils.INPUT() assert re.match(r'^\d+\.\d+\.\d+$', previous_release_version) extra_jobs_to_run = get_extra_jobs_due_to_schema_changes( remote_alias, previous_release_version) if did_supported_audio_languages_change(remote_alias, previous_release_version): # This job is run so that an opportunity is created for # contributors to translate and voiceover an exploration # whenever a new audio language is added. # Refer: https://github.com/oppia/oppia/issues/8027. extra_jobs_to_run.append( 'ExplorationOpportunitySummaryModelRegenerationJob') if extra_jobs_to_run: common.ask_user_to_confirm( 'Please add the following jobs to release journal and ' 'run them after deployment:\n%s' % '\n'.join(extra_jobs_to_run)) common.open_new_tab_in_browser_if_possible( release_constants.REPEATABLE_JOBS_SPREADSHEETS_URL) common.open_new_tab_in_browser_if_possible( release_constants.ONE_TIME_JOBS_SPREADSHEET_URL) common.ask_user_to_confirm( 'Please copy the names of the jobs to be run for this release along ' 'with author names, author mail ids & instruction docs.\n' 'Note: Copy only those jobs that have been successfully run ' 'on backup server.') common.open_new_tab_in_browser_if_possible( release_constants.RELEASE_DRIVE_URL) common.ask_user_to_confirm( 'Please enter the above job names to release journal.') cut_release_branch()
def draft_new_release(): """Drafts a new release tag on github.""" release_version = common.get_current_release_version_number( common.get_current_branch_name()) remote_alias = common.get_remote_alias(release_constants.REMOTE_URL) subprocess.check_call([ 'git', 'tag', '-a', 'v%s' % release_version, '-m', 'Version %s' % release_version]) subprocess.check_call([ 'git', 'push', remote_alias, 'v%s' % release_version]) common.open_new_tab_in_browser_if_possible( release_constants.NEW_RELEASE_URL) common.open_new_tab_in_browser_if_possible( release_constants.GITHUB_RELEASE_TAB_URL) common.ask_user_to_confirm( 'Please draft a new release on GitHub pointing to the ' 'new tag and including relevant changelog information.\n' 'The two tabs in your browser point to: ' 'Page to draft a new release, examples of previous releases.')
def execute_branch_cut(target_version, hotfix_number): """Creates & pushes the new release branch to Github. Args: target_version: str. The release version. hotfix_number: int. The number for the hotfix branch. Raises: Exception: Travis tests are failing on the branch from which the new branch is cut. """ # Construct the new branch name. if not hotfix_number: new_branch_type, new_branch_name = _get_release_branch_type_and_name( target_version) else: new_branch_type, new_branch_name = _get_hotfix_branch_type_and_name( target_version, hotfix_number) # Do prerequisite checks. common.require_cwd_to_be_oppia() common.verify_local_repo_is_clean() common.verify_current_branch_name('develop') # Update the local repo. remote_alias = common.get_remote_alias(release_constants.REMOTE_URL) subprocess.check_call(['git', 'pull', remote_alias, 'develop']) verify_target_branch_does_not_already_exist(remote_alias, new_branch_name) # The release coordinator should verify that tests are passing on the parent # branch before checking out the new branch. common.open_new_tab_in_browser_if_possible( 'https://travis-ci.org/oppia/oppia/branches') while True: if not hotfix_number: branch_to_check = 'develop' elif hotfix_number == 1: branch_to_check = 'release-%s' % target_version else: branch_to_check = 'release-%s-hotfix-%s' % (target_version, hotfix_number - 1) python_utils.PRINT( 'Please confirm: are Travis checks passing on %s? (y/n) ' % (branch_to_check)) answer = python_utils.INPUT().lower() if answer in release_constants.AFFIRMATIVE_CONFIRMATIONS: break elif answer: raise Exception( 'Tests should pass on %s before this script is run.' % (branch_to_check)) # Cut a new release or hotfix branch. if new_branch_type == release_constants.BRANCH_TYPE_HOTFIX: verify_hotfix_number_is_one_ahead_of_previous_hotfix_number( remote_alias, target_version, hotfix_number) if hotfix_number == 1: branch_to_cut_from = 'release-%s' % target_version else: branch_to_cut_from = 'release-%s-hotfix-%s' % (target_version, hotfix_number - 1) python_utils.PRINT('Cutting a new hotfix branch: %s' % new_branch_name) subprocess.check_call( ['git', 'checkout', '-b', new_branch_name, branch_to_cut_from]) else: verify_target_version_compatible_with_latest_release(target_version) python_utils.PRINT('Cutting a new release branch: %s' % new_branch_name) subprocess.check_call(['git', 'checkout', '-b', new_branch_name]) # Push the new release branch to GitHub. python_utils.PRINT('Pushing new %s branch to GitHub.' % new_branch_type) subprocess.check_call(['git', 'push', remote_alias, new_branch_name]) python_utils.PRINT('') python_utils.PRINT( 'New %s branch successfully cut. You are now on branch %s' % (new_branch_type, new_branch_name)) python_utils.PRINT('Done!') common.ask_user_to_confirm( 'Ask Sean (or Ben, if Sean isn\'t available) to create ' 'a new branch protection rule by:\n' '1. Going to this page: https://github.com/oppia/oppia/' 'settings/branch_protection_rules/new.\n' '2. Typing in the full branch name %s.\n' '3. Checking the box: Restrict who can push to matching ' 'branches (then add the oppia/release-coordinators team)\n' % (new_branch_name))
def execute_branch_cut(target_version, hotfix_number): """Creates & pushes the new release branch to Github. Args: target_version: str. The release version. hotfix_number: int. The number for the hotfix branch. Raises: Exception. Actions tests are failing on the branch from which the new branch is cut. """ # Construct the new branch name. if not hotfix_number: new_branch_type, new_branch_name = _get_release_branch_type_and_name( target_version) else: new_branch_type, new_branch_name = _get_hotfix_branch_type_and_name( target_version, hotfix_number) # Do prerequisite checks. common.require_cwd_to_be_oppia() common.verify_local_repo_is_clean() common.verify_current_branch_name('develop') # Update the local repo. remote_alias = common.get_remote_alias( constants.release_constants.REMOTE_URLS) subprocess.check_call(['git', 'pull', remote_alias, 'develop']) verify_target_branch_does_not_already_exist(remote_alias, new_branch_name) if not hotfix_number: branch_to_check = 'develop' elif hotfix_number == 1: branch_to_check = 'release-%s' % target_version else: branch_to_check = 'release-%s-hotfix-%s' % (target_version, hotfix_number - 1) # The release coordinator should verify that tests are passing on # the parent branch before checking out the new branch. common.open_new_tab_in_browser_if_possible( 'https://github.com/oppia/oppia/actions?query=branch:%s' % branch_to_check) print('Please confirm: are Actions checks passing on %s? (y/n) ' % (branch_to_check)) answer = input().lower() if answer not in common.AFFIRMATIVE_CONFIRMATIONS: raise Exception('Tests should pass on %s before this script is run.' % (branch_to_check)) # Cut a new release or hotfix branch. if new_branch_type == constants.release_constants.BRANCH_TYPE_HOTFIX: verify_hotfix_number_is_one_ahead_of_previous_hotfix_number( remote_alias, target_version, hotfix_number) if hotfix_number == 1: branch_to_cut_from = 'release-%s' % target_version else: branch_to_cut_from = 'release-%s-hotfix-%s' % (target_version, hotfix_number - 1) print('Cutting a new hotfix branch: %s' % new_branch_name) subprocess.check_call(['git', 'checkout', branch_to_cut_from]) common.update_branch_with_upstream() subprocess.check_call( ['git', 'checkout', '-b', new_branch_name, branch_to_cut_from]) else: verify_target_version_compatible_with_latest_release(target_version) print('Cutting a new release branch: %s' % new_branch_name) subprocess.check_call(['git', 'checkout', '-b', new_branch_name]) # Push the new release branch to GitHub. if new_branch_type == constants.release_constants.BRANCH_TYPE_RELEASE: print('Pushing new %s branch to GitHub.' % new_branch_type) subprocess.check_call(['git', 'push', remote_alias, new_branch_name]) else: print('Please cherrypick the required PRs and push the branch ' 'to Github once this script is done.\n' 'Note: It is fine to push the branch only after creating the ' 'branch protection rule and doing all the cherrypicks.') print('') print('New %s branch successfully cut. You are now on branch %s' % (new_branch_type, new_branch_name)) print('Done!')
def main(): """Performs task to initiate the release.""" common.require_cwd_to_be_oppia() common.open_new_tab_in_browser_if_possible( release_constants.RELEASE_NOTES_URL) common.ask_user_to_confirm( 'Please check if anything extra is required for the release. ' 'If so, keep track of this and do it at the appropriate point.') common.open_new_tab_in_browser_if_possible( release_constants.RELEASE_DRIVE_URL) python_utils.PRINT( 'Has the QA lead created a document for the current release?\n' 'Confirm by entering y/ye/yes.\n') doc_for_release_created = python_utils.INPUT().lower() if doc_for_release_created not in ( release_constants.AFFIRMATIVE_CONFIRMATIONS): raise Exception('Please ensure a new doc is created for the ' 'release before starting with the release process.') remote_alias = common.get_remote_alias(release_constants.REMOTE_URL) python_utils.PRINT('Enter version of previous release.') previous_release_version = python_utils.INPUT() assert re.match(r'^\d+\.\d+\.\d+$', previous_release_version) extra_jobs_to_run = get_extra_jobs_due_to_schema_changes( remote_alias, previous_release_version) if did_supported_audio_languages_change(remote_alias, previous_release_version): # This job is run so that an opportunity is created for # contributors to translate and voiceover an exploration # whenever a new audio language is added. # Refer: https://github.com/oppia/oppia/issues/8027. extra_jobs_to_run.append( 'ExplorationOpportunitySummaryModelRegenerationJob') if extra_jobs_to_run: common.ask_user_to_confirm( 'Please add the following jobs to release journal and ' 'run them after deployment:\n%s' % '\n'.join(extra_jobs_to_run)) try: # The file here is opened and closed just to create an empty # file where the release co-ordinator can enter the credentials. f = python_utils.open_file(RELEASE_CREDENTIALS_FILEPATH, 'w') f.close() common.ask_user_to_confirm( 'Copy the release json credentials from the release ' 'doc and paste them in the file %s. Make sure to save the ' 'file once you are done.' % (RELEASE_CREDENTIALS_FILEPATH)) client = pygsheets.authorize( client_secret=RELEASE_CREDENTIALS_FILEPATH) repeatable_jobs_sheet = client.open( 'Oppia release team: Submitting an existing job for ' 'testing on the Oppia test server (Responses)').sheet1 repeatable_job_details = get_job_details_for_current_release( repeatable_jobs_sheet.get_all_records(), 'Select the job you want to test', 'Which upcoming release are you targeting this job for? ', 'Email Address', 'What is your name? ', 'Please give a clear description of why you want to ' 'run this job on the test server') repeatable_job_names = [ job_detail['job_name'] for job_detail in repeatable_job_details ] one_time_jobs_sheet = client.open( 'Oppia release team: Submitting a job for testing (Responses)' ).sheet1 one_time_job_details = get_job_details_for_current_release( one_time_jobs_sheet.get_all_records(), 'What is the name of the job to be run?', 'Which upcoming release are you targeting this job for?', 'Email Address', 'What is your name?', 'URL of a Google Doc with clear instructions on what the tester ' 'needs to do:') one_time_job_names = [ job_detail['job_name'] for job_detail in one_time_job_details ] if repeatable_job_names: python_utils.PRINT('Repeatable jobs to run:\n%s\n' % ('\n').join(repeatable_job_names)) if one_time_job_names: python_utils.PRINT('One time jobs to run:\n%s\n' % ('\n').join(one_time_job_names)) if repeatable_job_names or one_time_job_names: common.open_new_tab_in_browser_if_possible( release_constants.RELEASE_DRIVE_URL) common.ask_user_to_confirm( 'Please enter the above job names to release journal.') common.open_new_tab_in_browser_if_possible( release_constants.REPEATABLE_JOBS_SPREADSHEETS_URL) common.open_new_tab_in_browser_if_possible( release_constants.ONE_TIME_JOBS_SPREADSHEET_URL) python_utils.PRINT( get_mail_message_template(repeatable_job_details + one_time_job_details)) author_mail_ids = [ job_details['author_email'] for job_details in (repeatable_job_details + one_time_job_details) ] common.ask_user_to_confirm( 'Note: Send the mail only after deploying to backup server.\n\n' 'Note: Add author email ids: %s to the cc list when you send ' 'the mail.\n\n' 'Note: Please check manually if the details in the above mail ' 'are correct and add anything extra if required.\n\n' 'Copy and save the above template for sending a mail to Sean ' 'to run these jobs on backup server.\n\n' 'If the jobs are successful on backup server, run them on test ' 'and prod server.' % list(set(author_mail_ids))) else: python_utils.PRINT('No jobs to run for the release.') common.open_new_tab_in_browser_if_possible( release_constants.REPEATABLE_JOBS_SPREADSHEETS_URL) common.open_new_tab_in_browser_if_possible( release_constants.ONE_TIME_JOBS_SPREADSHEET_URL) common.ask_user_to_confirm( 'Please check manually if there are no jobs to run.') finally: if os.path.isfile(RELEASE_CREDENTIALS_FILEPATH): os.remove(RELEASE_CREDENTIALS_FILEPATH) if os.path.isfile(AUTH_FILEPATH): os.remove(AUTH_FILEPATH)
def execute_branch_cut(): """Pushes the new release branch to Github.""" parsed_args = _PARSER.parse_args() if parsed_args.new_version: target_version = parsed_args.new_version else: raise Exception('ERROR: A "new_version" arg must be specified.') # Construct the new branch name. hotfix_number = int(parsed_args.hotfix_number) if not hotfix_number: new_branch_type, new_branch_name = _get_release_branch_type_and_name( target_version) else: new_branch_type, new_branch_name = _get_hotfix_branch_type_and_name( target_version, hotfix_number) # Do prerequisite checks. common.require_cwd_to_be_oppia() common.verify_local_repo_is_clean() common.verify_current_branch_name('develop') # Update the local repo. remote_alias = common.get_remote_alias(release_constants.REMOTE_URL) subprocess.check_call(['git', 'pull', remote_alias, 'develop']) verify_target_branch_does_not_already_exist(remote_alias, new_branch_name) # The release coordinator should verify that tests are passing on develop # before checking out the release branch. common.open_new_tab_in_browser_if_possible( 'https://github.com/oppia/oppia#oppia---') while True: if not hotfix_number: branch_to_check = 'develop' elif hotfix_number == 1: branch_to_check = 'release-%s' % target_version else: branch_to_check = 'release-%s-hotfix-%s' % ( target_version, hotfix_number - 1) python_utils.PRINT( 'Please confirm: are Travis checks passing on %s? (y/n) ' % ( branch_to_check)) answer = python_utils.INPUT().lower() if answer in release_constants.AFFIRMATIVE_CONFIRMATIONS: break elif answer: python_utils.PRINT( 'Tests should pass on %s before this script is run. ' 'Exiting.' % branch_to_check) sys.exit() # Cut a new release or hotfix branch. if new_branch_type == release_constants.BRANCH_TYPE_HOTFIX: verify_hotfix_number_is_one_ahead_of_previous_hotfix_number( remote_alias, target_version, hotfix_number) if hotfix_number == 1: branch_to_cut_from = 'release-%s' % target_version else: branch_to_cut_from = 'release-%s-hotfix-%s' % ( target_version, hotfix_number - 1) python_utils.PRINT('Cutting a new hotfix branch: %s' % new_branch_name) subprocess.check_call([ 'git', 'checkout', '-b', new_branch_name, branch_to_cut_from]) else: verify_target_version_compatible_with_latest_release( target_version) python_utils.PRINT('Cutting a new release branch: %s' % new_branch_name) subprocess.check_call(['git', 'checkout', '-b', new_branch_name]) # Push the new release branch to GitHub. python_utils.PRINT('Pushing new %s branch to GitHub.' % new_branch_type) subprocess.check_call(['git', 'push', remote_alias, new_branch_name]) python_utils.PRINT('') python_utils.PRINT( 'New %s branch successfully cut. You are now on branch %s' % ( new_branch_type, new_branch_name)) python_utils.PRINT('Done!')