Skip to content

Commit 90956fc

Browse files
authored
Merge Version 3.5.4 (#287)
2 parents e49829a + 3648b10 commit 90956fc

24 files changed

+393
-191
lines changed

.github/workflows/deploy.yml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
name: Trigger Builds on Release
2+
# Trigger builds on okd only on new releases
3+
# Assumes the branch is "master"
4+
# Uses secrets.OKD_BUILD_HOOK to know where to send the event to
5+
# OKD_BUILD_HOOK should be a generic build hook
6+
7+
on:
8+
release:
9+
types:
10+
- released
11+
12+
jobs:
13+
trigger_build:
14+
name: trigger build
15+
runs-on: ubuntu-latest
16+
steps:
17+
# Grab committer and author information from the commit
18+
- name: get commit
19+
id: commit
20+
run: |
21+
commit_url=$(
22+
jq -r '.repository.git_commits_url' $GITHUB_EVENT_PATH |
23+
sed 's/{.*}/\/${{ github.sha }}/'
24+
)
25+
curl --request GET \
26+
--silent \
27+
--show-error \
28+
--url "$commit_url" \
29+
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
30+
--fail > commit-out
31+
jq -C '.' commit-out
32+
echo "::set-output name=committer::$(jq -c '.committer' commit-out)"
33+
echo "::set-output name=author::$(jq -c '.author' commit-out)"
34+
35+
# Construct the json blob as per okd's webhook requirements
36+
- name: format payload
37+
run: |
38+
cat $GITHUB_EVENT_PATH | \
39+
jq '{
40+
git: {
41+
uri: .repository.html_url,
42+
ref: "master",
43+
commit: "${{ github.sha }}",
44+
author: ${{ steps.commit.outputs.author }},
45+
committer: ${{ steps.commit.outputs.committer }}
46+
}
47+
}' | \
48+
tee payload.json | \
49+
jq -C '.'
50+
51+
# send the webhook
52+
- name: trigger build
53+
id: hook
54+
env:
55+
OKD_BUILD_HOOK: ${{ secrets.OKD_BUILD_HOOK }}
56+
run: |
57+
curl \
58+
--insecure \
59+
--silent \
60+
--show-error \
61+
--header "Content-Type: application/json" \
62+
--request POST \
63+
--data @payload.json "$OKD_BUILD_HOOK" > curl-out
64+
jq -C '.' curl-out || (cat curl-out; false)
65+
echo "::set-output name=kind::$(jq '.kind' curl-out)"
66+
67+
# Fail if we recieved a Status response and it doesn't look good
68+
- name: test http code
69+
if: steps.hook.outputs.kind == 'Status'
70+
run: "[ `jq '.code' curl-out` -lt 400 ]"
71+
72+
- name: test status
73+
if: steps.hook.outputs.kind == 'Status'
74+
run: "[ `jq '.status' curl-out` == 'Success' ]"
75+
76+
- name: test if skipped
77+
if: steps.hook.outputs.kind == 'Status'
78+
run: "[[ `jq '.message' curl-out` != *skipping* ]]"

.github/workflows/python-app.yml

+25-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,28 @@ jobs:
3131
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
3232
- name: Lint with pylint
3333
run: |
34-
pylint packet
34+
pylint packet/routes packet
35+
36+
typecheck:
37+
runs-on: ubuntu-latest
38+
39+
strategy:
40+
matrix:
41+
python-version: [3.9]
42+
43+
steps:
44+
- name: Install ldap dependencies
45+
run: sudo apt-get update && sudo apt-get install libldap2-dev libsasl2-dev
46+
- uses: actions/checkout@v2
47+
- name: Set up Python ${{ matrix.python-version }}
48+
uses: actions/setup-python@v2
49+
with:
50+
python-version: ${{ matrix.python-version }}
51+
- name: Install dependencies
52+
run: |
53+
python -m pip install --upgrade pip
54+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
55+
- name: Typecheck with mypy
56+
run: |
57+
# Disabled error codes to discard errors from imports
58+
mypy --disable-error-code import --disable-error-code name-defined --disallow-untyped-defs --exclude routes packet

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,14 @@ All DB commands are from the `Flask-Migrate` library and are used to configure D
115115
docs [here](https://flask-migrate.readthedocs.io/en/latest/) for details.
116116

117117
## Code standards
118-
This project is configured to use Pylint. Commits will be pylinted by GitHub actions and if the score drops your build will
119-
fail blocking you from merging. To make your life easier just run it before making a PR.
118+
This project is configured to use Pylint and mypy. Commits will be pylinted and typechecked by GitHub actions and if the
119+
score drops your build will fail blocking you from merging. To make your life easier just run it before making a PR.
120120

121-
To run pylint use this command:
121+
To run pylint and mypy use these commands:
122122
```bash
123123
pylint packet/routes packet
124+
mypy --disable-error-code import --disable-error-code name-defined --disallow-untyped-defs --exclude routes packet
124125
```
125126

126127
All python files should have a top-level docstring explaining the contents of the file and complex functions should
127-
have docstrings explaining any non-obvious portions.
128+
have docstrings explaining any non-obvious portions. Functions should have type annotations.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"title": "CSH Packet",
33
"name": "csh-packet",
4-
"version": "3.5.3-1",
4+
"version": "3.5.4",
55
"description": "A web app implementation of the CSH introductory packet.",
66
"bugs": {
77
"url": "https://github.com/ComputerScienceHouse/packet/issues",
@@ -26,7 +26,7 @@
2626
"gulp-clean-css": "^4.2.0",
2727
"gulp-minify": "^3.1.0",
2828
"gulp-real-favicon": "^0.3.2",
29-
"gulp-rename": "^1.4.0",
29+
"gulp-rename": "^2.0.0",
3030
"gulp-sass": "^4.0.2",
3131
"require-dir": "^1.2.0"
3232
}

packet/__init__.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88

99
import csh_ldap
10-
import onesignal
10+
import onesignal_sdk.client as onesignal
1111
from flask import Flask
1212
from flask_gzip import Gzip
1313
from flask_migrate import Migrate
@@ -21,7 +21,7 @@
2121

2222
from .git import get_version
2323

24-
app = Flask(__name__)
24+
app: Flask = Flask(__name__)
2525
gzip = Gzip(app)
2626

2727
# Load default configuration and any environment variable overrides
@@ -38,7 +38,7 @@
3838

3939
# Logger configuration
4040
logging.getLogger().setLevel(app.config['LOG_LEVEL'])
41-
app.logger.info('Launching packet v' + app.config['VERSION'])
41+
app.logger.info('Launching packet ' + app.config['VERSION'])
4242
app.logger.info('Using the {} realm'.format(app.config['REALM']))
4343

4444
# Initialize the extensions
@@ -57,7 +57,7 @@
5757
app.config['ONESIGNAL_CSH_APP_ID']:
5858
csh_onesignal_client = onesignal.Client(
5959
user_auth_key=app.config['ONESIGNAL_USER_AUTH_KEY'],
60-
app_auth_key=app.config['ONESIGNAL_CSH_APP_AUTH_KEY'],
60+
rest_api_key=app.config['ONESIGNAL_CSH_APP_AUTH_KEY'],
6161
app_id=app.config['ONESIGNAL_CSH_APP_ID']
6262
)
6363
app.logger.info('CSH Onesignal configured and notifications enabled')
@@ -68,7 +68,7 @@
6868
app.config['ONESIGNAL_INTRO_APP_ID']:
6969
intro_onesignal_client = onesignal.Client(
7070
user_auth_key=app.config['ONESIGNAL_USER_AUTH_KEY'],
71-
app_auth_key=app.config['ONESIGNAL_INTRO_APP_AUTH_KEY'],
71+
rest_api_key=app.config['ONESIGNAL_INTRO_APP_AUTH_KEY'],
7272
app_id=app.config['ONESIGNAL_INTRO_APP_ID']
7373
)
7474
app.logger.info('Intro Onesignal configured and notifications enabled')
@@ -78,6 +78,7 @@
7878
app.logger.info('OIDCAuth configured')
7979

8080
# Sentry
81+
# pylint: disable=abstract-class-instantiated
8182
sentry_sdk.init(
8283
dsn=app.config['SENTRY_DSN'],
8384
integrations=[FlaskIntegration(), SqlalchemyIntegration()]

packet/commands.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sys
66

77
from secrets import token_hex
8-
from datetime import datetime, time
8+
from datetime import datetime, time, date
99
import csv
1010
import click
1111

@@ -15,7 +15,7 @@
1515

1616

1717
@app.cli.command('create-secret')
18-
def create_secret():
18+
def create_secret() -> None:
1919
"""
2020
Generates a securely random token. Useful for creating a value for use in the "SECRET_KEY" config setting.
2121
"""
@@ -28,13 +28,13 @@ def create_secret():
2828

2929

3030
class CSVFreshman:
31-
def __init__(self, row):
31+
def __init__(self, row: list[str]) -> None:
3232
self.name = row[0].strip()
3333
self.rit_username = row[3].strip()
3434
self.onfloor = row[1].strip() == 'TRUE'
3535

3636

37-
def parse_csv(freshmen_csv):
37+
def parse_csv(freshmen_csv: str) -> dict[str, CSVFreshman]:
3838
print('Parsing file...')
3939
try:
4040
with open(freshmen_csv, newline='') as freshmen_csv_file:
@@ -44,7 +44,7 @@ def parse_csv(freshmen_csv):
4444
raise e
4545

4646

47-
def input_date(prompt):
47+
def input_date(prompt: str) -> date:
4848
while True:
4949
try:
5050
date_str = input(prompt + ' (format: MM/DD/YYYY): ')
@@ -55,7 +55,7 @@ def input_date(prompt):
5555

5656
@app.cli.command('sync-freshmen')
5757
@click.argument('freshmen_csv')
58-
def sync_freshmen(freshmen_csv):
58+
def sync_freshmen(freshmen_csv: str) -> None:
5959
"""
6060
Updates the freshmen entries in the DB to match the given CSV.
6161
"""
@@ -68,7 +68,7 @@ def sync_freshmen(freshmen_csv):
6868

6969
@app.cli.command('create-packets')
7070
@click.argument('freshmen_csv')
71-
def create_packets(freshmen_csv):
71+
def create_packets(freshmen_csv: str) -> None:
7272
"""
7373
Creates a new packet season for each of the freshmen in the given CSV.
7474
"""
@@ -84,7 +84,7 @@ def create_packets(freshmen_csv):
8484

8585

8686
@app.cli.command('ldap-sync')
87-
def ldap_sync():
87+
def ldap_sync() -> None:
8888
"""
8989
Updates the upper and misc sigs in the DB to match ldap.
9090
"""
@@ -97,7 +97,7 @@ def ldap_sync():
9797
help='The file to write to. If no file provided, output is sent to stdout.')
9898
@click.option('--csv/--no-csv', 'use_csv', required=False, default=False, help='Format output as comma separated list.')
9999
@click.option('--date', 'date_str', required=False, default='', help='Packet end date in the format MM/DD/YYYY.')
100-
def fetch_results(file_path, use_csv, date_str):
100+
def fetch_results(file_path: str, use_csv: bool, date_str: str) -> None:
101101
"""
102102
Fetches and prints the results from a given packet season.
103103
"""
@@ -150,7 +150,7 @@ def fetch_results(file_path, use_csv, date_str):
150150

151151
@app.cli.command('extend-packet')
152152
@click.argument('packet_id')
153-
def extend_packet(packet_id):
153+
def extend_packet(packet_id: int) -> None:
154154
"""
155155
Extends the given packet by setting a new end date.
156156
"""
@@ -168,7 +168,7 @@ def extend_packet(packet_id):
168168
print('Packet successfully extended')
169169

170170

171-
def remove_sig(packet_id, username, is_member):
171+
def remove_sig(packet_id: int, username: str, is_member: bool) -> None:
172172
packet = Packet.by_id(packet_id)
173173

174174
if not packet.is_open():
@@ -200,7 +200,7 @@ def remove_sig(packet_id, username, is_member):
200200
@app.cli.command('remove-member-sig')
201201
@click.argument('packet_id')
202202
@click.argument('member')
203-
def remove_member_sig(packet_id, member):
203+
def remove_member_sig(packet_id: int, member: str) -> None:
204204
"""
205205
Removes the given member's signature from the given packet.
206206
:param member: The member's CSH username
@@ -211,7 +211,7 @@ def remove_member_sig(packet_id, member):
211211
@app.cli.command('remove-freshman-sig')
212212
@click.argument('packet_id')
213213
@click.argument('freshman')
214-
def remove_freshman_sig(packet_id, freshman):
214+
def remove_freshman_sig(packet_id: int, freshman: str) -> None:
215215
"""
216216
Removes the given freshman's signature from the given packet.
217217
:param freshman: The freshman's RIT username

packet/context_processors.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@
55
import urllib
66
from functools import lru_cache
77
from datetime import datetime
8+
from typing import Callable
89

9-
from packet.models import Freshman
10+
from packet.models import Freshman, UpperSignature
1011
from packet import app, ldap
1112

1213

1314
# pylint: disable=bare-except
1415
@lru_cache(maxsize=128)
15-
def get_csh_name(username):
16+
def get_csh_name(username: str) -> str:
1617
try:
1718
member = ldap.get_member(username)
1819
return member.cn + ' (' + member.uid + ')'
1920
except:
2021
return username
2122

2223

23-
def get_roles(sig):
24+
def get_roles(sig: UpperSignature) -> dict[str, str]:
2425
"""
2526
Converts a signature's role fields to a dict for ease of access.
2627
:return: A dictionary of role short names to role long names
@@ -45,7 +46,7 @@ def get_roles(sig):
4546

4647
# pylint: disable=bare-except
4748
@lru_cache(maxsize=256)
48-
def get_rit_name(username):
49+
def get_rit_name(username: str) -> str:
4950
try:
5051
freshman = Freshman.query.filter_by(rit_username=username).first()
5152
return freshman.name + ' (' + username + ')'
@@ -55,7 +56,7 @@ def get_rit_name(username):
5556

5657
# pylint: disable=bare-except
5758
@lru_cache(maxsize=256)
58-
def get_rit_image(username):
59+
def get_rit_image(username: str) -> str:
5960
if username:
6061
addresses = [username + '@rit.edu', username + '@g.rit.edu']
6162
for addr in addresses:
@@ -69,15 +70,15 @@ def get_rit_image(username):
6970
return 'https://www.gravatar.com/avatar/freshmen?d=mp&f=y'
7071

7172

72-
def log_time(label):
73+
def log_time(label: str) -> None:
7374
"""
7475
Used during debugging to log timestamps while rendering templates
7576
"""
7677
print(label, datetime.now())
7778

7879

7980
@app.context_processor
80-
def utility_processor():
81+
def utility_processor() -> dict[str, Callable]:
8182
return dict(
8283
get_csh_name=get_csh_name, get_rit_name=get_rit_name, get_rit_image=get_rit_image, log_time=log_time,
8384
get_roles=get_roles

0 commit comments

Comments
 (0)