feat: Initial commit

This commit is contained in:
ptrcnull 2022-06-20 06:17:48 +02:00
commit 60a0d7d7b9
Signed by: ptrcnull
GPG key ID: 411F7B30801DD9CA
31 changed files with 5192 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
node_modules

32
dist/common.js vendored Normal file
View file

@ -0,0 +1,32 @@
// native functions
const invoke = window.__TAURI__.invoke
const isLoggedIn = () => invoke('is_logged_in')
const logIn = token => invoke('log_in', { token })
const listRunners = () => invoke('list_runners')
const listJobsForRunner = id => invoke('list_jobs_for_runner', { runnerId: id })
const retryJob = (projectId, jobId) => invoke('retry_job', { projectId, jobId })
// shit helpers
const $ = (...args) => {
if (args.length === 1) return document.querySelector(args[0])
return args[0].querySelector(args[1])
}
const $$ = document.querySelectorAll.bind(document)
function initTemplate(template, data) {
const fragment = template.content.cloneNode(true)
Object.keys(data).forEach(key => {
const field = fragment.querySelector('.' + key)
const value = data[key]
if (typeof value === 'string') {
field.textContent = value
} else if (typeof value === 'number') {
field.textContent = value.toString()
} else if (value.link) {
const a = field.querySelector('a')
a.href = value.link
a.textContent = value.text
}
})
return fragment
}

38
dist/index.html vendored Normal file
View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<style>
#error-text {
color: red;
}
</style>
</head>
<body>
<h1>hi! pls give gitlab token</h1>
<input id="gitlab-token" placeholder="put token here">
<button id="login-button">Submit</button>
<span id="error-text"></span>
<script src="common.js"></script>
<script>
isLoggedIn().then(loggedIn => {
if (loggedIn) document.location.href = 'runners.html'
})
const errorText = $('#error-text')
$('#login-button').addEventListener('click', ev => {
errorText.textContent = ''
logIn($('#gitlab-token').value)
.then(() => {
document.location.href = 'runners.html'
})
.catch(err => {
errorText.textContent = err.toString()
})
})
</script>
</body>
</html>

155
dist/runner.html vendored Normal file
View file

