*: Format with prettier

Use latest prettier as devDependency so tools will use (or can be
configured to use) the npm provided version.
The unique config change is to use single quotes instead of double
quotes.
This commit is contained in:
Simone Gotti 2022-02-23 16:13:44 +01:00
parent ca0c494425
commit 3f2c57394a
55 changed files with 2001 additions and 1461 deletions

3
.prettierrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
singleQuote: true,
};

View File

@ -1,5 +1,3 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
presets: ['@vue/cli-plugin-babel/preset'],
};

View File

@ -2,12 +2,8 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": [
"types/*"
]
"*": ["types/*"]
}
},
"include": [
"./src/**/*"
]
"include": ["./src/**/*"]
}

16
package-lock.json generated
View File

@ -1977,6 +1977,13 @@
"yallist": "^2.1.2"
}
},
"prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true,
"optional": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -9849,11 +9856,10 @@
"dev": true
},
"prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true,
"optional": true
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"pretty-error": {
"version": "2.1.2",

View File

@ -29,6 +29,7 @@
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"prettier": "2.5.1",
"sass-loader": "^8.0.2",
"tailwindcss": "^1.9.6",
"vue-template-compiler": "^2.6.12"

View File

@ -1,6 +1,3 @@
module.exports = {
"plugins": [
require('tailwindcss')('tailwind.js'),
require('autoprefixer')(),
]
}
plugins: [require('tailwindcss')('tailwind.js'), require('autoprefixer')()],
};

View File

