My Very Own CI-server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
7.7 KiB

// core contains the most central elements of mvoCI, except the webserver.
// it contains the global config object and the database connection.
// could be used without the webserver to have the same backend for e.g. CLI UI
package core
import (
// configuration file
const ConfigFile string = "mvo.cfg"
// build time entered by the linker (see Makefile)
var BuildTime string = ""
// Compiler version entered by the linker (see Makefile)
var Compiler string = ""
// version entered by the linker (see Makefile), either release number or git-sha
var Version string = "development"
// git commit hash entered by the linker (see Makefile)
var GitHash string = ""
// structure for directory configurations
type ConfigDirectory struct {
Repo string // directory for checkout of the repositories for building
Build string // directory for storing the build artifacts
// information about the author for the impress
type ConfigAuthor struct {
Email string // email address of the webmaster
Name string // name of the webmaster
Street string // address information
Zip string // zip code
City string // the city
Privacy string // privacy and gdpr information text
// configuration about the database connection
type ConfigDatabase struct {
Provider string // database provider, i.e. sqlite, mssql, mysql or postgres (supported by
Username string // username for auth at the database
Password string // password for auth at the database
Port int // port of the databases socket
Host string // host, as it may be on another machine
Dbname string // name of the database to use
Filename string // filename of the sqlite database if used
PostgresSSL string // whether or not postgres should use ssl/tsl encryption
// configuration about the build page, with an ongoing build
// if the api is enabled, this gives the pace at which the pace at which query will be sent by javascript
type ConfigBuild struct {
RefreshPage bool // whether or not the page should be refreshed automagically at all
RefreshInterval int // interval in seconds at which the page should be refreshed automagically
// information about the API
// Enable the api for more elegant page refresh on ongoing builds and a search feature on repos
type ConfigApi struct {
Enable bool // is the API feature enabled?
ListingEnable bool // should a listing of the API be available
ListingNeedsAuth bool // is authentication needed for showing the listing of the API?
// information about (a single) OAuth token
type ConfigOauth struct {
OauthClientId string // OAuth Client ID
OauthClientSecret string // OAuth Secret
// global config structure
type Config struct {
AppTitle string // title of the application (defaults to mvoCI)
Install bool // is the application currently in installation mode
Debug bool // set it into debug mode
HttpPort int // the http port to be used
HttpHost string // the http host to be used
LogFile string // path to the log file
LogServer string // log file for the echo webserver
LogFileEnable bool // enable the file-log
LogMode string // writing logs to stdout or stderr?
LoginTokenDuration int // how long does a loginToken survive if inactive?
LoginTokenInvalidate bool // invalidate all loginTokens at start-up?
ParallelBuilds int // number of parallel workers to be spawned
CompressionMethod string // compression method to be used for the artifacts (file-ending to be used with tar command)
RepoSecretShow bool // should the repo-page show the secret or a placeholder?
Directory ConfigDirectory
Author ConfigAuthor
Database ConfigDatabase
WebHookLog bool // log events of the webhook for debugging purposes?
PublicEnable bool // have a page with the publically visible repositories?
Api ConfigApi
Build ConfigBuild
GiteaApi ConfigOauth
Auth ConfigAuth
// information about the authentication providers
type ConfigAuth struct {
TOTPEnable bool // is totp enabled?
NativeEnable bool // is native password auth enabled?
// generate the default configuration
func ConfigDefault () Config {
return Config{
AppTitle: "mvoCI",
Install: false,
Debug: false,
HttpPort: 4042,
HttpHost: "localhost",
LoginTokenDuration: 300,
LoginTokenInvalidate: true,
CompressionMethod: "gz",
ParallelBuilds: 2,
LogFile: "log/mvoCI.log",
LogFileEnable: true,
LogMode: "stderr",
LogServer: "log/server.log",
Directory: ConfigDirectory{
Repo: "repo/",
Build: "builds/",
WebHookLog: false,
PublicEnable: false,
Api: ConfigApi {
Enable: false,
ListingNeedsAuth: true,
ListingEnable: false,
Build: ConfigBuild {
RefreshPage: true,
RefreshInterval: 5000, // 1000 -> 1 second
Auth: ConfigAuth {
NativeEnable: true,
TOTPEnable: true,
// Author -> empty strings
// Database -> empty strings
// global configuration object
var cfg Config
// reflect into the config-object for using config in the templates, the recursive work-part of reflection
func (cfg Config) recurseReflect ( current reflect.Value, parts []string ) string {
var v reflect.Value = current.FieldByName ( parts[0] )
if v.Type().Name() == "int" {
return strconv.FormatInt( v.Int(), 10 )
} else if v.Type().Name() == "string" {
return v.String()
} else if v.Type().Name() == "bool" {
if v.Bool() {
return "true";
} else {
return "false";
if len (parts) > 1 {
return cfg.recurseReflect ( v, parts[1:] )
return "Invalid Type"
// reflect into the config-object for using config in the templates
func (cfg Config) Reflect ( q string ) string {
var parts []string = strings.Split ( q, "." )
current := reflect.Indirect(reflect.ValueOf(cfg))
var r string = cfg.recurseReflect ( current, parts )
//Console.Log ("Found value", r, "in reflecting cfg for", q)
return r
// write the config to the a file
func ConfigWrite ( cfg *Config, filename string ) error {
b, err := json.MarshalIndent ( cfg, "", " " )
if err != nil {
return err
err = ioutil.WriteFile ( filename, b, 0644 )
return err
// helper function for parsing JSON-files into an object of our choosing
func GenericJSONDecode ( rd io.Reader, pl interface{} ) error {
dec := json.NewDecoder( rd )
for dec.More() {
err := dec.Decode( pl )
if err != nil {
// early debug
//Console.Log ( "JSON-decode error: ", err )
return err
return nil
// reads the configuration from file to a KeyValueStore-object
func ConfigRead ( filename string ) (Config, error) {
cfg = ConfigDefault()
f, err := os.Open ( filename )
if err != nil {
// Todo: early console
//Console.Fail ("Cannot open config file")
return cfg, err;
defer f.Close();
err = GenericJSONDecode ( f, &cfg )
if err != nil {
// TODO: early config
//Console.Log ( "Fatal error", err )
return cfg, err;
return cfg, nil;