Просмотр исходного кода

web: use /builds endpoint for serving build info and
/builds/<build-id>/artifacts for artifacts

Shiv Tyagi 10 месяцев назад
Родитель
Сommit
b645fef913
3 измененных файлов с 81 добавлено и 24 удалено
  1. 47 5
      web/app.py
  2. 33 18
      web/static/js/index.js
  3. 1 1
      web/wsgi.py

+ 47 - 5
web/app.py

@@ -50,7 +50,7 @@ cmd_opts, cmd_args = parser.parse_args()
 # define directories
 basedir = os.path.abspath(cmd_opts.basedir)
 sourcedir = os.path.join(basedir, 'ardupilot')
-outdir_parent = os.path.join(basedir, 'builds')
+outdir_parent = os.path.join(basedir, 'artifacts')
 workdir_parent = os.path.join(basedir, 'workdir')
 
 appdir = os.path.dirname(__file__)
@@ -221,10 +221,15 @@ def home(token):
     app.logger.info('Rendering index.html')
     return render_template('index.html', token=token)
 
-@app.route("/builds/<path:name>")
-def download_file(name):
-    app.logger.info('Downloading %s' % name)
-    return send_from_directory(os.path.join(basedir,'builds'), name, as_attachment=False)
+@app.route("/builds/<string:build_id>/artifacts/<path:name>")
+def download_file(build_id, name):
+    path = os.path.join(
+        basedir,
+        'artifacts',
+        build_id,
+    )
+    app.logger.info('Downloading %s/%s' % (path, name))
+    return send_from_directory(path, name, as_attachment=False)
 
 @app.route("/boards_and_features/<string:vehicle_name>/<string:remote_name>/<string:commit_reference>", methods=['GET'])
 def boards_and_features(vehicle_name, remote_name, commit_reference):
@@ -321,5 +326,42 @@ def get_deafults(vehicle_name, remote_name, commit_reference, board_name):
     # omit the last two elements as they are always blank
     return jsonify(result[:-2])
 
+@app.route('/builds', methods=['GET'])
+def get_all_builds():
+    all_build_ids = manager.get_all_build_ids()
+    all_build_info = [
+        {
+            **manager.get_build_info(build_id).to_dict(),
+            'build_id': build_id
+        }
+        for build_id in all_build_ids
+    ]
+
+    all_build_info_sorted = sorted(
+        all_build_info,
+        key=lambda x: x['time_created'],
+        reverse=True,
+    )
+
+    return (
+        jsonify(all_build_info_sorted),
+        200
+    )
+
+@app.route('/builds/<string:build_id>', methods=['GET'])
+def get_build_by_id(build_id):
+    if not manager.build_exists(build_id):
+        response = {
+            'error': f'build with id {build_id} does not exist.',
+        }
+        return jsonify(response), 200
+
+    response = {
+        **manager.get_build_info(build_id).to_dict(),
+        'build_id': build_id
+    }
+
+    return jsonify(response), 200
+
 if __name__ == '__main__':
     app.run()

+ 33 - 18
web/static/js/index.js

