Beispiel #1
0
def manage_repos(conf, backend, args, action):
    """Performs an action (lambda) on all student repos
    """
    hw_name = args.name
    dry_run = args.dry_run

    namespace = conf.namespace
    semester = conf.semester
    backend_conf = conf.backend

    roster = get_filtered_roster(conf.roster, args.section, args.student)

    count = 0
    for student in progress.iterate(roster):
        username = student["username"]
        student_section = student["section"]
        if "id" not in student:
            logging.warning("Student %s does not have a gitlab account.",
                            username)
            continue

        full_name = backend.student_repo.build_name(semester, student_section,
                                                    hw_name, username)

        repo = backend.student_repo(backend_conf, namespace, full_name)
        if not dry_run:
            if action(repo, student):
                count += 1
        else:
            count += 1

    print("Changed {} repositories.".format(count))
Beispiel #2
0
def open_all_assignments(conf, backend, args):
    """Adds each student in the roster to their respective homework
    repositories as Developers so they can pull/commit/push their work.
    """
    hw_name = args.name
    namespace = conf.namespace
    semester = conf.semester
    backend_conf = conf.backend

    roster = get_filtered_roster(conf.roster, args.section, args.student)

    count = 0
    for student in progress.iterate(roster):
        username = student["username"]
        student_section = student["section"]
        full_name = backend.student_repo.build_name(semester, student_section,
                                                    hw_name, username)

        try:
            repo = backend.student_repo(backend_conf, namespace, full_name)
            if "id" not in student:
                student["id"] = backend.repo.get_user_id(
                    username, backend_conf)

            open_assignment(repo, student, backend.access.developer)
            count += 1
        except UserInAssignerGroup:
            logging.info(
                "%s already has access via group membership, skipping...",
                username)
        except RepoError:
            logging.warning("Could not add %s to %s.", username, full_name)

    print("Granted access to {} repositories.".format(count))
Beispiel #3
0
def score_assignments(conf: Config, backend: BackendBase,
                      args: argparse.Namespace) -> None:
    """Goes through each student repository and grabs the most recent CI
    artifact, which contains their autograded score
    """
    student = args.student

    roster = get_filtered_roster(conf.roster, args.section, student)

    scores = []
    for student in progress.iterate(roster):
        score = handle_scoring(conf, backend, args, student)
        if score is not None:
            scores.append(score)

    print("Scored {} repositories.".format(len(scores)))
    print_statistics(scores)
Beispiel #4
0
def _push(conf, backend, args):
    hw_name = args.name
    hw_path = args.path
    namespace = conf.namespace
    semester = conf.semester
    backend_conf = conf.backend
    branch = args.branch
    force = args.force
    push_unlocked = args.push_unlocked

    path = os.path.join(hw_path, hw_name)

    roster = get_filtered_roster(conf.roster, args.section, args.student)

    for student in progress.iterate(roster):
        username = student["username"]
        student_section = student["section"]
        full_name = backend.student_repo.build_name(semester, student_section,
                                                    hw_name, username)

        try:
            repo = backend.student_repo(backend_conf, namespace, full_name)
            repo_dir = os.path.join(path, username)
            repo.add_local_copy(repo_dir)

            if repo.is_locked() or push_unlocked:
                info = repo.repo.remote().push(branch,
                                               force=force,
                                               set_upstream=True)
                for line in info:
                    logging.debug("%s: flags: %s, branch: %s, summary: %s",
                                  full_name, line.flags, line.local_ref,
                                  line.summary)
                    if line.flags & line.ERROR:
                        logging.warning("%s: push to %s failed: %s", full_name,
                                        line.local_ref, line.summary)
            else:
                logging.warning(
                    "%s: repo is not locked (run 'assigner lock %s' first)",
                    full_name, hw_name)

        except NoSuchPathError:
            logging.warning("Local repo for %s does not exist; skipping...",
                            username)
        except RepoError as e:
            logging.warning(e)
Beispiel #5
0
def integrity_check(conf: Config, backend: BackendBase,
                    args: argparse.Namespace) -> None:
    """Checks that none of the grading files were modified in the timeframe
    during which students could push to their repository
    """
    student = args.student
    files_to_check = set(args.files)
    roster = get_filtered_roster(conf.roster, args.section, None)

    for student in progress.iterate(roster):
        username = student["username"]
        student_section = student["section"]
        full_name = backend.student_repo.build_name(conf.semester,
                                                    student_section, args.name,
                                                    username)

        try:
            repo = backend.student_repo(conf.backend, conf.namespace,
                                        full_name)
            check_repo_integrity(repo, files_to_check)
        except RepoError as e:
            logger.debug(e)
            logger.warning("Unable to find repo for %s with URL %s", username,
                           full_name)
