예제 #1
0
    def wrapper(self):
        if not which("valgrind"):
            raise Error("valgrind not installed", result=Checks.SKIP)

        self._valgrind = True
        try:
            func(self)
            self._check_valgrind()
        finally:
            self._valgrind = False
예제 #2
0
def run():
    global args
    global exes
    global prefix
    r = 1

    optional_prefix = False
    for arg in args:
        if arg.startswith('---p='):
            arg = arg[len('---p='):]
            prefix += arg.split(',')
            break
        if arg.startswith('---op='):
            arg = arg[len('---op='):]
            prefix += arg.split(',')
            break

    if prefix and not optional_prefix and not which(prefix[0]):
        print >> sys.stderr, "prefix not found"
        return 1

    if "---l" in args:
        print os.path.join(cwd,exes[0])
        return 0
    elif "---d" in args:
        print os.path.dirname(cwd)
        return 0
    else:
        args = [a for a in args if not a.startswith("---")]
        try:
            with open(os.path.join(cwd, os.path.dirname(exes[0]), "sg.json"), 'r') as f:
                j = json.load(f)
                args += j['args']
        except:
            pass
        
        if os.name=="nt":
            r = subprocess.call(prefix + [os.path.join(cwd,exes[0])] + args, cwd=(cwd))
        else:
            r = subprocess.call(prefix + ['./'+exes[0]] + args, cwd=(cwd))
    return r
