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.
 
 
 
 
 

184 lines
5.8 KiB

// mvoCI is a simple continuous integration server aimed at personal use.
// You really should not use it as an open CI server and only provide it to
// people and code you trust. mvoCI does not isolate build-processes well, if
// you need that, please choose a different solution.
//
// mvoCI aims to be simple and easy to use, not a lot of features, just enough
// to build simple projects and maybe deploy them to your server fo example.
// It is not equipped with distributed building atm (while you of course could
// script that in the build script).
//
// Be aware, mvoCI lets you shoot of your foot. Don't.
package main
import (
"os"
"fmt"
"flag"
"time"
"bytes"
"errors"
"strconv"
"net/http"
"io/ioutil"
. "codeberg.org/snaums/mvoCI/core"
"codeberg.org/snaums/mvoCI/core"
"codeberg.org/snaums/mvoCI/web"
"codeberg.org/snaums/mvoCI/build"
. "codeberg.org/snaums/mvoCI/hook"
"encoding/json"
"gorm.io/gorm"
)
// send a webhook to the local instance of mvoCI to trigger a build event
func SendLocalBuild ( db *gorm.DB, port int, rID uint, secret string ) error {
var r core.Repository
db.Where("id = ?", rID).First( &r )
if r.ID != rID {
return errors.New ("Repository not found")
}
if r.Secret != secret {
return errors.New ("Secret not accepted")
}
if r.LocalBuildEnable == false {
return errors.New ("Local Build is not enabled")
}
var l LocalPayload = LocalPayload {
Secret: r.Secret,
RepositoryID: r.ID,
RepositoryName: r.Name,
Action: "build",
}
// set-up JSON data
b, err := json.Marshal(l)
if err != nil {
return err
}
// send JSON data to the local instance (find out the correct URL)
url := "http://127.0.0.1:" + strconv.FormatInt ( int64(port), 10 ) + "/push/hook/local"
req, _ := http.NewRequest( "POST", url, bytes.NewBuffer( b ) )
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
// wait for response and report an error back (or nil if success)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, _ := ioutil.ReadAll ( resp.Body )
return errors.New( string(body) )
}
return nil
}
// entry point of mvoCI, sets up the config data structure for the rest of the
// system and bootstraps the database and the web-server
func main() {
// set up command line parameters
var install bool;
var w bool;
var port int;
var d bool;
var localhook bool;
var repositoryID int;
var localSecret string;
var err error
flag.BoolVar ( &w, "web", false, "Starts the complete web-application, if not: only webhook and local hook");
flag.BoolVar ( &install, "install", false, "Starts " + os.Args[0] + " with installer enabled")
flag.IntVar ( &port, "port", 0, "Specify the port to bind to")
flag.BoolVar ( &d, "debug", false, "Starts " + os.Args[0] + " with verbose debug output")
flag.BoolVar ( &localhook, "local", false, "Send a local build hook")
flag.IntVar ( &repositoryID, "repository", -1, "The ID of the repository to build")
flag.StringVar ( &localSecret, "secret", "", "Secret for starting a build locally")
flag.Parse()
// read the config
var cfg core.Config;
cfg, err = core.ConfigRead ( core.ConfigFile )
if ( install ) {
cfg.Install = true
}
os.Mkdir ( "log", 0777 )
lf := cfg.LogFile;
lfenable := cfg.LogFileEnable;
lmode := cfg.LogMode;
if lfenable == true && len(lf) > 0 {
core.LogFileEnable = true;
}
if lmode == "stdout" {
core.LogStdoutEnable = true;
core.LogStderrEnable = false;
} else {
core.LogStdoutEnable = false;
core.LogStderrEnable = true;
}
if ( d ) {
cfg.Debug = true;
}
if ( port != 0 ) {
cfg.HttpPort = port
}
core.LogInit ( lf )
defer core.LogClose();
Console.Log ("Read Configuration (", ConfigFile, ") successfully" )
// set up gorm database connection
var db *gorm.DB;
if cfg.Install == true {
Console.Log ("Installation mode: Skipping Database connection");
db = nil
} else {
Console.Log ("Connecting to database: ", cfg.Database.Provider );
db, err = core.DBConnect ( &cfg );
if err != nil {
Console.Log ("Cannot connect to database: ", err )
}
}
// send build hook locally
if cfg.Install != true && localhook {
if repositoryID < 0 {
fmt.Println ("[FAIL] No valid repository ID given")
} else {
if db == nil {
fmt.Println ("[FAIL] Cannot procede with no database connection")
}
err = SendLocalBuild( db, cfg.HttpPort, uint(repositoryID), localSecret )
if err == nil {
fmt.Println ("[ OK ] Sending hook for R (", repositoryID, ") successful")
} else {
fmt.Println ("[FAIL] An error occured while sending hook for R (", repositoryID, "): ", err)
}
}
return;
}
if cfg.Install == false {
Console.Log ( "Setting up build workers" )
build.SetupWorkerThreads ( &cfg, db );
if cfg.LoginTokenInvalidate == true {
db.Where("type = ?", "login").Delete ( &core.LoginToken{} )
} else {
db.Where ( "type = ? AND (step != ? OR expires_at < ?)", "login", "fin", time.Now() ).Delete( &core.LoginToken{} );
}
}
Console.Log ( "Starting", cfg.AppTitle )
// start eh web server and hand everything over to it
server := web.NewServer ( &cfg, db )
server.Start();
}