@ -1,7 +1,9 @@
<template>
<div id="app">
<nav class="bg-gray-800 p-3 text-white">
<div class="container flex items-center justify-between flex-wrap bg-gray-800">
<div
class="container flex items-center justify-between flex-wrap bg-gray-800"
>
<div class="mr-6">
<router-link
class="font-semibold flex items-center flex-shrink-0 text-xl tracking-tight"
@ -28,12 +30,12 @@
</div>
<div
class="w-full block flex-grow lg:flex lg:items-center lg:w-auto"
:class="{'hidden' : !navActive}"
:class="{ hidden: !navActive }"
>
<div class="text-sm lg:flex-grow"></div>
<div v-if="user" class="relative mr-3">
<button
v-click-outside="() => createDropdownActive = false"
v-click-outside="() => (createDropdownActive = false)"
@click="createDropdownActive = !createDropdownActive"
class="relative flex items-center focus:outline-none"
>
@ -49,7 +51,8 @@
<router-link
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
to="/neworganization"
>New Organization</router-link>
>New Organization</router-link
>
</li>
</ul>
</div>
@ -57,7 +60,7 @@
<div v-if="user" class="relative">
<div class="flex">
<button
v-click-outside="() => userDropdownActive = false"
v-click-outside="() => (userDropdownActive = false)"
@click="userDropdownActive = !userDropdownActive"
class="relative flex items-center focus:outline-none"
>
@ -98,18 +101,30 @@
</div>
</div>
<div v-else class="navbar-item">
<router-link class="btn btn-blue" to="/register">Sign up</router-link>
<router-link class="ml-2 btn btn-blue" to="/login">Login</router-link>
<router-link class="btn btn-blue" to="/register"
>Sign up</router-link
>
<router-link class="ml-2 btn btn-blue" to="/login"
>Login</router-link
>
</div>
</div>
</div>
</nav>
<div v-if="error" class="container h-screen" role="alert">
<div v-if="error" class="h-full flex justify-center items-center" role="alert">
<div
v-if="error"
class="h-full flex justify-center items-center"
role="alert"
>
<div v-if="error" class="w-full" role="alert">
<div class="bg-red-500 text-white font-bold rounded-t px-4 py-2">Error</div>
<div class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700">
<div class="bg-red-500 text-white font-bold rounded-t px-4 py-2">
Error
</div>
<div
class="border border-t-0 border-red-400 rounded-b bg-red-100 px-4 py-3 text-red-700"
>
<p class="mb-8">Failed to fetch data: {{ error }}</p>
<button class="btn btn-red" @click="reload()">Retry</button>
</div>
@ -123,48 +138,46 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import { mapGetters } from "vuex";
import { mapGetters } from 'vuex';
import { ownerSettingsLink } from "@/util/link.js";
import { ownerSettingsLink } from '@/util/link.js';
export default {
name: "App",
name: 'App',
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
components: {},
computed: {
...mapGetters(["error", "user"])
...mapGetters(['error', 'user']),
},
data() {
return {
routerActive: true,
navActive: false,
userDropdownActive: false,
createDropdownActive: false
createDropdownActive: false,
};
},
watch: {
$route: function () {
this.userDropdownActive = false;
this.createDropdownActive = false;
}
},
},
// method to reload current view from https://github.com/vuejs/vue-router/issues/296#issuecomment-356530037
methods: {
ownerSettingsLink: ownerSettingsLink,
reload() {
this.$store.dispatch("setError", null);
this.$store.dispatch('setError', null);
this.routerActive = false;
this.$nextTick(() => (this.routerActive = true));
}
}
},
},
};
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@ -8,19 +8,21 @@
type="text"
placeholder="Organization name"
v-model="orgName"
>
/>
</div>
</div>
<div class="mb-4">
<label>
<input type="checkbox" v-model="orgIsPrivate">
<input type="checkbox" v-model="orgIsPrivate" />
Private
</label>
</div>
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
@click="createOrg()"
>Create Organization</button>
>
Create Organization
</button>
<div
v-if="createOrgError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
@ -32,19 +34,19 @@
</template>
<script>
import { createOrganization } from "@/util/data.js";
import { createOrganization } from '@/util/data.js';
import { ownerLink } from "@/util/link.js";
import { ownerLink } from '@/util/link.js';
export default {
components: {},
name: "createorganization",
name: 'createorganization',
props: {},
data() {
return {
createOrgError: null,
orgIsPrivate: false,
orgName: null
orgName: null,
};
},
methods: {
@ -54,9 +56,9 @@ export default {
async createOrg() {
this.resetErrors();
let visibility = "public";
let visibility = 'public';
if (this.orgIsPrivate) {
visibility = "private";
visibility = 'private';
}
let { error } = await createOrganization(this.orgName, visibility);
@ -65,14 +67,11 @@ export default {
return;
}
this.$router.push(ownerLink("org", this.orgName));
}
this.$router.push(ownerLink('org', this.orgName));
},
created: async function() {}
},
created: async function () {},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,17 +7,18 @@
type="text"
placeholder="Project Name"
v-model="projectName"
>
/>
<div class="mb-4">
<label>
<input type="checkbox" v-model="projectIsPrivate">
<input type="checkbox" v-model="projectIsPrivate" />
Private
</label>
</div>
<div class="mb-4">
<label class="checkbox">
<input type="checkbox" v-model="pass_vars_to_forked_pr" />
Pass variables to run even if triggered by PR from forked repo (DANGEROUS)
Pass variables to run even if triggered by PR from forked repo
(DANGEROUS)
</label>
</div>
<div class="mb-3 flex items-center">
@ -31,31 +32,48 @@
v-for="(rs, index) in remoteSources"
v-bind:key="rs.id"
:value="index"
>{{ rs.name }}</option>
>
{{ rs.name }}
</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path>
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2"
>
<svg
class="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path
d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
></path>
</svg>
</div>
</div>
<button
class="ml-3 btn btn-blue"
v-bind:class="{ 'spinner': fetchRemoteReposLoading }"
v-bind:class="{ spinner: fetchRemoteReposLoading }"
:disabled="selectedRemoteSourceIndex == null"
@click="fetchRemoteRepos()"
>Fetch remote repositories</button>
>
Fetch remote repositories
</button>
</div>
<div v-if="remoteRepos.length">
<h4 class="text-xl">Available remote repositories</h4>
<remoterepos :remoterepos="remoteRepos" v-on:reposelected="repoSelected($event)"/>
<remoterepos
:remoterepos="remoteRepos"
v-on:reposelected="repoSelected($event)"
/>
<button
class="btn btn-blue"
v-bind:class="{ 'spinner': createProjectLoading }"
v-bind:class="{ spinner: createProjectLoading }"
:disabled="!createProjectButtonEnabled"
@click="createProject()"
>Create Project</button>
>
Create Project
</button>
<div
v-if="createProjectError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
@ -72,20 +90,20 @@ import {
fetchCurrentUser,
fetchRemoteSources,
createProject,
userRemoteRepos
} from "@/util/data.js";
userRemoteRepos,
} from '@/util/data.js';
import { projectLink } from "@/util/link.js";
import { projectLink } from '@/util/link.js';
import remoterepos from "@/components/remoterepos.vue";
import remoterepos from '@/components/remoterepos.vue';
export default {
components: { remoterepos },
name: "createproject",
name: 'createproject',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
@ -98,15 +116,15 @@ export default {
remoteSources: null,
remoteRepos: [],
selectedRemoteSourceIndex: null,
projectName: "",
projectName: '',
projectIsPrivate: false,
remoteRepoPath: null
remoteRepoPath: null,
};
},
computed: {
createProjectButtonEnabled: function () {
return this.projectName.length && this.remoteRepoPath;
}
},
},
watch: {},
methods: {
@ -143,7 +161,7 @@ export default {
let { data, error } = await userRemoteRepos(remoteSource.id);
this.stopFetchRemoteReposLoading();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remoteRepos = data;
@ -155,11 +173,11 @@ export default {
if (this.projectgroupref) {
refArray = [...refArray, ...this.projectgroupref];
}
let parentref = refArray.join("/");
let parentref = refArray.join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectIsPrivate) {
visibility = "private";
visibility = 'private';
}
let remoteSource = this.remoteSources[this.selectedRemoteSourceIndex];
@ -186,12 +204,12 @@ export default {
this.$router.push(
projectLink(this.ownertype, this.ownername, projectref)
);
}
},
},
created: async function () {
let { data, error } = await fetchCurrentUser();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.user = data;
@ -199,7 +217,7 @@ export default {
// TODO(sgotti) filter only remote source where the user has a linked account
({ data, error } = await fetchRemoteSources());
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
@ -215,10 +233,8 @@ export default {
}
this.remoteSources = remoteSources;
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -5,9 +5,11 @@
<button
@click="clicked"
class="relative flex items-center focus:outline-none bg-green-500 hover:bg-green-600 text-white font-semibold hover:text-white py-2 px-4 border border-green-700 rounded rounded-r-none"
>{{ buttonValue }}</button>
>
{{ buttonValue }}
</button>
<button
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
class="relative flex items-center focus:outline-none bg-green-500 hover:bg-green-600 text-white font-semibold hover:text-white py-2 px-4 border border-l-0 border-green-700 rounded rounded-l-none"
>
@ -24,14 +26,16 @@
href="#"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
@click="setButton('project')"
>New Project</a>
>New Project</a
>
</li>
<li>
<a
href="#"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
@click="setButton('projectgroup')"
>New Project Group</a>
>New Project Group</a
>
</li>
</ul>
</div>
@ -40,28 +44,28 @@
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
export default {
components: {},
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
name: "createprojectbutton",
name: 'createprojectbutton',
props: {},
data() {
return {
dropdownActive: false,
type: "project"
type: 'project',
};
},
computed: {
buttonValue: function () {
if (this.type == "project") {
return "New Project";
}
return "New Project Group";
if (this.type == 'project') {
return 'New Project';
}
return 'New Project Group';
},
},
methods: {
setButton(type) {
@ -69,12 +73,10 @@ export default {
this.dropdownActive = false;
},
clicked() {
this.$emit("click", this.type);
}
}
this.$emit('click', this.type);
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,20 +7,22 @@
type="text"
placeholder="Project Group Name"
v-model="projectGroupName"
>
/>
<div class="mb-4">
<label>
<input type="checkbox" v-model="projectGroupIsPrivate">
<input type="checkbox" v-model="projectGroupIsPrivate" />
Private
</label>
</div>
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
v-bind:class="{ 'spinner': createProjectGroupLoading }"
v-bind:class="{ spinner: createProjectGroupLoading }"
:disabled="!createProjectGroupButtonEnabled"
@click="createProjectGroup()"
>Create ProjectGroup</button>
>
Create ProjectGroup
</button>
<div
v-if="createProjectGroupError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
@ -32,31 +34,31 @@
</template>
<script>
import { createProjectGroup } from "@/util/data.js";
import { createProjectGroup } from '@/util/data.js';
import { projectGroupLink } from "@/util/link.js";
import { projectGroupLink } from '@/util/link.js';
export default {
components: {},
name: "createprojectgroup",
name: 'createprojectgroup',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
createProjectGroupError: null,
createProjectGroupLoading: false,
createProjectGroupLoadingTimeout: null,
projectGroupName: "",
projectGroupIsPrivate: false
projectGroupName: '',
projectGroupIsPrivate: false,
};
},
computed: {
createProjectGroupButtonEnabled: function () {
return this.projectGroupName.length;
}
},
},
methods: {
resetErrors() {
@ -78,11 +80,11 @@ export default {
if (this.projectgroupref) {
refArray = [...refArray, ...this.projectgroupref];
}
let parentref = refArray.join("/");
let parentref = refArray.join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectGroupIsPrivate) {
visibility = "private";
visibility = 'private';
}
this.startProjectGroupLoading();
@ -104,12 +106,9 @@ export default {
this.$router.push(
projectGroupLink(this.ownertype, this.ownername, projectgroupref)
);
}
}
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -12,17 +12,31 @@
</h1>
<form
class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
@submit.prevent="$emit('createSource', { token, name, type, clientId, clientSecret, url, skipVerify, sshHostKey, skipSshHostKeyCheck })"
@submit.prevent="
$emit('createSource', {
token,
name,
type,
clientId,
clientSecret,
url,
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
})
"
>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="token">Agola Admin token</label>
<label class="block text-sm font-bold mb-2" for="token"
>Agola Admin token</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="token"
type="password"
placeholder="Agola Admin token"
v-model="token"
>
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="type">Type</label>
@ -37,7 +51,9 @@
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="name">Source name</label>
<label class="block text-sm font-bold mb-2" for="name"
>Source name</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="name"
@ -45,17 +61,19 @@
type="text"
placeholder="Source name (only numbers, letters and -)"
v-model="name"
>
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="url">Source API URL</label>
<label class="block text-sm font-bold mb-2" for="url"
>Source API URL</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="url"
type="text"
placeholder="API URL"
v-model="url"
>
/>
</div>
<div class="mb-4 flex flex-row">
<input
@ -63,11 +81,15 @@
type="checkbox"
class="h-6 w-6 border border-gray-300 rounded-md checked:bg-blue-600 checked:border-transparent focus:outline-none"
v-model="skipVerify"
/>
<label class="text-sm mt-auto font-bold mx-2" for="skip_verify"
>Skip TLS certificate validation</label
>
<label class="text-sm mt-auto font-bold mx-2" for="skip_verify">Skip TLS certificate validation</label>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="ssh_key">Source Public SSH Key</label>
<label class="block text-sm font-bold mb-2" for="ssh_key"
>Source Public SSH Key</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="ssh_key"
@ -75,7 +97,7 @@
autocomplete="off"
placeholder="Public SSH key"
v-model="sshHostKey"
>
/>
</div>
<div class="mb-4 flex flex-row">
<input
@ -83,45 +105,52 @@
type="checkbox"
class="h-6 w-6 border border-gray-300 rounded-md checked:bg-blue-600 checked:border-transparent focus:outline-none"
v-model="skipSshHostKeyCheck"
/>
<label class="text-sm mt-auto font-bold mx-2" for="skip_ssh_key_check"
>Skip SSH host key check</label
>
<label class="text-sm mt-auto font-bold mx-2" for="skip_ssh_key_check">Skip SSH host key check</label>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="client_id">Source client ID</label>
<label class="block text-sm font-bold mb-2" for="client_id"
>Source client ID</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="client_id"
type="text"
placeholder="Client ID"
v-model="clientId"
>
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="client_secret">Source client secret</label>
<label class="block text-sm font-bold mb-2" for="client_secret"
>Source client secret</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="client_secret"
type="password"
placeholder="Client secret"
v-model="clientSecret"
>
/>
</div>
<div class="flex justify-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>Create</button>
>
Create
</button>
</div>
</form>
</div>
</template>
<script>
import { GITHUB_SSH_KEY, GITHUB_API_URL } from "@/util/data"
import { GITHUB_SSH_KEY, GITHUB_API_URL } from '@/util/data';
export default {
name: "CreateSourceForm",
name: 'CreateSourceForm',
data: function () {
return {
name: null,
@ -137,7 +166,7 @@ export default {
},
watch: {
type: function (value) {
if (value === "github") {
if (value === 'github') {
this.url = GITHUB_API_URL;
this.sshHostKey = GITHUB_SSH_KEY;
this.skipVerify = false;
@ -147,5 +176,3 @@ export default {
},
};
</script>

View File

@ -43,18 +43,18 @@
</template>
<script>
import { apiurl, fetch } from "@/util/auth";
import AnsiUp from "ansi_up";
import { apiurl, fetch } from '@/util/auth';
import AnsiUp from 'ansi_up';
export default {
name: "Log",
name: 'Log',
props: {
show: Boolean,
runid: String,
taskid: String,
setup: Boolean,
step: Number,
stepphase: String
stepphase: String,
},
computed: {},
data() {
@ -65,7 +65,7 @@ export default {
fetchAbort: null,
items: [],
lastitem: "",
lastitem: '',
lines: [],
formatter: formatter,
es: null,
@ -73,7 +73,7 @@ export default {
streaming: false,
done: false,
logExists: null,
error: null
error: null,
};
},
methods: {
@ -83,7 +83,7 @@ export default {
}
let follow = false;
if (this.stepphase == "running") {
if (this.stepphase == 'running') {
follow = true;
}
@ -94,14 +94,14 @@ export default {
this.logExists = null;
this.error = null;
let path = "/logs?runID=" + this.runid + "&taskID=" + this.taskid;
let path = '/logs?runID=' + this.runid + '&taskID=' + this.taskid;
if (this.setup) {
path += "&setup";
path += '&setup';
} else {
path += "&step=" + this.step;
path += '&step=' + this.step;
}
if (follow) {
path += "&follow";
path += '&follow';
}
try {
@ -111,7 +111,7 @@ export default {
this.streaming = true;
const reader = res.body.getReader();
let lastline = "";
let lastline = '';
let j = 0;
for (;;) {
let { done, value } = await reader.read();
@ -122,12 +122,12 @@ export default {
return;
}
let data = new TextDecoder("utf-8").decode(value, { stream: true });
let data = new TextDecoder('utf-8').decode(value, { stream: true });
let part = "";
let part = '';
for (var i = 0; i < data.length; i++) {
let c = data.charAt(i);
if (c == "\r") {
if (c == '\r') {
// replace lastline from start, simulating line feed (go to start of line)
// this isn't perfect since the previous line contents could have
// been written using different colors and this will lose them but
@ -136,17 +136,17 @@ export default {
lastline.slice(0, j) + part + lastline.slice(j + part.length);
j = 0;
this.lastitem = this.formatter.ansi_to_html(lastline);
part = "";
} else if (c == "\n") {
part = '';
} else if (c == '\n') {
lastline =
lastline.slice(0, j) + part + lastline.slice(j + part.length);
j += part.length;
this.lastitem = this.formatter.ansi_to_html(lastline);
this.items.push(this.lastitem);
this.lastitem = "";
lastline = "";
this.lastitem = '';
lastline = '';
j = 0;
part = "";
part = '';
} else {
part += c;
}
@ -174,7 +174,7 @@ export default {
this.fetchAbort.abort();
}
this.fetchAbort = new AbortController();
}
},
},
watch: {
show: function (post, pre) {
@ -193,14 +193,14 @@ export default {
if (this.fetching) {
return;
}
if (post == "running") {
if (post == 'running') {
this.abortFetch();
this.getLogs(true);
} else {
this.abortFetch();
this.getLogs(false);
}
}
},
},
created: function () {
this.fetchAbort = new AbortController();
@ -217,6 +217,6 @@ export default {
if (this.es !== null) {
this.es.close();
}
}
},
};
</script>

View File

@ -5,50 +5,53 @@
@submit.prevent="$emit('login', { username, password })"
>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="username">Username</label>
<label class="block text-sm font-bold mb-2" for="username"
>Username</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="username"
type="text"
placeholder="Username"
v-model="username"
>
/>
</div>
<div class="mb-6">
<label class="block text-sm font-bold mb-2" for="password">Password</label>
<label class="block text-sm font-bold mb-2" for="password"
>Password</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 mb-3 leading-tight focus:outline-none focus:shadow-outline"
id="password"
type="password"
placeholder="******************"
v-model="password"
>
/>
</div>
<div class="flex justify-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>{{action}} with {{name}}</button>
>
{{ action }} with {{ name }}
</button>
</div>
</form>
</div>
</template>
<script>
export default {
name: "LoginForm",
name: 'LoginForm',
props: {
action: String,
name: String
name: String,
},
data: function () {
return {
username: null,
password: null
password: null,
};
}
},
};
</script>

View File

@ -12,39 +12,38 @@
</template>
<script>
import { fetchOrgMembers } from "@/util/data.js";
import { fetchOrgMembers } from '@/util/data.js';
export default {
components: {},
name: "orgmembers",
name: 'orgmembers',
props: {
orgname: String
orgname: String,
},
data() {
return {
members: []
members: [],
};
},
watch: {
$route: async function () {
this.fetchOrgMembers(this.orgname);
}
},
},
methods: {
async fetchOrgMembers(orgname) {
let { data, error } = await fetchOrgMembers(orgname);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.members = data.members;
}
},
},
created: function () {
this.fetchOrgMembers(this.orgname);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -8,51 +8,61 @@
<span class="mx-2">/</span>
</li>
<li>
<router-link :to="ownerLink(ownertype, ownername)">{{ownername}}</router-link>
<router-link :to="ownerLink(ownertype, ownername)">{{
ownername
}}</router-link>
</li>
<li v-for="(ref, i) in projectref" v-bind:key="i">
<span class="mx-2">/</span>
<router-link
v-if="i + 1 < projectref.length"
:to="projectGroupLink(ownertype, ownername, projectref.slice(0, i+1))"
>{{ref}}</router-link>
:to="
projectGroupLink(ownertype, ownername, projectref.slice(0, i + 1))
"
>{{ ref }}</router-link
>
<router-link
v-else
:to="projectLink(ownertype, ownername, projectref.slice(0, i + 1))"
>{{ref}}</router-link>
>{{ ref }}</router-link
>
</li>
<li v-for="(ref, i) in projectgroupref" v-bind:key="i">
<span class="mx-2">/</span>
<router-link
:to="projectGroupLink(ownertype, ownername, projectgroupref.slice(0, i+1))"
>{{ref}}</router-link>
:to="
projectGroupLink(
ownertype,
ownername,
projectgroupref.slice(0, i + 1)
)
"
>{{ ref }}</router-link
>
</li>
</ol>
</nav>
</template>
<script>
import { ownerLink, projectLink, projectGroupLink } from "@/util/link.js";
import { ownerLink, projectLink, projectGroupLink } from '@/util/link.js';
export default {
name: "projbreadcrumbs",
name: 'projbreadcrumbs',
components: {},
props: {
ownertype: String,
ownername: String,
projectref: Array,
projectgroupref: Array
projectgroupref: Array,
},
methods: {
ownerLink: ownerLink,
projectLink: projectLink,
projectGroupLink: projectGroupLink
}
projectGroupLink: projectGroupLink,
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -20,7 +20,9 @@
Private
</label>
</div>
<button class="btn btn-blue" @click="updateProjectGroup()">Update</button>
<button class="btn btn-blue" @click="updateProjectGroup()">
Update
</button>
<div
v-if="updateProjectGroupError"
@ -35,14 +37,22 @@
<div class="panel">
<p class="panel-title">Secrets</p>
<div class="p-4">
<projectsecrets :secrets="secrets" :allsecrets="allsecrets" type="projectgroup" />
<projectsecrets
:secrets="secrets"
:allsecrets="allsecrets"
type="projectgroup"
/>
</div>
</div>
<div class="panel">
<p class="panel-title">Variables</p>
<div class="p-4">
<projectvars :variables="variables" :allvariables="allvariables" type="projectgroup" />
<projectvars
:variables="variables"
:allvariables="allvariables"
type="projectgroup"
/>
</div>
</div>
@ -56,16 +66,13 @@
>
<p>
This operation
<strong>CANNOT</strong> be undone.
This operation will remove
<strong>CANNOT</strong> be undone. This operation will remove
<strong>{{ projectGroupPath }}</strong>
</p>
</div>
<label class="block mb-2">
Please type the project group name for confirmation:
<span
class="text-red-500 font-bold"
>{{ projectGroupName }}</span>
<span class="text-red-500 font-bold">{{ projectGroupName }}</span>
</label>
<div class="mb-4">
<input
@ -79,7 +86,9 @@
class="btn btn-red"
@click="deleteProjectGroup()"
:disabled="!deleteButtonEnabled"
>Delete Project Group</button>
>
Delete Project Group
</button>
</div>
</div>
<div
@ -98,21 +107,21 @@ import {
fetchSecrets,
fetchVariables,
updateProjectGroup,
deleteProjectGroup
} from "@/util/data.js";
deleteProjectGroup,
} from '@/util/data.js';
import { projectGroupLink } from "@/util/link.js";
import { projectGroupLink } from '@/util/link.js';
import projectsecrets from "@/components/projectsecrets";
import projectvars from "@/components/projectvars";
import projectsecrets from '@/components/projectsecrets';
import projectvars from '@/components/projectvars';
export default {
components: { projectsecrets, projectvars },
name: "projectgroupsettings",
name: 'projectgroupsettings',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
@ -124,7 +133,7 @@ export default {
allsecrets: [],
variables: [],
allvariables: [],
projectGroupNameToDelete: ""
projectGroupNameToDelete: '',
};
},
computed: {
@ -132,8 +141,8 @@ export default {
return this.projectgroupref[this.projectgroupref.length - 1];
},
projectGroupPath: function () {
return ["", this.ownertype, this.ownername, ...this.projectgroupref].join(
"/"
return ['', this.ownertype, this.ownername, ...this.projectgroupref].join(
'/'
);
},
deleteButtonEnabled: function () {
@ -141,7 +150,7 @@ export default {
},
isRootProjectGroup() {
return this.projectgroupref.length == 0;
}
},
},
methods: {
resetErrors() {
@ -154,12 +163,12 @@ export default {
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
...this.projectgroupref,
].join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectGroupIsPrivate) {
visibility = "private";
visibility = 'private';
}
let { error } = await updateProjectGroup(
projectgroupref,
@ -175,8 +184,8 @@ export default {
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
...this.projectgroupref,
].join('/');
if (this.projectGroupNameToDelete == this.projectGroupName) {
let { error } = await deleteProjectGroup(projectgroupref);
@ -192,70 +201,68 @@ export default {
)
);
}
}
},
},
created: async function () {
let projectgroupref = [
this.ownertype,
this.ownername,
...this.projectgroupref
].join("/");
...this.projectgroupref,
].join('/');
let { data, error } = await fetchProjectGroup(projectgroupref);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.projectGroup = data;
this.projectGroupIsPrivate = this.projectGroup.visibility == "private";
this.projectGroupIsPrivate = this.projectGroup.visibility == 'private';
({ data, error } = await fetchSecrets(
"projectgroup",
'projectgroup',
projectgroupref,
false
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.secrets = data;
({ data, error } = await fetchSecrets(
"projectgroup",
'projectgroup',
projectgroupref,
true
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allsecrets = data;
({ data, error } = await fetchVariables(
"projectgroup",
'projectgroup',
projectgroupref,
false
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.variables = data;
({ data, error } = await fetchVariables(
"projectgroup",
'projectgroup',
projectgroupref,
true
));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allvariables = data;
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -2,12 +2,19 @@
<div>
<h4 class="text-xl my-3">Projects</h4>
<div v-if="fetchProjectsLoading" class="ml-6 flex w-48">
<div v-bind:class="{ 'spinner': fetchProjectsLoading }"></div>
<div v-bind:class="{ spinner: fetchProjectsLoading }"></div>
</div>
<ul v-else-if="projects.length > 0">
<li class="mb-2 border rounded-l" v-for="project in projects" v-bind:key="project.id">
<li
class="mb-2 border rounded-l"
v-for="project in projects"
v-bind:key="project.id"
>
<div class="pl-4 py-4 flex items-center">
<router-link class="item" :to="projectLink(ownertype, ownername, ref(project.name))">
<router-link
class="item"
:to="projectLink(ownertype, ownername, ref(project.name))"
>
<span class="font-bold">{{ project.name }}</span>
</router-link>
</div>
@ -19,7 +26,7 @@
<h4 class="text-xl my-3">Project Groups</h4>
<div v-if="fetchProjectGroupsLoading" class="ml-6 flex w-48">
<div v-bind:class="{ 'spinner': fetchProjectGroupsLoading }"></div>
<div v-bind:class="{ spinner: fetchProjectGroupsLoading }"></div>
</div>
<ul v-else-if="projectgroups.length > 0">
<li
@ -44,18 +51,18 @@
<script>
import {
fetchProjectGroupProjects,
fetchProjectGroupSubgroups
} from "@/util/data.js";
fetchProjectGroupSubgroups,
} from '@/util/data.js';
import { projectLink, projectGroupLink } from "@/util/link.js";
import { projectLink, projectGroupLink } from '@/util/link.js';
export default {
components: {},
name: "Projects",
name: 'Projects',
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
@ -65,7 +72,7 @@ export default {
fetchProjectsLoading: false,
projects: [],
projectgroups: []
projectgroups: [],
};
},
watch: {
@ -76,7 +83,7 @@ export default {
this.fetchAbort = new AbortController();
this.fetchProjects(this.ownertype, this.ownername);
this.fetchProjectGroups(this.ownertype, this.ownername);
}
},
},
methods: {
startFetchProjectsLoading() {
@ -107,7 +114,7 @@ export default {
this.startFetchProjectsLoading();
let { data, error, aborted } = await fetchProjectGroupProjects(
projectgroupref.join("/"),
projectgroupref.join('/'),
this.fetchAbort.signal
);
this.stopFetchProjectsLoading();
@ -115,7 +122,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.projects = data;
@ -127,7 +134,7 @@ export default {
}
this.startFetchProjectGroupsLoading();
let { data, error, aborted } = await fetchProjectGroupSubgroups(
projectgroupref.join("/"),
projectgroupref.join('/'),
this.fetchAbort.signal
);
this.stopFetchProjectGroupsLoading();
@ -135,13 +142,13 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.projectgroups = data;
},
projectLink: projectLink,
projectGroupLink: projectGroupLink
projectGroupLink: projectGroupLink,
},
created: function () {
this.fetchAbort = new AbortController();
@ -152,9 +159,8 @@ export default {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,33 +7,34 @@
<hr class="my-6 border-t" />
<h5 class="text-2xl">All secrets (local and inherited)</h5>
<secrets v-if="allsecrets.length" :secrets="allsecrets" :showparentpath="true" />
<secrets
v-if="allsecrets.length"
:secrets="allsecrets"
:showparentpath="true"
/>
<span v-else>No secrets</span>
</div>
</template>
<script>
import secrets from "@/components/secrets";
import secrets from '@/components/secrets';
export default {
components: { secrets },
name: "projectsecrets",
name: 'projectsecrets',
props: {
secrets: Array,
allsecrets: Array,
type: String
type: String,
},
computed: {
typetitle() {
if (this.type == "project") return "Project";
if (this.type == "projectgroup") return "Project group";
return "";
}
}
if (this.type == 'project') return 'Project';
if (this.type == 'projectgroup') return 'Project group';
return '';
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -21,7 +21,8 @@
<div class="mb-4">
<label class="checkbox">
<input type="checkbox" v-model="project.pass_vars_to_forked_pr" />
Pass variables to run even if triggered by PR from forked repo (DANGEROUS)
Pass variables to run even if triggered by PR from forked repo
(DANGEROUS)
</label>
</div>
<button class="btn btn-blue" @click="updateProject()">Update</button>
@ -38,14 +39,22 @@
<div class="panel">
<p class="panel-title">Secrets</p>
<div class="p-4">
<projectsecrets :secrets="secrets" :allsecrets="allsecrets" type="project" />
<projectsecrets
:secrets="secrets"
:allsecrets="allsecrets"
type="project"
/>
</div>
</div>
<div class="panel">
<p class="panel-title">Variables</p>
<div class="p-4">
<projectvars :variables="variables" :allvariables="allvariables" type="project" />
<projectvars
:variables="variables"
:allvariables="allvariables"
type="project"
/>
</div>
</div>
@ -59,16 +68,13 @@
>
<p>
This operation
<strong>CANNOT</strong> be undone.
This operation will remove
<strong>CANNOT</strong> be undone. This operation will remove
<strong>{{ projectPath }}</strong>
</p>
</div>
<label class="block mb-2">
Please type the project name for confirmation:
<span
class="text-red-500 font-bold"
>{{ projectName }}</span>
<span class="text-red-500 font-bold">{{ projectName }}</span>
</label>
<div class="mb-4">
<input
@ -82,7 +88,9 @@
class="btn btn-red"
@click="deleteProject()"
:disabled="!deleteButtonEnabled"
>Delete Project</button>
>
Delete Project
</button>
<div
v-if="deleteProjectError"
@ -93,21 +101,30 @@
</div>
</div>
<div class="p-4 border-t">
<h4 class="mb-4 title text-xl">Change remote repository linked account</h4>
<h4 class="mb-4 title text-xl">
Change remote repository linked account
</h4>
<div
class="mb-4 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded"
role="alert"
>
<p>This operation will change the linked account associated with the project remote repository to the current user linked account</p>
<p>
This operation will change the linked account associated with the
project remote repository to the current user linked account
</p>
</div>
<button class="btn btn-red" @click="updateRepoLinkedAccount()">Change</button>
<button class="btn btn-red" @click="updateRepoLinkedAccount()">
Change
</button>
<div
v-if="updateRepoLinkedAccountError"
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert"
>
<span class="block sm:inline">{{ updateRepoLinkedAccountError }}</span>
<span class="block sm:inline">{{
updateRepoLinkedAccountError
}}</span>
</div>
</div>
</div>
@ -121,21 +138,21 @@ import {
fetchVariables,
updateProject,
deleteProject,
projectUpdateRepoLinkedAccount
} from "@/util/data.js";
projectUpdateRepoLinkedAccount,
} from '@/util/data.js';
import { projectGroupLink } from "@/util/link.js";
import { projectGroupLink } from '@/util/link.js';
import projectsecrets from "@/components/projectsecrets";
import projectvars from "@/components/projectvars";
import projectsecrets from '@/components/projectsecrets';
import projectvars from '@/components/projectvars';
export default {
components: { projectsecrets, projectvars },
name: "projectsettings",
name: 'projectsettings',
props: {
ownertype: String,
ownername: String,
projectref: Array
projectref: Array,
},
data() {
return {
@ -148,7 +165,7 @@ export default {
allsecrets: [],
variables: [],
allvariables: [],
projectNameToDelete: ""
projectNameToDelete: '',
};
},
computed: {
@ -156,11 +173,11 @@ export default {
return this.projectref[this.projectref.length - 1];
},
projectPath: function () {
return ["", this.ownertype, this.ownername, ...this.projectref].join("/");
return ['', this.ownertype, this.ownername, ...this.projectref].join('/');
},
deleteButtonEnabled: function () {
return this.projectNameToDelete == this.projectName;
}
},
},
methods: {
resetErrors() {
@ -174,12 +191,12 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
let visibility = "public";
let visibility = 'public';
if (this.projectIsPrivate) {
visibility = "private";
visibility = 'private';
}
let { error } = await updateProject(
projectref,
@ -198,8 +215,8 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
if (this.projectNameToDelete == this.projectName) {
let { error } = await deleteProject(projectref);
@ -222,60 +239,59 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
let { error } = await projectUpdateRepoLinkedAccount(projectref);
if (error) {
this.updateRepoLinkedAccountError = error;
return;
}
}
},
},
created: async function () {
let projectref = [this.ownertype, this.ownername, ...this.projectref].join(
"/"
'/'
);
let { data, error } = await fetchProject(projectref);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.project = data;
this.projectIsPrivate = this.project.visibility == "private";
this.projectIsPrivate = this.project.visibility == 'private';
({ data, error } = await fetchSecrets("project", projectref, false));
({ data, error } = await fetchSecrets('project', projectref, false));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.secrets = data;
({ data, error } = await fetchSecrets("project", projectref, true));
({ data, error } = await fetchSecrets('project', projectref, true));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allsecrets = data;
({ data, error } = await fetchVariables("project", projectref, false));
({ data, error } = await fetchVariables('project', projectref, false));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.variables = data;
({ data, error } = await fetchVariables("project", projectref, true));
({ data, error } = await fetchVariables('project', projectref, true));
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.allvariables = data;
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,32 +7,34 @@
<hr class="my-6 border-t" />
<h5 class="text-2xl">All variables (local and inherited)</h5>
<vars v-if="allvariables.length" :variables="allvariables" :showparentpath="true" />
<vars
v-if="allvariables.length"
:variables="allvariables"
:showparentpath="true"
/>
<span v-else>No variables</span>
</div>
</template>
<script>
import vars from "@/components/vars";
import vars from '@/components/vars';
export default {
components: { vars },
name: "projectvars",
name: 'projectvars',
props: {
variables: Array,
allvariables: Array,
type: String
type: String,
},
computed: {
typetitle() {
if (this.type == "project") return "Project";
if (this.type == "projectgroup") return "Project group";
return "";
}
}
if (this.type == 'project') return 'Project';
if (this.type == 'projectgroup') return 'Project group';
return '';
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -5,7 +5,9 @@
@submit.prevent="$emit('login', { username })"
>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="username">Remote Username</label>
<label class="block text-sm font-bold mb-2" for="username"
>Remote Username</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="username"
@ -13,38 +15,38 @@
placeholder="Username"
:disabled="true"
v-model="remoteUsername"
>
/>
</div>
<div class="mb-4">
<label class="block text-sm font-bold mb-2" for="username">Username</label>
<label class="block text-sm font-bold mb-2" for="username"
>Username</label
>
<input
class="appearance-none border rounded w-full py-2 px-3 leading-tight focus:outline-none focus:shadow-outline"
id="username"
type="text"
placeholder="Username"
v-model="username"
>
/>
</div>
<div class="flex justify-center">
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="submit"
>Register</button>
>
Register
</button>
</div>
</form>
</div>
</template>
<script>
export default {
name: "RegisterForm",
name: 'RegisterForm',
props: {
remoteUsername: String,
username: String
}
username: String,
},
};
</script>

View File

@ -7,7 +7,7 @@
v-bind:key="repo.id"
@click="select(index)"
>
<input type="radio" :checked="selectedrepo == index">
<input type="radio" :checked="selectedrepo == index" />
{{ repo.path }}
</label>
</div>
@ -18,23 +18,22 @@
<script>
export default {
components: {},
name: "remoterepos",
name: 'remoterepos',
props: {
remoterepos: Array
remoterepos: Array,
},
data() {
return {
selectedrepo: null
selectedrepo: null,
};
},
methods: {
select(index) {
this.selectedrepo = index;
this.$emit("reposelected", this.remoterepos[index].path);
}
}
this.$emit('reposelected', this.remoterepos[index].path);
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -31,15 +31,23 @@
<span
class="mr-3 rounded px-2 py-1 text-xs"
:class="'is-' + runResultClass(run)"
>{{ runStatus(run) | capitalize }}</span>
>{{ runStatus(run) | capitalize }}</span
>
<span
v-if="stillRunning(run)"
class="rounded bg-gray-500 text-white px-2 py-1 text-xs"
>Still running</span>
>Still running</span
>
</div>
<div class="mb-6">
{{ run.annotations.message.split(/\r?\n/)[0] }}
</div>
<div class="mb-6">{{run.annotations.message.split(/\r?\n/)[0]}}</div>
<div>
<a :href="run.annotations.commit_link" class="block" target="_blank">
<a
:href="run.annotations.commit_link"
class="block"
target="_blank"
>
<i class="mdi mdi-source-commit mdi-rotate-90"></i>
<span>{{ run.annotations.commit_sha.substring(0, 8) }}</span>
</a>
@ -85,12 +93,18 @@
<div class="w-1/6 flex items-start justify-between">
<div class="relative ml-auto mr-3">
<div
v-if="run.can_restart_from_scratch || run.can_restart_from_failed_tasks"
v-if="
run.can_restart_from_scratch ||
run.can_restart_from_failed_tasks
"
class="flex"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
>
<div class="flex items-center">
<button class="btn btn-blue" @click="dropdownActive = !dropdownActive">
<button
class="btn btn-blue"
@click="dropdownActive = !dropdownActive"
>
<span>Restart</span>
<i class="ml-3 mdi mdi-restart" aria-hidden="true"></i>
</button>
@ -107,14 +121,16 @@
v-if="run.can_restart_from_scratch"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white cursor-pointer"
@click="restartRun(run.id, true)"
>From start</a>
>From start</a
>
</li>
<li>
<a
v-if="run.can_restart_from_failed_tasks"
class="block px-4 py-2 hover:bg-blue-500 hover:text-white cursor-pointer"
@click="restartRun(run.id)"
>From failed tasks</a>
>From failed tasks</a
>
</li>
</ul>
</div>
@ -122,13 +138,17 @@
class="btn btn-red"
v-if="run.phase == 'queued'"
@click="cancelRun(run.id)"
>Cancel</button>
>
Cancel
</button>
<button
class="btn btn-red"
v-if="run.phase == 'running'"
:disabled="run.stopping"
@click="stopRun(run.id)"
>Stop</button>
>
Stop
</button>
</div>
</div>
</div>
@ -138,27 +158,27 @@
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import { cancelRun, stopRun, restartRun } from "@/util/data.js";
import { userDirectRunLink, projectRunLink } from "@/util/link.js";
import { runStatus, runResultClass } from "@/util/run.js";
import { cancelRun, stopRun, restartRun } from '@/util/data.js';
import { userDirectRunLink, projectRunLink } from '@/util/link.js';
import { runStatus, runResultClass } from '@/util/run.js';
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
name: "rundetail",
name: 'rundetail',
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
ownertype: String,
ownername: String,
projectref: Array,
run: Object
run: Object,
},
data() {
return {
@ -166,7 +186,7 @@ export default {
stopRunError: null,
cancelRunError: null,
restartRunError: null,
dropdownActive: false
dropdownActive: false,
};
},
methods: {
@ -178,14 +198,14 @@ export default {
this.restartRunError = null;
},
stillRunning(run) {
return run.result != "unknown" && run.phase == "running";
return run.result != 'unknown' && run.phase == 'running';
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
return "unknown";
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
return 'unknown';
},
async stopRun(runid) {
this.resetErrors();
@ -207,7 +227,7 @@ export default {
return;
}
this.run.phase = "cancelled";
this.run.phase = 'cancelled';
},
async restartRun(runid, fromStart) {
this.dropdownActive = false;
@ -231,7 +251,7 @@ export default {
this.$router.push(runLink);
},
duration(run) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(run.start_time);
let end = moment(run.end_time);
@ -244,30 +264,29 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
endTime(run) {
let formatString = "lll";
let formatString = 'lll';
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return "Finished " + end.format(formatString);
return 'Finished ' + end.format(formatString);
},
endTimeHuman(run) {
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return end.fromNow();
}
},
},
created: function () {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -9,7 +9,7 @@
</div>
<div class="ml-6 flex w-48">
<div v-bind:class="{ 'spinner': fetchRunsLoading }"></div>
<div v-bind:class="{ spinner: fetchRunsLoading }"></div>
</div>
<div v-if="runs">
<ul>
@ -67,14 +67,20 @@
<span
v-if="waitingApproval(run)"
class="w-2/12 bg-gray-200 rounded-full px-3 py-1 text-sm text-center font-semibold mr-2"
>Waiting Approval</span>
>Waiting Approval</span
>
<span
v-if="stillRunning(run)"
class="w-2/12 bg-gray-200 rounded-full px-3 py-1 text-sm text-center font-semibold mr-2"
>Still running</span>
>Still running</span
>
<div class="w-32">
<span>#{{ run.counter }}</span>
<a :href="run.annotations.commit_link" class="block" target="_blank">
<a
:href="run.annotations.commit_link"
class="block"
target="_blank"
>
<i class="mdi mdi-source-commit mdi-rotate-90 mr-1"></i>
<span>{{ run.annotations.commit_sha.substring(0, 8) }}</span>
</a>
@ -97,7 +103,9 @@
v-if="hasMoreRuns"
class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded"
@click="loadMoreRuns()"
>Load more...</button>
>
Load more...
</button>
</div>
</div>
<div v-if="runs && runs.length == 0" class>No runs</div>
@ -105,22 +113,22 @@
</template>
<script>
import { fetchUser, fetchProject, fetchRuns } from "@/util/data.js";
import { userDirectRunLink, projectRunLink } from "@/util/link.js";
import { runResultClass } from "@/util/run.js";
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import { fetchUser, fetchProject, fetchRuns } from '@/util/data.js';
import { userDirectRunLink, projectRunLink } from '@/util/link.js';
import { runResultClass } from '@/util/run.js';
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
components: {},
name: "runs",
name: 'runs',
props: {
ownertype: String,
ownername: String,
projectref: Array,
query: String
query: String,
},
data() {
return {
@ -137,7 +145,7 @@ export default {
wantedRunsNumber: 25,
hasMoreRuns: false,
project: null,
user: null
user: null,
};
},
watch: {
@ -145,7 +153,7 @@ export default {
this.runs = null;
this.hasMoreRuns = false;
this.update();
}
},
},
methods: {
projectRunLink: projectRunLink,
@ -161,7 +169,7 @@ export default {
this.fetchRunsLoading = false;
},
stillRunning(run) {
return run.result != "unknown" && run.phase == "running";
return run.result != 'unknown' && run.phase == 'running';
},
waitingApproval(run) {
return run.tasks_waiting_approval.length > 0;
@ -183,8 +191,8 @@ export default {
let projectref = [
this.ownertype,
this.ownername,
...this.projectref
].join("/");
...this.projectref,
].join('/');
let { data, error, aborted } = await fetchProject(
projectref,
@ -194,7 +202,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.project = data;
@ -212,7 +220,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.user = data;
@ -228,17 +236,17 @@ export default {
let group;
let lastrun = false;
if (this.project !== null) {
if (this.query == "branches") {
group = "/project/" + this.project.id + "/branch";
} else if (this.query == "tags") {
group = "/project/" + this.project.id + "/tag";
} else if (this.query == "pullrequests") {
group = "/project/" + this.project.id + "/pr";
if (this.query == 'branches') {
group = '/project/' + this.project.id + '/branch';
} else if (this.query == 'tags') {
group = '/project/' + this.project.id + '/tag';
} else if (this.query == 'pullrequests') {
group = '/project/' + this.project.id + '/pr';
} else {
group = "/project/" + this.project.id;
group = '/project/' + this.project.id;
}
} else if (this.user !== null) {
group = "/user/" + this.user.id;
group = '/user/' + this.user.id;
}
let newRuns = [];
@ -289,7 +297,7 @@ export default {
}, 2000);
},
duration(run) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(run.start_time);
let end = moment(run.end_time);
@ -302,22 +310,22 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
endTime(run) {
let formatString = "lll";
let formatString = 'lll';
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return "Finished " + end.format(formatString);
return 'Finished ' + end.format(formatString);
},
endTimeHuman(run) {
let end = moment(run.end_time);
if (run.end_time === null) {
return "";
return '';
}
return end.fromNow();
}
},
},
created: function () {
window.setInterval(() => {
@ -331,9 +339,8 @@ export default {
this.fetchAbort.abort();
}
clearTimeout(this.fetchRunsSchedule);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,7 +7,12 @@
>
<div>{{ fetchRunError }}</div>
</div>
<rundetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref" />
<rundetail
:run="run"
:ownertype="ownertype"
:ownername="ownername"
:projectref="projectref"
/>
<div v-if="run">
<div v-if="run.phase != 'setuperror'">
<div class="flex items-center my-6 justify-between">
@ -42,7 +47,8 @@
class="font-mono leading-snug text-xs"
v-for="(error, i) in run.setup_errors"
v-bind:key="i"
>{{error}}</pre>
>{{ error }}</pre
>
</div>
</div>
</div>
@ -50,21 +56,21 @@
</template>
<script>
import { fetchRun } from "@/util/data.js";
import { userDirectRunTaskLink, projectRunTaskLink } from "@/util/link.js";
import { fetchRun } from '@/util/data.js';
import { userDirectRunTaskLink, projectRunTaskLink } from '@/util/link.js';
import rundetail from "@/components/rundetail.vue";
import tasks from "@/components/tasks.vue";
import tasksgraph from "@/components/tasksgraph.vue";
import rundetail from '@/components/rundetail.vue';
import tasks from '@/components/tasks.vue';
import tasksgraph from '@/components/tasksgraph.vue';
export default {
name: "runsummary",
name: 'runsummary',
components: { rundetail, tasks, tasksgraph },
props: {
ownertype: String,
ownername: String,
projectref: Array,
runid: String
runid: String,
},
data() {
return {
@ -80,7 +86,7 @@ export default {
taskYSpace: 20,
hoverTask: null,
tasksDisplay: "graph"
tasksDisplay: 'graph',
};
},
watch: {
@ -93,7 +99,7 @@ export default {
this.fetchAbort = new AbortController();
this.fetchRun();
}
},
},
methods: {
runTaskLink(task) {
@ -110,16 +116,16 @@ export default {
}
},
parents(task) {
return Object.keys(task.depends).map(key => {
return Object.keys(task.depends).map((key) => {
return this.run.tasks[task.depends[key].task_id].name;
});
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
return "unknown";
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
return 'unknown';
},
async fetchRun() {
let { data, error, aborted } = await fetchRun(
@ -145,9 +151,8 @@ export default {
let task = tasks[taskID];
task.link = this.runTaskLink(task);
task.parents = this.parents(task);
task.waiting_approval = this.run.tasks_waiting_approval.includes(
taskID
);
task.waiting_approval =
this.run.tasks_waiting_approval.includes(taskID);
}
this.scheduleFetchRun();
@ -157,7 +162,7 @@ export default {
this.fetchRunSchedule = setTimeout(() => {
this.fetchRun();
}, 2000);
}
},
},
created: function () {
this.fetchAbort = new AbortController();
@ -168,9 +173,8 @@ export default {
this.fetchAbort.abort();
}
clearTimeout(this.fetchRunSchedule);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -6,7 +6,9 @@
<div class="flex" v-for="secret in secrets" v-bind:key="secret.id">
<div class="w-2/12">
<span class="name">{{ secret.name }}</span>
<div v-if="showparentpath" class="text-sm font-light">from {{secret.parent_path}}</div>
<div v-if="showparentpath" class="text-sm font-light">
from {{ secret.parent_path }}
</div>
</div>
</div>
</div>
@ -15,16 +17,12 @@
<script>
export default {
components: {},
name: "secrets",
name: 'secrets',
props: {
secrets: Array,
showparentpath: Boolean
}
showparentpath: Boolean,
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -20,18 +20,24 @@
<div v-if="step.type == 'run'">
<div class="p-3 rounded-t bg-gray-900 text-white">
<span>
<span class="w-2/12 bg-gray-700 rounded-l px-3 py-1 text-center font-semibold">Shell</span>
<span
class="w-2/12 bg-gray-700 rounded-l px-3 py-1 text-center font-semibold"
>Shell</span
>
<span
class="w-2/12 bg-gray-600 rounded-r px-3 py-1 text-center font-semibold mr-2"
>{{ step.shell}}</span>
>{{ step.shell }}</span
>
</span>
<span v-if="step.exit_status != undefined">
<span
class="w-2/12 bg-gray-700 rounded-l px-3 py-1 text-center font-semibold"
>Exit Status</span>
>Exit Status</span
>
<span
class="w-2/12 bg-gray-600 rounded-r px-3 py-1 text-center font-semibold mr-2"
>{{ step.exit_status}}</span>
>{{ step.exit_status }}</span
>
</span>
</div>
<div
@ -40,22 +46,27 @@
>
<i
class="inline-block mr-1 mdi mdi-arrow-right"
:class="{ 'arrow-down': commandActive, 'arrow-right': !commandActive }"
:class="{
'arrow-down': commandActive,
'arrow-right': !commandActive,
}"
></i>
<span>Command</span>
</div>
<div
v-show="commandActive"
class="p-3 bg-gray-900 text-white overflow-x-auto"
:class="{ 'rounded': step.type != 'run' }"
:class="{ rounded: step.type != 'run' }"
>
<pre class="font-mono text-xs">{{ step.command }}</pre>
</div>
</div>
<div v-if="step.type == 'run'" class="px-3 py-2 bg-gray-700 text-white">Log</div>
<div v-if="step.type == 'run'" class="px-3 py-2 bg-gray-700 text-white">
Log
</div>
<div
class="p-3 rounded-b bg-gray-900 text-white"
:class="{ 'rounded': step.type != 'run' }"
:class="{ rounded: step.type != 'run' }"
>
<Log
v-bind:runid="runid"
@ -72,22 +83,22 @@
</template>
<script>
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import Log from "@/components/log.vue";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
import Log from '@/components/log.vue';
momentDurationFormatSetup(moment);
export default {
name: "step",
name: 'step',
components: {
Log
Log,
},
data() {
return {
active: false,
commandActive: true,
now: moment()
now: moment(),
};
},
props: {
@ -95,11 +106,11 @@ export default {
taskid: String,
setup: Boolean,
stepnum: Number,
step: Object
step: Object,
},
computed: {
duration() {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(this.step.start_time);
let end = moment(this.step.end_time);
@ -112,12 +123,12 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
stepClass() {
if (this.step.phase == "success") return "success";
if (this.step.phase == "failed") return "failed";
if (this.step.phase == "stopped") return "failed";
if (this.step.phase == "running") return "running";
return "unknown";
}
if (this.step.phase == 'success') return 'success';
if (this.step.phase == 'failed') return 'failed';
if (this.step.phase == 'stopped') return 'failed';
if (this.step.phase == 'running') return 'running';
return 'unknown';
},
},
methods: {
toggle() {
@ -125,13 +136,13 @@ export default {
},
toggleCommand() {
this.commandActive = !this.commandActive;
}
},
},
created() {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>

View File

@ -1,14 +1,18 @@
<template>
<div class="arrow">
<svg viewBox="0 0 15 15">
<path fill="none" stroke="#9d9d9d" d="M4.32.5l6.247 6.942L4.32 14.5"></path>
<path
fill="none"
stroke="#9d9d9d"
d="M4.32.5l6.247 6.942L4.32 14.5"
></path>
</svg>
</div>
</template>
<script>
export default {
name: "tabarrow"
name: 'tabarrow',
};
</script>

View File

@ -2,7 +2,9 @@
<ul>
<li v-for="task in sortedTasks" v-bind:key="task.id">
<div class="mb-2 border-l-5 rounded-l" :class="taskClass(task)">
<div class="px-4 py-4 flex justify-between items-center border border-l-0 rounded-r">
<div
class="px-4 py-4 flex justify-between items-center border border-l-0 rounded-r"
>
<router-link class="w-1/3 font-bold" tag="a" :to="task.link">
<span class="w-1/3 font-bold">{{ task.name }}</span>
</router-link>
@ -10,16 +12,21 @@
<span
v-if="task.waiting_approval"
class="w-2/12 bg-gray-200 rounded-full px-3 py-1 text-sm text-center font-semibold mr-2"
>Waiting Approval</span>
>Waiting Approval</span
>
</div>
<div class="w-1/4">
<span class="block text-xs" v-if="task.parents.length > 0">depends on: &nbsp;</span>
<span class="block text-xs" v-if="task.parents.length > 0"
>depends on: &nbsp;</span
>
<ul>
<li
class="font-thin text-xs text-gray-600"
v-for="dep in task.parents"
v-bind:key="dep"
>{{dep}}</li>
>
{{ dep }}
</li>
</ul>
</div>
<span class="w-16 text-right">{{ task.duration }}</span>
@ -30,21 +37,21 @@
</template>
<script>
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
name: "tasks",
name: 'tasks',
components: {},
data() {
return {
now: moment()
now: moment(),
};
},
props: {
tasks: Object
tasks: Object,
},
computed: {
sortedTasks() {
@ -59,18 +66,18 @@ export default {
? -1
: 0
)
.map(k => tasks[k]);
.map((k) => tasks[k]);
for (let task of sortedTasks) {
task.duration = this.duration(task);
}
return sortedTasks;
}
},
},
methods: {
duration(task) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(task.start_time);
let end = moment(task.end_time);
@ -83,18 +90,18 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
if (task.status == "skipped") return "skipped";
return "unknown";
}
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
if (task.status == 'skipped') return 'skipped';
return 'unknown';
},
},
created() {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>

View File

@ -64,13 +64,13 @@
</template>
<script>
import * as moment from "moment";
import momentDurationFormatSetup from "moment-duration-format";
import * as moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';
momentDurationFormatSetup(moment);
export default {
name: "tasksgraph",
name: 'tasksgraph',
components: {},
data() {
return {
@ -85,11 +85,11 @@ export default {
taskYSpace: 20,
hoverTask: null,
height: "400px"
height: '400px',
};
},
props: {
tasks: Object
tasks: Object,
},
computed: {
segments: function () {
@ -107,7 +107,7 @@ export default {
}
// TODO(sgotti) set different colors to edges based on source task status???
let stroke = "text-dark";
let stroke = 'text-dark';
segments.push({
edge: edge,
x1: edge.edgePoints[i].x,
@ -115,7 +115,7 @@ export default {
x2: edge.edgePoints[i + 1].x,
y2: edge.edgePoints[i + 1].y,
strokeWidth: strokeWidth,
stroke: stroke
stroke: stroke,
});
}
}
@ -130,16 +130,16 @@ export default {
}
return this.graphTasks;
}
},
},
watch: {
tasks: function (tasks) {
this.update(tasks);
}
},
},
methods: {
duration(task) {
let formatString = "h:mm:ss[s]";
let formatString = 'h:mm:ss[s]';
let start = moment(task.start_time);
let end = moment(task.end_time);
@ -152,12 +152,12 @@ export default {
return moment.duration(end.diff(start)).format(formatString);
},
taskClass(task) {
if (task.status == "success") return "success";
if (task.status == "failed") return "failed";
if (task.status == "stopped") return "failed";
if (task.status == "running") return "running";
if (task.status == "skipped") return "skipped";
return "unknown";
if (task.status == 'success') return 'success';
if (task.status == 'failed') return 'failed';
if (task.status == 'stopped') return 'failed';
if (task.status == 'running') return 'running';
if (task.status == 'skipped') return 'skipped';
return 'unknown';
},
update(tasks) {
// sort tasks by level
@ -169,7 +169,7 @@ export default {
? -1
: 0
)
.map(k => tasks[k]);
.map((k) => tasks[k]);
this.graphTasks = graphTasks;
@ -344,7 +344,7 @@ export default {
sourceTask: pTask,
targetTask: curTask,
source: { level: pTask.level, row: pTask.row },
target: { level: curTask.level, row: curTask.row }
target: { level: curTask.level, row: curTask.row },
});
}
}
@ -362,30 +362,30 @@ export default {
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.source.level + taskWidth,
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2,
});
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.target.level - taskXSpace / 2,
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.source.row + taskHeight / 2,
});
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.target.level - taskXSpace / 2,
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2,
});
edge.edgePoints.push({
x: (taskWidth + taskXSpace) * edge.target.level,
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2
y: (taskHeight + taskYSpace) * edge.target.row + taskHeight / 2,
});
}
let width = (maxlevel + 1) * (this.taskWidth + this.taskXSpace);
this.width = width + "px";
this.width = width + 'px';
let height =
(levelsMaxRow(graphTasks, -1) + 1) *
(this.taskHeight + this.taskYSpace);
this.height = height + "px";
}
this.height = height + 'px';
},
},
created() {
this.update(this.tasks);
@ -393,6 +393,6 @@ export default {
window.setInterval(() => {
this.now = moment();
}, 500);
}
},
};
</script>

View File

@ -8,7 +8,12 @@
<div>Error fetching Run: {{ fetchRunError }}</div>
<div>Error fetching Task: {{ fetchTaskError }}</div>
</div>
<rundetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref" />
<rundetail
:run="run"
:ownertype="ownertype"
:ownername="ownername"
:projectref="projectref"
/>
<div v-if="task != null">
<div class="mt-8 mb-4 flex justify-between items-center">
<div class="flex items-center">
@ -17,13 +22,16 @@
<span
class="mr-3 rounded px-2 py-1 text-xs"
:class="taskClass(task)"
>{{ task.status | capitalize }}</span>
>{{ task.status | capitalize }}</span
>
</div>
<button
v-if="task.waiting_approval"
class="btn btn-blue"
@click="approveTask(run.id, task.id)"
>Approve</button>
>
Approve
</button>
</div>
<step
v-bind:runid="runid"
@ -44,23 +52,23 @@
</template>
<script>
import { fetchRun, fetchTask, approveTask } from "@/util/data.js";
import { fetchRun, fetchTask, approveTask } from '@/util/data.js';
import step from "@/components/step.vue";
import rundetail from "@/components/rundetail.vue";
import step from '@/components/step.vue';
import rundetail from '@/components/rundetail.vue';
export default {
components: {
step,
rundetail
rundetail,
},
name: "tasksummary",
name: 'tasksummary',
props: {
ownertype: String,
ownername: String,
projectref: Array,
runid: String,
taskid: String
taskid: String,
},
data() {
return {
@ -70,7 +78,7 @@ export default {
fetchTaskError: null,
run: null,
task: null
task: null,
};
},
watch: {
@ -85,16 +93,16 @@ export default {
this.fetchRun();
this.fetchTask();
}
},
},
methods: {
taskClass(task) {
if (task.status == "success") return "is-success";
if (task.status == "failed") return "is-failed";
if (task.status == "stopped") return "is-failed";
if (task.status == "running") return "is-running";
if (task.status == "skipped") return "is-skipped";
return "unknown";
if (task.status == 'success') return 'is-success';
if (task.status == 'failed') return 'is-failed';
if (task.status == 'stopped') return 'is-failed';
if (task.status == 'running') return 'is-running';
if (task.status == 'skipped') return 'is-skipped';
return 'unknown';
},
async fetchRun() {
let { data, error, aborted } = await fetchRun(
@ -143,7 +151,7 @@ export default {
this.fetchTask();
}, 2000);
},
approveTask: approveTask
approveTask: approveTask,
},
created: function () {
this.fetchAbort = new AbortController();
@ -157,9 +165,8 @@ export default {
}
clearTimeout(this.fetchRunSchedule);
clearTimeout(this.fetchTaskSchedule);
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -7,7 +7,10 @@
class="mb-4 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded"
role="alert"
>
<p>Removing a Linked Account will also block all the projects that uses this Linked Account to access their remote repository</p>
<p>
Removing a Linked Account will also block all the projects that uses
this Linked Account to access their remote repository
</p>
</div>
<ul v-if="user.linked_accounts">
<li
@ -17,9 +20,13 @@
>
<div>
<span class="font-bold">{{ la.remote_user_name }}</span>
<span class="ml-1">(on remote source {{laRemoteSourceName(la)}})</span>
<span class="ml-1"
>(on remote source {{ laRemoteSourceName(la) }})</span
>
</div>
<button class="btn btn-red" @click="deleteLinkedAccount(la)">Delete</button>
<button class="btn btn-red" @click="deleteLinkedAccount(la)">
Delete
</button>
</li>
<div
v-if="deleteLinkedAccountError"
@ -40,9 +47,13 @@
class="block appearance-none w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
v-model="selectedRemoteSourceName"
>
<option v-for="rs in remotesources" v-bind:key="rs.id">{{ rs.name }}</option>
<option v-for="rs in remotesources" v-bind:key="rs.id">
{{ rs.name }}
</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2">
<div
class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2"
>
<svg
class="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
@ -56,7 +67,9 @@
</div>
</div>
<button class="ml-3 btn btn-blue" @click="addLinkedAccount()">Add Linked Account</button>
<button class="ml-3 btn btn-blue" @click="addLinkedAccount()">
Add Linked Account
</button>
</div>
</div>
@ -70,7 +83,9 @@
v-bind:key="token"
>
<span class="font-bold">{{ token }}</span>
<button class="btn btn-red" @click="deleteUserToken(token)">Delete</button>
<button class="btn btn-red" @click="deleteUserToken(token)">
Delete
</button>
</li>
<div
v-if="deleteUserTokenError"
@ -102,7 +117,9 @@
</svg>
</div>
<div>
<p class="font-bold">User token created. Copy it now since it won't be showed again</p>
<p class="font-bold">
User token created. Copy it now since it won't be showed again
</p>
<p class="text-sm">{{ token }}</p>
</div>
</div>
@ -130,8 +147,10 @@
type="text"
placeholder="Token name"
v-model="newtokenname"
>
<button class="ml-3 btn btn-blue" @click="createUserToken()">Create Token</button>
/>
<button class="ml-3 btn btn-blue" @click="createUserToken()">
Create Token
</button>
</div>
<div
v-if="createUserTokenError"
@ -151,14 +170,14 @@ import {
fetchRemoteSources,
createUserToken,
deleteUserToken,
deleteLinkedAccount
} from "@/util/data.js";
deleteLinkedAccount,
} from '@/util/data.js';
import { userAddLinkedAccountLink } from "@/util/link.js";
import { userAddLinkedAccountLink } from '@/util/link.js';
export default {
components: {},
name: "usersettings",
name: 'usersettings',
props: {},
data() {
return {
@ -169,7 +188,7 @@ export default {
remotesources: [],
token: null,
newtokenname: null,
selectedRemoteSourceName: null
selectedRemoteSourceName: null,
};
},
methods: {
@ -181,7 +200,7 @@ export default {
async fetchCurrentUser() {
let { data, error } = await fetchCurrentUser();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.user = data;
@ -189,7 +208,7 @@ export default {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remotesources = data;
@ -249,15 +268,13 @@ export default {
return;
}
this.fetchCurrentUser();
}
},
},
created: function () {
this.fetchCurrentUser();
this.fetchRemoteSources();
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -19,16 +19,17 @@
<div class="flex" v-for="variable in variables" v-bind:key="variable.id">
<div class="w-2/12">
<span class="name">{{ variable.name }}</span>
<div v-if="showparentpath" class="text-sm font-light">from {{variable.parent_path}}</div>
<div v-if="showparentpath" class="text-sm font-light">
from {{ variable.parent_path }}
</div>
</div>
<div class="w-10/12">
<div class="flex" v-for="val in variable.values" v-bind:key="val.id">
<div class="w-2/12">
<span>{{ val.secret_name }}</span>
<div
v-if="val.matching_secret_parent_path"
class="text-sm"
>using secret from {{val.matching_secret_parent_path}}</div>
<div v-if="val.matching_secret_parent_path" class="text-sm">
using secret from {{ val.matching_secret_parent_path }}
</div>
<div v-else class="text-sm text-red-600">no matching secret</div>
</div>
<div class="w-2/12">
@ -43,12 +44,18 @@
<span>{{ type }}</span>
</div>
<div class="w-1/3">
<div v-for="include in val.when[type].include" v-bind:key="include.match">
<div
v-for="include in val.when[type].include"
v-bind:key="include.match"
>
<div>{{ include.match }}</div>
</div>
</div>
<div class="w-1/3">
<div v-for="exclude in val.when[type].exclude" v-bind:key="exclude.match">
<div
v-for="exclude in val.when[type].exclude"
v-bind:key="exclude.match"
>
<div>{{ exclude.match }}</div>
</div>
</div>
@ -66,15 +73,12 @@
<script>
export default {
components: {},
name: "vars",
name: 'vars',
props: {
variables: Array,
showparentpath: Boolean
}
showparentpath: Boolean,
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -1,7 +1,7 @@
@tailwind base;
@tailwind components;
@import "@/css/_ansi.scss";
@import '@/css/_ansi.scss';
.btn {
@apply font-bold py-2 px-4 rounded;
@ -119,7 +119,7 @@
border-radius: 290486px;
border-right-color: transparent;
border-top-color: transparent;
content: "";
content: '';
display: block;
width: 1em;
height: 1em;

View File

@ -1,11 +1,11 @@
import "@/css/tailwind.scss";
import { getUser } from "@/util/auth";
import "@mdi/font/css/materialdesignicons.css";
import Vue from "vue";
import Vue2Filters from "vue2-filters";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import '@/css/tailwind.scss';
import { getUser } from '@/util/auth';
import '@mdi/font/css/materialdesignicons.css';
import Vue from 'vue';
import Vue2Filters from 'vue2-filters';
import App from './App.vue';
import router from './router';
import store from './store';
Vue.use(Vue2Filters);
@ -16,9 +16,9 @@ new Vue({
created: function () {
let user = getUser();
if (user) {
store.dispatch("setUser", user);
store.dispatch('setUser', user);
}
store.dispatch("setRegisterUser", null);
store.dispatch('setRegisterUser', null);
},
render: h => h(App)
}).$mount("#app");
render: (h) => h(App),
}).$mount('#app');

View File

@ -1,249 +1,348 @@
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "./views/Home.vue";
import User from "./views/User.vue";
import Org from "./views/Org.vue";
import Project from "./views/Project.vue";
import ProjectGroup from "./views/ProjectGroup.vue";
import AddLinkedAccount from "./views/AddLinkedAccount.vue";
import usersettings from "./components/usersettings.vue";
import projects from "./components/projects.vue";
import projectsettings from "./components/projectsettings.vue";
import projectgroupsettings from "./components/projectgroupsettings.vue";
import createproject from "./components/createproject.vue";
import createprojectgroup from "./components/createprojectgroup.vue";
import createorganization from "./components/createorganization.vue";
import orgmembers from "./components/orgmembers.vue";
import runs from "./components/runs.vue";
import runsummary from "./components/runsummary.vue";
import tasksummary from "./components/tasksummary.vue";
import Oauth2 from "./views/Oauth2.vue";
import Register from "./views/Register.vue";
import Login from "./views/Login.vue";
import Logout from "./views/Logout.vue";
import CreateSource from "./views/CreateSource.vue";
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './views/Home.vue';
import User from './views/User.vue';
import Org from './views/Org.vue';
import Project from './views/Project.vue';
import ProjectGroup from './views/ProjectGroup.vue';
import AddLinkedAccount from './views/AddLinkedAccount.vue';
import usersettings from './components/usersettings.vue';
import projects from './components/projects.vue';
import projectsettings from './components/projectsettings.vue';
import projectgroupsettings from './components/projectgroupsettings.vue';
import createproject from './components/createproject.vue';
import createprojectgroup from './components/createprojectgroup.vue';
import createorganization from './components/createorganization.vue';
import orgmembers from './components/orgmembers.vue';
import runs from './components/runs.vue';
import runsummary from './components/runsummary.vue';
import tasksummary from './components/tasksummary.vue';
import Oauth2 from './views/Oauth2.vue';
import Register from './views/Register.vue';
import Login from './views/Login.vue';
import Logout from './views/Logout.vue';
import CreateSource from './views/CreateSource.vue';
import { parseRef, projectRunLink } from "@/util/link.js";
import { fetchProject } from "@/util/data.js";
import { parseRef, projectRunLink } from '@/util/link.js';
import { fetchProject } from '@/util/data.js';
import store from "./store";
import store from './store';
Vue.use(VueRouter);
const router = new VueRouter({
mode: "history",
mode: 'history',
routes: [
{
path: "/register",
name: "register",
path: '/register',
name: 'register',
component: Register,
},
{
path: "/newsource",
name: "newsource",
path: '/newsource',
name: 'newsource',
component: CreateSource,
},
{
path: "/login",
name: "login",
component: Login
path: '/login',
name: 'login',
component: Login,
},
{
path: "/logout",
name: "logout",
component: Logout
path: '/logout',
name: 'logout',
component: Logout,
},
{
path: "/oauth2/callback",
name: "oauth2callback",
component: Oauth2
path: '/oauth2/callback',
name: 'oauth2callback',
component: Oauth2,
},
{
path: "/",
name: "home",
component: Home
path: '/',
name: 'home',
component: Home,
},
{
path: "/neworganization",
path: '/neworganization',
component: createorganization,
},
{
path: "/user/:username",
path: '/user/:username',
component: User,
props: (route) => ({ username: route.params.username }),
children: [
{
path: "",
name: "user",
path: '',
name: 'user',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "projects",
name: "user projects",
path: 'projects',
name: 'user projects',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "runs",
name: "user direct runs",
path: 'runs',
name: 'user direct runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "runs/:runid",
name: "user direct run",
path: 'runs/:runid',
name: 'user direct run',
component: runsummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, runid: route.params.runid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
runid: route.params.runid,
}),
},
{
path: "runs/:runid/tasks/:taskid",
name: "user direct run task",
path: 'runs/:runid/tasks/:taskid',
name: 'user direct run task',
component: tasksummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, runid: route.params.runid, taskid: route.params.taskid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
runid: route.params.runid,
taskid: route.params.taskid,
}),
},
{
path: "settings",
name: "user settings",
path: 'settings',
name: 'user settings',
component: usersettings,
props: (route) => ({ username: route.params.username }),
},
{
path: "linkedaccounts/add/:remotesource",
name: "user add linked account",
path: 'linkedaccounts/add/:remotesource',
name: 'user add linked account',
component: AddLinkedAccount,
props: (route) => ({ username: route.params.username, remoteSourceName: route.params.remotesource })
props: (route) => ({
username: route.params.username,
remoteSourceName: route.params.remotesource,
}),
},
{
path: "createprojectgroup",
name: "user create project group",
path: 'createprojectgroup',
name: 'user create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
{
path: "createproject",
name: "user create project",
path: 'createproject',
name: 'user create project',
component: createproject,
props: (route) => ({ ownertype: "user", ownername: route.params.username })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
}),
},
]
],
},
{
path: "/user/:username/projects/:projectref(.*\\.proj)",
path: '/user/:username/projects/:projectref(.*\\.proj)',
component: Project,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) }),
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
children: [
{
path: "",
name: "user project",
path: '',
name: 'user project',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "runs",
name: "user project runs",
path: 'runs',
name: 'user project runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "branches",
name: "user project branches runs",
path: 'branches',
name: 'user project branches runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), query: "branches" })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
query: 'branches',
}),
},
{
path: "tags",
name: "user project tags runs",
path: 'tags',
name: 'user project tags runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), query: "tags" })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
query: 'tags',
}),
},
{
path: "pullrequests",
name: "user project pull requests runs",
path: 'pullrequests',
name: 'user project pull requests runs',
component: runs,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), query: "pullrequests" })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
query: 'pullrequests',
}),
},
{
path: "runs/:runid",
name: "user project run",
path: 'runs/:runid',
name: 'user project run',
component: runsummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), runid: route.params.runid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
}),
},
{
path: "runs/:runid/tasks/:taskid",
name: "user project run task",
path: 'runs/:runid/tasks/:taskid',
name: 'user project run task',
component: tasksummary,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), runid: route.params.runid, taskid: route.params.taskid })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
taskid: route.params.taskid,
}),
},
{
path: "settings",
name: "user project settings",
path: 'settings',
name: 'user project settings',
component: projectsettings,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectref: parseRef(route.params.projectref),
}),
},
]
],
},
{
path: "/user/:username/projectgroups/:projectgroupref(.*\\.proj)",
path: '/user/:username/projectgroups/:projectgroupref(.*\\.proj)',
component: ProjectGroup,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
children: [
{
path: "",
name: "user project group",
path: '',
name: 'user project group',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "projects",
name: "user project group projects",
path: 'projects',
name: 'user project group projects',
component: projects,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "settings",
name: "user project group settings",
path: 'settings',
name: 'user project group settings',
component: projectgroupsettings,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createprojectgroup",
name: "user project group create project group",
path: 'createprojectgroup',
name: 'user project group create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createproject",
name: "user project group create project",
path: 'createproject',
name: 'user project group create project',
component: createproject,
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'user',
ownername: route.params.username,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
]
],
},
{
path: "/org/:orgname",
path: '/org/:orgname',
component: Org,
props: (route) => ({ orgname: route.params.orgname }),
children: [
{
path: "",
name: "org",
path: '',
name: 'org',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
{
path: "projects",
name: "org projects",
path: 'projects',
name: 'org projects',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
{
path: "members",
name: "org members",
path: 'members',
name: 'org members',
component: orgmembers,
props: (route) => ({ orgname: route.params.orgname })
props: (route) => ({ orgname: route.params.orgname }),
},
/* {
path: "settings",
@ -252,141 +351,213 @@ const router = new VueRouter({
props: (route) => ({ username: route.params.username }),
}, */
{
path: "createprojectgroup",
name: "org create project group",
path: 'createprojectgroup',
name: 'org create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
{
path: "createproject",
name: "org create project",
path: 'createproject',
name: 'org create project',
component: createproject,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
}),
},
]
],
},
{
path: "/org/:orgname/projects/:projectref(.*\\.proj)",
path: '/org/:orgname/projects/:projectref(.*\\.proj)',
component: Project,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) }),
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
children: [
{
path: "",
name: "org project",
path: '',
name: 'org project',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "runs",
name: "org project runs",
path: 'runs',
name: 'org project runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
},
{
path: "branches",
name: "org project branches runs",
path: 'branches',
name: 'org project branches runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), query: "branches" })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
query: 'branches',
}),
},
{
path: "tags",
name: "org project tags runs",
path: 'tags',
name: 'org project tags runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), query: "tags" })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
query: 'tags',
}),
},
{
path: "pullrequests",
name: "org project pull requests runs",
path: 'pullrequests',
name: 'org project pull requests runs',
component: runs,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), query: "pullrequests" })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
query: 'pullrequests',
}),
},
{
path: "runs/:runid",
name: "org project run",
path: 'runs/:runid',
name: 'org project run',
component: runsummary,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), runid: route.params.runid })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
}),
},
{
path: "runs/:runid/tasks/:taskid",
name: "org project run task",
path: 'runs/:runid/tasks/:taskid',
name: 'org project run task',
component: tasksummary,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), runid: route.params.runid, taskid: route.params.taskid })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
runid: route.params.runid,
taskid: route.params.taskid,
}),
},
{
path: "settings",
name: "org project settings",
path: 'settings',
name: 'org project settings',
component: projectsettings,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectref: parseRef(route.params.projectref),
}),
},
]
],
},
{
path: "/org/:orgname/projectgroups/:projectgroupref(.*\\.proj)",
path: '/org/:orgname/projectgroups/:projectgroupref(.*\\.proj)',
component: ProjectGroup,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
children: [
{
path: "",
name: "org project group",
path: '',
name: 'org project group',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) }),
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "projects",
name: "org project group projects",
path: 'projects',
name: 'org project group projects',
component: projects,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "settings",
name: "org project group settings",
path: 'settings',
name: 'org project group settings',
component: projectgroupsettings,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createprojectgroup",
name: "org project group create project group",
path: 'createprojectgroup',
name: 'org project group create project group',
component: createprojectgroup,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
{
path: "createproject",
name: "org project group create project",
path: 'createproject',
name: 'org project group create project',
component: createproject,
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectgroupref: parseRef(route.params.projectgroupref) })
props: (route) => ({
ownertype: 'org',
ownername: route.params.orgname,
projectgroupref: parseRef(route.params.projectgroupref),
}),
},
]
],
},
]
],
});
router.beforeEach(async (to, from, next) => {
store.dispatch("setError", null);
store.dispatch('setError', null);
const { path, query } = to
const { path, query } = to;
if (path == "/run") {
if (path == '/run') {
// generic run handler by projectref and runid
let projectref = query.projectref
let runid = query.runid
let projectref = query.projectref;
let runid = query.runid;
let { data, error } = await fetchProject(projectref);
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
let project = data;
let parts = project.path.split("/")
let nextPath = projectRunLink(parts[0], parts[1], parts.slice(2), runid)
let parts = project.path.split('/');
let nextPath = projectRunLink(parts[0], parts[1], parts.slice(2), runid);
next({ path: nextPath.path, replace: true })
next({ path: nextPath.path, replace: true });
}
next()
})
next();
});
export default router
export default router;

