Compare commits

...

204 Commits

Author SHA1 Message Date
Jef Roosens 1a992806fa
fix: update PKGBUILD to use libvieter
ci/woodpecker/tag/lint Pipeline was successful Details
ci/woodpecker/tag/docs Pipeline was successful Details
ci/woodpecker/tag/arch-rel Pipeline was successful Details
ci/woodpecker/tag/build Pipeline was successful Details
ci/woodpecker/tag/gitea Pipeline was successful Details
ci/woodpecker/tag/test Pipeline was successful Details
ci/woodpecker/tag/man Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
ci/woodpecker/push/docs Pipeline failed Details
2023-07-17 13:53:20 +02:00
Jef Roosens 22577d3411 Merge pull request 'Release 0.6.0' (#360) from release-0.6.0 into main
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/tag/build Pipeline was successful Details
ci/woodpecker/tag/lint Pipeline failed Details
ci/woodpecker/tag/test Pipeline failed Details
ci/woodpecker/tag/docs Pipeline failed Details
ci/woodpecker/tag/arch-rel Pipeline failed Details
ci/woodpecker/tag/docker unknown status Details
ci/woodpecker/tag/gitea unknown status Details
ci/woodpecker/tag/man unknown status Details
Reviewed-on: vieter-v/vieter#360
2023-07-17 13:35:03 +02:00
Jef Roosens 47c0f0405b
chore: update versions to 0.6.0
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
2023-07-17 13:26:38 +02:00
Jef Roosens 076ee24b1b
chore: update changelog
ci/woodpecker/push/arch Pipeline is pending Details
ci/woodpecker/push/build Pipeline is pending Details
ci/woodpecker/push/deploy Pipeline is pending Details
ci/woodpecker/push/docker Pipeline is pending Details
ci/woodpecker/push/docs Pipeline is pending Details
ci/woodpecker/push/lint Pipeline is pending Details
ci/woodpecker/push/man Pipeline is pending Details
ci/woodpecker/push/test Pipeline is pending Details
2023-05-04 09:36:03 +02:00
Jef Roosens de8764b281 Merge pull request 'Implement global build timeout' (#358) from Chewing_Bever/vieter:build-timeout into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
ci/woodpecker/push/docs Pipeline failed Details
Reviewed-on: vieter-v/vieter#358
2023-05-02 21:41:26 +02:00
Jef Roosens b278ebd73f
fix: set default timeout to 60 minutes
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-05-02 21:31:24 +02:00
Jef Roosens afb38256ac
feat: implement build timeout 2023-05-02 14:52:41 +02:00
Jef Roosens ac3a89500b
feat: add non-functional build timeout setting 2023-05-02 14:52:40 +02:00
Jef Roosens 8a76860363 Merge pull request 'Error when upload fails before all bytes are received' (#357) from Chewing_Bever/vieter:dev into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#357
2023-04-28 12:44:22 +02:00
Jef Roosens 7595eb7bbe fix: error when upload failed before all bytes received
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-04-28 12:05:12 +02:00
Jef Roosens 37f368b769 Merge pull request 'Agent worker threads' (#355) from Chewing_Bever/vieter:agent-threads into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
ci/woodpecker/push/docs Pipeline failed Details
Reviewed-on: vieter-v/vieter#355
2023-04-05 11:20:43 +02:00
Jef Roosens 094634084b
feat(agent): use worker thread approach
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-04-05 10:50:30 +02:00
Jef Roosens 3b24ad0f2c
fix(web): don't log new metric for every query param
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
ci/woodpecker/push/docs Pipeline failed Details
2023-02-19 16:38:53 +01:00
Jef Roosens 69cc2404db Merge pull request 'update to V 0.3.3' (#347) from Chewing_Bever/vieter:v-0.3.3 into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#347
2023-02-19 16:03:02 +01:00
Jef Roosens f423dcf26b
chore: updated PKGBUILDs
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-02-19 15:54:31 +01:00
Jef Roosens beae2cebd2
fix(ci): lock slate version
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-02-16 13:33:00 +01:00
Jef Roosens a3a83a94ae chore(ci): update CI to new Vlang version
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-02-15 20:50:55 +01:00
Jef Roosens 455f3b5f41 fix: compile with selected V version 2023-02-15 20:07:07 +01:00
Jef Roosens 4dc82515f4 chore: stop shadowing import names with variables 2023-02-15 16:24:07 +01:00
Jef Roosens bff817ccd9
fix: add temporary fix to compile with V 0.3.3
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-02-08 11:30:38 +01:00
Jef Roosens 8a08423907
chore(ci): update V images
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-02-08 11:15:41 +01:00
Jef Roosens b9598ca046
chore: rewrite docstrings with generics
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/test Pipeline failed Details
2023-02-08 11:11:28 +01:00
Jef Roosens 91a976c634
chore: rename db module to avoid conflict with vlib 2023-02-08 11:09:18 +01:00
Jef Roosens b3a119f221
chore: ran v fmt for v 0.3.3 changes 2023-02-08 11:00:17 +01:00
Jef Roosens e10b450abd
fix: metrics no longer bloat memory
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2023-01-28 17:35:01 +01:00
Jef Roosens 8f32888dff
fix: i'm too lazy to test these
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2023-01-28 15:27:47 +01:00
Jef Roosens da370f42fd
fix: update md5sums in pkgbuild
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/arch Pipeline failed Details
ci/woodpecker/push/build Pipeline failed Details
ci/woodpecker/push/man unknown status Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/deploy unknown status Details
2023-01-28 15:22:04 +01:00
Jef Roosens 0d6ca8d3e4 Merge pull request 'Cron implementation in C' (#341) from Chewing_Bever/vieter:c-cron into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/arch Pipeline failed Details
ci/woodpecker/push/man unknown status Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/deploy unknown status Details
Reviewed-on: vieter-v/vieter#341
2023-01-28 15:19:39 +01:00
Jef Roosens 434c4eb558
chore: updated changelog
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-01-28 14:53:43 +01:00
Jef Roosens 3b320ac7c3
fix: accidentally changed submodule commit
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-01-28 14:39:12 +01:00
Jef Roosens 8d14d5c3fd
chore: update PKGBUILD to use git submodule
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline failed Details
2023-01-28 14:36:46 +01:00
Jef Roosens 6ca53ce534 chore: use libvieter dev branch instead
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-01-28 13:18:44 +01:00
Jef Roosens ba89110eab chore: some fixes 2023-01-28 13:18:44 +01:00
Jef Roosens ad19bc660a chore: switch to alpine 3.17 ci image 2023-01-28 13:18:44 +01:00
Jef Roosens beb90d5756 refactor: link libvieter; remove cron code & daemon
This giant commit removes the old cron daemon & parser, replacing the
latter with a C implementation that will now be maintained in a separate
C library that gets developed independently. This commit lays the
groundwork for implementing features of Vieter in C where possible.
2023-01-28 13:16:36 +01:00
Jef Roosens bfd28d6f70 Merge pull request 'Fix for configured log level being ignored.' (#339) from GreekStapler/vieter:fix/logging_level into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#339
2023-01-08 09:27:12 +01:00
GreekStapler 8432f5915d Fix for configured log level being ignored.
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-01-07 21:09:55 +00:00
Jef Roosens 5176266ca1
refactor: work with new docker lib
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2023-01-05 17:05:57 +01:00
Jef Roosens b5ff50066b Merge pull request 'Search targets using API' (#332) from Chewing_Bever/vieter:search-targets into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#332
2023-01-04 14:50:49 +01:00
Jef Roosens 398e2bd9eb
chore: update docs; final read
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-01-04 14:37:41 +01:00
Jef Roosens 39a026fdb3
feat: add filtering of targets by arch 2023-01-04 14:13:26 +01:00
Jef Roosens b0fe6b7384
chore: ran formatter 2023-01-04 14:13:26 +01:00
Jef Roosens c9edb55abc
feat(db): implemented iterator over targets 2023-01-04 14:13:25 +01:00
Jef Roosens f8f611f5c5
feat(api): add search query to targets 2023-01-04 14:12:34 +01:00
Jef Roosens 60d5fb77e0
feat(metrics): add prefix; use base unit for time
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2023-01-04 09:19:02 +01:00
Jef Roosens 849bf54979 Merge pull request 'Add Prometheus metrics endpoint' (#325) from Chewing_Bever/vieter:metrics into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#325
2023-01-03 21:51:16 +01:00
Jef Roosens 4ed4ef4a27
chore: generate man pages using debug build
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2023-01-03 09:29:55 +01:00
Jef Roosens 4ca2521937 feat(server): ability to disable metrics
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline failed Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-29 22:00:43 +01:00
Jef Roosens c0f58ddc77 feat(server): add metric collection 2022-12-29 21:53:47 +01:00
Jef Roosens 8a0214babe Merge pull request 'Release 0.5.0' (#330) from release-0.5.0 into main
ci/woodpecker/tag/lint Pipeline was successful Details
ci/woodpecker/tag/docs Pipeline was successful Details
ci/woodpecker/tag/arch-rel Pipeline was successful Details
ci/woodpecker/tag/build Pipeline was successful Details
ci/woodpecker/tag/man Pipeline was successful Details
ci/woodpecker/tag/test Pipeline was successful Details
ci/woodpecker/tag/gitea Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#330
2022-12-29 20:17:51 +01:00
Jef Roosens 1c70bce9e4
chore: bump versions to 0.5.0
ci/woodpecker/push/arch unknown status Details
ci/woodpecker/push/deploy unknown status Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/pr/docs unknown status Details
ci/woodpecker/pr/lint unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/test unknown status Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
2022-12-29 15:49:59 +01:00
Jef Roosens 6738f8de67 Merge pull request 'Some final stuff before 0.5.0' (#323) from Chewing_Bever/vieter:final-stuff into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#323
2022-12-28 22:42:34 +01:00
Jef Roosens 4635127ba2
docs: removed an outdated page
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-28 22:15:48 +01:00
Jef Roosens bb4406404d
chore: use new conf features
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-28 22:02:02 +01:00
Jef Roosens cac74db086
feat(console): add commands for removing repos, arch-repos, packages 2022-12-28 21:56:02 +01:00
Jef Roosens b7af051103
feat(client): support removing repos, arch-repos & packages 2022-12-28 21:24:30 +01:00
Jef Roosens 641cf22669
feat(cli): add flag to filter logs by exit codes
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-23 08:18:49 +01:00
Jef Roosens 3342eedfa4
chore: compile with -skip-unused
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-22 23:18:01 +01:00
Jef Roosens dc517c23c5 Merge pull request 'Release 0.5.0: release candidate 2' (#320) from release-0.5.0-rc.2 into main
ci/woodpecker/tag/lint Pipeline was successful Details
ci/woodpecker/tag/docs Pipeline was successful Details
ci/woodpecker/tag/arch-rel Pipeline was successful Details
ci/woodpecker/tag/build Pipeline was successful Details
ci/woodpecker/tag/man Pipeline was successful Details
ci/woodpecker/tag/test Pipeline was successful Details
ci/woodpecker/tag/gitea Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#320
2022-12-22 00:15:57 +01:00
Jef Roosens be3762835d
chore: bump versions to 0.5.0-rc.2
ci/woodpecker/push/arch unknown status Details
ci/woodpecker/push/deploy unknown status Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/pr/docs unknown status Details
ci/woodpecker/pr/lint unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/test unknown status Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
2022-12-21 23:45:42 +01:00
Jef Roosens ab6da78738
feat(cli): use posx-style long options
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2022-12-21 23:42:15 +01:00
Jef Roosens 2c93316688
fix: log removal daemon now properly cleans all old logs
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2022-12-19 12:43:46 +01:00
Jef Roosens 0a3e883f4d Merge pull request 'Removal of logs' (#318) from Chewing_Bever/vieter:logs-removal into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#318
2022-12-19 12:15:39 +01:00
Jef Roosens ab81eebd87
refactor: some small changes before PR
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-19 11:58:35 +01:00
Jef Roosens b66d1161ed
docs: update docs some more
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-19 11:24:22 +01:00
Jef Roosens 26796f2228
feat(server): use cron schedule for log removal instead
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-19 09:49:03 +01:00
Jef Roosens 09c61143b0
docs: updated the docs 2022-12-19 09:48:56 +01:00
Jef Roosens a9ad3088bb
feat(server): add log removal daemon 2022-12-17 17:11:19 +01:00
Jef Roosens af409011e6
feat: add api & cli command to remove log 2022-12-17 16:24:01 +01:00
Jef Roosens 8b72a9fc0f Merge pull request 'Release 0.5.0: release candidate 1' (#316) from release-0.5.0-rc.1 into main
ci/woodpecker/tag/lint Pipeline was successful Details
ci/woodpecker/tag/docs Pipeline was successful Details
ci/woodpecker/tag/arch-rel Pipeline failed Details
ci/woodpecker/tag/build Pipeline was successful Details
ci/woodpecker/tag/man Pipeline was successful Details
ci/woodpecker/tag/test Pipeline was successful Details
ci/woodpecker/tag/gitea Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#316
2022-12-17 14:13:05 +01:00
Jef Roosens f9bb4b81de
chore: bump versions
ci/woodpecker/pr/docs unknown status Details
ci/woodpecker/pr/lint unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/test unknown status Details
ci/woodpecker/push/arch unknown status Details
ci/woodpecker/push/deploy unknown status Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
2022-12-17 14:00:51 +01:00
Jef Roosens 300c5490a6 Merge pull request 'Correctly calculate agent sleep time' (#310) from Chewing_Bever/vieter:more-fixes into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#310
2022-12-16 22:13:41 +01:00
Jef Roosens b067f9c589 refactor: streamline agent loop code
ci/woodpecker/pr/test Pipeline is pending Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/build Pipeline failed Details
2022-12-16 22:06:26 +01:00
Jef Roosens 1797c0f560 fix(agent): correctly calculate sleep time 2022-12-16 21:47:02 +01:00
Jef Roosens 946d9acd59 Merge pull request 'Actually use path variable' (#307) from Chewing_Bever/vieter:fix-path-build into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#307
2022-12-16 20:46:16 +01:00
Jef Roosens 402fef475a fix: actually use path setting when building
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
2022-12-16 20:38:26 +01:00
Jef Roosens 894323ddcb Merge pull request 'Address some small problems' (#306) from Chewing_Bever/vieter:image-fixes into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#306
2022-12-16 19:54:44 +01:00
Jef Roosens fe3e6e2bab chore: some final revisions before pr merge
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-16 18:55:44 +01:00
Jef Roosens af4c9e1d00
chore: updated changelog
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-16 16:35:40 +01:00
Jef Roosens 0604de26c4
feat(agent): ensure images exist when starting build 2022-12-16 16:27:24 +01:00
Jef Roosens 489931eaa8
fix: don't buffer stdout even if not a terminal
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-16 11:37:51 +01:00
Jef Roosens 1ce7b9d571
feat: add option to specify subdirectory in repo to use
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-16 11:21:28 +01:00
Jef Roosens a48358fd75 fix: don't run prepare step twice in builds 2022-12-15 23:47:41 +01:00
Jef Roosens dbbe5c1e51
fix(agent): remove infinite loop and account for externally removed
images
2022-12-15 12:09:43 +01:00
Jef Roosens b634775ca3
refactor(server): clean up server responses a bit 2022-12-15 10:46:58 +01:00
Jef Roosens 0727d0fd25
refactor(client): streamline code & improve error propagation 2022-12-15 10:01:45 +01:00
Jef Roosens 0bd5158608
feat(client): handle empty and non-successful responses 2022-12-15 09:46:48 +01:00
Jef Roosens bfe1aafcf1 Merge pull request 'Polling-based agent-server architecture' (#301) from Chewing_Bever/vieter:agent-server-polling into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline failed Details
Reviewed-on: vieter-v/vieter#301
2022-12-14 17:34:48 +01:00
Jef Roosens 60cb91c18c
chore: final read before merging
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-14 17:23:51 +01:00
Jef Roosens 51df1874f5
agent: some better logging
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-14 16:33:50 +01:00
Jef Roosens d7a04c6ebf
chore: please the great lint
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-14 16:03:57 +01:00
Jef Roosens 2cc3e8404e
feat: queue one-time builds from CLI
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-13 23:31:47 +01:00
Jef Roosens 6a208dbe6c
feat: allow queueing one-time builds 2022-12-13 21:22:22 +01:00
Jef Roosens f6c5e7c246
feat: add option to force-build package 2022-12-13 19:59:18 +01:00
Jef Roosens 8a2f720bdf
docs(agent): added agent configuration docs
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-13 19:33:21 +01:00
Jef Roosens d3151863ee
refactor(build): remove some code duplication from queue 2022-12-13 18:24:21 +01:00
Jef Roosens 03f2240ff6
chore: please the linter
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-12-13 17:51:42 +01:00
Jef Roosens 5cbfc0ebcb
feat(agent): clean up code a bit; add frequent polling when active
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline failed Details
2022-12-13 17:42:49 +01:00
Jef Roosens 6342789921
feat(server): update job queue when adding, removing or updating targets
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline failed Details
2022-12-13 13:58:51 +01:00
Jef Roosens e742d3de6d
fix(db): return correct id when adding targets 2022-12-13 13:46:07 +01:00
Jef Roosens b6168a3060
fix(build): change tests to use BuildConfig instead 2022-12-13 12:38:39 +01:00
Jef Roosens 882a9a60a9
feat(build): allowed invalidating entries in build queue 2022-12-13 08:58:27 +01:00
Jef Roosens 3611123f45
feat(agent): initial working version 2022-12-13 08:37:30 +01:00
Jef Roosens 6f23d690a7
feat(agent): partially wrote daemon code 2022-12-13 08:37:30 +01:00
Jef Roosens 7ef8d4b846
feat(agent): wrote ImageManager 2022-12-13 08:37:30 +01:00
Jef Roosens 5bab1f77f0
feat(agent): begin reforming for new api 2022-12-13 08:37:30 +01:00
Jef Roosens 0a5c4295e0
feat(server): properly reschedule jobs after polling 2022-12-13 08:37:29 +01:00
Jef Roosens c57de4d8ee
feat(server): initialize job queue on start; api endpoint for polling
jobs
2022-12-13 08:37:29 +01:00
Jef Roosens 9a49d96e20
feat(build): start of server-side job queue 2022-12-13 08:37:29 +01:00
Jef Roosens 6281ef7607
feat: start of agent code 2022-12-06 13:50:25 +01:00
Jef Roosens 9e11237ff9 Merge pull request 'refactoring for fun' (#275) from Chewing_Bever/vieter:refactor-zen into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline failed Details
Reviewed-on: vieter-v/vieter#275
2022-11-19 17:12:34 +01:00
Jef Roosens 71c77e90bc
refactor(cron): expression parser now uses bitfields (closes #148)
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-11-19 17:03:45 +01:00
Jef Roosens 54f40b7638
chore(repo): added readme 2022-11-19 17:01:40 +01:00
Jef Roosens 9493796160
refactor(package): split module into two files 2022-11-19 17:01:40 +01:00
Jef Roosens 3636dd92db Merge pull request 'Some small bug fixes' (#296) from Chewing_Bever/vieter:289-fix into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline failed Details
Reviewed-on: vieter-v/vieter#296
2022-11-07 21:56:43 +01:00
Jef Roosens 17e58c91ed chore: updated changelog; ran formatter
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-11-07 21:40:59 +01:00
Jef Roosens fc4dc30f74 fix(api): always return JSON response on success (fixes #276)
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/test Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
2022-11-07 21:35:49 +01:00
Jef Roosens 5542be0418 fix(api): set arch if not provided or empty (fixes #278) 2022-11-07 21:13:40 +01:00
Jef Roosens 9a552f5302 fix(server): remove NOT NULL constraint on branch (fixes #289) 2022-11-07 21:11:10 +01:00
Jef Roosens aff6dff06a Merge pull request 'Update to V 0.3.2' (#282) from Chewing_Bever/vieter:v-0.3.2 into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline failed Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline failed Details
Reviewed-on: vieter-v/vieter#282
2022-11-02 18:59:48 +01:00
Jef Roosens 3095daed7d
chore: update dockerfile & changelog
ci/woodpecker/pr/test Pipeline is pending Details
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/build Pipeline failed Details
2022-11-02 18:43:20 +01:00
Jef Roosens cc9dcb3058
fix(ci): install dependencies when linting
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-11-02 18:30:59 +01:00
Jef Roosens 96a9798d3f
refactor: updated tests to new syntax
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-11-01 22:30:48 +01:00
Jef Roosens 161341a108
fix: still use openssl
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/test Pipeline failed Details
2022-11-01 22:07:14 +01:00
Jef Roosens a2fda0d4b7
refactor: compile without warnings
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/test Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
2022-11-01 21:59:18 +01:00
Jef Roosens 23632be7a4
refactor: use relocated module names
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-11-01 21:43:25 +01:00
Jef Roosens 22fd6e395b
refactor: compile on V 0.3.2
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline failed Details
2022-11-01 21:11:31 +01:00
Jef Roosens ed29102717
chore(ci): update to V 0.3.2 image 2022-11-01 20:54:06 +01:00
Jef Roosens ae29fe5ef8 Merge pull request 'Release 0.4.0' (#274) from release-0.4.0 into main
ci/woodpecker/tag/lint Pipeline was successful Details
ci/woodpecker/tag/docs Pipeline was successful Details
ci/woodpecker/tag/arch-rel Pipeline was successful Details
ci/woodpecker/tag/build Pipeline was successful Details
ci/woodpecker/tag/man Pipeline was successful Details
ci/woodpecker/tag/test Pipeline was successful Details
ci/woodpecker/tag/gitea Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#274
2022-10-01 17:16:27 +02:00
Jef Roosens 66928216e5
chore: bumped versions to 0.4.0
ci/woodpecker/push/arch unknown status Details
ci/woodpecker/push/deploy unknown status Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man unknown status Details
ci/woodpecker/push/docs Pipeline failed Details
ci/woodpecker/push/build Pipeline failed Details
ci/woodpecker/pr/docs unknown status Details
ci/woodpecker/pr/lint unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/test unknown status Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
2022-10-01 17:04:56 +02:00
Jef Roosens 7ef7dcd725 Merge pull request 'Some small fixes' (#273) from Chewing_Bever/vieter:explicitely-empty-values into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#273
2022-10-01 16:56:25 +02:00
Jef Roosens e9fdbc9426
chore: removed duplicate changelog entries [CI SKIP] 2022-10-01 16:54:33 +02:00
Jef Roosens 15c2d72743
docs: updated json response types for create routes [CI SKIP]
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
2022-10-01 16:53:16 +02:00
Jef Roosens f34eefd59b
fix(server): prevent `api` as a repository name
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-10-01 16:38:38 +02:00
Jef Roosens ae98c3e717
feat(server): repo POST requests now return information 2022-10-01 16:38:38 +02:00
Jef Roosens 559ef3e505
feat: logs api now also returns id 2022-10-01 16:38:36 +02:00
Jef Roosens 847d77b2bc
chore(ci): refactor ci configs a bit 2022-10-01 16:38:10 +02:00
Jef Roosens 95d32e2d51
fix(server): prevent `api` as a repository name 2022-10-01 16:38:09 +02:00
Jef Roosens 575c04189d
fix(client): allow empty values as params 2022-10-01 16:37:52 +02:00
Jef Roosens 39e2d12827 Merge pull request 'improved API & CLI UX' (#271) from Chewing_Bever/vieter:api-client-ux into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#271
2022-10-01 16:30:05 +02:00
Jef Roosens 851a446a95
chore: updated changelog [CI SKIP] 2022-10-01 16:21:49 +02:00
Jef Roosens fab8ca20b8
cli: targets add now supports raw flag
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-10-01 16:05:27 +02:00
Jef Roosens 8a08788935
feat(console): tabled outputs now optionally return without decorations
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-09-11 21:50:29 +02:00
Jef Roosens cf67b46df0
feat(server): less verbose repo DELETE responses 2022-09-11 21:28:37 +02:00
Jef Roosens b6cd2f0bc2
feat(server): repo POST requests now return information 2022-09-11 21:24:29 +02:00
Jef Roosens 210508f1ee
feat: logs api now also returns id 2022-09-11 20:50:23 +02:00
Jef Roosens 7b59277931
feat: adding target returns id of added entry 2022-09-05 10:13:50 +02:00
Jef Roosens 3e0a2584fa Merge pull request 'Rework of web framework' (#266) from Chewing_Bever/vieter:web-rework into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#266
2022-09-04 22:29:41 +02:00
Jef Roosens 9dfdfbf724
chore: update README [CI SKIP] 2022-09-04 20:16:58 +02:00
Jef Roosens 272f14b264
refactor(server): migrated all routes to new auth system
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-09-04 19:36:08 +02:00
Jef Roosens 4887af26d3
feat(web): added authentication as function attribute 2022-09-04 19:32:22 +02:00
Jef Roosens 9268ef0302
refactor(web): some small cleanup 2022-09-01 09:17:39 +02:00
Jef Roosens e23635a1d3
refactor: moved response module to web.response 2022-08-13 13:16:31 +02:00
Jef Roosens cc5df95a1a
feat(web): file() now supports HTTP byte range
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-08-12 17:11:44 +02:00
Jef Roosens e7b45bf251
feat(web): file() now handles HEAD requests 2022-08-12 15:08:05 +02:00
Jef Roosens 3a73ea0632
refactor(web): simplified web framework in general 2022-08-12 14:39:42 +02:00
Jef Roosens 78b0918df7 Merge pull request 'Delete packages, arch-repos & repos' (#265) from Chewing_Bever/vieter:repo-delete-routes into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#265
2022-08-11 19:53:20 +02:00
Jef Roosens 50918da672
docs: fixed some typos
ci/woodpecker/pr/lint Pipeline is pending Details
ci/woodpecker/pr/test Pipeline is pending Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/build Pipeline failed Details
2022-08-11 19:47:26 +02:00
Jef Roosens ba3b00572b
chore: updated changelog [CI SKIP]
ci/woodpecker/pr/test Pipeline is pending Details
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline failed Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/build Pipeline failed Details
2022-08-11 19:44:22 +02:00
Jef Roosens 68b7e5e71e
feat(server): added routes for removing arch-repo & repo
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-08-11 19:29:40 +02:00
Jef Roosens 6283cbea9c
feat(repo): added function to remove arch-repo 2022-08-11 19:16:38 +02:00
Jef Roosens 49ddb312de
feat(server): added endpoint to remove package from arch-repo 2022-08-11 19:07:54 +02:00
Jef Roosens 8a2b121cc7 Merge pull request 'AUR tools' (#263) from Chewing_Bever/vieter:aur-tools into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#263
2022-07-17 13:53:49 +02:00
Jef Roosens 0f6630b940
chore: updated PKGBUILDs to use vlang package
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-07-17 13:38:46 +02:00
Jef Roosens 0d0fb323f2
chore: switched to vlang 0.3 Docker image
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-07-03 14:11:29 +02:00
Jef Roosens 1a940f2f98
feat(cli): added "aur add" command
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-24 20:30:09 +02:00
Jef Roosens 487b235727
feat(cli): add aur search command 2022-06-24 20:30:06 +02:00
Jef Roosens 1b7cabdd74 Merge pull request 'Update to newest V version' (#260) from Chewing_Bever/vieter:v-update into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#260
2022-06-22 21:00:34 +02:00
Jef Roosens 6336d801d3
docs: mention AUR packages
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-22 20:43:14 +02:00
Jef Roosens 25d87fb5e6
fix: make code compile with updated V version 2022-06-22 20:17:11 +02:00
Jef Roosens af4fbc4ccc Merge pull request 'Split docker, rename org & modules' (#259) from Chewing_Bever/vieter:split-docker into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter-v/vieter#259
2022-06-22 10:25:45 +02:00
Jef Roosens c8fc4c6a96
fix(ci): set VMODULES for tests
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-22 09:59:44 +02:00
Jef Roosens 9dd9222a69
fix(ci): use extended hugo; install modules for tests
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline failed Details
2022-06-22 09:44:25 +02:00
Jef Roosens a4c2508fe7
chore: removed unnecessary things from Makefile
ci/woodpecker/pr/docs Pipeline failed Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline failed Details
ci/woodpecker/pr/man unknown status Details
ci/woodpecker/pr/docker unknown status Details
ci/woodpecker/pr/test Pipeline failed Details
2022-06-22 09:33:48 +02:00
Jef Roosens e58ac49680
chore: hopefully changed all URLs to new org 2022-06-22 09:31:09 +02:00
Jef Roosens 461f227169
refactor: use new module names 2022-06-22 09:31:08 +02:00
Jef Roosens d060366dcb
refactor: move docker code to external vdocker module 2022-06-22 09:31:08 +02:00
Jef Roosens 39eb03077e
docs: mention matrix room in README [CI SKIP] 2022-06-20 17:31:05 +02:00
Jef Roosens 1ac76ac452 Merge pull request 'fix(build): explicitely set PATH variable in build containers' (#257) from Chewing_Bever/vieter:better-path-var into dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter/vieter#257
2022-06-17 20:31:06 +02:00
Jef Roosens 4200f5c8de
fix(build): explicitely set PATH variable in build containers
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-17 20:19:15 +02:00
Jef Roosens 424b0651e9 Merge pull request 'Add direct PKGBUILD link as target option' (#254) from Chewing_Bever/vieter:direct-pkgbuild-target into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter/vieter#254
2022-06-17 16:44:52 +02:00
Jef Roosens 5e11a91f3d
refactor: renamed cron & build code to use "target" naming
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-17 16:24:12 +02:00
Jef Roosens 449656eb97
docs: updated to new 'kind' field
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-17 14:52:59 +02:00
Jef Roosens 8f91c1fde5
feat(build): added support for 'url' kind 2022-06-17 14:31:34 +02:00
Jef Roosens bd07964509
feat(api): prevent invalid kind values 2022-06-17 13:56:38 +02:00
Jef Roosens bb5643bb03
feat: added ability to specify kind of target 2022-06-17 13:45:21 +02:00
Jef Roosens 10ad8297fb Merge pull request 'Stop calculating MD5 hashes' (#247) from Chewing_Bever/vieter:no-md5 into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter/vieter#247
2022-06-16 22:51:46 +02:00
Jef Roosens a8d647cca3
fix(docs): use versioned endpoints in HTTP API docs
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-16 22:38:42 +02:00
Jef Roosens 1b7c14e7dc
feat(server): no longer calculate md5 hashes for packages 2022-06-16 22:36:11 +02:00
Jef Roosens f81039d2bb Merge pull request 'Some breaking API changes' (#245) from Chewing_Bever/vieter:api-changes into dev
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter/vieter#245
2022-06-16 18:17:53 +02:00
Jef Roosens 3d38df6d03
fix(client): use new "target" name for param
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-16 18:10:25 +02:00
Jef Roosens cf94b64400
docs: forgot some renames
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-16 17:57:29 +02:00
Jef Roosens fcdcf9c5ca
feat(server): add config option for server port
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-16 16:56:58 +02:00
Jef Roosens 9727b86203
docs(api): updated to new "targets" naming 2022-06-16 16:56:58 +02:00
Jef Roosens 102a7f8899
refactor: renamed codebase to "targets" 2022-06-16 16:56:58 +02:00
Jef Roosens faec08f846
refactor(console): renamed stuff to 'targets' 2022-06-16 16:56:58 +02:00
Jef Roosens 4d581da7bf
refactor: renamed api routes & client code to 'targets' 2022-06-16 16:56:58 +02:00
Jef Roosens 6b79f7b5ed
feat(server): moved api routes under /v1 namespace 2022-06-16 16:56:55 +02:00
Jef Roosens 3a5ac5d32b Merge pull request 'fix: added VIETER_ prefix to vconf.load calls' (#248) from Chewing_Bever/vieter:fix into dev
ci/woodpecker/push/arch Pipeline failed Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
Reviewed-on: vieter/vieter#248
2022-06-15 23:02:34 +02:00
Jef Roosens 339267e6b2
fix: added VIETER_ prefix to vconf.load calls
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-15 22:54:27 +02:00
Jef Roosens 233dd20345
fix(ci): also install modules when building on dev
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/arch Pipeline was successful Details
ci/woodpecker/push/build Pipeline was successful Details
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/push/man Pipeline was successful Details
ci/woodpecker/push/docker Pipeline was successful Details
ci/woodpecker/push/deploy Pipeline was successful Details
2022-06-15 19:59:48 +02:00
Jef Roosens ae04fe63a7 Merge pull request 'Migrated env module to own Git repository' (#244) from Chewing_Bever/vieter:split-env into dev
ci/woodpecker/push/test Pipeline is pending Details
ci/woodpecker/push/build Pipeline failed Details
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/lint Pipeline failed Details
ci/woodpecker/push/docker unknown status Details
ci/woodpecker/push/man unknown status Details
ci/woodpecker/push/deploy unknown status Details
ci/woodpecker/push/arch Pipeline failed Details
Reviewed-on: vieter/vieter#244
2022-06-15 19:57:43 +02:00
Jef Roosens 592241c743
chore: updated CI config & PKGBUILDs to new module split
ci/woodpecker/pr/docs Pipeline was successful Details
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/build Pipeline was successful Details
ci/woodpecker/pr/man Pipeline was successful Details
ci/woodpecker/pr/docker Pipeline was successful Details
ci/woodpecker/pr/test Pipeline was successful Details
2022-06-15 18:17:00 +02:00
Jef Roosens 44696fc11b
refactor: migrated env code to own external module 2022-06-15 13:20:29 +02:00
138 changed files with 4336 additions and 3384 deletions

4
.clang-format 100644
View File

@ -0,0 +1,4 @@
# To stay consistent with the V formatting style, we use tabs
UseTab: Always
IndentWidth: 4
TabWidth: 4

View File

@ -5,6 +5,5 @@ root = true
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
[*.v] [*.{v,c,h}]
# vfmt wants it :(
indent_style = tab indent_style = tab

6
.gitignore vendored
View File

@ -1,4 +1,4 @@
*.c vieter.c
/data/ /data/
# Build artifacts # Build artifacts
@ -26,4 +26,8 @@ gdb.txt
# Generated docs # Generated docs
_docs/ _docs/
docs/resources/_gen/
/man/ /man/
# VLS logs
vls.log

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "docs/themes/hugo-book"] [submodule "docs/themes/hugo-book"]
path = docs/themes/hugo-book path = docs/themes/hugo-book
url = https://github.com/alex-shpak/hugo-book url = https://github.com/alex-shpak/hugo-book
[submodule "src/libvieter"]
path = src/libvieter
url = https://git.rustybever.be/vieter-v/libvieter

View File

@ -1,13 +0,0 @@
# These checks already get performed on the feature branches
branches:
exclude: [ main ]
platform: 'linux/amd64'
pipeline:
lint:
image: 'chewingbever/vlang:latest'
pull: true
commands:
- make lint
when:
event: [ pull_request ]

View File

@ -1,17 +0,0 @@
matrix:
PLATFORM:
- 'linux/amd64'
- 'linux/arm64'
branches:
exclude: [ main ]
platform: ${PLATFORM}
pipeline:
test:
image: 'chewingbever/vlang:latest'
pull: true
commands:
- make test
when:
event: [pull_request]

View File

@ -9,7 +9,8 @@ skip_clone: true
pipeline: pipeline:
build: build:
image: 'menci/archlinuxarm:base-devel' image: 'git.rustybever.be/vieter-v/vieter-builder'
pull: true
commands: commands:
# Add the vieter repository so we can use the compiler # Add the vieter repository so we can use the compiler
- echo -e '[vieter]\nServer = https://arch.r8r.be/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf - echo -e '[vieter]\nServer = https://arch.r8r.be/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf
@ -23,7 +24,7 @@ pipeline:
- su builder - su builder
# Due to a bug with the V compiler, we can't just use the PKGBUILD from # Due to a bug with the V compiler, we can't just use the PKGBUILD from
# inside the repo # inside the repo
- curl -OL "https://git.rustybever.be/vieter/vieter/raw/tag/$CI_COMMIT_TAG/PKGBUILD" - curl -OL "https://git.rustybever.be/vieter-v/vieter/raw/tag/$CI_COMMIT_TAG/PKGBUILD"
- makepkg -s --noconfirm --needed - makepkg -s --noconfirm --needed
when: when:
event: tag event: tag

View File

@ -9,7 +9,8 @@ skip_clone: true
pipeline: pipeline:
build: build:
image: 'menci/archlinuxarm:base-devel' image: 'git.rustybever.be/vieter-v/vieter-builder'
pull: true
commands: commands:
# Add the vieter repository so we can use the compiler # Add the vieter repository so we can use the compiler
- echo -e '[vieter]\nServer = https://arch.r8r.be/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf - echo -e '[vieter]\nServer = https://arch.r8r.be/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf
@ -23,7 +24,7 @@ pipeline:
- su builder - su builder
# Due to a bug with the V compiler, we can't just use the PKGBUILD from # Due to a bug with the V compiler, we can't just use the PKGBUILD from
# inside the repo # inside the repo
- curl -o PKGBUILD -L https://git.rustybever.be/vieter/vieter/raw/branch/dev/PKGBUILD.dev - curl -o PKGBUILD -L https://git.rustybever.be/vieter-v/vieter/raw/branch/dev/PKGBUILD.dev
- makepkg -s --noconfirm --needed - makepkg -s --noconfirm --needed
when: when:
event: push event: push

View File

@ -1,3 +1,6 @@
variables:
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
matrix: matrix:
PLATFORM: PLATFORM:
- 'linux/amd64' - 'linux/amd64'
@ -6,10 +9,19 @@ matrix:
platform: ${PLATFORM} platform: ${PLATFORM}
pipeline: pipeline:
debug: install-modules:
image: 'chewingbever/vlang:latest' image: *vlang_image
pull: true pull: true
commands: commands:
- export VMODULES=$PWD/.vmodules
- 'cd src && v install'
when:
event: [push, pull_request]
debug:
image: *vlang_image
commands:
- export VMODULES=$PWD/.vmodules
- make - make
when: when:
event: [pull_request] event: [pull_request]
@ -17,11 +29,11 @@ pipeline:
exclude: [main] exclude: [main]
prod: prod:
image: 'chewingbever/vlang:latest' image: *vlang_image
pull: true
environment: environment:
- LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static - LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static
commands: commands:
- export VMODULES=$PWD/.vmodules
# Apparently this -D is *very* important # Apparently this -D is *very* important
- CFLAGS='-DGC_THREADS=1' make prod - CFLAGS='-DGC_THREADS=1' make prod
# Make sure the binary is actually statically built # Make sure the binary is actually statically built
@ -35,7 +47,7 @@ pipeline:
event: [push, pull_request] event: [push, pull_request]
upload: upload:
image: 'chewingbever/vlang:latest' image: *vlang_image
secrets: [ s3_username, s3_password ] secrets: [ s3_username, s3_password ]
commands: commands:
# https://gist.github.com/JustinTimperio/7c7115f87b775618637d67ac911e595f # https://gist.github.com/JustinTimperio/7c7115f87b775618637d67ac911e595f
@ -45,7 +57,7 @@ pipeline:
- export OBJ_PATH="/vieter/commits/$CI_COMMIT_SHA/vieter-$(echo '${PLATFORM}' | sed 's:/:-:g')" - export OBJ_PATH="/vieter/commits/$CI_COMMIT_SHA/vieter-$(echo '${PLATFORM}' | sed 's:/:-:g')"
- export SIG_STRING="PUT\n\n$CONTENT_TYPE\n$DATE\n$OBJ_PATH" - 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)" - export SIGNATURE="$(echo -en $SIG_STRING | openssl dgst -sha1 -hmac $S3_PASSWORD -binary | base64)"
- > - >
curl curl
--silent --silent

View File

@ -1,25 +1,29 @@
variables:
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
platform: 'linux/amd64' platform: 'linux/amd64'
branches: branches:
exclude: [ main ] exclude: [ main ]
pipeline: pipeline:
docs: docs:
image: 'klakegg/hugo:alpine' image: 'klakegg/hugo:ext-alpine'
group: 'generate' group: 'generate'
commands: commands:
- apk add git - apk add git
- make docs - make docs
api-docs: api-docs:
image: 'chewingbever/vlang:latest' image: *vlang_image
pull: true pull: true
group: 'generate' group: 'generate'
commands: commands:
- make api-docs - make api-docs
slate-docs: slate-docs:
image: 'slatedocs/slate' image: 'slatedocs/slate:v2.13.0'
group: 'generate' group: 'generate'
# Slate requires a specific directory to run in
commands: commands:
- cd docs/api - cd docs/api
- bundle exec middleman build --clean - bundle exec middleman build --clean

View File

@ -1,3 +1,6 @@
variables:
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
platform: 'linux/amd64' platform: 'linux/amd64'
branches: [ 'main' ] branches: [ 'main' ]
depends_on: depends_on:
@ -8,7 +11,7 @@ skip_clone: true
pipeline: pipeline:
prepare: prepare:
image: 'chewingbever/vlang:latest' image: *vlang_image
pull: true pull: true
secrets: [ s3_username, s3_password ] secrets: [ s3_username, s3_password ]
commands: commands:

View File

@ -0,0 +1,27 @@
variables:
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
# These checks already get performed on the feature branches
branches:
exclude: [ main ]
platform: 'linux/amd64'
pipeline:
# vfmt seems to get confused if these aren't present
install-modules:
image: *vlang_image
pull: true
commands:
- export VMODULES=$PWD/.vmodules
- 'cd src && v install'
when:
event: [pull_request]
lint:
image: *vlang_image
pull: true
commands:
- export VMODULES=$PWD/.vmodules
- make lint
when:
event: [pull_request]

View File

@ -1,3 +1,6 @@
variables:
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
platform: 'linux/amd64' platform: 'linux/amd64'
branches: branches:
exclude: [ main ] exclude: [ main ]
@ -5,15 +8,21 @@ branches:
depends_on: depends_on:
- build - build
skip_clone: true
pipeline: pipeline:
generate: install-modules:
image: 'chewingbever/vlang:latest' image: *vlang_image
pull: true pull: true
commands: commands:
- curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" - export VMODULES=$PWD/.vmodules
- chmod +x vieter - 'cd src && v install'
generate:
image: *vlang_image
commands:
# - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64"
# - chmod +x vieter
- export VMODULES=$PWD/.vmodules
- make
- ./vieter man man - ./vieter man man
- cd man - cd man

View File

@ -0,0 +1,30 @@
variables:
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
matrix:
PLATFORM:
- 'linux/amd64'
- 'linux/arm64'
branches:
exclude: [ main ]
platform: ${PLATFORM}
pipeline:
install-modules:
image: *vlang_image
pull: true
commands:
- export VMODULES=$PWD/.vmodules
- 'cd src && v install'
when:
event: [pull_request]
test:
image: *vlang_image
pull: true
commands:
- export VMODULES=$PWD/.vmodules
- make test
when:
event: [pull_request]

View File

@ -5,13 +5,120 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://git.rustybever.be/vieter/vieter/src/branch/dev) ## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev)
## [0.3.0](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0) ## [0.6.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.6.0)
### Added
* Metrics endpoint for Prometheus integration
* Search in list of targets using API & CLI
* Allow filtering targets by arch value
* Configurable global timeout for builds
### Changed
* Rewrote cron expression logic in C
* Updated codebase to V commit after 0.3.3
* Agents now use worker threads and no longer spawn a new thread for every
build
### Fixed
* Package upload now fails if TCP connection is closed before all bytes have
been received
### Removed
* Deprecated cron daemon
## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0)
### 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)
### Added
* API route for removing logs & accompanying CLI command
* Daemon for periodically removing old logs
* CLI flag to filter logs by specific exit codes
### Changed
* Use `--long-option` instead of `-long-option` for CLI
## [0.5.0-rc.1](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0-rc.1)
### Added
* Allow specifying subdirectory inside Git repository
* Added option to deploy using agent-server architecture instead of cron daemon
* Allow scheduling builds on the server from the CLI tool instead of building
them locally
* Allow force-building packages, meaning the build won't check if the
repository is already up to date
### Changed
* Migrated codebase to V 0.3.2
* Cron expression parser now uses bitfields instead of bool arrays
### Fixed
* Arch value for target is now properly set if not provided
* Allow NULL values for branch in database
* Endpoint for adding targets now returns the correct id
* CLI now correctly errors and doesn't error when sending requests
* Fixed possible infinite loop when removing old build images
* Check whether build image still exists before starting build
* Don't run makepkg `prepare()` function twice
* Don't buffer stdout in Docker containers
## [0.4.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.4.0)
### Added
* Server port can now be configured
* Targets now have a 'kind' field describing whether it's a Git repository or a
URL to a PKGBUILD
* Targets with kind 'url' can provide a direct URL to a PKGBUILD instead of
providing a Git repository
* CLI commands for searching the AUR & directly adding packages
* HTTP routes for removing packages, arch-repos & repos
* All endpoints serving files now support HTTP byte range requests
* Better CLI UX
* When adding targets, the ID of the created target is returned
* The `-r` flag only shows raw data of action
* When adding a target, only ID is shown and not surrounding text
* Tabled output returns a tab-separated list (easy to script using
`cut`)
### Changed
* Moved all API routes under `/v1` namespace
* Renamed `vieter repos` to `vieter targets`
* Renamed `/api/v1/repos` namespace to `/api/v1/targets`
* Branch name for 'git' targets is now optional; if not provided, the
repository will be cloned with the default branch
* Build containers now explicitely set the PATH variable
* Refactor of web framework
* API endpoints now return id of newly created entries
* Repo POST requests now return information on published package
* `api` can no longer be used as a repository name
* CLI client now allows setting values to an empty value
### Removed
* md5 hashes are no longer calculated for packages
## [0.3.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0)
Nothing besides bumping the versions. Nothing besides bumping the versions.
## [0.3.0-rc.1](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-rc.1) ## [0.3.0-rc.1](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0-rc.1)
### Added ### Added
@ -39,7 +146,7 @@ Nothing besides bumping the versions.
* `POST /api/logs` now correctly uses epoch timestamps instead of strings * `POST /api/logs` now correctly uses epoch timestamps instead of strings
## [0.3.0-alpha.2](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-alpha.2) ## [0.3.0-alpha.2](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0-alpha.2)
### Added ### Added
@ -64,7 +171,7 @@ Nothing besides bumping the versions.
* `vieter-git` is the latest commit on the dev branch * `vieter-git` is the latest commit on the dev branch
* Full refactor of Docker socket code * Full refactor of Docker socket code
## [0.3.0-alpha.1](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-alpha.1) ## [0.3.0-alpha.1](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0-alpha.1)
### Changed ### Changed
@ -83,7 +190,7 @@ Nothing besides bumping the versions.
* Binary no longer panics when an env var is missing * Binary no longer panics when an env var is missing
## [0.2.0](https://git.rustybever.be/vieter/vieter/src/tag/0.2.0) ## [0.2.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.2.0)
### Changed ### Changed
@ -117,13 +224,13 @@ Nothing besides bumping the versions.
* Packages with unknown fields in .PKGINFO are now allowed * Packages with unknown fields in .PKGINFO are now allowed
* Old packages are now properly removed * Old packages are now properly removed
## [0.1.0](https://git.rustybever.be/vieter/vieter/src/tag/0.1.0) ## [0.1.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.1.0)
### Changed ### Changed
* Improved logging * Improved logging
## [0.1.0-rc.1](https://git.rustybever.be/vieter/vieter/src/tag/0.1.0-rc.1) ## [0.1.0-rc.1](https://git.rustybever.be/vieter-v/vieter/src/tag/0.1.0-rc.1)
### Added ### Added

View File

@ -1,4 +1,4 @@
FROM chewingbever/vlang:latest AS builder FROM git.rustybever.be/chewing_bever/vlang:0.3.2 AS builder
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG CI_COMMIT_SHA ARG CI_COMMIT_SHA
@ -23,6 +23,7 @@ RUN if [ -n "${CI_COMMIT_SHA}" ]; then \
"https://s3.rustybever.be/vieter/commits/${CI_COMMIT_SHA}/vieter-$(echo "${TARGETPLATFORM}" | sed 's:/:-:g')" && \ "https://s3.rustybever.be/vieter/commits/${CI_COMMIT_SHA}/vieter-$(echo "${TARGETPLATFORM}" | sed 's:/:-:g')" && \
chmod +x vieter ; \ chmod +x vieter ; \
else \ else \
cd src && v install && cd .. && \
LDFLAGS='-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static' make prod && \ LDFLAGS='-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static' make prod && \
mv pvieter vieter ; \ mv pvieter vieter ; \
fi fi

View File

@ -1,16 +1,20 @@
# =====CONFIG===== # =====CONFIG=====
SRC_DIR := src SRC_DIR := src
SOURCES != find '$(SRC_DIR)' -iname '*.v' SRCS != find '$(SRC_DIR)' -iname '*.v'
V_PATH ?= v V_PATH ?= v
V := $(V_PATH) -showcc -gc boehm V := $(V_PATH) -showcc -gc boehm -d use_openssl -skip-unused
all: vieter all: vieter
# =====COMPILATION===== # =====COMPILATION=====
.PHONY: libvieter
libvieter:
make -C '$(SRC_DIR)/libvieter' CFLAGS='-O3'
# Regular binary # Regular binary
vieter: $(SOURCES) vieter: $(SOURCES) libvieter
$(V) -g -o vieter $(SRC_DIR) $(V) -g -o vieter $(SRC_DIR)
# Debug build using gcc # Debug build using gcc
@ -18,7 +22,7 @@ vieter: $(SOURCES)
# multi-threaded and causes issues when running vieter inside gdb. # multi-threaded and causes issues when running vieter inside gdb.
.PHONY: debug .PHONY: debug
debug: dvieter debug: dvieter
dvieter: $(SOURCES) dvieter: $(SOURCES) libvieter
$(V_PATH) -showcc -keepc -cg -o dvieter $(SRC_DIR) $(V_PATH) -showcc -keepc -cg -o dvieter $(SRC_DIR)
# Run the debug build inside gdb # Run the debug build inside gdb
@ -29,12 +33,12 @@ gdb: dvieter
# Optimised production build # Optimised production build
.PHONY: prod .PHONY: prod
prod: pvieter prod: pvieter
pvieter: $(SOURCES) pvieter: $(SOURCES) libvieter
$(V) -o pvieter -prod $(SRC_DIR) $(V) -o pvieter -prod $(SRC_DIR)
# Only generate C code # Only generate C code
.PHONY: c .PHONY: c
c: $(SOURCES) c: $(SOURCES) libvieter
$(V) -o vieter.c $(SRC_DIR) $(V) -o vieter.c $(SRC_DIR)
@ -67,6 +71,7 @@ man: vieter
# =====OTHER===== # =====OTHER=====
# Linting
.PHONY: lint .PHONY: lint
lint: lint:
$(V) fmt -verify $(SRC_DIR) $(V) fmt -verify $(SRC_DIR)
@ -74,34 +79,33 @@ lint:
$(V_PATH) missdoc -p $(SRC_DIR) $(V_PATH) missdoc -p $(SRC_DIR)
@ [ $$($(V_PATH) missdoc -p $(SRC_DIR) | wc -l) = 0 ] @ [ $$($(V_PATH) missdoc -p $(SRC_DIR) | wc -l) = 0 ]
# Format the V codebase
# Formatting
.PHONY: fmt .PHONY: fmt
fmt: fmt:
$(V) fmt -w $(SRC_DIR) $(V) fmt -w $(SRC_DIR)
# Testing
.PHONY: test .PHONY: test
test: test: libvieter
$(V) test $(SRC_DIR) $(V) -g test $(SRC_DIR)
# Build & patch the V compiler
.PHONY: v
v: v/v
v/v:
git clone --single-branch https://git.rustybever.be/Chewing_Bever/v v
make -C v
# Cleaning
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf 'data' 'vieter' 'dvieter' 'pvieter' 'vieter.c' 'dvieterctl' 'vieterctl' 'pkg' 'src/vieter' *.pkg.tar.zst 'suvieter' 'afvieter' '$(SRC_DIR)/_docs' 'docs/public' rm -rf 'data' 'vieter' 'dvieter' 'pvieter' 'vieter.c' 'pkg' 'src/vieter' *.pkg.tar.zst 'suvieter' 'afvieter' '$(SRC_DIR)/_docs' 'docs/public'
make -C '$(SRC_DIR)/libvieter' clean
# =====EXPERIMENTAL===== # =====EXPERIMENTAL=====
.PHONY: autofree .PHONY: autofree
autofree: afvieter autofree: afvieter
afvieter: $(SOURCES) afvieter: $(SOURCES)
$(V_PATH) -showcc -autofree -o afvieter $(SRC_DIR) $(V) -showcc -autofree -o afvieter $(SRC_DIR)
.PHONY: skip-unused .PHONY: skip-unused
skip-unused: suvieter skip-unused: suvieter
suvieter: $(SOURCES) suvieter: $(SOURCES)
$(V_PATH) -showcc -skip-unused -o suvieter $(SRC_DIR) $(V) -skip-unused -o suvieter $(SRC_DIR)

View File

@ -3,18 +3,36 @@
pkgbase='vieter' pkgbase='vieter'
pkgname='vieter' pkgname='vieter'
pkgver='0.3.0' pkgver='0.6.0'
pkgrel=1 pkgrel=1
pkgdesc="Vieter is a lightweight implementation of an Arch repository server." pkgdesc="Lightweight Arch repository server & package build system"
depends=('glibc' 'openssl' 'libarchive' 'sqlite') depends=('glibc' 'openssl' 'libarchive' 'sqlite')
makedepends=('git' 'vieter-v') makedepends=('git' 'vieter-vlang')
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url='https://git.rustybever.be/vieter/vieter' url='https://git.rustybever.be/vieter-v/vieter'
license=('AGPL3') license=('AGPL3')
source=("$pkgname::git+https://git.rustybever.be/vieter/vieter#tag=${pkgver//_/-}") source=(
md5sums=('SKIP') "$pkgname::git+https://git.rustybever.be/vieter-v/vieter#tag=${pkgver//_/-}"
"libvieter::git+https://git.rustybever.be/vieter-v/libvieter"
)
md5sums=('SKIP' 'SKIP')
prepare() {
cd "${pkgname}"
# Add the libvieter submodule
git submodule init
git config submodules.src/libvieter.url "${srcdir}/libvieter"
git -c protocol.file.allow=always submodule update
export VMODULES="${srcdir}/.vmodules"
cd src && v install
}
build() { build() {
export VMODULES="$srcdir/.vmodules"
cd "$pkgname" cd "$pkgname"
make prod make prod

View File

@ -5,25 +5,43 @@ pkgbase='vieter-git'
pkgname='vieter-git' pkgname='vieter-git'
pkgver=0.2.0.r25.g20112b8 pkgver=0.2.0.r25.g20112b8
pkgrel=1 pkgrel=1
pkgdesc="Vieter is a lightweight implementation of an Arch repository server." pkgdesc="Lightweight Arch repository server & package build system (development version)"
depends=('glibc' 'openssl' 'libarchive' 'sqlite') depends=('glibc' 'openssl' 'libarchive' 'sqlite')
makedepends=('git' 'vieter-v') makedepends=('git' 'vieter-vlang')
arch=('x86_64' 'aarch64') arch=('x86_64' 'aarch64')
url='https://git.rustybever.be/vieter/vieter' url='https://git.rustybever.be/vieter-v/vieter'
license=('AGPL3') license=('AGPL3')
source=("$pkgname::git+https://git.rustybever.be/vieter/vieter#branch=dev") source=(
md5sums=('SKIP') "${pkgname}::git+https://git.rustybever.be/vieter-v/vieter#branch=dev"
"libvieter::git+https://git.rustybever.be/vieter-v/libvieter"
)
md5sums=('SKIP' 'SKIP')
provides=('vieter') provides=('vieter')
conflicts=('vieter') conflicts=('vieter')
pkgver() { pkgver() {
cd "$pkgname" cd "${pkgname}"
git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
} }
prepare() {
cd "${pkgname}"
# Add the libvieter submodule
git submodule init
git config submodules.src/libvieter.url "${srcdir}/libvieter"
git -c protocol.file.allow=always submodule update
export VMODULES="${srcdir}/.vmodules"
cd src && v install
}
build() { build() {
cd "$pkgname" export VMODULES="${srcdir}/.vmodules"
cd "${pkgname}"
make prod make prod
@ -34,9 +52,9 @@ build() {
} }
package() { package() {
install -dm755 "$pkgdir/usr/bin" install -dm755 "${pkgdir}/usr/bin"
install -Dm755 "$pkgname/pvieter" "$pkgdir/usr/bin/vieter" install -Dm755 "${pkgname}/pvieter" "${pkgdir}/usr/bin/vieter"
install -dm755 "$pkgdir/usr/share/man/man1" install -dm755 "${pkgdir}/usr/share/man/man1"
install -Dm644 "$pkgname/man"/*.1 "$pkgdir/usr/share/man/man1" install -Dm644 "${pkgname}/man"/*.1 "${pkgdir}/usr/share/man/man1"
} }

View File

@ -1,11 +1,12 @@
# Vieter # Vieter
## Documentation
I host documentation for Vieter over at https://rustybever.be/docs/vieter/. API I host documentation for Vieter over at https://rustybever.be/docs/vieter/. API
documentation for the current codebase can be found at documentation for the current codebase can be found at
https://rustybever.be/api-docs/vieter/. https://rustybever.be/api-docs/vieter/.
For more information, questions or just a chat, there's
[#vieter:rustybever.be](https://matrix.to/#/#vieter:rustybever.be) on Matrix!
## Overview ## Overview
Vieter is a restart of the Pieter project. The goal is to create a simple, Vieter is a restart of the Pieter project. The goal is to create a simple,
@ -20,7 +21,8 @@ quicker.
I chose [V](https://vlang.io/) as I've been very intrigued by this language for I chose [V](https://vlang.io/) as I've been very intrigued by this language for
a while now. I wanted a fast language that I could code while relaxing, without a while now. I wanted a fast language that I could code while relaxing, without
having to exert too much mental effort & V seemed like the right choice for having to exert too much mental effort & V seemed like the right choice for
that. that. Sadly, this didn't quite turn out the way I expected, but I'm sticking
with it anyways ;p
## Features ## Features
@ -36,22 +38,19 @@ that.
Besides a V installer, Vieter also requires the following libraries to work: Besides a V installer, Vieter also requires the following libraries to work:
* gc
* libarchive * libarchive
* openssl * openssl
* sqlite3 * sqlite3
Vieter also depends on some external V modules which you can install using `cd
src && v install`. Make sure to keep these dependencies up to date using `v
update`.
### Compiler ### Compiler
Vieter compiles with the standard Vlang compiler. However, I do maintain a V is developed using a specific compiler commit that is usually updated
[mirror](https://git.rustybever.be/vieter/v). This is to ensure my CI does not whenever a new version is released. Information on this can be found in the
break without reason, as I control when & how frequently the mirror is updated [tools](https://git.rustybever.be/vieter-v/tools) repository.
to reflect the official repository.
If you encounter issues using the latest V compiler, try using my mirror
instead. `make v` will clone the repository & build the mirror. Afterwards,
prepending any make command with `V_PATH=v/v` tells make to use the locally
compiled mirror instead.
## Contributing ## Contributing

View File

@ -0,0 +1,78 @@
# Jobs
<aside class="notice">
All routes in this section require authentication.
</aside>
## Manually schedule a job
```shell
curl \
-H 'X-Api-Key: secret' \
https://example.com/api/v1/jobs/queue?target=10&force&arch=x86_64
```
Manually schedule a job on the server.
### HTTP Request
`POST /api/v1/jobs/queue`
### Query Parameters
Parameter | Description
--------- | -----------
target | Id of target to schedule build for
arch | Architecture to build on
force | Whether it's a forced build (true if present)
## Poll for new jobs
<aside class="warning">
This endpoint is used by the agents and should not be used manually. It's just
here for completeness. Requests to this endpoint modify the build queue,
meaning manual requests can cause builds to be skipped.
</aside>
```shell
curl \
-H 'X-Api-Key: secret' \
https://example.com/api/v1/jobs/poll?arch=x86_64&max=2
```
> JSON output format
```json
{
"message": "",
"data": [
{
"target_id": 1,
"kind": "git",
"url": "https://aur.archlinux.org/discord-ptb.git",
"branch": "master",
"path": "",
"repo": "bur",
"base_image": "archlinux:base-devel",
"force": true
}
]
}
```
Poll the server for new builds.
### HTTP Request
`GET /api/v1/jobs/poll`
### Query Parameters
Parameter | Description
--------- | -----------
arch | For which architecture to receive jobs
max | How many jobs to receive at most

View File

@ -13,7 +13,7 @@ Endpoints for interacting with stored build logs.
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/logs?offset=10&limit=20 https://example.com/api/v1/logs?offset=10&limit=20
``` ```
> JSON output format > JSON output format
@ -24,7 +24,7 @@ curl \
"data": [ "data": [
{ {
"id": 1, "id": 1,
"repo_id": 3, "target_id": 3,
"start_time": 1652008554, "start_time": 1652008554,
"end_time": 1652008559, "end_time": 1652008559,
"arch": "x86_64", "arch": "x86_64",
@ -38,7 +38,7 @@ Retrieve a list of build logs.
### HTTP Request ### HTTP Request
`GET /api/logs` `GET /api/v1/logs`
### Query Parameters ### Query Parameters
@ -46,7 +46,7 @@ Parameter | Description
--------- | ----------- --------- | -----------
limit | Maximum amount of results to return. limit | Maximum amount of results to return.
offset | Offset of results. offset | Offset of results.
repo | Only return builds published to this repository. target | Only return builds for this target id.
before | Only return logs started before this time (UTC epoch) before | Only return logs started before this time (UTC epoch)
after | Only return logs started after this time (UTC epoch) after | Only return logs started after this time (UTC epoch)
arch | Only return logs built on this architecture arch | Only return logs built on this architecture
@ -58,7 +58,7 @@ exit_codes | Comma-separated list of exit codes to limit result to; using `!` as
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/logs/15 https://example.com/api/v1/logs/1
``` ```
> JSON output format > JSON output format
@ -68,7 +68,7 @@ curl \
"message": "", "message": "",
"data": { "data": {
"id": 1, "id": 1,
"repo_id": 3, "target_id": 3,
"start_time": 1652008554, "start_time": 1652008554,
"end_time": 1652008559, "end_time": 1652008559,
"arch": "x86_64", "arch": "x86_64",
@ -81,7 +81,7 @@ Retrieve info about a specific build log.
### HTTP Request ### HTTP Request
`GET /api/logs/:id` `GET /api/v1/logs/:id`
### URL Parameters ### URL Parameters
@ -94,7 +94,7 @@ id | ID of requested log
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/logs/15/content https://example.com/api/v1/logs/15/content
``` ```
Retrieve the contents of a build log. The response is the build log in Retrieve the contents of a build log. The response is the build log in
@ -102,7 +102,7 @@ plaintext.
### HTTP Request ### HTTP Request
`GET /api/logs/:id/content` `GET /api/v1/logs/:id/content`
### URL Parameters ### URL Parameters
@ -112,10 +112,21 @@ id | ID of requested log
## Publish build log ## Publish build log
> JSON output format
```json
{
"message": "",
"data": {
"id": 15
}
}
```
<aside class="warning"> <aside class="warning">
You should probably not use this endpoint, as it's used by the build system to This endpoint is used by the agents and should not be used manually unless you
publish its logs. know what you're doing. It's just here for completeness.
</aside> </aside>
@ -123,18 +134,39 @@ Publish a new build log to the server.
### HTTP Request ### HTTP Request
`POST /api/logs` `POST /api/v1/logs`
### Query parameters ### Query parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | ID of requested log
startTime | Start time of the build (UTC epoch) startTime | Start time of the build (UTC epoch)
endTime | End time of the build (UTC epoch) endTime | End time of the build (UTC epoch)
arch | Architecture on which the build was done arch | Architecture on which the build was done
exitCode | Exit code of the build container exitCode | Exit code of the build container
target | id of target this build is for
### Request body ### Request body
Plaintext contents of the build log. Plaintext contents of the build log.
## Remove a build log
```shell
curl \
-XDELETE \
-H 'X-Api-Key: secret' \
https://example.com/api/v1/logs/1
```
Remove a build log from the server.
### HTTP Request
`DELETE /api/v1/logs/:id`
### URL Parameters
Parameter | Description
--------- | -----------
id | id of log to remove

View File

@ -93,3 +93,87 @@ other already present arch-repos.
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
repo | Repository to publish package to repo | Repository to publish package to
## Remove package from arch-repo
<aside class="notice">
This endpoint requests authentication.
</aside>
```shell
curl \
-H 'X-Api-Key: secret' \
-XDELETE \
https://example.com/vieter/x86_64/mike
```
This endpoint allows you to remove a package from a given arch-repo.
### HTTP Request
`DELETE /:repo/:arch/:pkg`
### URL Parameters
Parameter | Description
--------- | -----------
repo | Repository to delete package from
arch | Specific arch-repo to remove package from
pkg | Name of package to remove (without any version information)
## Remove arch-repo
<aside class="notice">
This endpoint requests authentication.
</aside>
```shell
curl \
-H 'X-Api-Key: secret' \
-XDELETE \
https://example.com/vieter/x86_64
```
This endpoint allows removing an entire arch-repo.
### HTTP Request
`DELETE /:repo/:arch`
### URL Parameters
Parameter | Description
--------- | -----------
repo | Repository to delete arch-repo from
arch | Specific architecture to remove
## Remove repo
<aside class="notice">
This endpoint requests authentication.
</aside>
```shell
curl \
-H 'X-Api-Key: secret' \
-XDELETE \
https://example.com/vieter
```
This endpoint allows removing an entire repo.
### HTTP Request
`DELETE /:repo`
### URL Parameters
Parameter | Description
--------- | -----------
repo | Repository to delete

View File

@ -1,4 +1,4 @@
# Git Repositories # Targets
<aside class="notice"> <aside class="notice">
@ -6,15 +6,14 @@ All routes in this section require authentication.
</aside> </aside>
Endpoints for interacting with the list of Git repositories stored on the Endpoints for interacting with the list of targets stored on the server.
server.
## List repos ## List targets
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/repos?offset=10&limit=20 https://example.com/api/v1/targets?offset=10&limit=20
``` ```
> JSON output format > JSON output format
@ -25,14 +24,16 @@ curl \
"data": [ "data": [
{ {
"id": 1, "id": 1,
"kind": "git",
"url": "https://aur.archlinux.org/discord-ptb.git", "url": "https://aur.archlinux.org/discord-ptb.git",
"branch": "master", "branch": "master",
"path" : "",
"repo": "bur", "repo": "bur",
"schedule": "", "schedule": "",
"arch": [ "arch": [
{ {
"id": 1, "id": 1,
"repo_id": 1, "target_id": 1,
"value": "x86_64" "value": "x86_64"
} }
] ]
@ -41,11 +42,11 @@ curl \
} }
``` ```
Retrieve a list of Git repositories. Retrieve a list of targets.
### HTTP Request ### HTTP Request
`GET /api/repos` `GET /api/v1/targets`
### Query Parameters ### Query Parameters
@ -53,14 +54,16 @@ Parameter | Description
--------- | ----------- --------- | -----------
limit | Maximum amount of results to return. limit | Maximum amount of results to return.
offset | Offset of results. offset | Offset of results.
repo | Limit results to repositories that publish to the given repo. repo | Limit results to targets that publish to the given repo.
query | Only return targets that have this substring in their URL, path or branch.
arch | Only return targets that publish to this arch.
## Get a repo ## Get specific target
```shell ```shell
curl \ curl \
-H 'X-Api-Key: secret' \ -H 'X-Api-Key: secret' \
https://example.com/api/repos/15 https://example.com/api/v1/targets/1
``` ```
> JSON output format > JSON output format
@ -70,14 +73,16 @@ curl \
"message": "", "message": "",
"data": { "data": {
"id": 1, "id": 1,
"kind": "git",
"url": "https://aur.archlinux.org/discord-ptb.git", "url": "https://aur.archlinux.org/discord-ptb.git",
"branch": "master", "branch": "master",
"path": "",
"repo": "bur", "repo": "bur",
"schedule": "0 3", "schedule": "0 2",
"arch": [ "arch": [
{ {
"id": 1, "id": 1,
"repo_id": 1, "target_id": 1,
"value": "x86_64" "value": "x86_64"
} }
] ]
@ -85,70 +90,92 @@ curl \
} }
``` ```
Get info about a specific Git repository. Get info about a specific target.
### HTTP Request ### HTTP Request
`GET /api/repos/:id` `GET /api/v1/targets/:id`
### URL Parameters ### URL Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | ID of requested repo id | id of requested target
## Create a new repo ## Create a new target
Create a new Git repository with the given data. > JSON output format
```json
{
"message": "",
"data": {
"id": 15
}
}
```
Create a new target with the given data.
### HTTP Request ### HTTP Request
`POST /api/repos` `POST /api/v1/targets`
### Query Parameters ### Query Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
kind | Kind of target to add; one of 'git', 'url'.
url | URL of the Git repository. url | URL of the Git repository.
branch | Branch of the Git repository. branch | Branch of the Git repository.
path | Subdirectory inside Git repository to use.
repo | Vieter repository to publish built packages to. repo | Vieter repository to publish built packages to.
schedule | Cron build schedule (syntax explained [here](https://rustybever.be/docs/vieter/usage/builds/schedule/)) schedule | Cron build schedule (syntax explained [here](https://rustybever.be/docs/vieter/usage/builds/schedule/))
arch | Comma-separated list of architectures to build package on. arch | Comma-separated list of architectures to build package on.
## Modify a repo ## Modify a target
Modify the data of an existing Git repository. Modify the data of an existing target.
### HTTP Request ### HTTP Request
`PATCH /api/repos/:id` `PATCH /api/v1/targets/:id`
### URL Parameters ### URL Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | ID of requested repo id | id of target to modify
### Query Parameters ### Query Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
kind | Kind of target; one of 'git', 'url'.
url | URL of the Git repository. url | URL of the Git repository.
branch | Branch of the Git repository. branch | Branch of the Git repository.
path | Subdirectory inside Git repository to use.
repo | Vieter repository to publish built packages to. repo | Vieter repository to publish built packages to.
schedule | Cron build schedule schedule | Cron build schedule
arch | Comma-separated list of architectures to build package on. arch | Comma-separated list of architectures to build package on.
## Remove a repo ## Remove a target
Remove a Git repository from the server. ```shell
curl \
-XDELETE \
-H 'X-Api-Key: secret' \
https://example.com/api/v1/targets/1
```
Remove a target from the server.
### HTTP Request ### HTTP Request
`DELETE /api/repos/:id` `DELETE /api/v1/targets/:id`
### URL Parameters ### URL Parameters
Parameter | Description Parameter | Description
--------- | ----------- --------- | -----------
id | ID of repo to remove id | id of target to remove

View File

@ -9,8 +9,9 @@ toc_footers:
includes: includes:
- repository - repository
- git - targets
- logs - logs
- jobs
search: true search: true

View File

@ -38,7 +38,7 @@ enableGitInfo = true
weight = 20 weight = 20
[[menu.after]] [[menu.after]]
name = "Vieter" name = "Vieter"
url = "https://git.rustybever.be/vieter/vieter" url = "https://git.rustybever.be/vieter-v/vieter"
weight = 30 weight = 30
[[menu.after]] [[menu.after]]
name = "Hugo Theme" name = "Hugo Theme"
@ -70,7 +70,7 @@ enableGitInfo = true
# Set source repository location. # Set source repository location.
# Used for 'Last Modified' and 'Edit this page' links. # Used for 'Last Modified' and 'Edit this page' links.
BookRepo = 'https://git.rustybever.be/vieter/vieter' BookRepo = 'https://git.rustybever.be/vieter-v/vieter'
# (Optional, default 'commit') Specifies commit portion of the link to the page's last modified # (Optional, default 'commit') Specifies commit portion of the link to the page's last modified
# commit hash for 'doc' page type. # commit hash for 'doc' page type.

View File

@ -17,7 +17,7 @@ If a variable is both present in the config file & as an environment variable,
the value in the environment variable is used. the value in the environment variable is used.
{{< hint info >}} {{< hint info >}}
**Note** **Note**
All environment variables can also be provided from a file by appending them All environment variables can also be provided from a file by appending them
with `_FILE`. This for example allows you to provide the API key from a Docker with `_FILE`. This for example allows you to provide the API key from a Docker
secrets file. secrets file.
@ -26,17 +26,17 @@ secrets file.
## Commands ## Commands
The first argument passed to Vieter determines which command you wish to use. The first argument passed to Vieter determines which command you wish to use.
Each of these can contain subcommands (e.g. `vieter repos list`), but all Each of these can contain subcommands (e.g. `vieter targets list`), but all
subcommands will use the same configuration. Below you can find the subcommands will use the same configuration. Below you can find the
configuration variable required for each command. configuration variable required for each command.
### `vieter server` ### `vieter server`
* `port`: HTTP port to run on
* Default: `8000`
* `log_level`: log verbosity level. Value should be one of `FATAL`, `ERROR`, * `log_level`: log verbosity level. Value should be one of `FATAL`, `ERROR`,
`WARN`, `INFO` or `DEBUG`. `WARN`, `INFO` or `DEBUG`.
* Default: `WARN` * Default: `WARN`
* `log_file`: log file to write logs to.
* Default: `vieter.log` (in the current directory)
* `pkg_dir`: where Vieter should store the actual package archives. * `pkg_dir`: where Vieter should store the actual package archives.
* `data_dir`: where Vieter stores the repositories, log file & database. * `data_dir`: where Vieter stores the repositories, log file & database.
* `api_key`: the API key to use when authenticating requests. * `api_key`: the API key to use when authenticating requests.
@ -44,8 +44,26 @@ configuration variable required for each command.
* Packages with architecture `any` are always added to this architecture. * Packages with architecture `any` are always added to this architecture.
This prevents the server from being confused when an `any` package is This prevents the server from being confused when an `any` package is
published as the very first package for a repository. published as the very first package for a repository.
* Git repositories added without an `arch` value use this value instead. * Targets added without an `arch` value use this value instead.
* `global_schedule`: build schedule for any target that does not have a
schedule defined. For information about this syntax, see
[here](/usage/builds/schedule).
* Default: `0 3` (3AM every night)
* `base_image`: Docker image to use when building a package. Any Pacman-based
distro image should work, as long as `/etc/pacman.conf` is used &
`base-devel` exists in the repositories. Make sure that the image supports
the architecture of your cron daemon.
* Default: `archlinux:base-devel` (only works on `x86_64`). If you require
`aarch64` support, consider using
[`menci/archlinuxarm:base-devel`](https://hub.docker.com/r/menci/archlinuxarm)
([GitHub](https://github.com/Menci/docker-archlinuxarm)). This is the
image used for the Vieter CI builds.
* `max_log_age`: maximum age of logs (in days). Logs older than this will get
cleaned by the log removal daemon. If set to zero, no logs are ever removed.
The age of logs is determined by the time the build was started.
* Default: `0`
* `log_removal_schedule`: cron schedule defining when to clean old logs.
* Default: `0 0` (every day at midnight)
### `vieter cron` ### `vieter cron`
@ -88,11 +106,33 @@ configuration variable required for each command.
* `api_key`: the API key to use when authenticating requests. * `api_key`: the API key to use when authenticating requests.
* `address`: Base URL of your Vieter instance, e.g. https://example.com * `address`: Base URL of your Vieter instance, e.g. https://example.com
### `vieter repos` ### `vieter targets`
* `api_key`: the API key to use when authenticating requests. * `api_key`: the API key to use when authenticating requests.
* `address`: Base URL of your Vieter instance, e.g. https://example.com * `address`: Base URL of your Vieter instance, e.g. https://example.com
* `base_image`: image to use when building a package using `vieter repos * `base_image`: image to use when building a package using `vieter targets
build`. build`.
* Default: `archlinux:base-devel` * Default: `archlinux:base-devel`
### `vieter agent`
* `log_level`: log verbosity level. Value should be one of `FATAL`, `ERROR`,
`WARN`, `INFO` or `DEBUG`.
* Default: `WARN`
* `address`: *public* URL of the Vieter repository server to build for. From
this server jobs are retrieved. All built packages are published to this
server.
* `api_key`: API key of the above server.
* `data_dir`: directory to store log file in.
* `max_concurrent_builds`: how many builds to run at the same time.
* Default: `1`
* `polling_frequency`: how often (in seconds) to poll the server for new
builds. Note that the agent might poll more frequently when it's actively
processing builds.
* `image_rebuild_frequency`: Vieter periodically builds images that are then
used as a basis for running build containers. This is to prevent each build
from downloading an entire repository worth of dependencies. This setting
defines how frequently (in minutes) to rebuild these images.
* Default: `1440` (every 24 hours)
* `arch`: architecture for which this agent should pull down builds (e.g.
`x86_64`)

View File

@ -21,17 +21,17 @@ branch. This branch will be the most up to date, but does not give any
guarantees about stability, so beware! guarantees about stability, so beware!
Thanks to the single-binary design of Vieter, this image can be used both for Thanks to the single-binary design of Vieter, this image can be used both for
the repository server & the cron daemon. the repository server, the cron daemon and the agent.
Below is an example compose file to set up both the repository server & the Below is a minimal compose file to set up both the repository server & a build
cron daemon: agent:
```yaml ```yaml
version: '3' version: '3'
services: services:
server: server:
image: 'chewingbever/vieter:dev' image: 'chewingbever/vieter:0.5.0-rc.1'
restart: 'always' restart: 'always'
environment: environment:
@ -41,18 +41,19 @@ services:
- 'data:/data' - 'data:/data'
cron: cron:
image: 'chewingbever/vieter:dev' image: 'chewingbever/vieter:0.5.0-rc.1'
restart: 'always' restart: 'always'
# Required to connect to the Docker daemon
user: root user: root
command: 'vieter cron' command: 'vieter agent'
environment: environment:
- 'VIETER_API_KEY=secret' - 'VIETER_API_KEY=secret'
# MUST be public URL of Vieter repository # MUST be public URL of Vieter repository
- 'VIETER_ADDRESS=https://example.com' - 'VIETER_ADDRESS=https://example.com'
- 'VIETER_DEFAULT_ARCH=x86_64' # Architecture for which the agent builds
- 'VIETER_ARCH=x86_64'
- 'VIETER_MAX_CONCURRENT_BUILDS=2' - 'VIETER_MAX_CONCURRENT_BUILDS=2'
- 'VIETER_GLOBAL_SCHEDULE=0 3'
volumes: volumes:
- '/var/run/docker.sock:/var/run/docker.sock' - '/var/run/docker.sock:/var/run/docker.sock'
@ -63,20 +64,23 @@ volumes:
If you do not require the build system, the repository server can be used If you do not require the build system, the repository server can be used
independently as well. independently as well.
Of course, Vieter allows a lot more configuration than this. This compose file
is meant as a starting point for setting up your installation.
{{< hint info >}} {{< hint info >}}
**Note** **Note**
Builds are executed on the cron daemon's system using the host's Docker daemon. Builds are executed on the agent's system using the host's Docker daemon. An
A cron daemon on a specific architecture will only build packages for that agent for a specific `arch` will only build packages for that specific
specific architecture. Therefore, if you wish to build packages for both architecture. Therefore, if you wish to build packages for both `x86_64` &
`x86_64` & `aarch64`, you'll have to deploy two cron daemons, one on each `aarch64`, you'll have to deploy two agents, one on each architecture.
architecture. Afterwards, any Git repositories enabled for those two Afterwards, any Git repositories enabled for those two architectures will build
architectures will build on both. on both.
{{< /hint >}} {{< /hint >}}
## Binary ## Binary
On the On the
[releases](https://git.rustybever.be/vieter/vieter/releases) [releases](https://git.rustybever.be/vieter-v/vieter/releases)
page, you can find statically compiled binaries for all page, you can find statically compiled binaries for all
released versions. This is the same binary as used inside released versions. This is the same binary as used inside
the Docker images. the Docker images.
@ -96,7 +100,15 @@ SigLevel = Optional
Afterwards, you can update your system & install the `vieter` package for the Afterwards, you can update your system & install the `vieter` package for the
latest official release or `vieter-git` for the latest development release. latest official release or `vieter-git` for the latest development release.
### AUR
If you prefer building the packages locally (or on your own Vieter instance),
there's the [`vieter`](https://aur.archlinux.org/packages/vieter) &
[`vieter-git`](https://aur.archlinux.org/packages/vieter-git) packages on the
AUR. These packages build using the `vlang` compiler package, so I can't
guarantee that a compiler update won't temporarily break them.
## Building from source ## Building from source
The project [README](https://git.rustybever.be/vieter/vieter#building) contains The project [README](https://git.rustybever.be/vieter-v/vieter#building)
instructions for building Vieter from source. contains instructions for building Vieter from source.

View File

@ -1,3 +0,0 @@
---
weight: 100
---

View File

@ -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

View File

@ -16,28 +16,28 @@ info to the system. The Vieter repository server exposes an HTTP API for this
info). For ease of use, the Vieter binary contains a CLI interface for info). For ease of use, the Vieter binary contains a CLI interface for
interacting with this API (see [Configuration](/configuration) for interacting with this API (see [Configuration](/configuration) for
configuration details). The [man configuration details). The [man
pages](https://rustybever.be/man/vieter/vieter-repos.1.html) describe this in pages](https://rustybever.be/man/vieter/vieter-targets.1.html) describe this in
greater detail, but the basic usage is as follows: greater detail, but the basic usage is as follows:
``` ```
vieter repos add some-url some-branch some-repository vieter targets add some-url some-repository
``` ```
Here, `some-url` is the URL of the Git repository containing the PKGBUILD. This Here, `some-url` is the URL of the Git repository containing the PKGBUILD. This
URL is passed to `git clone`, meaning the repository should be public. Vieter URL is passed to `git clone`, meaning the repository should be public. Vieter
expects the same format as an AUR Git repository, so you can directly use AUR expects the same format as an AUR Git repository, so you can directly use AUR
URLs here. URLs here. Alternatively, you can also provide the URL to a PKGBUILD file
instead. See
[vieter-targets-add(1)](https://rustybever.be/man/vieter/vieter-targets-add.1.html)
for more information.
`some-branch` is the branch of the Git repository the build should check out. `some-repo` is the repository to which the built package archives should be
If you're using an AUR package, this should be `master`. published.
Finally, `some-repo` is the repository to which the built package archives
should be published.
The above command intentionally leaves out a few parameters to make the CLI The above command intentionally leaves out a few parameters to make the CLI
more useable. For information on how to modify all parameters using the CLI, more useable. For information on how to modify all parameters using the CLI,
see see
[vieter-repos-edit(1)](https://rustybever.be/man/vieter/vieter-repos-edit.1.html). [vieter-targets(1)](https://rustybever.be/man/vieter/vieter-targets.1.html).
## Reading logs ## Reading logs

View File

@ -0,0 +1,23 @@
---
weight: 20
---
# Cleanup
Vieter stores the logs of every single package build. While this is great for
debugging why builds fail, it also causes an active or long-running Vieter
instance to accumulate thousands of logs.
To combat this, a log removal daemon can be enabled that periodically removes
old build logs. By starting your server with the `max_log_age` variable (see
[Configuration](/configuration#vieter-server)), a daemon will get enabled that
periodically removes logs older than this setting. By default, this will happen
every day at midnight, but this behavior can be changed using the
`log_removal_schedule` variable.
{{< hint info >}}
**Note**
The daemon will always run a removal of logs on startup. Therefore, it's
possible the daemon will be *very* active when first enabling this setting.
After the initial surge of logs to remove, it'll calm down again.
{{< /hint >}}

View File

@ -1,3 +1,7 @@
---
weight: 10
---
# Cron schedule syntax # Cron schedule syntax
The Vieter cron daemon uses a subset of the cron expression syntax to schedule The Vieter cron daemon uses a subset of the cron expression syntax to schedule
@ -37,6 +41,6 @@ Each section can consist of as many of these parts as necessary.
## CLI tool ## CLI tool
The Vieter binary contains a command that shows you the next matching times for The Vieter binary contains a command that shows you the next matching times for
a given expression. This can be useful to understand the syntax. For more a given expression. This can be useful for understanding the syntax. For more
information, see information, see
[vieter-schedule(1)](https://rustybever.be/man/vieter/vieter-schedule.1.html). [vieter-schedule(1)](https://rustybever.be/man/vieter/vieter-schedule.1.html).

27
src/agent/agent.v 100644
View File

@ -0,0 +1,27 @@
module agent
import log
import os
import util
const log_file_name = 'vieter.agent.log'
// agent starts an agent service
pub fn agent(conf Config) ! {
log_level := log.level_from_tag(conf.log_level) or {
return error('Invalid log level. The allowed values are FATAL, ERROR, WARN, INFO & DEBUG.')
}
mut logger := log.Log{
level: log_level
}
os.mkdir_all(conf.data_dir) or { util.exit_with_message(1, 'Failed to create data directory.') }
log_file := os.join_path_single(conf.data_dir, agent.log_file_name)
logger.set_full_logpath(log_file)
logger.log_to_console_too()
mut d := agent_init(logger, conf)
d.run()
}

31
src/agent/cli.v 100644
View File

@ -0,0 +1,31 @@
module agent
import cli
import conf as vconf
struct Config {
pub:
log_level string = 'WARN'
// Architecture that the agent represents
arch string
api_key string
address string
data_dir string
max_concurrent_builds int = 1
polling_frequency int = 30
image_rebuild_frequency int = 1440
}
// cmd returns the cli module that handles the cron daemon.
pub fn cmd() cli.Command {
return cli.Command{
name: 'agent'
description: 'Start an agent daemon.'
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
agent(conf_)!
}
}
}

197
src/agent/daemon.v 100644
View File

@ -0,0 +1,197 @@
module agent
import log
import sync.stdatomic
import build
import models { BuildConfig }
import client
import time
import os
const (
build_empty = 0
build_running = 1
build_done = 2
)
struct AgentDaemon {
logger shared log.Log
conf Config
client client.Client
mut:
images ImageManager
// Atomic variables used to detect when a build has finished; length is
// conf.max_concurrent_builds. This approach is used as the difference
// between a recently finished build and an empty build slot is important
// for knowing whether the agent is currently "active".
atomics []u64
// Channel used to send builds to worker threads
build_channel chan BuildConfig
}
// agent_init initializes a new agent
fn agent_init(logger log.Log, conf Config) AgentDaemon {
mut d := AgentDaemon{
logger: logger
client: client.new(conf.address, conf.api_key)
conf: conf
images: new_image_manager(conf.image_rebuild_frequency * 60)
atomics: []u64{len: conf.max_concurrent_builds}
build_channel: chan BuildConfig{cap: conf.max_concurrent_builds}
}
return d
}
// run starts the actual agent daemon. This function will run forever.
pub fn (mut d AgentDaemon) run() {
// Spawn worker threads
for builder_index in 0 .. d.conf.max_concurrent_builds {
spawn d.builder_thread(d.build_channel, builder_index)
}
// This is just so that the very first time the loop is ran, the jobs are
// always polled
mut last_poll_time := time.now().add_seconds(-d.conf.polling_frequency)
mut sleep_time := 0 * time.second
mut finished, mut empty, mut running := 0, 0, 0
for {
if sleep_time > 0 {
d.ldebug('Sleeping for ${sleep_time}')
time.sleep(sleep_time)
}
finished, empty = d.update_atomics()
running = d.conf.max_concurrent_builds - finished - empty
// No new finished builds and no free slots, so there's nothing to be
// done
if finished + empty == 0 {
sleep_time = 1 * time.second
continue
}
// Builds have finished, so old builder images might have freed up.
// TODO this might query the docker daemon too frequently.
if finished > 0 {
d.images.clean_old_images()
}
// The agent will always poll for new jobs after at most
// `polling_frequency` seconds. However, when jobs have finished, the
// agent will also poll for new jobs. This is because jobs are often
// clustered together (especially when mostly using the global cron
// schedule), so there's a much higher chance jobs are available.
if finished > 0 || time.now() >= last_poll_time.add_seconds(d.conf.polling_frequency) {
d.ldebug('Polling for new jobs')
new_configs := d.client.poll_jobs(d.conf.arch, finished + empty) or {
d.lerror('Failed to poll jobs: ${err.msg()}')
// TODO pick a better delay here
sleep_time = 5 * time.second
continue
}
d.ldebug('Received ${new_configs.len} jobs')
last_poll_time = time.now()
for config in new_configs {
// Make sure a recent build base image is available for
// building the config
if !d.images.up_to_date(config.base_image) {
d.linfo('Building builder image from base image ${config.base_image}')
// TODO handle this better than to just skip the config
d.images.refresh_image(config.base_image) or {
d.lerror(err.msg())
continue
}
}
// It's technically still possible that the build image is
// removed in the very short period between building the
// builder image and starting a build container with it. If
// this happens, fate really just didn't want you to do this
// build.
d.build_channel <- config
running++
}
}
// The agent is not doing anything, so we just wait until the next poll
// time
if running == 0 {
sleep_time = last_poll_time.add_seconds(d.conf.polling_frequency) - time.now()
} else {
sleep_time = 1 * time.second
}
}
}
// update_atomics checks for each build whether it's completed, and sets it to
// empty again if so. The return value is a tuple `(finished, empty)` where
// `finished` is how many builds were just finished and thus set to empty, and
// `empty` is how many build slots were already empty. The amount of running
// builds can then be calculated by substracting these two values from the
// total allowed concurrent builds.
fn (mut d AgentDaemon) update_atomics() (int, int) {
mut finished := 0
mut empty := 0
for i in 0 .. d.atomics.len {
if stdatomic.load_u64(&d.atomics[i]) == agent.build_done {
stdatomic.store_u64(&d.atomics[i], agent.build_empty)
finished++
} else if stdatomic.load_u64(&d.atomics[i]) == agent.build_empty {
empty++
}
}
return finished, empty
}
// run_build actually starts the build process for a given target.
fn (mut d AgentDaemon) run_build(build_index int, config BuildConfig) {
d.linfo('started build: ${config}')
// 0 means success, 1 means failure
mut status := 0
new_config := BuildConfig{
...config
base_image: d.images.get(config.base_image)
}
res := build.build_config(d.client.address, d.client.api_key, new_config) or {
d.ldebug('build_config error: ${err.msg()}')
status = 1
build.BuildResult{}
}
if status == 0 {
d.linfo('Uploading build logs for ${config}')
// TODO use the arch value here
build_arch := os.uname().machine
d.client.add_build_log(config.target_id, res.start_time, res.end_time, build_arch,
res.exit_code, res.logs) or { d.lerror('Failed to upload logs for ${config}') }
} else {
d.lwarn('an error occurred during build: ${config}')
}
stdatomic.store_u64(&d.atomics[build_index], agent.build_done)
}
// builder_thread is a thread that constantly listens for builds to process
fn (mut d AgentDaemon) builder_thread(ch chan BuildConfig, builder_index int) {
for {
build_config := <-ch or { break }
d.run_build(builder_index, build_config)
}
}

119
src/agent/images.v 100644
View File

@ -0,0 +1,119 @@
module agent
import time
import docker
import build
// An ImageManager is a utility that creates builder images from given base
// images, updating these builder images if they've become too old. This
// structure can manage images from any number of base images, paving the way
// for configurable base images per target/repository.
struct ImageManager {
max_image_age int [required]
mut:
// For each base image, one or more builder images can exist at the same
// time
images map[string][]string [required]
// For each base image, we track when its newest image was built
timestamps map[string]time.Time [required]
}
// new_image_manager initializes a new image manager.
fn new_image_manager(max_image_age int) ImageManager {
return ImageManager{
max_image_age: max_image_age
images: map[string][]string{}
timestamps: map[string]time.Time{}
}
}
// get returns the name of the newest image for the given base image. Note that
// this function should only be called *after* a first call to `refresh_image`.
pub fn (m &ImageManager) get(base_image string) string {
return m.images[base_image].last()
}
// up_to_date returns true if the last known builder image exists and is up to
// date. If this function returns true, the last builder image may be used to
// perform a build.
pub fn (mut m ImageManager) up_to_date(base_image string) bool {
if base_image !in m.timestamps
|| m.timestamps[base_image].add_seconds(m.max_image_age) <= time.now() {
return false
}
// It's possible the image has been removed by some external event, so we
// check whether it actually exists as well.
mut dd := docker.new_conn() or { return false }
defer {
dd.close() or {}
}
dd.image_inspect(m.images[base_image].last()) or {
// Image doesn't exist, so we stop tracking it
if err.code() == 404 {
m.images[base_image].delete_last()
m.timestamps.delete(base_image)
}
// If the inspect fails, it's either because the image doesn't exist or
// because of some other error. Either way, we can't know *for certain*
// that the image exists, so we return false.
return false
}
return true
}
// refresh_image builds a new builder image from the given base image. This
// function should only be called if `up_to_date` returned false.
fn (mut m ImageManager) refresh_image(base_image string) ! {
// TODO use better image tags for built images
new_image := build.create_build_image(base_image) or {
return error('Failed to build builder image from base image ${base_image}')
}
m.images[base_image] << new_image
m.timestamps[base_image] = time.now()
}
// clean_old_images removes all older builder images that are no longer in use.
// The function will always leave at least one builder image, namely the newest
// one.
fn (mut m ImageManager) clean_old_images() {
mut dd := docker.new_conn() or { return }
defer {
dd.close() or {}
}
mut i := 0
for image in m.images.keys() {
i = 0
for i < m.images[image].len - 1 {
// For each builder image, we try to remove it by calling the Docker
// API. If the function returns an error or false, that means the image
// wasn't deleted. Therefore, we move the index over. If the function
// returns true, the array's length has decreased by one so we don't
// move the index.
dd.image_remove(m.images[image][i]) or {
// The image was removed by an external event
if err.code() == 404 {
m.images[image].delete(i)
}
// The image couldn't be removed, so we need to keep track of
// it
else {
i += 1
}
continue
}
m.images[image].delete(i)
}
}
}

36
src/agent/log.v 100644
View File

@ -0,0 +1,36 @@
module agent
// lfatal create a log message with the fatal level
pub fn (mut d AgentDaemon) lfatal(msg string) {
lock d.logger {
d.logger.fatal(msg)
}
}
// lerror create a log message with the error level
pub fn (mut d AgentDaemon) lerror(msg string) {
lock d.logger {
d.logger.error(msg)
}
}
// lwarn create a log message with the warn level
pub fn (mut d AgentDaemon) lwarn(msg string) {
lock d.logger {
d.logger.warn(msg)
}
}
// linfo create a log message with the info level
pub fn (mut d AgentDaemon) linfo(msg string) {
lock d.logger {
d.logger.info(msg)
}
}
// ldebug create a log message with the debug level
pub fn (mut d AgentDaemon) ldebug(msg string) {
lock d.logger {
d.logger.debug(msg)
}
}

View File

@ -4,7 +4,7 @@
#include "archive.h" #include "archive.h"
struct C.archive {} pub struct C.archive {}
// Create a new archive struct for reading // Create a new archive struct for reading
fn C.archive_read_new() &C.archive fn C.archive_read_new() &C.archive
@ -71,7 +71,7 @@ fn C.archive_filter_code(&C.archive, int) int
#include "archive_entry.h" #include "archive_entry.h"
struct C.archive_entry {} pub struct C.archive_entry {}
// Create a new archive_entry struct // Create a new archive_entry struct
fn C.archive_entry_new() &C.archive_entry fn C.archive_entry_new() &C.archive_entry

View File

@ -6,11 +6,14 @@ import time
import os import os
import strings import strings
import util import util
import models { GitRepo } import models { BuildConfig, Target }
const ( const (
container_build_dir = '/build' container_build_dir = '/build'
build_image_repo = 'vieter-build' build_image_repo = 'vieter-build'
// Contents of PATH variable in build containers
path_dirs = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', '/usr/local/sbin',
'/usr/local/bin', '/usr/bin/site_perl', '/usr/bin/vendor_perl', '/usr/bin/core_perl']
) )
// create_build_image creates a builder image given some base image which can // create_build_image creates a builder image given some base image which can
@ -18,8 +21,8 @@ const (
// system, install some necessary packages & creates a non-root user to run // system, install some necessary packages & creates a non-root user to run
// makepkg with. The base image should be some Linux distribution that uses // makepkg with. The base image should be some Linux distribution that uses
// Pacman as its package manager. // Pacman as its package manager.
pub fn create_build_image(base_image string) ?string { pub fn create_build_image(base_image string) !string {
mut dd := docker.new_conn()? mut dd := docker.new_conn()!
defer { defer {
dd.close() or {} dd.close() or {}
@ -42,7 +45,7 @@ pub fn create_build_image(base_image string) ?string {
c := docker.NewContainer{ c := docker.NewContainer{
image: base_image image: base_image
env: ['BUILD_SCRIPT=$cmds_str'] env: ['BUILD_SCRIPT=${cmds_str}']
entrypoint: ['/bin/sh', '-c'] entrypoint: ['/bin/sh', '-c']
cmd: ['echo \$BUILD_SCRIPT | base64 -d | /bin/sh -e'] cmd: ['echo \$BUILD_SCRIPT | base64 -d | /bin/sh -e']
} }
@ -54,15 +57,15 @@ pub fn create_build_image(base_image string) ?string {
image_tag := if image_parts.len > 1 { image_parts[1] } else { 'latest' } image_tag := if image_parts.len > 1 { image_parts[1] } else { 'latest' }
// We pull the provided image // We pull the provided image
dd.pull_image(image_name, image_tag)? dd.image_pull(image_name, image_tag)!
id := dd.create_container(c)?.id id := dd.container_create(c)!.id
// id := docker.create_container(c)? // id := docker.create_container(c)!
dd.start_container(id)? dd.container_start(id)!
// This loop waits until the container has stopped, so we can remove it after // This loop waits until the container has stopped, so we can remove it after
for { for {
data := dd.inspect_container(id)? data := dd.container_inspect(id)!
if !data.state.running { if !data.state.running {
break break
@ -76,8 +79,8 @@ pub fn create_build_image(base_image string) ?string {
// TODO also add the base image's name into the image name to prevent // TODO also add the base image's name into the image name to prevent
// conflicts. // conflicts.
tag := time.sys_mono_now().str() tag := time.sys_mono_now().str()
image := dd.create_image_from_container(id, 'vieter-build', tag)? image := dd.image_from_container(id, 'vieter-build', tag)!
dd.remove_container(id)? dd.container_remove(id)!
return image.id return image.id
} }
@ -90,51 +93,72 @@ pub:
logs string logs string
} }
// build_repo builds, packages & publishes a given Arch package based on the // build_target builds the given target. Internally it calls `build_config`.
// provided GitRepo. The base image ID should be of an image previously created pub fn build_target(address string, api_key string, base_image_id string, target &Target, force bool, timeout int) !BuildResult {
config := target.as_build_config(base_image_id, force, timeout)
return build_config(address, api_key, config)
}
// build_config builds, packages & publishes a given Arch package based on the
// provided target. The base image ID should be of an image previously created
// by create_build_image. It returns the logs of the container. // by create_build_image. It returns the logs of the container.
pub fn build_repo(address string, api_key string, base_image_id string, repo &GitRepo) ?BuildResult { pub fn build_config(address string, api_key string, config BuildConfig) !BuildResult {
mut dd := docker.new_conn()? mut dd := docker.new_conn()!
defer { defer {
dd.close() or {} dd.close() or {}
} }
build_arch := os.uname().machine build_arch := os.uname().machine
build_script := create_build_script(address, repo, build_arch) build_script := create_build_script(address, config, build_arch)
// We convert the build script into a base64 string, which then gets passed // We convert the build script into a base64 string, which then gets passed
// to the container as an env var // to the container as an env var
base64_script := base64.encode_str(build_script) base64_script := base64.encode_str(build_script)
c := docker.NewContainer{ c := docker.NewContainer{
image: '$base_image_id' image: '${config.base_image}'
env: ['BUILD_SCRIPT=$base64_script', 'API_KEY=$api_key'] env: [
'BUILD_SCRIPT=${base64_script}',
'API_KEY=${api_key}',
// `archlinux:base-devel` does not correctly set the path variable,
// causing certain builds to fail. This fixes it.
'PATH=${build.path_dirs.join(':')}',
]
entrypoint: ['/bin/sh', '-c'] entrypoint: ['/bin/sh', '-c']
cmd: ['echo \$BUILD_SCRIPT | base64 -d | /bin/bash -e'] cmd: ['echo \$BUILD_SCRIPT | base64 -d | /bin/bash -e']
work_dir: '/build' work_dir: '/build'
user: '0:0' user: '0:0'
} }
id := dd.create_container(c)?.id id := dd.container_create(c)!.id
dd.start_container(id)? dd.container_start(id)!
mut data := dd.inspect_container(id)? mut data := dd.container_inspect(id)!
start_time := time.now()
// This loop waits until the container has stopped, so we can remove it after // This loop waits until the container has stopped, so we can remove it after
for data.state.running { for data.state.running {
if time.now() - start_time > config.timeout * time.second {
dd.container_kill(id)!
dd.container_remove(id)!
return error('Build killed due to timeout (${config.timeout}s)')
}
time.sleep(1 * time.second) time.sleep(1 * time.second)
data = dd.inspect_container(id)? data = dd.container_inspect(id)!
} }
mut logs_stream := dd.get_container_logs(id)? mut logs_stream := dd.container_get_logs(id)!
// Read in the entire stream // Read in the entire stream
mut logs_builder := strings.new_builder(10 * 1024) mut logs_builder := strings.new_builder(10 * 1024)
util.reader_to_writer(mut logs_stream, mut logs_builder)? util.reader_to_writer(mut logs_stream, mut logs_builder)!
dd.remove_container(id)? dd.container_remove(id)!
return BuildResult{ return BuildResult{
start_time: data.state.start_time start_time: data.state.start_time

219
src/build/queue.v 100644
View File

@ -0,0 +1,219 @@
module build
import models { BuildConfig, Target }
import cron
import time
import datatypes { MinHeap }
import util
struct BuildJob {
pub mut:
// Time at which this build job was created/queued
created time.Time
// Next timestamp from which point this job is allowed to be executed
timestamp time.Time
// Required for calculating next timestamp after having pop'ed a job
ce &cron.Expression = unsafe { nil }
// Actual build config sent to the agent
config BuildConfig
// Whether this is a one-time job
single bool
}
// Allows BuildJob structs to be sorted according to their timestamp in
// MinHeaps
fn (r1 BuildJob) < (r2 BuildJob) bool {
return r1.timestamp < r2.timestamp
}
// The build job queue is responsible for managing the list of scheduled builds
// for each architecture. Agents receive jobs from this queue.
pub struct BuildJobQueue {
// Schedule to use for targets without explicitely defined cron expression
default_schedule &cron.Expression
// Base image to use for targets without defined base image
default_base_image string
// After how many minutes a build should be forcefully cancelled
default_build_timeout int
mut:
mutex shared util.Dummy
// For each architecture, a priority queue is tracked
queues map[string]MinHeap[BuildJob]
// When a target is removed from the server or edited, its previous build
// configs will be invalid. This map allows for those to be simply skipped
// by ignoring any build configs created before this timestamp.
invalidated map[int]time.Time
}
// new_job_queue initializes a new job queue
pub fn new_job_queue(default_schedule &cron.Expression, default_base_image string, default_build_timeout int) BuildJobQueue {
return BuildJobQueue{
default_schedule: unsafe { default_schedule }
default_base_image: default_base_image
default_build_timeout: default_build_timeout
invalidated: map[int]time.Time{}
}
}
// insert_all executes insert for each architecture of the given Target.
pub fn (mut q BuildJobQueue) insert_all(target Target) ! {
for arch in target.arch {
q.insert(target: target, arch: arch.value)!
}
}
[params]
pub struct InsertConfig {
target Target [required]
arch string [required]
single bool
force bool
now bool
}
// insert a new target's job into the queue for the given architecture. This
// job will then be endlessly rescheduled after being pop'ed, unless removed
// explicitely.
pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! {
lock q.mutex {
if input.arch !in q.queues {
q.queues[input.arch] = MinHeap[BuildJob]{}
}
mut job := BuildJob{
created: time.now()
single: input.single
config: input.target.as_build_config(q.default_base_image, input.force, q.default_build_timeout)
}
if !input.now {
ce := if input.target.schedule != '' {
cron.parse_expression(input.target.schedule) or {
return error("Error while parsing cron expression '${input.target.schedule}' (id ${input.target.id}): ${err.msg()}")
}
} else {
q.default_schedule
}
job.timestamp = ce.next_from_now()
job.ce = ce
} else {
job.timestamp = time.now()
}
q.queues[input.arch].insert(job)
}
}
// reschedule the given job by calculating the next timestamp and re-adding it
// to its respective queue. This function is called by the pop functions
// *after* having pop'ed the job.
fn (mut q BuildJobQueue) reschedule(job BuildJob, arch string) {
new_timestamp := job.ce.next_from_now()
new_job := BuildJob{
...job
created: time.now()
timestamp: new_timestamp
}
q.queues[arch].insert(new_job)
}
// pop_invalid pops all invalid jobs.
fn (mut q BuildJobQueue) pop_invalid(arch string) {
for {
job := q.queues[arch].peek() or { return }
if job.config.target_id in q.invalidated
&& job.created < q.invalidated[job.config.target_id] {
// This pop *should* never fail according to the source code
q.queues[arch].pop() or {}
} else {
break
}
}
}
// peek shows the first job for the given architecture that's ready to be
// executed, if present.
pub fn (mut q BuildJobQueue) peek(arch string) ?BuildJob {
// Even peek requires a write lock, because pop_invalid can modify the data
// structure
lock q.mutex {
if arch !in q.queues {
return none
}
q.pop_invalid(arch)
job := q.queues[arch].peek() or { return none }
if job.timestamp < time.now() {
return job
}
}
return none
}
// pop removes the first job for the given architecture that's ready to be
// executed from the queue and returns it, if present.
pub fn (mut q BuildJobQueue) pop(arch string) ?BuildJob {
lock q.mutex {
if arch !in q.queues {
return none
}
q.pop_invalid(arch)
mut job := q.queues[arch].peek() or { return none }
if job.timestamp < time.now() {
job = q.queues[arch].pop() or { return none }
if !job.single {
q.reschedule(job, arch)
}
return job
}
}
return none
}
// pop_n tries to pop at most n available jobs for the given architecture.
pub fn (mut q BuildJobQueue) pop_n(arch string, n int) []BuildJob {
lock q.mutex {
if arch !in q.queues {
return []
}
mut out := []BuildJob{}
for out.len < n {
q.pop_invalid(arch)
mut job := q.queues[arch].peek() or { break }
if job.timestamp < time.now() {
job = q.queues[arch].pop() or { break }
if !job.single {
q.reschedule(job, arch)
}
out << job
} else {
break
}
}
return out
}
return []
}
// invalidate a target's old build jobs.
pub fn (mut q BuildJobQueue) invalidate(target_id int) {
q.invalidated[target_id] = time.now()
}

View File

@ -4,8 +4,8 @@ echo -e '+ pacman -Syu --needed --noconfirm'
pacman -Syu --needed --noconfirm pacman -Syu --needed --noconfirm
echo -e '+ su builder' echo -e '+ su builder'
su builder su builder
echo -e '+ git clone --single-branch --depth 1 --branch main https://examplerepo.com repo' echo -e '+ git clone --single-branch --depth 1 '\''https://examplerepo.com'\'' repo'
git clone --single-branch --depth 1 --branch main https://examplerepo.com repo git clone --single-branch --depth 1 'https://examplerepo.com' repo
echo -e '+ cd repo' echo -e '+ cd repo'
cd repo cd repo
echo -e '+ makepkg --nobuild --syncdeps --needed --noconfirm' echo -e '+ makepkg --nobuild --syncdeps --needed --noconfirm'
@ -16,5 +16,5 @@ echo -e '+ curl -s --head --fail https://example.com/vieter/x86_64/$pkgname-$pkg
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' echo -e '+ [ "$(id -u)" == 0 ] && exit 0'
[ "$(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' echo -e '+ MAKEFLAGS="-j$(nproc)" makepkg -s --noconfirm --needed --noextract && 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 MAKEFLAGS="-j$(nproc)" makepkg -s --noconfirm --needed --noextract && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done

View File

@ -0,0 +1,20 @@
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 --noextract && 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 --noextract && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done

View File

@ -0,0 +1,20 @@
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 '\''https://examplerepo.com'\'' repo'
git clone --single-branch --depth 1 'https://examplerepo.com' repo
echo -e '+ cd '\''repo/example/path'\'''
cd 'repo/example/path'
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 --noextract && 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 --noextract && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done

View File

@ -0,0 +1,20 @@
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 '\''https://examplerepo.com'\'' repo'
git clone --single-branch --depth 1 'https://examplerepo.com' repo
echo -e '+ cd '\''repo/example/path with spaces'\'''
cd 'repo/example/path with spaces'
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 --noextract && 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 --noextract && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done

View File

@ -0,0 +1,22 @@
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 '+ mkdir repo'
mkdir repo
echo -e '+ curl -o repo/PKGBUILD -L '\''https://examplerepo.com'\'''
curl -o repo/PKGBUILD -L 'https://examplerepo.com'
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 --noextract && 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 --noextract && for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $API_KEY" https://example.com/vieter/publish; done

View File

@ -1,6 +1,6 @@
module build module build
import models { GitRepo } import models { BuildConfig }
// escape_shell_string escapes any characters that could be interpreted // escape_shell_string escapes any characters that could be interpreted
// incorrectly by a shell. The resulting value should be safe to use inside an // incorrectly by a shell. The resulting value should be safe to use inside an
@ -22,34 +22,72 @@ pub fn echo_commands(cmds []string) []string {
return out return out
} }
// create_build_script generates a shell script that builds a given GitRepo. // create_build_script generates a shell script that builds a given Target.
fn create_build_script(address string, repo &GitRepo, build_arch string) string { fn create_build_script(address string, config BuildConfig, build_arch string) string {
repo_url := '$address/$repo.repo' repo_url := '${address}/${config.repo}'
commands := echo_commands([ mut commands := [
// This will later be replaced by a proper setting for changing the // This will later be replaced by a proper setting for changing the
// mirrorlist // mirrorlist
"echo -e '[$repo.repo]\\nServer = $address/\$repo/\$arch\\nSigLevel = Optional' >> /etc/pacman.conf" "echo -e '[${config.repo}]\\nServer = ${address}/\$repo/\$arch\\nSigLevel = Optional' >> /etc/pacman.conf"
// We need to update the package list of the repo we just added above. // We need to update the package list of the repo we just added above.
// This should however not pull in a lot of packages as long as the // This should however not pull in a lot of packages as long as the
// builder image is rebuilt frequently. // builder image is rebuilt frequently.
'pacman -Syu --needed --noconfirm', 'pacman -Syu --needed --noconfirm',
// makepkg can't run as root // makepkg can't run as root
'su builder', 'su builder',
'git clone --single-branch --depth 1 --branch $repo.branch $repo.url repo', ]
'cd repo',
commands << match config.kind {
'git' {
if config.branch == '' {
[
"git clone --single-branch --depth 1 '${config.url}' repo",
]
} else {
[
"git clone --single-branch --depth 1 --branch ${config.branch} '${config.url}' repo",
]
}
}
'url' {
[
'mkdir repo',
"curl -o repo/PKGBUILD -L '${config.url}'",
]
}
else {
panic("Invalid kind. This shouldn't be possible.")
}
}
commands << if config.path != '' {
"cd 'repo/${config.path}'"
} else {
'cd repo'
}
commands << [
'makepkg --nobuild --syncdeps --needed --noconfirm', 'makepkg --nobuild --syncdeps --needed --noconfirm',
'source PKGBUILD', 'source PKGBUILD',
]
if !config.force {
// The build container checks whether the package is already present on // The build container checks whether the package is already present on
// the server. // the server.
'curl -s --head --fail $repo_url/$build_arch/\$pkgname-\$pkgver-\$pkgrel && exit 0', commands << [
// If the above curl command succeeds, we don't need to rebuild the 'curl -s --head --fail ${repo_url}/${build_arch}/\$pkgname-\$pkgver-\$pkgrel && exit 0',
// package. However, because we're in a su shell, the exit command will // If the above curl command succeeds, we don't need to rebuild the
// drop us back into the root shell. Therefore, we must check whether // package. However, because we're in a su shell, the exit command will
// we're in root so we don't proceed. // drop us back into the root shell. Therefore, we must check whether
'[ "\$(id -u)" == 0 ] && exit 0', // we're in root so we don't proceed.
'MAKEFLAGS="-j\$(nproc)" makepkg -s --noconfirm --needed && for pkg in \$(ls -1 *.pkg*); do curl -XPOST -T "\$pkg" -H "X-API-KEY: \$API_KEY" $repo_url/publish; done', '[ "\$(id -u)" == 0 ] && exit 0',
]) ]
}
return commands.join('\n') commands << [
'MAKEFLAGS="-j\$(nproc)" makepkg -s --noconfirm --needed --noextract && for pkg in \$(ls -1 *.pkg*); do curl -XPOST -T "\$pkg" -H "X-API-KEY: \$API_KEY" ${repo_url}/publish; done',
]
return echo_commands(commands).join('\n')
} }

View File

@ -1,16 +1,75 @@
module build module build
import models { GitRepo } import models { BuildConfig }
fn test_create_build_script() { fn test_create_build_script_git() {
repo := GitRepo{ config := BuildConfig{
id: 1 target_id: 1
kind: 'git'
url: 'https://examplerepo.com' url: 'https://examplerepo.com'
branch: 'main'
repo: 'vieter' repo: 'vieter'
base_image: 'not-used:latest'
} }
build_script := create_build_script('https://example.com', repo, 'x86_64')
expected := $embed_file('build_script.sh') build_script := create_build_script('https://example.com', config, 'x86_64')
expected := $embed_file('scripts/git.sh')
assert build_script == expected.to_string().trim_space()
}
fn test_create_build_script_git_path() {
mut config := BuildConfig{
target_id: 1
kind: 'git'
url: 'https://examplerepo.com'
repo: 'vieter'
path: 'example/path'
base_image: 'not-used:latest'
}
mut build_script := create_build_script('https://example.com', config, 'x86_64')
mut expected := $embed_file('scripts/git_path.sh')
assert build_script == expected.to_string().trim_space()
config = BuildConfig{
...config
path: 'example/path with spaces'
}
build_script = create_build_script('https://example.com', config, 'x86_64')
expected = $embed_file('scripts/git_path_spaces.sh')
assert build_script == expected.to_string().trim_space()
}
fn test_create_build_script_git_branch() {
config := BuildConfig{
target_id: 1
kind: 'git'
url: 'https://examplerepo.com'
branch: 'main'
repo: 'vieter'
base_image: 'not-used:latest'
}
build_script := create_build_script('https://example.com', config, 'x86_64')
expected := $embed_file('scripts/git_branch.sh')
assert build_script == expected.to_string().trim_space()
}
fn test_create_build_script_url() {
config := BuildConfig{
target_id: 1
kind: 'url'
url: 'https://examplerepo.com'
repo: 'vieter'
base_image: 'not-used:latest'
}
build_script := create_build_script('https://example.com', config, 'x86_64')
expected := $embed_file('scripts/url.sh')
assert build_script == expected.to_string().trim_space() assert build_script == expected.to_string().trim_space()
} }

View File

@ -2,7 +2,7 @@ module client
import net.http { Method } import net.http { Method }
import net.urllib import net.urllib
import response { Response } import web.response { Response, new_data_response }
import json import json
pub struct Client { pub struct Client {
@ -21,8 +21,8 @@ pub fn new(address string, api_key string) Client {
// send_request_raw sends an HTTP request, returning the http.Response object. // send_request_raw sends an HTTP request, returning the http.Response object.
// It encodes the params so that they're safe to pass as HTTP query parameters. // It encodes the params so that they're safe to pass as HTTP query parameters.
fn (c &Client) send_request_raw(method Method, url string, params map[string]string, body string) ?http.Response { fn (c &Client) send_request_raw(method Method, url string, params map[string]string, body string) !http.Response {
mut full_url := '$c.address$url' mut full_url := '${c.address}${url}'
if params.len > 0 { if params.len > 0 {
mut params_escaped := map[string]string{} mut params_escaped := map[string]string{}
@ -30,41 +30,61 @@ fn (c &Client) send_request_raw(method Method, url string, params map[string]str
// Escape each query param // Escape each query param
for k, v in params { for k, v in params {
// An empty parameter should be the same as not providing it at all // An empty parameter should be the same as not providing it at all
if v != '' { params_escaped[k] = urllib.query_escape(v)
params_escaped[k] = urllib.query_escape(v)
}
} }
params_str := params_escaped.keys().map('$it=${params[it]}').join('&') params_str := params_escaped.keys().map('${it}=${params_escaped[it]}').join('&')
full_url = '$full_url?$params_str' full_url = '${full_url}?${params_str}'
} }
mut req := http.new_request(method, full_url, body)? // Looking at the source code, this function doesn't actually fail, so I'm
req.add_custom_header('X-Api-Key', c.api_key)? // not sure why it returns an optional
mut req := http.new_request(method, full_url, body) or { return error('') }
req.add_custom_header('X-Api-Key', c.api_key)!
res := req.do()? res := req.do()!
return res return res
} }
// send_request<T> just calls send_request_with_body<T> with an empty body. // send_request<T> just calls send_request_with_body<T> with an empty body.
fn (c &Client) send_request<T>(method Method, url string, params map[string]string) ?Response<T> { fn (c &Client) send_request[T](method Method, url string, params map[string]string) !Response[T] {
return c.send_request_with_body<T>(method, url, params, '') return c.send_request_with_body[T](method, url, params, '')
} }
// send_request_with_body<T> calls send_request_raw_response & parses its // send_request_with_body<T> calls send_request_raw_response & parses its
// output as a Response<T> object. // output as a Response<T> object.
fn (c &Client) send_request_with_body<T>(method Method, url string, params map[string]string, body string) ?Response<T> { fn (c &Client) send_request_with_body[T](method Method, url string, params map[string]string, body string) !Response[T] {
res_text := c.send_request_raw_response(method, url, params, body)? res := c.send_request_raw(method, url, params, body)!
data := json.decode(Response<T>, res_text)? status := res.status()
// Non-successful requests are expected to return either an empty body or
// Response<string>
if status.is_error() {
// A non-successful status call will have an empty body
if res.body == '' {
return error('Error ${res.status_code} (${status.str()}): (empty response)')
}
data := json.decode(Response[string], res.body)!
return error('Status ${res.status_code} (${status.str()}): ${data.message}')
}
// Just return an empty successful response
if res.body == '' {
return new_data_response(T{})
}
data := json.decode(Response[T], res.body)!
return data return data
} }
// send_request_raw_response returns the raw text response for an HTTP request. // send_request_raw_response returns the raw text response for an HTTP request.
fn (c &Client) send_request_raw_response(method Method, url string, params map[string]string, body string) ?string { fn (c &Client) send_request_raw_response(method Method, url string, params map[string]string, body string) !string {
res := c.send_request_raw(method, url, params, body)? res := c.send_request_raw(method, url, params, body)!
return res.body return res.body
} }

View File

@ -1,73 +0,0 @@
module client
import models { GitRepo, GitRepoFilter }
import net.http { Method }
import response { Response }
// get_git_repos returns a list of GitRepo's, given a filter object.
pub fn (c &Client) get_git_repos(filter GitRepoFilter) ?[]GitRepo {
params := models.params_from(filter)
data := c.send_request<[]GitRepo>(Method.get, '/api/repos', params)?
return data.data
}
// get_all_git_repos retrieves *all* GitRepo's from the API using the default
// limit.
pub fn (c &Client) get_all_git_repos() ?[]GitRepo {
mut repos := []GitRepo{}
mut offset := u64(0)
for {
sub_repos := c.get_git_repos(offset: offset)?
if sub_repos.len == 0 {
break
}
repos << sub_repos
offset += u64(sub_repos.len)
}
return repos
}
// get_git_repo returns the repo for a specific ID.
pub fn (c &Client) get_git_repo(id int) ?GitRepo {
data := c.send_request<GitRepo>(Method.get, '/api/repos/$id', {})?
return data.data
}
// add_git_repo adds a new repo to the server.
pub fn (c &Client) add_git_repo(url string, branch string, repo string, arch []string) ?Response<string> {
mut params := {
'url': url
'branch': branch
'repo': repo
}
if arch.len > 0 {
params['arch'] = arch.join(',')
}
data := c.send_request<string>(Method.post, '/api/repos', params)?
return data
}
// remove_git_repo removes the repo with the given ID from the server.
pub fn (c &Client) remove_git_repo(id int) ?Response<string> {
data := c.send_request<string>(Method.delete, '/api/repos/$id', {})?
return data
}
// patch_git_repo sends a PATCH request to the given repo with the params as
// payload.
pub fn (c &Client) patch_git_repo(id int, params map[string]string) ?Response<string> {
data := c.send_request<string>(Method.patch, '/api/repos/$id', params)?
return data
}

23
src/client/jobs.v 100644
View File

@ -0,0 +1,23 @@
module client
import models { BuildConfig }
// poll_jobs requests a list of new build jobs from the server.
pub fn (c &Client) poll_jobs(arch string, max int) ![]BuildConfig {
data := c.send_request[[]BuildConfig](.get, '/api/v1/jobs/poll', {
'arch': arch
'max': max.str()
})!
return data.data
}
// queue_job adds a new one-time build job for the given target to the job
// queue.
pub fn (c &Client) queue_job(target_id int, arch string, force bool) ! {
c.send_request[string](.post, '/api/v1/jobs/queue', {
'target': target_id.str()
'arch': arch
'force': force.str()
})!
}

View File

@ -1,54 +1,47 @@
module client module client
import models { BuildLog, BuildLogFilter } import models { BuildLog, BuildLogFilter }
import net.http { Method } import web.response { Response }
import 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) ?Response<[]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/logs', params)? data := c.send_request[[]BuildLog](.get, '/api/v1/logs', params)!
return data return data.data
}
// get_build_logs_for_repo returns all build logs for a given repo.
pub fn (c &Client) get_build_logs_for_repo(repo_id int) ?Response<[]BuildLog> {
params := {
'repo': repo_id.str()
}
data := c.send_request<[]BuildLog>(Method.get, '/api/logs', params)?
return 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) ?Response<BuildLog> { pub fn (c &Client) get_build_log(id int) !BuildLog {
data := c.send_request<BuildLog>(Method.get, '/api/logs/$id', {})? data := c.send_request[BuildLog](.get, '/api/v1/logs/${id}', {})!
return 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/logs/$id/content', {}, '')? data := c.send_request_raw_response(.get, '/api/v1/logs/${id}/content', {}, '')!
return data return data
} }
// add_build_log adds a new build log to the server. // add_build_log adds a new build log to the server.
pub fn (c &Client) add_build_log(repo_id int, start_time time.Time, end_time time.Time, arch string, exit_code int, content string) ?Response<string> { pub fn (c &Client) add_build_log(target_id int, start_time time.Time, end_time time.Time, arch string, exit_code int, content string) !Response[int] {
params := { params := {
'repo': repo_id.str() 'target': target_id.str()
'startTime': start_time.unix_time().str() 'startTime': start_time.unix_time().str()
'endTime': end_time.unix_time().str() 'endTime': end_time.unix_time().str()
'arch': arch 'arch': arch
'exitCode': exit_code.str() 'exitCode': exit_code.str()
} }
data := c.send_request_with_body<string>(Method.post, '/api/logs', params, content)? data := c.send_request_with_body[int](.post, '/api/v1/logs', params, content)!
return data return data
} }
// remove_build_log removes the build log with the given id from the server.
pub fn (c &Client) remove_build_log(id int) ! {
c.send_request[string](.delete, '/api/v1/logs/${id}', {})!
}

16
src/client/repos.v 100644
View File

@ -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}', {})!
}

View File

@ -0,0 +1,71 @@
module client
import models { Target, TargetFilter }
// get_targets returns a list of targets, given a filter object.
pub fn (c &Client) get_targets(filter TargetFilter) ![]Target {
params := models.params_from(filter)
data := c.send_request[[]Target](.get, '/api/v1/targets', params)!
return data.data
}
// get_all_targets retrieves *all* targs from the API using the default
// limit.
pub fn (c &Client) get_all_targets() ![]Target {
mut targets := []Target{}
mut offset := u64(0)
for {
sub_targets := c.get_targets(offset: offset)!
if sub_targets.len == 0 {
break
}
targets << sub_targets
offset += u64(sub_targets.len)
}
return targets
}
// get_target returns the target for a specific id.
pub fn (c &Client) get_target(id int) !Target {
data := c.send_request[Target](.get, '/api/v1/targets/${id}', {})!
return data.data
}
pub struct NewTarget {
kind string
url string
branch string
repo string
path string
arch []string
}
// add_target adds a new target to the server.
pub fn (c &Client) add_target(t NewTarget) !int {
params := models.params_from[NewTarget](t)
data := c.send_request[int](.post, '/api/v1/targets', params)!
return data.data
}
// remove_target removes the target with the given id from the server.
pub fn (c &Client) remove_target(id int) !string {
data := c.send_request[string](.delete, '/api/v1/targets/${id}', {})!
return data.data
}
// patch_target sends a PATCH request to the given target with the params as
// payload.
pub fn (c &Client) patch_target(id int, params map[string]string) !string {
data := c.send_request[string](.patch, '/api/v1/targets/${id}', params)!
return data.data
}

View File

@ -0,0 +1,62 @@
module aur
import cli
import console
import client
import aur
import conf as vconf
struct Config {
address string [required]
api_key string [required]
}
// cmd returns the cli module for interacting with the AUR API.
pub fn cmd() cli.Command {
return cli.Command{
name: 'aur'
description: 'Interact with the AUR.'
commands: [
cli.Command{
name: 'search'
description: 'Search for packages.'
required_args: 1
execute: fn (cmd cli.Command) ! {
c := aur.new()
pkgs := c.search(cmd.args[0])!
data := pkgs.map([it.name, it.description])
println(console.pretty_table(['name', 'description'], data)!)
}
},
cli.Command{
name: 'add'
usage: 'repo pkg-name [pkg-name...]'
description: 'Add the given AUR package(s) to Vieter. Non-existent packages will be silently ignored.'
required_args: 2
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
c := aur.new()
pkgs := c.info(cmd.args[1..])!
vc := client.new(conf_.address, conf_.api_key)
for pkg in pkgs {
vc.add_target(
kind: 'git'
url: 'https://aur.archlinux.org/${pkg.package_base}' + '.git'
repo: cmd.args[0]
) or {
println('Failed to add ${pkg.name}: ${err.msg()}')
continue
}
println('Added ${pkg.name}' + '.')
}
}
},
]
}
}

View File

@ -5,10 +5,15 @@ import strings
import cli import cli
import os import os
// tabbed_table returns a simple textual table, with tabs as separators.
pub fn tabbed_table(data [][]string) string {
return data.map(it.join('\t')).join('\n')
}
// pretty_table converts a list of string data into a pretty table. Many thanks // pretty_table converts a list of string data into a pretty table. Many thanks
// to @hungrybluedev in the Vlang Discord for providing this code! // to @hungrybluedev in the Vlang Discord for providing this code!
// https://ptb.discord.com/channels/592103645835821068/592106336838352923/970278787143045192 // https://ptb.discord.com/channels/592103645835821068/592106336838352923/970278787143045192
pub fn pretty_table(header []string, data [][]string) ?string { pub fn pretty_table(header []string, data [][]string) !string {
column_count := header.len column_count := header.len
mut column_widths := []int{len: column_count, init: header[it].len} mut column_widths := []int{len: column_count, init: header[it].len}
@ -21,7 +26,7 @@ pub fn pretty_table(header []string, data [][]string) ?string {
} }
} }
single_line_length := arrays.sum(column_widths)? + (column_count + 1) * 3 - 4 single_line_length := arrays.sum(column_widths)! + (column_count + 1) * 3 - 4
horizontal_line := '+' + strings.repeat(`-`, single_line_length) + '+' horizontal_line := '+' + strings.repeat(`-`, single_line_length) + '+'
mut buffer := strings.new_builder(data.len * single_line_length) mut buffer := strings.new_builder(data.len * single_line_length)
@ -59,12 +64,12 @@ pub fn pretty_table(header []string, data [][]string) ?string {
// export_man_pages recursively generates all man pages for the given // export_man_pages recursively generates all man pages for the given
// cli.Command & writes them to the given directory. // cli.Command & writes them to the given directory.
pub fn export_man_pages(cmd cli.Command, path string) ? { pub fn export_man_pages(cmd cli.Command, path string) ! {
man := cmd.manpage() man := cmd.manpage()
os.write_file(os.join_path_single(path, cmd.full_name().replace(' ', '-') + '.1'), os.write_file(os.join_path_single(path, cmd.full_name().replace(' ', '-') + '.1'),
man)? man)!
for sub_cmd in cmd.commands { for sub_cmd in cmd.commands {
export_man_pages(sub_cmd, path)? export_man_pages(sub_cmd, path)!
} }
} }

View File

@ -1,34 +0,0 @@
module git
import client
import docker
import os
import build
// build builds every Git repo in the server's list.
fn build(conf Config, repo_id int) ? {
c := client.new(conf.address, conf.api_key)
repo := c.get_git_repo(repo_id)?
build_arch := os.uname().machine
println('Creating base image...')
image_id := build.create_build_image(conf.base_image)?
println('Running build...')
res := build.build_repo(conf.address, conf.api_key, image_id, repo)?
println('Removing build image...')
mut dd := docker.new_conn()?
defer {
dd.close() or {}
}
dd.remove_image(image_id)?
println('Uploading logs to Vieter...')
c.add_build_log(repo.id, res.start_time, res.end_time, build_arch, res.exit_code,
res.logs)?
}

View File

@ -1,229 +0,0 @@
module git
import cli
import env
import cron.expression { parse_expression }
import client
import console
import models { GitRepoFilter }
struct Config {
address string [required]
api_key string [required]
base_image string = 'archlinux:base-devel'
}
// cmd returns the cli submodule that handles the repos API interaction
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.'
flags: [
cli.Flag{
name: 'limit'
description: 'How many results to return.'
flag: cli.FlagType.int
},
cli.Flag{
name: 'offset'
description: 'Minimum index to return.'
flag: cli.FlagType.int
},
cli.Flag{
name: 'repo'
description: 'Only return Git repos that publish to this repo.'
flag: cli.FlagType.string
},
]
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
mut filter := GitRepoFilter{}
limit := cmd.flags.get_int('limit')?
if limit != 0 {
filter.limit = u64(limit)
}
offset := cmd.flags.get_int('offset')?
if offset != 0 {
filter.offset = u64(offset)
}
repo := cmd.flags.get_string('repo')?
if repo != '' {
filter.repo = repo
}
list(conf, filter)?
}
},
cli.Command{
name: 'add'
required_args: 3
usage: 'url branch repo'
description: 'Add a new repository.'
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
add(conf, cmd.args[0], cmd.args[1], cmd.args[2])?
}
},
cli.Command{
name: 'remove'
required_args: 1
usage: 'id'
description: 'Remove a repository that matches the given ID prefix.'
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
remove(conf, cmd.args[0])?
}
},
cli.Command{
name: 'info'
required_args: 1
usage: 'id'
description: 'Show detailed information for the repo matching the ID prefix.'
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
info(conf, cmd.args[0])?
}
},
cli.Command{
name: 'edit'
required_args: 1
usage: 'id'
description: 'Edit the repository that matches the given ID prefix.'
flags: [
cli.Flag{
name: 'url'
description: 'URL of the Git repository.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'branch'
description: 'Branch of the Git repository.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'repo'
description: 'Repo to publish builds to.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'arch'
description: 'Comma-separated list of architectures to build on.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'schedule'
description: 'Cron schedule for repository.'
flag: cli.FlagType.string
},
]
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
found := cmd.flags.get_all_found()
mut params := map[string]string{}
for f in found {
if f.name != 'config-file' {
params[f.name] = f.get_string()?
}
}
patch(conf, cmd.args[0], params)?
}
},
cli.Command{
name: 'build'
required_args: 1
usage: 'id'
description: 'Build the repo with the given id & publish it.'
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
build(conf, cmd.args[0].int())?
}
},
]
}
}
// get_repo_by_prefix tries to find the repo with the given prefix in its
// ID. If multiple or none are found, an error is raised.
// list prints out a list of all repositories.
fn list(conf Config, filter GitRepoFilter) ? {
c := client.new(conf.address, conf.api_key)
repos := c.get_git_repos(filter)?
data := repos.map([it.id.str(), it.url, it.branch, it.repo])
println(console.pretty_table(['id', 'url', 'branch', 'repo'], data)?)
}
// add adds a new repository to the server's list.
fn add(conf Config, url string, branch string, repo string) ? {
c := client.new(conf.address, conf.api_key)
res := c.add_git_repo(url, branch, repo, [])?
println(res.message)
}
// remove removes a repository from the server's list.
fn remove(conf Config, id string) ? {
// id, _ := get_repo_by_prefix(conf, id_prefix) ?
id_int := id.int()
if id_int != 0 {
c := client.new(conf.address, conf.api_key)
res := c.remove_git_repo(id_int)?
println(res.message)
}
}
// patch patches a given repository with the provided params.
fn patch(conf Config, id string, params map[string]string) ? {
// We check the cron expression first because it's useless to send an
// invalid one to the server.
if 'schedule' in params && params['schedule'] != '' {
parse_expression(params['schedule']) or {
return error('Invalid cron expression: $err.msg()')
}
}
id_int := id.int()
if id_int != 0 {
c := client.new(conf.address, conf.api_key)
res := c.patch_git_repo(id_int, params)?
println(res.message)
}
}
// info shows detailed information for a given repo.
fn info(conf Config, id string) ? {
id_int := id.int()
if id_int == 0 {
return
}
c := client.new(conf.address, conf.api_key)
repo := c.get_git_repo(id_int)?
println(repo)
}

View File

@ -1,7 +1,7 @@
module logs module logs
import cli import cli
import env import conf as vconf
import client import client
import console import console
import time import time
@ -12,7 +12,7 @@ struct Config {
api_key string [required] api_key string [required]
} }
// cmd returns the cli module that handles the build repos API. // cmd returns the cli module that handles the build logs API.
pub fn cmd() cli.Command { pub fn cmd() cli.Command {
return cli.Command{ return cli.Command{
name: 'logs' name: 'logs'
@ -24,31 +24,35 @@ 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
}, },
cli.Flag{ cli.Flag{
name: 'repo' name: 'target'
description: 'Only return logs for this repo id.' description: 'Only return logs for this target id.'
flag: cli.FlagType.int flag: cli.FlagType.int
}, },
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,31 +66,36 @@ 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')!
conf := env.load<Config>(config_file)? conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
mut filter := BuildLogFilter{} mut filter := BuildLogFilter{}
limit := cmd.flags.get_int('limit')? limit := cmd.flags.get_int('limit')!
if limit != 0 { if limit != 0 {
filter.limit = u64(limit) filter.limit = u64(limit)
} }
offset := cmd.flags.get_int('offset')? offset := cmd.flags.get_int('offset')!
if offset != 0 { if offset != 0 {
filter.offset = u64(offset) filter.offset = u64(offset)
} }
repo_id := cmd.flags.get_int('repo')? target_id := cmd.flags.get_int('target')!
if repo_id != 0 { if target_id != 0 {
filter.repo = repo_id filter.target = target_id
} }
tz_offset := time.offset() tz_offset := time.offset()
if cmd.flags.get_bool('today')? { if cmd.flags.get_bool('today')! {
today := time.now() today := time.now()
filter.after = time.new_time(time.Time{ filter.after = time.new_time(time.Time{
@ -98,12 +107,12 @@ pub fn cmd() cli.Command {
} }
// The -today flag overwrites any of the other date flags. // The -today flag overwrites any of the other date flags.
else { else {
day_str := cmd.flags.get_string('day')? day_str := cmd.flags.get_string('day')!
before_str := cmd.flags.get_string('before')? before_str := cmd.flags.get_string('before')!
after_str := cmd.flags.get_string('after')? after_str := cmd.flags.get_string('after')!
if day_str != '' { if day_str != '' {
day := time.parse_rfc3339(day_str)? day := time.parse_rfc3339(day_str)!
day_utc := time.new_time(time.Time{ day_utc := time.new_time(time.Time{
year: day.year year: day.year
month: day.month month: day.month
@ -118,22 +127,38 @@ pub fn cmd() cli.Command {
filter.before = day_utc.add_days(1) filter.before = day_utc.add_days(1)
} else { } else {
if before_str != '' { if before_str != '' {
filter.before = time.parse(before_str)?.add_seconds(-tz_offset) filter.before = time.parse(before_str)!.add_seconds(-tz_offset)
} }
if after_str != '' { if after_str != '' {
filter.after = time.parse(after_str)?.add_seconds(-tz_offset) filter.after = time.parse(after_str)!.add_seconds(-tz_offset)
} }
} }
} }
if cmd.flags.get_bool('failed')? { if cmd.flags.get_bool('failed')! {
filter.exit_codes = [ filter.exit_codes = [
'!0', '!0',
] ]
} else {
filter.exit_codes = cmd.flags.get_strings('code')!
} }
list(conf, filter)? raw := cmd.flags.get_bool('raw')!
list(conf_, filter, raw)!
}
},
cli.Command{
name: 'remove'
required_args: 1
usage: 'id'
description: 'Remove a build log that matches the given id.'
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
remove(conf_, cmd.args[0])!
} }
}, },
cli.Command{ cli.Command{
@ -141,12 +166,12 @@ pub fn cmd() cli.Command {
required_args: 1 required_args: 1
usage: 'id' usage: 'id'
description: 'Show all info for a specific build log.' description: 'Show all info for a specific build log.'
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')!
conf := env.load<Config>(config_file)? conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
id := cmd.args[0].int() id := cmd.args[0].int()
info(conf, id)? info(conf_, id)!
} }
}, },
cli.Command{ cli.Command{
@ -154,12 +179,12 @@ pub fn cmd() cli.Command {
required_args: 1 required_args: 1
usage: 'id' usage: 'id'
description: 'Output the content of a build log to stdout.' description: 'Output the content of a build log to stdout.'
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')!
conf := env.load<Config>(config_file)? conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
id := cmd.args[0].int() id := cmd.args[0].int()
content(conf, id)? content(conf_, id)!
} }
}, },
] ]
@ -167,42 +192,44 @@ pub fn cmd() cli.Command {
} }
// print_log_list prints a list of logs. // print_log_list prints a list of logs.
fn print_log_list(logs []BuildLog) ? { fn print_log_list(logs []BuildLog, raw bool) ! {
data := logs.map([it.id.str(), it.repo_id.str(), it.start_time.local().str(), data := logs.map([it.id.str(), it.target_id.str(), it.start_time.local().str(),
it.exit_code.str()]) it.exit_code.str()])
println(console.pretty_table(['id', 'repo', 'start time', 'exit code'], data)?) if raw {
println(console.tabbed_table(data))
} else {
println(console.pretty_table(['id', 'target', 'start time', 'exit code'], data)!)
}
} }
// list prints a list of all build logs. // list prints a list of all build logs.
fn list(conf Config, filter BuildLogFilter) ? { fn list(conf_ Config, filter BuildLogFilter, raw bool) ! {
c := client.new(conf.address, conf.api_key) c := client.new(conf_.address, conf_.api_key)
logs := c.get_build_logs(filter)?.data logs := c.get_build_logs(filter)!
print_log_list(logs)? print_log_list(logs, raw)!
}
// list prints a list of all build logs for a given repo.
fn list_for_repo(conf Config, repo_id int) ? {
c := client.new(conf.address, conf.api_key)
logs := c.get_build_logs_for_repo(repo_id)?.data
print_log_list(logs)?
} }
// info print the detailed info for a given build log. // info print the detailed info for a given build log.
fn info(conf Config, id int) ? { fn info(conf_ Config, id int) ! {
c := client.new(conf.address, conf.api_key) c := client.new(conf_.address, conf_.api_key)
log := c.get_build_log(id)?.data log := c.get_build_log(id)!
print(log) print(log)
} }
// content outputs the contents of the log file for a given build log to // content outputs the contents of the log file for a given build log to
// stdout. // stdout.
fn content(conf Config, id int) ? { fn content(conf_ Config, id int) ! {
c := client.new(conf.address, conf.api_key) c := client.new(conf_.address, conf_.api_key)
content := c.get_build_log_content(id)? content := c.get_build_log_content(id)!
println(content) println(content)
} }
// remove removes a build log from the server's list.
fn remove(conf_ Config, id string) ! {
c := client.new(conf_.address, conf_.api_key)
c.remove_build_log(id.int())!
}

View File

@ -11,11 +11,11 @@ pub fn cmd() cli.Command {
description: 'Generate all man pages & save them in the given directory.' description: 'Generate all man pages & save them in the given directory.'
usage: 'dir' usage: 'dir'
required_args: 1 required_args: 1
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ! {
root := cmd.root() root := cmd.root()
os.mkdir_all(cmd.args[0])? os.mkdir_all(cmd.args[0])!
console.export_man_pages(root, cmd.args[0])? console.export_man_pages(root, cmd.args[0])!
} }
} }
} }

View File

@ -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])!
}
}
},
]
}
}

View File

@ -1,7 +1,7 @@
module schedule module schedule
import cli import cli
import cron.expression { parse_expression } import cron
import time import time
// cmd returns the cli submodule for previewing a cron schedule. // cmd returns the cli submodule for previewing a cron schedule.
@ -18,11 +18,11 @@ pub fn cmd() cli.Command {
default_value: ['5'] default_value: ['5']
}, },
] ]
execute: fn (cmd cli.Command) ? { execute: fn (cmd cli.Command) ! {
ce := parse_expression(cmd.args.join(' '))? ce := cron.parse_expression(cmd.args.join(' '))!
count := cmd.flags.get_int('count')? count := cmd.flags.get_int('count')!
for t in ce.next_n(time.now(), count)? { for t in ce.next_n(time.now(), count) {
println(t) println(t)
} }
} }

View File

@ -0,0 +1,34 @@
module targets
import client
import docker
import os
import build
// build locally builds the target with the given id.
fn build_target(conf Config, target_id int, force bool, timeout int) ! {
c := client.new(conf.address, conf.api_key)
target := c.get_target(target_id)!
build_arch := os.uname().machine
println('Creating base image...')
image_id := build.create_build_image(conf.base_image)!
println('Running build...')
res := build.build_target(conf.address, conf.api_key, image_id, target, force, timeout)!
println('Removing build image...')
mut dd := docker.new_conn()!
defer {
dd.close() or {}
}
dd.image_remove(image_id)!
println('Uploading logs to Vieter...')
c.add_build_log(target.id, res.start_time, res.end_time, build_arch, res.exit_code,
res.logs)!
}

View File

@ -0,0 +1,319 @@
module targets
import cli
import conf as vconf
import cron
import client { NewTarget }
import console
import models { TargetFilter }
struct Config {
address string [required]
api_key string [required]
base_image string = 'archlinux:base-devel'
}
// cmd returns the cli submodule that handles the targets API interaction
pub fn cmd() cli.Command {
return cli.Command{
name: 'targets'
description: 'Interact with the targets API.'
commands: [
cli.Command{
name: 'list'
description: 'List the current targets.'
flags: [
cli.Flag{
name: 'limit'
abbrev: 'l'
description: 'How many results to return.'
flag: cli.FlagType.int
},
cli.Flag{
name: 'offset'
abbrev: 'o'
description: 'Minimum index to return.'
flag: cli.FlagType.int
},
cli.Flag{
name: 'repo'
description: 'Only return targets that publish to this repo.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'query'
abbrev: 'q'
description: 'Search string to filter targets by.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'arch'
description: 'Only list targets that build for this arch.'
flag: cli.FlagType.string
},
]
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
mut filter := TargetFilter{}
limit := cmd.flags.get_int('limit')!
if limit != 0 {
filter.limit = u64(limit)
}
offset := cmd.flags.get_int('offset')!
if offset != 0 {
filter.offset = u64(offset)
}
repo := cmd.flags.get_string('repo')!
if repo != '' {
filter.repo = repo
}
query := cmd.flags.get_string('query')!
if query != '' {
filter.query = query
}
arch := cmd.flags.get_string('arch')!
if arch != '' {
filter.arch = arch
}
raw := cmd.flags.get_bool('raw')!
list(conf_, filter, raw)!
}
},
cli.Command{
name: 'add'
required_args: 2
usage: 'url repo'
description: 'Add a new target with the given URL & target repo.'
flags: [
cli.Flag{
name: 'kind'
description: "Kind of target to add. Defaults to 'git' if not specified. One of 'git', 'url'."
flag: cli.FlagType.string
default_value: ['git']
},
cli.Flag{
name: 'branch'
description: "Which branch to clone; only applies to kind 'git'."
flag: cli.FlagType.string
},
cli.Flag{
name: 'path'
description: 'Subdirectory inside Git repository to use.'
flag: cli.FlagType.string
},
]
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
t := NewTarget{
kind: cmd.flags.get_string('kind')!
url: cmd.args[0]
repo: cmd.args[1]
branch: cmd.flags.get_string('branch') or { '' }
path: cmd.flags.get_string('path') or { '' }
}
raw := cmd.flags.get_bool('raw')!
add(conf_, t, raw)!
}
},
cli.Command{
name: 'remove'
required_args: 1
usage: 'id'
description: 'Remove a target that matches the given id.'
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
remove(conf_, cmd.args[0])!
}
},
cli.Command{
name: 'info'
required_args: 1
usage: 'id'
description: 'Show detailed information for the target matching the id.'
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
info(conf_, cmd.args[0])!
}
},
cli.Command{
name: 'edit'
required_args: 1
usage: 'id'
description: 'Edit the target that matches the given id.'
flags: [
cli.Flag{
name: 'url'
description: 'URL value. Meaning depends on kind of target.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'branch'
description: 'Branch of the Git repository.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'repo'
description: 'Repo to publish builds to.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'arch'
description: 'Comma-separated list of architectures to build on.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'schedule'
description: 'Cron schedule for repository.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'kind'
description: 'Kind of target.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'path'
description: 'Subdirectory inside Git repository to use.'
flag: cli.FlagType.string
},
]
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
found := cmd.flags.get_all_found()
mut params := map[string]string{}
for f in found {
if f.name != 'config-file' {
params[f.name] = f.get_string()!
}
}
patch(conf_, cmd.args[0], params)!
}
},
cli.Command{
name: 'build'
required_args: 1
usage: 'id'
description: 'Build the target with the given id & publish it.'
flags: [
cli.Flag{
name: 'force'
description: 'Build the target without checking whether it needs to be renewed.'
flag: cli.FlagType.bool
},
cli.Flag{
name: 'remote'
description: 'Schedule the build on the server instead of running it locally.'
flag: cli.FlagType.bool
},
cli.Flag{
name: 'arch'
description: 'Architecture to schedule build for. Required when using -remote.'
flag: cli.FlagType.string
},
cli.Flag{
name: 'timeout'
description: 'After how many minutes to cancel the build. Only applies to local builds.'
flag: cli.FlagType.int
default_value: ['3600']
},
]
execute: fn (cmd cli.Command) ! {
config_file := cmd.flags.get_string('config-file')!
conf_ := vconf.load[Config](prefix: 'VIETER_', default_path: config_file)!
remote := cmd.flags.get_bool('remote')!
force := cmd.flags.get_bool('force')!
timeout := cmd.flags.get_int('timeout')!
target_id := cmd.args[0].int()
if remote {
arch := cmd.flags.get_string('arch')!
if arch == '' {
return error('When scheduling the build remotely, you have to specify an architecture.')
}
c := client.new(conf_.address, conf_.api_key)
c.queue_job(target_id, arch, force)!
} else {
build_target(conf_, target_id, force, timeout)!
}
}
},
]
}
}
// list prints out a list of all repositories.
fn list(conf_ Config, filter TargetFilter, raw bool) ! {
c := client.new(conf_.address, conf_.api_key)
targets := c.get_targets(filter)!
data := targets.map([it.id.str(), it.kind, it.url, it.repo])
if raw {
println(console.tabbed_table(data))
} else {
println(console.pretty_table(['id', 'kind', 'url', 'repo'], data)!)
}
}
// add adds a new target to the server's list.
fn add(conf_ Config, t &NewTarget, raw bool) ! {
c := client.new(conf_.address, conf_.api_key)
target_id := c.add_target(t)!
if raw {
println(target_id)
} else {
println('Target added with id ${target_id}')
}
}
// remove removes a target from the server's list.
fn remove(conf_ Config, id string) ! {
c := client.new(conf_.address, conf_.api_key)
c.remove_target(id.int())!
}
// patch patches a given target with the provided params.
fn patch(conf_ Config, id string, params map[string]string) ! {
// We check the cron expression first because it's useless to send an
// invalid one to the server.
if 'schedule' in params && params['schedule'] != '' {
cron.parse_expression(params['schedule']) or {
return error('Invalid cron expression: ${err.msg()}')
}
}
c := client.new(conf_.address, conf_.api_key)
c.patch_target(id.int(), params)!
}
// info shows detailed information for a given target.
fn info(conf_ Config, id string) ! {
c := client.new(conf_.address, conf_.api_key)
target := c.get_target(id.int())!
println(target)
}

View File

@ -1,32 +0,0 @@
module cron
import cli
import env
struct Config {
pub:
log_level string = 'WARN'
api_key string
address string
data_dir string
base_image string = 'archlinux:base-devel'
max_concurrent_builds int = 1
api_update_frequency int = 15
image_rebuild_frequency int = 1440
// Replicates the behavior of the original cron system
global_schedule string = '0 3'
}
// cmd returns the cli module that handles the cron daemon.
pub fn cmd() cli.Command {
return cli.Command{
name: 'cron'
description: 'Start the cron service that periodically runs builds.'
execute: fn (cmd cli.Command) ? {
config_file := cmd.flags.get_string('config-file')?
conf := env.load<Config>(config_file)?
cron(conf)?
}
}
}

View File

@ -1,33 +0,0 @@
module cron
import log
import cron.daemon
import cron.expression
import os
const log_file_name = 'vieter.cron.log'
// cron starts a cron daemon & starts periodically scheduling builds.
pub fn cron(conf Config) ? {
// Configure logger
log_level := log.level_from_tag(conf.log_level) or {
return error('Invalid log level. The allowed values are FATAL, ERROR, WARN, INFO & DEBUG.')
}
mut logger := log.Log{
level: log_level
}
log_file := os.join_path_single(conf.data_dir, cron.log_file_name)
logger.set_full_logpath(log_file)
logger.log_to_console_too()
ce := expression.parse_expression(conf.global_schedule) or {
return error('Error while parsing global cron expression: $err.msg()')
}
mut d := daemon.init_daemon(logger, conf.address, conf.api_key, conf.base_image, ce,
conf.max_concurrent_builds, conf.api_update_frequency, conf.image_rebuild_frequency)?
d.run()
}

View File

@ -1,113 +0,0 @@
module daemon
import time
import sync.stdatomic
import build
import os
const (
build_empty = 0
build_running = 1
build_done = 2
)
// clean_finished_builds removes finished builds from the build slots & returns
// them.
fn (mut d Daemon) clean_finished_builds() []ScheduledBuild {
mut out := []ScheduledBuild{}
for i in 0 .. d.atomics.len {
if stdatomic.load_u64(&d.atomics[i]) == daemon.build_done {
stdatomic.store_u64(&d.atomics[i], daemon.build_empty)
out << d.builds[i]
}
}
return out
}
// update_builds starts as many builds as possible.
fn (mut d Daemon) start_new_builds() {
now := time.now()
for d.queue.len() > 0 {
elem := d.queue.peek() or {
d.lerror("queue.peek() unexpectedly returned an error. This shouldn't happen.")
break
}
if elem.timestamp < now {
sb := d.queue.pop() or {
d.lerror("queue.pop() unexpectedly returned an error. This shouldn't happen.")
break
}
// If this build couldn't be scheduled, no more will be possible.
if !d.start_build(sb) {
d.queue.insert(sb)
break
}
} else {
break
}
}
}
// start_build starts a build for the given ScheduledBuild object.
fn (mut d Daemon) start_build(sb ScheduledBuild) bool {
for i in 0 .. d.atomics.len {
if stdatomic.load_u64(&d.atomics[i]) == daemon.build_empty {
stdatomic.store_u64(&d.atomics[i], daemon.build_running)
d.builds[i] = sb
go d.run_build(i, sb)
return true
}
}
return false
}
// run_build actually starts the build process for a given repo.
fn (mut d Daemon) run_build(build_index int, sb ScheduledBuild) {
d.linfo('started build: $sb.repo.url $sb.repo.branch')
// 0 means success, 1 means failure
mut status := 0
res := build.build_repo(d.client.address, d.client.api_key, d.builder_images.last(),
&sb.repo) or {
d.ldebug('build_repo error: $err.msg()')
status = 1
build.BuildResult{}
}
if status == 0 {
d.linfo('finished build: $sb.repo.url $sb.repo.branch; uploading logs...')
build_arch := os.uname().machine
d.client.add_build_log(sb.repo.id, res.start_time, res.end_time, build_arch, res.exit_code,
res.logs) or { d.lerror('Failed to upload logs for $sb.repo.url $sb.repo.arch') }
} else {
d.linfo('failed build: $sb.repo.url $sb.repo.branch')
}
stdatomic.store_u64(&d.atomics[build_index], daemon.build_done)
}
// current_build_count returns how many builds are currently running.
fn (mut d Daemon) current_build_count() int {
mut res := 0
for i in 0 .. d.atomics.len {
if stdatomic.load_u64(&d.atomics[i]) == daemon.build_running {
res += 1
}
}
return res
}

View File

@ -1,273 +0,0 @@
module daemon
import time
import log
import datatypes { MinHeap }
import cron.expression { CronExpression, parse_expression }
import math
import build
import docker
import os
import client
import models { GitRepo }
const (
// How many seconds to wait before retrying to update API if failed
api_update_retry_timeout = 5
// How many seconds to wait before retrying to rebuild image if failed
rebuild_base_image_retry_timout = 30
)
struct ScheduledBuild {
pub:
repo GitRepo
timestamp time.Time
}
// Overloaded operator for comparing ScheduledBuild objects
fn (r1 ScheduledBuild) < (r2 ScheduledBuild) bool {
return r1.timestamp < r2.timestamp
}
pub struct Daemon {
mut:
client client.Client
base_image string
builder_images []string
global_schedule CronExpression
api_update_frequency int
image_rebuild_frequency int
// Repos currently loaded from API.
repos []GitRepo
// At what point to update the list of repositories.
api_update_timestamp time.Time
image_build_timestamp time.Time
queue MinHeap<ScheduledBuild>
// Which builds are currently running
builds []ScheduledBuild
// Atomic variables used to detect when a build has finished; length is the
// same as builds
atomics []u64
logger shared log.Log
}
// init_daemon initializes a new Daemon object. It renews the repositories &
// populates the build queue for the first time.
pub fn init_daemon(logger log.Log, address string, api_key string, base_image string, global_schedule CronExpression, max_concurrent_builds int, api_update_frequency int, image_rebuild_frequency int) ?Daemon {
mut d := Daemon{
client: client.new(address, api_key)
base_image: base_image
global_schedule: global_schedule
api_update_frequency: api_update_frequency
image_rebuild_frequency: image_rebuild_frequency
atomics: []u64{len: max_concurrent_builds}
builds: []ScheduledBuild{len: max_concurrent_builds}
logger: logger
}
// Initialize the repos & queue
d.renew_repos()
d.renew_queue()
if !d.rebuild_base_image() {
return error('The base image failed to build. The Vieter cron daemon cannot run without an initial builder image.')
}
return d
}
// run starts the actual daemon process. It runs builds when possible &
// periodically refreshes the list of repositories to ensure we stay in sync.
pub fn (mut d Daemon) run() {
for {
finished_builds := d.clean_finished_builds()
// Update the API's contents if needed & renew the queue
if time.now() >= d.api_update_timestamp {
d.renew_repos()
d.renew_queue()
}
// The finished builds should only be rescheduled if the API contents
// haven't been renewed.
else {
for sb in finished_builds {
d.schedule_build(sb.repo)
}
}
// TODO remove old builder images.
// This issue is less trivial than it sounds, because a build could
// still be running when the image has to be rebuilt. That would
// prevent the image from being removed. Therefore, we will need to
// keep track of a list or something & remove an image once we have
// made sure it isn't being used anymore.
if time.now() >= d.image_build_timestamp {
d.rebuild_base_image()
// In theory, executing this function here allows an old builder
// image to exist for at most image_rebuild_frequency minutes.
d.clean_old_base_images()
}
// Schedules new builds when possible
d.start_new_builds()
// If there are builds currently running, the daemon should refresh
// every second to clean up any finished builds & start new ones.
mut delay := time.Duration(1 * time.second)
// Sleep either until we have to refresh the repos or when the next
// build has to start, with a minimum of 1 second.
if d.current_build_count() == 0 {
now := time.now()
delay = d.api_update_timestamp - now
if d.queue.len() > 0 {
elem := d.queue.peek() or {
d.lerror("queue.peek() unexpectedly returned an error. This shouldn't happen.")
// This is just a fallback option. In theory, queue.peek()
// should *never* return an error or none, because we check
// its len beforehand.
time.sleep(1)
continue
}
time_until_next_job := elem.timestamp - now
delay = math.min(delay, time_until_next_job)
}
}
// We sleep for at least one second. This is to prevent the program
// from looping agressively when a cronjob can be scheduled, but
// there's no spots free for it to be started.
delay = math.max(delay, 1 * time.second)
d.ldebug('Sleeping for ${delay}...')
time.sleep(delay)
}
}
// schedule_build adds the next occurence of the given repo build to the queue.
fn (mut d Daemon) schedule_build(repo GitRepo) {
ce := if repo.schedule != '' {
parse_expression(repo.schedule) or {
// TODO This shouldn't return an error if the expression is empty.
d.lerror("Error while parsing cron expression '$repo.schedule' (id $repo.id): $err.msg()")
d.global_schedule
}
} else {
d.global_schedule
}
// A repo that can't be scheduled will just be skipped for now
timestamp := ce.next_from_now() or {
d.lerror("Couldn't calculate next timestamp from '$repo.schedule'; skipping")
return
}
d.queue.insert(ScheduledBuild{
repo: repo
timestamp: timestamp
})
}
// renew_repos requests the newest list of Git repos from the server & replaces
// the old one.
fn (mut d Daemon) renew_repos() {
d.linfo('Renewing repos...')
mut new_repos := d.client.get_all_git_repos() or {
d.lerror('Failed to renew repos. Retrying in ${daemon.api_update_retry_timeout}s...')
d.api_update_timestamp = time.now().add_seconds(daemon.api_update_retry_timeout)
return
}
// Filter out any repos that shouldn't run on this architecture
cur_arch := os.uname().machine
new_repos = new_repos.filter(it.arch.any(it.value == cur_arch))
d.repos = new_repos
d.api_update_timestamp = time.now().add_seconds(60 * d.api_update_frequency)
}
// renew_queue replaces the old queue with a new one that reflects the newest
// values in repos_map.
fn (mut d Daemon) renew_queue() {
d.linfo('Renewing queue...')
mut new_queue := MinHeap<ScheduledBuild>{}
// Move any jobs that should have already started from the old queue onto
// the new one
now := time.now()
// For some reason, using
// ```v
// for d.queue.len() > 0 && d.queue.peek() ?.timestamp < now {
//```
// here causes the function to prematurely just exit, without any errors or anything, very weird
// https://github.com/vlang/v/issues/14042
for d.queue.len() > 0 {
elem := d.queue.pop() or {
d.lerror("queue.pop() returned an error. This shouldn't happen.")
continue
}
if elem.timestamp < now {
new_queue.insert(elem)
} else {
break
}
}
d.queue = new_queue
// For each repository in repos_map, parse their cron expression (or use
// the default one if not present) & add them to the queue
for repo in d.repos {
d.schedule_build(repo)
}
}
// rebuild_base_image recreates the builder image.
fn (mut d Daemon) rebuild_base_image() bool {
d.linfo('Rebuilding builder image....')
d.builder_images << build.create_build_image(d.base_image) or {
d.lerror('Failed to rebuild base image. Retrying in ${daemon.rebuild_base_image_retry_timout}s...')
d.image_build_timestamp = time.now().add_seconds(daemon.rebuild_base_image_retry_timout)
return false
}
d.image_build_timestamp = time.now().add_seconds(60 * d.image_rebuild_frequency)
return true
}
// clean_old_base_images tries to remove any old but still present builder
// images.
fn (mut d Daemon) clean_old_base_images() {
mut i := 0
mut dd := docker.new_conn() or {
d.lerror('Failed to connect to Docker socket.')
return
}
defer {
dd.close() or {}
}
for i < d.builder_images.len - 1 {
// For each builder image, we try to remove it by calling the Docker
// API. If the function returns an error or false, that means the image
// wasn't deleted. Therefore, we move the index over. If the function
// returns true, the array's length has decreased by one so we don't
// move the index.
dd.remove_image(d.builder_images[i]) or { i += 1 }
}
}

View File

@ -1,35 +0,0 @@
module daemon
import log
// log reate a log message with the given level
pub fn (mut d Daemon) log(msg &string, level log.Level) {
lock d.logger {
d.logger.send_output(msg, level)
}
}
// lfatal create a log message with the fatal level
pub fn (mut d Daemon) lfatal(msg &string) {
d.log(msg, log.Level.fatal)
}
// lerror create a log message with the error level
pub fn (mut d Daemon) lerror(msg &string) {
d.log(msg, log.Level.error)
}
// lwarn create a log message with the warn level
pub fn (mut d Daemon) lwarn(msg &string) {
d.log(msg, log.Level.warn)
}
// linfo create a log message with the info level
pub fn (mut d Daemon) linfo(msg &string) {
d.log(msg, log.Level.info)
}
// ldebug create a log message with the debug level
pub fn (mut d Daemon) ldebug(msg &string) {
d.log(msg, log.Level.debug)
}

View File

@ -0,0 +1,101 @@
module cron
#flag -I @VMODROOT/libvieter/include
#flag -L @VMODROOT/libvieter/build
#flag -lvieter
#include "vieter_cron.h"
[typedef]
pub struct C.vieter_cron_expression {
minutes &u8
hours &u8
days &u8
months &u8
minute_count u8
hour_count u8
day_count u8
month_count u8
}
pub type Expression = C.vieter_cron_expression
// == returns whether the two expressions are equal by value.
fn (ce1 Expression) == (ce2 Expression) bool {
if ce1.month_count != ce2.month_count || ce1.day_count != ce2.day_count
|| ce1.hour_count != ce2.hour_count || ce1.minute_count != ce2.minute_count {
return false
}
for i in 0 .. ce1.month_count {
unsafe {
if ce1.months[i] != ce2.months[i] {
return false
}
}
}
for i in 0 .. ce1.day_count {
unsafe {
if ce1.days[i] != ce2.days[i] {
return false
}
}
}
for i in 0 .. ce1.hour_count {
unsafe {
if ce1.hours[i] != ce2.hours[i] {
return false
}
}
}
for i in 0 .. ce1.minute_count {
unsafe {
if ce1.minutes[i] != ce2.minutes[i] {
return false
}
}
}
return true
}
[typedef]
struct C.vieter_cron_simple_time {
year int
month int
day int
hour int
minute int
}
type SimpleTime = C.vieter_cron_simple_time
enum ParseError as u8 {
ok = 0
invalid_expression = 1
invalid_number = 2
out_of_range = 3
too_many_parts = 4
not_enough_parts = 5
}
// str returns the string representation of a ParseError.
fn (e ParseError) str() string {
return match e {
.ok { '' }
.invalid_expression { 'Invalid expression' }
.invalid_number { 'Invalid number' }
.out_of_range { 'Out of range' }
.too_many_parts { 'Too many parts' }
.not_enough_parts { 'Not enough parts' }
}
}
fn C.vieter_cron_expr_init() &C.vieter_cron_expression
fn C.vieter_cron_expr_free(ce &C.vieter_cron_expression)
fn C.vieter_cron_expr_next(out &C.vieter_cron_simple_time, ce &C.vieter_cron_expression, ref &C.vieter_cron_simple_time)
fn C.vieter_cron_expr_next_from_now(out &C.vieter_cron_simple_time, ce &C.vieter_cron_expression)
fn C.vieter_cron_expr_parse(out &C.vieter_cron_expression, s &char) ParseError

View File

@ -0,0 +1,73 @@
module cron
import time
// free the memory associated with the Expression.
[unsafe]
pub fn (ce &Expression) free() {
C.vieter_cron_expr_free(ce)
}
// parse_expression parses a string into an Expression.
pub fn parse_expression(exp string) !&Expression {
out := C.vieter_cron_expr_init()
res := C.vieter_cron_expr_parse(out, exp.str)
if res != .ok {
return error(res.str())
}
return out
}
// next calculates the next occurence of the cron schedule, given a reference
// point.
pub fn (ce &Expression) next(ref time.Time) time.Time {
st := SimpleTime{
year: ref.year
month: ref.month
day: ref.day
hour: ref.hour
minute: ref.minute
}
out := SimpleTime{}
C.vieter_cron_expr_next(&out, ce, &st)
return time.new_time(time.Time{
year: out.year
month: out.month
day: out.day
hour: out.hour
minute: out.minute
})
}
// next_from_now calculates the next occurence of the cron schedule with the
// current time as reference.
pub fn (ce &Expression) next_from_now() time.Time {
out := SimpleTime{}
C.vieter_cron_expr_next_from_now(&out, ce)
return time.new_time(time.Time{
year: out.year
month: out.month
day: out.day
hour: out.hour
minute: out.minute
})
}
// next_n returns the n next occurences of the expression, given a starting
// time.
pub fn (ce &Expression) next_n(ref time.Time, n int) []time.Time {
mut times := []time.Time{cap: n}
times << ce.next(ref)
for i in 1 .. n {
times << ce.next(times[i - 1])
}
return times
}

View File

@ -1,275 +0,0 @@
module expression
import time
pub struct CronExpression {
minutes []int
hours []int
days []int
months []int
}
// next calculates the earliest time this cron expression is valid. It will
// always pick a moment in the future, even if ref matches completely up to the
// minute. This function conciously does not take gap years into account.
pub fn (ce &CronExpression) next(ref time.Time) ?time.Time {
// If the given ref matches the next cron occurence up to the minute, it
// will return that value. Because we always want to return a value in the
// future, we artifically shift the ref 60 seconds to make sure we always
// match in the future. A shift of 60 seconds is enough because the cron
// expression does not allow for accuracy smaller than one minute.
sref := ref
// For all of these values, the rule is the following: if their value is
// the length of their respective array in the CronExpression object, that
// means we've looped back around. This means that the "bigger" value has
// to be incremented by one. For example, if the minutes have looped
// around, that means that the hour has to be incremented as well.
mut minute_index := 0
mut hour_index := 0
mut day_index := 0
mut month_index := 0
// This chain is the same logic multiple times, namely that if a "bigger"
// value loops around, then the smaller value will always reset as well.
// For example, if we're going to a new day, the hour & minute will always
// be their smallest value again.
for month_index < ce.months.len && sref.month > ce.months[month_index] {
month_index++
}
if month_index < ce.months.len && sref.month == ce.months[month_index] {
for day_index < ce.days.len && sref.day > ce.days[day_index] {
day_index++
}
if day_index < ce.days.len && ce.days[day_index] == sref.day {
for hour_index < ce.hours.len && sref.hour > ce.hours[hour_index] {
hour_index++
}
if hour_index < ce.hours.len && ce.hours[hour_index] == sref.hour {
// Minute is the only value where we explicitely make sure we
// can't match sref's value exactly. This is to ensure we only
// return values in the future.
for minute_index < ce.minutes.len && sref.minute >= ce.minutes[minute_index] {
minute_index++
}
}
}
}
// Here, we increment the "bigger" values by one if the smaller ones loop
// around. The order is important, as it allows a sort-of waterfall effect
// to occur which updates all values if required.
if minute_index == ce.minutes.len && hour_index < ce.hours.len {
hour_index += 1
}
if hour_index == ce.hours.len && day_index < ce.days.len {
day_index += 1
}
if day_index == ce.days.len && month_index < ce.months.len {
month_index += 1
}
mut minute := ce.minutes[minute_index % ce.minutes.len]
mut hour := ce.hours[hour_index % ce.hours.len]
mut day := ce.days[day_index % ce.days.len]
// Sometimes, we end up with a day that does not exist within the selected
// month, e.g. day 30 in February. When this occurs, we reset day back to
// the smallest value & loop over to the next month that does have this
// day.
if day > time.month_days[ce.months[month_index % ce.months.len] - 1] {
day = ce.days[0]
month_index += 1
for day > time.month_days[ce.months[month_index & ce.months.len] - 1] {
month_index += 1
// If for whatever reason the day value ends up being something
// that can't be scheduled in any month, we have to make sure we
// don't create an infinite loop.
if month_index == 2 * ce.months.len {
return error('No schedulable moment.')
}
}
}
month := ce.months[month_index % ce.months.len]
mut year := sref.year
// If the month loops over, we need to increment the year.
if month_index >= ce.months.len {
year++
}
return time.new_time(time.Time{
year: year
month: month
day: day
minute: minute
hour: hour
})
}
// next_from_now returns the result of ce.next(ref) where ref is the result of
// time.now().
pub fn (ce &CronExpression) next_from_now() ?time.Time {
return ce.next(time.now())
}
// next_n returns the n next occurences of the expression, given a starting
// time.
pub fn (ce &CronExpression) next_n(ref time.Time, n int) ?[]time.Time {
mut times := []time.Time{cap: n}
times << ce.next(ref)?
for i in 1 .. n {
times << ce.next(times[i - 1])?
}
return times
}
// parse_range parses a given string into a range of sorted integers, if
// possible.
fn parse_range(s string, min int, max int, mut bitv []bool) ? {
mut start := min
mut end := max
mut interval := 1
exps := s.split('/')
if exps.len > 2 {
return error('Invalid expression.')
}
if exps[0] != '*' {
dash_parts := exps[0].split('-')
if dash_parts.len > 2 {
return error('Invalid expression.')
}
start = dash_parts[0].int()
// The builtin parsing functions return zero if the string can't be
// parsed into a number, so we have to explicitely check whether they
// actually entered zero or if it's an invalid number.
if start == 0 && dash_parts[0] != '0' {
return error('Invalid number.')
}
// Check whether the start value is out of range
if start < min || start > max {
return error('Out of range.')
}
if dash_parts.len == 2 {
end = dash_parts[1].int()
if end == 0 && dash_parts[1] != '0' {
return error('Invalid number.')
}
if end < start || end > max {
return error('Out of range.')
}
}
}
if exps.len > 1 {
interval = exps[1].int()
// interval being zero is always invalid, but we want to check why
// it's invalid for better error messages.
if interval == 0 {
if exps[1] != '0' {
return error('Invalid number.')
} else {
return error('Step size zero not allowed.')
}
}
if interval > max - min {
return error('Step size too large.')
}
}
// Here, s solely consists of a number, so that's the only value we
// should return.
else if exps[0] != '*' && !exps[0].contains('-') {
bitv[start - min] = true
return
}
for start <= end {
bitv[start - min] = true
start += interval
}
}
// bitv_to_ints converts a bit vector into an array containing the
// corresponding values.
fn bitv_to_ints(bitv []bool, min int) []int {
mut out := []int{}
for i in 0 .. bitv.len {
if bitv[i] {
out << min + i
}
}
return out
}
// parse_part parses a given part of a cron expression & returns the
// corresponding array of ints.
fn parse_part(s string, min int, max int) ?[]int {
mut bitv := []bool{len: max - min + 1, init: false}
for range in s.split(',') {
parse_range(range, min, max, mut bitv)?
}
return bitv_to_ints(bitv, min)
}
// parse_expression parses an entire cron expression string into a
// CronExpression object, if possible.
pub fn parse_expression(exp string) ?CronExpression {
// The filter allows for multiple spaces between parts
mut parts := exp.split(' ').filter(it != '')
if parts.len < 2 || parts.len > 4 {
return error('Expression must contain between 2 and 4 space-separated parts.')
}
// For ease of use, we allow the user to only specify as many parts as they
// need.
for parts.len < 4 {
parts << '*'
}
mut part_results := [][]int{}
mins := [0, 0, 1, 1]
maxs := [59, 23, 31, 12]
// This for loop allows us to more clearly propagate the error to the user.
for i, min in mins {
part_results << parse_part(parts[i], min, maxs[i]) or {
return error('An error occurred with part $i: $err.msg()')
}
}
return CronExpression{
minutes: part_results[0]
hours: part_results[1]
days: part_results[2]
months: part_results[3]
}
}

View File

@ -1,98 +0,0 @@
module expression
// parse_range_error returns the returned error message. If the result is '',
// that means the function didn't error.
fn parse_range_error(s string, min int, max int) string {
mut bitv := []bool{len: max - min + 1, init: false}
parse_range(s, min, max, mut bitv) or { return err.msg }
return ''
}
// =====parse_range=====
fn test_range_star_range() ? {
mut bitv := []bool{len: 6, init: false}
parse_range('*', 0, 5, mut bitv)?
assert bitv == [true, true, true, true, true, true]
}
fn test_range_number() ? {
mut bitv := []bool{len: 6, init: false}
parse_range('4', 0, 5, mut bitv)?
assert bitv_to_ints(bitv, 0) == [4]
}
fn test_range_number_too_large() ? {
assert parse_range_error('10', 0, 6) == 'Out of range.'
}
fn test_range_number_too_small() ? {
assert parse_range_error('0', 2, 6) == 'Out of range.'
}
fn test_range_number_invalid() ? {
assert parse_range_error('x', 0, 6) == 'Invalid number.'
}
fn test_range_step_star_1() ? {
mut bitv := []bool{len: 21, init: false}
parse_range('*/4', 0, 20, mut bitv)?
assert bitv_to_ints(bitv, 0) == [0, 4, 8, 12, 16, 20]
}
fn test_range_step_star_2() ? {
mut bitv := []bool{len: 8, init: false}
parse_range('*/3', 1, 8, mut bitv)?
assert bitv_to_ints(bitv, 1) == [1, 4, 7]
}
fn test_range_step_star_too_large() ? {
assert parse_range_error('*/21', 0, 20) == 'Step size too large.'
}
fn test_range_step_zero() ? {
assert parse_range_error('*/0', 0, 20) == 'Step size zero not allowed.'
}
fn test_range_step_number() ? {
mut bitv := []bool{len: 21, init: false}
parse_range('5/4', 2, 22, mut bitv)?
assert bitv_to_ints(bitv, 2) == [5, 9, 13, 17, 21]
}
fn test_range_step_number_too_large() ? {
assert parse_range_error('10/4', 0, 5) == 'Out of range.'
}
fn test_range_step_number_too_small() ? {
assert parse_range_error('2/4', 5, 10) == 'Out of range.'
}
fn test_range_dash() ? {
mut bitv := []bool{len: 10, init: false}
parse_range('4-8', 0, 9, mut bitv)?
assert bitv_to_ints(bitv, 0) == [4, 5, 6, 7, 8]
}
fn test_range_dash_step() ? {
mut bitv := []bool{len: 10, init: false}
parse_range('4-8/2', 0, 9, mut bitv)?
assert bitv_to_ints(bitv, 0) == [4, 6, 8]
}
// =====parse_part=====
fn test_part_single() ? {
assert parse_part('*', 0, 5)? == [0, 1, 2, 3, 4, 5]
}
fn test_part_multiple() ? {
assert parse_part('*/2,2/3', 1, 8)? == [1, 2, 3, 5, 7, 8]
}

View File

@ -1,34 +0,0 @@
module expression
import time { parse }
fn util_test_time(exp string, t1_str string, t2_str string) ? {
ce := parse_expression(exp)?
t1 := parse(t1_str)?
t2 := parse(t2_str)?
t3 := ce.next(t1)?
assert t2.year == t3.year
assert t2.month == t3.month
assert t2.day == t3.day
assert t2.hour == t3.hour
assert t2.minute == t3.minute
}
fn test_next_simple() ? {
// Very simple
util_test_time('0 3', '2002-01-01 00:00:00', '2002-01-01 03:00:00')?
// Overlap to next day
util_test_time('0 3', '2002-01-01 03:00:00', '2002-01-02 03:00:00')?
util_test_time('0 3', '2002-01-01 04:00:00', '2002-01-02 03:00:00')?
util_test_time('0 3/4', '2002-01-01 04:00:00', '2002-01-01 07:00:00')?
// Overlap to next month
util_test_time('0 3', '2002-11-31 04:00:00', '2002-12-01 03:00:00')?
// Overlap to next year
util_test_time('0 3', '2002-12-31 04:00:00', '2003-01-01 03:00:00')?
}

View File

@ -0,0 +1,35 @@
module cron
import time { parse }
fn util_test_time(exp string, t1_str string, t2_str string) ! {
ce := parse_expression(exp)!
t1 := parse(t1_str)!
t2 := parse(t2_str)!
t3 := ce.next(t1)
assert t2.year == t3.year
assert t2.month == t3.month
assert t2.day == t3.day
assert t2.hour == t3.hour
assert t2.minute == t3.minute
}
fn test_next_simple() ! {
// Very simple
// util_test_time('0 3', '2002-01-01 00:00:00', '2002-01-01 03:00:00')!
// Overlap to next day
mut exp := '0 3 '
util_test_time(exp, '2002-01-01 03:00:00', '2002-01-02 03:00:00')!
util_test_time(exp, '2002-01-01 04:00:00', '2002-01-02 03:00:00')!
util_test_time('0 3-7/4,7-19', '2002-01-01 04:00:00', '2002-01-01 07:00:00')!
//// Overlap to next month
util_test_time('0 3', '2002-11-31 04:00:00', '2002-12-01 03:00:00')!
//// Overlap to next year
util_test_time('0 3', '2002-12-31 04:00:00', '2003-01-01 03:00:00')!
}

View File

@ -0,0 +1,42 @@
module cron
fn test_not_allowed() {
illegal_expressions := [
'4 *-7',
'4 *-7/4',
'4 7/*',
'0 0 30 2',
'0 /5',
'0 ',
'0',
' 0',
' 0 ',
'1 2 3 4~9',
'1 1-3-5',
'0 5/2-5',
'',
'1 1/2/3',
'*5 8',
'x 8',
]
mut res := false
for exp in illegal_expressions {
res = false
parse_expression(exp) or { res = true }
assert res, "'${exp}' should produce an error"
}
}
fn test_auto_extend() ! {
ce1 := parse_expression('5 5')!
ce2 := parse_expression('5 5 *')!
ce3 := parse_expression('5 5 * *')!
assert ce1 == ce2 && ce2 == ce3
}
fn test_four() {
parse_expression('0 1 2 3 ') or { assert false }
}

View File

@ -1,99 +0,0 @@
module db
import models { GitRepo, GitRepoArch, GitRepoFilter }
// get_git_repos returns all GitRepo's in the database.
pub fn (db &VieterDb) get_git_repos(filter GitRepoFilter) []GitRepo {
// This seems to currently be blocked by a bug in the ORM, I'll have to ask
// around.
if filter.repo != '' {
res := sql db.conn {
select from GitRepo where repo == filter.repo order by id limit filter.limit offset filter.offset
}
return res
}
res := sql db.conn {
select from GitRepo order by id limit filter.limit offset filter.offset
}
return res
}
// get_git_repo tries to return a specific GitRepo.
pub fn (db &VieterDb) get_git_repo(repo_id int) ?GitRepo {
res := sql db.conn {
select from GitRepo where id == repo_id
}
// If a select statement fails, it returns a zeroed object. By
// checking one of the required fields, we can see whether the query
// returned a result or not.
if res.id == 0 {
return none
}
return res
}
// add_git_repo inserts the given GitRepo into the database.
pub fn (db &VieterDb) add_git_repo(repo GitRepo) {
sql db.conn {
insert repo into GitRepo
}
}
// delete_git_repo deletes the repo with the given ID from the database.
pub fn (db &VieterDb) delete_git_repo(repo_id int) {
sql db.conn {
delete from GitRepo where id == repo_id
delete from GitRepoArch where repo_id == repo_id
}
}
// update_git_repo updates any non-array values for a given GitRepo.
pub fn (db &VieterDb) update_git_repo(repo_id int, params map[string]string) {
mut values := []string{}
// TODO does this allow for SQL injection?
$for field in GitRepo.fields {
if field.name in params {
// Any fields that are array types require their own update method
$if field.typ is string {
values << "$field.name = '${params[field.name]}'"
}
}
}
values_str := values.join(', ')
// I think this is actual SQL & not the ORM language
query := 'update GitRepo set $values_str where id == $repo_id'
db.conn.exec_none(query)
}
// update_git_repo_archs updates a given GitRepo's arch value.
pub fn (db &VieterDb) update_git_repo_archs(repo_id int, archs []GitRepoArch) {
archs_with_id := archs.map(GitRepoArch{
...it
repo_id: repo_id
})
sql db.conn {
delete from GitRepoArch where repo_id == repo_id
}
for arch in archs_with_id {
sql db.conn {
insert arch into GitRepoArch
}
}
}
// git_repo_exists is a utility function that checks whether a repo with the
// given id exists.
pub fn (db &VieterDb) git_repo_exists(repo_id int) bool {
db.get_git_repo(repo_id) or { return false }
return true
}

View File

@ -1,9 +1,9 @@
module db module dbms
import sqlite import db.sqlite
import time import time
struct VieterDb { pub struct VieterDb {
conn sqlite.DB conn sqlite.DB
} }
@ -13,13 +13,25 @@ struct MigrationVersion {
} }
const ( const (
migrations_up = [$embed_file('migrations/001-initial/up.sql')] migrations_up = [
migrations_down = [$embed_file('migrations/001-initial/down.sql')] $embed_file('migrations/001-initial/up.sql'),
$embed_file('migrations/002-rename-to-targets/up.sql'),
$embed_file('migrations/003-target-url-type/up.sql'),
$embed_file('migrations/004-nullable-branch/up.sql'),
$embed_file('migrations/005-repo-path/up.sql'),
]
migrations_down = [
$embed_file('migrations/001-initial/down.sql'),
$embed_file('migrations/002-rename-to-targets/down.sql'),
$embed_file('migrations/003-target-url-type/down.sql'),
$embed_file('migrations/004-nullable-branch/down.sql'),
$embed_file('migrations/005-repo-path/down.sql'),
]
) )
// init initializes a database & adds the correct tables. // init initializes a database & adds the correct tables.
pub fn init(db_path string) ?VieterDb { pub fn init(db_path string) !VieterDb {
conn := sqlite.connect(db_path)? conn := sqlite.connect(db_path)!
sql conn { sql conn {
create table MigrationVersion create table MigrationVersion
@ -37,13 +49,13 @@ pub fn init(db_path string) ?VieterDb {
} }
// Apply each migration in order // Apply each migration in order
for i in cur_version.version .. db.migrations_up.len { for i in cur_version.version .. dbms.migrations_up.len {
migration := db.migrations_up[i].to_string() migration := dbms.migrations_up[i].to_string()
version_num := i + 1 version_num := i + 1
// vfmt does not like these dots // vfmt does not like these dots
println('Applying migration $version_num' + '...') println('Applying migration ${version_num}' + '...')
// The sqlite library seems to not like it when multiple statements are // The sqlite library seems to not like it when multiple statements are
// passed in a single exec. Therefore, we split them & run them all // passed in a single exec. Therefore, we split them & run them all
@ -52,7 +64,7 @@ pub fn init(db_path string) ?VieterDb {
res := conn.exec_none(part) res := conn.exec_none(part)
if res != sqlite.sqlite_done { if res != sqlite.sqlite_done {
return error('An error occurred while applying migration $version_num') return error('An error occurred while applying migration ${version_num}: SQLite error code ${res}')
} }
} }
@ -68,9 +80,9 @@ pub fn init(db_path string) ?VieterDb {
} }
} }
// row_into<T> converts an sqlite.Row into a given type T by parsing each field // row_into[T] converts an sqlite.Row into a given type T by parsing each field
// from a string according to its type. // from a string according to its type.
pub fn row_into<T>(row sqlite.Row) T { pub fn row_into[T](row sqlite.Row) T {
mut i := 0 mut i := 0
mut out := T{} mut out := T{}

View File

@ -1,4 +1,4 @@
module db module dbms
import models { BuildLog, BuildLogFilter } import models { BuildLog, BuildLogFilter }
import time import time
@ -7,21 +7,21 @@ import time
pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog { pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog {
mut where_parts := []string{} mut where_parts := []string{}
if filter.repo != 0 { if filter.target != 0 {
where_parts << 'repo_id == $filter.repo' where_parts << 'target_id == ${filter.target}'
} }
if filter.before != time.Time{} { if filter.before != time.Time{} {
where_parts << 'start_time < $filter.before.unix_time()' where_parts << 'start_time < ${filter.before.unix_time()}'
} }
if filter.after != time.Time{} { if filter.after != time.Time{} {
where_parts << 'start_time > $filter.after.unix_time()' where_parts << 'start_time > ${filter.after.unix_time()}'
} }
// NOTE: possible SQL injection // NOTE: possible SQL injection
if filter.arch != '' { if filter.arch != '' {
where_parts << "arch == '$filter.arch'" where_parts << "arch == '${filter.arch}'"
} }
mut parts := []string{} mut parts := []string{}
@ -30,36 +30,36 @@ pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog {
if exp[0] == `!` { if exp[0] == `!` {
code := exp[1..].int() code := exp[1..].int()
parts << 'exit_code != $code' parts << 'exit_code != ${code}'
} else { } else {
code := exp.int() code := exp.int()
parts << 'exit_code == $code' parts << 'exit_code == ${code}'
} }
} }
if parts.len > 0 { if parts.len > 0 {
where_parts << parts.map('($it)').join(' or ') where_parts << parts.map('(${it})').join(' or ')
} }
mut where_str := '' mut where_str := ''
if where_parts.len > 0 { if where_parts.len > 0 {
where_str = 'where ' + where_parts.map('($it)').join(' and ') where_str = 'where ' + where_parts.map('(${it})').join(' and ')
} }
query := 'select * from BuildLog $where_str limit $filter.limit offset $filter.offset' query := 'select * from BuildLog ${where_str} limit ${filter.limit} offset ${filter.offset}'
rows, _ := db.conn.exec(query) rows, _ := db.conn.exec(query)
res := rows.map(row_into<BuildLog>(it)) res := rows.map(row_into[BuildLog](it))
return res return res
} }
// get_build_logs_for_repo returns all BuildLog's in the database for a given // get_build_logs_for_target returns all BuildLog's in the database for a given
// repo. // target.
pub fn (db &VieterDb) get_build_logs_for_repo(repo_id int) []BuildLog { pub fn (db &VieterDb) get_build_logs_for_target(target_id int) []BuildLog {
res := sql db.conn { res := sql db.conn {
select from BuildLog where repo_id == repo_id order by id select from BuildLog where target_id == target_id order by id
} }
return res return res
@ -79,10 +79,16 @@ pub fn (db &VieterDb) get_build_log(id int) ?BuildLog {
} }
// add_build_log inserts the given BuildLog into the database. // add_build_log inserts the given BuildLog into the database.
pub fn (db &VieterDb) add_build_log(log BuildLog) { pub fn (db &VieterDb) add_build_log(log BuildLog) int {
sql db.conn { sql db.conn {
insert log into BuildLog insert log into BuildLog
} }
// Here, this does work because a log doesn't contain any foreign keys,
// meaning the ORM only has to do a single add
inserted_id := db.conn.last_id() as int
return inserted_id
} }
// delete_build_log delete the BuildLog with the given ID from the database. // delete_build_log delete the BuildLog with the given ID from the database.

View File

@ -0,0 +1,5 @@
ALTER TABLE Target RENAME TO GitRepo;
ALTER TABLE TargetArch RENAME TO GitRepoArch;
ALTER TABLE GitRepoArch RENAME COLUMN target_id TO repo_id;
ALTER TABLE BuildLog RENAME COLUMN target_id TO repo_id;

View File

@ -0,0 +1,5 @@
ALTER TABLE GitRepo RENAME TO Target;
ALTER TABLE GitRepoArch RENAME TO TargetArch;
ALTER TABLE TargetArch RENAME COLUMN repo_id TO target_id;
ALTER TABLE BuildLog RENAME COLUMN repo_id TO target_id;

View File

@ -0,0 +1,4 @@
-- I'm not sure whether I should remove any non-git targets here. Keeping them
-- will result in invalid targets, but removing them means losing data.
ALTER TABLE Target DROP COLUMN kind;

View File

@ -0,0 +1 @@
ALTER TABLE Target ADD COLUMN kind TEXT NOT NULL DEFAULT 'git';

View File

@ -0,0 +1,26 @@
-- This down won't really work because it'll throw NOT NULL errors, but I'm
-- just putting it here for future reference (still not sure whether I'm even
-- gonna use these)
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE Target RENAME TO _Target_old;
CREATE TABLE Target (
id INTEGER PRIMARY KEY,
url TEXT NOT NULL,
branch TEXT NOT NULL,
repo TEXT NOT NULL,
schedule TEXT,
kind TEXT NOT NULL DEFAULT 'git'
);
INSERT INTO Target (id, url, branch, repo, schedule, kind)
SELECT id, url, branch, repo, schedule, kind FROM _Target_old;
DROP TABLE _Target_old;
COMMIT;
PRAGMA foreign_keys=on;

View File

@ -0,0 +1,23 @@
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE Target RENAME TO _Target_old;
CREATE TABLE Target (
id INTEGER PRIMARY KEY,
url TEXT NOT NULL,
branch TEXT,
repo TEXT NOT NULL,
schedule TEXT,
kind TEXT NOT NULL DEFAULT 'git'
);
INSERT INTO Target (id, url, branch, repo, schedule, kind)
SELECT id, url, branch, repo, schedule, kind FROM _Target_old;
DROP TABLE _Target_old;
COMMIT;
PRAGMA foreign_keys=on;

View File

@ -0,0 +1 @@
ALTER TABLE Target DROP COLUMN path;

View File

@ -0,0 +1 @@
ALTER TABLE Target ADD COLUMN path TEXT;

87
src/dbms/targets.v 100644
View File

@ -0,0 +1,87 @@
module dbms
import models { Target, TargetArch }
// get_target tries to return a specific target.
pub fn (db &VieterDb) get_target(target_id int) ?Target {
res := sql db.conn {
select from Target where id == target_id
}
// If a select statement fails, it returns a zeroed object. By
// checking one of the required fields, we can see whether the query
// returned a result or not.
if res.id == 0 {
return none
}
return res
}
// add_target inserts the given target into the database.
pub fn (db &VieterDb) add_target(target Target) int {
sql db.conn {
insert target into Target
}
// ID of inserted target is the largest id
inserted_target := sql db.conn {
select from Target order by id desc limit 1
}
return inserted_target.id
}
// delete_target deletes the target with the given id from the database.
pub fn (db &VieterDb) delete_target(target_id int) {
sql db.conn {
delete from Target where id == target_id
delete from TargetArch where target_id == target_id
}
}
// update_target updates any non-array values for a given target.
pub fn (db &VieterDb) update_target(target_id int, params map[string]string) {
mut values := []string{}
// TODO does this allow for SQL injection?
$for field in Target.fields {
if field.name in params {
// Any fields that are array types require their own update method
$if field.typ is string {
values << "${field.name} = '${params[field.name]}'"
}
}
}
values_str := values.join(', ')
// I think this is actual SQL & not the ORM language
query := 'update Target set ${values_str} where id == ${target_id}'
db.conn.exec_none(query)
}
// update_target_archs updates a given target's arch value.
pub fn (db &VieterDb) update_target_archs(target_id int, archs []TargetArch) {
archs_with_id := archs.map(TargetArch{
...it
target_id: target_id
})
sql db.conn {
delete from TargetArch where target_id == target_id
}
for arch in archs_with_id {
sql db.conn {
insert arch into TargetArch
}
}
}
// target_exists is a utility function that checks whether a target with the
// given id exists.
pub fn (db &VieterDb) target_exists(target_id int) bool {
db.get_target(target_id) or { return false }
return true
}

View File

@ -0,0 +1,129 @@
module dbms
import models { Target, TargetFilter }
import db.sqlite
// Iterator providing a filtered view into the list of targets currently stored
// in the database. It replaces functionality usually performed in the database
// using SQL queries that can't currently be used due to missing stuff in V's
// ORM.
pub struct TargetsIterator {
conn sqlite.DB
filter TargetFilter
window_size int = 32
mut:
window []Target
window_index u64
// Offset in entire list of unfiltered targets
offset int
// Offset in filtered list of targets
filtered_offset u64
started bool
done bool
}
// targets returns an iterator allowing filtered access to the list of targets.
pub fn (db &VieterDb) targets(filter TargetFilter) TargetsIterator {
window_size := 32
return TargetsIterator{
conn: db.conn
filter: filter
window: []Target{cap: window_size}
window_size: window_size
}
}
// advance_window moves the sliding window over the filtered list of targets
// until it either reaches the end of the list of targets, or has encountered a
// non-empty window.
fn (mut ti TargetsIterator) advance_window() {
for {
ti.window = sql ti.conn {
select from Target order by id limit ti.window_size offset ti.offset
}
ti.offset += ti.window.len
if ti.window.len == 0 {
ti.done = true
return
}
if ti.filter.repo != '' {
ti.window = ti.window.filter(it.repo == ti.filter.repo)
}
if ti.filter.arch != '' {
ti.window = ti.window.filter(it.arch.any(it.value == ti.filter.arch))
}
if ti.filter.query != '' {
ti.window = ti.window.filter(it.url.contains(ti.filter.query)
|| it.path.contains(ti.filter.query) || it.branch.contains(ti.filter.query))
}
// We break out of the loop once we found a non-empty window
if ti.window.len > 0 {
break
}
}
}
// next returns the next target, if possible.
pub fn (mut ti TargetsIterator) next() ?Target {
if ti.done {
return none
}
// The first call to `next` will cause the sliding window to move to where
// the requested offset starts
if !ti.started {
ti.advance_window()
// Skip all matched targets until the requested offset
for !ti.done && ti.filtered_offset + u64(ti.window.len) <= ti.filter.offset {
ti.filtered_offset += u64(ti.window.len)
ti.advance_window()
}
if ti.done {
return none
}
left_inside_window := ti.filter.offset - ti.filtered_offset
ti.window_index = left_inside_window
ti.filtered_offset += left_inside_window
ti.started = true
}
return_value := ti.window[ti.window_index]
ti.window_index++
ti.filtered_offset++
// Next call will be past the requested offset
if ti.filter.limit > 0 && ti.filtered_offset == ti.filter.offset + ti.filter.limit {
ti.done = true
}
// Ensure the next call has a new valid window
if ti.window_index == u64(ti.window.len) {
ti.advance_window()
ti.window_index = 0
}
return return_value
}
// collect consumes the entire iterator & returns the result as an array.
pub fn (mut ti TargetsIterator) collect() []Target {
mut out := []Target{}
for t in ti {
out << t
}
return out
}

View File

@ -1,3 +0,0 @@
This module implements part of the Docker Engine API v1.41
([documentation](https://docs.docker.com/engine/api/v1.41/)) using socket-based
HTTP communication.

View File

@ -1,123 +0,0 @@
module docker
import json
import net.urllib
import time
import net.http { Method }
struct DockerError {
message string
}
pub struct NewContainer {
image string [json: Image]
entrypoint []string [json: Entrypoint]
cmd []string [json: Cmd]
env []string [json: Env]
work_dir string [json: WorkingDir]
user string [json: User]
}
struct CreatedContainer {
pub:
id string [json: Id]
warnings []string [json: Warnings]
}
// create_container creates a new container with the given config.
pub fn (mut d DockerConn) create_container(c NewContainer) ?CreatedContainer {
d.send_request_with_json(Method.post, urllib.parse('/v1.41/containers/create')?, c)?
head, res := d.read_response()?
if head.status_code != 201 {
data := json.decode(DockerError, res)?
return error(data.message)
}
data := json.decode(CreatedContainer, res)?
return data
}
// start_container starts the container with the given id.
pub fn (mut d DockerConn) start_container(id string) ? {
d.send_request(Method.post, urllib.parse('/v1.41/containers/$id/start')?)?
head, body := d.read_response()?
if head.status_code != 204 {
data := json.decode(DockerError, body)?
return error(data.message)
}
}
struct ContainerInspect {
pub mut:
state ContainerState [json: State]
}
struct ContainerState {
pub:
running bool [json: Running]
status string [json: Status]
exit_code int [json: ExitCode]
// These use a rather specific format so they have to be parsed later
start_time_str string [json: StartedAt]
end_time_str string [json: FinishedAt]
pub mut:
start_time time.Time [skip]
end_time time.Time [skip]
}
// inspect_container returns detailed information for a given container.
pub fn (mut d DockerConn) inspect_container(id string) ?ContainerInspect {
d.send_request(Method.get, urllib.parse('/v1.41/containers/$id/json')?)?
head, body := d.read_response()?
if head.status_code != 200 {
data := json.decode(DockerError, body)?
return error(data.message)
}
mut data := json.decode(ContainerInspect, body)?
// The Docker engine API *should* always return UTC time.
data.state.start_time = time.parse_rfc3339(data.state.start_time_str)?
if data.state.status == 'exited' {
data.state.end_time = time.parse_rfc3339(data.state.end_time_str)?
}
return data
}
// remove_container removes the container with the given id.
pub fn (mut d DockerConn) remove_container(id string) ? {
d.send_request(Method.delete, urllib.parse('/v1.41/containers/$id')?)?
head, body := d.read_response()?
if head.status_code != 204 {
data := json.decode(DockerError, body)?
return error(data.message)
}
}
// get_container_logs returns a reader object allowing access to the
// container's logs.
pub fn (mut d DockerConn) get_container_logs(id string) ?&StreamFormatReader {
d.send_request(Method.get, urllib.parse('/v1.41/containers/$id/logs?stdout=true&stderr=true')?)?
head := d.read_response_head()?
if head.status_code != 200 {
content_length := head.header.get(http.CommonHeader.content_length)?.int()
body := d.read_response_body(content_length)?
data := json.decode(DockerError, body)?
return error(data.message)
}
return d.get_stream_format_reader()
}

View File

@ -1,137 +0,0 @@
module docker
import net.unix
import io
import net.http
import strings
import net.urllib
import json
import util
const (
socket = '/var/run/docker.sock'
buf_len = 10 * 1024
http_separator = [u8(`\r`), `\n`, `\r`, `\n`]
http_chunk_separator = [u8(`\r`), `\n`]
)
pub struct DockerConn {
mut:
socket &unix.StreamConn
reader &io.BufferedReader
}
// new_conn creates a new connection to the Docker daemon.
pub fn new_conn() ?&DockerConn {
s := unix.connect_stream(docker.socket)?
d := &DockerConn{
socket: s
reader: io.new_buffered_reader(reader: s)
}
return d
}
// close closes the underlying socket connection.
pub fn (mut d DockerConn) close() ? {
d.socket.close()?
}
// send_request sends an HTTP request without body.
pub fn (mut d DockerConn) send_request(method http.Method, url urllib.URL) ? {
req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\n\n'
d.socket.write_string(req)?
// When starting a new request, the reader needs to be reset.
d.reader = io.new_buffered_reader(reader: d.socket)
}
// send_request_with_body sends an HTTP request with the given body.
pub fn (mut d DockerConn) send_request_with_body(method http.Method, url urllib.URL, content_type string, body string) ? {
req := '$method $url.request_uri() HTTP/1.1\nHost: localhost\nContent-Type: $content_type\nContent-Length: $body.len\n\n$body\n\n'
d.socket.write_string(req)?
// When starting a new request, the reader needs to be reset.
d.reader = io.new_buffered_reader(reader: d.socket)
}
// send_request_with_json<T> is a convenience wrapper around
// send_request_with_body that encodes the input as JSON.
pub fn (mut d DockerConn) send_request_with_json<T>(method http.Method, url urllib.URL, data &T) ? {
body := json.encode(data)
return d.send_request_with_body(method, url, 'application/json', body)
}
// read_response_head consumes the socket's contents until it encounters
// '\r\n\r\n', after which it parses the response as an HTTP response.
// Importantly, this function never consumes the reader past the HTTP
// separator, so the body can be read fully later on.
pub fn (mut d DockerConn) read_response_head() ?http.Response {
mut res := []u8{}
util.read_until_separator(mut d.reader, mut res, docker.http_separator)?
return http.parse_response(res.bytestr())
}
// read_response_body reads `length` bytes from the stream. It can be used when
// the response encoding isn't chunked to fully read it.
pub fn (mut d DockerConn) read_response_body(length int) ?string {
if length == 0 {
return ''
}
mut buf := []u8{len: docker.buf_len}
mut c := 0
mut builder := strings.new_builder(docker.buf_len)
for builder.len < length {
c = d.reader.read(mut buf) or { break }
builder.write(buf[..c])?
}
return builder.str()
}
// read_response is a convenience function which always consumes the entire
// response & returns it. It should only be used when we're certain that the
// result isn't too large.
pub fn (mut d DockerConn) read_response() ?(http.Response, string) {
head := d.read_response_head()?
if head.header.get(http.CommonHeader.transfer_encoding) or { '' } == 'chunked' {
mut builder := strings.new_builder(1024)
mut body := d.get_chunked_response_reader()
util.reader_to_writer(mut body, mut builder)?
return head, builder.str()
}
content_length := head.header.get(http.CommonHeader.content_length)?.int()
res := d.read_response_body(content_length)?
return head, res
}
// get_chunked_response_reader returns a ChunkedResponseReader using the socket
// as reader.
pub fn (mut d DockerConn) get_chunked_response_reader() &ChunkedResponseReader {
r := new_chunked_response_reader(d.reader)
return r
}
// get_stream_format_reader returns a StreamFormatReader using the socket as
// reader.
pub fn (mut d DockerConn) get_stream_format_reader() &StreamFormatReader {
r := new_chunked_response_reader(d.reader)
r2 := new_stream_format_reader(r)
return r2
}

View File

@ -1,61 +0,0 @@
module docker
import net.http { Method }
import net.urllib
import json
struct Image {
pub:
id string [json: Id]
}
// pull_image pulls the given image:tag.
pub fn (mut d DockerConn) pull_image(image string, tag string) ? {
d.send_request(Method.post, urllib.parse('/v1.41/images/create?fromImage=$image&tag=$tag')?)?
head := d.read_response_head()?
if head.status_code != 200 {
content_length := head.header.get(http.CommonHeader.content_length)?.int()
body := d.read_response_body(content_length)?
data := json.decode(DockerError, body)?
return error(data.message)
}
// Keep reading the body until the pull has completed
mut body := d.get_chunked_response_reader()
mut buf := []u8{len: 1024}
for {
body.read(mut buf) or { break }
}
}
// create_image_from_container creates a new image from a container.
pub fn (mut d DockerConn) create_image_from_container(id string, repo string, tag string) ?Image {
d.send_request(Method.post, urllib.parse('/v1.41/commit?container=$id&repo=$repo&tag=$tag')?)?
head, body := d.read_response()?
if head.status_code != 201 {
data := json.decode(DockerError, body)?
return error(data.message)
}
data := json.decode(Image, body)?
return data
}
// remove_image removes the image with the given id.
pub fn (mut d DockerConn) remove_image(id string) ? {
d.send_request(Method.delete, urllib.parse('/v1.41/images/$id')?)?
head, body := d.read_response()?
if head.status_code != 200 {
data := json.decode(DockerError, body)?
return error(data.message)
}
}

View File

@ -1,135 +0,0 @@
module docker
import io
import util
import encoding.binary
import encoding.hex
// ChunkedResponseReader parses an underlying HTTP chunked response, exposing
// it as if it was a continuous stream of data.
struct ChunkedResponseReader {
mut:
reader io.BufferedReader
bytes_left_in_chunk u64
started bool
}
// new_chunked_response_reader creates a new ChunkedResponseReader on the heap
// with the provided reader.
pub fn new_chunked_response_reader(reader io.BufferedReader) &ChunkedResponseReader {
r := &ChunkedResponseReader{
reader: reader
}
return r
}
// read satisfies the io.Reader interface.
pub fn (mut r ChunkedResponseReader) read(mut buf []u8) ?int {
if r.bytes_left_in_chunk == 0 {
// An io.BufferedReader always returns none if its stream has
// ended.
r.bytes_left_in_chunk = r.read_chunk_size()?
}
mut c := 0
// Make sure we don't read more than we can safely read. This is to avoid
// the underlying reader from becoming out of sync with our parsing:
if buf.len > r.bytes_left_in_chunk {
c = r.reader.read(mut buf[..r.bytes_left_in_chunk])?
} else {
c = r.reader.read(mut buf)?
}
r.bytes_left_in_chunk -= u64(c)
return c
}
// read_chunk_size advances the reader & reads the size of the next HTTP chunk.
// This function should only be called if the previous chunk has been
// completely consumed.
fn (mut r ChunkedResponseReader) read_chunk_size() ?u64 {
if r.started {
mut buf := []u8{len: 2}
// Each chunk ends with a `\r\n` which we want to skip first
r.reader.read(mut buf)?
}
r.started = true
mut res := []u8{}
util.read_until_separator(mut r.reader, mut res, http_chunk_separator)?
// The length of the next chunk is provided as a hexadecimal
mut num_data := hex.decode(res#[..-2].bytestr())?
for num_data.len < 8 {
num_data.insert(0, 0)
}
num := binary.big_endian_u64(num_data)
// This only occurs for the very last chunk, which always reports a size of
// 0.
if num == 0 {
return none
}
return num
}
// StreamFormatReader parses an underlying stream of Docker logs, removing the
// header bytes.
struct StreamFormatReader {
mut:
reader ChunkedResponseReader
bytes_left_in_chunk u32
}
// new_stream_format_reader creates a new StreamFormatReader using the given
// reader.
pub fn new_stream_format_reader(reader ChunkedResponseReader) &StreamFormatReader {
r := &StreamFormatReader{
reader: reader
}
return r
}
// read satisfies the io.Reader interface.
pub fn (mut r StreamFormatReader) read(mut buf []u8) ?int {
if r.bytes_left_in_chunk == 0 {
r.bytes_left_in_chunk = r.read_chunk_size()?
}
mut c := 0
if buf.len > r.bytes_left_in_chunk {
c = r.reader.read(mut buf[..r.bytes_left_in_chunk])?
} else {
c = r.reader.read(mut buf)?
}
r.bytes_left_in_chunk -= u32(c)
return c
}
// read_chunk_size advances the reader & reads the header bytes for the length
// of the next chunk.
fn (mut r StreamFormatReader) read_chunk_size() ?u32 {
mut buf := []u8{len: 8}
r.reader.read(mut buf)?
num := binary.big_endian_u32(buf[4..])
if num == 0 {
return none
}
return num
}

7
src/env/README.md vendored
View File

@ -1,7 +0,0 @@
This module provides a framework for parsing a configuration, defined as a
struct, from both a TOML configuration file & environment variables. Some
notable features are:
* Overwrite values in config file using environment variables
* Allow default values in config struct
* Read environment variable value from file

102
src/env/env.v vendored
View File

@ -1,102 +0,0 @@
module env
import os
import toml
const (
// The prefix that every environment variable should have
prefix = 'VIETER_'
// The suffix an environment variable in order for it to be loaded from a file
// instead
file_suffix = '_FILE'
)
// get_env_var tries to read the contents of the given environment variable. It
// looks for either `${env.prefix}${field_name.to_upper()}` or
// `${env.prefix}${field_name.to_upper()}${env.file_suffix}`, returning the
// contents of the file instead if the latter. If both or neither exist, the
// function returns an error.
fn get_env_var(field_name string) ?string {
env_var_name := '$env.prefix$field_name.to_upper()'
env_file_name := '$env.prefix$field_name.to_upper()$env.file_suffix'
env_var := os.getenv(env_var_name)
env_file := os.getenv(env_file_name)
// If both are missing, we return an empty string
if env_var == '' && env_file == '' {
return ''
}
// If they're both set, we report a conflict
if env_var != '' && env_file != '' {
return error('Only one of $env_var_name or $env_file_name can be defined.')
}
// If it's the env var itself, we return it.
// I'm pretty sure this also prevents variable ending in _FILE (e.g.
// VIETER_LOG_FILE) from being mistakingely read as an _FILE suffixed env
// var.
if env_var != '' {
return env_var
}
// Otherwise, we process the file
return os.read_file(env_file) or {
error('Failed to read file defined in $env_file_name: ${err.msg()}.')
}
}
// load<T> attempts to create an object of type T from the given path to a toml
// file & environment variables. For each field, it will select either a value
// given from an environment variable, a value defined in the config file or a
// configured default if present, in that order.
pub fn load<T>(path string) ?T {
mut res := T{}
if os.exists(path) {
// We don't use reflect here because reflect also sets any fields not
// in the toml back to their zero value, which we don't want
doc := toml.parse_file(path)?
$for field in T.fields {
s := doc.value(field.name)
if s !is toml.Null {
$if field.typ is string {
res.$(field.name) = s.string()
} $else $if field.typ is int {
res.$(field.name) = s.int()
}
}
}
}
$for field in T.fields {
env_value := get_env_var(field.name)?
// The value of an env var will always take precedence over the toml
// file.
if env_value != '' {
$if field.typ is string {
res.$(field.name) = env_value
} $else $if field.typ is int {
res.$(field.name) = env_value.int()
}
}
// Now, we check whether a value is present. If there isn't, that means
// it isn't in the config file, nor is there a default or an env var.
mut has_value := false
$if field.typ is string {
has_value = res.$(field.name) != ''
} $else $if field.typ is int {
has_value = res.$(field.name) != 0
}
if !has_value {
return error("Missing config variable '$field.name' with no provided default. Either add it to the config file or provide it using an environment variable.")
}
}
return res
}

Some files were not shown because too many files have changed in this diff Show More