Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Change Log
This project adheres to Semantic Versioning (https://semver.org/).


2023-08-09
~~~~~~~~~~

* Removed validation of people.yaml files. The OSPR bot no longer reads
people.yaml, so that file will be deleted.

2023-07-25
~~~~~~~~~~

Expand Down
7 changes: 4 additions & 3 deletions pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
# SERIOUSLY.
#
# ------------------------------
# Generated by edx-lint version: 5.2.5
# Generated by edx-lint version: 5.3.4
# ------------------------------
[MASTER]
ignore = migrations
Expand Down Expand Up @@ -259,6 +259,7 @@ enable =
useless-suppression,
disable =
bad-indentation,
broad-exception-raised,
consider-using-f-string,
duplicate-code,
file-ignored,
Expand Down Expand Up @@ -380,6 +381,6 @@ ext-import-graph =
int-import-graph =

[EXCEPTIONS]
overgeneral-exceptions = Exception
overgeneral-exceptions = builtins.Exception

# afac0308552b9baa632c39eb574d26e9725303f1
# 69626f4a89ab0a7387bf870b30a5bbcd72e11ce1
2 changes: 1 addition & 1 deletion repo_tools_data_schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

__version__ = '1.1'

from .repo_tools_data_schema import validate_orgs, validate_people, validate_salesforce_export
from .repo_tools_data_schema import validate_orgs, validate_salesforce_export
173 changes: 5 additions & 168 deletions repo_tools_data_schema/repo_tools_data_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,13 @@

import collections
import csv
import datetime
import difflib
import functools
import os
import pathlib
import re

import backoff
import requests
import yaml
from schema import Optional, Or, Schema
from yaml.constructor import ConstructorError

from schema import And, Optional, Or, Schema, SchemaError


def valid_agreement(s):
"""Is this a valid "agreement" value?"""
return s in ['institution', 'individual', 'none']


def valid_email(s):
"""Is this a valid email?"""
Expand All @@ -33,64 +21,11 @@ def valid_email(s):
)


@functools.lru_cache(maxsize=None)
@backoff.on_exception(backoff.expo, SchemaError, max_time=60)
def github_repo_exists(full_name):
"""
Determine if a GitHub repo exists.

Returns True, or raises an exception with details.
"""
headers = None
if (token := os.environ.get("GITHUB_TOKEN")):
headers = {"authorization": f"Bearer {token}"}

resp = requests.get(f"https://api.github.com/repos/{full_name}", headers=headers, timeout=60)
if resp.status_code != 200:
raise SchemaError(f"GitHub responded with {resp.status_code} for repo {full_name}")
repo_actual_name = resp.json()["full_name"]
if repo_actual_name != full_name:
raise SchemaError(f"Repo {full_name} is actually at {repo_actual_name}")
return True


def valid_org(s):
"""Is this a valid GitHub org?"""
return isinstance(s, str) and re.match(r"^[^/]+$", s)


def valid_repo(s):
"""Is this a valid repo?"""
return (
isinstance(s, str) and
re.match(r"^[^/]+/[^/]+$", s) and
github_repo_exists(s)
)


def existing_person(s):
"""Is this an existing person in people.yaml?"""
return isinstance(s, str) and s in ALL_PEOPLE


def not_empty_string(s):
"""A string that can't be empty."""
return isinstance(s, str) and len(s) > 0


def check_institution(d):
"""If the agreement is institution, then we have to have an institution."""
if "agreement" in d:
if d['agreement'] == 'institution':
if 'institution' in d:
if d['institution'] not in ALL_ORGS:
raise SchemaError("Institution {!r} isn't in orgs.yaml: {}".format(d['institution'], d))
if d['agreement'] == 'none':
if 'institution' in d:
raise SchemaError("No-agreement should have no institution")
return True


def github_username(s):
"""Is this a valid GitHub username?"""
# Usernames can have "[bot]" at the end for bots.
Expand All @@ -104,77 +39,6 @@ def github_username(s):
return re.match(r"^[a-zA-Z0-9_-]+\*?$", s)


def not_data_key(s):
"""Make sure the GitHub name is not a data line at the wrong indent."""
return s not in [
'name', 'email', 'agreement', 'institution', 'jira',
'comments', 'other_emails', 'before', 'beta', 'committer', 'email_ok',
]


def one_of_keys(*keys):
"""Checks that at least one key is present (not exclusive OR)"""
def _check(d):
if sum(k in d for k in keys) > 0:
return True
raise SchemaError("Must have at least one of {}".format(keys))
return _check


