227 lines
7.2 KiB
Python
227 lines
7.2 KiB
Python
# This file is part of CAT-SOOP
|
|
# Copyright (c) 2011-2025 by The CAT-SOOP Developers <catsoop-dev@mit.edu>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
#
|
|
# Portions of this file were forked from the setup.py file for xonsh
|
|
# (https://xon.sh/), which is available under the MIT/Expat license.
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
|
|
from setuptools import setup
|
|
|
|
from catsoop import __version__ as CS_VERSION, __codename__ as CODENAME
|
|
|
|
logo = (
|
|
"\\ "
|
|
"\n/ /\\__/\\ "
|
|
"\n\\__=( o_O )="
|
|
"\n(__________) "
|
|
"\n |_ |_ |_ |_ "
|
|
"\n "
|
|
"\n CAT-SOOP "
|
|
)
|
|
|
|
|
|
VERSION_FNAME = os.path.join(os.path.dirname(__file__), "catsoop", "__init__.py")
|
|
ORIGINAL_VERSION = None
|
|
|
|
|
|
def get_version_and_distance(hash_command, current_sha, tags):
|
|
ordered_hashes = {
|
|
hash_: ix
|
|
for ix, hash_ in enumerate(
|
|
reversed(subprocess.check_output(hash_command).decode("ascii").splitlines())
|
|
)
|
|
}
|
|
current_rev = ordered_hashes[current_sha]
|
|
most_recent_version = "v0.0.0"
|
|
N = 99999999
|
|
for t, h in tags.items():
|
|
if t != "tip" and h in ordered_hashes:
|
|
distance = current_rev - ordered_hashes[h]
|
|
if 0 <= distance < N:
|
|
N = distance
|
|
most_recent_version = t
|
|
if N == 0:
|
|
break
|
|
return most_recent_version, N
|
|
|
|
|
|
def dev_number():
|
|
try:
|
|
branch = (
|
|
subprocess.check_output(["git", "branch", "--show-current"])
|
|
.decode("ascii")
|
|
.strip()
|
|
)
|
|
assert branch
|
|
except:
|
|
print("failed to find git branch", file=sys.stderr)
|
|
return
|
|
try:
|
|
raw_tags = subprocess.check_output(["git", "tag"]).decode("ascii").splitlines()
|
|
tags = {}
|
|
except Exception:
|
|
print("failed to read git tags", file=sys.stderr)
|
|
return
|
|
for t in raw_tags:
|
|
t = t.strip()
|
|
tags[t] = (
|
|
subprocess.check_output(["git", "rev-parse", "%s^{}" % t])
|
|
.decode("ascii")
|
|
.strip()
|
|
)
|
|
try:
|
|
sha = (
|
|
subprocess.check_output(["git", "rev-parse", "HEAD"])
|
|
.decode("ascii")
|
|
.strip()
|
|
)
|
|
except:
|
|
return None
|
|
_cmd = ["git", "log", branch, "--format=%H"]
|
|
most_recent_version, N = get_version_and_distance(_cmd, sha, tags)
|
|
try:
|
|
_cmd = ["git", "show", "-s", "--format=%cD", sha]
|
|
_date = subprocess.check_output(_cmd)
|
|
_date = _date.decode("ascii")
|
|
_date = "".join(_date.split(" ", 1)[1:]).strip()
|
|
except:
|
|
_date = ""
|
|
print("failed to get git commit date", file=sys.stderr)
|
|
return
|
|
try:
|
|
dirty = len(
|
|
subprocess.check_output(["git", "status", "--porcelain"])
|
|
.decode("ascii")
|
|
.strip()
|
|
.splitlines()
|
|
)
|
|
except:
|
|
return
|
|
return {
|
|
"vcs": "Git",
|
|
"shortvcs": "git",
|
|
"branch": None if branch == "main" else branch,
|
|
"version": most_recent_version,
|
|
"hash": sha,
|
|
"distance": N,
|
|
"date": _date,
|
|
"changes": dirty,
|
|
}
|
|
|
|
|
|
def dirty_version():
|
|
"""
|
|
If install/sdist is run from a git directory, add a devN suffix to reported
|
|
version number and write an ignored file that holds info about the current
|
|
state of the repo.
|
|
"""
|
|
global CS_VERSION, ORIGINAL_VERSION
|
|
|
|
dev_num = dev_number()
|
|
if dev_num:
|
|
CS_VERSION = dev_num["version"]
|
|
if dev_num["distance"] != 0:
|
|
CS_VERSION = f"%s+%s.%s.%s%s" % (
|
|
CS_VERSION,
|
|
dev_num["shortvcs"],
|
|
dev_num["distance"],
|
|
dev_num["hash"][:8],
|
|
(".l%s" % dev_num["changes"]) if dev_num["changes"] else "",
|
|
)
|
|
with open(
|
|
os.path.join(os.path.dirname(__file__), "catsoop", "dev.hash"), "w"
|
|
) as f:
|
|
f.write(
|
|
"{}|{}|{}|{}".format(
|
|
dev_num["vcs"],
|
|
dev_num["hash"],
|
|
dev_num["date"],
|
|
dev_num["changes"],
|
|
)
|
|
)
|
|
with open(VERSION_FNAME, "r") as f:
|
|
ORIGINAL_VERSION = f.read()
|
|
with open(VERSION_FNAME, "w") as f:
|
|
f.write("__version__ = %r\n" % CS_VERSION.lstrip("v"))
|
|
f.write("__codename__= %r\n" % CODENAME)
|
|
|
|
|
|
def main():
|
|
if sys.version_info[:2] < (3, 11):
|
|
sys.exit("catsoop currently requires Python 3.11+")
|
|
|
|
if "--name" not in sys.argv:
|
|
print(logo)
|
|
|
|
with open(os.path.join(os.path.dirname(__file__), "requirements.txt"), "r") as f:
|
|
requirements = f.read().split("\n")
|
|
|
|
with open(os.path.join(os.path.dirname(__file__), "README.md"), "r") as f:
|
|
readme = f.read()
|
|
|
|
try:
|
|
dirty_version()
|
|
setup(
|
|
name="catsoop",
|
|
version=CS_VERSION.lstrip("v"),
|
|
author="CAT-SOOP Contributors",
|
|
author_email="catsoop-dev@mit.edu",
|
|
maintainer="adam j hartz",
|
|
maintainer_email="hz@mit.edu",
|
|
packages=[
|
|
"catsoop",
|
|
"catsoop.test",
|
|
"catsoop.thirdparty",
|
|
"catsoop.thirdparty.oauth2",
|
|
"catsoop.scripts",
|
|
],
|
|
scripts=[],
|
|
url="https://catsoop.org",
|
|
license="AGPLv3+",
|
|
description="CAT-SOOP is a tool for automatic collection and assessment of online exercises.",
|
|
long_description=readme,
|
|
long_description_content_type="text/markdown",
|
|
include_package_data=True,
|
|
entry_points={
|
|
"console_scripts": ["catsoop = catsoop.__main__:command_line_interface"]
|
|
},
|
|
install_requires=requirements,
|
|
extras_require={"server": ["uwsgi"]},
|
|
package_dir={"catsoop": "catsoop"},
|
|
package_data={"catsoop": ["scripts/*"]},
|
|
classifiers=[
|
|
"Development Status :: 4 - Beta",
|
|
"Intended Audience :: Education",
|
|
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Python :: 3.14",
|
|
"Topic :: Education",
|
|
],
|
|
)
|
|
finally:
|
|
if ORIGINAL_VERSION is not None:
|
|
with open(VERSION_FNAME, "w") as f:
|
|
f.write(ORIGINAL_VERSION)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|