예제 #1
0
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.')
예제 #2
0
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()
예제 #3
0
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))
예제 #5
0
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!')
예제 #6
0
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)
예제 #7
0
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!')