Skip to content

Commit

Permalink
overhaul UI and add jobs
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticroentgen committed Jul 30, 2024
1 parent 88253b5 commit 0e542cd
Show file tree
Hide file tree
Showing 9 changed files with 680 additions and 184 deletions.
317 changes: 313 additions & 4 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@headlessui/vue": "^1.7.22",
"@heroicons/vue": "^2.1.5",
"axios": "^1.7.2",
"core-js": "^3.8.3",
"vue": "^3.2.13"
"moment": "^2.30.1",
"vue": "^3.2.13",
"vue-router": "^4.4.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
Expand All @@ -19,7 +23,8 @@
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
"eslint-plugin-vue": "^8.0.3",
"naive-ui": "^2.39.0"
},
"eslintConfig": {
"root": true,
Expand Down
21 changes: 10 additions & 11 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<style>
body {
background-color: #0d1117;
}
a {
color: #3DC2EC;
}
a:visited {
color: #3DC2EC;
}
</style>
<style>
body {
font-family: Avenir, Helvetica, Arial, sans-serif;
background-color: rgb(24 24 28);
}
.dotted-underline {
text-decoration: underline;
text-decoration-style: dotted; /* This makes the underline dotted */
}
</style>
</head>
<body>
<noscript>
Expand Down
37 changes: 28 additions & 9 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
<template>
<RunnersView />
<n-config-provider :theme="darkTheme">

<n-card title="Github Actions Runners UI" style="margin-bottom: 16px">
<n-tabs type="line" animated>
<n-tab-pane name="runners" tab="Show Runners">
<RunnersView/>
</n-tab-pane>
<n-tab-pane name="jobs" tab="Show Jobs">
<JobsView/>
</n-tab-pane>
</n-tabs>
</n-card>
</n-config-provider>
</template>

<script>
import RunnersView from './components/Runners.vue'
import { darkTheme } from 'naive-ui'
import RunnersView from './components/Runners.vue'
import JobsView from './components/Jobs.vue'
export default {
name: 'App',
components: {
RunnersView
export default {
name: 'App',
components: {
RunnersView,
JobsView
},
setup() {
return {
darkTheme
}
}
}
}
</script>

<style>
Expand All @@ -20,7 +39,7 @@ export default {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
margin-top: 60px;
color: white;
}
</style>
31 changes: 31 additions & 0 deletions src/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export function parseRunnerState(statusCode) {
const RunnerStatus = {
0: "Unknown",
1: "🏗️ Creation queued",
2: "🏗️ Created",
3: "✅ Provisioned",
4: "⏳ Processing",
5: "💣 Deletion queued",
6: "🪦 Deleted",
7: "⚠️ Failure",
8: "VanishedOnCloud",
9: "🧹 Cleanup"
};
if (Object.prototype.hasOwnProperty.call(RunnerStatus, statusCode)) {
return RunnerStatus[statusCode];
}
return "Unknown"; // Default case if status code is not found
}

export function parseJobState(statusCode) {
const JobState = {
0: "Unknown",
1: "⏸️ Queued",
2: "▶️ In Progress",
3: "✅ Completed"
};
if (Object.prototype.hasOwnProperty.call(JobState, statusCode)) {
return JobState[statusCode];
}
return "Unknown"; // Default case if status code is not found
}
81 changes: 81 additions & 0 deletions src/components/Jobs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<n-form
inline
:label-width="80"
>
<n-form-item>
<n-checkbox v-model:checked="hideComplete">Hide completed jobs</n-checkbox>
</n-form-item>
<n-form-item>
<n-button @click="fetchJobs">Refresh</n-button>
</n-form-item>
</n-form>
<n-table striped>
<thead>
<tr>
<th>Owner</th>
<th>Repository</th>
<th>Status</th>
<th>Requested Size</th>
<th>Requested Profile</th>
<th>Queued</th>
<th>In progress</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
<tr v-for="j in filteredJobs" v-bind:key="j.jobId">
<td>{{ j.owner }}</td>
<td>{{ j.repository }}</td>
<td>{{ getJobState(j.state) }}</td>
<td>{{ j.requestedSize }}</td>
<td>{{ j.requestedProfile }}</td>
<td>{{ j.queueTime }}</td>
<td>{{ j.inProgressTime }}</td>
<td>{{ j.completeTime }}</td>
</tr>
</tbody>
</n-table>
</template>

<script>
import axios from 'axios';
import {parseJobState} from '../common'
export default {
name: 'RunnersView',
data() {
return {
jobs: [],
hideComplete: false
};
},
mounted() {
this.fetchJobs()
},
computed: {
filteredJobs() {
return this.jobs
.filter(job => {
if(this.hideComplete === true) {
return job.state !== 3
} else {
return true;
}
});
}
},
methods: {
async fetchJobs() {
console.info("Loading Jobs")
let jobResp = await axios.get(process.env.VUE_APP_API_URL + '/api/get-jobs');
this.jobs = jobResp.data;
},
getJobState(code) {
return parseJobState(code)
},
}
}
</script>
134 changes: 134 additions & 0 deletions src/components/RunnerDetails.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<template>
<h3>Runner Details</h3>
<n-table>
<tr>
<th>Hostname</th><td>{{ getStatusIcon(runner.isOnline) }} {{ runner.hostname }}</td>
<th>Status</th><td>{{ getRunnerState(runner.lastState) }}</td>
</tr>
<tr>
<th>IPv4 Address</th><td>{{ runner.iPv4 }}</td>
<th>Cloud Provider</th><td>{{ runner.cloud }}</td>
</tr>
<tr>
<th>Size</th><td>{{ runner.size }}</td>
<th>Profile</th><td>{{ runner.profile }}</td>
</tr>
<tr>
<th>Created</th><td><span class="dotted-underline" :title="runner.lifecycle[0].eventTimeUtc">{{ getTimeSince(runner.lifecycle[0].eventTimeUtc) }}</span></td>
<th>Last State Update</th><td><span class="dotted-underline" :title="runner.lifecycle[runner.lifecycle.length-1].eventTimeUtc">{{ getTimeSince(runner.lifecycle[runner.lifecycle.length-1].eventTimeUtc) }}</span></td>
</tr>
</n-table>



<h3>Job Details</h3>
<div v-if="runner.job">
<n-table>
<tr>
<th>Repository</th><td><n-button text tag="a" type="primary" :href="'https://github.com/' + runner.job.repository" target="_blank">{{ runner.job.repository}}</n-button></td>
<th>Status</th><td>{{ getJobState(runner.job.state)}}</td>
</tr>
<tr v-if="ghLoaded">
<th>GitHub Workflow</th><td><n-button text tag="a" type="primary" :href="githubJob.html_url" target="_blank">{{ githubJob.workflow_name}} => {{ githubJob.name }}</n-button></td>
<th>GitHub Status</th><td>{{ githubJob.status }} [{{ githubJob.conclusion}}]</td>
</tr>
<tr v-if="ghLoaded">
<th>Labels</th><td>
<ul v-for="l in githubJob.labels" v-bind:key="l">
<li>{{ l }}</li>
</ul>
</td>
<th>Branch/Ref</th><td>{{ githubJob.head_branch }} [<n-button text tag="a" type="primary" :href="getCommitUrl(githubJob.head_sha, runner.job.repository)" target="_blank"><code>{{ githubJob.head_sha}}</code></n-button>]</td>
</tr>
<tr v-else>
<td colSpan=2>Loading GitHub job...</td>
</tr>
</n-table>
</div>
<div v-else>
No Job assigned jet.
</div>

<h3>Runner Timeline</h3>
<n-table striped>
<thead>
<tr>
<th>Event Time</th>
<th>Status</th>
<th>Event</th>
</tr>
</thead>
<tbody>
<tr v-for="event in runner.lifecycle" :key="event.runnerLifecycleId">
<td><span class="dotted-underline" :title="event.eventTimeUtc" >{{ getEventTime(event.eventTimeUtc) }}</span></td>
<td>{{ getRunnerState(event.status)}}</td>
<td>{{ event.event }}</td>
</tr>
</tbody>
</n-table>
</template>
<script>
import {parseRunnerState, parseJobState} from '../common';
import moment from 'moment';
import axios from 'axios';
export default {
name: 'RunnerDetails',
data() {
return {
ghLoaded: false,
githubJob: {}
}
},
mounted() {
if(this.runner.job) {
this.loadGitHubjob()
}
},
props: {
runner: Object
},
computed: {
},
methods: {
getRunnerState(statusCode) {
return parseRunnerState(statusCode)
},
getTimeSince(timestamp) {
return moment(timestamp).fromNow();
},
getJobState(code) {
return parseJobState(code)
},
getCommitUrl(sha, repo) {
return "https://github.com/" + repo + "/commit/" + sha
},
getStatusIcon(isOnline) {
return isOnline ? "🟢" : "🔴";
},
getEventTime(eventTime) {
if(eventTime === this.runner.lifecycle[0].eventTimeUtc) {
return new moment(eventTime).format("HH:mm:ss")
} else {
var x = new moment(this.runner.lifecycle[0].eventTimeUtc)
var y = new moment(eventTime)
var duration = moment.duration(x.diff(y));
return duration.humanize();
}
},
async loadGitHubjob() {
try {
let resp = await axios.get(this.runner.job.jobUrl);
this.githubJob = resp.data;
this.ghLoaded = true;
}
catch {
console.log("unable to query github")
}
}
}
}
</script>
Loading

0 comments on commit 0e542cd

Please sign in to comment.