2022-06-15 19:33:29 +00:00
// 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"
2023-12-21 10:13:25 +00:00
"errors"
2022-06-15 19:33:29 +00:00
"fmt"
2023-10-09 16:15:53 +00:00
"regexp"
2022-06-15 19:33:29 +00:00
"github.com/rs/zerolog/log"
2023-12-21 10:13:25 +00:00
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
2023-12-08 07:15:08 +00:00
"go.woodpecker-ci.org/woodpecker/v2/server"
"go.woodpecker-ci.org/woodpecker/v2/server/forge"
2023-12-21 10:13:25 +00:00
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
2023-12-08 07:15:08 +00:00
"go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/server/store"
2022-06-15 19:33:29 +00:00
)
2023-10-09 16:15:53 +00:00
var skipPipelineRegex = regexp . MustCompile ( ` \[(?i:ci *skip|skip *ci)\] ` )
2024-05-13 20:58:21 +00:00
// Create a new pipeline and start it.
2022-10-18 01:24:12 +00:00
func Create ( ctx context . Context , _store store . Store , repo * model . Repo , pipeline * model . Pipeline ) ( * model . Pipeline , error ) {
2022-06-15 19:33:29 +00:00
repoUser , err := _store . GetUser ( repo . UserID )
if err != nil {
msg := fmt . Sprintf ( "failure to find repo owner via id '%d'" , repo . UserID )
log . Error ( ) . Err ( err ) . Str ( "repo" , repo . FullName ) . Msg ( msg )
return nil , fmt . Errorf ( msg )
}
2024-04-14 12:25:28 +00:00
if pipeline . Event == model . EventPush || pipeline . Event == model . EventPull || pipeline . Event == model . EventPullClosed {
skipMatch := skipPipelineRegex . FindString ( pipeline . Message )
if len ( skipMatch ) > 0 {
ref := pipeline . Commit
if len ( ref ) == 0 {
ref = pipeline . Ref
}
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Msgf ( "ignoring pipeline as skip-ci was found in the commit (%s) message '%s'" , ref , pipeline . Message )
return nil , ErrFiltered
2024-01-30 16:39:00 +00:00
}
2023-10-09 16:15:53 +00:00
}
2024-04-16 06:04:55 +00:00
_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 , fmt . Errorf ( msg )
}
2023-10-08 12:05:06 +00:00
// If the forge has a refresh token, the current access token
2022-06-15 19:33:29 +00:00
// may be stale. Therefore, we should refresh prior to dispatching
2022-10-18 01:24:12 +00:00
// the pipeline.
2024-02-11 17:42:33 +00:00
forge . Refresh ( ctx , _forge , _store , repoUser )
2022-06-15 19:33:29 +00:00
2022-10-18 01:24:12 +00:00
// update some pipeline fields
pipeline . RepoID = repo . ID
2023-12-12 20:30:52 +00:00
pipeline . Status = model . StatusCreated
2023-11-26 18:59:36 +00:00
setGatedState ( repo , pipeline )
err = _store . CreatePipeline ( pipeline )
if err != nil {
msg := fmt . Errorf ( "failed to save pipeline for %s" , repo . FullName )
log . Error ( ) . Str ( "repo" , repo . FullName ) . Err ( err ) . Msg ( msg . Error ( ) )
return nil , msg
}
2022-06-15 19:33:29 +00:00
2023-10-08 12:05:06 +00:00
// fetch the pipeline file from the forge
2024-02-11 17:42:33 +00:00
configService := server . Config . Services . Manager . ConfigServiceFromRepo ( repo )
forgeYamlConfigs , configFetchErr := configService . Fetch ( ctx , _forge , repoUser , repo , pipeline , nil , false )
2023-12-21 10:13:25 +00:00
if errors . Is ( configFetchErr , & forge_types . ErrConfigNotFound { } ) {
2022-10-18 01:24:12 +00:00
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Err ( configFetchErr ) . Msgf ( "cannot find config '%s' in '%s' with user: '%s'" , repo . Config , pipeline . Ref , repoUser . Login )
2023-12-21 10:13:25 +00:00
if err := _store . DeletePipeline ( pipeline ) ; err != nil {
log . Error ( ) . Str ( "repo" , repo . FullName ) . Err ( err ) . Msg ( "failed to delete pipeline without config" )
}
return nil , ErrFiltered
} else if configFetchErr != nil {
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Err ( configFetchErr ) . Msgf ( "error while fetching config '%s' in '%s' with user: '%s'" , repo . Config , pipeline . Ref , repoUser . Login )
2024-04-16 06:04:55 +00:00
return nil , updatePipelineWithErr ( ctx , _forge , _store , pipeline , repo , repoUser , fmt . Errorf ( "pipeline definition not found in %s" , repo . FullName ) )
2023-10-08 12:05:06 +00:00
}
2024-04-16 06:04:55 +00:00
pipelineItems , parseErr := parsePipeline ( _forge , _store , pipeline , repoUser , repo , forgeYamlConfigs , nil )
2023-12-21 10:13:25 +00:00
if pipeline_errors . HasBlockingErrors ( parseErr ) {
2022-08-15 12:37:46 +00:00
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Err ( parseErr ) . Msg ( "failed to parse yaml" )
2024-04-16 06:04:55 +00:00
return nil , updatePipelineWithErr ( ctx , _forge , _store , pipeline , repo , repoUser , parseErr )
2023-11-03 10:44:03 +00:00
} else if parseErr != nil {
2023-12-21 10:13:25 +00:00
pipeline . Errors = pipeline_errors . GetPipelineErrors ( parseErr )
2022-06-15 19:33:29 +00:00
}
2023-10-08 12:05:06 +00:00
if len ( pipelineItems ) == 0 {
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Msg ( ErrFiltered . Error ( ) )
2023-12-12 20:30:52 +00:00
if err := _store . DeletePipeline ( pipeline ) ; err != nil {
log . Error ( ) . Str ( "repo" , repo . FullName ) . Err ( err ) . Msg ( "failed to delete empty pipeline" )
}
2023-10-08 12:05:06 +00:00
return nil , ErrFiltered
}
pipeline = setPipelineStepsOnPipeline ( pipeline , pipelineItems )
2022-10-18 01:24:12 +00:00
// persist the pipeline config for historical correctness, restarts, etc
2023-10-08 12:05:06 +00:00
var configs [ ] * model . Config
2022-11-04 23:35:06 +00:00
for _ , forgeYamlConfig := range forgeYamlConfigs {
2023-10-08 12:05:06 +00:00
config , err := findOrPersistPipelineConfig ( _store , pipeline , forgeYamlConfig )
2022-06-15 19:33:29 +00:00
if err != nil {
2023-10-08 12:05:06 +00:00
msg := fmt . Sprintf ( "failed to find or persist pipeline config for %s" , repo . FullName )
2022-06-15 19:33:29 +00:00
log . Error ( ) . Err ( err ) . Msg ( msg )
return nil , fmt . Errorf ( msg )
}
2023-10-08 12:05:06 +00:00
configs = append ( configs , config )
2022-06-15 19:33:29 +00:00
}
2023-10-08 12:05:06 +00:00
// link pipeline to persisted configs
if err := linkPipelineConfigs ( _store , configs , pipeline . ID ) ; err != nil {
msg := fmt . Sprintf ( "failed to find or persist pipeline config for %s" , repo . FullName )
2022-06-15 19:33:29 +00:00
log . Error ( ) . Err ( err ) . Msg ( msg )
return nil , fmt . Errorf ( msg )
}
2024-04-16 06:04:55 +00:00
if err := prepareStart ( ctx , _forge , _store , pipeline , repoUser , repo ) ; err != nil {
2024-02-22 14:48:29 +00:00
log . Error ( ) . Err ( err ) . Str ( "repo" , repo . FullName ) . Msgf ( "error preparing pipeline for %s#%d" , repo . FullName , pipeline . Number )
return nil , err
}
2022-10-18 01:24:12 +00:00
if pipeline . Status == model . StatusBlocked {
return pipeline , nil
2022-06-15 19:33:29 +00:00
}
2024-04-16 06:04:55 +00:00
if err := updatePipelinePending ( ctx , _forge , _store , pipeline , repo , repoUser ) ; err != nil {
2023-12-17 16:44:48 +00:00
return nil , err
}
2024-04-16 06:04:55 +00:00
pipeline , err = start ( ctx , _forge , _store , pipeline , repoUser , repo , pipelineItems )
2022-06-15 19:33:29 +00:00
if err != nil {
2023-10-08 12:05:06 +00:00
msg := fmt . Sprintf ( "failed to start pipeline for %s" , repo . FullName )
2022-06-15 19:33:29 +00:00
log . Error ( ) . Err ( err ) . Msg ( msg )
return nil , fmt . Errorf ( msg )
}
2022-10-18 01:24:12 +00:00
return pipeline , nil
2022-06-15 19:33:29 +00:00
}
2023-10-08 12:05:06 +00:00
2024-04-16 06:04:55 +00:00
func updatePipelineWithErr ( ctx context . Context , _forge forge . Forge , _store store . Store , pipeline * model . Pipeline , repo * model . Repo , repoUser * model . User , err error ) error {
2023-12-15 09:03:05 +00:00
_pipeline , err := UpdateToStatusError ( _store , * pipeline , err )
if err != nil {
return err
2023-10-08 12:05:06 +00:00
}
2023-12-15 09:03:05 +00:00
// update value in ref
* pipeline = * _pipeline
2023-10-08 12:05:06 +00:00
2024-04-16 06:04:55 +00:00
publishPipeline ( ctx , _forge , pipeline , repo , repoUser )
2023-10-08 12:05:06 +00:00
return nil
}
2023-12-12 20:30:52 +00:00
2024-04-16 06:04:55 +00:00
func updatePipelinePending ( ctx context . Context , _forge forge . Forge , _store store . Store , pipeline * model . Pipeline , repo * model . Repo , repoUser * model . User ) error {
2023-12-15 09:03:05 +00:00
_pipeline , err := UpdateToStatusPending ( _store , * pipeline , "" )
if err != nil {
return err
2023-12-12 20:30:52 +00:00
}
2023-12-15 09:03:05 +00:00
// update value in ref
* pipeline = * _pipeline
2023-12-12 20:30:52 +00:00
2024-04-16 06:04:55 +00:00
publishPipeline ( ctx , _forge , pipeline , repo , repoUser )
2023-12-12 20:30:52 +00:00
return nil
}