@ -0,0 +1,155 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<style>
table {
height: 100vh;
width: 100vw;
}
td.status {
font-weight: bold;
}
tr.failed td.status {
color: #ff0055;
}
tr.success td.status {
color: #009e73;
}
tr.running td.status {
color: #56b4e9;
}
td.retry {
cursor: pointer;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Pipeline</th>
<th>Job name</th>
<th>Created at</th>
<th class="status">Status</th>
<th>Project</th>
<th>Runner</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
<template>
<tr>
<td class="pipeline"><a target="_blank"></a></td>
<td class="name"><a target="_blank"></a></td>
<td class="created_at"></td>
<td class="status"></td>
<td class="project"><a target="_blank"></a></td>
<td class="runner"></td>
<td class="retry">retry</td>
</tr>
</template>
<script src="common.js"></script>
<script>
const runnerId = new URLSearchParams(window.location.search).get('id')
// $('h2').textContent = runnerId
const template = $('template')
const table = $('tbody')
let notifications = true
$('th.status').addEventListener('click', ev => {
notifications = !notifications
window.__TAURI__.notification.sendNotification({
title: 'GitLab Jobs',
body: `Notifications are now ${notifications ? 'enabled' : 'disabled'}`
})
})
async function updateJobs() {
try {
let jobs
if (runnerId) {
jobs = await listJobsForRunner(runnerId)
} else {
const runners = await listRunners()
const promises = runners.map(runner => listJobsForRunner(runner.id.toString()).then(jobs => jobs.map(job => ({ ...job, runner }))))
jobs = (await Promise.all(promises)).flat()
}
jobs.forEach(job => {
const existing = $('#job' + job.id)
if (existing != null) {
const status = $(existing, '.status')
if (job.status !== status.textContent) {
status.textContent = job.status
existing.className = ''
existing.classList.add(job.status)
if (['success', 'failed'].includes(job.status)) {
window.__TAURI__.notification.sendNotification({
title: 'GitLab Jobs',
body: `Job ${job.name} for ${job.project.name}: ${job.status}`
})
}
}
return
}
const fragment = initTemplate(template, {
pipeline: {
link: job.pipeline.web_url,
text: '#' + job.pipeline.id.toString()
},
status: job.status,
name: {
link: job.web_url,
text: job.name
},
created_at: job.created_at.substr(0, 19).replace('T', ' '),
project: {
link: 'https://gitlab.com/' + job.project.path_with_namespace,
text: job.project.name
},
runner: job.runner.description
})
const row = $(fragment, 'tr')
row.id = 'job' + job.id
row.classList.add(job.status)
$(row, '.retry').addEventListener('click', ev => {
try {
retryJob(job.project.id.toString(), job.id)
} catch (err) {
console.error(err)
alert(err)
}
})
const before = Array.from(table.children).find(child => {
return parseInt(child.id.substr(3)) < job.id
})
if (before) {
table.insertBefore(row, before)
} else {
table.appendChild(row)
}
})
} catch (err) {
console.error(err)
alert(err)
}
}
updateJobs()
setInterval(updateJobs, 5000)
</script>
</body>
</html>

54
dist/runners.html vendored Normal file
View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<style>
table tbody tr:hover {
cursor: pointer;
}
</style>
</head>
<body>
<h1>Pick a runner</h1>
<table>
<thead>
<tr class="heading">
<th>ID</th>
<th>Description</th>
</tr>
</thead>
<tbody></tbody>
</table>
<a href="runner.html">Show all runners</a>
<template>
<tr>
<td class="id"></td>
<td class="desc"></td>
</tr>
</template>
<script src="common.js"></script>
<script>
const template = $('template')
const table = $('tbody')
listRunners()
.then(runners => {
runners.forEach(runner => {
const fragment = initTemplate(template, {
id: runner.id,
desc: runner.description
})
fragment.querySelector('tr').addEventListener('click', ev => {
console.log('cock')
document.location.href = 'runner.html?id=' + runner.id
})
table.appendChild(fragment)
})
})
.catch(err => alert(err))
</script>
</body>
</html>

37
dist/style.css vendored Normal file
View file

@ -0,0 +1,37 @@
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background-color: #1f1f1f;
color: #ffffff;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
table {
border: 1px solid #f0f0f0;
}
table td,
table th {
padding: 8px;
}
table tr:nth-child(even) {
background-color: #252525;
}
table tbody tr:hover {
background-color: #444444;
}
a {
color: #56b4e9;
}

310
package-lock.json generated Normal file
View file

@ -0,0 +1,310 @@
{
"name": "gitlab-jobs-tauri",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "gitlab-jobs-tauri",
"dependencies": {
"@tauri-apps/api": "^1.0.1"
},
"devDependencies": {
"@tauri-apps/cli": "^1.0.0"
}
},
"node_modules/@tauri-apps/api": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.1.tgz",
"integrity": "sha512-TJwKkXxtF52kN9Auu5TWD2AE4ssqTrsfdpIrixYwRb3gQ/FuYwvZjrMc9weYpgsW2cMhVNkvKgneNXF/4n04lw==",
"dependencies": {
"type-fest": "2.13.1"
},
"engines": {
"node": ">= 12.22.0",
"npm": ">= 6.6.0",
"yarn": ">= 1.19.1"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
}
},
"node_modules/@tauri-apps/cli": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.0.0.tgz",
"integrity": "sha512-4eHnk3p0xnCXd9Zel3kLvdiiSURnN98GMFvWUAdirm5AjyOjcx8TIET/jqRYmYKE5yd+LMQqYMUfHRwA6JJUkg==",
"dev": true,
"bin": {
"tauri": "tauri.js"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
},
"optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "1.0.0",
"@tauri-apps/cli-darwin-x64": "1.0.0",
"@tauri-apps/cli-linux-arm-gnueabihf": "1.0.0",
"@tauri-apps/cli-linux-arm64-gnu": "1.0.0",
"@tauri-apps/cli-linux-arm64-musl": "1.0.0",
"@tauri-apps/cli-linux-x64-gnu": "1.0.0",
"@tauri-apps/cli-linux-x64-musl": "1.0.0",
"@tauri-apps/cli-win32-ia32-msvc": "1.0.0",
"@tauri-apps/cli-win32-x64-msvc": "1.0.0"
}
},
"node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.0.0.tgz",
"integrity": "sha512-0ryomgLjdpylXypMPVXLU3PZCde3Sw5nwN4coUhBcHPBLFRb8QPet+nweVK/HiZ3mxg8WeIazvpx2s8hS0l2GQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-darwin-x64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.0.0.tgz",
"integrity": "sha512-oejvYUT4dEfzBi+FWMj+CMz4cZ6C2gEFHrUtKVLdTXr8Flj5UTwdB1YPGQjiOqk73LOI7cB/vXxb9DZT+Lrxgg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.0.0.tgz",
"integrity": "sha512-yAu78v8TeXNx/ETS5F2G2Uw/HX+LQvZkX94zNiqFsAj7snfWI/IqSUM52OBrdh/D0EC9NCdjUJ7Vuo32uxf7tg==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.0.0.tgz",
"integrity": "sha512-YFUN/S58AN317njAynzcQ+EHhRsCDXqmp5g9Oiqmcdg1vU7fPWZivVLc1WHz+0037C7JnsX5PtKpNYewP/+Oqw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.0.0.tgz",
"integrity": "sha512-al+TxMGoNVikEvRQfMyYE/mdjUcUNMo5brkCIAb+fL4rWQlAhAnYVzmg/rM8N4nhdXm1MOaYAagQmxr8898dNA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.0.0.tgz",
"integrity": "sha512-KQmYlYyGpn6/2kSl9QivWG6EIepm6PJd57e6IKmYwAyNhLr2XfGl1CLuocUQQgO+jprjT70HXp+MXD0tcB0+Sw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.0.0.tgz",
"integrity": "sha512-Qpaq5lZz569Aea6jfrRchgfEJaOrfLpCRBATcF8CJFFwVKmfCUcoV+MxbCIW30Zqw5Y06njC/ffa3261AV/ZIQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.0.0.tgz",
"integrity": "sha512-e2DzFqEMI+s+gv14UupdI91gPxTbUJTbbfQlTHdQlOsTk4HEZTsh+ibAYBcCLAaMRW38NEsFlAUe1lQA0iRu/w==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.0.0.tgz",
"integrity": "sha512-lWSs90pJeQX+L31IqIzmRhwLayEeyTh7mga0AxX8G868hvdLtcXCQA/rKoFtGdVLuHAx4+M+CBF5SMYb76xGYA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/type-fest": {
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.13.1.tgz",
"integrity": "sha512-hXYyrPFwETT2swFLHeoKtJrvSF/ftG/sA15/8nGaLuaDGfVAaq8DYFpu4yOyV4tzp082WqnTEoMsm3flKMI2FQ==",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
}
},
"dependencies": {
"@tauri-apps/api": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.0.1.tgz",
"integrity": "sha512-TJwKkXxtF52kN9Auu5TWD2AE4ssqTrsfdpIrixYwRb3gQ/FuYwvZjrMc9weYpgsW2cMhVNkvKgneNXF/4n04lw==",
"requires": {
"type-fest": "2.13.1"
}
},
"@tauri-apps/cli": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.0.0.tgz",
"integrity": "sha512-4eHnk3p0xnCXd9Zel3kLvdiiSURnN98GMFvWUAdirm5AjyOjcx8TIET/jqRYmYKE5yd+LMQqYMUfHRwA6JJUkg==",
"dev": true,
"requires": {
"@tauri-apps/cli-darwin-arm64": "1.0.0",
"@tauri-apps/cli-darwin-x64": "1.0.0",
"@tauri-apps/cli-linux-arm-gnueabihf": "1.0.0",
"@tauri-apps/cli-linux-arm64-gnu": "1.0.0",
"@tauri-apps/cli-linux-arm64-musl": "1.0.0",
"@tauri-apps/cli-linux-x64-gnu": "1.0.0",
"@tauri-apps/cli-linux-x64-musl": "1.0.0",
"@tauri-apps/cli-win32-ia32-msvc": "1.0.0",
"@tauri-apps/cli-win32-x64-msvc": "1.0.0"
}
},
"@tauri-apps/cli-darwin-arm64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.0.0.tgz",
"integrity": "sha512-0ryomgLjdpylXypMPVXLU3PZCde3Sw5nwN4coUhBcHPBLFRb8QPet+nweVK/HiZ3mxg8WeIazvpx2s8hS0l2GQ==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-darwin-x64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.0.0.tgz",
"integrity": "sha512-oejvYUT4dEfzBi+FWMj+CMz4cZ6C2gEFHrUtKVLdTXr8Flj5UTwdB1YPGQjiOqk73LOI7cB/vXxb9DZT+Lrxgg==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.0.0.tgz",
"integrity": "sha512-yAu78v8TeXNx/ETS5F2G2Uw/HX+LQvZkX94zNiqFsAj7snfWI/IqSUM52OBrdh/D0EC9NCdjUJ7Vuo32uxf7tg==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-linux-arm64-gnu": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.0.0.tgz",
"integrity": "sha512-YFUN/S58AN317njAynzcQ+EHhRsCDXqmp5g9Oiqmcdg1vU7fPWZivVLc1WHz+0037C7JnsX5PtKpNYewP/+Oqw==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-linux-arm64-musl": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.0.0.tgz",
"integrity": "sha512-al+TxMGoNVikEvRQfMyYE/mdjUcUNMo5brkCIAb+fL4rWQlAhAnYVzmg/rM8N4nhdXm1MOaYAagQmxr8898dNA==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-linux-x64-gnu": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.0.0.tgz",
"integrity": "sha512-KQmYlYyGpn6/2kSl9QivWG6EIepm6PJd57e6IKmYwAyNhLr2XfGl1CLuocUQQgO+jprjT70HXp+MXD0tcB0+Sw==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-linux-x64-musl": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.0.0.tgz",
"integrity": "sha512-Qpaq5lZz569Aea6jfrRchgfEJaOrfLpCRBATcF8CJFFwVKmfCUcoV+MxbCIW30Zqw5Y06njC/ffa3261AV/ZIQ==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-win32-ia32-msvc": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.0.0.tgz",
"integrity": "sha512-e2DzFqEMI+s+gv14UupdI91gPxTbUJTbbfQlTHdQlOsTk4HEZTsh+ibAYBcCLAaMRW38NEsFlAUe1lQA0iRu/w==",
"dev": true,
"optional": true
},
"@tauri-apps/cli-win32-x64-msvc": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.0.0.tgz",
"integrity": "sha512-lWSs90pJeQX+L31IqIzmRhwLayEeyTh7mga0AxX8G868hvdLtcXCQA/rKoFtGdVLuHAx4+M+CBF5SMYb76xGYA==",
"dev": true,
"optional": true
},
"type-fest": {
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.13.1.tgz",
"integrity": "sha512-hXYyrPFwETT2swFLHeoKtJrvSF/ftG/sA15/8nGaLuaDGfVAaq8DYFpu4yOyV4tzp082WqnTEoMsm3flKMI2FQ=="
}
}
}

12
package.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "gitlab-jobs-tauri",
"scripts": {
"tauri": "tauri"
},
"dependencies": {
"@tauri-apps/api": "^1.0.1"
},
"devDependencies": {
"@tauri-apps/cli": "^1.0.0"
}
}

3
src-tauri/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

4270
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

30
src-tauri/Cargo.toml Normal file
View file

@ -0,0 +1,30 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["ptrcnull"]
license = ""
repository = ""
default-run = "app"
edition = "2021"
rust-version = "1.57"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.0.0", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0", features = ["api-all"] }
gitlab = "0.1500.0"
chrono = { version = "0.4", default-features = false, features = ["clock", "serde"] }
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = [ "custom-protocol" ]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = [ "tauri/custom-protocol" ]

3
src-tauri/build.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

BIN
src-tauri/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
src-tauri/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src-tauri/icons/icon.icns Normal file

Binary file not shown.

BIN
src-tauri/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
src-tauri/icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,77 @@
// additional queries not defined in the gitlab crate
use gitlab::{api::{endpoint_prelude::Method, Endpoint}, *};
use serde::{Serialize, Deserialize};
use chrono::{DateTime, Utc};
pub struct RunnerJobs {
pub runner_id: String,
}
impl Endpoint for RunnerJobs {
fn method(&self) -> Method {
Method::GET
}
fn endpoint(&self) -> std::borrow::Cow<'static, str> {
format!("runners/{}/jobs?order_by=id", self.runner_id).into()
}
}
pub struct Runners {}
impl Endpoint for Runners {
fn method(&self) -> Method {
Method::GET
}
fn endpoint(&self) -> std::borrow::Cow<'static, str> {
format!("runners").into()
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Job {
/// The ID of the job.
pub id: JobId,
/// The status of the job.
pub status: StatusState,
pub stage: String,
/// The name of the job.
pub name: String,
#[serde(rename = "ref")]
/// The name of the reference that was tested.
pub ref_: Option<String>,
pub tag: bool,
pub coverage: Option<f64>,
/// When the job was created or marked as pending.
pub created_at: DateTime<Utc>,
/// When the job was started.
pub started_at: Option<DateTime<Utc>>,
/// When the job completed.
pub finished_at: Option<DateTime<Utc>>,
/// The user which ran the job.
pub user: Option<User>,
/// The artifact file uploaded from the job.
pub artifacts_file: Option<JobArtifactFile>,
/// The commit the job tested.
pub commit: RepoCommit,
/// The runner which ran the job.
pub runner: Option<Runner>,
/// The pipeline the job belongs to.
pub pipeline: PipelineBasic,
pub allow_failure: bool,
pub duration: Option<f64>,
pub artifacts: Option<Vec<JobArtifact>>,
pub artifacts_expire_at: Option<DateTime<Utc>>,
pub web_url: String,
pub project: ProjectInfo,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectInfo {
pub id: ProjectId,
pub name: String,
pub name_with_namespace: String,
pub path: String,
pub path_with_namespace: String,
}

103
src-tauri/src/main.rs Normal file
View file

@ -0,0 +1,103 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use std::sync::Mutex;
use gitlab::{api::{Query, AsyncQuery, projects::jobs::RetryJob}, Runner, Gitlab, AsyncGitlab};
use gitlab_api::Job;
pub mod gitlab_api;
struct AppState {
gl: Option<AsyncGitlab>
}
impl AppState {
fn default() -> Self {
Self {
gl: None
}
}
fn get_client(&self) -> Result<AsyncGitlab, String> {
match &self.gl {
Some(client) => Ok(client.clone()),
None => {
Err("not logged in".to_string())
}
}
}
}
type State<'a> = tauri::State<'a, Mutex<AppState>>;
#[tauri::command]
fn is_logged_in(state: State) -> bool {
state.lock().unwrap().gl.is_some()
}
#[tauri::command]
async fn log_in(token: String, state: State<'_>) -> Result<(), String> {
let gitlab_client = gitlab::GitlabBuilder::new("gitlab.com", token).build_async().await;
match gitlab_client {
Ok(client) => {
state.lock().unwrap().gl.replace(client);
Ok(())
},
Err(error) => Err(error.to_string())
}
}
#[tauri::command]
async fn list_runners(state: State<'_>) -> Result<Vec<Runner>, String> {
let client = {
let appstate = state.lock().unwrap();
appstate.get_client()
}?;
let endpoint = gitlab_api::Runners{};
let response: Result<Vec<Runner>, _> = endpoint.query_async(&client).await;
response.map_err(|err| err.to_string())
}
#[tauri::command]
async fn list_jobs_for_runner(runner_id: String, state: State<'_>) -> Result<Vec<Job>, String> {
let client = {
let appstate = state.lock().unwrap();
appstate.get_client()
}?;
let endpoint = gitlab_api::RunnerJobs{runner_id};
let response: Result<Vec<Job>, _> = endpoint.query_async(&client).await;
response.map_err(|err| err.to_string())
}
#[tauri::command]
async fn retry_job(project_id: String, job_id: u64, state: State<'_>) -> Result<Job, String> {
let client = {
let appstate = state.lock().unwrap();
appstate.get_client()
}?;
let endpoint = RetryJob::builder().project(project_id).job(job_id).build().unwrap();
let response: Result<Job, _> = endpoint.query_async(&client).await;
response.map_err(|err| err.to_string())
}
fn main() {
let context = tauri::generate_context!();
tauri::Builder::default()
.manage(Mutex::new(AppState::default()))
.invoke_handler(tauri::generate_handler![
is_logged_in,
log_in,
list_runners,
list_jobs_for_runner,
retry_job
])
.menu(tauri::Menu::os_default(&context.package_info().name))
.run(context)
.expect("error while running tauri application");
}

67
src-tauri/tauri.conf.json Normal file
View file

@ -0,0 +1,67 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "",
"beforeDevCommand": "",
"devPath": "http://localhost:8000",
"distDir": "../dist",
"withGlobalTauri": true
},
"package": {
"productName": "gitlab-jobs-tauri",
"version": "0.1.0"
},
"tauri": {
"allowlist": {
"all": true
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"identifier": "com.tauri.dev",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": null
},
"updater": {
"active": false
},
"windows": [
{
"fullscreen": false,
"height": 600,
"resizable": true,
"title": "GitLab Jobs",
"width": 800
}
]
}
}