Skip to content

Commit b20d32a

Browse files
authored
Branch and filename are now always not null (#602)
* Branch and filename are now always not null * Branch is always set to value in directory * Changed test logic * Reverted checkout branch for local directory
1 parent 4c048dc commit b20d32a

File tree

9 files changed

+80
-59
lines changed

9 files changed

+80
-59
lines changed

api/api_helpers.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,15 @@ def get_run_info(run_id):
103103
return DB().fetch_one(query, params=params, row_factory=psycopg_rows_dict_row)
104104

105105

106-
def get_timeline_query(uri,filename,machine_id, branch, metrics, phase, start_date=None, end_date=None, detail_name=None, limit_365=False, sorting='run'):
106+
def get_timeline_query(uri, filename, machine_id, branch, metrics, phase, start_date=None, end_date=None, detail_name=None, limit_365=False, sorting='run'):
107107

108108
if filename is None or filename.strip() == '':
109109
filename = 'usage_scenario.yml'
110110

111-
params = [uri, filename, machine_id, f"%{phase}"]
111+
if branch is None or branch.strip() != '':
112+
branch = 'main'
112113

113-
branch_condition = 'AND r.branch IS NULL'
114-
if branch is not None and branch.strip() != '':
115-
branch_condition = 'AND r.branch = %s'
116-
params.append(branch)
114+
params = [uri, filename, branch, machine_id, f"%{phase}"]
117115

118116
metrics_condition = ''
119117
if metrics is None or metrics.strip() == '' or metrics.strip() == 'key':
@@ -156,10 +154,10 @@ def get_timeline_query(uri,filename,machine_id, branch, metrics, phase, start_da
156154
WHERE
157155
r.uri = %s
158156
AND r.filename = %s
157+
AND r.branch = %s
159158
AND r.end_measurement IS NOT NULL
160159
AND r.machine_id = %s
161160
AND p.phase LIKE %s
162-
{branch_condition}
163161
{metrics_condition}
164162
{start_date_condition}
165163
{end_date_condition}
@@ -171,13 +169,14 @@ def get_timeline_query(uri,filename,machine_id, branch, metrics, phase, start_da
171169
p.phase ASC, {sorting_condition}
172170
173171
"""
172+
174173
return (query, params)
175174

176175
def determine_comparison_case(ids):
177176

178177
query = '''
179178
WITH uniques as (
180-
SELECT uri, filename, machine_id, commit_hash, COALESCE(branch, 'main / master') as branch FROM runs
179+
SELECT uri, filename, machine_id, commit_hash, branch FROM runs
181180
WHERE id = ANY(%s::uuid[])
182181
GROUP BY uri, filename, machine_id, commit_hash, branch
183182
)
@@ -277,7 +276,7 @@ def get_phase_stats(ids):
277276
query = """
278277
SELECT
279278
a.phase, a.metric, a.detail_name, a.value, a.type, a.max_value, a.min_value, a.unit,
280-
b.uri, c.description, b.filename, b.commit_hash, COALESCE(b.branch, 'main / master') as branch
279+
b.uri, c.description, b.filename, b.commit_hash, b.branch
281280
FROM phase_stats as a
282281
LEFT JOIN runs as b on b.id = a.run_id
283282
LEFT JOIN machines as c on c.id = b.machine_id

api/main.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ async def get_repositories(uri: str | None = None, branch: str | None = None, ma
227227
async def get_runs(uri: str | None = None, branch: str | None = None, machine_id: int | None = None, machine: str | None = None, filename: str | None = None, limit: int | None = None):
228228

229229
query = """
230-
SELECT r.id, r.name, r.uri, COALESCE(r.branch, 'main / master'), r.created_at, r.invalid_run, r.filename, m.description, r.commit_hash, r.end_measurement
230+
SELECT r.id, r.name, r.uri, r.branch, r.created_at, r.invalid_run, r.filename, m.description, r.commit_hash, r.end_measurement
231231
FROM runs as r
232232
LEFT JOIN machines as m on r.machine_id = m.id
233233
WHERE 1=1
@@ -303,7 +303,7 @@ async def compare_in_repo(ids: str):
303303
machine = machines[run_info['machine_id']]
304304
uri = run_info['uri']
305305
usage_scenario = run_info['usage_scenario']['name']
306-
branch = run_info['branch'] if run_info['branch'] is not None else 'main / master'
306+
branch = run_info['branch']
307307
commit = run_info['commit_hash']
308308
filename = run_info['filename']
309309

@@ -519,8 +519,8 @@ async def get_timeline_projects():
519519
FROM runs as r
520520
WHERE
521521
p.url = r.uri
522-
AND COALESCE(p.branch, 'main / master') = COALESCE(r.branch, 'main / master')
523-
AND COALESCE(p.filename, 'usage_scenario.yml') = COALESCE(r.filename, 'usage_scenario.yml')
522+
AND p.branch = r.branch
523+
AND p.filename = r.filename
524524
AND p.machine_id = r.machine_id
525525
ORDER BY r.created_at DESC
526526
LIMIT 1
@@ -1017,15 +1017,15 @@ async def software_add(software: Software):
10171017
if software.email is None or software.email.strip() == '':
10181018
raise RequestValidationError('E-mail is empty')
10191019

1020-
if not DB().fetch_one('SELECT id FROM machines WHERE id=%s AND available=TRUE', params=(software.machine_id,)):
1021-
raise RequestValidationError('Machine does not exist')
1020+
if software.branch is None or software.branch.strip() == '':
1021+
software.branch = 'main'
10221022

1023+
if software.filename is None or software.filename.strip() == '':
1024+
software.filename = 'usage_scenario.yml'
10231025

1024-
if software.branch.strip() == '':
1025-
software.branch = None
1026+
if not DB().fetch_one('SELECT id FROM machines WHERE id=%s AND available=TRUE', params=(software.machine_id,)):
1027+
raise RequestValidationError('Machine does not exist')
10261028

1027-
if software.filename.strip() == '':
1028-
software.filename = 'usage_scenario.yml'
10291029

10301030
if software.schedule_mode not in ['one-off', 'time', 'commit', 'variance']:
10311031
raise RequestValidationError(f"Please select a valid measurement interval. ({software.schedule_mode}) is unknown.")

docker/structure.sql

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ CREATE TABLE jobs (
2222
name text,
2323
email text,
2424
url text,
25-
branch text,
26-
filename text,
25+
branch text NOT NULL,
26+
filename text NOT NULL,
2727
categories int[],
2828
machine_id int REFERENCES machines(id) ON DELETE SET NULL ON UPDATE CASCADE,
2929
created_at timestamp with time zone DEFAULT now(),
@@ -39,13 +39,13 @@ CREATE TABLE runs (
3939
job_id integer REFERENCES jobs(id) ON DELETE SET NULL ON UPDATE CASCADE UNIQUE,
4040
name text,
4141
uri text,
42-
branch text,
42+
branch text NOT NULL,
4343
commit_hash text,
4444
commit_timestamp timestamp with time zone,
4545
email text,
4646
categories int[],
4747
usage_scenario json,
48-
filename text,
48+
filename text NOT NULL,
4949
machine_specs jsonb,
5050
runner_arguments json,
5151
machine_id int REFERENCES machines(id) ON DELETE SET NULL ON UPDATE CASCADE,
@@ -196,8 +196,8 @@ CREATE TABLE timeline_projects (
196196
name text,
197197
url text,
198198
categories integer[],
199-
branch text DEFAULT 'NULL'::text,
200-
filename text,
199+
branch text NOT NULL,
200+
filename text NOT NULL,
201201
machine_id integer REFERENCES machines(id) ON DELETE RESTRICT ON UPDATE CASCADE NOT NULL,
202202
schedule_mode text NOT NULL,
203203
last_scheduled timestamp with time zone,

frontend/request.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ <h1 class="ui header float left">
7777
<input type="text" placeholder="Filename (optional - default: usage_scenario.yml)" id="text-field-hero-input4" name="filename">
7878
</div>
7979
<div class="ui fluid icon input">
80-
<input type="text" placeholder="Branch (optional - default: main/master)" id="text-field-hero-input5" name="branch">
80+
<input type="text" placeholder="Branch (optional - default: main)" id="text-field-hero-input5" name="branch">
8181
</div>
8282
<div class="ui fluid icon input">
8383
<select name="machine_id" class="ui fluid dropdown" required>

frontend/timeline.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ <h4>What is Timeline View?</h4>
112112
</div>
113113
<div class="field">
114114
<label>Branch:</label>
115-
<input type="text" name="branch" value="" placeholder="Leave empty for default (main/master)" class="ui input large">
115+
<input type="text" name="branch" value="" placeholder="Leave empty for default (main)" class="ui input large">
116116
</div>
117117
</div>
118118
<div class="inline fields">
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
UPDATE runs SET filename = 'usage_scenario.yml' WHERE filename IS NULL;
2+
UPDATE jobs SET filename = 'usage_scenario.yml' WHERE filename IS NULL;
3+
UPDATE timeline_projects SET filename = 'usage_scenario.yml' WHERE filename IS NULL;
4+
5+
UPDATE runs SET branch = 'main' WHERE branch IS NULL;
6+
UPDATE jobs SET branch = 'main' WHERE branch IS NULL;
7+
UPDATE timeline_projects SET branch = 'main' WHERE branch IS NULL;
8+
9+
10+
ALTER TABLE "public"."jobs"
11+
ALTER COLUMN "branch" DROP DEFAULT,
12+
ALTER COLUMN "branch" SET NOT NULL,
13+
ALTER COLUMN "filename" DROP DEFAULT,
14+
ALTER COLUMN "filename" SET NOT NULL;
15+
16+
ALTER TABLE "public"."timeline_projects"
17+
ALTER COLUMN "branch" DROP DEFAULT,
18+
ALTER COLUMN "branch" SET NOT NULL,
19+
ALTER COLUMN "filename" DROP DEFAULT,
20+
ALTER COLUMN "filename" SET NOT NULL;
21+
22+
ALTER TABLE "public"."runs"
23+
ALTER COLUMN "branch" DROP DEFAULT,
24+
ALTER COLUMN "branch" SET NOT NULL,
25+
ALTER COLUMN "filename" DROP DEFAULT,
26+
ALTER COLUMN "filename" SET NOT NULL;

runner.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ def __init__(self,
116116
self._sci = {'R_d': None, 'R': 0}
117117
self._job_id = job_id
118118
self._arguments = locals()
119+
self._commit_hash = None
120+
self._commit_timestamp = None
119121
del self._arguments['self'] # self is not needed and also cannot be serialzed. We remove it
120122

121123

@@ -147,12 +149,15 @@ def custom_sleep(self, sleep_time):
147149
time.sleep(sleep_time)
148150

149151
def initialize_run(self):
150-
# We issue a fetch_one() instead of a query() here, cause we want to get the RUN_ID
152+
# We issue a fetch_one() instead of a query() here, cause we want to get the RUN_ID
153+
154+
# we also update the branch here again, as this might not be main in case of local filesystem
155+
151156
self.__run_id = DB().fetch_one("""
152-
INSERT INTO runs (job_id, name, uri, email, branch, runner_arguments, created_at)
153-
VALUES (%s, %s, %s, 'manual', %s, %s, NOW())
157+
INSERT INTO runs (job_id, name, uri, email, branch, filename, commit_hash, commit_timestamp, runner_arguments, created_at)
158+
VALUES (%s, %s, %s, 'manual', %s, %s, %s, %s, %s, NOW())
154159
RETURNING id
155-
""", params=(self._job_id, self._name, self._uri, self._branch, json.dumps(self._arguments)))[0]
160+
""", params=(self._job_id, self._name, self._uri, self._branch, self._original_filename, self._commit_hash, self._commit_timestamp, json.dumps(self._arguments)))[0]
156161
return self.__run_id
157162

158163
def initialize_folder(self, path):
@@ -218,37 +223,32 @@ def checkout_repository(self):
218223

219224
else:
220225
if self._branch:
226+
# we never want to checkout a local directory to a different branch as this might also be the GMT directory itself and might confuse the tool
221227
raise RuntimeError('Specified --branch but using local URI. Did you mean to specify a github url?')
222228
self.__folder = self._uri
223229

230+
self._branch = subprocess.check_output(['git', 'branch', '--show-current'], cwd=self.__folder, encoding='UTF-8').strip()
231+
224232
# we can safely do this, even with problematic folders, as the folder can only be a local unsafe one when
225233
# running in CLI mode
226-
commit_hash = subprocess.run(
234+
self._commit_hash = subprocess.run(
227235
['git', 'rev-parse', 'HEAD'],
228236
check=True,
229237
capture_output=True,
230238
encoding='UTF-8',
231239
cwd=self.__folder
232240
)
233-
commit_hash = commit_hash.stdout.strip("\n")
241+
self._commit_hash = self._commit_hash.stdout.strip("\n")
234242

235-
commit_timestamp = subprocess.run(
243+
self._commit_timestamp = subprocess.run(
236244
['git', 'show', '-s', '--format=%ci'],
237245
check=True,
238246
capture_output=True,
239247
encoding='UTF-8',
240248
cwd=self.__folder
241249
)
242-
commit_timestamp = commit_timestamp.stdout.strip("\n")
243-
parsed_timestamp = datetime.strptime(commit_timestamp, "%Y-%m-%d %H:%M:%S %z")
244-
245-
DB().query("""
246-
UPDATE runs
247-
SET
248-
commit_hash=%s,
249-
commit_timestamp=%s
250-
WHERE id = %s
251-
""", params=(commit_hash, parsed_timestamp, self.__run_id))
250+
self._commit_timestamp = self._commit_timestamp.stdout.strip("\n")
251+
self._commit_timestamp = datetime.strptime(self._commit_timestamp, "%Y-%m-%d %H:%M:%S %z")
252252

253253
# This method loads the yml file and takes care that the includes work and are secure.
254254
# It uses the tagging infrastructure provided by https://pyyaml.org/wiki/PyYAMLDocumentation
@@ -464,14 +464,13 @@ def update_and_insert_specs(self):
464464
UPDATE runs
465465
SET
466466
machine_id=%s, machine_specs=%s, measurement_config=%s,
467-
usage_scenario = %s, filename=%s, gmt_hash=%s
467+
usage_scenario = %s, gmt_hash=%s
468468
WHERE id = %s
469469
""", params=(
470470
config['machine']['id'],
471471
escape(json.dumps(machine_specs), quote=False),
472472
json.dumps(measurement_config),
473473
escape(json.dumps(self._usage_scenario), quote=False),
474-
self._original_filename,
475474
gmt_hash,
476475
self.__run_id)
477476
)
@@ -812,8 +811,7 @@ def setup_services(self):
812811
docker_run_string.append(f"{env_key}={env_value}")
813812

814813
if env_var_check_errors:
815-
raise RuntimeError("Docker container environment setup has problems:\n" +
816-
"\n".join(env_var_check_errors))
814+
raise RuntimeError('Docker container environment setup has problems:\n\n'.join(env_var_check_errors))
817815

818816
if 'networks' in service:
819817
for network in service['networks']:
@@ -1286,9 +1284,9 @@ def run(self):
12861284
try:
12871285
config = GlobalConfig().config
12881286
self.check_system('start')
1289-
return_run_id = self.initialize_run()
12901287
self.initialize_folder(self._tmp_folder)
12911288
self.checkout_repository()
1289+
return_run_id = self.initialize_run()
12921290
self.initial_parse()
12931291
self.import_metric_providers()
12941292
self.populate_image_names()
@@ -1521,7 +1519,7 @@ def run(self):
15211519
error_helpers.log_error('Base exception occured in runner.py: ', e, successful_run_id)
15221520
finally:
15231521
if args.print_logs:
1524-
for container_id, std_out in runner.get_logs().items():
1525-
print(f"Container logs of '{container_id}':")
1522+
for container_id_outer, std_out in runner.get_logs().items():
1523+
print(f"Container logs of '{container_id_outer}':")
15261524
print(std_out)
1527-
print(f"\n-----------------------------\n")
1525+
print('\n-----------------------------\n')

tests/api/test_api_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_escape_dict():
3131
assert escaped['link'] == escaped_link
3232

3333
def test_escape_run():
34-
messy_run = Run(name="test<?>", url='testURL', email='testEmail', branch='', machine_id=0)
34+
messy_run = Run(name="test<?>", url='testURL', email='testEmail', branch='main', machine_id=0)
3535
escaped_name = 'test&lt;?&gt;'
3636
escaped = api_helpers.html_escape_multi(messy_run.model_copy())
3737

@@ -42,7 +42,7 @@ def test_escape_measurement():
4242
value=123,
4343
unit='mJ',
4444
repo='link<some_place>',
45-
branch='',
45+
branch='main',
4646
cpu='',
4747
commit_hash='',
4848
workflow='',

tests/test_functions.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,13 @@ def setup_runner(usage_scenario, docker_compose=None, uri='default', uri_type='f
6262
def run_until(runner, step):
6363
try:
6464
config = GlobalConfig().config
65-
return_run_id = runner.initialize_run()
66-
67-
# do a meaningless operation on return_run_id so pylint doesn't complain
68-
print(return_run_id)
69-
7065
runner.check_system('start')
7166
runner.initialize_folder(runner._tmp_folder)
7267
runner.checkout_repository()
68+
runner.initialize_run()
7369
runner.initial_parse()
70+
if step == 'import_metric_providers':
71+
return
7472
runner.import_metric_providers()
7573
runner.populate_image_names()
7674
runner.prepare_docker()

0 commit comments

Comments
 (0)