View File

@ -1,55 +1,54 @@
import Vue from 'vue'
import Vuex from 'vuex'
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
Vue.use(Vuex);
const state = {
error: null,
user: null,
registeruser: null,
}
};
const getters = {
// global error
error: state => {
return state.error
error: (state) => {
return state.error;
},
user: state => {
return state.user
user: (state) => {
return state.user;
},
registeruser: state => {
return state.registeruser
}
}
registeruser: (state) => {
return state.registeruser;
},
};
const mutations = {
setError(state, error) {
state.error = error
state.error = error;
},
setUser(state, user) {
state.user = user
state.user = user;
},
setRegisterUser(state, user) {
state.registeruser = user
}
}
state.registeruser = user;
},
};
const actions = {
setError({ commit }, error) {
commit('setError', error)
commit('setError', error);
},
setUser({ commit }, user) {
commit('setUser', user)
commit('setUser', user);
},
setRegisterUser({ commit }, user) {
commit('setRegisterUser', user)
}
}
commit('setRegisterUser', user);
},
};
export default new Vuex.Store({
state,
getters,
actions,
mutations,
})
});

View File

@ -1,8 +1,8 @@
import store from "@/store";
import store from '@/store';
const ID_TOKEN_KEY = "id_token";
const USER_KEY = "user";
const LOGIN_REDIRECT_KEY = "login_redirect";
const ID_TOKEN_KEY = 'id_token';
const USER_KEY = 'user';
const LOGIN_REDIRECT_KEY = 'login_redirect';
let API_URL = window.CONFIG.API_URL;
let API_BASE_PATH = window.CONFIG.API_BASE_PATH;
@ -10,20 +10,20 @@ let API_BASE_PATH = window.CONFIG.API_BASE_PATH;
export function setLoggedUser(token, user) {
setIdToken(token);
setUser(user);
store.dispatch("setUser", user);
store.dispatch('setUser', user);
}
export function doLogout() {
unsetIdToken();
unsetUser();
store.dispatch("setUser", null);
store.dispatch('setUser', null);
}
export function apiurlwithtoken(path) {
let u = new URL(API_URL + API_BASE_PATH + path);
let idToken = getIdToken();
if (idToken) {
u.searchParams.append("access_token", idToken);
u.searchParams.append('access_token', idToken);
}
return u;
}
@ -33,19 +33,19 @@ export function apiurl(path) {
}
export function loginurl() {
return apiurl("/auth/login");
return apiurl('/auth/login');
}
export function authorizeurl() {
return apiurl("/auth/authorize");
return apiurl('/auth/authorize');
}
export function registerurl() {
return new apiurl("/auth/register");
return new apiurl('/auth/register');
}
export function oauth2callbackurl() {
return new apiurl("/auth/oauth2/callback");
return new apiurl('/auth/oauth2/callback');
}
export async function loginapi(init) {
@ -64,19 +64,19 @@ export async function registerapi(init) {
return await window.fetch(registerurl(), init);
}
export async function fetch(url, init, signal, token, tokenType = "bearer") {
export async function fetch(url, init, signal, token, tokenType = 'bearer') {
if (!init) {
init = {};
}
if (init.headers === undefined) {
init["headers"] = {};
init['headers'] = {};
}
if (signal) {
init["signal"] = signal;
init['signal'] = signal;
}
let idToken = token || getIdToken();
if (idToken) {
init.headers["Authorization"] = tokenType + " " + idToken;
init.headers['Authorization'] = tokenType + ' ' + idToken;
}
return await window.fetch(url, init);

View File

@ -1,8 +1,9 @@
import router from "@/router";
import { apiurl, fetch as authfetch, loginapi, registerapi } from "@/util/auth";
import router from '@/router';
import { apiurl, fetch as authfetch, loginapi, registerapi } from '@/util/auth';
export const GITHUB_API_URL = "https://api.github.com";
export const GITHUB_SSH_KEY = "github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==";
export const GITHUB_API_URL = 'https://api.github.com';
export const GITHUB_SSH_KEY =
'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==';
export async function fetch(url, init, signal, token, tokenType) {
try {
@ -10,8 +11,8 @@ export async function fetch(url, init, signal, token, tokenType) {
if (!res.ok) {
if (res.status === 401) {
router.push({
name: "login",
query: { redirect: router.currentRoute.fullPath }
name: 'login',
query: { redirect: router.currentRoute.fullPath },
});
// if we return a response containing an error what happens is
// that router.push mounts the login view before the calling
@ -36,21 +37,21 @@ export async function fetch(url, init, signal, token, tokenType) {
return { data: await res.json(), error: null };
}
} catch (e) {
if (e.name == "AbortError") {
if (e.name == 'AbortError') {
return { data: null, error: null, aborted: true };
}
return { data: null, error: "api call failed: " + e.message };
return { data: null, error: 'api call failed: ' + e.message };
}
}
export async function login(username, password, remotesourcename) {
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
remote_source_name: remotesourcename,
login_name: username,
password: password
})
password: password,
}),
};
try {
@ -62,7 +63,7 @@ export async function login(username, password, remotesourcename) {
return { data: await res.json(), error: null };
}
} catch (e) {
return { data: null, error: "api call failed: " + e.message };
return { data: null, error: 'api call failed: ' + e.message };
}
}
@ -73,13 +74,13 @@ export async function register(
remotepassword
) {
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
username: username,
remote_source_name: remotesourcename,
remote_source_login_name: remoteloginname,
remote_source_login_password: remotepassword
})
remote_source_login_password: remotepassword,
}),
};
try {
@ -91,111 +92,122 @@ export async function register(
return { data: await res.json(), error: null };
}
} catch (e) {
return { data: null, error: "api call failed: " + e.message };
return { data: null, error: 'api call failed: ' + e.message };
}
}
export async function fetchCurrentUser(signal) {
let path = "/user";
let path = '/user';
return await fetch(apiurl(path), null, signal);
}
export async function fetchOrgMembers(orgref, signal) {
let path = "/orgs/" + orgref + "/members";
let path = '/orgs/' + orgref + '/members';
return await fetch(apiurl(path), null, signal);
}
export async function fetchRuns(group, startRunID, lastrun, signal) {
let u = apiurl("/runs");
let u = apiurl('/runs');
if (group) {
u.searchParams.append("group", group);
u.searchParams.append('group', group);
}
if (lastrun) {
u.searchParams.append("lastrun", true);
u.searchParams.append('lastrun', true);
}
if (startRunID) {
u.searchParams.append("start", startRunID);
u.searchParams.append('start', startRunID);
}
return await fetch(u, null, signal);
}
export async function fetchRun(runid, signal) {
return await fetch(apiurl("/runs/" + runid), null, signal);
return await fetch(apiurl('/runs/' + runid), null, signal);
}
export async function fetchTask(runid, taskid, signal) {
return await fetch(apiurl("/runs/" + runid + "/tasks/" + taskid), signal);
return await fetch(apiurl('/runs/' + runid + '/tasks/' + taskid), signal);
}
export async function fetchUser(username, signal) {
let path = "/users/" + username;
let path = '/users/' + username;
return await fetch(apiurl(path), null, signal);
}
export async function fetchProjectGroup(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
return await fetch(apiurl(path), null, signal);
}
export async function fetchProjectGroupSubgroups(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
path += "/subgroups";
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
path += '/subgroups';
return await fetch(apiurl(path), null, signal);
}
export async function fetchProjectGroupProjects(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
path += "/projects";
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
path += '/projects';
return await fetch(apiurl(path), null, signal);
}
export async function fetchProject(ref, signal) {
let path = "/projects/" + encodeURIComponent(ref);
let path = '/projects/' + encodeURIComponent(ref);
return await fetch(apiurl(path), null, signal);
}
export async function fetchSecrets(ownertype, ref, all, signal) {
let path;
if (ownertype == "project") {
path = "/projects/";
} else if (ownertype == "projectgroup") {
path = "/projectgroups/";
if (ownertype == 'project') {
path = '/projects/';
} else if (ownertype == 'projectgroup') {
path = '/projectgroups/';
}
path += encodeURIComponent(ref);
path += "/secrets";
path += '/secrets';
if (all) {
path += "?tree&removeoverridden";
path += '?tree&removeoverridden';
}
return await fetch(apiurl(path), null, signal);
}
export async function fetchVariables(ownertype, ref, all, signal) {
let path;
if (ownertype == "project") {
path = "/projects/";
} else if (ownertype == "projectgroup") {
path = "/projectgroups/";
if (ownertype == 'project') {
path = '/projects/';
} else if (ownertype == 'projectgroup') {
path = '/projectgroups/';
}
path += encodeURIComponent(ref);
path += "/variables";
path += '/variables';
if (all) {
path += "?tree&removeoverridden";
path += '?tree&removeoverridden';
}
return await fetch(apiurl(path), null, signal);
}
export async function createRemoteSource(
token, type, name, clientID, clientSecret, apiURL, authType, skipVerify,
sshHostKey, skipSshHostKeyCheck, registrationEnabled, loginEnabled, signal,
token,
type,
name,
clientID,
clientSecret,
apiURL,
authType,
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
registrationEnabled,
loginEnabled,
signal
) {
let path = "/remotesources";
let path = '/remotesources';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name,
apiurl: apiURL,
@ -208,38 +220,38 @@ export async function createRemoteSource(
oauth_2_client_secret: clientSecret,
registration_enabled: registrationEnabled,
login_enabled: loginEnabled,
})
}),
};
return await fetch(apiurl(path), init, signal, token, "token");
return await fetch(apiurl(path), init, signal, token, 'token');
}
export async function createOrganization(orgname, visibility, signal) {
let path = "/orgs";
let path = '/orgs';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name: orgname,
visibility: visibility
})
visibility: visibility,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function createUserToken(username, tokenname, signal) {
let path = "/users/" + username + "/tokens";
let path = '/users/' + username + '/tokens';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
token_name: tokenname
})
token_name: tokenname,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteUserToken(username, tokenname, signal) {
let path = "/users/" + username + "/tokens/" + tokenname;
let path = '/users/' + username + '/tokens/' + tokenname;
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}
@ -251,90 +263,90 @@ export async function createUserLinkedAccount(
password,
signal
) {
let path = "/users/" + username + "/linkedaccounts";
let path = '/users/' + username + '/linkedaccounts';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
remote_source_name: remotesourcename,
remote_source_login_name: loginname,
remote_source_login_password: password
})
remote_source_login_password: password,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteLinkedAccount(username, laid, signal) {
let path = "/users/" + username + "/linkedaccounts/" + laid;
let path = '/users/' + username + '/linkedaccounts/' + laid;
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}
export async function restartRun(runid, fromStart, signal) {
let path = "/runs/" + runid + "/actions";
let path = '/runs/' + runid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "restart",
from_start: fromStart
})
action_type: 'restart',
from_start: fromStart,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function cancelRun(runid, signal) {
let path = "/runs/" + runid + "/actions";
let path = '/runs/' + runid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "cancel"
})
action_type: 'cancel',
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function stopRun(runid, signal) {
let path = "/runs/" + runid + "/actions";
let path = '/runs/' + runid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "stop"
})
action_type: 'stop',
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function approveTask(runid, taskid, signal) {
let path = "/runs/" + runid + "/tasks/" + taskid + "/actions";
let path = '/runs/' + runid + '/tasks/' + taskid + '/actions';
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
action_type: "approve"
})
action_type: 'approve',
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function fetchRemoteSources(signal) {
let path = "/remotesources";
let path = '/remotesources';
return await fetch(apiurl(path), null, signal);
}
export async function userRemoteRepos(remotesourceid, signal) {
let path = "/user/remoterepos/" + remotesourceid;
let path = '/user/remoterepos/' + remotesourceid;
return await fetch(apiurl(path, null, signal));
}
export async function createProjectGroup(parentref, name, visibility, signal) {
let path = "/projectgroups";
let path = '/projectgroups';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name: name,
parent_ref: parentref,
visibility: visibility
})
visibility: visibility,
}),
};
return await fetch(apiurl(path), init, signal);
}
@ -345,13 +357,13 @@ export async function updateProjectGroup(
visibility,
signal
) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
name: name,
visibility: visibility
})
visibility: visibility,
}),
};
return await fetch(apiurl(path), init, signal);
}
@ -365,56 +377,61 @@ export async function createProject(
passvarstoforkedpr,
signal
) {
let path = "/projects";
let path = '/projects';
let init = {
method: "POST",
method: 'POST',
body: JSON.stringify({
name: name,
parent_ref: parentref,
visibility: visibility,
remote_source_name: remotesourcename,
repo_path: remoterepopath,
pass_vars_to_forked_pr: passvarstoforkedpr
})
pass_vars_to_forked_pr: passvarstoforkedpr,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function updateProject(projectref, name, visibility, passvarstoforkedpr, signal) {
let path = "/projects/" + encodeURIComponent(projectref);
export async function updateProject(
projectref,
name,
visibility,
passvarstoforkedpr,
signal
) {
let path = '/projects/' + encodeURIComponent(projectref);
let init = {
method: "PUT",
method: 'PUT',
body: JSON.stringify({
name: name,
visibility: visibility,
pass_vars_to_forked_pr: passvarstoforkedpr
})
pass_vars_to_forked_pr: passvarstoforkedpr,
}),
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteProject(projectref, signal) {
let path = "/projects/" + encodeURIComponent(projectref);
let path = '/projects/' + encodeURIComponent(projectref);
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}
export async function projectUpdateRepoLinkedAccount(projectref, signal) {
let path =
"/projects/" + encodeURIComponent(projectref) + "/updaterepolinkedaccount";
'/projects/' + encodeURIComponent(projectref) + '/updaterepolinkedaccount';
let init = {
method: "PUT"
method: 'PUT',
};
return await fetch(apiurl(path), init, signal);
}
export async function deleteProjectGroup(projectgroupref, signal) {
let path = "/projectgroups/" + encodeURIComponent(projectgroupref);
let path = '/projectgroups/' + encodeURIComponent(projectgroupref);
let init = {
method: "DELETE"
method: 'DELETE',
};
return await fetch(apiurl(path), init, signal);
}

View File

@ -1,130 +1,173 @@
export function parseRef(ref) {
ref = ref.replace(/\.proj/, "")
ref = ref.replace(/\.proj/, '');
// return empty array or split return an array with the empty element
if (!ref) {
return []
return [];
}
return ref.split("/")
return ref.split('/');
}
export function ownerLink(ownertype, ownername) {
if (ownertype == "user") {
return { name: ownertype, params: { username: ownername } }
} else if (ownertype == "org") {
return { name: ownertype, params: { orgname: ownername } }
if (ownertype == 'user') {
return { name: ownertype, params: { username: ownername } };
} else if (ownertype == 'org') {
return { name: ownertype, params: { orgname: ownername } };
}
}
export function ownerProjectsLink(ownertype, ownername) {
return { name: ownertype + " projects", params: { ownername: ownername } }
return { name: ownertype + ' projects', params: { ownername: ownername } };
}
export function ownerSettingsLink(ownertype, ownername) {
if (ownertype == "user") {
return { name: ownertype + " settings", params: { username: ownername } }
} else if (ownertype == "org") {
return { name: ownertype + " settings", params: { orgname: ownername } }
if (ownertype == 'user') {
return { name: ownertype + ' settings', params: { username: ownername } };
} else if (ownertype == 'org') {
return { name: ownertype + ' settings', params: { orgname: ownername } };
}
}
export function userDirectRunsLink(username) {
return { name: "user direct runs", params: { username: username } }
return { name: 'user direct runs', params: { username: username } };
}
export function userDirectRunLink(username, runid) {
return { name: "user direct run", params: { username: username, runid: runid } }
return {
name: 'user direct run',
params: { username: username, runid: runid },
};
}
export function userDirectRunTaskLink(username, runid, taskid) {
return { name: "user direct run task", params: { username: username, runid: runid, taskid: taskid } }
return {
name: 'user direct run task',
params: { username: username, runid: runid, taskid: taskid },
};
}
export function userAddLinkedAccountLink(username, remotesourcename) {
return { name: "user add linked account", params: { username: username, remotesource: remotesourcename } }
return {
name: 'user add linked account',
params: { username: username, remotesource: remotesourcename },
};
}
export function orgMembersLink(orgname) {
return { name: "org members", params: { orgname: orgname } }
return { name: 'org members', params: { orgname: orgname } };
}
// Note, when creating a router link containing a project/projectgroup ref (a
// path), unfortunately, we cannot use route name and params since it will path
// escape it
export function projectGroupPath(ownertype, ownername, projectgroupref) {
let path = `/${ownertype}/${ownername}`
let path = `/${ownertype}/${ownername}`;
// root project group will have a .proj without a name
let projectgrouppath = (projectgroupref.join("/") + ".proj")
path = `${path}/projectgroups/${projectgrouppath}`
return path
let projectgrouppath = projectgroupref.join('/') + '.proj';
path = `${path}/projectgroups/${projectgrouppath}`;
return path;
}
export function projectPath(ownertype, ownername, projectref) {
let path = `/${ownertype}/${ownername}`
let projectpath = (projectref.join("/") + ".proj")
path = `${path}/projects/${projectpath}`
return path
let path = `/${ownertype}/${ownername}`;
let projectpath = projectref.join('/') + '.proj';
path = `${path}/projects/${projectpath}`;
return path;
}
export function projectGroupLink(ownertype, ownername, projectgroupref) {
return { path: projectGroupPath(ownertype, ownername, projectgroupref) }
return { path: projectGroupPath(ownertype, ownername, projectgroupref) };
}
export function projectGroupProjectsLink(ownertype, ownername, projectgroupref) {
let projectgrouppath = (projectgroupref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projectgroups/${projectgrouppath}/projects` }
export function projectGroupProjectsLink(
ownertype,
ownername,
projectgroupref
) {
let projectgrouppath = projectgroupref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projectgroups/${projectgrouppath}/projects`,
};
}
export function projectLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}` }
let projectpath = projectref.join('/') + '.proj';
return { path: `/${ownertype}/${ownername}/projects/${projectpath}` };
}
export function projectRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs` }
let projectpath = projectref.join('/') + '.proj';
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs` };
}
export function projectBranchesRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/branches` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/branches`,
};
}
export function projectTagsRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/tags` }
let projectpath = projectref.join('/') + '.proj';
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/tags` };
}
export function projectPRsRunsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/pullrequests` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/pullrequests`,
};
}
export function projectRunLink(ownertype, ownername, projectref, runid) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}`,
};
}
export function projectRunTaskLink(ownertype, ownername, projectref, runid, taskid) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}/tasks/${taskid}` }
export function projectRunTaskLink(
ownertype,
ownername,
projectref,
runid,
taskid
) {
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/runs/${runid}/tasks/${taskid}`,
};
}
export function projectGroupSettingsLink(ownertype, ownername, projectgroupref) {
let path = projectGroupPath(ownertype, ownername, projectgroupref)
return { path: `${path}/settings` }
export function projectGroupSettingsLink(
ownertype,
ownername,
projectgroupref
) {
let path = projectGroupPath(ownertype, ownername, projectgroupref);
return { path: `${path}/settings` };
}
export function projectSettingsLink(ownertype, ownername, projectref) {
let projectpath = (projectref.join("/") + ".proj")
return { path: `/${ownertype}/${ownername}/projects/${projectpath}/settings` }
let projectpath = projectref.join('/') + '.proj';
return {
path: `/${ownertype}/${ownername}/projects/${projectpath}/settings`,
};
}
export function projectGroupCreateProjectGroupLink(ownertype, ownername, projectgroupref) {
let path = projectGroupPath(ownertype, ownername, projectgroupref)
return { path: `${path}/createprojectgroup` }
export function projectGroupCreateProjectGroupLink(
ownertype,
ownername,
projectgroupref
) {
let path = projectGroupPath(ownertype, ownername, projectgroupref);
return { path: `${path}/createprojectgroup` };
}
export function projectGroupCreateProjectLink(ownertype, ownername, projectgroupref) {
let path = projectGroupPath(ownertype, ownername, projectgroupref)
return { path: `${path}/createproject` }
export function projectGroupCreateProjectLink(
ownertype,
ownername,
projectgroupref
) {
let path = projectGroupPath(ownertype, ownername, projectgroupref);
return { path: `${path}/createproject` };
}

View File

@ -1,11 +1,10 @@
export function runStatus(run) {
// * if the run has a result then return the result
// * if stopping return "stopping"
// * return the phase
if (run.result != "unknown") return run.result;
if (run.stopping) return "stopping";
if (run.phase != "finished") return run.phase;
if (run.result != 'unknown') return run.result;
if (run.stopping) return 'stopping';
if (run.phase != 'finished') return run.phase;
return run.result;
}
@ -13,13 +12,13 @@ export function runStatus(run) {
export function runResultClass(run) {
let status = runStatus(run);
if (status == "setuperror") return "setuperror";
if (status == "queued") return "unknown";
if (status == "cancelled") return "failed";
if (status == "running") return "running";
if (status == "stopping") return "failed";
if (status == "stopped") return "failed";
if (status == "success") return "success";
if (status == "failed") return "failed";
return "unknown";
if (status == 'setuperror') return 'setuperror';
if (status == 'queued') return 'unknown';
if (status == 'cancelled') return 'failed';
if (status == 'running') return 'running';
if (status == 'stopping') return 'failed';
if (status == 'stopped') return 'failed';
if (status == 'success') return 'success';
if (status == 'failed') return 'failed';
return 'unknown';
}

View File

@ -13,36 +13,44 @@
v-if="remotesource.auth_type == 'password'"
action="Add Linked Account"
:name="remotesource.name"
v-on:login="doAddLinkedAccount(remotesource.name, $event.username, $event.password)"
v-on:login="
doAddLinkedAccount(
remotesource.name,
$event.username,
$event.password
)
"
/>
<button
v-else
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
@click="doAddLinkedAccount(remotesource.name)"
>Add Linked Account with {{remotesource.name}}</button>
>
Add Linked Account with {{ remotesource.name }}
</button>
</div>
</div>
</div>
</template>
<script>
import LoginForm from "@/components/loginform";
import LoginForm from '@/components/loginform';
import { fetchRemoteSources, createUserLinkedAccount } from "@/util/data";
import { fetchRemoteSources, createUserLinkedAccount } from '@/util/data';
export default {
name: "AddLinkedAccount",
name: 'AddLinkedAccount',
props: {
username: String,
remoteSourceName: String
remoteSourceName: String,
},
components: {
LoginForm
LoginForm,
},
data: function () {
return {
addLinkedAccountError: null,
remotesource: null
remotesource: null,
};
},
methods: {
@ -52,7 +60,7 @@ export default {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
for (var i = 0; i < data.length; i++) {
@ -79,17 +87,13 @@ export default {
return;
}
this.$router.push({
name: "user settings",
params: { username: this.username }
name: 'user settings',
params: { username: this.username },
});
}
},
},
created: function () {
this.fetchRemoteSources();
}
},
};
</script>

View File

@ -9,12 +9,8 @@
</div>
<div>
<div>
<div
class="flex justify-center items-center w-max"
>
<CreateSourceForm
v-on:createSource="createSource($event)"
/>
<div class="flex justify-center items-center w-max">
<CreateSourceForm v-on:createSource="createSource($event)" />
</div>
</div>
</div>
@ -22,15 +18,14 @@
</template>
<script>
import CreateSourceForm from "@/components/createsourceform";
import { createRemoteSource } from "@/util/data.js";
import router from "@/router";
import CreateSourceForm from '@/components/createsourceform';
import { createRemoteSource } from '@/util/data.js';
import router from '@/router';
export default {
name: "CreateSource",
name: 'CreateSource',
components: {
CreateSourceForm
CreateSourceForm,
},
data: function () {
return {
@ -39,23 +34,37 @@ export default {
},
methods: {
async createSource({
token, name, type, clientId, clientSecret, url, skipVerify, sshHostKey,
token,
name,
type,
clientId,
clientSecret,
url,
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
}) {
const res = await createRemoteSource(
token, type, name, clientId, clientSecret, url, "oauth2", skipVerify,
sshHostKey, skipSshHostKeyCheck, true, true, undefined,
token,
type,
name,
clientId,
clientSecret,
url,
'oauth2',
skipVerify,
sshHostKey,
skipSshHostKeyCheck,
true,
true,
undefined
);
if (res.error)
this.$store.dispatch("setError", res.error);
else
router.push({name: "login"});
}
if (res.error) this.$store.dispatch('setError', res.error);
else router.push({ name: 'login' });
},
},
mounted: function () {
this.$store.dispatch("setError", null);
this.$store.dispatch('setError', null);
},
};
</script>

View File

@ -1,25 +1,15 @@
<template>
<div class="home flex flex-col items-center">
<img
class="w-64 h-64"
src="/img/agola-logo-name.svg" alt="agola logo"
/>
<img class="w-64 h-64" src="/img/agola-logo-name.svg" alt="agola logo" />
<h1 class="text-2xl">CI/CD redefined</h1>
<div class="m-8">
<h1 class="text-lg">
Hi, you are almost ready to go! Just
<router-link
class="underline text-blue-600"
to="/login"
>
<router-link class="underline text-blue-600" to="/login">
login
</router-link>
into your account
or create a
<router-link
class="underline text-blue-600"
to="/register"
>
into your account or create a
<router-link class="underline text-blue-600" to="/register">
new one
</router-link>
</h1>
@ -28,22 +18,22 @@
</template>
<script>
import { mapGetters } from "vuex";
import { mapGetters } from 'vuex';
export default {
name: "Home",
name: 'Home',
components: {},
computed: {
...mapGetters(["user"])
...mapGetters(['user']),
},
created: function () {
let user = this.$store.getters.user;
if (user) {
this.$router.push({
name: "user",
params: { username: this.user.username }
name: 'user',
params: { username: this.user.username },
});
}
}
},
};
</script>

View File

@ -13,13 +13,8 @@
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
>
No remote sources defined
<router-link
class="underline text-blue-600 block"
to="/newsource"
>
<button class="btn btn-blue">
Create one
</button>
<router-link class="underline text-blue-600 block" to="/newsource">
<button class="btn btn-blue">Create one</button>
</router-link>
</div>
<div
@ -61,25 +56,25 @@
</template>
<script>
import { fetchRemoteSources, login } from "@/util/data";
import { fetchRemoteSources, login } from '@/util/data';
import {
setLoggedUser,
unsetLoginRedirect,
setLoginRedirect,
doLogout
} from "@/util/auth";
doLogout,
} from '@/util/auth';
import LoginForm from "@/components/loginform";
import LoginForm from '@/components/loginform';
export default {
name: "Login",
name: 'Login',
components: {
LoginForm
LoginForm,
},
data: function () {
return {
error: null,
remotesources: null
remotesources: null,
};
},
computed: {
@ -96,20 +91,20 @@ export default {
}
}
return false;
}
},
},
methods: {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remotesources = data;
},
async doLogin(username, password, remotesourcename) {
unsetLoginRedirect();
let redirect = this.$route.query["redirect"];
let redirect = this.$route.query['redirect'];
this.error = null;
@ -131,18 +126,16 @@ export default {
unsetLoginRedirect();
this.$router.push(redirect);
} else {
this.$router.push({ name: "home" });
}
this.$router.push({ name: 'home' });
}
},
},
mounted: function () {
this.$store.dispatch("setError", null);
this.$store.dispatch('setError', null);
},
created: function () {
doLogout();
this.fetchRemoteSources();
}
},
};
</script>

View File

@ -1,13 +1,11 @@
<script>
import { doLogout } from "@/util/auth";
import { doLogout } from '@/util/auth';
export default {
name: "Logout",
name: 'Logout',
created: function () {
doLogout();
this.$router.push("/");
}
this.$router.push('/');
},
};
</script>

View File

@ -11,32 +11,32 @@
</template>
<script>
import { fetch } from "@/util/data";
import { fetch } from '@/util/data';
import {
oauth2callbackurl,
setLoggedUser,
unsetLoginRedirect,
getLoginRedirect
} from "@/util/auth";
getLoginRedirect,
} from '@/util/auth';
export default {
components: {},
name: "Oauth2",
name: 'Oauth2',
props: {},
data() {
return {
error: null,
run: null,
code: this.$route.query.code,
username: null
username: null,
};
},
methods: {
async doOauth2() {
let u = oauth2callbackurl();
u.searchParams.append("code", this.$route.query.code);
u.searchParams.append("state", this.$route.query.state);
u.searchParams.append('code', this.$route.query.code);
u.searchParams.append('state', this.$route.query.state);
let { data, error } = await fetch(u);
if (error) {
// set local login error on failed oauth2.
@ -44,29 +44,28 @@ export default {
return;
}
if (data.request_type === "loginuser") {
if (data.request_type === 'loginuser') {
setLoggedUser(data.response.token, data.response.user);
let redirect = getLoginRedirect(redirect);
if (redirect) {
unsetLoginRedirect();
this.$router.push(redirect);
} else {
this.$router.push({ name: "home" });
this.$router.push({ name: 'home' });
}
} else if (data.request_type === "authorize") {
this.$store.dispatch("setRegisterUser", data.response);
this.$router.push("/register");
} else if (data.request_type === "createuserla") {
} else if (data.request_type === 'authorize') {
this.$store.dispatch('setRegisterUser', data.response);
this.$router.push('/register');
} else if (data.request_type === 'createuserla') {
this.$router.push({
name: "user settings",
params: { username: this.username }
name: 'user settings',
params: { username: this.username },
});
}
}
},
},
created: function () {
this.doOauth2();
}
},
};
</script>

