diff --git a/.woodpecker/.build.yml b/.woodpecker/.build.yml index 4cddc6a..a4772b3 100644 --- a/.woodpecker/.build.yml +++ b/.woodpecker/.build.yml @@ -36,22 +36,6 @@ pipeline: when: event: push - cli: - image: 'chewingbever/vlang:latest' - environment: - - LDFLAGS=-static - commands: - - make cli-prod - # Make sure the binary is actually statically built - - readelf -d vieterctl - - du -h vieterctl - - '[ "$(readelf -d vieterctl | grep NEEDED | wc -l)" = 0 ]' - # This removes so much, it's amazing - - strip -s vieterctl - - du -h vieterctl - when: - event: push - upload: image: 'chewingbever/vlang:latest' secrets: [ s3_username, s3_password ] @@ -74,20 +58,5 @@ pipeline: -H "Content-Type: $CONTENT_TYPE" -H "Authorization: AWS $S3_USERNAME:$SIGNATURE" https://$URL$OBJ_PATH - - # Also update the CLI tool - - export OBJ_PATH="/vieter/commits/$CI_COMMIT_SHA/vieterctl-$(echo '${PLATFORM}' | sed 's:/:-:g')" - - export SIG_STRING="PUT\n\n$CONTENT_TYPE\n$DATE\n$OBJ_PATH" - - export SIGNATURE=`echo -en $SIG_STRING | openssl sha1 -hmac $S3_PASSWORD -binary | base64` - - > - curl - --silent - -XPUT - -T vieterctl - -H "Host: $URL" - -H "Date: $DATE" - -H "Content-Type: $CONTENT_TYPE" - -H "Authorization: AWS $S3_USERNAME:$SIGNATURE" - https://$URL$OBJ_PATH when: event: push diff --git a/Makefile b/Makefile index 7b0fb1b..2238585 100644 --- a/Makefile +++ b/Makefile @@ -42,18 +42,6 @@ pvieter: $(SOURCES) c: $(V) -o vieter.c $(SRC_DIR) -# Build the CLI tool -.PHONY: cli -cli: dvieterctl -dvieterctl: cli.v - $(V_PATH) -showcc -g -o dvieterctl cli.v - -.PHONY: cli-prod -cli-prod: vieterctl -vieterctl: cli.v -cli-prod: - $(V_PATH) -showcc -o vieterctl -prod cli.v - # =====EXECUTION===== # Run the server in the default 'data' directory .PHONY: run diff --git a/PKGBUILD b/PKGBUILD index 767c07c..cb0c24a 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Jef Roosens pkgbase='vieter' -pkgname=('vieter' 'vieterctl') +pkgname='vieter' pkgver=0.1.0.rc1.r45.g6d3ff8a pkgrel=1 depends=('glibc' 'openssl' 'libarchive' 'gc') @@ -23,21 +23,12 @@ build() { # Build the compiler CFLAGS= make v - # Build the server & the CLI tool make prod - make cli-prod } -package_vieter() { +package() { pkgdesc="Vieter is a lightweight implementation of an Arch repository server." install -dm755 "$pkgdir/usr/bin" install -Dm755 "$pkgbase/pvieter" "$pkgdir/usr/bin/vieter" } - -package_vieterctl() { - pkgdesc="Allows you to configure a Vieter server's list of Git repositories." - install -dm755 "$pkgdir/usr/bin" - - install -Dm755 "$pkgbase/vieterctl" "$pkgdir/usr/bin/vieterctl" -} diff --git a/src/build.v b/src/build/build.v similarity index 96% rename from src/build.v rename to src/build/build.v index 51de64c..f2ff9af 100644 --- a/src/build.v +++ b/src/build/build.v @@ -1,4 +1,4 @@ -module main +module build import docker import encoding.base64 @@ -7,6 +7,8 @@ import json import server import env import net.http +import cli +import git const container_build_dir = '/build' @@ -62,15 +64,13 @@ fn create_build_image() ?string { return image.id } -fn build() ? { - conf := env.load() ? - +fn build(conf env.BuildConfig) ? { // We get the repos list from the Vieter instance mut req := http.new_request(http.Method.get, '$conf.address/api/repos', '') ? req.add_custom_header('X-Api-Key', conf.api_key) ? res := req.do() ? - repos := json.decode([]server.GitRepo, res.text) ? + repos := json.decode([]git.GitRepo, res.text) ? // No point in doing work if there's no repos present if repos.len == 0 { diff --git a/src/build/cli.v b/src/build/cli.v new file mode 100644 index 0000000..bb3c016 --- /dev/null +++ b/src/build/cli.v @@ -0,0 +1,16 @@ +module build + +import cli +import env + +pub fn cmd() cli.Command { + return cli.Command{ + name: 'build' + description: 'Run the build process.' + execute: fn (cmd cli.Command) ? { + conf := env.load() ? + + build(conf) ? + } + } +} diff --git a/src/env.v b/src/env.v index bd544cf..3babd55 100644 --- a/src/env.v +++ b/src/env.v @@ -56,15 +56,9 @@ fn get_env_var(field_name string) ?string { } } -// load attempts to create the given type from environment variables. For -// each field, the corresponding env var is its name in uppercase prepended -// with the hardcoded prefix. If this one isn't present, it looks for the env -// var with the file_suffix suffix. -pub fn load() ?T { - res := T{} - +fn load_in_place(mut o T) ? { $for field in T.fields { - res.$(field.name) = get_env_var(field.name) or { + o.$(field.name) = get_env_var(field.name) or { // We use the default instead, if it's present mut default := '' @@ -82,5 +76,19 @@ pub fn load() ?T { default } } +} + +// load attempts to create the given type from environment variables. For +// each field, the corresponding env var is its name in uppercase prepended +// with the hardcoded prefix. If this one isn't present, it looks for the env +// var with the file_suffix suffix. +pub fn load() ?T { + mut res := T{} + + load_in_place(mut res) ? + return res } + +pub fn load_with_file(path string) ?T { +} diff --git a/src/git/cli.v b/src/git/cli.v new file mode 100644 index 0000000..a91f5e2 --- /dev/null +++ b/src/git/cli.v @@ -0,0 +1,37 @@ +module git + +import cli +import env +import net.http + +struct Config { + address string [required] + api_key string [required] +} + +pub fn cmd() cli.Command { + return cli.Command{ + name: 'repos' + description: 'Interact with the repos API.' + commands: [ + cli.Command{ + name: 'list' + description: 'List the current repos.' + execute: fn (cmd cli.Command) ? { + conf := env.load() ? + + list(conf) ? + } + }, + ] + } +} + +fn list(conf Config) ? { + mut req := http.new_request(http.Method.get, '$conf.address/api/repos', '') ? + req.add_custom_header('X-API-Key', conf.api_key) ? + + res := req.do() ? + + println(res.text) +} diff --git a/src/git/git.v b/src/git/git.v new file mode 100644 index 0000000..597aa3c --- /dev/null +++ b/src/git/git.v @@ -0,0 +1,39 @@ +module git + +import os +import json + +pub struct GitRepo { +pub: + url string [required] + branch string [required] +} + +pub fn read_repos(path string) ?[]GitRepo { + if !os.exists(path) { + mut f := os.create(path) ? + + defer { + f.close() + } + + f.write_string('[]') ? + + return [] + } + + content := os.read_file(path) ? + res := json.decode([]GitRepo, content) ? + return res +} + +pub fn write_repos(path string, repos []GitRepo) ? { + mut f := os.create(path) ? + + defer { + f.close() + } + + value := json.encode(repos) + f.write_string(value) ? +} diff --git a/src/main.v b/src/main.v index 156f0a3..18a822b 100644 --- a/src/main.v +++ b/src/main.v @@ -3,15 +3,33 @@ module main import os import server import util +import cli +import env +import build +import git fn main() { - if os.args.len == 1 { - util.exit_with_message(1, 'No action provided.') + mut app := cli.Command{ + name: 'vieter' + description: 'Arch repository server' + version: '0.1.0' + flags: [ + cli.Flag{ + flag: cli.FlagType.string + name: 'config-file' + abbrev: 'f' + description: 'Location of Vieter config file; defaults to ~/.vieterrc.' + global: true + default_value: [os.expand_tilde_to_home('~/.vieterrc')] + }, + ] + commands: [ + server.cmd(), + build.cmd(), + git.cmd(), + ] } - match os.args[1] { - 'server' { server.server() ? } - 'build' { build() ? } - else { util.exit_with_message(1, 'Unknown action: ${os.args[1]}') } - } + app.setup() + app.parse(os.args) } diff --git a/src/server/cli.v b/src/server/cli.v new file mode 100644 index 0000000..2b51c29 --- /dev/null +++ b/src/server/cli.v @@ -0,0 +1,16 @@ +module server + +import cli +import env + +pub fn cmd() cli.Command { + return cli.Command{ + name: 'server' + description: 'Start the Vieter server' + execute: fn (cmd cli.Command) ? { + conf := env.load() ? + + server(conf) ? + } + } +} diff --git a/src/server/git.v b/src/server/git.v index 0147d87..2e014db 100644 --- a/src/server/git.v +++ b/src/server/git.v @@ -3,44 +3,10 @@ module server import web import os import json +import git const repos_file = 'repos.json' -pub struct GitRepo { -pub: - url string [required] - branch string [required] -} - -fn read_repos(path string) ?[]GitRepo { - if !os.exists(path) { - mut f := os.create(path) ? - - defer { - f.close() - } - - f.write_string('[]') ? - - return [] - } - - content := os.read_file(path) ? - res := json.decode([]GitRepo, content) ? - return res -} - -fn write_repos(path string, repos []GitRepo) ? { - mut f := os.create(path) ? - - defer { - f.close() - } - - value := json.encode(repos) - f.write_string(value) ? -} - ['/api/repos'; get] fn (mut app App) get_repos() web.Result { if !app.is_authorized() { @@ -48,7 +14,7 @@ fn (mut app App) get_repos() web.Result { } repos := rlock app.git_mutex { - read_repos(app.conf.repos_file) or { + git.read_repos(app.conf.repos_file) or { app.lerror('Failed to read repos file.') return app.server_error(500) @@ -68,13 +34,13 @@ fn (mut app App) post_repo() web.Result { return app.server_error(400) } - new_repo := GitRepo{ + new_repo := git.GitRepo{ url: app.query['url'] branch: app.query['branch'] } mut repos := rlock app.git_mutex { - read_repos(app.conf.repos_file) or { + git.read_repos(app.conf.repos_file) or { app.lerror('Failed to read repos file.') return app.server_error(500) @@ -91,7 +57,7 @@ fn (mut app App) post_repo() web.Result { repos << new_repo lock app.git_mutex { - write_repos(app.conf.repos_file, repos) or { return app.server_error(500) } + git.write_repos(app.conf.repos_file, repos) or { return app.server_error(500) } } return app.ok('Repo added successfully.') @@ -107,13 +73,13 @@ fn (mut app App) delete_repo() web.Result { return app.server_error(400) } - repo_to_remove := GitRepo{ + repo_to_remove := git.GitRepo{ url: app.query['url'] branch: app.query['branch'] } mut repos := rlock app.git_mutex { - read_repos(app.conf.repos_file) or { + git.read_repos(app.conf.repos_file) or { app.lerror('Failed to read repos file.') return app.server_error(500) @@ -122,7 +88,7 @@ fn (mut app App) delete_repo() web.Result { filtered := repos.filter(it != repo_to_remove) lock app.git_mutex { - write_repos(app.conf.repos_file, filtered) or { return app.server_error(500) } + git.write_repos(app.conf.repos_file, filtered) or { return app.server_error(500) } } return app.ok('Repo removed successfully.') diff --git a/src/server/server.v b/src/server/server.v index 4b31b99..7f129e9 100644 --- a/src/server/server.v +++ b/src/server/server.v @@ -6,6 +6,7 @@ import log import repo import env import util +import cli const port = 8000 @@ -20,9 +21,7 @@ pub mut: } // server starts the web server & starts listening for requests -pub fn server() ? { - conf := env.load() ? - +pub fn server(conf env.ServerConfig) ? { // Configure logger log_level := log.level_from_tag(conf.log_level) or { util.exit_with_message(1, 'Invalid log level. The allowed values are FATAL, ERROR, WARN, INFO & DEBUG.')