1
0
mirror of https://codeberg.org/crowci/crow.git synced 2025-04-21 02:27:45 +03:00
crow/server/pipeline/restart.go
pat-s a554f93226 refactor: frontend error handler & notification system (#56)
- Use title and message in error message
- Errors: don't auto-remove popup, force click
- Show API method and endpoint in error message title
- Use black/white bg and notification type color only on the side
- Return errors as JSON instead of strings. Include the api endpoint in the error message.
- improve error messages when missing access token
- improve pipeling config load error messages
- Error messages are shown for 9s (instead of 3)
- remove custom `notifyError` function
- Increase popup width from 300px to 340px

Co-authored-by: crowci-bot <admin@crowci.dev>
Co-authored-by: Anbraten <6918444+anbraten@users.noreply.github.com>
Co-authored-by: qwerty287 <80460567+qwerty287@users.noreply.github.com>
Reviewed-on: https://codeberg.org/crowci/crow/pulls/56
Co-authored-by: pat-s <patrick.schratz@gmail.com>
Co-committed-by: pat-s <patrick.schratz@gmail.com>
2025-02-19 16:04:31 +00:00

136 lines
4.7 KiB
Go

// Copyright 2022 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pipeline
import (
"context"
"errors"
"fmt"
"github.com/rs/zerolog/log"
"codeberg.org/crowci/crow/v3/server"
forge_types "codeberg.org/crowci/crow/v3/server/forge/types"
"codeberg.org/crowci/crow/v3/server/model"
"codeberg.org/crowci/crow/v3/server/store"
)
// Restart a pipeline by creating a new one out of the old and start it.
func Restart(ctx context.Context, store store.Store, lastPipeline *model.Pipeline, user *model.User, repo *model.Repo, envs map[string]string) (*model.Pipeline, error) {
forge, err := server.Config.Services.Manager.ForgeFromRepo(repo)
if err != nil {
msg := fmt.Sprintf("failure to load forge for repo '%s'", repo.FullName)
log.Error().Err(err).Str("repo", repo.FullName).Msg(msg)
return nil, errors.New(msg)
}
if lastPipeline.Status == model.StatusBlocked {
return nil, &ErrBadRequest{Msg: "cannot restart a pipeline with status blocked"}
}
// fetch the old pipeline config from the database
configs, err := store.ConfigsForPipeline(lastPipeline.ID)
if err != nil {
log.Error().Err(err).Msgf("failure to get pipeline config for %s", repo.FullName)
return nil, &ErrNotFound{Msg: fmt.Sprintf("failure to get pipeline config for %s. %s", repo.FullName, err)}
}
var pipelineFiles []*forge_types.FileMeta
for _, y := range configs {
pipelineFiles = append(pipelineFiles, &forge_types.FileMeta{Data: y.Data, Name: y.Name})
}
// If the config service is active we should refetch the config in case something changed
configService := server.Config.Services.Manager.ConfigServiceFromRepo(repo)
pipelineFiles, err = configService.Fetch(ctx, forge, user, repo, lastPipeline, pipelineFiles, true)
if err != nil {
return nil, &ErrBadRequest{
Msg: fmt.Sprintf("On fetching external pipeline config: %s", err),
}
}
newPipeline := createNewOutOfOld(lastPipeline)
newPipeline.Parent = lastPipeline.Number
err = store.CreatePipeline(newPipeline)
if err != nil {
msg := fmt.Sprintf("failure to save pipeline for %s", repo.FullName)
log.Error().Err(err).Msg(msg)
return nil, errors.New(msg)
}
if len(configs) == 0 {
newPipeline, uErr := UpdateToStatusError(store, *newPipeline, errors.New("There was an error loading the pipeline definition of the current run. This is usually caused by connectivity/timeout issues on the forge side. Restarting the build will not help, a new (fresh) commit (or force-push) must be issued to trigger a clean reload.")) //nolint
if uErr != nil {
log.Debug().Err(uErr).Msg("failure to update pipeline status")
} else {
updatePipelineStatus(ctx, forge, newPipeline, repo, user)
}
return newPipeline, nil
}
if err := linkPipelineConfigs(store, configs, newPipeline.ID); err != nil {
msg := fmt.Sprintf("failure to persist pipeline config for %s.", repo.FullName)
log.Error().Err(err).Msg(msg)
return nil, errors.New(msg)
}
newPipeline, pipelineItems, err := createPipelineItems(ctx, forge, store, newPipeline, user, repo, pipelineFiles, envs)
if err != nil {
msg := fmt.Sprintf("failure to createPipelineItems for %s", repo.FullName)
log.Error().Err(err).Msg(msg)
return nil, errors.New(msg)
}
if err := prepareStart(ctx, forge, store, newPipeline, user, repo); err != nil {
msg := fmt.Sprintf("failure to prepare pipeline for %s", repo.FullName)
log.Error().Err(err).Msg(msg)
return nil, errors.New(msg)
}
newPipeline, err = start(ctx, forge, store, newPipeline, user, repo, pipelineItems)
if err != nil {
msg := fmt.Sprintf("failure to start pipeline for %s", repo.FullName)
log.Error().Err(err).Msg(msg)
return nil, errors.New(msg)
}
return newPipeline, nil
}
func linkPipelineConfigs(store store.Store, configs []*model.Config, pipelineID int64) error {
for _, conf := range configs {
pipelineConfig := &model.PipelineConfig{
ConfigID: conf.ID,
PipelineID: pipelineID,
}
err := store.PipelineConfigCreate(pipelineConfig)
if err != nil {
return err
}
}
return nil
}
func createNewOutOfOld(old *model.Pipeline) *model.Pipeline {
newPipeline := *old
newPipeline.ID = 0
newPipeline.Number = 0
newPipeline.Status = model.StatusPending
newPipeline.Started = 0
newPipeline.Finished = 0
newPipeline.Errors = nil
return &newPipeline
}