mirror of
https://github.com/semaphoreui/semaphore.git
synced 2024-11-21 17:01:04 +01:00
project runners (#2547)
* feat: add feature flag * feat(runners): pro runners ui * feat(runner): api for project level * fix(ui): key of list * feat(be): add mocks for project runners * feat: pro alert * feat(pro): upgrade button * feat(pro): upgrade button * feat(pro): icon and color * feat(pro): change color
This commit is contained in:
parent
cd0755f5a9
commit
4996728daa
51
api/projects/runners.go
Normal file
51
api/projects/runners.go
Normal file
@ -0,0 +1,51 @@
|
||||
package projects
|
||||
|
||||
import (
|
||||
"github.com/gorilla/context"
|
||||
"github.com/semaphoreui/semaphore/api/helpers"
|
||||
"github.com/semaphoreui/semaphore/db"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func GetRunners(w http.ResponseWriter, r *http.Request) {
|
||||
project := context.Get(r, "project").(db.Project)
|
||||
runners, err := helpers.Store(r).GetRunners(project.ID)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var result = make([]db.Runner, 0)
|
||||
|
||||
for _, runner := range runners {
|
||||
result = append(result, runner)
|
||||
}
|
||||
|
||||
helpers.WriteJSON(w, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func AddRunner(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func RunnerMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func GetRunner(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func UpdateRunner(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func DeleteRunner(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func SetRunnerActive(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
@ -12,13 +12,13 @@ import (
|
||||
|
||||
"github.com/semaphoreui/semaphore/api/runners"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/semaphoreui/semaphore/api/helpers"
|
||||
"github.com/semaphoreui/semaphore/api/projects"
|
||||
"github.com/semaphoreui/semaphore/api/sockets"
|
||||
"github.com/semaphoreui/semaphore/api/tasks"
|
||||
"github.com/semaphoreui/semaphore/db"
|
||||
"github.com/semaphoreui/semaphore/util"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var startTime = time.Now().UTC()
|
||||
@ -222,6 +222,16 @@ func Route() *mux.Router {
|
||||
projectUserAPI.Path("/integrations").HandlerFunc(projects.AddIntegration).Methods("POST")
|
||||
projectUserAPI.Path("/backup").HandlerFunc(projects.GetBackup).Methods("GET", "HEAD")
|
||||
|
||||
projectUserAPI.Path("/runners").HandlerFunc(projects.GetRunners).Methods("GET", "HEAD")
|
||||
projectUserAPI.Path("/runners").HandlerFunc(projects.AddRunner).Methods("POST")
|
||||
|
||||
projectRunnersAPI := projectUserAPI.PathPrefix("/runners").Subrouter()
|
||||
projectRunnersAPI.Use(globalRunnerMiddleware)
|
||||
projectRunnersAPI.Path("/{runner_id}").HandlerFunc(projects.GetRunner).Methods("GET", "HEAD")
|
||||
projectRunnersAPI.Path("/{runner_id}").HandlerFunc(projects.UpdateRunner).Methods("PUT", "POST")
|
||||
projectRunnersAPI.Path("/{runner_id}/active").HandlerFunc(projects.SetRunnerActive).Methods("POST")
|
||||
projectRunnersAPI.Path("/{runner_id}").HandlerFunc(projects.DeleteRunner).Methods("DELETE")
|
||||
|
||||
//
|
||||
// Updating and deleting project
|
||||
projectAdminAPI := authenticatedAPI.Path("/project/{project_id}").Subrouter()
|
||||
@ -475,6 +485,10 @@ func getSystemInfo(w http.ResponseWriter, r *http.Request) {
|
||||
"ansible": util.AnsibleVersion(),
|
||||
"web_host": host,
|
||||
"use_remote_runner": util.Config.UseRemoteRunner,
|
||||
|
||||
"premium_features": map[string]bool{
|
||||
"project_runners": false,
|
||||
},
|
||||
}
|
||||
|
||||
helpers.WriteJSON(w, http.StatusOK, body)
|
||||
|
@ -9,14 +9,6 @@ import (
|
||||
"github.com/gorilla/context"
|
||||
)
|
||||
|
||||
//type minimalGlobalRunner struct {
|
||||
// ID int `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// Active bool `json:"active"`
|
||||
// Webhook string `db:"webhook" json:"webhook"`
|
||||
// MaxParallelTasks int `db:"max_parallel_tasks" json:"max_parallel_tasks"`
|
||||
//}
|
||||
|
||||
func getGlobalRunners(w http.ResponseWriter, r *http.Request) {
|
||||
runners, err := helpers.Store(r).GetGlobalRunners(false)
|
||||
|
||||
|
@ -528,6 +528,7 @@
|
||||
:isAdmin="(user || {}).admin"
|
||||
:webHost="(systemInfo || {}).web_host"
|
||||
:version="(systemInfo || {version: ''}).version.split('-')[0]"
|
||||
:premiumFeatures="((systemInfo || {premium_features: {}}).premium_features)"
|
||||
:user="user"
|
||||
></router-view>
|
||||
</v-main>
|
||||
|
@ -64,7 +64,7 @@
|
||||
'rgba(200, 200, 200, 0.38)' :
|
||||
'rgba(0, 0, 0, 0.38)'
|
||||
}">
|
||||
<legend style="padding: 0 3px;">{{ $t('Args') }}</legend>
|
||||
<legend style="padding: 0 3px;">{{ title || $t('Args') }}</legend>
|
||||
<v-chip-group column style="margin-top: -4px;">
|
||||
<v-chip
|
||||
v-for="(v, i) in modifiedVars"
|
||||
@ -89,6 +89,7 @@
|
||||
export default {
|
||||
props: {
|
||||
vars: Array,
|
||||
title: String,
|
||||
},
|
||||
watch: {
|
||||
vars(val) {
|
||||
|
42
web/src/components/DashboardMenu.vue
Normal file
42
web/src/components/DashboardMenu.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<v-tabs show-arrows class="pl-4">
|
||||
<v-tab
|
||||
v-if="projectType === ''"
|
||||
key="history"
|
||||
:to="`/project/${projectId}/history`"
|
||||
>{{ $t('history') }}
|
||||
</v-tab>
|
||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||
<v-tab
|
||||
v-if="canUpdateProject"
|
||||
key="settings"
|
||||
:to="`/project/${projectId}/settings`"
|
||||
>{{ $t('settings') }}
|
||||
</v-tab>
|
||||
<v-tab key="runners" :to="`/project/${projectId}/runners`">
|
||||
{{ $t('runners') }}
|
||||
<!-- <v-chip small class="ml-1" color="purple" style="color: white">Pro</v-chip> -->
|
||||
<v-icon class="ml-1" large color="hsl(348deg, 86%, 61%)">mdi-professional-hexagon</v-icon>
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
</template>
|
||||
<script>
|
||||
import PermissionsCheck from '@/components/PermissionsCheck';
|
||||
|
||||
export default {
|
||||
|
||||
mixins: [PermissionsCheck],
|
||||
|
||||
props: {
|
||||
projectId: Number,
|
||||
projectType: String,
|
||||
canUpdateProject: Boolean,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
id: null,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
@ -59,7 +59,7 @@ export default {
|
||||
},
|
||||
|
||||
getItemsUrl() {
|
||||
return '/api/projects/restore';
|
||||
return '/api/project/restore';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -274,6 +274,7 @@
|
||||
<ArgsPicker
|
||||
:vars="args"
|
||||
@change="setArgs"
|
||||
title="CLI args"
|
||||
/>
|
||||
|
||||
<v-checkbox
|
||||
|
@ -301,7 +301,7 @@ export default {
|
||||
taskLocation: 'Location',
|
||||
empty: 'Empty',
|
||||
noValues: 'No values',
|
||||
addArg: 'Add Arg',
|
||||
addArg: 'Add arg',
|
||||
|
||||
status_success: 'Success',
|
||||
status_failed: 'Failed',
|
||||
|
@ -44,6 +44,10 @@ const routes = [
|
||||
path: '/project/:projectId/activity',
|
||||
component: Activity,
|
||||
},
|
||||
{
|
||||
path: '/project/:projectId/runners',
|
||||
component: Runners,
|
||||
},
|
||||
{
|
||||
path: '/project/:projectId/schedule',
|
||||
component: Schedule,
|
||||
|
@ -1,5 +1,19 @@
|
||||
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
|
||||
<div v-if="items != null">
|
||||
|
||||
<v-toolbar flat >
|
||||
<v-app-bar-nav-icon @click="showDrawer()"></v-app-bar-nav-icon>
|
||||
<v-toolbar-title>
|
||||
{{ $t('dashboard2') }}
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<DashboardMenu
|
||||
:project-id="projectId"
|
||||
project-type=""
|
||||
:can-update-project="can(USER_PERMISSIONS.updateProject)"
|
||||
/>
|
||||
|
||||
<EditDialog
|
||||
v-model="editDialog"
|
||||
:save-button-text="itemId === 'new' ? $t('create') : $t('save')"
|
||||
@ -101,7 +115,7 @@
|
||||
@yes="deleteItem(itemId)"
|
||||
/>
|
||||
|
||||
<v-toolbar flat>
|
||||
<v-toolbar flat v-if="!projectId">
|
||||
<v-btn
|
||||
icon
|
||||
class="mr-4"
|
||||
@ -109,6 +123,7 @@
|
||||
>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-toolbar-title>{{ $t('runners') }}</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
@ -118,6 +133,21 @@
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-alert
|
||||
v-if="!premiumFeatures.project_runners"
|
||||
type="info"
|
||||
text
|
||||
color="hsl(348deg, 86%, 61%)"
|
||||
style="border-radius: 0;"
|
||||
>
|
||||
Project Runners available only in <b>PRO</b> version.
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
color="hsl(348deg, 86%, 61%)"
|
||||
href="https://semaphoreui.com/pro"
|
||||
>Upgrade</v-btn>
|
||||
</v-alert>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="items"
|
||||
@ -169,11 +199,13 @@ import ItemListPageBase from '@/components/ItemListPageBase';
|
||||
import EditDialog from '@/components/EditDialog.vue';
|
||||
import RunnerForm from '@/components/RunnerForm.vue';
|
||||
import axios from 'axios';
|
||||
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||
|
||||
export default {
|
||||
mixins: [ItemListPageBase],
|
||||
|
||||
components: {
|
||||
DashboardMenu,
|
||||
RunnerForm,
|
||||
YesNoDialog,
|
||||
EditDialog,
|
||||
@ -182,6 +214,8 @@ export default {
|
||||
props: {
|
||||
webHost: String,
|
||||
version: String,
|
||||
projectId: Number,
|
||||
premiumFeatures: Object,
|
||||
},
|
||||
|
||||
computed: {
|
||||
@ -269,10 +303,18 @@ semaphore runner --no-config`;
|
||||
},
|
||||
|
||||
getItemsUrl() {
|
||||
if (this.projectId) {
|
||||
return `/api/project/${this.projectId}/runners`;
|
||||
}
|
||||
|
||||
return '/api/runners';
|
||||
},
|
||||
|
||||
getSingleItemUrl() {
|
||||
if (this.projectId) {
|
||||
return `/api/project/${this.projectId}/runners/${this.itemId}`;
|
||||
}
|
||||
|
||||
return `/api/runners/${this.itemId}`;
|
||||
},
|
||||
|
||||
|
@ -5,21 +5,11 @@
|
||||
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<v-tabs show-arrows class="pl-4">
|
||||
<v-tab
|
||||
v-if="projectType === ''"
|
||||
key="history"
|
||||
:to="`/project/${projectId}/history`"
|
||||
>{{ $t('history') }}</v-tab>
|
||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||
<v-tab
|
||||
v-if="can(USER_PERMISSIONS.updateProject)"
|
||||
key="settings"
|
||||
:to="`/project/${projectId}/settings`"
|
||||
>
|
||||
{{ $t('settings') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<DashboardMenu
|
||||
:project-id="projectId"
|
||||
project-type=""
|
||||
:can-update-project="can(USER_PERMISSIONS.updateProject)"
|
||||
/>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
@ -35,8 +25,10 @@
|
||||
</template>
|
||||
<script>
|
||||
import ItemListPageBase from '@/components/ItemListPageBase';
|
||||
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||
|
||||
export default {
|
||||
components: { DashboardMenu },
|
||||
|
||||
mixins: [ItemListPageBase],
|
||||
|
||||
|
@ -1,26 +1,17 @@
|
||||
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
|
||||
<div v-if="items != null">
|
||||
<v-toolbar flat >
|
||||
<v-toolbar flat>
|
||||
<v-app-bar-nav-icon @click="showDrawer()"></v-app-bar-nav-icon>
|
||||
<v-toolbar-title>
|
||||
{{ $t('dashboard2') }}
|
||||
</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<v-tabs show-arrows class="pl-4">
|
||||
<v-tab
|
||||
v-if="projectType === ''"
|
||||
key="history"
|
||||
:to="`/project/${projectId}/history`"
|
||||
>{{ $t('history') }}</v-tab>
|
||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||
<v-tab
|
||||
v-if="can(USER_PERMISSIONS.updateProject)"
|
||||
key="settings"
|
||||
:to="`/project/${projectId}/settings`"
|
||||
>{{ $t('settings') }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<DashboardMenu
|
||||
:project-id="projectId"
|
||||
project-type=""
|
||||
:can-update-project="can(USER_PERMISSIONS.updateProject)"
|
||||
/>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
@ -102,6 +93,7 @@ import TaskLink from '@/components/TaskLink.vue';
|
||||
import socket from '@/socket';
|
||||
import { TEMPLATE_TYPE_ICONS } from '@/lib/constants';
|
||||
import AppsMixin from '@/components/AppsMixin';
|
||||
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||
|
||||
export default {
|
||||
mixins: [ItemListPageBase, AppsMixin],
|
||||
@ -110,7 +102,7 @@ export default {
|
||||
return { TEMPLATE_TYPE_ICONS };
|
||||
},
|
||||
|
||||
components: { TaskStatus, TaskLink },
|
||||
components: { DashboardMenu, TaskStatus, TaskLink },
|
||||
|
||||
watch: {
|
||||
async projectId() {
|
||||
|
@ -12,15 +12,11 @@
|
||||
<v-toolbar-title>{{ $t('dashboard') }}</v-toolbar-title>
|
||||
</v-toolbar>
|
||||
|
||||
<v-tabs show-arrows class="pl-4">
|
||||
<v-tab
|
||||
v-if="projectType === ''"
|
||||
key="history"
|
||||
:to="`/project/${projectId}/history`"
|
||||
>{{ $t('history') }}</v-tab>
|
||||
<v-tab key="activity" :to="`/project/${projectId}/activity`">{{ $t('activity') }}</v-tab>
|
||||
<v-tab key="settings" :to="`/project/${projectId}/settings`">{{ $t('settings') }}</v-tab>
|
||||
</v-tabs>
|
||||
<DashboardMenu
|
||||
:project-id="projectId"
|
||||
project-type=""
|
||||
:can-update-project="true"
|
||||
/>
|
||||
|
||||
<div class="project-settings-form">
|
||||
<div style="height: 300px;">
|
||||
@ -107,9 +103,10 @@ import { getErrorMessage } from '@/lib/error';
|
||||
import axios from 'axios';
|
||||
import YesNoDialog from '@/components/YesNoDialog.vue';
|
||||
import delay from '@/lib/delay';
|
||||
import DashboardMenu from '@/components/DashboardMenu.vue';
|
||||
|
||||
export default {
|
||||
components: { YesNoDialog, ProjectForm },
|
||||
components: { DashboardMenu, YesNoDialog, ProjectForm },
|
||||
props: {
|
||||
projectId: Number,
|
||||
projectType: String,
|
||||
|
Loading…
Reference in New Issue
Block a user