first trigger of builds via GUI

This commit is contained in:
andreas 2023-09-05 21:15:07 +02:00
parent 56b5598c15
commit 0e0be14415
7 changed files with 239 additions and 37 deletions

3
webinstall/cibuild.css Normal file
View File

@ -0,0 +1,3 @@
.hidden{
display: none;
}

38
webinstall/cibuild.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<script type="module" src="cibuild.js"></script>
<link rel="stylesheet" href="cibuild.css"/>
</head>
<body>
<h1>Build your own ESP32-NMEA2000</h1>
<div class="xrow">
<label>Board type</label>
<input type="text" id="environment" value="m5stack-atom-generic">
</div>
<div class="xrow">
<label>Build Flags</label>
<input type="text" id="buildflags" value="">
</div>
<div class="xrow">
<button id="start">Start</button>
</div>
<div class="xrow">
<label>Pipeline Id</label>
<div id="pipeline">---</div>
</div>
<div class="xrow">
<label>Status</label>
<div id="status">---</div>
</div>
<div class="xrow hidden">
<a target="_" id="link">WebStatus</a>
</div>
<div class="xrow hidden">
<button id="download">Download</button>
</div>
<iframe id="dlframe" width="1" height="1"></iframe>
</body>
</html>

98
webinstall/cibuild.js Normal file
View File

@ -0,0 +1,98 @@
import { setButtons,fillValues, setValue, buildUrl, fetchJson, setVisible, enableEl } from "./helper";
(function(){
const STATUS_INTERVAL=2000;
const CURRENT_PIPELINE='pipeline';
let API="cibuild.php";
let currentPipeline=undefined;
let downloadUrl=undefined;
let timer=undefined;
const fetchStatus=()=>{
if (currentPipeline === undefined) return;
fetchJson(API,{api:'status',pipeline:currentPipeline})
.then((st)=>{
setValue('status',st.status);
let l=document.getElementById('link');
if (l){
if (st.status_url){
l.setAttribute('href',st.status_url);
setVisible(l.parentElement,true);
}
else{
setVisible(l.parentElement,false);
}
}
if (st.status === 'success'){
enableEl('start',true);
fetchJson(API,{api:'artifacts',pipeline:currentPipeline})
.then((ar)=>{
if (! ar.items || ar.items.length < 1){
throw new Error("no download link");
}
downloadUrl=ar.items[0].url;
setVisible(document.getElementById('download'),true,true);
})
.catch((err)=>alert("Unable to get build result: "+err));
return;
}
else{
setVisible(document.getElementById('download'),false,true);
}
timer=window.setTimeout(fetchStatus,STATUS_INTERVAL)
})
.catch((e)=>{
timer=window.setTimeout(fetchStatus,STATUS_INTERVAL);
})
}
const setCurrentPipeline=(pipeline)=>{
currentPipeline=pipeline;
window.localStorage.setItem(CURRENT_PIPELINE,pipeline);
};
const startBuild=()=>{
let param={};
currentPipeline=undefined;
if (timer) window.clearTimeout(timer);
timer=undefined;
fillValues(param,['environment','buildflags']);
setValue('status','requested');
fetchJson(API,Object.assign({
api:'start'},param))
.then((json)=>{
if (json.status === 'error'){
throw new Error("unable to create job "+(json.error||''));
}
if (!json.id) throw new Error("unable to create job, no id");
setCurrentPipeline(json.id);
setValue('pipeline',currentPipeline);
setValue('status',json.status);
enableEl('start',false);
timer=window.setTimeout(fetchStatus,STATUS_INTERVAL);
})
.catch((err)=>{
setValue('status','error');
enableEl('start',true);
alert(err);
});
}
const runDownload=()=>{
if (! downloadUrl) return;
let df=document.getElementById('dlframe');
if (df){
df.setAttribute('src',null);
df.setAttribute('src',downloadUrl);
}
}
const btConfig={
start:startBuild,
download:runDownload
};
window.onload=()=>{
setButtons(btConfig);
currentPipeline=window.localStorage.getItem(CURRENT_PIPELINE);
if (currentPipeline){
setValue('pipeline',currentPipeline);
enableEl('start',false);
fetchStatus();
}
}
})();

96
webinstall/helper.js Normal file
View File

