2022-10-18 03:24:12 +02:00
// Copyright 2022 Woodpecker Authors
2018-02-19 14:24:10 -08:00
// Copyright 2018 Drone.IO Inc.
2018-03-10 20:09:14 +01:00
//
2018-02-19 14:24:10 -08:00
// 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
2018-03-10 20:09:14 +01:00
//
2018-02-19 14:24:10 -08:00
// http://www.apache.org/licenses/LICENSE-2.0
2018-03-10 20:09:14 +01:00
//
2018-02-19 14:24:10 -08:00
// 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.
2017-06-29 18:51:22 -04:00
package main
2017-05-03 23:25:33 +02:00
import (
2021-11-25 17:15:36 +01:00
"context"
2024-06-27 16:52:09 +02:00
"encoding/base32"
"errors"
2017-06-28 13:21:22 -04:00
"fmt"
2024-07-13 04:11:07 -07:00
"net/url"
2021-10-28 21:02:43 +02:00
"os"
2024-07-13 04:11:07 -07:00
"strings"
2017-09-20 12:29:57 -07:00
"time"
2017-06-28 13:21:22 -04:00
2021-10-12 02:25:13 -05:00
"github.com/rs/zerolog/log"
2025-12-20 09:23:09 +01:00
"github.com/tink-crypto/tink-go/v2/subtle/random"
2024-07-17 16:26:35 -07:00
"github.com/urfave/cli/v3"
2021-10-12 02:25:13 -05:00
2024-12-22 11:44:34 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server"
"go.woodpecker-ci.org/woodpecker/v3/server/cache"
"go.woodpecker-ci.org/woodpecker/v3/server/forge/setup"
"go.woodpecker-ci.org/woodpecker/v3/server/logging"
"go.woodpecker-ci.org/woodpecker/v3/server/model"
2026-03-30 19:32:44 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server/pubsub/memory"
2024-12-22 11:44:34 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server/queue"
2026-04-10 10:03:40 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server/scheduler"
2024-12-22 11:44:34 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server/services"
2026-03-28 15:45:23 +01:00
service_log "go.woodpecker-ci.org/woodpecker/v3/server/services/log"
2025-10-21 08:40:30 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server/services/log/addon"
2024-12-22 11:44:34 +02:00
"go.woodpecker-ci.org/woodpecker/v3/server/services/log/file"
"go.woodpecker-ci.org/woodpecker/v3/server/services/permissions"
"go.woodpecker-ci.org/woodpecker/v3/server/store"
"go.woodpecker-ci.org/woodpecker/v3/server/store/datastore"
"go.woodpecker-ci.org/woodpecker/v3/server/store/types"
2017-05-03 23:25:33 +02:00
)
2024-07-13 16:46:01 -07:00
const (
queueInfoRefreshInterval = 500 * time . Millisecond
storeInfoRefreshInterval = 10 * time . Second
)
2024-07-17 16:26:35 -07:00
func setupStore ( ctx context . Context , c * cli . Command ) ( store . Store , error ) {
2024-11-05 15:03:22 +01:00
datasource := c . String ( "db-datasource" )
driver := c . String ( "db-driver" )
2023-07-15 01:15:13 +02:00
xorm := store . XORM {
2024-11-05 15:03:22 +01:00
Log : c . Bool ( "db-log" ) ,
ShowSQL : c . Bool ( "db-log-sql" ) ,
2025-04-22 10:55:07 +02:00
MaxOpenConns : c . Int ( "db-max-open-connections" ) ,
MaxIdleConns : c . Int ( "db-max-idle-connections" ) ,
2024-11-05 15:03:22 +01:00
ConnMaxLifetime : c . Duration ( "db-max-connection-timeout" ) ,
2023-07-15 01:15:13 +02:00
}
2021-10-30 14:53:24 +02:00
2021-11-13 20:18:06 +01:00
if driver == "sqlite3" {
if datastore . SupportedDriver ( "sqlite3" ) {
2024-01-10 20:57:12 +01:00
log . Debug ( ) . Msg ( "server has sqlite3 support" )
2021-11-13 20:18:06 +01:00
} else {
2024-01-10 20:57:12 +01:00
log . Debug ( ) . Msg ( "server was built without sqlite3 support!" )
2021-11-13 20:18:06 +01:00
}
}
if ! datastore . SupportedDriver ( driver ) {
2024-01-10 15:34:44 +01:00
return nil , fmt . Errorf ( "database driver '%s' not supported" , driver )
2021-11-13 20:18:06 +01:00
}
if driver == "sqlite3" {
2023-07-28 13:31:25 +02:00
if err := checkSqliteFileExist ( datasource ) ; err != nil {
2024-01-10 15:34:44 +01:00
return nil , fmt . Errorf ( "check sqlite file: %w" , err )
2021-10-30 14:53:24 +02:00
}
2021-10-28 21:02:43 +02:00
}
2021-11-13 20:18:06 +01:00
opts := & store . Opts {
2021-10-30 14:53:24 +02:00
Driver : driver ,
Config : datasource ,
2023-07-15 01:15:13 +02:00
XORM : xorm ,
2021-10-19 11:44:49 +02:00
}
2024-12-18 16:46:36 +02:00
log . Debug ( ) . Str ( "driver" , driver ) . Any ( "xorm" , xorm ) . Msg ( "setting up datastore" )
2021-11-13 20:18:06 +01:00
store , err := datastore . NewEngine ( opts )
if err != nil {
2024-01-10 15:34:44 +01:00
return nil , fmt . Errorf ( "could not open datastore: %w" , err )
2021-11-13 20:18:06 +01:00
}
2025-03-17 07:16:16 +01:00
if err = store . Ping ( ) ; err != nil {
return nil , err
}
2024-07-13 16:46:01 -07:00
if err := store . Migrate ( ctx , c . Bool ( "migrations-allow-long" ) ) ; err != nil {
2024-01-10 15:34:44 +01:00
return nil , fmt . Errorf ( "could not migrate datastore: %w" , err )
2021-11-13 20:18:06 +01:00
}
2024-01-10 15:34:44 +01:00
return store , nil
2017-05-03 23:25:33 +02:00
}
2023-07-28 13:31:25 +02:00
func checkSqliteFileExist ( path string ) error {
_ , err := os . Stat ( path )
if err != nil && os . IsNotExist ( err ) {
log . Warn ( ) . Msgf ( "no sqlite3 file found, will create one at '%s'" , path )
return nil
2021-10-30 14:53:24 +02:00
}
2023-07-28 13:31:25 +02:00
return err
2021-10-28 21:02:43 +02:00
}
2024-11-05 04:03:40 +01:00
func setupQueue ( ctx context . Context , s store . Store ) ( queue . Queue , error ) {
return queue . New ( ctx , queue . Config {
Backend : queue . TypeMemory ,
Store : s ,
} )
2017-05-04 02:02:08 +02:00
}
2024-07-13 16:46:01 -07:00
func setupMembershipService ( _ context . Context , _store store . Store ) cache . MembershipService {
2024-04-16 08:04:55 +02:00
return cache . NewMembershipService ( _store )
2017-06-28 13:21:22 -04:00
}
2017-06-29 18:51:22 -04:00
2026-03-28 15:45:23 +01:00
func setupLogStore ( c * cli . Command , s store . Store ) ( service_log . Service , error ) {
2024-06-06 14:34:57 +02:00
switch c . String ( "log-store" ) {
case "file" :
return file . NewLogStore ( c . String ( "log-store-file-path" ) )
2025-10-21 08:40:30 +02:00
case "addon" :
return addon . Load ( c . String ( "log-store-file-path" ) )
2024-06-06 14:34:57 +02:00
default :
return s , nil
}
}
2024-06-27 16:52:09 +02:00
const jwtSecretID = "jwt-secret"
func setupJWTSecret ( _store store . Store ) ( string , error ) {
jwtSecret , err := _store . ServerConfigGet ( jwtSecretID )
2026-03-19 13:17:56 +01:00
if errors . Is ( err , types . ErrRecordNotExist ) {
2024-06-27 16:52:09 +02:00
jwtSecret := base32 . StdEncoding . EncodeToString (
2025-10-06 23:43:29 +02:00
random . GetRandomBytes ( 32 ) ,
2024-06-27 16:52:09 +02:00
)
err = _store . ServerConfigSet ( jwtSecretID , jwtSecret )
if err != nil {
return "" , err
}
log . Debug ( ) . Msg ( "created jwt secret" )
return jwtSecret , nil
}
if err != nil {
return "" , err
}
return jwtSecret , nil
}
2024-07-13 04:11:07 -07:00
2024-11-05 04:03:40 +01:00
func setupEvilGlobals ( ctx context . Context , c * cli . Command , s store . Store ) ( err error ) {
2024-07-13 04:11:07 -07:00
// services
server . Config . Services . Logs = logging . New ( )
2024-07-13 16:46:01 -07:00
server . Config . Services . Membership = setupMembershipService ( ctx , s )
2026-04-10 10:03:40 +02:00
pubsub := memory . New ( )
queue , err := setupQueue ( ctx , s )
2024-11-05 04:03:40 +01:00
if err != nil {
return fmt . Errorf ( "could not setup queue: %w" , err )
}
2026-04-10 10:03:40 +02:00
server . Config . Services . Scheduler = scheduler . NewScheduler ( queue , pubsub )
2024-11-05 04:03:40 +01:00
server . Config . Services . Manager , err = services . NewManager ( c , s , setup . Forge )
2024-07-13 04:11:07 -07:00
if err != nil {
return fmt . Errorf ( "could not setup service manager: %w" , err )
}
server . Config . Services . LogStore , err = setupLogStore ( c , s )
if err != nil {
return fmt . Errorf ( "could not setup log store: %w" , err )
}
2024-11-11 18:51:14 +01:00
// agents
server . Config . Agent . DisableUserRegisteredAgentRegistration = c . Bool ( "disable-user-agent-registration" )
2024-07-13 04:11:07 -07:00
// authentication
server . Config . Pipeline . AuthenticatePublicRepos = c . Bool ( "authenticate-public-repos" )
2026-06-01 11:37:31 +02:00
server . Config . Server . AsyncRepositoryUpdate = c . Bool ( "async-repository-update" )
2024-07-13 04:11:07 -07:00
2025-02-20 12:28:28 -05:00
// Pull requests
server . Config . Pipeline . DefaultAllowPullRequests = c . Bool ( "default-allow-pull-requests" )
2025-08-11 12:59:02 +02:00
// Approval mode
approvalMode := model . ApprovalMode ( c . String ( "default-approval-mode" ) )
if ! approvalMode . Valid ( ) {
return fmt . Errorf ( "approval mode %s is not valid" , approvalMode )
}
server . Config . Pipeline . DefaultApprovalMode = approvalMode
2024-07-13 04:11:07 -07:00
// Cloning
2024-09-01 20:41:10 +02:00
server . Config . Pipeline . DefaultClonePlugin = c . String ( "default-clone-plugin" )
server . Config . Pipeline . TrustedClonePlugins = c . StringSlice ( "plugins-trusted-clone" )
server . Config . Pipeline . TrustedClonePlugins = append ( server . Config . Pipeline . TrustedClonePlugins , server . Config . Pipeline . DefaultClonePlugin )
2024-07-13 04:11:07 -07:00
// Execution
_events := c . StringSlice ( "default-cancel-previous-pipeline-events" )
events := make ( [ ] model . WebhookEvent , 0 , len ( _events ) )
for _ , v := range _events {
2025-08-11 12:59:02 +02:00
e := model . WebhookEvent ( v )
if err := e . Validate ( ) ; err != nil {
return err
}
events = append ( events , e )
2024-07-13 04:11:07 -07:00
}
server . Config . Pipeline . DefaultCancelPreviousPipelineEvents = events
2025-04-22 10:55:07 +02:00
server . Config . Pipeline . DefaultTimeout = c . Int64 ( "default-pipeline-timeout" )
server . Config . Pipeline . MaxTimeout = c . Int64 ( "max-pipeline-timeout" )
2024-07-13 04:11:07 -07:00
2024-11-14 23:23:42 +02:00
_labels := c . StringSlice ( "default-workflow-labels" )
labels := make ( map [ string ] string , len ( _labels ) )
for _ , v := range _labels {
name , value , ok := strings . Cut ( v , "=" )
if ! ok {
return fmt . Errorf ( "invalid label filter: %s" , v )
}
labels [ name ] = value
}
server . Config . Pipeline . DefaultWorkflowLabels = labels
2024-07-13 04:11:07 -07:00
// backend options for pipeline compiler
server . Config . Pipeline . Proxy . No = c . String ( "backend-no-proxy" )
server . Config . Pipeline . Proxy . HTTP = c . String ( "backend-http-proxy" )
server . Config . Pipeline . Proxy . HTTPS = c . String ( "backend-https-proxy" )
2026-05-28 14:08:36 +02:00
// pipeline config paths
server . Config . Pipeline . ConfigPaths = c . StringSlice ( "default-pipeline-configs" )
server . Config . Pipeline . ConfigExtensions = c . StringSlice ( "default-pipeline-config-extensions" )
2024-07-13 04:11:07 -07:00
// server configuration
server . Config . Server . JWTSecret , err = setupJWTSecret ( s )
if err != nil {
return fmt . Errorf ( "could not setup jwt secret: %w" , err )
}
server . Config . Server . Cert = c . String ( "server-cert" )
server . Config . Server . Key = c . String ( "server-key" )
server . Config . Server . AgentToken = c . String ( "agent-secret" )
serverHost := strings . TrimSuffix ( c . String ( "server-host" ) , "/" )
server . Config . Server . Host = serverHost
if c . IsSet ( "server-webhook-host" ) {
server . Config . Server . WebhookHost = c . String ( "server-webhook-host" )
} else {
server . Config . Server . WebhookHost = serverHost
}
2024-07-23 14:25:39 +02:00
server . Config . Server . OAuthHost = serverHost
2024-07-13 04:11:07 -07:00
server . Config . Server . Port = c . String ( "server-addr" )
server . Config . Server . PortTLS = c . String ( "server-addr-tls" )
server . Config . Server . StatusContext = c . String ( "status-context" )
server . Config . Server . StatusContextFormat = c . String ( "status-context-format" )
server . Config . Server . SessionExpires = c . Duration ( "session-expires" )
u , _ := url . Parse ( server . Config . Server . Host )
rootPath := strings . TrimSuffix ( u . Path , "/" )
if rootPath != "" && ! strings . HasPrefix ( rootPath , "/" ) {
rootPath = "/" + rootPath
}
server . Config . Server . RootPath = rootPath
server . Config . Server . CustomCSSFile = strings . TrimSpace ( c . String ( "custom-css-file" ) )
server . Config . Server . CustomJsFile = strings . TrimSpace ( c . String ( "custom-js-file" ) )
server . Config . Pipeline . Networks = c . StringSlice ( "network" )
server . Config . Pipeline . Volumes = c . StringSlice ( "volume" )
server . Config . WebUI . EnableSwagger = c . Bool ( "enable-swagger" )
server . Config . WebUI . SkipVersionCheck = c . Bool ( "skip-version-check" )
2026-03-16 22:00:40 +01:00
server . Config . WebUI . MaxPipelineLogLineCount = c . Uint ( "max-pipeline-log-line-count" )
2024-09-02 10:41:20 +02:00
server . Config . Pipeline . PrivilegedPlugins = c . StringSlice ( "plugins-privileged" )
2024-08-31 19:04:47 +02:00
2026-04-17 17:10:03 +02:00
// TODO: remove with version 4.x
server . Config . Pipeline . ForceIgnoreServiceFailure = c . Bool ( "force-ignore-service-failure" )
if server . Config . Pipeline . ForceIgnoreServiceFailure {
log . Info ( ) . Msg ( "WOODPECKER_FORCE_IGNORE_SERVICE_FAILURE is true by default. To prepare for v4.0.0, set it to false and update your pipeline definitions if needed." )
}
2024-07-13 04:11:07 -07:00
// prometheus
server . Config . Prometheus . AuthToken = c . String ( "prometheus-auth-token" )
// permissions
server . Config . Permissions . Open = c . Bool ( "open" )
server . Config . Permissions . Admins = permissions . NewAdmins ( c . StringSlice ( "admin" ) )
server . Config . Permissions . Orgs = permissions . NewOrgs ( c . StringSlice ( "orgs" ) )
server . Config . Permissions . OwnersAllowlist = permissions . NewOwnersAllowlist ( c . StringSlice ( "repo-owners" ) )
return nil
}