예제 #3
0
def submit(org, branch):
    """Submit work."""

    # check announcements
    res = requests.get("https://cs50.me/status/submit50")
    if res.status_code == 200 and res.text.strip():
        raise Error(res.text.strip())

    # require git 2.7+, so that credential-cache--daemon ignores SIGHUP
    # https://github.com/git/git/blob/v2.7.0/credential-cache--daemon.c
    if not which("git"):
        raise Error(
            _("You don't have git. Install git, then re-run {}!".format(org)))
    version = subprocess.check_output(["git", "--version"]).decode("utf-8")
    matches = re.search(r"^git version (\d+\.\d+\.\d+).*$", version)
    if not matches or StrictVersion(matches.group(1)) < StrictVersion("2.7.0"):
        raise Error(
            _("You have an old version of git. Install version 2.7 or later, then re-run {}!"
              .format(org)))

    # update progress
    progress("Connecting")

    # compute timestamp
    global timestamp
    headers = requests.get("https://api.github.com/").headers
    timestamp = datetime.datetime.strptime(headers["Date"],
                                           "%a, %d %b %Y %H:%M:%S %Z")
    timestamp = timestamp.strftime("%Y%m%dT%H%M%SZ")

    # check version
    res = requests.get("https://cs50.me/versions/submit50")
    if res.status_code != 200:
        raise Error(
            _("You have an unknown version of submit50. "
              "Email [email protected]!"))
    version_required = res.text.strip()
    if parse_version(version_required) > parse_version(
            get_distribution("submit50").version):
        raise Error(
            _("You have an old version of submit50. "
              "Run update50, then re-run {}!".format(org)))

    # separate branch into slug and repo
    check_repo = "@cs50/checks"
    branch = branch if not branch.endswith(
        check_repo) else branch[:-len(check_repo)]
    try:
        slug, src = branch.split("@")
    except ValueError:
        slug, src = branch, "cs50/checks"

    # ensure slug exists
    file, submit.EXCLUDE = tempfile.mkstemp()
    url = "https://github.com/{}/raw/master/{}/submit50/exclude".format(
        src, slug)
    try:
        urllib.request.urlretrieve(url, filename=submit.EXCLUDE)
        lines = open(submit.EXCLUDE)
    except Exception as e:
        if run.verbose:
            cprint(str(e))
        e = Error(_("Invalid slug. Did you mean to submit something else?"))
        e.__cause__ = None
        raise e
    if slug.startswith("/") and slug.endswith("/"):
        raise Error(
            _("Invalid slug. Did you mean {}, without the leading and trailing slashes?"
              .format(slug.strip("/"))))
    elif slug.startswith("/"):
        raise Error(
            _("Invalid slug. Did you mean {}, without the leading slash?".
              format(slug.strip("/"))))
    elif slug.endswith("/"):
        raise Error(
            _("Invalid slug. Did you mean {}, without the trailing slash?".
              format(slug.strip("/"))))

    # check for missing files
    missing = []
    for line in lines:
        matches = re.match(r"^\s*#\s*([^\s]+)\s*$", line)
        if matches:
            pattern = matches.group(1)
            if pattern[:-1] == "/":
                if not os.path.isdir(pattern):
                    missing.append(pattern)
            elif not os.path.isfile(pattern):
                missing.append(pattern)
    if missing:
        cprint(_("You seem to be missing these files:"))
        for pattern in missing:
            cprint(" {}".format(pattern))
        raise Error(_("Ensure you have the required files before submitting."))

    # update progress
    progress(_("Authenticating"))

    # authenticate user via SSH
    try:

        # require ssh
        assert which("ssh")

        # require GitHub username in ~/.gitconfig
        username, password = run(
            "git config --global credential.https://github.com/submit50.username",
            quiet=True), None
        email = "{}@users.noreply.github.com".format(username)
        repo = "[email protected]:{}/{}.git".format(org, username)
        progress(False)

        # require ssh-agent
        child = pexpect.spawn("ssh [email protected]")
        i = child.expect([
            "Enter passphrase for key",
            "Are you sure you want to continue connecting", pexpect.EOF
        ])
        child.close()
        assert i == 2

    # authenticate user via HTTPS
    except BaseException:
        username, password, email = authenticate(org)
        repo = "https://{}@github.com/{}/{}".format(username, org, username)

    # update progress
    progress(_("Preparing"))

    # clone repository
    try:
        run("git clone --bare {} {}".format(shlex.quote(repo),
                                            shlex.quote(run.GIT_DIR)),
            password=password)
    except BaseException:
        if password:
            e = Error(
                _("Looks like {} isn't enabled for your account yet. "
                  "Go to https://cs50.me/authorize and make sure you accept any pending invitations!"
                  .format(org, org)))
        else:
            e = Error(
                _("Looks like you have the wrong username in ~/.gitconfig or {} isn't yet enabled for your account. "
                  "Double-check ~/.gitconfig and then log into https://cs50.me/ in a browser, "
                  "click \"Authorize application\" if prompted, and re-run {} here."
                  .format(org, org)))
        e.__cause__ = None
        raise e

    # check out .gitattributes, if any, temporarily shadowing student's, if any
    if os.path.isfile(".gitattributes"):
        submit.ATTRIBUTES = ".gitattributes.{}".format(round(time.time()))
        os.rename(".gitattributes", submit.ATTRIBUTES)
    try:
        run("git checkout --force {} .gitattributes".format(branch))
    except Exception:
        pass

    # set options
    tag = "{}@{}".format(branch, timestamp)
    run("git config user.email {}".format(shlex.quote(email)))
    run("git config user.name {}".format(shlex.quote(username)))
    run("git symbolic-ref HEAD refs/heads/{}".format(shlex.quote(branch)))

    # patterns of file names to exclude
    run("git config core.excludesFile {}".format(shlex.quote(submit.EXCLUDE)))

    # blocklist for git-lfs
    # https://github.com/git-lfs/git-lfs/blob/master/commands/command_track.go
    with open("{}/info/exclude".format(run.GIT_DIR), "w") as file:
        file.write(".git*\n")
        file.write(".lfs*\n")

    # adds, modifies, and removes index entries to match the working tree
    run("git add --all")

    # get file lists
    files = run("git ls-files").splitlines()
    others = run("git ls-files --exclude-from={}/info/exclude --other".format(
        run.GIT_DIR)).splitlines()

    # unescape any octal codes in lists
    # https://stackoverflow.com/a/46650050/5156190
    def unescape(s):
        if s.startswith('"') and s.endswith('"'):
            return (s.replace('"', '').encode("latin1").decode(
                "unicode-escape").encode("latin1").decode("utf8"))
        return s

    files = [unescape(file) for file in files]
    others = [unescape(other) for other in others]

    # hide .gitattributes, if any, from output
    if ".gitattributes" in files:
        files.remove(".gitattributes")

    # check for large files > 100 MB (and huge files > 2 GB)
    # https://help.github.com/articles/conditions-for-large-files/
    # https://help.github.com/articles/about-git-large-file-storage/
    larges, huges = [], []
    for file in files:
        size = os.path.getsize(file)
        if size > (100 * 1024 * 1024):
            larges.append(file)
        elif size > (2 * 1024 * 1024 * 1024):
            huges.append(file)
    if len(huges) > 0:
        raise Error(
            _("These files are too large to be submitted:\n{}\n"
              "Remove these files from your directory "
              "and then re-run {}!").format("\n".join(huges), org))
    elif len(larges) > 0:
        if not which("git-lfs"):
            raise Error(
                _("These files are too large to be submitted:\n{}\n"
                  "Install git-lfs (or remove these files from your directory) "
                  "and then re-run {}!").format("\n".join(larges), org))
        run("git lfs install --local")
        run("git config credential.helper cache")  # for pre-push hook
        for large in larges:
            run("git rm --cached {}".format(large))
            run("git lfs track {}".format(large))
            run("git add {}".format(large))
        run("git add --force .gitattributes")

    # files that will be submitted
    if len(files) == 0:
        raise Error(
            _("No files in this directory are expected for submission."))

    # prompts for submit50
    if org == "submit50":
        if files:
            cprint(_("Files that will be submitted:"), "green")
            for file in files:
                cprint("./{}".format(file), "green")

        # files that won't be submitted
        if others:
            cprint(_("Files that won't be submitted:"), "yellow")
            for other in others:
                cprint("./{}".format(other), "yellow")

        # prompt for honesty
        readline.clear_history()
        try:
            answer = input(
                _("Keeping in mind the course's policy on academic honesty, "
                  "are you sure you want to submit these files (yes/no)? "))
        except EOFError:
            answer = None
            print()
        if not answer or not re.match("^\s*(?:y|yes)\s*$", answer, re.I):
            raise Error(_("No files were submitted."))

    # update progress
    if org == "submit50":
        progress(_("Submitting"))
    else:
        progress(_("Uploading"))

    # push branch
    run("git commit --allow-empty --message='{}'".format(timestamp))
    commit_hash = run("git rev-parse HEAD")
    run("git push origin 'refs/heads/{}'".format(branch), password=password)

    # successful submission
    if org == "submit50":
        cprint(
            _("Submitted {}! See https://cs50.me/submissions.").format(branch),
            "green")
    progress(False)
    return username, commit_hash