@@ -8,7 +8,7 @@ function init() {
 
 function refresh_builds() {
     var xhr = new XMLHttpRequest();
-    xhr.open('GET', "/builds/status.json");
+    xhr.open('GET', "/builds");
 
     // disable cache, thanks to: https://stackoverflow.com/questions/22356025/force-cache-control-no-cache-in-chrome-via-xmlhttprequest-on-f5-reload
     xhr.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
@@ -31,9 +31,22 @@ function showFeatures(row_num) {
     return;
 }
 
-function updateBuildsTable(status_json) {
+function timeAgo(timestampStr) {
+    const timestamp = parseFloat(timestampStr);
+    const now = Date.now() / 1000;
+    const diff = now - timestamp;
+
+    if (diff < 0) return "In the future";
+
+    const hours = Math.floor(diff / 3600);
+    const minutes = Math.floor((diff % 3600) / 60);
+
+    return `${hours}h ${minutes}m`;
+}
+
+function updateBuildsTable(builds) {
     let output_container = document.getElementById('build_table_container');
-    if (Object.keys(status_json).length == 0) {
+    if (builds.length == 0) {
         output_container.innerHTML = `<div class="alert alert-success" role="alert" id="welcome_alert">
                                         <h4 class="alert-heading">Welcome!</h4>
                                         <p>No builds were queued to run on the server recently. To queue one, please click <a href="./add_build" class="alert-link">add a build</a>.</p>
@@ -48,38 +61,40 @@ function updateBuildsTable(status_json) {
 
     let table_body_html = '';
     let row_num = 0;
-    Object.keys(status_json).forEach((build_id) => {
-        let build_info = status_json[build_id];
+    builds.forEach((build_info) => {
         let status_color = 'primary';
-        if (build_info['status'] == 'SUCCESS') {
+        if (build_info['progress']['state'] == 'SUCCESS') {
             status_color = 'success';
-        } else if (build_info['status'] == 'PENDING') {
+        } else if (build_info['progress']['state'] == 'PENDING') {
             status_color = 'warning';
-        } else if (build_info['status'] == 'FAILURE' || build_info['status'] == 'ERROR') {
+        } else if (build_info['progress']['state'] == 'FAILURE' || build_info['progress']['state'] == 'ERROR') {
             status_color = 'danger';
         }
 
+        const features_string = build_info['selected_features'].join(', ')
+        const build_age = timeAgo(build_info['time_created'])
+
         table_body_html +=  `<tr>
-                                <td class="align-middle"><span class="badge text-bg-${status_color}">${build_info['status']}</span></td>
-                                <td class="align-middle">${build_info['age']}</td>
-                                <td class="align-middle"><a href="https://github.com/ArduPilot/ardupilot/commit/${build_info['git_hash_short']}">${build_info['git_hash_short']}</a></td>
+                                <td class="align-middle"><span class="badge text-bg-${status_color}">${build_info['progress']['state']}</span></td>
+                                <td class="align-middle">${build_age}</td>
+                                <td class="align-middle"><a href="https://github.com/ArduPilot/ardupilot/commit/${build_info['git_hash']}">${build_info['git_hash'].substring(0,8)}</a></td>
                                 <td class="align-middle">${build_info['board']}</td>
                                 <td class="align-middle">${build_info['vehicle']}</td>
                                 <td class="align-middle" id="${row_num}_features">
-                                        ${build_info['features'].substring(0, 100)}... 
-                                        <span id="${row_num}_features_all" style="display:none;">${build_info['features']}</span>
+                                        ${features_string.substring(0, 100)}... 
+                                        <span id="${row_num}_features_all" style="display:none;">${features_string}</span>
                                     <a href="javascript: showFeatures(${row_num});">show more</a>
                                 </td>
                                 <td class="align-middle">
                                     <div class="progress border" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
-                                        <div class="progress-bar bg-${status_color}" style="width: ${build_info['progress']}%">${build_info['progress']}%</div>
+                                        <div class="progress-bar bg-${status_color}" style="width: ${build_info['progress']['percent']}%">${build_info['progress']['percent']}%</div>
                                     </div>
                                 </td>
                                 <td class="align-middle">
-                                    <button class="btn btn-md btn-outline-primary m-1 tooltip-button" data-bs-toggle="tooltip" data-bs-animation="false" data-bs-title="View log" onclick="launchLogModal('${build_id}');">
+                                    <button class="btn btn-md btn-outline-primary m-1 tooltip-button" data-bs-toggle="tooltip" data-bs-animation="false" data-bs-title="View log" onclick="launchLogModal('${build_info['build_id']}');">
                                         <i class="bi bi-file-text"></i>
                                     </button>
-                                    <button class="btn btn-md btn-outline-primary m-1 tooltip-button" data-bs-toggle="tooltip" data-bs-animation="false" data-bs-title="Download build artifacts" onclick="window.location.href = '/builds/${build_id}/${build_id}.tar.gz';">
+                                    <button class="btn btn-md btn-outline-primary m-1 tooltip-button" data-bs-toggle="tooltip" data-bs-animation="false" data-bs-title="Download build artifacts" onclick="window.location.href = '/builds/${build_info['build_id']}/artifacts/${build_info['build_id']}.tar.gz';">
                                         <i class="bi bi-download"></i>
                                     </button>
                                 </td>
@@ -90,7 +105,7 @@ function updateBuildsTable(status_json) {
     let table_html =    `<table class="table table-hover table-light shadow">
                             <thead class="table-dark">
                                 <th scope="col" style="width: 5%">Status</th>
-                                <th scope="col" style="width: 5%">Age (hr:min)</th>
+                                <th scope="col" style="width: 5%">Age</th>
                                 <th scope="col" style="width: 5%">Git Hash</th>
                                 <th scope="col" style="width: 5%">Board</th>
                                 <th scope="col" style="width: 5%">Vehicle</th>
@@ -132,7 +147,7 @@ const LogFetch = (() => {
         }
 
         var xhr = new XMLHttpRequest();
-        xhr.open('GET', `/builds/${build_id}/build.log`);
+        xhr.open('GET', `/builds/${build_id}/artifacts/build.log`);
 
         // disable cache, thanks to: https://stackoverflow.com/questions/22356025/force-cache-control-no-cache-in-chrome-via-xmlhttprequest-on-f5-reload
         xhr.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");

+ 1 - 1
web/wsgi.py

@@ -8,7 +8,7 @@ cbs_basedir = os.environ.get('CBS_BASEDIR')
 
 if cbs_basedir:
     # Ensure base subdirectories exist
-    os.makedirs(os.path.join(cbs_basedir, 'builds'), exist_ok=True)
+    os.makedirs(os.path.join(cbs_basedir, 'artifacts'), exist_ok=True)
     os.makedirs(os.path.join(cbs_basedir, 'configs'), exist_ok=True)
 
     # Ensure remotes.json exists