COMMITTER_SCHEMA = Schema(
Or(
# "committer: false" means this person is not a committer.
False,
# or explain where they are a committer:
And(
{
Optional('orgs'): [valid_org],
Optional('repos'): [valid_repo],
Optional('champions'): [existing_person],
Optional('branches'): [not_empty_string],
},
# You have to specify at least one of orgs, repos, or branches:
one_of_keys("orgs", "repos", "branches"),
),
),
)

PEOPLE_SCHEMA = Schema(
Or(
{
And(github_username, not_data_key): And(
{
'name': not_empty_string,
'email': valid_email,
'agreement': valid_agreement,
Optional('institution'): not_empty_string,
Optional('is_robot'): True,
Optional('jira'): not_empty_string,
Optional('comments'): [str],
Optional('other_emails'): [valid_email],
Optional('before'): {
datetime.date: And(
{
Optional('agreement'): valid_agreement,
Optional('institution'): not_empty_string,
Optional('comments'): [str],
Optional('committer'): COMMITTER_SCHEMA,
},
check_institution,
),
},
Optional('beta'): bool,
Optional('contractor'): bool,
Optional('committer'): COMMITTER_SCHEMA,
Optional('email_ok'): bool,
},
check_institution,
),
},
{},
),
)

ORGS_SCHEMA = Schema(
Or(
{
Expand Down Expand Up @@ -230,43 +94,16 @@ def validate_orgs(filename):
assert_sorted(orgs, "Keys in {}".format(filename))


ALL_ORGS = set()
ALL_PEOPLE = set()


def validate_people(filename):
"""
Validate that `filename` conforms to our people.yaml schema.
Supporting files are found in the same directory as `filename`.
"""
with open(filename) as f:
people = yaml.safe_load(f)

global ALL_ORGS, ALL_PEOPLE
with open(pathlib.Path(filename).parent / "orgs.yaml") as orgsf:
org_data = yaml.safe_load(orgsf)
ALL_ORGS = set(org_data)
for orgd in org_data.values():
name = orgd.get("name")
if name:
ALL_ORGS.add(name)

ALL_PEOPLE = set(people)

PEOPLE_SCHEMA.validate(people)
# keys should be sorted.
assert_sorted(people, "Keys in {}".format(filename))


def validate_salesforce_export(filename, encoding="cp1252"):
"""
Validate that `filename` is a Salesforce export we expect.
"""
with open(filename, encoding=encoding) as fcsv:
reader = csv.DictReader(fcsv)
# fields are:
# "First Name","Last Name","Number of Active Ind. CLA Contracts",
# "Title","Account Name","Number of Active Entity CLA Contracts","GitHub Username"
assert reader.fieldnames == [
"First Name", "Last Name", "Number of Active Ind. CLA Contracts",
"Title", "Account Name", "Number of Active Entity CLA Contracts", "GitHub Username",
]
for row in reader:
acct = row["Account Name"]
if acct == "Opfocus Test":
Expand Down
2 changes: 0 additions & 2 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@
-c constraints.txt

PyYAML
requests
schema
backoff
14 changes: 1 addition & 13 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,9 @@
#
# make upgrade
#
backoff==2.2.1
# via -r requirements/base.in
certifi==2022.12.7
# via requests
charset-normalizer==3.0.1
# via requests
contextlib2==21.6.0
# via schema
idna==3.4
# via requests
pyyaml==6.0
# via -r requirements/base.in
requests==2.28.2
pyyaml==6.0.1
# via -r requirements/base.in
schema==0.7.5
# via -r requirements/base.in
urllib3==1.26.14
# via requests
12 changes: 6 additions & 6 deletions requirements/ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
#
# make upgrade
#
distlib==0.3.6
distlib==0.3.7
# via virtualenv
filelock==3.9.0
filelock==3.12.2
# via
# tox
# virtualenv
packaging==23.0
packaging==23.1
# via tox
platformdirs==3.0.0
platformdirs==3.10.0
# via virtualenv
pluggy==1.0.0
pluggy==1.2.0
# via tox
py==1.11.0
# via tox
Expand All @@ -26,5 +26,5 @@ tox==3.28.0
# via
# -c requirements/common_constraints.txt
# -r requirements/ci.in
virtualenv==20.19.0
virtualenv==20.24.2
# via tox
5 changes: 0 additions & 5 deletions requirements/common_constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,3 @@ django-simple-history==3.0.0
# tox>4.0.0 isn't yet compatible with many tox plugins, causing CI failures in almost all repos.
# Details can be found in this discussion: https://github.com/tox-dev/tox/discussions/1810
tox<4.0.0

# edx-sphinx-theme is not compatible with latest Sphinx==6.0.0 version
# Pinning Sphinx version unless the compatibility issue gets resolved
# For details, see issue https://github.com/openedx/edx-sphinx-theme/issues/197
sphinx<6.0.0
Loading