예제 #4
0
    from backports.shutil_which import which
else:
    import subprocess
    from shutil import which

try:
    import threading
except ImportError:
    import dummy_threading as threading

try:
    import psutil
    TIMEOUT = 1
except ImportError:
    psutil = None
    PS_PATH = which('ps')
    PGREP_PATH = which('pgrep')
    TIMEOUT = None if PS_PATH is None else 1

__all__ = ["openpty", "fork", "spawn"]

STDIN_FILENO = 0
STDOUT_FILENO = 1
STDERR_FILENO = 2

CHILD = 0


def openpty():
    """openpty() -> (master_fd, slave_fd)
    Open a pty master/slave pair, using os.openpty() if possible."""
예제 #5
0
from os.path import join, dirname
from subprocess import check_call
import json
import re
import shutil
import tempfile

try:
    which = shutil.which
except:
    from backports.shutil_which import which

NPM = which("npm")
HERE = dirname(__file__)
TESTS = join(HERE, "src", "tests")

# TODO: hoist these to the recipe
PKG = {
    "devDependencies": {
        "jsdoc": "3.6.3",
        "typedoc": "0.15.0"
    },
    "scripts": {
        "test": "python -m pytest -vv"
    }
}


def test_in_tmp(tmp):
    print(
        "- creating package.json to ensure js/tsdoc are installed/on path...")
