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"
2022-08-15 12:37:46 +00:00
"time"
2022-06-15 19:33:29 +00:00
"github.com/rs/zerolog/log"
"github.com/woodpecker-ci/woodpecker/server"
2022-11-04 23:35:06 +00:00
"github.com/woodpecker-ci/woodpecker/server/forge"
2022-11-06 11:44:04 +00:00
"github.com/woodpecker-ci/woodpecker/server/forge/types"
2022-06-15 19:33:29 +00:00
"github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/store"
)
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 )
}
2022-11-04 23:35: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.
2022-11-04 23:35:06 +00:00
if refresher , ok := server . Config . Services . Forge . ( forge . Refresher ) ; ok {
2022-06-15 19:33:29 +00:00
refreshed , err := refresher . Refresh ( ctx , repoUser )
if err != nil {
log . Error ( ) . Err ( err ) . Msgf ( "failed to refresh oauth2 token for repoUser: %s" , repoUser . Login )
} else if refreshed {
if err := _store . UpdateUser ( repoUser ) ; err != nil {
log . Error ( ) . Err ( err ) . Msgf ( "error while updating repoUser: %s" , repoUser . Login )
// move forward
}
}
}
2022-08-15 12:37:46 +00:00
var (
2022-11-06 11:44:04 +00:00
forgeYamlConfigs [ ] * types . FileMeta
2022-11-04 23:35:06 +00:00
configFetchErr error
filtered bool
parseErr error
2022-08-15 12:37:46 +00:00
)
2022-11-04 23:35:06 +00:00
// fetch the pipeline file from the forge
2023-02-01 17:53:19 +00:00
configFetcher := forge . NewConfigFetcher ( server . Config . Services . Forge , server . Config . Services . Timeout , server . Config . Services . ConfigService , repoUser , repo , pipeline )
2022-11-04 23:35:06 +00:00
forgeYamlConfigs , configFetchErr = configFetcher . Fetch ( ctx )
2022-08-15 12:37:46 +00:00
if configFetchErr == nil {
2022-11-04 23:35:06 +00:00
filtered , parseErr = checkIfFiltered ( pipeline , forgeYamlConfigs )
2022-08-15 12:37:46 +00:00
if parseErr == nil {
if filtered {
err := ErrFiltered { Msg : "branch does not match restrictions defined in yaml" }
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Msgf ( "%v" , err )
return nil , err
}
2022-06-15 19:33:29 +00:00
2022-11-04 23:35:06 +00:00
if zeroSteps ( pipeline , forgeYamlConfigs ) {
2022-08-15 12:37:46 +00:00
err := ErrFiltered { Msg : "step conditions yield zero runnable steps" }
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Msgf ( "%v" , err )
return nil , err
}
}
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 . Verified = true
pipeline . Status = model . StatusPending
2022-06-15 19:33:29 +00:00
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 )
pipeline . Started = time . Now ( ) . Unix ( )
pipeline . Finished = pipeline . Started
pipeline . Status = model . StatusError
pipeline . Error = fmt . Sprintf ( "pipeline definition not found in %s" , repo . FullName )
2022-08-15 12:37:46 +00:00
} else if parseErr != nil {
log . Debug ( ) . Str ( "repo" , repo . FullName ) . Err ( parseErr ) . Msg ( "failed to parse yaml" )
2022-10-18 01:24:12 +00:00
pipeline . Started = time . Now ( ) . Unix ( )
pipeline . Finished = pipeline . Started
pipeline . Status = model . StatusError
pipeline . Error = fmt . Sprintf ( "failed to parse pipeline: %s" , parseErr . Error ( ) )
2022-08-15 12:37:46 +00:00
} else if repo . IsGated {
// TODO(336) extend gated feature with an allow/block List
2022-10-18 01:24:12 +00:00
pipeline . Status = model . StatusBlocked
2022-06-15 19:33:29 +00:00
}
2022-10-28 15:38:53 +00:00
err = _store . CreatePipeline ( pipeline , pipeline . Steps ... )
2022-06-15 19:33:29 +00:00
if err != nil {
2022-10-18 01:24:12 +00:00
msg := fmt . Sprintf ( "failure to save 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
// persist the pipeline config for historical correctness, restarts, etc
2022-11-04 23:35:06 +00:00
for _ , forgeYamlConfig := range forgeYamlConfigs {
_ , err := findOrPersistPipelineConfig ( _store , pipeline , forgeYamlConfig )
2022-06-15 19:33:29 +00:00
if err != nil {
msg := fmt . Sprintf ( "failure to find or persist pipeline config for %s" , repo . FullName )
log . Error ( ) . Err ( err ) . Msg ( msg )
return nil , fmt . Errorf ( msg )
}
}
2022-10-18 01:24:12 +00:00
if pipeline . Status == model . StatusError {
if err := publishToTopic ( ctx , pipeline , repo ) ; err != nil {
2022-08-15 12:37:46 +00:00
log . Error ( ) . Err ( err ) . Msg ( "publishToTopic" )
}
2022-10-18 01:24:12 +00:00
if err := updatePipelineStatus ( ctx , pipeline , repo , repoUser ) ; err != nil {
log . Error ( ) . Err ( err ) . Msg ( "updatePipelineStatus" )
2022-08-15 12:37:46 +00:00
}
2022-10-18 01:24:12 +00:00
return pipeline , nil
2022-08-15 12:37:46 +00:00
}
2022-11-04 23:35:06 +00:00
pipeline , pipelineItems , err := createPipelineItems ( ctx , _store , pipeline , repoUser , repo , forgeYamlConfigs , nil )
2022-06-15 19:33:29 +00:00
if err != nil {
2022-10-18 01:24:12 +00:00
msg := fmt . Sprintf ( "failure to createPipelineItems 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 {
if err := publishToTopic ( ctx , pipeline , repo ) ; err != nil {
2022-06-15 19:33:29 +00:00
log . Error ( ) . Err ( err ) . Msg ( "publishToTopic" )
}
2022-10-18 01:24:12 +00:00
if err := updatePipelineStatus ( ctx , pipeline , repo , repoUser ) ; err != nil {
log . Error ( ) . Err ( err ) . Msg ( "updatePipelineStatus" )
2022-06-15 19:33:29 +00:00
}
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 {
2022-10-18 01:24:12 +00:00
msg := fmt . Sprintf ( "failure 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
}