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"
"fmt"
2023-10-09 16:15:53 +00:00
"regexp"
2022-08-15 12:37:46 +00:00
"time"
2022-06-15 19:33:29 +00:00
"github.com/rs/zerolog/log"
2023-11-07 07:04:33 +00:00
"go.woodpecker-ci.org/woodpecker/pipeline/errors"
"go.woodpecker-ci.org/woodpecker/server"
"go.woodpecker-ci.org/woodpecker/server/forge"
"go.woodpecker-ci.org/woodpecker/server/model"
"go.woodpecker-ci.org/woodpecker/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)\] ` )
2022-10-18 01:24:12 +00:00
// Create a new pipeline and start it
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 )
}
2023-10-09 16:15:53 +00:00
skipMatch := skipPipelineRegex . FindString ( pipeline . Message )
if len ( skipMatch ) > 0 {
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Msgf ( "ignoring pipeline as skip-ci was found in the commit (%s) message '%s'" , pipeline . Commit , pipeline . Message )
return nil , ErrFiltered
}
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.
2023-10-08 12:05:06 +00:00
forge . Refresh ( ctx , server . Config . Services . 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
pipeline . Status = model . StatusPending
2022-06-15 19:33:29 +00:00
2023-10-08 12:05:06 +00:00
// fetch the pipeline file from the forge
configFetcher := forge . NewConfigFetcher ( server . Config . Services . Forge , server . Config . Services . Timeout , server . Config . Services . ConfigService , repoUser , repo , pipeline )
forgeYamlConfigs , configFetchErr := configFetcher . Fetch ( ctx )
2022-08-15 12:37:46 +00:00
if configFetchErr != nil {
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-11-03 10:44:03 +00:00
return nil , persistPipelineWithErr ( ctx , _store , pipeline , repo , repoUser , fmt . Errorf ( "pipeline definition not found in %s" , repo . FullName ) )
2023-10-08 12:05:06 +00:00
}
pipelineItems , parseErr := parsePipeline ( _store , pipeline , repoUser , repo , forgeYamlConfigs , nil )
2023-11-03 10:44:03 +00:00
if errors . HasBlockingErrors ( parseErr ) {
2022-08-15 12:37:46 +00:00
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Err ( parseErr ) . Msg ( "failed to parse yaml" )
2023-11-03 10:44:03 +00:00
return nil , persistPipelineWithErr ( ctx , _store , pipeline , repo , repoUser , parseErr )
} else if parseErr != nil {
pipeline . Errors = 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 ( ) )
return nil , ErrFiltered
}
setGatedState ( repo , pipeline )
2023-06-27 16:01:18 +00:00
err = _store . CreatePipeline ( pipeline )
2022-06-15 19:33:29 +00:00
if err != nil {
2023-10-08 12:05:06 +00:00
msg := fmt . Errorf ( "failed to save pipeline for %s" , repo . FullName )
2023-11-04 05:51:26 +00:00
log . Error ( ) . Str ( "repo" , repo . FullName ) . Err ( err ) . Msg ( msg . Error ( ) )
2023-10-08 12:05:06 +00:00
return nil , msg
2022-06-15 19:33:29 +00:00
}
2023-10-08 12:05:06 +00:00
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 )
}
2022-10-18 01:24:12 +00:00
if pipeline . Status == model . StatusBlocked {
2023-10-08 12:05:06 +00:00
publishPipeline ( ctx , pipeline , repo , repoUser )
2022-10-18 01:24:12 +00:00
return pipeline , nil
2022-06-15 19:33:29 +00:00
}
2022-10-18 01:24:12 +00:00
pipeline , err = start ( ctx , _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
2023-11-03 10:44:03 +00:00
func persistPipelineWithErr ( ctx context . Context , _store store . Store , pipeline * model . Pipeline , repo * model . Repo , repoUser * model . User , err error ) error {
2023-10-08 12:05:06 +00:00
pipeline . Started = time . Now ( ) . Unix ( )
pipeline . Finished = pipeline . Started
pipeline . Status = model . StatusError
2023-11-03 10:44:03 +00:00
pipeline . Errors = errors . GetPipelineErrors ( err )
2023-10-08 12:05:06 +00:00
dbErr := _store . CreatePipeline ( pipeline )
if dbErr != nil {
msg := fmt . Errorf ( "failed to save pipeline for %s" , repo . FullName )
log . Error ( ) . Err ( dbErr ) . Msg ( msg . Error ( ) )
return msg
}
publishPipeline ( ctx , pipeline , repo , repoUser )
return nil
}