mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-29 20:00:30 +00:00
parent
ed44c3b50f
commit
de4e62cfcf
11 changed files with 76 additions and 16 deletions
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/woodpecker-ci/woodpecker/server"
|
||||
cronScheduler "github.com/woodpecker-ci/woodpecker/server/cron"
|
||||
"github.com/woodpecker-ci/woodpecker/server/model"
|
||||
"github.com/woodpecker-ci/woodpecker/server/pipeline"
|
||||
"github.com/woodpecker-ci/woodpecker/server/router/middleware/session"
|
||||
"github.com/woodpecker-ci/woodpecker/server/store"
|
||||
)
|
||||
|
@ -45,6 +46,37 @@ func GetCron(c *gin.Context) {
|
|||
c.JSON(200, cron)
|
||||
}
|
||||
|
||||
// RunCron starts a cron job now.
|
||||
func RunCron(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
_store := store.FromContext(c)
|
||||
id, err := strconv.ParseInt(c.Param("cron"), 10, 64)
|
||||
if err != nil {
|
||||
c.String(400, "Error parsing cron id. %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
cron, err := _store.CronFind(repo, id)
|
||||
if err != nil {
|
||||
c.String(http.StatusNotFound, "Error getting cron %q. %s", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
repo, newPipeline, err := cronScheduler.CreatePipeline(c, _store, server.Config.Services.Remote, cron)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error creating pipeline for cron %q. %s", id, err)
|
||||
return
|
||||
}
|
||||
|
||||
pl, err := pipeline.Create(c, _store, repo, newPipeline)
|
||||
if err != nil {
|
||||
handlePipelineErr(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, pl)
|
||||
}
|
||||
|
||||
// PostCron persists the cron job to the database.
|
||||
func PostCron(c *gin.Context) {
|
||||
repo := session.Repo(c)
|
||||
|
|
|
@ -21,6 +21,7 @@ package api
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -28,6 +29,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/woodpecker-ci/woodpecker/server"
|
||||
"github.com/woodpecker-ci/woodpecker/server/store/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
@ -95,6 +97,10 @@ func GetPipelines(c *gin.Context) {
|
|||
|
||||
pipelines, err := store.FromContext(c).GetPipelineList(repo, page)
|
||||
if err != nil {
|
||||
if errors.Is(err, types.RecordNotExist) {
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -96,16 +96,16 @@ func runCron(store store.Store, remote remote.Remote, cron *model.Cron, now time
|
|||
return nil
|
||||
}
|
||||
|
||||
repo, newBuild, err := createBuild(ctx, store, remote, cron)
|
||||
repo, newPipeline, err := CreatePipeline(ctx, store, remote, cron)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = pipeline.Create(ctx, store, repo, newBuild)
|
||||
_, err = pipeline.Create(ctx, store, repo, newPipeline)
|
||||
return err
|
||||
}
|
||||
|
||||
func createBuild(ctx context.Context, store store.Store, remote remote.Remote, cron *model.Cron) (*model.Repo, *model.Pipeline, error) {
|
||||
func CreatePipeline(ctx context.Context, store store.Store, remote remote.Remote, cron *model.Cron) (*model.Repo, *model.Pipeline, error) {
|
||||
repo, err := store.GetRepo(cron.RepoID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
|
@ -49,7 +49,7 @@ func TestCreateBuild(t *testing.T) {
|
|||
store.On("GetUser", mock.Anything).Return(creator, nil)
|
||||
remote.On("BranchHead", mock.Anything, creator, repo1, "default").Return("sha1", nil)
|
||||
|
||||
_, pipeline, err := createBuild(ctx, store, remote, &model.Cron{
|
||||
_, pipeline, err := CreatePipeline(ctx, store, remote, &model.Cron{
|
||||
Name: "test",
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -114,6 +114,7 @@ func apiRoutes(e *gin.Engine) {
|
|||
repo.GET("/cron", session.MustPush, api.GetCronList)
|
||||
repo.POST("/cron", session.MustPush, api.PostCron)
|
||||
repo.GET("/cron/:cron", session.MustPush, api.GetCron)
|
||||
repo.POST("/cron/:cron", session.MustPush, api.RunCron)
|
||||
repo.PATCH("/cron/:cron", session.MustPush, api.PatchCron)
|
||||
repo.DELETE("/cron/:cron", session.MustPush, api.DeleteCron)
|
||||
|
||||
|
|
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
|
@ -32,6 +32,7 @@ declare module '@vue/runtime-core' {
|
|||
IIcBaselineFileDownload: typeof import('~icons/ic/baseline-file-download')['default']
|
||||
IIcBaselineFileDownloadOff: typeof import('~icons/ic/baseline-file-download-off')['default']
|
||||
IIcBaselineHealing: typeof import('~icons/ic/baseline-healing')['default']
|
||||
IIcBaselinePlayArrow: typeof import('~icons/ic/baseline-play-arrow')['default']
|
||||
IIconoirArrowLeft: typeof import('~icons/iconoir/arrow-left')['default']
|
||||
IIconParkOutlineAlarmClock: typeof import('~icons/icon-park-outline/alarm-clock')['default']
|
||||
IIcRoundLightMode: typeof import('~icons/ic/round-light-mode')['default']
|
||||
|
|
|
@ -154,6 +154,7 @@
|
|||
"deleted": "Cron deleted",
|
||||
"next_exec": "Next execution",
|
||||
"not_executed_yet": "Not executed yet",
|
||||
"run": "Run now",
|
||||
"branch": {
|
||||
"title": "Branch",
|
||||
"placeholder": "Branch (uses default branch if empty)"
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<i-icon-park-outline-alarm-clock v-else-if="name === 'stopwatch'" class="h-6 w-6" />
|
||||
<i-ic-baseline-file-download v-else-if="name === 'auto-scroll'" class="h-6 w-6" />
|
||||
<i-ic-baseline-file-download-off v-else-if="name === 'auto-scroll-off'" class="h-6 w-6" />
|
||||
<i-ic-baseline-play-arrow v-else-if="name === 'play'" class="h-6 w-6" />
|
||||
<div v-else-if="name === 'blank'" class="h-6 w-6" />
|
||||
</template>
|
||||
|
||||
|
@ -90,7 +91,8 @@ export type IconNames =
|
|||
| 'stopwatch'
|
||||
| 'download'
|
||||
| 'auto-scroll'
|
||||
| 'auto-scroll-off';
|
||||
| 'auto-scroll-off'
|
||||
| 'play';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Icon',
|
||||
|
|
|
@ -31,12 +31,8 @@
|
|||
{{ $t('repo.settings.crons.next_exec') }}: {{ date.toLocaleString(new Date(cron.next_exec * 1000)) }}</span
|
||||
>
|
||||
<span v-else class="ml-auto">{{ $t('repo.settings.crons.not_executed_yet') }}</span>
|
||||
<IconButton
|
||||
icon="edit"
|
||||
class="ml-auto w-8 h-8"
|
||||
:title="$t('repo.settings.crons.edit')"
|
||||
@click="selectedCron = cron"
|
||||
/>
|
||||
<IconButton icon="play" class="ml-auto w-8 h-8" :title="$t('repo.settings.crons.run')" @click="runCron(cron)" />
|
||||
<IconButton icon="edit" class="w-8 h-8" :title="$t('repo.settings.crons.edit')" @click="selectedCron = cron" />
|
||||
<IconButton
|
||||
icon="trash"
|
||||
class="w-8 h-8 hover:text-red-400 hover:dark:text-red-500"
|
||||
|
@ -105,6 +101,7 @@ import { useAsyncAction } from '~/compositions/useAsyncAction';
|
|||
import { useDate } from '~/compositions/useDate';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { Cron, Repo } from '~/lib/api/types';
|
||||
import router from '~/router';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
|
@ -156,6 +153,22 @@ const { doSubmit: deleteCron, isLoading: isDeleting } = useAsyncAction(async (_c
|
|||
await loadCrons();
|
||||
});
|
||||
|
||||
const { doSubmit: runCron } = useAsyncAction(async (_cron: Cron) => {
|
||||
if (!repo?.value) {
|
||||
throw new Error("Unexpected: Can't load repo");
|
||||
}
|
||||
|
||||
const pipeline = await apiClient.runCron(repo.value.owner, repo.value.name, _cron.id);
|
||||
await router.push({
|
||||
name: 'repo-pipeline',
|
||||
params: {
|
||||
repoOwner: repo.value.owner,
|
||||
repoName: repo.value.name,
|
||||
pipelineId: pipeline.number,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await loadCrons();
|
||||
});
|
||||
|
|
|
@ -166,16 +166,16 @@ export default defineComponent({
|
|||
text: i18n.t('repo.settings.general.visibility.public.public'),
|
||||
description: i18n.t('repo.settings.general.visibility.public.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Private,
|
||||
text: i18n.t('repo.settings.general.visibility.private.private'),
|
||||
description: i18n.t('repo.settings.general.visibility.private.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Internal,
|
||||
text: i18n.t('repo.settings.general.visibility.internal.internal'),
|
||||
description: i18n.t('repo.settings.general.visibility.internal.desc'),
|
||||
},
|
||||
{
|
||||
value: RepoVisibility.Private,
|
||||
text: i18n.t('repo.settings.general.visibility.private.private'),
|
||||
description: i18n.t('repo.settings.general.visibility.private.desc'),
|
||||
},
|
||||
];
|
||||
|
||||
const cancelPreviousPipelineEventsOptions: CheckboxOption[] = [
|
||||
|
|
|
@ -162,6 +162,10 @@ export default class WoodpeckerClient extends ApiClient {
|
|||
return this._delete(`/api/repos/${owner}/${repo}/cron/${cronId}`);
|
||||
}
|
||||
|
||||
runCron(owner: string, repo: string, cronId: number): Promise<Pipeline> {
|
||||
return this._post(`/api/repos/${owner}/${repo}/cron/${cronId}`) as Promise<Pipeline>;
|
||||
}
|
||||
|
||||
getOrgPermissions(owner: string): Promise<OrgPermissions> {
|
||||
return this._get(`/api/orgs/${owner}/permissions`) as Promise<OrgPermissions>;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue