Skip to content

Commit 0416a59

Browse files
Merge branch 'master' into fix/release-webhook-commit-hash
2 parents 70e7893 + 78c3f60 commit 0416a59

File tree

22 files changed

+345
-91
lines changed

22 files changed

+345
-91
lines changed

.github/workflows/main.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ jobs:
1515

1616
runs-on: ubuntu-latest
1717
strategy:
18+
fail-fast: false
1819
matrix:
19-
python-version: ['3.8', '3.9', '3.10', '3.11']
20+
python-version: ['3.10', '3.12', '3.13', '3.14']
21+
include:
22+
- python-version: '3.13'
23+
experimental: true
24+
- python-version: '3.14'
25+
experimental: true
26+
27+
continue-on-error: ${{ matrix.experimental || false }}
2028

2129
steps:
2230
- uses: actions/checkout@v3
@@ -27,7 +35,7 @@ jobs:
2735
- name: Install dependencies
2836
run: |
2937
sudo apt-get update
30-
sudo apt-get install libvirt-dev
38+
sudo apt-get install libvirt-dev libxml2-dev libxslt-dev
3139
python -m pip install --upgrade pip
3240
if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi
3341
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
@@ -50,7 +58,7 @@ jobs:
5058
run: |
5159
nose2
5260
- name: Upload to codecov
53-
uses: codecov/codecov-action@v3
61+
uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3
5462
with:
5563
flags: unittests
5664
name: sample-platform