@ -0,0 +1,96 @@
const getParam = (key) => {
let value = RegExp("" + key + "[^&]+").exec(window.location.search);
// Return the unescaped value minus everything starting from the equals sign or an empty string
return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./, "") : "");
};
/**
* add an HTML element
* @param {*} type
* @param {*} clazz
* @param {*} parent
* @param {*} text
* @returns
*/
const addEl = (type, clazz, parent, text) => {
let el = document.createElement(type);
if (clazz) {
if (!(clazz instanceof Array)) {
clazz = clazz.split(/ */);
}
clazz.forEach(function (ce) {
el.classList.add(ce);
});
}
if (text) el.textContent = text;
if (parent) parent.appendChild(el);
return el;
}
/**
* call a function for each matching element
* @param {*} selector
* @param {*} cb
*/
const forEachEl = (selector, cb) => {
let arr = document.querySelectorAll(selector);
for (let i = 0; i < arr.length; i++) {
cb(arr[i]);
}
}
const setButtons=(config)=>{
for (let k in config){
let bt=document.getElementById(k);
if (bt){
bt.addEventListener('click',config[k]);
}
}
}
const fillValues=(values,items)=>{
items.forEach((it)=>{
let e=document.getElementById(it);
if (e){
values[it]=e.value; //TODO: type of el
}
})
};
const setValue=(id,value)=>{
let el=document.getElementById(id);
if (! el) return;
if (el.tagName == 'DIV'){
el.textContent=value;
return;
}
if (el.tagName == 'INPUT'){
el.value=value;
}
}
const buildUrl=(url,pars)=>{
let delim=(url.match("[?]"))?"&":"?";
for (let k in pars){
url+=delim;
delim="&";
url+=encodeURIComponent(k);
url+="=";
url+=encodeURIComponent(pars[k]);
}
return url;
}
const fetchJson=(url,pars)=>{
let furl=buildUrl(url,pars);
return fetch(furl).then((rs)=>rs.json());
}
const setVisible=(el,vis,useParent)=>{
if (! el) return;
if (useParent) el=el.parentElement;
if (! el) return;
if (vis) el.classList.remove('hidden');
else el.classList.add('hidden');
}
const enableEl=(id,en)=>{
let el=document.getElementById(id);
if (!el) return;
if (en) el.disabled=false;
else el.disabled=true;
}
export { getParam, addEl, forEachEl,setButtons,fillValues, setValue,buildUrl,fetchJson,setVisible, enableEl }

View File

@ -9,9 +9,9 @@
</head> </head>
<body> <body>
</body>
<div class="heading"></div> <div class="heading"></div>
<div class="console"></div> <div class="console"></div>
<div class="content"></div> <div class="content"></div>
<div id="terminal"></div> <div id="terminal"></div>
</body>
</html> </html>

View File

@ -1,10 +1,10 @@
import {XtermOutputHandler} from "./installUtil.js"; import {XtermOutputHandler} from "./installUtil.js";
import ESPInstaller from "./installUtil.js"; import ESPInstaller from "./installUtil.js";
import { addEl, getParam } from "./helper.js";
(function(){ (function(){
let espLoaderTerminal; let espLoaderTerminal;
let espInstaller; let espInstaller;
let releaseData={}; let releaseData={};
const addEl=ESPInstaller.addEl; //shorter typing
let showConsole; let showConsole;
let hideConsole; let hideConsole;
const enableConsole=(enable,disableBoth)=>{ const enableConsole=(enable,disableBoth)=>{
@ -134,8 +134,8 @@ import ESPInstaller from "./installUtil.js";
showError("your browser does not support the ESP flashing (no serial)"); showError("your browser does not support the ESP flashing (no serial)");
return; return;
} }
let user = window.gitHubUser||ESPInstaller.getParam('user'); let user = window.gitHubUser||getParam('user');
let repo = window.gitHubRepo || ESPInstaller.getParam('repo'); let repo = window.gitHubRepo || getParam('repo');
if (!user || !repo) { if (!user || !repo) {
alert("missing parameter user or repo"); alert("missing parameter user or repo");
} }

View File

@ -72,39 +72,6 @@ class ESPInstaller{
// Return the unescaped value minus everything starting from the equals sign or an empty string // Return the unescaped value minus everything starting from the equals sign or an empty string
return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./,"") : ""); return decodeURIComponent(!!value ? value.toString().replace(/^[^=]+./,"") : "");
}; };
/**
* add an HTML element
* @param {*} type
* @param {*} clazz
* @param {*} parent
* @param {*} text
* @returns
*/
static addEl(type, clazz, parent, text) {
let el = document.createElement(type);
if (clazz) {
if (!(clazz instanceof Array)) {
clazz = clazz.split(/ */);
}
clazz.forEach(function (ce) {
el.classList.add(ce);
});
}
if (text) el.textContent = text;
if (parent) parent.appendChild(el);
return el;
}
/**
* call a function for each matching element
* @param {*} selector
* @param {*} cb
*/
static forEachEl(selector,cb){
let arr=document.querySelectorAll(selector);
for (let i=0;i<arr.length;i++){
cb(arr[i]);
}
}
static checkAvailable(){ static checkAvailable(){
if (! navigator.serial || ! navigator.serial.requestPort) return false; if (! navigator.serial || ! navigator.serial.requestPort) return false;
return true; return true;