View File

@ -9,7 +9,9 @@
<span class="mx-2">/</span>
</li>
<li>
<router-link :to="ownerLink('org', orgname)">{{orgname}}</router-link>
<router-link :to="ownerLink('org', orgname)">{{
orgname
}}</router-link>
</li>
</ol>
</nav>
@ -23,7 +25,12 @@
<ul class="flex-grow tab">
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name === 'org projects' || $route.name === 'org' }]"
:class="[
{
'tab-element-selected':
$route.name === 'org projects' || $route.name === 'org',
},
]"
>
<router-link :to="ownerProjectsLink('org', orgname)">
<i class="mr-1 mdi mdi-home" />
@ -42,7 +49,13 @@
<li
v-if="$route.name.endsWith('org project group settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('org project group settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith(
'org project group settings'
),
},
]"
>
<router-link :to="projectGroupSettingsLink('org', orgname, [])">
<i class="mr-1 mdi mdi-settings" />
@ -52,7 +65,9 @@
<li
v-if="$route.name.endsWith('org settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('org settings') }]"
:class="[
{ 'tab-element-selected': $route.name.endsWith('org settings') },
]"
>
<router-link :to="ownerSettingsLink('org', orgname)">
<i class="mr-1 mdi mdi-settings" />
@ -65,7 +80,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -108,9 +123,8 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
ownerLink,
@ -119,23 +133,23 @@ import {
orgMembersLink,
projectGroupCreateProjectGroupLink,
projectGroupCreateProjectLink,
projectGroupSettingsLink
} from "@/util/link.js";
projectGroupSettingsLink,
} from '@/util/link.js';
import createprojectbutton from "@/components/createprojectbutton.vue";
import createprojectbutton from '@/components/createprojectbutton.vue';
export default {
name: "Org",
name: 'Org',
components: { createprojectbutton },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
orgname: String
orgname: String,
},
data() {
return {
dropdownActive: false
dropdownActive: false,
};
},
methods: {
@ -147,19 +161,18 @@ export default {
projectGroupCreateProjectLink: projectGroupCreateProjectLink,
projectGroupSettingsLink: projectGroupSettingsLink,
goToCreate(type) {
if (type == "project") {
if (type == 'project') {
this.$router.push(
projectGroupCreateProjectLink("org", this.orgname, [])
projectGroupCreateProjectLink('org', this.orgname, [])
);
return;
}
this.$router.push(
projectGroupCreateProjectGroupLink("org", this.orgname, [])
projectGroupCreateProjectGroupLink('org', this.orgname, [])
);
}
}
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -1,6 +1,10 @@
<template>
<div>
<projbreadcrumbs :ownertype="ownertype" :ownername="ownername" :projectref="projectref" />
<projbreadcrumbs
:ownertype="ownertype"
:ownername="ownername"
:projectref="projectref"
/>
<div class="mb-8">
<span class="text-3xl">{{ projectName() }}</span>
@ -17,7 +21,13 @@
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project runs') || $route.name.endsWith('project') }]"
:class="[
{
'tab-element-selected':
$route.name.match('project runs') ||
$route.name.endsWith('project'),
},
]"
>
<router-link :to="projectRunsLink(ownertype, ownername, projectref)">
<i class="mr-1 mdi mdi-asterisk" />
@ -26,42 +36,81 @@
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project branches runs') }]"
:class="[
{
'tab-element-selected': $route.name.match(
'project branches runs'
),
},
]"
>
<router-link
:to="projectBranchesRunsLink(ownertype, ownername, projectref)"
>
<router-link :to="projectBranchesRunsLink(ownertype, ownername, projectref)">
<i class="mr-1 mdi mdi-source-branch" />
<span>Branches</span>
</router-link>
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project tags runs') }]"
:class="[
{ 'tab-element-selected': $route.name.match('project tags runs') },
]"
>
<router-link
:to="projectTagsRunsLink(ownertype, ownername, projectref)"
>
<router-link :to="projectTagsRunsLink(ownertype, ownername, projectref)">
<i class="mr-1 mdi mdi-tag" />
<span>Tags</span>
</router-link>
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project pull requests runs') }]"
:class="[
{
'tab-element-selected': $route.name.match(
'project pull requests runs'
),
},
]"
>
<router-link
:to="projectPRsRunsLink(ownertype, ownername, projectref)"
>
<router-link :to="projectPRsRunsLink(ownertype, ownername, projectref)">
<i class="mr-1 mdi mdi-source-pull" />
<span>Pull Requests</span>
</router-link>
</li>
<li
v-if="run && ($route.name.endsWith('project run') || $route.name.endsWith('project run task'))"
v-if="
run &&
($route.name.endsWith('project run') ||
$route.name.endsWith('project run task'))
"
>
<tabarrow />
</li>
<li
class="tab-element"
v-if="run && ($route.name.endsWith('project run') || $route.name.endsWith('project run task'))"
:class="[{ 'tab-element-selected': $route.name.endsWith('project run') }]"
v-if="
run &&
($route.name.endsWith('project run') ||
$route.name.endsWith('project run task'))
"
:class="[
{ 'tab-element-selected': $route.name.endsWith('project run') },
]"
>
<router-link
:to="
projectRunLink(
ownertype,
ownername,
projectref,
$route.params.runid
)
"
>
<router-link :to="projectRunLink(ownertype, ownername, projectref, $route.params.runid)">
<p>
Run
<strong>#{{ run.counter }}</strong>
@ -74,10 +123,22 @@
<li
class="tab-element"
v-if="run && $route.name.endsWith('project run task')"
:class="[{ 'tab-element-selected': $route.name.endsWith('project run task') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith('project run task'),
},
]"
>
<router-link
:to="projectRunTaskLink(ownertype, ownername, projectref, $route.params.runid, $route.params.taskid)"
:to="
projectRunTaskLink(
ownertype,
ownername,
projectref,
$route.params.runid,
$route.params.taskid
)
"
>
<p>
Task
@ -88,9 +149,15 @@
<li
v-if="$route.name.endsWith('project settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('project settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith('project settings'),
},
]"
>
<router-link
:to="projectSettingsLink(ownertype, ownername, projectref)"
>
<router-link :to="projectSettingsLink(ownertype, ownername, projectref)">
<i class="mr-1 mdi mdi-settings" />
<span>Project Settings</span>
</router-link>
@ -101,7 +168,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -135,9 +202,8 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
projectLink,
@ -147,31 +213,31 @@ import {
projectPRsRunsLink,
projectRunLink,
projectRunTaskLink,
projectSettingsLink
} from "@/util/link.js";
projectSettingsLink,
} from '@/util/link.js';
import { fetchRun } from "@/util/data.js";
import { fetchRun } from '@/util/data.js';
import projbreadcrumbs from "@/components/projbreadcrumbs.vue";
import tabarrow from "@/components/tabarrow.vue";
import projbreadcrumbs from '@/components/projbreadcrumbs.vue';
import tabarrow from '@/components/tabarrow.vue';
export default {
name: "Project",
name: 'Project',
components: { projbreadcrumbs, tabarrow },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
ownertype: String,
ownername: String,
projectref: Array
projectref: Array,
},
data() {
return {
fetchAbort: null,
dropdownActive: false,
run: null
run: null,
};
},
watch: {
@ -191,12 +257,12 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
}
}
},
},
methods: {
projectLink: projectLink,
@ -209,7 +275,7 @@ export default {
projectSettingsLink: projectSettingsLink,
projectName() {
return this.projectref[this.projectref.length - 1];
}
},
},
created: async function () {
this.fetchAbort = new AbortController();
@ -223,7 +289,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
@ -233,9 +299,8 @@ export default {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -1,4 +1,3 @@
<template>
<div>
<projbreadcrumbs
@ -16,9 +15,19 @@
<ul class="flex-grow tab">
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.match('project group project') || $route.name.endsWith('project group') }]"
:class="[
{
'tab-element-selected':
$route.name.match('project group project') ||
$route.name.endsWith('project group'),
},
]"
>
<router-link
:to="
projectGroupProjectsLink(ownertype, ownername, projectgroupref)
"
>
<router-link :to="projectGroupProjectsLink(ownertype, ownername, projectgroupref)">
<i class="mdi mdi-home" />
<span>Projects</span>
</router-link>
@ -26,9 +35,19 @@
<li
v-if="$route.name.endsWith('project group settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('project group settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith(
'project group settings'
),
},
]"
>
<router-link
:to="
projectGroupSettingsLink(ownertype, ownername, projectgroupref)
"
>
<router-link :to="projectGroupSettingsLink(ownertype, ownername, projectgroupref)">
<i class="mdi mdi-settings" />
<span>Project Group Settings</span>
</router-link>
@ -39,7 +58,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -57,7 +76,13 @@
<li>
<router-link
class="block px-4 py-2 hover:bg-blue-500 hover:text-white"
:to="projectGroupSettingsLink(ownertype, ownername, projectgroupref)"
:to="
projectGroupSettingsLink(
ownertype,
ownername,
projectgroupref
)
"
>
<i class="mdi mdi-settings" />
<span>Project Group Settings</span>
@ -73,34 +98,33 @@
</div>
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
projectGroupProjectsLink,
projectGroupSettingsLink,
projectGroupCreateProjectGroupLink,
projectGroupCreateProjectLink
} from "@/util/link.js";
projectGroupCreateProjectLink,
} from '@/util/link.js';
import projbreadcrumbs from "@/components/projbreadcrumbs.vue";
import createprojectbutton from "@/components/createprojectbutton.vue";
import projbreadcrumbs from '@/components/projbreadcrumbs.vue';
import createprojectbutton from '@/components/createprojectbutton.vue';
export default {
name: "ProjectGroup",
name: 'ProjectGroup',
components: { projbreadcrumbs, createprojectbutton },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
ownertype: String,
ownername: String,
projectgroupref: Array
projectgroupref: Array,
},
data() {
return {
dropdownActive: false
dropdownActive: false,
};
},
methods: {
@ -110,12 +134,12 @@ export default {
projectGroupCreateProjectLink: projectGroupCreateProjectLink,
projectGroupName() {
if (!this.projectgroupref.length) {
return "Root Project Group";
return 'Root Project Group';
}
return this.projectgroupref[this.projectgroupref.length - 1];
},
goToCreate(type) {
if (type == "project") {
if (type == 'project') {
this.$router.push(
projectGroupCreateProjectLink(
this.ownertype,
@ -132,10 +156,9 @@ export default {
this.projectgroupref
)
);
}
}
},
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -28,13 +28,8 @@
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
>
No remote sources defined
<router-link
class="underline text-blue-600 block"
to="/newsource"
>
<button class="btn btn-blue">
Create one
</button>
<router-link class="underline text-blue-600 block" to="/newsource">
<button class="btn btn-blue">Create one</button>
</router-link>
</div>
<div
@ -75,29 +70,29 @@
</template>
<script>
import { mapGetters } from "vuex";
import { mapGetters } from 'vuex';
import LoginForm from "@/components/loginform";
import RegisterForm from "@/components/registerform";
import LoginForm from '@/components/loginform';
import RegisterForm from '@/components/registerform';
import { fetchRemoteSources, register } from "@/util/data";
import { fetchRemoteSources, register } from '@/util/data';
import { authorizeurl, fetch, doLogout } from "@/util/auth";
import { authorizeurl, fetch, doLogout } from '@/util/auth';
export default {
name: "Register",
name: 'Register',
components: {
LoginForm,
RegisterForm
RegisterForm,
},
data: function () {
return {
error: null,
remotesources: null
remotesources: null,
};
},
computed: {
...mapGetters(["registeruser"]),
...mapGetters(['registeruser']),
hasRemoteSources() {
if (this.remotesources) {
@ -112,13 +107,13 @@ export default {
}
}
return false;
}
},
},
methods: {
async fetchRemoteSources() {
let { data, error } = await fetchRemoteSources();
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.remotesources = data;
@ -127,12 +122,12 @@ export default {
let u = authorizeurl();
let res = await (
await fetch(u, {
method: "POST",
method: 'POST',
body: JSON.stringify({
remote_source_name: remotesourcename,
login_name: username,
password: password
})
password: password,
}),
})
).json();
@ -140,11 +135,11 @@ export default {
window.location = res.oauth2_redirect;
return;
}
this.$store.dispatch("setRegisterUser", {
this.$store.dispatch('setRegisterUser', {
remote_user_info: res.remote_user_info,
remote_source_name: res.remote_source_name,
remote_source_login_name: username,
remote_source_login_password: password
remote_source_login_password: password,
});
},
async doRegister(
@ -170,18 +165,15 @@ export default {
window.location = data.oauth2_redirect;
return;
}
this.$router.push({ name: "home" });
}
this.$router.push({ name: 'home' });
},
},
mounted: function () {
this.$store.dispatch("setError", null);
this.$store.dispatch('setError', null);
},
created: function () {
doLogout();
this.fetchRemoteSources();
}
},
};
</script>

View File

@ -9,7 +9,9 @@
<span class="mx-2">/</span>
</li>
<li>
<router-link :to="ownerLink('user', username)">{{username}}</router-link>
<router-link :to="ownerLink('user', username)">{{
username
}}</router-link>
</li>
</ol>
</nav>
@ -23,7 +25,12 @@
<ul class="flex-grow tab">
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name === 'user projects' || $route.name === 'user' }]"
:class="[
{
'tab-element-selected':
$route.name === 'user projects' || $route.name === 'user',
},
]"
>
<router-link :to="ownerProjectsLink('user', username)">
<i class="mr-1 mdi mdi-home" />
@ -32,7 +39,9 @@
</li>
<li
class="tab-element"
:class="[{ 'tab-element-selected': $route.name === 'user direct runs' }]"
:class="[
{ 'tab-element-selected': $route.name === 'user direct runs' },
]"
>
<router-link :to="userDirectRunsLink(username)">
<i class="mr-1 mdi mdi-run-fast" />
@ -40,14 +49,24 @@
</router-link>
</li>
<li
v-if="run && ($route.name === 'user direct run' || $route.name == 'user direct run task')"
v-if="
run &&
($route.name === 'user direct run' ||
$route.name == 'user direct run task')
"
>
<tabarrow />
</li>
<li
class="tab-element"
v-if="run && ($route.name === 'user direct run' || $route.name == 'user direct run task')"
:class="[{ 'tab-element-selected': $route.name === 'user direct run' }]"
v-if="
run &&
($route.name === 'user direct run' ||
$route.name == 'user direct run task')
"
:class="[
{ 'tab-element-selected': $route.name === 'user direct run' },
]"
>
<router-link :to="userDirectRunLink(username, $route.params.runid)">
<span>
@ -62,10 +81,18 @@
<li
class="tab-element"
v-if="run && $route.name == 'user direct run task'"
:class="[{ 'tab-element-selected': $route.name === 'user direct run task' }]"
:class="[
{ 'tab-element-selected': $route.name === 'user direct run task' },
]"
>
<router-link
:to="userDirectRunTaskLink(username, $route.params.runid, $route.params.taskid)"
:to="
userDirectRunTaskLink(
username,
$route.params.runid,
$route.params.taskid
)
"
>
<span>
Task
@ -76,7 +103,13 @@
<li
v-if="$route.name.endsWith('user project group settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('user project group settings') }]"
:class="[
{
'tab-element-selected': $route.name.endsWith(
'user project group settings'
),
},
]"
>
<router-link :to="projectGroupSettingsLink('user', username, [])">
<i class="mr-1 mdi mdi-settings" />
@ -86,7 +119,9 @@
<li
v-if="$route.name.endsWith('user settings')"
class="tab-element"
:class="[{ 'tab-element-selected': $route.name.endsWith('user settings') }]"
:class="[
{ 'tab-element-selected': $route.name.endsWith('user settings') },
]"
>
<router-link :to="ownerSettingsLink('user', username)">
<i class="mr-1 mdi mdi-settings" />
@ -99,7 +134,7 @@
<div class="relative">
<div
class="flex -mt-3"
v-click-outside="() => dropdownActive = false"
v-click-outside="() => (dropdownActive = false)"
@click="dropdownActive = !dropdownActive"
>
<button
@ -134,7 +169,7 @@
</template>
<script>
import * as vClickOutside from "v-click-outside-x";
import * as vClickOutside from 'v-click-outside-x';
import {
ownerLink,
@ -145,29 +180,29 @@ import {
ownerSettingsLink,
projectGroupCreateProjectGroupLink,
projectGroupCreateProjectLink,
projectGroupSettingsLink
} from "@/util/link.js";
projectGroupSettingsLink,
} from '@/util/link.js';
import { fetchRun } from "@/util/data.js";
import { fetchRun } from '@/util/data.js';
import createprojectbutton from "@/components/createprojectbutton.vue";
import tabarrow from "@/components/tabarrow.vue";
import createprojectbutton from '@/components/createprojectbutton.vue';
import tabarrow from '@/components/tabarrow.vue';
export default {
name: "User",
name: 'User',
components: { createprojectbutton, tabarrow },
directives: {
clickOutside: vClickOutside.directive
clickOutside: vClickOutside.directive,
},
props: {
username: String
username: String,
},
data() {
return {
fetchAbort: null,
dropdownActive: false,
run: null
run: null,
};
},
watch: {
@ -187,12 +222,12 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
}
}
},
},
methods: {
ownerLink: ownerLink,
@ -205,16 +240,16 @@ export default {
projectGroupCreateProjectLink: projectGroupCreateProjectLink,
projectGroupSettingsLink: projectGroupSettingsLink,
goToCreate(type) {
if (type == "project") {
if (type == 'project') {
this.$router.push(
projectGroupCreateProjectLink("user", this.username, [])
projectGroupCreateProjectLink('user', this.username, [])
);
return;
}
this.$router.push(
projectGroupCreateProjectGroupLink("user", this.username, [])
projectGroupCreateProjectGroupLink('user', this.username, [])
);
}
},
},
created: async function () {
this.fetchAbort = new AbortController();
@ -228,7 +263,7 @@ export default {
return;
}
if (error) {
this.$store.dispatch("setError", error);
this.$store.dispatch('setError', error);
return;
}
this.run = data;
@ -238,9 +273,8 @@ export default {
if (this.fetchAbort) {
this.fetchAbort.abort();
}
}
},
};
</script>
<style scoped lang="scss">
</style>
<style scoped lang="scss"></style>

