gitea actions

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2026-04-09 22:11:43 +02:00
parent 71bec8e86f
commit 2460fb32d6
9 changed files with 258 additions and 2 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

+5 -1
View File
@@ -10,6 +10,10 @@
"Every 5 min": "Alle 5 Minuten",
"Every 10 min": "Alle 10 Minuten",
"Every 30 min": "Alle 30 Minuten",
"Every Hour": "Jede Stunde"
"Every Hour": "Jede Stunde",
"Gitea URL": "Gitea URL",
"Owner": "Eigentümer",
"Repository": "Repository",
"API Token": "API Token"
}
}
+5 -1
View File
@@ -10,6 +10,10 @@
"Every 5 min": "Every 5 min",
"Every 10 min": "Every 10 min",
"Every 30 min": "Every 30 min",
"Every Hour": "Every Hour"
"Every Hour": "Every Hour",
"Gitea URL": "Gitea URL",
"Owner": "Owner",
"Repository": "Repository",
"API Token": "API Token"
}
}
+13
View File
@@ -36,6 +36,19 @@
],
"Tooltip": "Displays GitHub Copilot usage percentage as a gauge",
"UUID": "dev.mars3142.ulanzideck.collection.copilot"
},
{
"Name": "Gitea Actions",
"Icon": "assets/icons/gitea.png",
"PropertyInspectorPath": "property-inspector/gitea/inspector.html",
"States": [
{
"Name": "Default",
"Image": "assets/icons/gitea.png"
}
],
"Tooltip": "Shows the latest Gitea Actions run status for a repository",
"UUID": "dev.mars3142.ulanzideck.collection.gitea"
}
],
"OS": [
+152
View File
@@ -0,0 +1,152 @@
class GiteaAction {
constructor($UD, context) {
this.$UD = $UD;
this.context = context;
this.config = {
url: '',
owner: '',
repo: '',
token: '',
refreshRate: '4',
};
this.refreshTimer = null;
this.debounceTimer = 0;
}
setActive() {}
onClear() {
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
this.refreshTimer = null;
}
if (this.debounceTimer) {
clearTimeout(this.debounceTimer);
this.debounceTimer = 0;
}
}
setParams(jsn) {
this.config = Object.assign(this.config, (jsn && jsn.param) || {});
this.start();
}
onRun() {
if (this.config.url && this.config.owner && this.config.repo) {
this.$UD.openUrl(`${this.config.url}/${this.config.owner}/${this.config.repo}/actions`);
}
}
start() {
this.onClear();
this.fetchRun();
const duration = this.getRefreshDuration();
if (duration > 0) {
this.refreshTimer = setInterval(() => this.fetchRun(), duration * 60 * 1000);
}
}
async fetchRun() {
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(async () => {
if (!this.config.url || !this.config.owner || !this.config.repo) {
this.renderButton(null);
return;
}
try {
const headers = this.config.token ? { Authorization: `token ${this.config.token}` } : {};
const response = await fetch(
`${this.config.url}/api/v1/repos/${this.config.owner}/${this.config.repo}/actions/runs?limit=1`,
{ headers }
);
if (!response.ok) {
this.renderButton({ error: response.status });
return;
}
const data = await response.json();
const run = data.workflow_runs && data.workflow_runs[0];
this.renderButton(run || null);
} catch (e) {
this.renderButton({ error: 'ERR' });
}
}, 300);
}
renderButton(run) {
const canvas = document.createElement('canvas');
canvas.width = 196;
canvas.height = 196;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, 196, 196);
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
// Owner — top
const ownerLabel = this.config.owner || '?';
ctx.fillStyle = '#888888';
ctx.font = '18px "Source Han Sans SC"';
ctx.fillText(ownerLabel.length > 14 ? ownerLabel.slice(0, 13) + '…' : ownerLabel, 98, 22);
// Repo name — below owner
const repoLabel = this.config.repo || '?';
ctx.fillStyle = '#7ec8e3';
ctx.font = 'bold 22px "Source Han Sans SC"';
ctx.fillText(repoLabel.length > 12 ? repoLabel.slice(0, 11) + '…' : repoLabel, 98, 46);
// Status symbol — center
let symbol, color;
if (!run) {
symbol = '?';
color = '#666666';
} else if (run.error) {
symbol = '✗';
color = '#ff6b6b';
} else if (run.status === 'running' || run.status === 'waiting') {
symbol = '⟳';
color = '#f0c040';
} else if (run.conclusion === 'success') {
symbol = '✓';
color = '#6bff6b';
} else if (run.conclusion === 'failure') {
symbol = '✗';
color = '#ff6b6b';
} else if (run.conclusion === 'cancelled') {
symbol = '⊘';
color = '#aaaaaa';
} else {
symbol = '?';
color = '#666666';
}
ctx.fillStyle = color;
ctx.font = 'bold 76px "Source Han Sans SC"';
ctx.fillText(symbol, 98, 115);
// Run info — bottom, single line
if (run && !run.error) {
const branch = run.head_branch || '';
const num = `#${run.run_number}`;
const info = branch ? `${num} · ${branch}` : num;
ctx.fillStyle = '#aaaaaa';
ctx.font = 'bold 20px "Source Han Sans SC"';
ctx.fillText(info.length > 16 ? info.slice(0, 15) + '…' : info, 98, 172);
}
this.$UD.setBaseDataIcon(this.context, canvas.toDataURL('image/png'));
}
getRefreshDuration() {
switch (this.config.refreshRate) {
case '1': return 1;
case '2': return 2;
case '3': return 5;
case '4': return 10;
case '5': return 30;
case '6': return 60;
default: return 0;
}
}
}
+1
View File
@@ -13,6 +13,7 @@
<script src="./actions/PetrolAction.js"></script>
<script src="./actions/CopilotAction.js"></script>
<script src="./actions/GiteaAction.js"></script>
<script src="./app.js"></script>
</body>
</html>
+2
View File
@@ -15,6 +15,8 @@ $UD.onAdd(jsn => {
ACTION_CACHES[context] = new PetrolAction($UD, context);
} else if (name === 'copilot') {
ACTION_CACHES[context] = new CopilotAction($UD, context);
} else if (name === 'gitea') {
ACTION_CACHES[context] = new GiteaAction($UD, context);
}
}
+49
View File
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Gitea Actions</title>
<link rel="stylesheet" href="../../libs/css/uspi.css">
</head>
<body>
<div class="uspi-wrapper">
<form id="property-inspector">
<div class="uspi-item">
<div class="uspi-item-label" data-localize>Gitea URL</div>
<input class="uspi-item-value" type="text" name="url" placeholder="https://git.example.com" />
</div>
<div class="uspi-item">
<div class="uspi-item-label" data-localize>Owner</div>
<input class="uspi-item-value" type="text" name="owner" placeholder="owner" />
</div>
<div class="uspi-item">
<div class="uspi-item-label" data-localize>Repository</div>
<input class="uspi-item-value" type="text" name="repo" placeholder="repo-name" />
</div>
<div class="uspi-item">
<div class="uspi-item-label" data-localize>API Token</div>
<input class="uspi-item-value" type="password" name="token" placeholder="gitea_token..." />
</div>
<div class="uspi-item">
<div class="uspi-item-label" data-localize>Refresh Rate</div>
<select class="uspi-item-value" name="refreshRate">
<option value="0" data-localize>On Press</option>
<option value="1" data-localize>Every 1 min</option>
<option value="2" data-localize>Every 2 min</option>
<option value="3" data-localize>Every 5 min</option>
<option value="4" selected data-localize>Every 10 min</option>
<option value="5" data-localize>Every 30 min</option>
<option value="6" data-localize>Every Hour</option>
</select>
</div>
</form>
</div>
<script src="../../libs/js/constants.js"></script>
<script src="../../libs/js/eventEmitter.js"></script>
<script src="../../libs/js/utils.js"></script>
<script src="../../libs/js/ulanzideckApi.js"></script>
<script src="./inspector.js"></script>
</body>
</html>
+31
View File
@@ -0,0 +1,31 @@
let ACTION_SETTING = {};
let form = '';
$UD.connect('dev.mars3142.ulanzideck.collection.gitea');
$UD.onConnected(() => {
form = document.querySelector('#property-inspector');
form.addEventListener('input', Utils.debounce(() => {
const value = Utils.getFormValue(form);
ACTION_SETTING = { ...ACTION_SETTING, ...value };
$UD.sendParamFromPlugin(ACTION_SETTING);
}));
});
$UD.onAdd(jsn => {
if (jsn && jsn.param) settingSaveParam(jsn.param);
});
$UD.onParamFromApp(jsn => {
settingSaveParam((jsn && jsn.param) || {});
});
$UD.onParamFromPlugin(jsn => {
settingSaveParam((jsn && jsn.param) || {});
});
function settingSaveParam(params) {
ACTION_SETTING = { ...ACTION_SETTING, ...params };
if (form) Utils.setFormValue(ACTION_SETTING, form);
}