Beispiel #6
0
def _push(conf, backend, args):
    backend_conf = conf.backend
    namespace = conf.namespace
    semester = conf.semester

    hw_name = args.name
    hw_path = args.path
    message = args.message
    branch = args.branch
    add = args.add
    remove = args.remove
    update = args.update
    allow_empty = args.allow_empty

    # Default behavior: commit changes to all tracked files
    if (add == []) and (remove == []):
        logging.debug("Nothing explicitly added or removed; defaulting to git add --update")
        update = True

    path = os.path.join(hw_path, hw_name)

    roster = get_filtered_roster(conf.roster, args.section, args.student)

    for student in progress.iterate(roster):
        username = student["username"]
        student_section = student["section"]
        full_name = backend.student_repo.build_name(semester, student_section,
                                                    hw_name, username)

        has_changes = False

        try:
            repo = backend.student_repo(backend_conf, namespace, full_name)
            repo_dir = os.path.join(path, username)
            repo.add_local_copy(repo_dir)

            logging.debug("%s: checking out branch %s", full_name, branch)
            repo.get_head(branch).checkout()
            index = repo.get_index()

            if update:
                # Stage modified and deleted files for commit
                # This exactly mimics the behavior of git add --update
                # (or the effect of doing git commit -a)
                for change in index.diff(None):
                    has_changes = True
                    if change.deleted_file:
                        logging.debug("%s: git rm %s", full_name, change.b_path)
                        index.remove([change.b_path])
                    else:
                        logging.debug("%s: git add %s", full_name, change.b_path)
                        index.add([change.b_path])

            if add:
                has_changes = True
                logging.debug("%s: adding %s", full_name, add)
                index.add(add)
            if remove:
                has_changes = True
                logging.debug("%s: removing %s", full_name, remove)
                index.remove(remove)

            if has_changes or allow_empty:
                logging.debug("%s: committing changes with message %s", full_name, message)
                index.commit(message)
            else:
                logging.warning("%s: No changes in repo; skipping commit.", full_name)

        except NoSuchPathError:
            logging.warning("Local repo for %s does not exist; skipping...", username)
        except RepoError as e:
            logging.warning(e)
        except HTTPError as e:
            if e.response.status_code == 404:
                logging.warning("Repository %s does not exist.", full_name)
            else:
                raise
Beispiel #7
0
def _push(conf, backend, args):
    backend_conf = conf.backend
    namespace = conf.namespace
    semester = conf.semester

    hw_name = args.name
    hw_path = args.path
    message = args.message
    branch = args.branch
    add = args.add
    remove = args.remove
    update = args.update
    allow_empty = args.allow_empty
    gpg_sign = args.gpg_sign

    # Default behavior: commit changes to all tracked files
    if (add == []) and (remove == []):
        logging.debug(
            "Nothing explicitly added or removed; defaulting to git add --update"
        )
        update = True

    path = os.path.join(hw_path, hw_name)

    roster = get_filtered_roster(conf.roster, args.section, args.student)

    for student in progress.iterate(roster):
        username = student["username"]
        student_section = student["section"]
        full_name = backend.student_repo.build_name(semester, student_section,
                                                    hw_name, username)

        has_changes = False

        try:
            repo = backend.student_repo(backend_conf, namespace, full_name)
            repo_dir = os.path.join(path, username)
            repo.add_local_copy(repo_dir)

            logging.debug("%s: checking out branch %s", full_name, branch)
            repo.get_head(branch).checkout()
            index = repo.get_index()

            if update:
                # Stage modified and deleted files for commit
                # This exactly mimics the behavior of git add --update
                # (or the effect of doing git commit -a)
                for change in index.diff(None):
                    has_changes = True
                    if change.deleted_file:
                        logging.debug("%s: git rm %s", full_name,
                                      change.b_path)
                        index.remove([change.b_path])
                    else:
                        logging.debug("%s: git add %s", full_name,
                                      change.b_path)
                        index.add([change.b_path])

            if add:
                has_changes = True
                logging.debug("%s: adding %s", full_name, add)
                index.add(add)
            if remove:
                has_changes = True
                logging.debug("%s: removing %s", full_name, remove)
                index.remove(remove)

            if has_changes or allow_empty:
                logging.debug("%s: committing changes with message %s",
                              full_name, message)
                if gpg_sign:
                    # The GitPython interface does not support signed commits, and
                    # launching via repo.git.commit will launch an inaccessible
                    # interactive prompt in the background
                    index.write(ignore_extension_data=True)
                    subprocess.check_call(
                        ["git", "commit", "-S", "-m", '"{}"'.format(message)],
                        stdout=subprocess.DEVNULL,
                        stderr=subprocess.DEVNULL,
                        cwd=repo_dir)
                else:
                    index.commit(message)
            else:
                logging.warning("%s: No changes in repo; skipping commit.",
                                full_name)

        except NoSuchPathError:
            logging.warning("Local repo for %s does not exist; skipping...",
                            username)
        except RepoError as e:
            logging.warning(e)