View File

@ -6,43 +6,54 @@ module.exports = {
padding: '2rem',
},
fontFamily: {
'sans': ['Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans', 'sans-serif', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji']
sans: [
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'Noto Sans',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji',
],
},
borderWidth: {
default: '1px',
'0': '0',
'2': '2px',
'4': '4px',
'5': '5px',
'6': '6px',
0: '0',
2: '2px',
4: '4px',
5: '5px',
6: '6px',
},
spacing: {
px: '1px',
'2px': '2px',
'3px': '3px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
'5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'20': '5rem',
'24': '6rem',
'32': '8rem',
'40': '10rem',
'48': '12rem',
'56': '14rem',
'64': '16rem',
0: '0',
1: '0.25rem',
2: '0.5rem',
3: '0.75rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
8: '2rem',
10: '2.5rem',
12: '3rem',
16: '4rem',
20: '5rem',
24: '6rem',
32: '8rem',
40: '10rem',
48: '12rem',
56: '14rem',
64: '16rem',
},
colors: {
dark: '#4a4a4a',
}
}
},
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'disabled'],
@ -56,9 +67,9 @@ module.exports = {
function ({ addVariant, e }) {
addVariant('disabled', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`disabled${separator}${className}`)}:disabled`
})
})
}
]
}
return `.${e(`disabled${separator}${className}`)}:disabled`;
});
});
},
],
};

View File

@ -1,7 +1,7 @@
const path = require("path");
const path = require('path');
module.exports = {
css: {
sourceMap: true
}
sourceMap: true,
},
};