Some final stuff before 0.5.0 #323
			
				
			
		
		
		
	|  | @ -7,12 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ||||||
| 
 | 
 | ||||||
| ## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev) | ## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev) | ||||||
| 
 | 
 | ||||||
|  | ### Added | ||||||
|  | 
 | ||||||
|  | * CLI commands for removing packages, arch-repos & repositories | ||||||
|  | 
 | ||||||
| ## [0.5.0-rc.2](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0-rc.2) | ## [0.5.0-rc.2](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0-rc.2) | ||||||
| 
 | 
 | ||||||
| ### Added | ### Added | ||||||
| 
 | 
 | ||||||
| * API route for removing logs & accompanying CLI command | * API route for removing logs & accompanying CLI command | ||||||
| * Daemon for periodically removing old logs | * Daemon for periodically removing old logs | ||||||
|  | * CLI flag to filter logs by specific exit codes | ||||||
| 
 | 
 | ||||||
| ### Changed | ### Changed | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							|  | @ -3,7 +3,7 @@ SRC_DIR := src | ||||||
| SOURCES != find '$(SRC_DIR)' -iname '*.v' | SOURCES != find '$(SRC_DIR)' -iname '*.v' | ||||||
| 
 | 
 | ||||||
| V_PATH ?= v | V_PATH ?= v | ||||||
| V := $(V_PATH) -showcc -gc boehm -W -d use_openssl | V := $(V_PATH) -showcc -gc boehm -W -d use_openssl -skip-unused | ||||||
| 
 | 
 | ||||||