database.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ def __init__(self, classname: str, bases: Union[Tuple[Type[DeclEnum]], Tuple[Typ
107107
self._reg: Dict
108108
self._reg = reg = self._reg.copy()
109109
for k, v in dict_.items():
110-
if isinstance(v, tuple):
110+
# Skip dunder attributes (Python 3.13+ adds __static_attributes__ tuple)
111+
# and validate tuple has exactly 2 elements (value, description)
112+
if isinstance(v, tuple) and not k.startswith('_') and len(v) == 2:
111113
sym = reg[v[0]] = EnumSymbol(self, k, *v)
112114
setattr(self, k, sym)
113115
return type.__init__(self, classname, bases, dict_)

manage.py

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,34 @@
11
#!/usr/local/bin/python3
2-
"""Root module to manage flask script commands."""
3-
from flask_script import Command, Manager
2+
"""Root module to manage flask CLI commands."""
3+
import click
44

55
from exceptions import CCExtractorEndedWithNonZero, MissingPathToCCExtractor
66
from mod_regression.update_regression import update_expected_results
77
from run import app
88

9-
manager = Manager(app)
109

11-
12-
@manager.add_command
13-
class UpdateResults(Command):
10+
@app.cli.command('update')
11+
@click.argument('path_to_ccex')
12+
def update_results(path_to_ccex):
1413
"""
1514
Update results for the present samples with new ccextractor version.
1615
17-
Pass path to CCExtractor binary as the first argument. Example, `python manage.py update /path/to/ccextractor`
16+
Pass path to CCExtractor binary as the argument.
17+
Example: flask update /path/to/ccextractor
1818
"""
19+
if not path_to_ccex:
20+
click.echo('path to ccextractor is missing')
21+
raise MissingPathToCCExtractor
1922

20-
name = 'update'
21-
capture_all_args = True
22-
23-
def run(self, remaining):
24-
"""Driver function for update command."""
25-
if len(remaining) == 0:
26-
print('path to ccextractor is missing')
27-
raise MissingPathToCCExtractor
28-
29-
path_to_ccex = remaining[0]
30-
print(f'path to ccextractor: {path_to_ccex}')
23+
click.echo(f'path to ccextractor: {path_to_ccex}')
3124

32-
if not update_expected_results(path_to_ccex):
33-
print('update function errored')
34-
raise CCExtractorEndedWithNonZero
25+
if not update_expected_results(path_to_ccex):
26+
click.echo('update function errored')
27+
raise CCExtractorEndedWithNonZero
3528

36-
print('update function finished')
37-
return 0
29+
click.echo('update function finished')
30+
return 0
3831

3932

4033
if __name__ == '__main__':
41-
manager.run()
34+
app.cli()

migrations/env.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
fileConfig(config.config_file_name)
2323
logger = logging.getLogger('alembic.env')
2424

25-
config.set_main_option(
26-
'sqlalchemy.url', current_app.config.get(
27-
'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
25+
db_uri = current_app.config.get('SQLALCHEMY_DATABASE_URI')
26+
if db_uri is not None:
27+
config.set_main_option('sqlalchemy.url', db_uri.replace('%', '%%'))
2828
target_metadata = current_app.extensions['migrate'].db.metadata
2929

3030
# other values from the config, defined by the needs of env.py,

mod_auth/controllers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ def manage():
469469
if user_to_update.email != form.email.data:
470470
old_email = user_to_update.email
471471
user_to_update.email = form.email.data
472-
if len(form.new_password.data) >= 10:
472+
if len(form.new_password.data or "") >= 10:
473473
password = True
474474
user_to_update.password = User.generate_hash(form.new_password.data)
475475
if user_to_update.name != form.name.data:

mod_auth/forms.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
from typing import Any, Callable, Optional, Type
55

66
from flask_wtf import FlaskForm
7-
from wtforms import PasswordField, SelectField, StringField, SubmitField
8-
from wtforms.fields.html5 import EmailField
9-
from wtforms.fields.simple import PasswordField
7+
from wtforms import (EmailField, PasswordField, SelectField, StringField,
8+
SubmitField)
109
from wtforms.validators import DataRequired, Email, ValidationError
1110

1211
import mod_auth.models
@@ -39,7 +38,7 @@ def valid_password(form: CompleteSignupForm, field: PasswordField) -> None:
3938
from run import config
4039
min_pwd_len = int(config['MIN_PWD_LEN'])
4140
max_pwd_len = int(config['MAX_PWD_LEN'])
42-
pass_size = len(field.data)
41+
pass_size = len(field.data or "")
4342
if pass_size == 0:
4443
raise ValidationError('new password cannot be empty')
4544
if pass_size < min_pwd_len or pass_size > max_pwd_len:
@@ -59,7 +58,7 @@ def email_not_in_use(has_user_field: bool = False) -> Callable:
5958
def _email_not_in_use(form, field):
6059
user_id = -1 if not has_user_field else form.user.id
6160
user = User.query.filter(User.email == field.data).first()
62-
if user is not None and user.id != user_id and len(field.data) > 0:
61+
if user is not None and user.id != user_id and len(field.data or "") > 0:
6362
raise ValidationError('This address is already in use')
6463

6564
return _email_not_in_use
@@ -165,7 +164,7 @@ def validate_new_password(form, field) -> None:
165164
:param field: The data value for the 'password' entered by User
166165
:type field : PasswordField
167166
"""
168-
if len(field.data) == 0 and len(form.new_password_repeat.data) == 0:
167+
if len(field.data or "") == 0 and len(form.new_password_repeat.data or "") == 0:
169168
return
170169

171170
valid_password(form, field)
@@ -181,7 +180,7 @@ def validate_new_password_repeat(form, field) -> None:
181180
:type field : PasswordField
182181
"""
183182
if form.email is not None:
184-
if len(field.data) == 0 and len(form.new_password.data) == 0:
183+
if len(field.data or "") == 0 and len(form.new_password.data or "") == 0:
185184
return
186185

187186
if field.data != form.new_password.data:

mod_ci/controllers.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
import requests
1717
from flask import (Blueprint, abort, flash, g, jsonify, redirect, request,
1818
url_for)
19-
from github import Commit, Github, GithubException, GithubObject, Repository
19+
from github import (Auth, Commit, Github, GithubException, GithubObject,
20+
Repository)
2021
from google.oauth2 import service_account
2122
from lxml import etree
2223
from markdown2 import markdown
@@ -146,7 +147,7 @@ def start_platforms(repository, delay=None, platform=None) -> None:
146147

147148
with app.app_context():
148149
from flask import current_app
149-
app = current_app._get_current_object()
150+
app = current_app._get_current_object() # type: ignore[attr-defined]
150151

151152
# Create a database session
152153
db = create_session(config.get('DATABASE_URI', ''))
@@ -792,7 +793,7 @@ def schedule_test(gh_commit: Commit.Commit) -> None:
792793

793794

794795
def update_status_on_github(gh_commit: Commit.Commit, state, description, context,
795-
target_url=GithubObject.NotSet): # type: ignore
796+
target_url=GithubObject.NotSet):
796797
"""
797798
Update status on GitHub.
798799
@@ -1005,7 +1006,11 @@ def start_ci():
10051006
g.log.warning(f'CI payload is empty')
10061007
abort(abort_code)
10071008

1008-
gh = Github(g.github['bot_token'])
1009+
if not g.github['bot_token']:
1010+
g.log.error('GitHub token not configured, cannot process webhook')
1011+
return json.dumps({'msg': 'GitHub token not configured'}), 500
1012+
1013+
gh = Github(auth=Auth.Token(g.github['bot_token']))
10091014
repository = gh.get_repo(f"{g.github['repository_owner']}/{g.github['repository']}")
10101015

10111016
if event == "push":
@@ -1413,7 +1418,11 @@ def progress_type_request(log, test, test_id, request) -> bool:
14131418
g.db.add(progress)
14141419
g.db.commit()
14151420

1416-
gh = Github(g.github['bot_token'])
1421+
if not g.github['bot_token']:
1422+
log.error('GitHub token not configured, cannot update status on GitHub')
1423+
return True
1424+
1425+
gh = Github(auth=Auth.Token(g.github['bot_token']))
14171426
repository = gh.get_repo(f"{g.github['repository_owner']}/{g.github['repository']}")
14181427
# Store the test commit for testing in case of commit
14191428
if status == TestStatus.completed and is_main_repo(test.fork.github):
@@ -1786,8 +1795,11 @@ def comment_pr(test: Test) -> str:
17861795
template = app.jinja_env.get_or_select_template('ci/pr_comment.txt')
17871796
message = template.render(comment_info=comment_info, test_id=test_id, platform=platform)
17881797
log.debug(f"GitHub PR Comment Message Created for Test_id: {test_id}")
1798+
if not g.github['bot_token']:
1799+
log.error(f"GitHub token not configured, cannot post PR comment for Test_id: {test_id}")
1800+
return Status.FAILURE
17891801
try:
1790-
gh = Github(g.github['bot_token'])
1802+
gh = Github(auth=Auth.Token(g.github['bot_token']))
17911803
repository = gh.get_repo(f"{g.github['repository_owner']}/{g.github['repository']}")
17921804
# Pull requests are just issues with code, so GitHub considers PR comments in issues
17931805
pull_request = repository.get_pull(number=test.pr_nr)
@@ -1858,9 +1870,13 @@ def blocked_users():
18581870
g.db.commit()
18591871
flash('User blocked successfully.')
18601872

1873+
if not g.github['bot_token']:
1874+
g.log.error('GitHub token not configured, cannot check blocked user PRs')
1875+
return redirect(url_for('.blocked_users'))
1876+
18611877
try:
18621878
# Remove any queued pull request from blocked user
1863-
gh = Github(g.github['bot_token'])
1879+
gh = Github(auth=Auth.Token(g.github['bot_token']))
18641880
repository = gh.get_repo(f"{g.github['repository_owner']}/{g.github['repository']}")
18651881
# Getting all pull requests by blocked user on the repo
18661882
pulls = repository.get_pulls(state='open')

mod_ci/cron.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
def cron(testing=False):
1212
"""Script to run from cron for Sampleplatform."""
1313
from flask import current_app
14-
from github import Github
14+
from github import Auth, Github
1515

1616
from database import create_session
1717
from mod_ci.controllers import TestPlatform, gcp_instance, start_platforms
1818
from run import config, log
1919

2020
log.info('Run the cron for kicking off CI platform(s).')
21-
gh = Github(config['GITHUB_TOKEN'])
21+
github_token = config['GITHUB_TOKEN']
22+
if not github_token:
23+
log.error('GITHUB_TOKEN not configured, cannot run CI cron')
24+
return
25+
gh = Github(auth=Auth.Token(github_token))
2226
repository = gh.get_repo(f"{config['GITHUB_OWNER']}/{config['GITHUB_REPOSITORY']}")
2327

2428
if testing is True:

mod_customized/controllers.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from datetime import datetime, timedelta
44

55
from flask import Blueprint, g, redirect, request, url_for
6-
from github import Github, GithubException
6+
from github import Auth, Github, GithubException
77
from sqlalchemy import and_
88

99
from decorators import template_renderer
@@ -47,8 +47,8 @@ def index():
4747
fork_test_form = TestForkForm(request.form)
4848
username = fetch_username_from_token()
4949
commit_options = False
50-
if username is not None:
51-
gh = Github(g.github['bot_token'])
50+
if username is not None and g.github['bot_token']:
51+
gh = Github(auth=Auth.Token(g.github['bot_token']))
5252
repository = gh.get_repo(f"{username}/{g.github['repository']}")
5353
# Only commits since last month
5454
last_month = datetime.now() - timedelta(days=30)
@@ -78,6 +78,8 @@ def index():
7878
fork_test_form.commit_hash.errors.append('Error contacting GitHub')
7979
else:
8080
fork_test_form.commit_hash.errors.append('Wrong Commit Hash')
81+
elif username is not None:
82+
g.log.error('GitHub token not configured, cannot fetch commits')
8183

8284
populated_categories = g.db.query(regressionTestLinkTable.c.category_id).subquery()
8385
categories = Category.query.filter(Category.id.in_(populated_categories)).order_by(Category.name.asc()).all()

mod_test/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def __init__(self, test_id, status, message, timestamp=None) -> None:
295295
tz = get_localzone()
296296

297297
if timestamp is None:
298-
timestamp = tz.localize(datetime.datetime.now())
298+
timestamp = datetime.datetime.now(tz)
299299
timestamp = timestamp.astimezone(pytz.UTC)
300300

301301
if timestamp.tzinfo is None:

0 commit comments

Comments
 (0)