예제 #6
0
def submit(org, problem):
    """Submit problem."""

    # require git 2.7+, so that credential-cache--daemon ignores SIGHUP
    # https://github.com/git/git/blob/v2.7.0/credential-cache--daemon.c
    if not which("git"):
        raise Error("You don't have git. Install git, then re-run submit50!.")
    version = subprocess.check_output(["git", "--version"]).decode("utf-8")
    matches = re.search(r"^git version (\d+\.\d+\.\d+).*$", version)
    if not matches or StrictVersion(matches.group(1)) < StrictVersion("2.7.0"):
        raise Error("You have an old version of git. Install version 2.7 or later, then re-run submit50!")

    # update spinner
    spin("Connecting")

    # compute timestamp
    global timestamp
    headers = requests.get("https://api.github.com/").headers
    timestamp = datetime.datetime.strptime(headers["Date"], "%a, %d %b %Y %H:%M:%S %Z")
    timestamp = timestamp.strftime("%Y%m%dT%H%M%SZ")

    # check version
    res = requests.get("https://cs50.me/versions/submit50")
    if res.status_code != 200:
        raise Error("You have an unknown version of submit50. " +
                    "Email [email protected]!")
    version_required = res.text.strip()
    if parse_version(version_required) > parse_version(get_distribution("submit50").version):
        raise Error("You have an old version of submit50. " +
                    "Run update50, then re-run submit50!")

    # assume cs50/ problem if problem name begins with a year
    branch = problem
    if problem.split("/")[0].isdigit():
        branch = os.path.join("cs50", problem)

    # ensure problem exists
    _, submit.EXCLUDE = tempfile.mkstemp()
    url = "https://cs50.me/excludes/{}/".format(branch)
    try:
        urllib.request.urlretrieve(url, filename=submit.EXCLUDE)
        lines = open(submit.EXCLUDE)
    except Exception as e:
        if run.verbose:
            cprint(str(e))
        e = Error("Invalid problem. Did you mean to submit something else?")
        e.__cause__ = None
        raise e

    # check for missing files
    missing = []
    for line in lines:
        matches = re.match(r"^\s*#\s*([^\s]+)\s*$", line)
        if matches:
            pattern = matches.group(1)
            if pattern[:-1] == "/":
                if not os.path.isdir(pattern):
                    missing.append(pattern)
            elif not os.path.isfile(pattern):
                missing.append(pattern)
    if missing:
        cprint("You seem to be missing these files:")
        for pattern in missing:
            cprint(" {}".format(pattern))
        raise Error("Ensure you have the required files before submitting.")

    # update spinner
    spin("Authenticating")

    # authenticate user via SSH
    try:
        assert which("ssh")
        username, password = run("git config --global credential.https://github.com/submit50.username", quiet=True), None
        email = "{}@users.noreply.github.com".format(username)
        repo = "[email protected]:{}/{}.git".format(org, username)
        with open(os.devnull, "w") as DEVNULL:
            spin(False)
            assert subprocess.call(["ssh", "*****@*****.**"], stderr=DEVNULL) == 1 # successfully authenticated

    # authenticate user via HTTPS
    except:
        username, password, email = authenticate(org)
        repo = "https://{}@github.com/{}/{}".format(username, org, username)

    # update spinner
    spin("Preparing")

    # clone repository
    try:
        run("git clone --bare {} {}".format(shlex.quote(repo), shlex.quote(run.GIT_DIR)), password=password)
    except:
        if password:
            e = Error("Looks like submit50 isn't enabled for your account yet. " +
                      "Log into https://cs50.me/ in a browser, click \"Authorize application\", and re-run submit50 here!")
        else:
            e = Error("Looks like you have the wrong username in ~/.gitconfig or submit50 isn't yet enabled for your account. " +
                      "Double-check ~/.gitconfig and then log into https://cs50.me/ in a browser, " +
                      "click \"Authorize application\" if prompted, and re-run submit50 here.")
        e.__cause__ = None
        raise e

    # set options
    tag = "{}@{}".format(branch, timestamp)
    run("git config user.email {}".format(shlex.quote(email)))
    run("git config user.name {}".format(shlex.quote(username)))
    run("git symbolic-ref HEAD refs/heads/{}".format(shlex.quote(branch)))

    # patterns of file names to exclude
    run("git config core.excludesFile {}".format(shlex.quote(submit.EXCLUDE)))

    # adds, modifies, and removes index entries to match the working tree
    run("git add --all")

    # get file lists
    files = run("git ls-files").split()
    other = run("git ls-files --other").split()

    # files that will be submitted
    if len(files) == 0:
        raise Error("No files in this directory are expected for submission.")
    cprint("Files that will be submitted:", "green")
    for f in files:
        cprint("./{}".format(f), "green")

    # files that won't be submitted
    if len(other) != 0:
        cprint("Files that won't be submitted:", "yellow")
        for f in other:
            cprint("./{}".format(f), "yellow")

    # prompt for academic honesty
    cprint("Keeping in mind the course's policy on academic honesty, " +
           "are you sure you want to submit these files?", end=" ")
    if not re.match("^\s*(?:y|yes)\s*$", input(), re.I):
        raise Error("No files were submitted.")

    # restart spinner
    spin("Submitting")

    # push branch
    run("git commit --allow-empty --message='{}'".format(timestamp))
    run("git push origin 'refs/heads/{}'".format(branch), password=password)

    # successful submission
    cprint("Submitted {}! ".format(problem) +
           "See https://cs50.me/submissions/{}.".format(branch),
           "green")