| all: vieter | all: vieter | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -59,10 +59,9 @@ configuration variable required for each command. | ||||||
|       ([GitHub](https://github.com/Menci/docker-archlinuxarm)). This is the |       ([GitHub](https://github.com/Menci/docker-archlinuxarm)). This is the | ||||||
|       image used for the Vieter CI builds. |       image used for the Vieter CI builds. | ||||||
| * `max_log_age`: maximum age of logs (in days). Logs older than this will get | * `max_log_age`: maximum age of logs (in days). Logs older than this will get | ||||||
|   cleaned by the log removal daemon. If set to a negative value, no logs are |   cleaned by the log removal daemon. If set to zero, no logs are ever removed. | ||||||
|   ever removed. The age of logs is determined by the time the build was |   The age of logs is determined by the time the build was started. | ||||||
|   started. |     * Default: `0` | ||||||
|     * Default: `-1` |  | ||||||
| * `log_removal_schedule`: cron schedule defining when to clean old logs. | * `log_removal_schedule`: cron schedule defining when to clean old logs. | ||||||
|     * Default: `0 0` (every day at midnight) |     * Default: `0 0` (every day at midnight) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| --- |  | ||||||
| weight: 100 |  | ||||||
| --- |  | ||||||
|  | @ -1,81 +0,0 @@ | ||||||
| # Builds In-depth |  | ||||||
| 
 |  | ||||||
| For those interested, this page describes how the build system works |  | ||||||
| internally. |  | ||||||
| 
 |  | ||||||
| ## Builder image |  | ||||||
| 
 |  | ||||||
| Every cron daemon perodically creates a builder image that is then used as a |  | ||||||
| base for all builds. This is done to prevent build containers having to pull |  | ||||||
| down a bunch of updates when they update their system. |  | ||||||
| 
 |  | ||||||
| The build container is created by running the following commands inside a |  | ||||||
| container started from the image defined in `base_image`: |  | ||||||
| 
 |  | ||||||
| ```sh |  | ||||||
| # Update repos & install required packages |  | ||||||
| pacman -Syu --needed --noconfirm base-devel git |  | ||||||
| # Add a non-root user to run makepkg |  | ||||||
| groupadd -g 1000 builder |  | ||||||
| useradd -mg builder builder |  | ||||||
| # Make sure they can use sudo without a password |  | ||||||
| echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers |  | ||||||
| # Create the directory for the builds & make it writeable for the |  | ||||||
| # build user |  | ||||||
| mkdir /build |  | ||||||
| chown -R builder:builder /build |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| This script updates the packages to their latest versions & creates a non-root |  | ||||||
| user to use when running `makepkg`. |  | ||||||
| 
 |  | ||||||
| This script is base64-encoded & passed to the container as an environment |  | ||||||
| variable. The container's entrypoint is set to `/bin/sh -c` & its command |  | ||||||
| argument to `echo $BUILD_SCRIPT | base64 -d | /bin/sh -e`, with the |  | ||||||
| `BUILD_SCRIPT` environment variable containing the base64-encoded script. |  | ||||||
| 
 |  | ||||||
| Once the container exits, a new Docker image is created from it. This image is |  | ||||||
| then used as the base for any builds. |  | ||||||
| 
 |  | ||||||
| ## Running builds |  | ||||||
| 
 |  | ||||||
| Each build has its own Docker container, using the builder image as its base. |  | ||||||
| The same base64-based technique as above is used, just with a different script. |  | ||||||
| To make the build logs more clear, each command is appended by an echo command |  | ||||||
| printing the next command to stdout. |  | ||||||
| 
 |  | ||||||
| Given the Git repository URL is `https://examplerepo.com` with branch `main`, |  | ||||||
| the URL of the Vieter server is `https://example.com` and `vieter` is the |  | ||||||
| repository we wish to publish to, we get the following script: |  | ||||||
| 
 |  | ||||||
| ```sh |  | ||||||
| echo -e '+ echo -e '\''[vieter]\\nServer = https://example.com/$repo/$arch\\nSigLevel = Optional'\'' >> /etc/pacman.conf' |  | ||||||
| echo -e '[vieter]\nServer = https://example.com/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf |  | ||||||
| echo -e '+ pacman -Syu --needed --noconfirm' |  | ||||||
| pacman -Syu --needed --noconfirm |  | ||||||
| echo -e '+ su builder' |  | ||||||
| su builder |  | ||||||
| echo -e '+ git clone --single-branch --depth 1 --branch main https://examplerepo.com repo' |  | ||||||
| git clone --single-branch --depth 1 --branch main https://examplerepo.com repo |  | ||||||
| echo -e '+ cd repo' |  | ||||||
| cd repo |  | ||||||
| echo -e '+ makepkg --nobuild --syncdeps --needed --noconfirm' |  | ||||||
| makepkg --nobuild --syncdeps --needed --noconfirm |  | ||||||
| echo -e '+ source PKGBUILD' |  | ||||||
| source PKGBUILD |  | ||||||
| echo -e '+ curl -s --head --fail https://example.com/vieter/x86_64/$pkgname-$pkgver-$pkgrel && exit 0' |  | ||||||
| curl -s --head --fail https://example.com/vieter/x86_64/$pkgname-$pkgver-$pkgrel && exit 0 |  | ||||||
| echo -e '+ [ "$(id -u)" == 0 ] && exit 0' |  | ||||||
| [ "$(id -u)" == 0 ] && exit 0 |  | ||||||
| echo -e '+ MAKEFLAGS="-j$(nproc)" makepkg -s --noconfirm --needed && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done' |  | ||||||
| MAKEFLAGS="-j$(nproc)" makepkg -s --noconfirm --needed && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| This script: |  | ||||||
| 
 |  | ||||||
| 1. Adds the target repository as a repository in the build container |  | ||||||
| 2. Updates mirrors & packages |  | ||||||
| 3. Clones the Git repository |  | ||||||
| 4. Runs `makepkg` without building to calculate `pkgver` |  | ||||||
| 5. Checks whether the package version is already present on the server |  | ||||||
| 6. If not, run `makepkg` & publish any generated package archives to the server |  | ||||||
|  | @ -1,28 +1,27 @@ | ||||||
| module client | module client | ||||||
| 
 | 
 | ||||||
| import models { BuildLog, BuildLogFilter } | import models { BuildLog, BuildLogFilter } | ||||||
| import net.http { Method } |  | ||||||
| import web.response { Response } | import web.response { Response } | ||||||
| import time | import time | ||||||
| 
 | 
 | ||||||
| // get_build_logs returns all build logs. | // get_build_logs returns all build logs. | ||||||
| pub fn (c &Client) get_build_logs(filter BuildLogFilter) ![]BuildLog { | pub fn (c &Client) get_build_logs(filter BuildLogFilter) ![]BuildLog { | ||||||
| 	params := models.params_from(filter) | 	params := models.params_from(filter) | ||||||
| 	data := c.send_request<[]BuildLog>(Method.get, '/api/v1/logs', params)! | 	data := c.send_request<[]BuildLog>(.get, '/api/v1/logs', params)! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // get_build_log returns a specific build log. | // get_build_log returns a specific build log. | ||||||
| pub fn (c &Client) get_build_log(id int) !BuildLog { | pub fn (c &Client) get_build_log(id int) !BuildLog { | ||||||
| 	data := c.send_request<BuildLog>(Method.get, '/api/v1/logs/$id', {})! | 	data := c.send_request<BuildLog>(.get, '/api/v1/logs/$id', {})! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // get_build_log_content returns the contents of the build log file. | // get_build_log_content returns the contents of the build log file. | ||||||
| pub fn (c &Client) get_build_log_content(id int) !string { | pub fn (c &Client) get_build_log_content(id int) !string { | ||||||
| 	data := c.send_request_raw_response(Method.get, '/api/v1/logs/$id/content', {}, '')! | 	data := c.send_request_raw_response(.get, '/api/v1/logs/$id/content', {}, '')! | ||||||
| 
 | 
 | ||||||
| 	return data | 	return data | ||||||
| } | } | ||||||
|  | @ -37,7 +36,7 @@ pub fn (c &Client) add_build_log(target_id int, start_time time.Time, end_time t | ||||||
| 		'exitCode':  exit_code.str() | 		'exitCode':  exit_code.str() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	data := c.send_request_with_body<int>(Method.post, '/api/v1/logs', params, content)! | 	data := c.send_request_with_body<int>(.post, '/api/v1/logs', params, content)! | ||||||
| 
 | 
 | ||||||
| 	return data | 	return data | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | module client | ||||||
|  | 
 | ||||||
|  | // remove_repo removes an entire repository. | ||||||
|  | pub fn (c &Client) remove_repo(repo string) ! { | ||||||
|  | 	c.send_request<string>(.delete, '/$repo', {})! | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // remove_arch_repo removes an entire arch-repo. | ||||||
|  | pub fn (c &Client) remove_arch_repo(repo string, arch string) ! { | ||||||
|  | 	c.send_request<string>(.delete, '/$repo/$arch', {})! | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // remove_package removes a single package from the given arch-repo. | ||||||
|  | pub fn (c &Client) remove_package(repo string, arch string, pkgname string) ! { | ||||||
|  | 	c.send_request<string>(.delete, '/$repo/$arch/$pkgname', {})! | ||||||
|  | } | ||||||
|  | @ -1,12 +1,11 @@ | ||||||
| module client | module client | ||||||
| 
 | 
 | ||||||
| import models { Target, TargetFilter } | import models { Target, TargetFilter } | ||||||
| import net.http { Method } |  | ||||||
| 
 | 
 | ||||||
| // get_targets returns a list of targets, given a filter object. | // get_targets returns a list of targets, given a filter object. | ||||||
| pub fn (c &Client) get_targets(filter TargetFilter) ![]Target { | pub fn (c &Client) get_targets(filter TargetFilter) ![]Target { | ||||||
| 	params := models.params_from(filter) | 	params := models.params_from(filter) | ||||||
| 	data := c.send_request<[]Target>(Method.get, '/api/v1/targets', params)! | 	data := c.send_request<[]Target>(.get, '/api/v1/targets', params)! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
|  | @ -34,7 +33,7 @@ pub fn (c &Client) get_all_targets() ![]Target { | ||||||
| 
 | 
 | ||||||
| // get_target returns the target for a specific id. | // get_target returns the target for a specific id. | ||||||
| pub fn (c &Client) get_target(id int) !Target { | pub fn (c &Client) get_target(id int) !Target { | ||||||
| 	data := c.send_request<Target>(Method.get, '/api/v1/targets/$id', {})! | 	data := c.send_request<Target>(.get, '/api/v1/targets/$id', {})! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
|  | @ -51,14 +50,14 @@ pub struct NewTarget { | ||||||
| // add_target adds a new target to the server. | // add_target adds a new target to the server. | ||||||
| pub fn (c &Client) add_target(t NewTarget) !int { | pub fn (c &Client) add_target(t NewTarget) !int { | ||||||
| 	params := models.params_from<NewTarget>(t) | 	params := models.params_from<NewTarget>(t) | ||||||
| 	data := c.send_request<int>(Method.post, '/api/v1/targets', params)! | 	data := c.send_request<int>(.post, '/api/v1/targets', params)! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // remove_target removes the target with the given id from the server. | // remove_target removes the target with the given id from the server. | ||||||
| pub fn (c &Client) remove_target(id int) !string { | pub fn (c &Client) remove_target(id int) !string { | ||||||
| 	data := c.send_request<string>(Method.delete, '/api/v1/targets/$id', {})! | 	data := c.send_request<string>(.delete, '/api/v1/targets/$id', {})! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
|  | @ -66,7 +65,7 @@ pub fn (c &Client) remove_target(id int) !string { | ||||||
| // patch_target sends a PATCH request to the given target with the params as | // patch_target sends a PATCH request to the given target with the params as | ||||||
| // payload. | // payload. | ||||||
| pub fn (c &Client) patch_target(id int, params map[string]string) !string { | pub fn (c &Client) patch_target(id int, params map[string]string) !string { | ||||||
| 	data := c.send_request<string>(Method.patch, '/api/v1/targets/$id', params)! | 	data := c.send_request<string>(.patch, '/api/v1/targets/$id', params)! | ||||||
| 
 | 
 | ||||||
| 	return data.data | 	return data.data | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,11 +24,13 @@ pub fn cmd() cli.Command { | ||||||
| 				flags: [ | 				flags: [ | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'limit' | 						name: 'limit' | ||||||
|  | 						abbrev: 'l' | ||||||
| 						description: 'How many results to return.' | 						description: 'How many results to return.' | ||||||
| 						flag: cli.FlagType.int | 						flag: cli.FlagType.int | ||||||
| 					}, | 					}, | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'offset' | 						name: 'offset' | ||||||
|  | 						abbrev: 'o' | ||||||
| 						description: 'Minimum index to return.' | 						description: 'Minimum index to return.' | ||||||
| 						flag: cli.FlagType.int | 						flag: cli.FlagType.int | ||||||
| 					}, | 					}, | ||||||
|  | @ -39,16 +41,18 @@ pub fn cmd() cli.Command { | ||||||
| 					}, | 					}, | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'today' | 						name: 'today' | ||||||
| 						description: 'Only list logs started today.' | 						abbrev: 't' | ||||||
|  | 						description: 'Only list logs started today. This flag overwrites any other date-related flag.' | ||||||
| 						flag: cli.FlagType.bool | 						flag: cli.FlagType.bool | ||||||
| 					}, | 					}, | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'failed' | 						name: 'failed' | ||||||
| 						description: 'Only list logs with non-zero exit codes.' | 						description: 'Only list logs with non-zero exit codes. This flag overwrites the --code flag.' | ||||||
| 						flag: cli.FlagType.bool | 						flag: cli.FlagType.bool | ||||||
| 					}, | 					}, | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'day' | 						name: 'day' | ||||||
|  | 						abbrev: 'd' | ||||||
| 						description: 'Only list logs started on this day. (format: YYYY-MM-DD)' | 						description: 'Only list logs started on this day. (format: YYYY-MM-DD)' | ||||||
| 						flag: cli.FlagType.string | 						flag: cli.FlagType.string | ||||||
| 					}, | 					}, | ||||||
|  | @ -62,6 +66,11 @@ pub fn cmd() cli.Command { | ||||||
| 						description: 'Only list logs started after this timestamp. (format: YYYY-MM-DD HH:mm:ss)' | 						description: 'Only list logs started after this timestamp. (format: YYYY-MM-DD HH:mm:ss)' | ||||||
| 						flag: cli.FlagType.string | 						flag: cli.FlagType.string | ||||||
| 					}, | 					}, | ||||||
|  | 					cli.Flag{ | ||||||
|  | 						name: 'code' | ||||||
|  | 						description: 'Only return logs with the given exit code. Prepend with `!` to exclude instead of include. Can be specified multiple times.' | ||||||
|  | 						flag: cli.FlagType.string_array | ||||||
|  | 					}, | ||||||
| 				] | 				] | ||||||
| 				execute: fn (cmd cli.Command) ! { | 				execute: fn (cmd cli.Command) ! { | ||||||
| 					config_file := cmd.flags.get_string('config-file')! | 					config_file := cmd.flags.get_string('config-file')! | ||||||
|  | @ -131,6 +140,8 @@ pub fn cmd() cli.Command { | ||||||
| 						filter.exit_codes = [ | 						filter.exit_codes = [ | ||||||
| 							'!0', | 							'!0', | ||||||
| 						] | 						] | ||||||
|  | 					} else { | ||||||
|  | 						filter.exit_codes = cmd.flags.get_strings('code')! | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					raw := cmd.flags.get_bool('raw')! | 					raw := cmd.flags.get_bool('raw')! | ||||||
|  |  | ||||||
|  | @ -0,0 +1,52 @@ | ||||||
|  | module repos | ||||||
|  | 
 | ||||||
|  | import cli | ||||||
|  | import conf as vconf | ||||||
|  | import client | ||||||
|  | 
 | ||||||
|  | struct Config { | ||||||
|  | 	address string [required] | ||||||
|  | 	api_key string [required] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // cmd returns the cli module that handles modifying the repository contents. | ||||||
|  | pub fn cmd() cli.Command { | ||||||
|  | 	return cli.Command{ | ||||||
|  | 		name: 'repos' | ||||||
|  | 		description: 'Interact with the repositories & packages stored on the server.' | ||||||
|  | 		commands: [ | ||||||
|  | 			cli.Command{ | ||||||
|  | 				name: 'remove' | ||||||
|  | 				required_args: 1 | ||||||
|  | 				usage: 'repo [arch [pkgname]]' | ||||||
|  | 				description: 'Remove a repo, arch-repo, or package from the server.' | ||||||
|  | 				flags: [ | ||||||
|  | 					cli.Flag{ | ||||||
|  | 						name: 'force' | ||||||
|  | 						flag: cli.FlagType.bool | ||||||
|  | 					}, | ||||||
|  | 				] | ||||||
|  | 				execute: fn (cmd cli.Command) ! { | ||||||
|  | 					config_file := cmd.flags.get_string('config-file')! | ||||||
|  | 					conf := vconf.load<Config>(prefix: 'VIETER_', default_path: config_file)! | ||||||
|  | 
 | ||||||
|  | 					if cmd.args.len < 3 { | ||||||
|  | 						if !cmd.flags.get_bool('force')! { | ||||||
|  | 							return error('Removing an arch-repo or repository is a very destructive command. If you really do wish to perform this operation, explicitely add the --force flag.') | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					client := client.new(conf.address, conf.api_key) | ||||||
|  | 
 | ||||||
|  | 					if cmd.args.len == 1 { | ||||||
|  | 						client.remove_repo(cmd.args[0])! | ||||||
|  | 					} else if cmd.args.len == 2 { | ||||||
|  | 						client.remove_arch_repo(cmd.args[0], cmd.args[1])! | ||||||
|  | 					} else { | ||||||
|  | 						client.remove_package(cmd.args[0], cmd.args[1], cmd.args[2])! | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 		] | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -25,11 +25,13 @@ pub fn cmd() cli.Command { | ||||||
| 				flags: [ | 				flags: [ | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'limit' | 						name: 'limit' | ||||||
|  | 						abbrev: 'l' | ||||||
| 						description: 'How many results to return.' | 						description: 'How many results to return.' | ||||||
| 						flag: cli.FlagType.int | 						flag: cli.FlagType.int | ||||||
| 					}, | 					}, | ||||||
| 					cli.Flag{ | 					cli.Flag{ | ||||||
| 						name: 'offset' | 						name: 'offset' | ||||||
|  | 						abbrev: 'o' | ||||||
| 						description: 'Minimum index to return.' | 						description: 'Minimum index to return.' | ||||||
| 						flag: cli.FlagType.int | 						flag: cli.FlagType.int | ||||||
| 					}, | 					}, | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import console.logs | ||||||
| import console.schedule | import console.schedule | ||||||
| import console.man | import console.man | ||||||
| import console.aur | import console.aur | ||||||
|  | import console.repos | ||||||
| import cron | import cron | ||||||
| import agent | import agent | ||||||
| 
 | 
 | ||||||
|  | @ -48,6 +49,7 @@ fn main() { | ||||||
| 			man.cmd(), | 			man.cmd(), | ||||||
| 			aur.cmd(), | 			aur.cmd(), | ||||||
| 			agent.cmd(), | 			agent.cmd(), | ||||||
|  | 			repos.cmd(), | ||||||
| 		] | 		] | ||||||
| 	} | 	} | ||||||
| 	app.setup() | 	app.setup() | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import web | ||||||
| import web.response { new_data_response, new_response } | import web.response { new_data_response, new_response } | ||||||
| 
 | 
 | ||||||
| // v1_poll_job_queue allows agents to poll for new build jobs. | // v1_poll_job_queue allows agents to poll for new build jobs. | ||||||
| ['/api/v1/jobs/poll'; auth; get] | ['/api/v1/jobs/poll'; auth; get; markused] | ||||||
| fn (mut app App) v1_poll_job_queue() web.Result { | fn (mut app App) v1_poll_job_queue() web.Result { | ||||||
| 	arch := app.query['arch'] or { | 	arch := app.query['arch'] or { | ||||||
| 		return app.json(.bad_request, new_response('Missing arch query arg.')) | 		return app.json(.bad_request, new_response('Missing arch query arg.')) | ||||||
|  | @ -21,7 +21,7 @@ fn (mut app App) v1_poll_job_queue() web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_queue_job allows queueing a new one-time build job for the given target. | // v1_queue_job allows queueing a new one-time build job for the given target. | ||||||
| ['/api/v1/jobs/queue'; auth; post] | ['/api/v1/jobs/queue'; auth; markused; post] | ||||||
| fn (mut app App) v1_queue_job() web.Result { | fn (mut app App) v1_queue_job() web.Result { | ||||||
| 	target_id := app.query['target'] or { | 	target_id := app.query['target'] or { | ||||||
| 		return app.json(.bad_request, new_response('Missing target query arg.')) | 		return app.json(.bad_request, new_response('Missing target query arg.')) | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import models { BuildLog, BuildLogFilter } | ||||||
| 
 | 
 | ||||||
| // v1_get_logs returns all build logs in the database. A 'target' query param can | // v1_get_logs returns all build logs in the database. A 'target' query param can | ||||||
| // optionally be added to limit the list of build logs to that repository. | // optionally be added to limit the list of build logs to that repository. | ||||||
| ['/api/v1/logs'; auth; get] | ['/api/v1/logs'; auth; get; markused] | ||||||
| fn (mut app App) v1_get_logs() web.Result { | fn (mut app App) v1_get_logs() web.Result { | ||||||
| 	filter := models.from_params<BuildLogFilter>(app.query) or { | 	filter := models.from_params<BuildLogFilter>(app.query) or { | ||||||
| 		return app.json(.bad_request, new_response('Invalid query parameters.')) | 		return app.json(.bad_request, new_response('Invalid query parameters.')) | ||||||
|  | @ -22,7 +22,7 @@ fn (mut app App) v1_get_logs() web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_get_single_log returns the build log with the given id. | // v1_get_single_log returns the build log with the given id. | ||||||
| ['/api/v1/logs/:id'; auth; get] | ['/api/v1/logs/:id'; auth; get; markused] | ||||||
| fn (mut app App) v1_get_single_log(id int) web.Result { | fn (mut app App) v1_get_single_log(id int) web.Result { | ||||||
| 	log := app.db.get_build_log(id) or { return app.status(.not_found) } | 	log := app.db.get_build_log(id) or { return app.status(.not_found) } | ||||||
| 
 | 
 | ||||||
|  | @ -30,7 +30,7 @@ fn (mut app App) v1_get_single_log(id int) web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_get_log_content returns the actual build log file for the given id. | // v1_get_log_content returns the actual build log file for the given id. | ||||||
| ['/api/v1/logs/:id/content'; auth; get] | ['/api/v1/logs/:id/content'; auth; get; markused] | ||||||
| fn (mut app App) v1_get_log_content(id int) web.Result { | fn (mut app App) v1_get_log_content(id int) web.Result { | ||||||
| 	log := app.db.get_build_log(id) or { return app.status(.not_found) } | 	log := app.db.get_build_log(id) or { return app.status(.not_found) } | ||||||
| 	file_name := log.start_time.custom_format('YYYY-MM-DD_HH-mm-ss') | 	file_name := log.start_time.custom_format('YYYY-MM-DD_HH-mm-ss') | ||||||
|  | @ -50,7 +50,7 @@ fn parse_query_time(query string) !time.Time { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_post_log adds a new log to the database. | // v1_post_log adds a new log to the database. | ||||||
| ['/api/v1/logs'; auth; post] | ['/api/v1/logs'; auth; markused; post] | ||||||
| fn (mut app App) v1_post_log() web.Result { | fn (mut app App) v1_post_log() web.Result { | ||||||
| 	// Parse query params | 	// Parse query params | ||||||
| 	start_time_int := app.query['startTime'].int() | 	start_time_int := app.query['startTime'].int() | ||||||
|  | @ -121,7 +121,7 @@ fn (mut app App) v1_post_log() web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_delete_log allows removing a build log from the system. | // v1_delete_log allows removing a build log from the system. | ||||||
| ['/api/v1/logs/:id'; auth; delete] | ['/api/v1/logs/:id'; auth; delete; markused] | ||||||
| fn (mut app App) v1_delete_log(id int) web.Result { | fn (mut app App) v1_delete_log(id int) web.Result { | ||||||
| 	log := app.db.get_build_log(id) or { return app.status(.not_found) } | 	log := app.db.get_build_log(id) or { return app.status(.not_found) } | ||||||
| 	full_path := os.join_path(app.conf.data_dir, logs_dir_name, log.path()) | 	full_path := os.join_path(app.conf.data_dir, logs_dir_name, log.path()) | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import db | ||||||
| import models { Target, TargetArch, TargetFilter } | import models { Target, TargetArch, TargetFilter } | ||||||
| 
 | 
 | ||||||
| // v1_get_targets returns the current list of targets. | // v1_get_targets returns the current list of targets. | ||||||
| ['/api/v1/targets'; auth; get] | ['/api/v1/targets'; auth; get; markused] | ||||||
| fn (mut app App) v1_get_targets() web.Result { | fn (mut app App) v1_get_targets() web.Result { | ||||||
| 	filter := models.from_params<TargetFilter>(app.query) or { | 	filter := models.from_params<TargetFilter>(app.query) or { | ||||||
| 		return app.json(.bad_request, new_response('Invalid query parameters.')) | 		return app.json(.bad_request, new_response('Invalid query parameters.')) | ||||||
|  | @ -17,7 +17,7 @@ fn (mut app App) v1_get_targets() web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_get_single_target returns the information for a single target. | // v1_get_single_target returns the information for a single target. | ||||||
| ['/api/v1/targets/:id'; auth; get] | ['/api/v1/targets/:id'; auth; get; markused] | ||||||
| fn (mut app App) v1_get_single_target(id int) web.Result { | fn (mut app App) v1_get_single_target(id int) web.Result { | ||||||
| 	target := app.db.get_target(id) or { return app.status(.not_found) } | 	target := app.db.get_target(id) or { return app.status(.not_found) } | ||||||
| 
 | 
 | ||||||
|  | @ -25,7 +25,7 @@ fn (mut app App) v1_get_single_target(id int) web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_post_target creates a new target from the provided query string. | // v1_post_target creates a new target from the provided query string. | ||||||
| ['/api/v1/targets'; auth; post] | ['/api/v1/targets'; auth; markused; post] | ||||||
| fn (mut app App) v1_post_target() web.Result { | fn (mut app App) v1_post_target() web.Result { | ||||||
| 	mut params := app.query.clone() | 	mut params := app.query.clone() | ||||||
| 
 | 
 | ||||||
|  | @ -55,7 +55,7 @@ fn (mut app App) v1_post_target() web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_delete_target removes a given target from the server's list. | // v1_delete_target removes a given target from the server's list. | ||||||
| ['/api/v1/targets/:id'; auth; delete] | ['/api/v1/targets/:id'; auth; delete; markused] | ||||||
| fn (mut app App) v1_delete_target(id int) web.Result { | fn (mut app App) v1_delete_target(id int) web.Result { | ||||||
| 	app.db.delete_target(id) | 	app.db.delete_target(id) | ||||||
| 	app.job_queue.invalidate(id) | 	app.job_queue.invalidate(id) | ||||||
|  | @ -64,7 +64,7 @@ fn (mut app App) v1_delete_target(id int) web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // v1_patch_target updates a target's data with the given query params. | // v1_patch_target updates a target's data with the given query params. | ||||||
| ['/api/v1/targets/:id'; auth; patch] | ['/api/v1/targets/:id'; auth; markused; patch] | ||||||
| fn (mut app App) v1_patch_target(id int) web.Result { | fn (mut app App) v1_patch_target(id int) web.Result { | ||||||
| 	app.db.update_target(id, app.query) | 	app.db.update_target(id, app.query) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ pub: | ||||||
| 	default_arch         string | 	default_arch         string | ||||||
| 	global_schedule      string = '0 3' | 	global_schedule      string = '0 3' | ||||||
| 	base_image           string = 'archlinux:base-devel' | 	base_image           string = 'archlinux:base-devel' | ||||||
| 	max_log_age          int    = -1 | 	max_log_age          int    [empty_default] | ||||||
| 	log_removal_schedule string = '0 0' | 	log_removal_schedule string = '0 0' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ import web.response { new_data_response, new_response } | ||||||
| 
 | 
 | ||||||
| // healthcheck just returns a string, but can be used to quickly check if the | // healthcheck just returns a string, but can be used to quickly check if the | ||||||
| // server is still responsive. | // server is still responsive. | ||||||
| ['/health'; get] | ['/health'; get; markused] | ||||||
| pub fn (mut app App) healthcheck() web.Result { | pub fn (mut app App) healthcheck() web.Result { | ||||||
| 	return app.json(.ok, new_response('Healthy.')) | 	return app.json(.ok, new_response('Healthy.')) | ||||||
| } | } | ||||||
|  | @ -18,7 +18,7 @@ pub fn (mut app App) healthcheck() web.Result { | ||||||
| // get_repo_file handles all Pacman-related routes. It returns both the | // get_repo_file handles all Pacman-related routes. It returns both the | ||||||
| // repository's archives, but also package archives or the contents of a | // repository's archives, but also package archives or the contents of a | ||||||
| // package's desc file. | // package's desc file. | ||||||
| ['/:repo/:arch/:filename'; get; head] | ['/:repo/:arch/:filename'; get; head; markused] | ||||||
| fn (mut app App) get_repo_file(repo string, arch string, filename string) web.Result { | fn (mut app App) get_repo_file(repo string, arch string, filename string) web.Result { | ||||||
| 	mut full_path := '' | 	mut full_path := '' | ||||||
| 
 | 
 | ||||||
|  | @ -48,7 +48,7 @@ fn (mut app App) get_repo_file(repo string, arch string, filename string) web.Re | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // put_package handles publishing a package to a repository. | // put_package handles publishing a package to a repository. | ||||||
| ['/:repo/publish'; auth; post] | ['/:repo/publish'; auth; markused; post] | ||||||
| fn (mut app App) put_package(repo string) web.Result { | fn (mut app App) put_package(repo string) web.Result { | ||||||
| 	// api is a reserved keyword for api routes & should never be allowed to be | 	// api is a reserved keyword for api routes & should never be allowed to be | ||||||
| 	// a repository. | 	// a repository. | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ module server | ||||||
| import web | import web | ||||||
| 
 | 
 | ||||||
| // delete_package tries to remove the given package. | // delete_package tries to remove the given package. | ||||||
| ['/:repo/:arch/:pkg'; auth; delete] | ['/:repo/:arch/:pkg'; auth; delete; markused] | ||||||
| fn (mut app App) delete_package(repo string, arch string, pkg string) web.Result { | fn (mut app App) delete_package(repo string, arch string, pkg string) web.Result { | ||||||
| 	res := app.repo.remove_pkg_from_arch_repo(repo, arch, pkg, true) or { | 	res := app.repo.remove_pkg_from_arch_repo(repo, arch, pkg, true) or { | ||||||
| 		app.lerror('Error while deleting package: $err.msg()') | 		app.lerror('Error while deleting package: $err.msg()') | ||||||
|  | @ -23,7 +23,7 @@ fn (mut app App) delete_package(repo string, arch string, pkg string) web.Result | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // delete_arch_repo tries to remove the given arch-repo. | // delete_arch_repo tries to remove the given arch-repo. | ||||||
| ['/:repo/:arch'; auth; delete] | ['/:repo/:arch'; auth; delete; markused] | ||||||
| fn (mut app App) delete_arch_repo(repo string, arch string) web.Result { | fn (mut app App) delete_arch_repo(repo string, arch string) web.Result { | ||||||
| 	res := app.repo.remove_arch_repo(repo, arch) or { | 	res := app.repo.remove_arch_repo(repo, arch) or { | ||||||
| 		app.lerror('Error while deleting arch-repo: $err.msg()') | 		app.lerror('Error while deleting arch-repo: $err.msg()') | ||||||
|  | @ -43,7 +43,7 @@ fn (mut app App) delete_arch_repo(repo string, arch string) web.Result { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // delete_repo tries to remove the given repo. | // delete_repo tries to remove the given repo. | ||||||
| ['/:repo'; auth; delete] | ['/:repo'; auth; delete; markused] | ||||||
| fn (mut app App) delete_repo(repo string) web.Result { | fn (mut app App) delete_repo(repo string) web.Result { | ||||||
| 	res := app.repo.remove_repo(repo) or { | 	res := app.repo.remove_repo(repo) or { | ||||||
| 		app.lerror('Error while deleting repo: $err.msg()') | 		app.lerror('Error while deleting repo: $err.msg()') | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import net.http | ||||||
| 
 | 
 | ||||||
| // Method attributes that should be ignored when parsing, as they're used | // Method attributes that should be ignored when parsing, as they're used | ||||||
| // elsewhere. | // elsewhere. | ||||||
| const attrs_to_ignore = ['auth'] | const attrs_to_ignore = ['auth', 'markused'] | ||||||
| 
 | 
 | ||||||
| // Parsing function attributes for methods and path. | // Parsing function attributes for methods and path. | ||||||
| fn parse_attrs(name string, attrs []string) !([]http.Method, string) { | fn parse_attrs(name string, attrs []string) !([]http.Method, string) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue